// Updated admin_post.go with better image handling package handlers import ( "fmt" "io" "net/http" "os" "path/filepath" "strconv" "strings" "time" "github.com/patel-mann/poll-system/app/internal/models" "github.com/patel-mann/poll-system/app/internal/utils" ) func PostsHandler(w http.ResponseWriter, r *http.Request) { userID := r.Context().Value("user_id").(int) role := r.Context().Value("user_role").(int) if r.Method == http.MethodPost { // Parse multipart form err := r.ParseMultipartForm(10 << 20) // 10MB max if err != nil { fmt.Printf("Error parsing form: %v\n", err) http.Error(w, "Invalid form", http.StatusBadRequest) return } content := r.FormValue("content") if strings.TrimSpace(content) == "" { http.Error(w, "Content cannot be empty", http.StatusBadRequest) return } var imagePath string file, handler, err := r.FormFile("image") if err == nil && file != nil { defer file.Close() // Validate file type allowedTypes := map[string]bool{ ".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true, } ext := strings.ToLower(filepath.Ext(handler.Filename)) if !allowedTypes[ext] { http.Error(w, "Invalid file type. Only images allowed.", http.StatusBadRequest) return } // Ensure uploads folder exists uploadDir := "uploads" if err := os.MkdirAll(uploadDir, 0755); err != nil { fmt.Printf("Error creating upload directory: %v\n", err) http.Error(w, "Unable to create upload directory", http.StatusInternalServerError) return } // Create unique filename filename := fmt.Sprintf("%d_%d%s", userID, time.Now().UnixNano(), ext) savePath := filepath.Join(uploadDir, filename) out, err := os.Create(savePath) if err != nil { fmt.Printf("Error creating file: %v\n", err) http.Error(w, "Unable to save file", http.StatusInternalServerError) return } defer out.Close() _, err = io.Copy(out, file) if err != nil { fmt.Printf("Error copying file: %v\n", err) http.Error(w, "Failed to save file", http.StatusInternalServerError) return } // Save path relative to the static route imagePath = "/uploads/" + filename fmt.Printf("Image saved at: %s\n", imagePath) } else if err != http.ErrMissingFile { fmt.Printf("Error getting file: %v\n", err) } // Insert post _, err = models.DB.Exec(`INSERT INTO post (author_id, content, image_url) VALUES ($1, $2, $3)`, userID, content, imagePath) if err != nil { fmt.Printf("Database error: %v\n", err) http.Error(w, "Failed to create post", http.StatusInternalServerError) return } fmt.Printf("Post created successfully with image: %s\n", imagePath) http.Redirect(w, r, "/posts", http.StatusSeeOther) return } // GET request: fetch posts rows, err := models.DB.Query(` SELECT p.post_id, p.author_id, u.first_name || ' ' || u.last_name AS author_name, p.content, COALESCE(p.image_url, '') as image_url, p.created_at FROM post p JOIN users u ON p.author_id = u.user_id ORDER BY p.created_at DESC `) if err != nil { fmt.Printf("Database query error: %v\n", err) http.Error(w, "Failed to fetch posts", http.StatusInternalServerError) return } defer rows.Close() var posts []models.Post for rows.Next() { var p models.Post err := rows.Scan(&p.PostID, &p.AuthorID, &p.AuthorName, &p.Content, &p.ImageURL, &p.CreatedAt) if err != nil { fmt.Printf("Row scan error: %v\n", err) continue } posts = append(posts, p) } // Add cache busting parameter to image URLs for i := range posts { if posts[i].ImageURL != "" { posts[i].ImageURL += "?t=" + strconv.FormatInt(time.Now().UnixNano(), 10) fmt.Printf("Post %d image URL: %s\n", posts[i].PostID, posts[i].ImageURL) } } // Get navigation flags showAdminNav, showVolunteerNav := getNavFlags(role) fmt.Printf("Rendering %d posts\n", len(posts)) utils.Render(w, "posts.html", map[string]interface{}{ "Title": "Posts", "IsAuthenticated": true, "ShowAdminNav": showAdminNav, "ShowVolunteerNav": showVolunteerNav, "Posts": posts, "ActiveSection": "posts", }) } // Helper function (add this to your main.go if not already there) func getNavFlags(role int) (bool, bool) { showAdminNav := role == 1 // Admin role showVolunteerNav := role == 3 // Volunteer role return showAdminNav, showVolunteerNav }