package api import ( "encoding/json" "net/http" "strconv" "github.com/mjensen/weimar/internal/db" ) func (a *API) HandleListImages(w http.ResponseWriter, r *http.Request) { tags := r.URL.Query()["tags"] uploader := r.URL.Query().Get("uploader") page, _ := strconv.Atoi(r.URL.Query().Get("page")) if page < 1 { page = 1 } perPage, _ := strconv.Atoi(r.URL.Query().Get("per_page")) if perPage < 1 || perPage > 100 { perPage = 20 } offset := (page - 1) * perPage result, err := a.DB.ListImages(r.Context(), tags, uploader, perPage, offset) if err != nil { writeError(w, http.StatusInternalServerError, "failed to list images") return } // Load tags and uploader for each image. for i := range result.Images { img := &result.Images[i] tags, err := a.DB.GetImageTags(r.Context(), img.ID) if err == nil { img.Tags = tags } user, err := a.DB.GetUserByID(r.Context(), img.UploadedBy) if err == nil { img.UploaderUsername = user.Username if user.AvatarPath != nil { img.AvatarURL = "/api/users/" + strconv.FormatInt(img.UploadedBy, 10) + "/avatar" } } } writeJSON(w, http.StatusOK, map[string]any{ "images": result.Images, "total": result.Total, "page": page, "per_page": perPage, }) } func (a *API) HandleGetImage(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { writeError(w, http.StatusBadRequest, "invalid image id") return } img, err := a.DB.GetImageByID(r.Context(), id) if err != nil { writeError(w, http.StatusNotFound, "image not found") return } tagsDetail, err := a.DB.GetImageTagsWithCounts(r.Context(), id) if err != nil { tagsDetail = make([]db.Tag, 0) } uploaderUsername := "" avatarURL := "" user, err := a.DB.GetUserByID(r.Context(), img.UploadedBy) if err == nil { uploaderUsername = user.Username if user.AvatarPath != nil { avatarURL = "/api/users/" + strconv.FormatInt(img.UploadedBy, 10) + "/avatar" } } authUser := UserFromContext(r.Context()) isOwner := authUser != nil && authUser.ID == img.UploadedBy writeJSON(w, http.StatusOK, map[string]any{ "id": img.ID, "filename": img.Filename, "storage_path": img.StoragePath, "thumbnail_path": img.ThumbnailPath, "uploaded_by": img.UploadedBy, "uploader_username": uploaderUsername, "avatar_url": avatarURL, "is_owner": isOwner, "file_size": img.FileSize, "width": img.Width, "height": img.Height, "mime_type": img.MimeType, "created_at": img.CreatedAt, "updated_at": img.UpdatedAt, "tags": img.Tags, "tags_detail": tagsDetail, }) } type tagsRequest struct { Tags []string `json:"tags"` } func (a *API) HandleAddTags(w http.ResponseWriter, r *http.Request) { user := UserFromContext(r.Context()) if user == nil { writeError(w, http.StatusUnauthorized, "unauthorized") return } idStr := r.PathValue("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { writeError(w, http.StatusBadRequest, "invalid image id") return } img, err := a.DB.GetImageByID(r.Context(), id) if err != nil { writeError(w, http.StatusNotFound, "image not found") return } if img.UploadedBy != user.ID { writeError(w, http.StatusForbidden, "you can only edit tags on your own uploads") return } var req tagsRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid JSON body") return } if len(req.Tags) == 0 { writeJSON(w, http.StatusOK, map[string]any{"tags_detail": img.Tags}) return } if err := a.DB.AddImageTags(r.Context(), id, req.Tags); err != nil { writeError(w, http.StatusInternalServerError, "failed to add tags") return } tagsDetail, err := a.DB.GetImageTagsWithCounts(r.Context(), id) if err != nil { tagsDetail = make([]db.Tag, 0) } writeJSON(w, http.StatusOK, map[string]any{"tags_detail": tagsDetail}) } func (a *API) HandleRemoveTags(w http.ResponseWriter, r *http.Request) { user := UserFromContext(r.Context()) if user == nil { writeError(w, http.StatusUnauthorized, "unauthorized") return } idStr := r.PathValue("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { writeError(w, http.StatusBadRequest, "invalid image id") return } img, err := a.DB.GetImageByID(r.Context(), id) if err != nil { writeError(w, http.StatusNotFound, "image not found") return } if img.UploadedBy != user.ID { writeError(w, http.StatusForbidden, "you can only edit tags on your own uploads") return } var req tagsRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid JSON body") return } if len(req.Tags) == 0 { writeJSON(w, http.StatusOK, map[string]any{"tags_detail": img.Tags}) return } if err := a.DB.RemoveImageTags(r.Context(), id, req.Tags); err != nil { writeError(w, http.StatusInternalServerError, "failed to remove tags") return } tagsDetail, err := a.DB.GetImageTagsWithCounts(r.Context(), id) if err != nil { tagsDetail = make([]db.Tag, 0) } writeJSON(w, http.StatusOK, map[string]any{"tags_detail": tagsDetail}) }