# External Integrations **Analysis Date:** 2026-02-15 ## APIs & External Services **None** - No external third-party APIs currently integrated. ## Data Storage **Databases:** - **PostgreSQL** (Primary) - Connection via `pg` client library (8.11.3) - Environment variables: - `DB_HOST` - Default: `postgres` (Docker service name) - `DB_PORT` - Default: `5432` - `DB_USER` - Default: `postgres` - `DB_PASSWORD` - Required secret - `DB_NAME` - Default: `gravl` - ORM/Client: `pg` (node-postgres) - Direct SQL queries, no ORM - Initialization: `db/init.sql` - Schema and seed data - Tables: users, programs, program_days, exercises, program_exercises, workout_logs, user_measurements, user_strength **File Storage:** - Local filesystem only - No external file storage service - Static assets served by Nginx from built frontend `dist/` directory **Caching:** - None detected - No Redis, Memcached, or other caching layer ## Authentication & Identity **Auth Provider:** - Custom JWT-based authentication - Implementation: `backend/src/index.js` (lines 22-29, 35-68) - Token generation: `jsonwebtoken` 9.0.2 - Password hashing: `bcryptjs` 2.4.3 - Secret: `JWT_SECRET` environment variable (default: `gravl-secret-key-change-in-production`) - Token expiration: 30 days **Auth Flow:** 1. Frontend `AuthContext` (`frontend/src/context/AuthContext.jsx`) manages user state 2. User registers or logs in via `/api/auth/register` or `/api/auth/login` 3. Backend verifies credentials, generates JWT token 4. Frontend stores token in `localStorage` as `token` 5. Subsequent requests include `Authorization: Bearer {token}` header 6. Backend `authMiddleware` validates token on protected routes 7. User profile fetched on app load via `/api/user/profile` **Protected Routes:** - `/api/user/profile` - GET/PUT (requires auth) - `/api/user/measurements` - GET/POST (requires auth) - `/api/user/strength` - GET/POST (requires auth) - `/api/logs` - GET/POST (no auth check in code - potential security gap) - `/api/progression/:programExerciseId` - GET (no auth check - potential security gap) **Public Routes:** - `/api/health` - Health check endpoint - `/api/auth/register` - User registration - `/api/auth/login` - User login - `/api/programs` - List all programs - `/api/programs/:id` - Get program details - `/api/days/:dayId/exercises` - Get exercises for a day - `/api/today/:programId` - Get workout for day ## Frontend-Backend Communication **API Base URL:** - Hardcoded as `/api` in `frontend/src/context/AuthContext.jsx` (line 2) - Development: Proxied by Vite to `http://localhost:3001` - Production: Proxied by Nginx to `http://gravl-backend:3001` **CORS:** - Enabled on backend via `cors` middleware 2.8.5 - `app.use(cors())` in `backend/src/index.js` (line 19) **HTTP Methods:** - POST `/api/auth/register` - Register user - POST `/api/auth/login` - Login user - GET `/api/user/profile` - Get user profile - PUT `/api/user/profile` - Update user profile - POST `/api/user/measurements` - Add measurements - GET `/api/user/measurements` - Get measurement history - POST `/api/user/strength` - Add strength record - GET `/api/user/strength` - Get strength history - GET `/api/programs` - List programs - GET `/api/programs/:id` - Get program with days and exercises - GET `/api/days/:dayId/exercises` - Get exercises for day - GET/POST `/api/logs` - Get/create workout logs - GET `/api/logs/last/:programExerciseId` - Get last workout for exercise - GET `/api/progression/:programExerciseId` - Calculate suggested weight **Request/Response Format:** - Content-Type: `application/json` - Token: Passed in `Authorization: Bearer {token}` header - Response: JSON objects ## Monitoring & Observability **Error Tracking:** - None detected - No Sentry, LogRocket, or similar **Logs:** - Backend: `console.error()` for errors (lines 48, 65, 98, 115, 133, 147, 165, 179, 190, 228, 246, 276, 294, 327, 380, 417) - Frontend: No error tracking integrated - Example: `console.error('Register error:', err)` in `backend/src/index.js` (line 48) **Database Logging:** - None detected - SQL queries not logged ## Webhooks & Callbacks **Incoming:** - None detected **Outgoing:** - None detected ## Environment Configuration **Required env vars for backend:** - `DB_HOST` - PostgreSQL hostname - `DB_PORT` - PostgreSQL port - `DB_USER` - Database user - `DB_PASSWORD` - Database password (REQUIRED SECRET) - `DB_NAME` - Database name - `JWT_SECRET` - JWT signing secret (REQUIRED SECRET for production) - `PORT` - Backend port (optional, default 3001) **Secrets location:** - Docker Compose: `docker-compose.yml` (lines 8-13) - **WARNING: Password visible in file** - Backend defaults: `backend/src/index.js` (lines 9, 14-16) - Frontend: None (token stored in browser localStorage) **Traefik Integration:** - Frontend exposed via Traefik reverse proxy - Host: `gravl.homelab.local` - HTTP and HTTPS support configured - Networks: `proxy` (external), `homelab` (internal Docker Compose network) ## Service Dependencies **Backend Dependencies:** - PostgreSQL (required) - Traefik proxy (for production routing) **Frontend Dependencies:** - Backend API at `/api` (required for all authenticated operations) - Can operate in degraded mode if API is unavailable --- *Integration audit: 2026-02-15*