package handler import ( "fmt" "io" "net/http" "os" "path/filepath" "github.com/google/uuid" ) const ( maxImageSize = 5 << 20 // 5 MB uploadDir = "uploads" ) func (h *Handler) handleListImages(w http.ResponseWriter, r *http.Request) { exerciseID, err := pathID(r, "id") if err != nil { writeError(w, http.StatusBadRequest, "Ungültige ID") return } images, err := h.store.ListExerciseImages(exerciseID) if err != nil { writeError(w, http.StatusInternalServerError, "Fehler beim Laden der Bilder") return } writeJSON(w, http.StatusOK, images) } func (h *Handler) handleUploadImage(w http.ResponseWriter, r *http.Request) { exerciseID, err := pathID(r, "id") if err != nil { writeError(w, http.StatusBadRequest, "Ungültige ID") return } // Übung muss existieren exercise, err := h.store.GetExercise(exerciseID) if err != nil { writeError(w, http.StatusInternalServerError, "Fehler beim Prüfen der Übung") return } if exercise == nil { writeError(w, http.StatusNotFound, "Übung nicht gefunden") return } r.Body = http.MaxBytesReader(w, r.Body, maxImageSize) if err := r.ParseMultipartForm(maxImageSize); err != nil { writeError(w, http.StatusBadRequest, "Datei zu groß (max 5 MB)") return } file, header, err := r.FormFile("image") if err != nil { writeError(w, http.StatusBadRequest, "Kein Bild im Request") return } defer file.Close() // Content-Type prüfen ct := header.Header.Get("Content-Type") if ct != "image/jpeg" { writeError(w, http.StatusBadRequest, "Nur JPG-Bilder erlaubt") return } // Datei speichern filename := uuid.New().String() + ".jpg" destPath := filepath.Join(uploadDir, filename) if err := os.MkdirAll(uploadDir, 0755); err != nil { writeError(w, http.StatusInternalServerError, "Fehler beim Erstellen des Upload-Verzeichnisses") return } dst, err := os.Create(destPath) if err != nil { writeError(w, http.StatusInternalServerError, "Fehler beim Speichern der Datei") return } defer dst.Close() if _, err := io.Copy(dst, file); err != nil { os.Remove(destPath) writeError(w, http.StatusInternalServerError, "Fehler beim Schreiben der Datei") return } img, err := h.store.CreateExerciseImage(exerciseID, filename) if err != nil { os.Remove(destPath) writeError(w, http.StatusInternalServerError, "Fehler beim Speichern des Bildes") return } writeJSON(w, http.StatusCreated, img) } func (h *Handler) handleDeleteImage(w http.ResponseWriter, r *http.Request) { imageID, err := pathID(r, "imageId") if err != nil { writeError(w, http.StatusBadRequest, "Ungültige Bild-ID") return } filename, err := h.store.DeleteExerciseImage(imageID) if err != nil { writeError(w, http.StatusNotFound, "Bild nicht gefunden") return } // Datei vom Dateisystem löschen os.Remove(filepath.Join(uploadDir, filename)) w.WriteHeader(http.StatusNoContent) } // RegisterImageRoutes registriert die Bild-spezifischen Routen und den statischen File-Server. func (h *Handler) RegisterImageRoutes(mux *http.ServeMux) { mux.HandleFunc("GET /api/v1/exercises/{id}/images", h.handleListImages) mux.HandleFunc("POST /api/v1/exercises/{id}/images", h.handleUploadImage) mux.HandleFunc("DELETE /api/v1/exercises/{id}/images/{imageId}", h.handleDeleteImage) // Bilder statisch servieren mux.Handle("GET /uploads/", http.StripPrefix("/uploads/", http.FileServer(http.Dir(uploadDir)))) // uploads-Verzeichnis sicherstellen os.MkdirAll(uploadDir, 0755) fmt.Println("Bild-Upload konfiguriert:", uploadDir) }