# Fitness Web Multi-user fitness tracking web app with AI coaching. Track workouts, log daily check-ins, explore exercise history, and chat with an AI coach powered by opencode. ## Features - **Workouts** — Plan and log workouts with set-level detail (reps, weight, RPE) - **Exercises** — Catalog with body-part filtering and history - **Check-ins** — Daily weight, calories, steps, sleep tracking - **AI Coach** — Persistent chat sidebar (desktop) / full-page chat (mobile) powered by opencode (Big Pickle model, free). Runs as a separate process. - **Multi-user** — Login-based, each user has independent data - **Calendar view** — See your training history at a glance - **NixOS-ready** — Docker-based deploy with provided NixOS module ## Quick Start ```bash # Install dependencies uv sync # Initialize and seed the database uv run python scripts/schema.py uv run python scripts/seed.py # Start the AI coach (in a separate terminal) opencode serve --port 4096 # Start the dev server uv run uvicorn app.main:app --reload ``` Open http://localhost:8000, register a user, and you're ready. > **Note:** Chat requires `opencode serve` running on port 4096. If `opencode` is not in PATH, use `~/.opencode/bin/opencode serve --port 4096`. ## Docker ```bash docker compose up -d ``` ## NixOS Deployment A single container runs both the web app and the AI coach (opencode-serve) together, sharing the SQLite database on the same filesystem. ### 1. Build image ```bash docker build -t fitness-web:latest . ``` ### 2. NixOS module Add `machines/cj/fitness-web.nix` to your nix_config: ```nix { serverIP, serverIP6 }: { fitness-web = { image = "fitness-web:latest"; ports = [ "8688:8000" ]; environment = { TZ = "America/New_York"; SESSION_SECRET = "change-me-in-production"; }; volumes = [ "/serverdata/fitness-web/data:/app/data" "/serverdata/fitness-web/opencode:/root/.config/opencode" ]; }; } ``` Wire it into `configuration.nix`: ```nix virtualisation.oci-containers.containers = let ips = { ... }; in { # ... existing containers ... fitness-web = (import ./fitness-web.nix ips).fitness-web; }; ``` ### 3. Nginx reverse proxy In `nginx.nix`, add a vhost: ```nix "fitness.jhink.org" = simpleProxy 8688; ``` ### 4. Firewall Add to `allowedTCPPorts` in `firewall.nix`: ```nix 8688 # fitness-web ``` ### 5. Deploy ```bash nixos-rebuild switch --flake .#cj ``` Then access at https://fitness.jhink.org (or your chosen domain). ## Architecture ``` app/ ├── main.py — FastAPI app factory with lifespan ├── config.py — Settings from environment ├── auth.py — Auth helpers (hash, verify, session) ├── models/ — SQLAlchemy ORM models ├── routers/ — Route handlers per feature ├── services/ — External service integrations (opencode) ├── templates/ — Jinja2 templates (Pico.css) └── static/ — CSS overrides scripts/ ├── schema.py — DB table creation └── seed.py — Seed exercises and phases data/ — SQLite database (gitignored) ``` ## Related - [fitness-agent](https://github.com/yourname/fitness-agent) — Original training repo with markdown logs and Juggernaut history. This web app replaces it.