3.9 KiB
Executable File
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Krafttrainer
Einzelnutzer-Webapplikation zur Verwaltung und Protokollierung von Kraftübungen.
Tech Stack
- Backend: Go 1.22+ mit
net/httpstdlib Router, SQLite (go-sqlite3),golang-migrate - Frontend: React 19, Vite 8, TypeScript (strict), Tailwind CSS 4 (
@tailwindcss/vite), Zustand, Recharts - Paketmanager: pnpm (Version in
packageManagerfield fixiert) - Produktion: Single Binary via
embed(Frontend inbackend/static/eingebettet)
Build & Run
make dev-backend # Go-Server auf :8090
make dev-frontend # Vite Dev-Server auf :5173 (Proxy → :8090)
make build # Single Binary ./krafttrainer (Frontend-Build + Go-Build mit CGO_ENABLED=1)
make clean # Binary + Dist-Ordner entfernen
Frontend TypeScript-Typecheck (ohne Build):
cd frontend && pnpm exec tsc --noEmit
Es gibt keine automatisierten Tests (weder Go-Tests noch Frontend-Tests).
Deployment
- Läuft auf
192.168.1.118:8090als systemd-Service (krafttrainer.service) - User:
christoph, Binary:/home/christoph/krafttrainer/krafttrainer - DB:
/home/christoph/krafttrainer/krafttrainer.db - Deploy:
scp krafttrainer christoph@192.168.1.118:~/krafttrainer/, dannsudo systemctl restart krafttrainer
Architektur
Backend-Flow
Handler → Store → DB. Jeder Handler folgt exakt diesem Muster:
decodeJSON()mitDisallowUnknownFields()model.Validate()aufrufen- Store-Methode aufrufen
- Fehler differenzieren und
writeJSON()/writeError()aufrufen
Store-Methoden geben nach Mutationen immer frisch aus der DB gelesene Objekte zurück (kein Rekonstruieren aus Input).
Fehlerbehandlung (Backend)
- Store-Fehler → 500 (generisch, kein DB-Leak)
- Validierungsfehler → 400
- Nicht gefunden (
sql.ErrNoRows) → 404 - UNIQUE-Verletzung → 409
Sentinel-Strings im Error-Message für Handler-Differenzierung: "UNIQUE_VIOLATION:", "SESSION_CLOSED". Diese werden mit strings.Contains() geprüft — kein custom error type.
Routing
Go 1.22+ Pattern-Matching im stdlib ServeMux mit {id}-Platzhaltern. Middleware-Chain: Recoverer → RequestLogger → CORS. SPA-Fallback in main.go: prüft zuerst ob statische Datei existiert, serviert sonst index.html.
Frontend-Stores (Zustand)
Stores sind flach und unabhängig — keine direkte Store-zu-Store-Kommunikation. Feedback läuft ausschließlich über useToastStore.getState().addToast(). activeSessionStore verwaltet einen Timer-Interval manuell (kein React-Effect-Cleanup — beim stopTimer() explizit clearen).
Alle HTTP-Aufrufe gehen über src/api/client.ts. ApiError (extends Error) hat status-Property für HTTP-Statuscodes.
Datenbank
- SQLite mit WAL-Mode und Foreign Keys (via Connection-String-Parameter in
store.go) - Migrations auto-run beim Start via embedded FS (
backend/migrations/embed.go) exercise_nameinsession_logsdenormalisiert gespeichert (damit gelöschte Übungen historische Daten nicht verwaisen lassen)- UNIQUE-Constraint auf
(session_id, exercise_id, set_number) - Soft-Delete bei Übungen via
deleted_atTimestamp
Konventionen
- API Prefix: Alle Endpoints unter
/api/v1. Fehler als{ "error": "..." }. - Gewichte: Immer in kg. Feldnamen mit
_kgSuffix (weight_kg,weight_step_kg). - UI-Sprache: Deutsch.
- Dark Mode: Default.
bg-gray-950Body,bg-gray-900Cards,blue-500Primary. - Touch-Targets: min 44×44px.
Vollständige Spezifikation
Siehe PRD.md im übergeordneten Verzeichnis (03_Projekte/fitness-pad/PRD.md) für:
- Vollständiges SQLite-Schema (Abschnitt 4.1)
- Alle Go-Structs und TypeScript-Typen (Abschnitt 4.2–4.3)
- Komplette API-Spezifikation mit Request/Response-Beispielen (Abschnitt 5)
- Validierungsregeln (Abschnitt 6)
- Frontend-Spezifikation und Komponentenverhalten (Abschnitt 7)