# 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*
