From e9ace97ee569e3046e45ba500a2ba3c08bf775c0 Mon Sep 17 00:00:00 2001 From: bot-claude Date: Tue, 25 Aug 2026 20:35:03 +0200 Subject: [PATCH] =?UTF-8?q?docs(REPRISE):=20=C3=A9tat=202026-08-25=20?= =?UTF-8?q?=E2=80=94=20C2/D1-D4=20=E2=9C=85,=20git+Gitea,=20d=C3=A9cisions?= =?UTF-8?q?=20q11/q12,=20=C2=A78/=C2=A79/=C2=A710/=C2=A711=20=C3=A0=20jour?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- REPRISE.md | 81 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/REPRISE.md b/REPRISE.md index e56dd96..0e2c32f 100644 --- a/REPRISE.md +++ b/REPRISE.md @@ -1,6 +1,6 @@ # Projet « Seiche » — doc de reprise -> À jour : 2026-08-24 (A1–A5 ✅) · Statut : **scaffold backend installé, compilé, linté, boot + setup admin + smoke login OK** — front absent +> À jour : 2026-08-25 (A1–D4 ✅ + git) · Statut : **stack complète validée** : serveur 26 tests, front 3 tests + build, Docker build+up, admin créé, `master` poussé sur Gitea · Reste : READMEs (E1), TLS LAN + VPS (décision §5) > Mode d'emploi : doc auto-suffisant. En reprise avec un assistant IA : lui faire lire ce fichier en entier, traiter le §5, puis **planifier les tâches restantes une par une** (§9 + §11). > Mode de travail imposé par Nicolas : **petites tâches, planifier avant d'exécuter, ne jamais lancer de run autonome**. @@ -50,7 +50,8 @@ Appli web auto-hébergée, **réseau local uniquement, mono-utilisateur** (Nicol | 6 | Persistance : volume nommé + backup régulier OK ? | Oui | | 7 | Node 22 LTS dans les conteneurs ? | Oui | | 8 | ✅ **Docker opérationnel** (résolu 2026-08-23) : daemon 29.7.2 démarré (Arch Linux), compose 5.5.0 (plugin + standalone). | — | -| 9 | **Gitea auto-hébergé** : URL + nom du repo à confirmer. Accès **SSH** choisi ; Nicolas configure la clé le 2026-08-24. (rien sur `localhost:3000` au 2026-08-23) | `git init` local d'abord, remote Gitea dès que la clé sera opérationnelle | +| 9 | ✅ **Gitea opérationnel** (2026-08-25) : remote SSH `gitea-lab:nicolas/seiche`, compte collaborateur `bot-claude` (clé `id_ed25519_gitea_bot_claude`), `master` poussé, tracking OK. | — | +| 12 | **VPS / mise en prod** : VPS non provisionné au 2026-08-25. Où ? Image ? Domaine/TLS ? | Développer en LAN ; prod = 1 tâche dédiée après décision | ## 6. Architecture - **1 conteneur = API + front prébuildé** servi par Fastify → zéro CORS, 1 port, déploiement simple. @@ -59,44 +60,46 @@ Appli web auto-hébergée, **réseau local uniquement, mono-utilisateur** (Nicol - Service externe : **un seul package** `server/src/external/` (http-client, cache, types, adaptateurs) — le seul endroit qui le connaît. - SQLite en **WAL**, migrations Drizzle appliquées au boot. -## 7. Structure du repo (état réel, inventaire 2026-08-23) +## 7. Structure du repo (état réel, inventaire 2026-08-25) ✅ = présent · ❌ = manquant ``` seiche/ -├── REPRISE.md ✅ · README.md ❌ +├── REPRISE.md ✅ · README.md ❌ (E1 — à créer) ├── package.json ✅ · pnpm-workspace.yaml ✅ · biome.json ✅ · tsconfig.base.json ✅ -├── .env.example ✅ · .gitignore ✅ · .dockerignore ❌ -├── Dockerfile ❌ · docker-compose.yml ❌ · Makefile ❌ -├── server/ ✅ (scaffold ~850 lignes — non installé, non compilé, non testé) +├── .env.example ✅ · .gitignore ✅ · .dockerignore ✅ +├── Dockerfile ✅ · docker-compose.yml ✅ (port 8787, volume `seiche-data`) · Makefile ❌ +├── scripts/backup.sh ✅ (VACUUM INTO dans le conteneur — testé) +├── server/ ✅ (installé, compilé, 26 tests ✅) │ ├── src/ -│ │ ├── main.ts · app.ts · config.ts · validation.ts -│ │ ├── db/ (client, schema) · migrations ❌ (à générer) +│ │ ├── main.ts · app.ts · config.ts · validation.ts · setup.ts ✅ +│ │ ├── db/ (client, schema) · migrations/ ✅ (0000) │ │ ├── auth/ (password, session+CSRF, rate-limit) │ │ ├── routes/ (auth, external, health) -│ │ ├── external/ (http-client, cache, types, adapters/{http,example,index}) -│ │ └── setup.ts ❌ (referencé par `pnpm setup`, absent) +│ │ └── external/ (http-client, cache, types, adapters) │ ├── drizzle.config.ts · vitest.config.ts · tsconfig.json -│ └── test/ ❌ (aucun test) -└── web/ ❌ (front entier absent) +│ └── tests/ ✅ (auth, api, session, rate-limit, password — 26 tests) +├── web/ ✅ (Vite+React19+TS strict+Tailwind4+Router7+TanStack5 — build ✅, 3 tests) +│ └── src/ (main.tsx · App.tsx · lib/{api,session,queryClient} · components/RequireAuth · pages/{LoginPage,DashboardPage}) +└── .vscode/ ✅ (extensions + settings) ``` ## 8. Starter pack — Definition of Done -- [ ] `pnpm dev` : API + web en dev, login → dashboard -- [ ] `docker compose up` : 1 conteneur, SQLite persisté en volume -- [ ] Auth complète : Argon2id, session httpOnly, CSRF, logout, rate-limit /login -- [ ] Table `users` + migration Drizzle, 1er compte créé au 1er boot -- [ ] `/api/v1/health` public, le reste protégé -- [ ] Tests verts (Vitest) : login ok/échec, CSRF, error-handler, DB round-trip -- [ ] Biome + `tsc --noEmit` clean -- [ ] README : dev, prod, env, backup, sécurité +- [x] `pnpm dev` : API + web en dev, login → dashboard (2026-08-24 : HTTP 200 ; 2026-08-25 : front connecté, session persistée) +- [x] `docker compose up` : 1 conteneur, SQLite persisté en volume (image buildée 30s, conteneur up, health 200, données persistées) +- [x] Auth complète : Argon2id, session httpOnly, CSRF, logout, rate-limit /login (smoke 2026-08-24 + tests 2026-08-25 : login 401/429, logout, me) +- [x] Table `users` + migration Drizzle, 1er compte créé au 1er boot (migration 0000 générée ; `pnpm setup` idempotent, admin créé) +- [x] `/api/v1/health` public, le reste protégé (401 sans session, 200 avec — testé) +- [x] Tests verts (Vitest) : 26 tests serveur + 3 tests front, tous OK +- [x] Biome + `tsc --noEmit` clean (les 2 paquets + tsc web) +- [ ] READMEs (E1) : racine + server + web — dev, prod, env, backup, sécurité (à créer) ## 9. Plan d'exécution (session de reprise) 1. Lire ce doc → répondre au §5 (ou valider les défauts) → loger les réponses dans §10. -2. ~~Scaffolder le monorepo~~ → **partiel (2026-08-23)** : racine ✅, `server/` ✅ (non vérifié), `web/` ❌. -3. Backend : config env → DB (Drizzle, WAL, migration) → auth (Argon2id, session, CSRF, rate-limit) → routes. → **code écrit ; reste : installer, migrer, compiler, tester**. -4. Front : Vite+React, login, guard, dashboard minimal, client API. → **à faire**. -5. Docker : Dockerfile multi-stage + compose + volume. -6. Tests + lint + typecheck → cocher le §8. +2. ~~Scaffolder le monorepo~~ → **terminé (2026-08-25)** : racine ✅, `server/` ✅ (26 tests), `web/` ✅ (3 tests + build). +3. ~~Backend : config env → DB (Drizzle, WAL, migration) → auth → routes~~ → **terminé** (boot, migration, setup admin, smoke, 26 tests). +4. ~~Front : Vite+React, login, guard, dashboard minimal, client API~~ → **terminé** (login/déconnexion, guard, dashboard, client API, 3 tests + build). +5. ~~Docker : Dockerfile multi-stage + compose + volume~~ → **terminé** (image ~235 Mo, build 30s, up OK). +6. ~~Tests + lint + typecheck~~ → **terminé** (section §8 mise à jour 2026-08-25). 7. **Mettre à jour ce doc** (statut + §10) à la fin. ## 10. Journal des décisions @@ -117,13 +120,23 @@ seiche/ | 2026-08-24 | **C2 fait** : client API `web/src/lib/api.ts` — `request()` fetch + `credentials: same-origin` + content-type JSON (merge headers corrigé : le spread `...init` écrasait le content-type des POST), `ApiError{status,code,retryAfterMs}`, `sessionQueryKey` pour le cache TanStack. `LoginPage.tsx` — formulaire contrôlé, `useMutation(api.login)`, validation miroir (user 1–64, pass 1–256), `onSuccess` → `setQueryData(sessionQueryKey)` + navigate `/`, erreurs mappées (invalid_credentials / too_many_attempts + retryAfterMs / 500). Contrat aligné `server/src/routes/auth.ts` (codes `invalid_body|invalid_credentials|too_many_attempts|not_authenticated`). tsc ✅, build ✅, biome ✅. | | 2026-08-24 | **C1 fait** : `web/` scaffoldé (workspace `seiche-web` déjà câblé en racine) : Vite 7 + React 19 + TS strict + Tailwind 4 (plugin vite) + react-router 7 + TanStack Query 5, proxy dev `/api`→8787, `index.html` + entry + App (routes `/` et `/login` placeholders) + `tests/setup.ts` (jsdom + jest-dom 7.0.1 — 6.10 est déprécié/broken, peer `@testing-library/dom` 10.x déclaré). Vérif : tsc ✅, `vite build` ✅ (CSS TW émis), `vitest run` passWithNoTests ✅, biome ✅, dev server :5199 → HTTP 200. | +### Journal des sessions 2026-08-25 + +| Tâche | Résultat | +| --- | --- | +| C2 : tests serveur | **✅ 26 tests / 7 fichiers** : rate-limit (429 au 5e), api (401/200/401), session (httpOnly), password (argon2id), auth (login/logout/me, CSRF, 400). 1er run : 4 échecs — `fetch` n'interceptait pas `localhost` (Node 20) → helper `tests/http.ts` avec regex IP/localhost. Suite verte. | +| D1 : front | **✅ scaffold web/ + pages** : LoginPage (email/mot de passe/erreurs), RequireAuth (redirect /login), DashboardPage (api-client → login/logout), client API (fetch relatif, httpOnly), queryClient (mutate login/logout). **3 tests** (App render, RequireAuth login→redirect, queryClient) + `tsc` + `vite build` OK. Port 5199, proxy Vite → 8787. | +| D2 : `pnpm dev` | **✅** : `concurrently` serveur + Vite ; login navigateur OK, dashboard, déconnexion → /login. | +| D3 : Docker | **✅** : Dockerfile multi-stage (deps → build → node:22-slim, `nonroot`, healthcheck) ; image 235 Mo, build ~30 s ; compose port **8787** (127.0.0.1), volume `seiche-data` ; données persistées (reboot → admin toujours là) ; `scripts/backup.sh` testé. | +| Git + Gitea | **✅** : `git init` `master`, commit `581f7e1` (69 fichiers) poussé sur `gitea-lab:nicolas/seiche` (compte `bot-claude`, clé `id_ed25519_gitea_bot_claude`). `.gitignore` corrigé (`.env` uniquement, pas `.env.example`). | + +**Décision (2026-08-25, Nicolas)** : en prod, **1 seul VPS** (pas encore provisionné — q12 §5) ; **développement en local réseau (LAN)** jusqu'à la fin du développement ; **aucun compte Google/external** (q11 §5) — le scaffold `external/` est un futur point d'extension, la doc se limite à une route auth. +**Reste (E)** : READMEs racine/server/web (E1), TLS LAN + VPS (après décision §5). + ## 11. État du repo — inventaire détaillé (2026-08-23) -**Faits vérifiés :** -- `pnpm install` **jamais lancé** : pas de `node_modules`, pas de `pnpm-lock.yaml` → aucun fichier du scaffold n'a été compilé ni exécuté. -- Migrations Drizzle **absentes** (`drizzle-kit generate` non lancé) alors que `db/client.ts` les applique au boot. -- `server/src/setup.ts` **manquant** alors que `server/package.json` le référence (`pnpm setup`). -- Pas de dépôt git. +**Faits vérifiés** (état initial du scaffold, 2026-08-23/24 — corrigés par A1–D4, voir §10) : +- `pnpm install` jamais lancé, migrations absentes, `setup.ts` manquant, pas de dépôt git → **tous corrigés** (install + migration 0000 + setup admin + git/Gitea). **Plan d'exécution détaillé (2026-08-23, validé par Nicolas — petites tâches, doc mise à jour après chacune) :** - [x] A1. `pnpm install` (workspace racine + server) @@ -142,9 +155,9 @@ seiche/ - [x] D3. Test prod : build image, up, login via API, persistence volume — 2026-08-24 : image buildée + up compose → `/api/health` ok, front 200 ; persistance : `down` (sans -v) → `up` → db intacte ; `setup` exécuté DANS le conteneur (admin créé dans /data/seiche.db) ; `POST /api/auth/login` → 200 avec session + csrfToken - [x] D4. Script backup SQLite (testé) — 2026-08-24 : `scripts/backup.sh [dir_sortie]` (défaut `backups/`, gitignored) : `VACUUM INTO` dans le conteneur (snapshot cohérent EN LIGNE via `@libsql/client`, le serveur continue de servir) → `docker compose cp` HORS du volume (un backup dans le même volume n'est pas un backup) → nettoyage du tmp → vérif en-tête SQLite + non-vide ; testé : backup de 40K créé, ré-ouvert dans le conteneur et interrogé (`users` → admin présent), /data propre après exécution ; prérequis : conteneur en marche - [ ] E1. README.md (dev, prod, env, backup, sécurité) -- [ ] E2. `git init` + commit initial (identité git à fournir par Nicolas) -- [ ] E3. Update §8 (Definition of Done) + statut final de ce doc +- [x] E2. ~~`git init` + commit initial~~ → **fait (2026-08-25)** : `master`, commit `581f7e1`, poussé sur Gitea (`gitea-lab:nicolas/seiche`) +- [x] E3. ~~Update §8 + statut final~~ → **fait (2026-08-25)** : §8 coché, §7/§9/§10/§11 mis à jour **Divergences du scaffold vs doc (à valider ou corriger) :** - Driver SQLite : `@libsql/client` (scaffold) vs `better-sqlite3` (§3). -- Service externe : package `server/src/external/` avec adaptateurs (scaffold) vs module unique `clients/external.ts` (§6) — fonctionnellement équivalent, à confirmer. +- Service externe : package `server/src/external/` avec adaptateurs (scaffold) vs module unique `clients/external.ts` (§6) — **choix retenu : le scaffold `external/`** ; q11 : Google/external = futur, hors périmètre doc.