docs: READMEs racine + server + web (E1) — dev, prod, env, backup, sécurité

This commit is contained in:
bot-claude
2026-08-26 08:20:04 +02:00
parent fdc76f82f5
commit d9590462cd
4 changed files with 173 additions and 5 deletions
+83
View File
@@ -0,0 +1,83 @@
# Seiche
Appli web auto-hébergée, **réseau local uniquement, mono-utilisateur** : gère un service externe via son API REST, protégée par mot de passe.
**Stack** : TypeScript strict · Node 22 + Fastify 5 · SQLite (libsql + Drizzle) · Vite + React 19 + Tailwind 4 · 1 conteneur (API + front statique).
- Doc de projet / décisions / plan : [REPRISE.md](REPRISE.md)
- Détail backend : [server/README.md](server/README.md) · Détail front : [web/README.md](web/README.md)
## Prérequis
- Node ≥ 22, pnpm (workspace)
- Docker + compose (pour la prod)
## Démarrage en développement
```bash
pnpm install
pnpm dev:server # terminal 1 — API sur http://localhost:8787
pnpm dev:web # terminal 2 — Vite sur http://localhost:5173 (proxy /api → 8787)
```
Premier compte (nécessaire une seule fois, idempotent) :
```bash
SEICHE_ADMIN_USERNAME=admin SEICHE_ADMIN_PASSWORD='<≥12 caractères>' pnpm setup
```
Puis ouvrir http://localhost:5173 et se connecter.
## Démarrage en production (Docker)
```bash
docker compose up -d --build
# Premier compte (dans le conteneur, base vide) :
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`.
## Variables d'environnement
Référent complet : [.env.example](.env.example) (copier dans `.env` pour la dev — **jamais committer le `.env`**).
| Variable | Défaut | Rôle |
|---|---|---|
| `SEICHE_PORT` / `SEICHE_HOST` | `8787` / `127.0.0.1` | Écoute du serveur (Docker force `0.0.0.0`) |
| `SEICHE_DATA_DIR` | `./data` | Dossier SQLite (volume `/data` en Docker) |
| `SEICHE_SESSION_TTL_MS` | 8 h | Durée de vie d'une session |
| `SEICHE_SESSION_ABSOLUTE_TTL_MS` | 7 j | Durée absolue max |
| `SEICHE_MAX_LOGIN_ATTEMPTS_PER_MINUTE` | `5` | Rate-limit `/login` par IP |
| `SEICHE_COOKIE_SECURE` | `0` | `1` derrière un proxy TLS (cookies `Secure`) |
| `SEICHE_TRUST_PROXY` | `0` | `1` derrière un proxy (lit `X-Forwarded-For`) |
| `SEICHE_EXTERNAL_*` | `example` | Service REST externe (adaptateur, URL, clé, cache…) |
## Sauvegarde
```bash
./scripts/backup.sh [dir_sortie] # défaut : ./backups/
```
Snapshot cohérente **en ligne** (`VACUUM INTO` dans le conteneur, le serveur continue de servir) → copie **hors** du volume → vérification de l'en-tête SQLite. Prérequis : conteneur en marche. Penser à cronner et à stocker les `.db` ailleurs que sur la machine (le dossier `backups/` est gitignoré).
## Sécurité — non négociables
- Mot de passe **Argon2id** (sel par compte), jamais en clair ni dans les logs.
- **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`.
- API minimale : `/api/health` public, tout le reste protégé.
## Structure
```
seiche/
├── server/ # Fastify : routes /api/v1/*, auth, DB, client externe
├── web/ # Vite + React : login, dashboard, client API
├── scripts/ # backup.sh
└── docker-compose.yml · Dockerfile · .env.example
```