This commit is contained in:
Christoph K.
2026-04-07 09:49:17 +02:00
parent 063aa67615
commit 4db170b467
37 changed files with 269 additions and 48 deletions

View File

@@ -6,6 +6,8 @@ import (
"net/http"
)
// handleListExercises behandelt GET /api/v1/exercises.
// Unterstützt optionale Query-Parameter: muscle_group und q (Namenssuche).
func (h *Handler) handleListExercises(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -23,6 +25,7 @@ func (h *Handler) handleListExercises(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, exercises)
}
// handleCreateExercise behandelt POST /api/v1/exercises.
func (h *Handler) handleCreateExercise(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -48,6 +51,7 @@ func (h *Handler) handleCreateExercise(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusCreated, exercise)
}
// handleUpdateExercise behandelt PUT /api/v1/exercises/{id}.
func (h *Handler) handleUpdateExercise(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -82,6 +86,8 @@ func (h *Handler) handleUpdateExercise(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, exercise)
}
// handleDeleteExercise behandelt DELETE /api/v1/exercises/{id}.
// Führt einen Soft-Delete durch (setzt deleted_at).
func (h *Handler) handleDeleteExercise(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {

View File

@@ -61,26 +61,32 @@ func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
// --- Hilfsfunktionen ---
// writeJSON schreibt data als JSON mit dem angegebenen HTTP-Statuscode.
func writeJSON(w http.ResponseWriter, status int, data any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
// writeError schreibt eine Fehlerantwort im Format {"error": "..."}.
func writeError(w http.ResponseWriter, status int, message string) {
writeJSON(w, status, map[string]string{"error": message})
}
// decodeJSON dekodiert den Request-Body als JSON in dst.
// Unbekannte Felder werden als Fehler behandelt.
func decodeJSON(r *http.Request, dst any) error {
dec := json.NewDecoder(r.Body)
dec.DisallowUnknownFields()
return dec.Decode(dst)
}
// pathID liest einen Integer-Pfadparameter aus dem Request.
func pathID(r *http.Request, name string) (int64, error) {
return strconv.ParseInt(r.PathValue(name), 10, 64)
}
// queryInt liest einen Integer-Query-Parameter. Gibt defaultVal zurück wenn der Parameter fehlt oder ungültig ist.
func queryInt(r *http.Request, name string, defaultVal int) int {
v := r.URL.Query().Get(name)
if v == "" {

View File

@@ -1,8 +1,8 @@
package handler
import (
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
@@ -15,6 +15,7 @@ const (
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 {
@@ -30,6 +31,9 @@ func (h *Handler) handleListImages(w http.ResponseWriter, r *http.Request) {
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 {
@@ -99,6 +103,8 @@ func (h *Handler) handleUploadImage(w http.ResponseWriter, r *http.Request) {
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 {
@@ -128,8 +134,8 @@ func (h *Handler) RegisterImageRoutes(mux *http.ServeMux) {
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)
// uploads-Verzeichnis beim Start sicherstellen
if err := os.MkdirAll(uploadDir, 0755); err != nil {
log.Printf("Warnung: Upload-Verzeichnis konnte nicht erstellt werden: %v", err)
}
}

View File

@@ -7,6 +7,9 @@ import (
"strings"
)
// handleGetActiveSession behandelt GET /api/v1/sessions/active.
// Gibt die offene Session des Nutzers mit den Übungen des zugehörigen Sets zurück,
// oder null wenn keine Session aktiv ist.
func (h *Handler) handleGetActiveSession(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -37,6 +40,8 @@ func (h *Handler) handleGetActiveSession(w http.ResponseWriter, r *http.Request)
})
}
// handleCreateSession behandelt POST /api/v1/sessions.
// Gibt 409 zurück wenn bereits eine offene Session existiert.
func (h *Handler) handleCreateSession(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -70,6 +75,8 @@ func (h *Handler) handleCreateSession(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusCreated, session)
}
// handleListSessions behandelt GET /api/v1/sessions.
// Unterstützt Query-Parameter: limit (Standard: 20) und offset (Standard: 0).
func (h *Handler) handleListSessions(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -87,6 +94,7 @@ func (h *Handler) handleListSessions(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, sessions)
}
// handleGetSession behandelt GET /api/v1/sessions/{id}.
func (h *Handler) handleGetSession(w http.ResponseWriter, r *http.Request) {
id, err := pathID(r, "id")
if err != nil {
@@ -106,6 +114,8 @@ func (h *Handler) handleGetSession(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, session)
}
// handleEndSession behandelt PUT /api/v1/sessions/{id}/end.
// Beendet eine offene Session und speichert optional eine Notiz.
func (h *Handler) handleEndSession(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -167,6 +177,9 @@ func (h *Handler) handleDeleteSession(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}
// handleCreateLog behandelt POST /api/v1/sessions/{id}/logs.
// Fügt einen Satz zu einer offenen Session hinzu.
// Gibt 409 zurück bei doppelter (exercise_id, set_number)-Kombination.
func (h *Handler) handleCreateLog(w http.ResponseWriter, r *http.Request) {
sessionID, err := pathID(r, "id")
if err != nil {
@@ -204,6 +217,8 @@ func (h *Handler) handleCreateLog(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusCreated, log)
}
// handleUpdateLog behandelt PUT /api/v1/sessions/{id}/logs/{logId}.
// Alle Felder im Request sind optional (Partial Update).
func (h *Handler) handleUpdateLog(w http.ResponseWriter, r *http.Request) {
sessionID, err := pathID(r, "id")
if err != nil {
@@ -242,6 +257,7 @@ func (h *Handler) handleUpdateLog(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, log)
}
// handleDeleteLog behandelt DELETE /api/v1/sessions/{id}/logs/{logId}.
func (h *Handler) handleDeleteLog(w http.ResponseWriter, r *http.Request) {
sessionID, err := pathID(r, "id")
if err != nil {

View File

@@ -2,6 +2,8 @@ package handler
import "net/http"
// handleGetLastLog behandelt GET /api/v1/exercises/{id}/last-log.
// Gibt den zuletzt geloggten Satz der Übung zurück, oder 404 wenn noch kein Satz existiert.
func (h *Handler) handleGetLastLog(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -26,6 +28,8 @@ func (h *Handler) handleGetLastLog(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, lastLog)
}
// handleGetExerciseHistory behandelt GET /api/v1/exercises/{id}/history.
// Unterstützt Query-Parameter: limit (Standard: 30).
func (h *Handler) handleGetExerciseHistory(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -47,6 +51,7 @@ func (h *Handler) handleGetExerciseHistory(w http.ResponseWriter, r *http.Reques
writeJSON(w, http.StatusOK, logs)
}
// handleGetStatsOverview behandelt GET /api/v1/stats/overview.
func (h *Handler) handleGetStatsOverview(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {

View File

@@ -7,6 +7,7 @@ import (
"strings"
)
// handleListSets behandelt GET /api/v1/sets.
func (h *Handler) handleListSets(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -22,6 +23,7 @@ func (h *Handler) handleListSets(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, sets)
}
// handleCreateSet behandelt POST /api/v1/sets.
func (h *Handler) handleCreateSet(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -51,6 +53,8 @@ func (h *Handler) handleCreateSet(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusCreated, set)
}
// handleUpdateSet behandelt PUT /api/v1/sets/{id}.
// Ersetzt Name und Übungszuordnungen vollständig.
func (h *Handler) handleUpdateSet(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {
@@ -89,6 +93,8 @@ func (h *Handler) handleUpdateSet(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, set)
}
// handleDeleteSet behandelt DELETE /api/v1/sets/{id}.
// Führt einen Soft-Delete durch (setzt deleted_at).
func (h *Handler) handleDeleteSet(w http.ResponseWriter, r *http.Request) {
uid, err := userID(r)
if err != nil {

View File

@@ -7,6 +7,7 @@ import (
"strings"
)
// handleListUsers behandelt GET /api/v1/users.
func (h *Handler) handleListUsers(w http.ResponseWriter, r *http.Request) {
users, err := h.store.ListUsers()
if err != nil {
@@ -16,6 +17,7 @@ func (h *Handler) handleListUsers(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, users)
}
// handleCreateUser behandelt POST /api/v1/users.
func (h *Handler) handleCreateUser(w http.ResponseWriter, r *http.Request) {
var req model.CreateUserRequest
if err := decodeJSON(r, &req); err != nil {
@@ -35,6 +37,8 @@ func (h *Handler) handleCreateUser(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusCreated, user)
}
// handleDeleteUser behandelt DELETE /api/v1/users/{id}.
// Gibt 409 zurück wenn es sich um den letzten Nutzer handelt.
func (h *Handler) handleDeleteUser(w http.ResponseWriter, r *http.Request) {
id, err := pathID(r, "id")
if err != nil {