package handler import ( "encoding/json" "errors" "krafttrainer/internal/store" "net/http" "strconv" ) // Handler bündelt alle HTTP-Handler und hält eine Referenz auf den Store. type Handler struct { store *store.Store } // New erstellt einen neuen Handler. func New(store *store.Store) *Handler { return &Handler{store: store} } // RegisterRoutes registriert alle API-Routen am ServeMux. func (h *Handler) RegisterRoutes(mux *http.ServeMux) { // Users (kein X-User-ID Header nötig) mux.HandleFunc("GET /api/v1/users", h.handleListUsers) mux.HandleFunc("POST /api/v1/users", h.handleCreateUser) mux.HandleFunc("DELETE /api/v1/users/{id}", h.handleDeleteUser) // Exercises mux.HandleFunc("GET /api/v1/exercises", h.handleListExercises) mux.HandleFunc("POST /api/v1/exercises", h.handleCreateExercise) mux.HandleFunc("PUT /api/v1/exercises/{id}", h.handleUpdateExercise) mux.HandleFunc("DELETE /api/v1/exercises/{id}", h.handleDeleteExercise) // Training Sets mux.HandleFunc("GET /api/v1/sets", h.handleListSets) mux.HandleFunc("POST /api/v1/sets", h.handleCreateSet) mux.HandleFunc("PUT /api/v1/sets/{id}", h.handleUpdateSet) mux.HandleFunc("DELETE /api/v1/sets/{id}", h.handleDeleteSet) // Sessions mux.HandleFunc("GET /api/v1/sessions/active", h.handleGetActiveSession) mux.HandleFunc("POST /api/v1/sessions", h.handleCreateSession) mux.HandleFunc("GET /api/v1/sessions", h.handleListSessions) mux.HandleFunc("GET /api/v1/sessions/{id}", h.handleGetSession) mux.HandleFunc("PUT /api/v1/sessions/{id}/end", h.handleEndSession) mux.HandleFunc("DELETE /api/v1/sessions/{id}", h.handleDeleteSession) // Session Logs mux.HandleFunc("POST /api/v1/sessions/{id}/logs", h.handleCreateLog) mux.HandleFunc("PUT /api/v1/sessions/{id}/logs/{logId}", h.handleUpdateLog) mux.HandleFunc("DELETE /api/v1/sessions/{id}/logs/{logId}", h.handleDeleteLog) // Stats mux.HandleFunc("GET /api/v1/exercises/{id}/last-log", h.handleGetLastLog) mux.HandleFunc("GET /api/v1/exercises/{id}/history", h.handleGetExerciseHistory) mux.HandleFunc("GET /api/v1/stats/overview", h.handleGetStatsOverview) // Export mux.HandleFunc("GET /api/v1/export", h.handleExport) } // --- 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 == "" { return defaultVal } n, err := strconv.Atoi(v) if err != nil { return defaultVal } return n } // userID liest die X-User-ID aus dem Request-Header. func userID(r *http.Request) (int64, error) { v := r.Header.Get("X-User-ID") if v == "" { return 0, errors.New("X-User-ID Header fehlt") } id, err := strconv.ParseInt(v, 10, 64) if err != nil || id <= 0 { return 0, errors.New("ungültige User-ID") } return id, nil }