Fills in the design tokens proposed for the app (palette, type scale, shadows/radii, and a real dark theme) and applies them to the existing auth and home pages: - _theme.scss: full token set — basil/vermillion/turmeric/raspberry palette, heading type scale (was missing above --font-size-base), elevation/radius scale, and a working dark theme via prefers-color-scheme (color-scheme: light dark was declared but unused). Includes a 3-tier allergen/intolerance color scale, kept distinct from --color-error, for when the recipe/allergy feature lands — not yet consumed by any component. - global.scss: box-sizing reset, headings on the display font stack, visible focus ring. - auth-form.scss / HomePage.scss(+tsx): both screens now render their content on a raised card (--color-surface, radius, shadow) instead of directly on the page background, so login/signup and home read as one coherent app. Also adds .claude/launch.json (pnpm --filter web dev, port 5173) used to preview the change locally. Verified: `vite build` passes; computed styles checked live against the token values in both themes. |
||
|---|---|---|
| .claude | ||
| .github/workflows | ||
| apps | ||
| packages | ||
| specs | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| .nvmrc | ||
| biome.json | ||
| docker-compose.yml | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| tsconfig.base.json | ||
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, prêt à être embarqué par Capacitor plus tard. Page de connexion/inscription en place ; le reste est encore un squelette générique.packages/shared— code partagé entreapietweb: schémas zod (signupSchema,loginSchema), types (SafeUserProfile), et le contrat d'erreurs (ErrorCodenumérique,ApiErrorResponse, voir specs/error-handling.md) — même règles des deux côtés, pas de risque de dérive entre front et back.packages/error-tools— gestion des erreurs, indépendante de tout framework HTTP (n'importe pasexpress) :HttpError,ErrorHandlerService. Séparé d'express-toolsprécisément parce que rien ici ne dépend d'Express. Détail : specs/error-handling.md.packages/express-tools— outillage Express générique et réutilisable :ExpressServer(init serveur, routes, middlewares),wrapAsyncHandler,createErrorMiddleware(adapteErrorHandlerServicedeerror-toolsà Express) — séparé d'apps/api, pas de logique métier. Détail : specs/backend-architecture.md.
packages/shared, packages/error-tools et packages/express-tools ont un vrai
build (tsc → dist/, voir leur package.json) : consommés en JS compilé, pas en
TS brut — nécessaire pour un runtime Node pur (Docker, pas de transpilation à la
volée), voir la note dans
specs/frontend-architecture.md.
Prérequis
- Node.js 22 (voir
.nvmrc) - pnpm 10 (
corepack enablepuiscorepack use pnpm@10.12.4, ou installation manuelle) - Docker (pour Postgres en local)
Installation
pnpm install
cp .env.example .env
cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.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 :
pnpm --filter web exec cypress install
(à faire une seule fois par machine ; le binaire est mis en cache localement, hors du repo).
Développement
# 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 renvoieP1000: Authentication failedalors que les identifiants sont corrects). Dans ce cas, metsPOSTGRES_PORT=5433(ou autre) dans ton.envet adapte le port dans laDATABASE_URLdeapps/api/.env.
Qualité / Tests
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.featuresert d'exemple)apps/api/features/step-definitions/*.steps.ts— implémentation des stepsapps/api/features/support/world.ts— contexte partagé entre les steps d'un scénario (instancie l'app Express in-process viacreateApp(), comme le fait déjà supertest côté Mocha — pas besoin de lancer un vrai serveur)apps/api/cucumber.cjs— config (extension.cjsvolontaire, 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"dupackage.json.cucumber.cjsévite le problème pour sa propre config en étant explicitement CommonJS ; les steps/world restent en.tsESM classique et sont chargés viatsx(NODE_OPTIONS=--import=tsx, voir le scripttest: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 incorrectPOST /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 version0.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 que0.31.2fonctionne parfaitement avec la même API. Si tu montes la version, revérifie concrètement (argon2.hash(...)dans unnode -e) avant de merger, unpnpm buildqui 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.
Les tests automatisés et
pnpm dev:apipartagent la même base Postgres locale. Lancerpnpm test/test:bddvideuser_profiles/house(TRUNCATE ... CASCADE, voirtest-support/reset-db.ts) — si tu es en train de tester manuellement à la main (via le navigateur ou curl) contre le serveur de dev, un run de tests en parallèle efface tes données de test sans prévenir. Pas un bug, juste à savoir.
Page de connexion / inscription (apps/web)
src/api/client.ts—ApiClient(classe, instance unique exportéeapiClient) : enveloppefetchvers l'API (credentials: "include", requis pour que le cookie de session httpOnly parte/revienne — l'API et le front sont sur des origines différentes). URL configurable viaVITE_API_URL(voir.env.example).src/features/auth/AuthContext.tsx— état d'auth global ; appelleGET /auth/meau chargement pour restaurer la session depuis le cookie.src/features/auth/RequireAuth.tsx/RedirectIfAuthenticated.tsx— gardes de route (react-router-dom) :/exige d'être connecté,/loginet/signupredirigent vers/si on l'est déjà.src/pages/{Login,Signup,Home}Page.tsx— validation client instantanée via les schémas zod partagés (packages/shared), erreurs API traduites viaErrorMessageService(voir ci-dessous).
Détail de l'organisation complète (dossiers, routing, SCSS/theming) : specs/frontend-architecture.md.
Tests Cypress (apps/web/cypress/e2e/) : smoke.cy.ts + auth.cy.ts mockent l'API via
cy.intercept plutôt que de dépendre d'un vrai backend — le job e2e de la CI ne
provisionne pas de Postgres/API, seulement le serveur de dev Vite. Le comportement
réel de l'API est couvert par les suites Mocha/Cucumber d'apps/api (contre une vraie
base).
Gestion des erreurs (API ↔ web)
Contrat d'erreurs partagé via packages/shared (ErrorCode, énumération
numérique groupée par famille — 4000 validation, 401x auth, 404x not
found, 500x interne — et ApiErrorResponse) : l'API renvoie toujours
{ code, message, details? } (message en anglais, dev-facing — jamais affiché tel
quel), et le client traduit code en libellé français via i18next
(ErrorMessageService, apps/web/src/services/error-message.service.ts →
apps/web/src/locales/fr/translation.json). Côté API, ErrorHandlerService
(packages/error-tools) et createErrorMiddleware (packages/express-tools)
centralisent la transformation de toute erreur levée en réponse HTTP conforme —
aucune valeur ErrorCode codée en dur nulle part (toujours ErrorCode.XXX, y
compris dans les mocks Cypress).
Détail complet (schéma, exemples, comment ajouter un nouveau code d'erreur) : specs/error-handling.md.
Le profil authentifié (requireAuth) passe par res.locals.userProfile
(typé via AuthLocals), pas par une augmentation du namespace global Express —
voir specs/backend-architecture.md pour le détail
et le pourquoi.
packages/shared fournit aussi assertIsNever (vérification d'exhaustivité de
switch/if-chain sur une union, erreur de compilation si un cas est oublié) —
voir specs/backend-architecture.md.
i18n
i18next + react-i18next — tout le texte affiché (formulaires, boutons,
erreurs) vient de fichiers de locale JSON (apps/web/src/locales/<lng>/translation.json),
jamais codé en dur dans un composant. Une seule langue existe aujourd'hui (fr) ;
en ajouter une est une question de fichier de locale, pas de code. Détail :
specs/frontend-architecture.md.
Données de test (faker.js)
apps/api utilise @faker-js/faker pour toutes les données
de test dans test/auth.test.ts (Mocha) et le "bruit" (prénom/nom de remplissage)
des steps Cucumber — jamais de nom/email qui ressemble à une vraie personne en dur
dans un fixture. Les valeurs littérales des scénarios .feature eux-mêmes
(ex. alice@example.com) restent volontairement statiques : c'est le point des
scénarios Gherkin lisibles (exemples illustratifs conventionnels en BDD, pas des
données réelles) — seules les données de remplissage hors du texte lisible du
scénario sont générées.