142 lines
3.9 KiB
Go
142 lines
3.9 KiB
Go
package handler
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const (
|
|
maxImageSize = 5 << 20 // 5 MB
|
|
uploadDir = "uploads"
|
|
)
|
|
|
|
// handleListImages behandelt GET /api/v1/exercises/{id}/images.
|
|
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)
|
|
}
|
|
|
|
// handleUploadImage behandelt POST /api/v1/exercises/{id}/images.
|
|
// Erwartet multipart/form-data mit Feld "image" (nur JPEG, max 5 MB).
|
|
// Die Datei wird unter uploads/<uuid>.jpg gespeichert.
|
|
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)
|
|
}
|
|
|
|
// handleDeleteImage behandelt DELETE /api/v1/exercises/{id}/images/{imageId}.
|
|
// Löscht den Datenbankeintrag und die zugehörige Datei vom Dateisystem.
|
|
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 beim Start sicherstellen
|
|
if err := os.MkdirAll(uploadDir, 0755); err != nil {
|
|
log.Printf("Warnung: Upload-Verzeichnis konnte nicht erstellt werden: %v", err)
|
|
}
|
|
}
|