# Safe Life API

Production-oriented NestJS 11 API for the existing Safe Life React + Vite frontend. It uses Prisma 7 with MySQL 8.4 and does not use Supabase.

## Local setup

Requirements: Node.js 20.19+ and Docker Desktop (or a compatible MySQL 8 server).

1. Copy `.env.example` to `.env` and replace all placeholder secrets.
2. Start MySQL: `docker compose up -d mysql`.
3. Install dependencies: `npm ci`.
4. Apply the schema: `npm run db:deploy`.
5. Import the approved frontend catalog: `npm run db:seed`.
6. Start the API: `npm run start:dev`.

The API listens at `http://localhost:3001/api/v1`. Swagger is available at `http://localhost:3001/api/docs` when `SWAGGER_ENABLED=true`.

The seed is idempotent for categories and products and preserves all frontend legacy product IDs. Set `ADMIN_EMAIL` and `ADMIN_PASSWORD` before seeding to create or rotate the initial admin account. Never use the example credentials in production.

Product images uploaded from the admin dashboard are stored below
`UPLOAD_ROOT/products` (default: `uploads/products`) and served from
`/uploads/products/...`. Back up this directory and mount it as persistent
storage in production; it is intentionally excluded from source control.

## Production commands

- `npm run prisma:validate` — validate the Prisma schema.
- `npm run db:deploy` — apply committed migrations without development prompts.
- `npm run build` — compile NestJS.
- `npm run start:prod` — run the compiled application.
- `npm run typecheck`, `npm run lint`, `npm test`, `npm run test:e2e` — verification gates.

## Security model

- Short-lived signed access tokens authorize protected API routes.
- Opaque refresh tokens are SHA-256 hashed in MySQL, rotated on every refresh, and sent only in an HTTP-only cookie.
- Passwords use Argon2id.
- Admin permissions are enforced server-side with role guards.
- CORS accepts only exact configured frontend origins.
- Helmet, body limits, request IDs, structured errors, DTO validation, global throttling, and tighter auth/contact/checkout limits are enabled.
- Checkout ignores all browser totals, re-reads product prices, checks stock, decrements stock in a serializable transaction, and stores immutable price snapshots.

## Deployment notes

Set `COOKIE_SECURE=true`, a strong `JWT_ACCESS_SECRET`, the production `DATABASE_URL`, and exact HTTPS `FRONTEND_ORIGINS`. Run migrations before the new application release. Configure the reverse proxy to forward client IPs and preserve `X-Request-Id`. Liveness is `/api/v1/health`; database readiness is `/api/v1/health/readiness`.

Route `/uploads/` through the same API origin (or a trusted reverse-proxy
mapping) and persist `UPLOAD_ROOT` across deployments. Uploaded filenames are
immutable UUIDs, so long-lived cache headers are safe.
