feat(deploy): prod = LXC Proxmox + nginx-proxy-manager — COOKIE_SECURE/TRUST_PROXY=1 en prod (F1), REPRISE §5/§9 + README à jour

This commit is contained in:
Nicolas
2026-08-26 09:28:46 +02:00
parent 9262c3a4f2
commit ef9f04c551
3 changed files with 25 additions and 8 deletions
+10 -3
View File
@@ -28,9 +28,12 @@ SEICHE_ADMIN_USERNAME=admin SEICHE_ADMIN_PASSWORD='<≥12 caractères>' pnpm set
Puis ouvrir http://localhost:5173 et se connecter.
## Démarrage en production (Docker)
## Production (LXC Proxmox + nginx-proxy-manager)
Déploiement retenu (REPRISE.md §5 q12) : **LXC sur Proxmox** (Docker + compose dedans), TLS terminé par **nginx-proxy-manager** (Let's Encrypt).
```bash
# Dans le LXC :
docker compose up -d --build
# Premier compte (dans le conteneur, base vide) :
@@ -38,7 +41,11 @@ docker compose exec seiche sh -c \
"SEICHE_ADMIN_USERNAME=admin SEICHE_ADMIN_PASSWORD='<≥12 caractères>' pnpm setup"
```
Le conteneur écoute `8787` (API + front buildé, servi par Fastify). La base SQLite persiste dans le volume `seiche-data` (`/data`). Healthcheck : `GET /api/health`.
Puis dans NPM : **Add Site** → hostname (ex. `seiche.mondomaine.fr`), **Proxy to** `IP_DU_LXC:8787`, **Let's Encrypt** activé.
Le compose active `SEICHE_COOKIE_SECURE=1` + `SEICHE_TRUST_PROXY=1` (TLS + `X-Forwarded-For` derrière NPM). **Sans proxy TLS** (test Docker en LAN) : passer ces deux variables à `0`, sinon les cookies `Secure` ne sont pas envoyés en HTTP.
Le conteneur écoute `0.0.0.0:8787` (API + front buildé, servi par Fastify). La base SQLite persiste dans le volume `seiche-data` (`/data`). Healthcheck : `GET /api/health`.
## Variables d'environnement
@@ -69,7 +76,7 @@ Snapshot cohérente **en ligne** (`VACUUM INTO` dans le conteneur, le serveur co
- **Rate-limit** sur `/login` (anti brute-force, par IP client).
- Session : cookie **httpOnly + SameSite=Lax**, durée limitée, révocation au logout ; **CSRF** (double-submit) sur toutes les mutations.
- Secrets uniquement en variables d'environnement ; SQLite hors de l'image (volume + backup).
- **LAN sans TLS = mots de passe en clair sur le réseau.** À trancher (REPRISE.md §5 q4) : Caddy/Tailscale ou HTTP assumé. Derrière un proxy TLS : `SEICHE_COOKIE_SECURE=1` + `SEICHE_TRUST_PROXY=1`.
- **TLS** : en prod derrière nginx-proxy-manager (Let's Encrypt) → cookies `Secure` + `trustProxy` (déjà dans le compose). En dev LAN sans proxy : HTTP (mots de passe en clair, dév uniquement).
- API minimale : `/api/health` public, tout le reste protégé.
## Structure
+9 -5
View File
@@ -1,6 +1,6 @@
# Projet « Seiche » — doc de reprise
> À jour : 2026-08-26 (A1–D4 + proxy + READMEs ✅, git) · Statut : **stack complète validée** : serveur 30 tests, front 3 tests + build, Docker build+up, admin créé, `master` poussé sur Gitea · Reste : TLS LAN + VPS (décision §5)
> À jour : 2026-08-26 (A1–D4 + proxy + READMEs + **déploiement LXC/NPM ✅**, git) · Statut : **stack complète validée** : serveur 30 tests, front 3 tests + build, Docker build+up, admin créé, `master` poussé sur Gitea · Reste : exécution du déploiement (LXC + règle NPM + domaine) = infra Nicolas (§5 q4/q12)
> 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**.
@@ -35,7 +35,7 @@ Appli web auto-hébergée, **réseau local uniquement, mono-utilisateur** (Nicol
5. Secrets **uniquement en variables d'env** (`.env` hors repo, `.env.example` versionné).
6. Logs **pino** structurés : jamais de mot de passe / token / cookie dans les logs.
7. SQLite **hors de l'image** : volume nommé `/data/app.db` + **script de backup** testé.
8. LAN sans TLS = mots de passe en clair sur le réseau → à trancher (§5 q4) : Caddy self-signed, Tailscale, ou HTTP assumé.
8. TLS : en prod derrière nginx-proxy-manager (Let's Encrypt, q4/q12 §5) ; dev LAN sans proxy = mots de passe en clair (acceptable en dev uniquement).
9. Dépendances : lockfile figé en prod, `npm audit` au rythme des maj.
10. Surface API minimale, pas de debug en prod.
@@ -45,13 +45,13 @@ Appli web auto-hébergée, **réseau local uniquement, mono-utilisateur** (Nicol
| 1 | **Quel service externe** ? URL, doc API, mode d'auth (token statique / OAuth / Basic ?) | Client isolé `server/src/external/` (adaptateurs), config env, mock en dev |
| 2 | **Périmètre concret du MVP** ? (lister X, modifier Y, déclencher Z, logs…) | 1 CRUD exemple + health |
| 3 | Un seul compte, ou plusieurs possibles ? | Schéma multi-comptes, 1 compte créé au 1er boot |
| 4 | **TLS en LAN** ? (Caddy / Tailscale / HTTP accepté) | HTTP + risque documenté dans le README |
| 4 | ✅ **TLS en LAN** (résolu 2026-08-26) : nginx-proxy-manager (Let's Encrypt) en prod ; dev LAN sans proxy = HTTP accepté (risque dév documenté). | — |
| 5 | UI Tailwind + shadcn/ui, interface en français ? | Oui / FR |
| 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 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 |
| 12 | ✅ **VPS / mise en prod** (résolu 2026-08-26) : pas de VPS — **LXC Proxmox** (Docker + compose dans le LXC), TLS via **nginx-proxy-manager** (Let's Encrypt, déjà fonctionnel). | — |
## 6. Architecture
- **1 conteneur = API + front prébuildé** servi par Fastify → zéro CORS, 1 port, déploiement simple.
@@ -131,9 +131,13 @@ seiche/
| 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`). |
| E4 : Proxy (2026-08-26) | **✅** : `SEICHE_TRUST_PROXY` (Fastify `trustProxy` → rate-limit par IP client derrière proxy) + `SEICHE_COOKIE_SECURE` (cookies Secure derrière TLS). 4 tests (`proxy.test.ts`) : 6 IPs distinctes limitées séparément, XFF ignoré par défaut, Secure on/off. Suite : 30/30. Session précédente interrompue sur cette tâche ; finalisée et commitée. |
| E1 : READMEs (2026-08-26) | **✅** : README racine (démarrage dev/Docker, env, backup, sécurité) + `server/README.md` (scripts, migrations au boot, structure, API) + `web/README.md` (scripts, structure, contrat API). Corrections doc : port dev Vite = 5173 (pas 5199), health = `/api/health` (pas `/api/v1/health`). |
| F1 : Déploiement (2026-08-26) | **✅ décision + config** : prod = **LXC Proxmox** (Docker + compose dans le LXC), TLS via **nginx-proxy-manager** (Let's Encrypt, déjà fonctionnel). `docker-compose.yml` : `SEICHE_COOKIE_SECURE=1` + `SEICHE_TRUST_PROXY=1` (variables E4 enfin câblées). README : section « Production (LXC + NPM) ». Reste côté Nicolas : créer le LXC, règle NPM (hostname + `IP_LXC:8787`), domaine/certificat. |
**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).
**Décision (2026-08-26, Nicolas, remplace « 1 VPS »)** : prod = **auto-hébergement LXC Proxmox** (Docker dans le LXC, compose), TLS par **nginx-proxy-manager** (Let's Encrypt, déjà en place). Exécution infra (LXC, règle NPM, domaine) = côté Nicolas.
**Reste** : READMEs (E1) ✅, décision + config TLS/VPS (F1) ✅. Exécution du déploiement = infra Nicolas (LXC Proxmox, règle NPM, domaine).
## 11. État du repo — inventaire détaillé (2026-08-23)
+6
View File
@@ -9,6 +9,12 @@ services:
volumes:
- seiche-data:/data
restart: unless-stopped
# Déploiement : LXC Proxmox derrière nginx-proxy-manager (TLS Let's Encrypt).
# NPM termine le TLS et ajoute X-Forwarded-For → cookies Secure + IP client réelle.
# Sans proxy TLS (test Docker en LAN) : passer ces deux valeurs à "0".
environment:
SEICHE_COOKIE_SECURE: "1"
SEICHE_TRUST_PROXY: "1"
# Variables d'option (toutes ont des valeurs par défaut dans server/src/config.ts) :
# SEICHE_EXTERNAL_ADAPTER, SEICHE_EXTERNAL_BASE_URL, SEICHE_EXTERNAL_API_KEY,
# SEICHE_PORT, SEICHE_SESSION_TTL_MS, ...