batchCooking/README.md
kyuno053 42d094764f
API: signup/login (profile creation + JWT auth) (#4)
* Add signup/login (profile creation + JWT auth)

API:
- POST /auth/signup — creates a house + user_profile (transactional),
  hashes the password with argon2, sets a JWT in an httpOnly cookie
- POST /auth/login — verifies credentials (generic 401 for both wrong
  email and wrong password, doesn't leak which), sets the cookie
- POST /auth/logout — clears the cookie
- GET /auth/me — current profile, behind requireAuth middleware
- requireAuth verifies the JWT and re-checks tokenVersion against the
  DB, so a stateless JWT can still be invalidated (password change /
  logout-everywhere, not built yet but the field is in place)

Schema: user_profiles gets password_hash + token_version (not in the
original spec doc — required for auth). New migration, with
COMMENT ON for the new columns per the established pattern.

Decisions from the auth planning discussion: JWT in httpOnly cookie
(not server-side sessions), first profile created also creates its
house, argon2 for hashing.

argon2 pinned to 0.31.2 (not ^, deliberately): 0.45.1 segfaults at
runtime on this Windows machine — reproduced consistently across bash
(sandboxed and unsandboxed) and PowerShell, while 0.31.2 works fine
with the same API. Documented in the README as a trap for future
upgrades, since `tsc`/`prisma generate` succeeding doesn't catch a
runtime native-binding crash.

Tests: Mocha (unit-style, apps/api/test/auth.test.ts) and a Cucumber
feature (apps/api/features/auth.feature) covering the full signup →
authenticated flow, duplicate email, wrong password. Both share
test-support/reset-db.ts (TRUNCATE ... CASCADE) to start each
test/scenario from a clean slate. Test-only argon2 cost parameters
(NODE_ENV=test) keep the suite fast — argon2's real cost is
deliberately expensive, which made hashing dozens of times per run
slow and occasionally timeout-flaky at default cost.

CI: added a Postgres service container to lint-and-test (previously
none — tests didn't touch a real DB), runs `prisma migrate deploy`
before the test steps.

Verified end-to-end manually against the dev server (curl): signup,
duplicate email (409), wrong password (401), valid login (200),
validation errors (400), /me with and without cookie, logout (204) —
all behave as intended. Full suite (lint, mocha, cucumber, build) run
multiple times locally with no flakiness after the timeout/cost fixes.

* Fix CI: generate Prisma Client via postinstall

CI failed with "@prisma/client did not initialize yet" — pnpm install
never ran `prisma generate`, and `prisma migrate deploy` (unlike
`migrate dev`) doesn't do it either. Worked locally only because prior
`prisma migrate dev` runs had already generated the client as a side
effect.

Adding a postinstall script fixes it for CI and for anyone cloning the
repo fresh and running plain `pnpm install`.
2026-08-16 13:45:23 +02:00

146 lines
6.5 KiB
Markdown

# batchCooking
## Structure
Monorepo pnpm workspaces :
- `apps/api` — backend Express/TypeScript (squelette générique : healthcheck, config env, Prisma non modélisé, tests Mocha + Cucumber/BDD)
- `apps/web` — frontend React/Vite/TypeScript (squelette générique, prêt à être embarqué par Capacitor plus tard)
- `packages/shared` — code partagé entre `api` et `web` (types, schémas de validation, constantes) — vide pour l'instant
Aucun module métier n'est encore implémenté : cette base ne contient que l'outillage générique (lint/format, tests, CI, DB locale).
## Prérequis
- Node.js 22 (voir `.nvmrc`)
- pnpm 10 (`corepack enable` puis `corepack use pnpm@10.12.4`, ou installation manuelle)
- Docker (pour Postgres en local)
## Installation
```bash
pnpm install
cp .env.example .env
cp apps/api/.env.example apps/api/.env
```
Puis **édite ces deux `.env`** pour renseigner de vrais `POSTGRES_USER`/`POSTGRES_PASSWORD`
(et la `DATABASE_URL` correspondante dans `apps/api/.env`) : les fichiers `.env.example`
ne contiennent volontairement aucun identifiant réel (juste `changeme`), et
`docker-compose.yml` refuse de démarrer tant que `POSTGRES_USER`/`PASSWORD`/`DB` ne
sont pas définis dans `.env` — pas de valeur par défaut en dur dans les fichiers commités.
Même règle pour `apps/api/.env` : `JWT_SECRET` est **requis, sans défaut** (génère le
tien, voir le commentaire dans `apps/api/.env.example`).
### Cypress : téléchargement du binaire
`pnpm install` installe le package `cypress` mais **pas forcément son binaire** (le
téléchargement du `.exe`/binaire natif peut être ignoré selon l'environnement où
`pnpm install` a été lancé — ex. un environnement sandboxé/CI dont le cache ne
correspond pas à celui de ta machine). Si `pnpm --filter web e2e` échoue avec une
erreur du type :
```
No version of Cypress is installed in: ...\AppData\Local\Cypress\Cache\...
Please reinstall Cypress by running: cypress install
```
lance simplement, depuis ta machine :
```bash
pnpm --filter web exec cypress install
```
(à faire une seule fois par machine ; le binaire est mis en cache localement,
hors du repo).
## Développement
```bash
# Base de données Postgres locale
docker compose up -d
# Applique le schéma (première fois / après un changement de prisma/schema.prisma)
pnpm --filter api exec prisma migrate dev
# Backend (http://localhost:3000)
pnpm dev:api
# Frontend (http://localhost:5173)
pnpm dev:web
```
> **Conflit de port possible sur `5432`** : si tu as déjà un Postgres natif installé
> sur ta machine (service Windows, Homebrew, etc.), il peut occuper le port 5432 et
> intercepter les connexions à la place du conteneur Docker (symptôme : Prisma
> renvoie `P1000: Authentication failed` alors que les identifiants sont corrects).
> Dans ce cas, mets `POSTGRES_PORT=5433` (ou autre) dans ton `.env` **et** adapte le
> port dans la `DATABASE_URL` de `apps/api/.env`.
## Qualité / Tests
```bash
pnpm lint # Biome (lint + format check)
pnpm lint:fix # Biome --write
pnpm test # tests unitaires/intégration (Mocha, apps/api)
pnpm --filter api test:bdd # tests d'intégration BDD (Cucumber/Gherkin, apps/api)
pnpm --filter web e2e # tests e2e (Cypress, démarre le serveur dev automatiquement)
pnpm build # build de tous les workspaces
```
La CI GitHub Actions (`.github/workflows/ci.yml`) exécute lint + tests + build sur chaque push/PR vers `main`, puis les tests e2e Cypress.
### Cucumber (apps/api)
Tests d'intégration lisibles en Gherkin, en complément de Mocha (qui reste pour les
tests unitaires purs) :
- `apps/api/features/*.feature` — scénarios en Given/When/Then (`health.feature` sert
d'exemple)
- `apps/api/features/step-definitions/*.steps.ts` — implémentation des steps
- `apps/api/features/support/world.ts` — contexte partagé entre les steps d'un
scénario (instancie l'app Express in-process via `createApp()`, comme le fait déjà
supertest côté Mocha — pas besoin de lancer un vrai serveur)
- `apps/api/cucumber.cjs` — config (extension `.cjs` volontaire, voir la remarque
TypeScript/ESM ci-dessous)
Pour ajouter un scénario : écrire le `.feature`, lancer `pnpm --filter api test:bdd`,
implémenter les steps manquants (Cucumber affiche des snippets tout prêts pour ceux
qui n'existent pas encore).
> **Piège TypeScript/ESM à connaître** (déjà rencontré avec `cypress.config.ts`) :
> les fichiers de config d'outils tiers qui font du chargement dynamique de TS
> (`cucumber.cjs`, `cypress.config.ts`…) sont sensibles au `"type": "module"` du
> `package.json`. `cucumber.cjs` évite le problème *pour sa propre config* en étant
> explicitement CommonJS ; les steps/world restent en `.ts` ESM classique et sont
> chargés via `tsx` (`NODE_OPTIONS=--import=tsx`, voir le script `test:bdd`).
## Auth (apps/api)
Inscription (création de profil + foyer) et connexion, JWT dans un cookie httpOnly.
- `POST /auth/signup``{ firstName, lastName, email, password }` → crée le foyer
(`house`) et le profil (`user_profiles`) en une transaction, pose le cookie de
session, renvoie le profil (201)
- `POST /auth/login``{ email, password }` → pose le cookie de session, renvoie le
profil (200) ; message d'erreur volontairement générique (401) que ce soit
l'email ou le mot de passe qui soit incorrect
- `POST /auth/logout` — efface le cookie (204)
- `GET /auth/me` — profil courant, nécessite le cookie de session (401 sinon)
Mots de passe hachés avec argon2. Le hash est indépendant du foyer : un profil crée
toujours son propre foyer à l'inscription (rejoindre un foyer existant n'est pas
encore implémenté).
> **argon2 : version pinnée à `0.31.2`, pas de `^`.** La version `0.45.1` (dernière au
> moment de l'écriture) segfault au runtime sur au moins une configuration Windows —
> reproduit de façon stable (bash sandboxé, bash non-sandboxé, PowerShell), alors que
> `0.31.2` fonctionne parfaitement avec la même API. Si tu montes la version, revérifie
> concrètement (`argon2.hash(...)` dans un `node -e`) avant de merger, un `pnpm build`
> qui passe ne suffit pas à détecter un crash runtime.
Les tests (Mocha + Cucumber) tournent avec un coût argon2 réduit
(`NODE_ENV=test`, voir `auth.service.ts`) — le coût par défaut est volontairement
élevé (sécurité), ce qui rendrait la suite de tests lente/instable sinon. La CI
provisionne un vrai Postgres de service (`.github/workflows/ci.yml`) et exécute
`prisma migrate deploy` avant les tests.