package handler import ( "database/sql" "krafttrainer/internal/model" "net/http" "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 { writeError(w, http.StatusBadRequest, err.Error()) return } session, err := h.store.GetActiveSession(uid) if err != nil { writeError(w, http.StatusInternalServerError, "Fehler beim Suchen der aktiven Session") return } if session == nil { writeJSON(w, http.StatusOK, nil) return } // Exercises des Sets mitliefern exercises, err := h.store.GetSetExercises(session.SetID) if err != nil { writeError(w, http.StatusInternalServerError, "Fehler beim Laden der Übungen") return } writeJSON(w, http.StatusOK, map[string]any{ "session": session, "exercises": exercises, }) } // 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 { writeError(w, http.StatusBadRequest, err.Error()) return } var req model.CreateSessionRequest if err := decodeJSON(r, &req); err != nil { writeError(w, http.StatusBadRequest, "Ungültiger Request-Body") return } if req.SetID == 0 { writeError(w, http.StatusBadRequest, "set_id ist erforderlich") return } session, err := h.store.CreateSession(uid, req.SetID) if err != nil { if strings.Contains(err.Error(), "SESSION_OPEN") { writeError(w, http.StatusConflict, "Es läuft bereits ein Training. Bitte zuerst beenden.") return } if strings.Contains(err.Error(), "existiert nicht") { writeError(w, http.StatusBadRequest, err.Error()) return } writeError(w, http.StatusInternalServerError, "Fehler beim Starten der Session") return } 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 { writeError(w, http.StatusBadRequest, err.Error()) return } limit := queryInt(r, "limit", 20) offset := queryInt(r, "offset", 0) sessions, err := h.store.ListSessions(uid, limit, offset) if err != nil { writeError(w, http.StatusInternalServerError, "Fehler beim Laden der Sessions") return } 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 { writeError(w, http.StatusBadRequest, "Ungültige ID") return } session, err := h.store.GetSession(id) if err != nil { writeError(w, http.StatusInternalServerError, "Fehler beim Laden der Session") return } if session == nil { writeError(w, http.StatusNotFound, "Session nicht gefunden") return } 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 { writeError(w, http.StatusBadRequest, err.Error()) return } id, err := pathID(r, "id") if err != nil { writeError(w, http.StatusBadRequest, "Ungültige ID") return } var body struct { Note string `json:"note"` } decodeJSON(r, &body) session, err := h.store.EndSession(id, uid, body.Note) if err != nil { writeError(w, http.StatusInternalServerError, "Fehler beim Beenden der Session") return } if session == nil { writeError(w, http.StatusNotFound, "Session nicht gefunden oder bereits beendet") return } writeJSON(w, http.StatusOK, session) } // handleDeleteSession handles DELETE /api/v1/sessions/{id}. // Löscht eine abgeschlossene Session samt aller Logs. Offene Sessions werden // mit 409 abgelehnt. Sessions anderer Nutzer oder nicht vorhandene Sessions // antworten mit 404. func (h *Handler) handleDeleteSession(w http.ResponseWriter, r *http.Request) { uid, err := userID(r) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } id, err := pathID(r, "id") if err != nil { writeError(w, http.StatusBadRequest, "Ungültige ID") return } err = h.store.DeleteSession(id, uid) if err != nil { if strings.Contains(err.Error(), "SESSION_NOT_FOUND") { writeError(w, http.StatusNotFound, "Session nicht gefunden") return } if strings.Contains(err.Error(), "SESSION_OPEN") { writeError(w, http.StatusConflict, "Nur abgeschlossene Sessions können gelöscht werden") return } writeError(w, http.StatusInternalServerError, "Fehler beim Löschen der Session") return } 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 { writeError(w, http.StatusBadRequest, "Ungültige Session-ID") return } var req model.CreateLogRequest if err := decodeJSON(r, &req); err != nil { writeError(w, http.StatusBadRequest, "Ungültiger Request-Body") return } if err := req.Validate(); err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } log, err := h.store.CreateLog(sessionID, &req) if err != nil { if strings.Contains(err.Error(), "SESSION_CLOSED") { writeError(w, http.StatusBadRequest, "Session ist bereits beendet") return } if strings.Contains(err.Error(), "UNIQUE_VIOLATION") { writeError(w, http.StatusConflict, err.Error()) return } if strings.Contains(err.Error(), "existiert nicht") { writeError(w, http.StatusBadRequest, err.Error()) return } writeError(w, http.StatusInternalServerError, "Fehler beim Loggen des Satzes") return } 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 { writeError(w, http.StatusBadRequest, "Ungültige Session-ID") return } logID, err := pathID(r, "logId") if err != nil { writeError(w, http.StatusBadRequest, "Ungültige Log-ID") return } var req model.UpdateLogRequest if err := decodeJSON(r, &req); err != nil { writeError(w, http.StatusBadRequest, "Ungültiger Request-Body") return } if err := req.Validate(); err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } log, err := h.store.UpdateLog(sessionID, logID, &req) if err != nil { if strings.Contains(err.Error(), "SESSION_CLOSED") { writeError(w, http.StatusBadRequest, "Session ist bereits beendet") return } writeError(w, http.StatusInternalServerError, "Fehler beim Aktualisieren des Satzes") return } if log == nil { writeError(w, http.StatusNotFound, "Log nicht gefunden") return } 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 { writeError(w, http.StatusBadRequest, "Ungültige Session-ID") return } logID, err := pathID(r, "logId") if err != nil { writeError(w, http.StatusBadRequest, "Ungültige Log-ID") return } err = h.store.DeleteLog(sessionID, logID) if err != nil { if strings.Contains(err.Error(), "SESSION_CLOSED") { writeError(w, http.StatusBadRequest, "Session ist bereits beendet") return } if err == sql.ErrNoRows { writeError(w, http.StatusNotFound, "Log nicht gefunden") return } writeError(w, http.StatusInternalServerError, "Fehler beim Löschen des Satzes") return } w.WriteHeader(http.StatusNoContent) }