feat(deploy): webhook auto-deploy Forgejo → VPS Paris (TD-04)
Some checks are pending
CI / quality (push) Waiting to run

This commit is contained in:
Hermann_Kitio 2026-07-01 12:34:39 +03:00
parent 85c760abee
commit 0ae2db3d8c
6 changed files with 333 additions and 37 deletions

View file

@ -24,9 +24,9 @@ Utilisateur (navigateur)
┌─────────────▼───────────────┐
│ BACKEND │
│ Hono.js (Node.js) │
Render (Frankfurt)
VPS Paris (systemd + Caddy)
│ — toutes les routes API │
│ — WebSocket proxy T2 EO
│ — WebSocket proxy T1/T2 EO │
└──────┬──────────────┬───────┘
│ │
┌──────▼──────┐ ┌────▼────────────────┐
@ -433,17 +433,17 @@ NODE_ENV=production
## 9. Déploiement
### Hébergement Git — GitHub
### Hébergement Git — Forgejo (auto-hébergé)
- Plateforme : github.com
- Dépôt frontend : `https://github.com/germannoff/expria-frontend`
- Dépôt backend : `https://github.com/germannoff/expria-backend`
- Note : compte GitHub réactivé le 17 avril 2026 après restriction OFAC levée
- Auto-deploy : disponible via Render (connecté à GitHub)
- Plateforme : Forgejo (fork Gitea) auto-hébergé sur le VPS Moscou (`157.22.190.91`), exposé via `git.expria.app` (proxifié par un Caddy Moscou).
- Dépôt frontend : `https://git.expria.app/hermann/expria-frontend`
- Dépôt backend : `https://git.expria.app/hermann/expria-backend`
- Push : HTTPS + token Forgejo (branche `main`). Un miroir SSH historique (`ssh://157.22.190.91:2222`) peut exister mais n'est pas la voie de référence.
- Note : migration depuis GitHub actée pour s'affranchir des restrictions OFAC et reprendre la main sur l'hébergement Git.
### Frontend — Cloudflare Pages
- Source : dépôt GitHub `expria-frontend`
- Source : dépôt Forgejo `expria-frontend`
- Build command : `npm run build`
- Output directory : `dist`
- Domaine : `expria.app` (DNS pointé depuis Vercel vers Cloudflare Pages)
@ -455,17 +455,14 @@ npm run build
npx wrangler pages deploy dist --project-name=expria
```
### Backend — Render
### Backend — VPS Paris (HostVDS)
- Source : dépôt GitHub `expria-backend`
- Type : Web Service (Node.js)
- Région : Frankfurt (EU) — proximité utilisateurs Afrique du Nord
- Build command : `npm run build`
- Start command : `npm start`
- Domaine : `api.expria.app` (certificat SSL actif)
- URL Render : `https://expria-backend.onrender.com` (alias)
- WebSocket : activé nativement sur Render
- Déploiement : **automatique à chaque push sur main (GitHub → Render)**
- Hébergement : VPS **HostVDS Paris**, IP `95.182.84.3`.
- Source : dépôt Forgejo `expria-backend`, checkout dans `/opt/expria/expria-backend`.
- Runtime : Node.js (v20.x) géré par **systemd** — unité `expria-backend.service` (`ExecStart=/usr/bin/node dist/index.js`, `Restart=always`). Le process écoute sur `localhost:4000`.
- Reverse proxy : **Caddy** (v2) sert `api.expria.app` en TLS (Let's Encrypt forcé, `acme_ca` production) et proxifie vers `localhost:4000`. WebSocket (T1/T2 Live) supporté nativement par Caddy.
- Proxy Gemini : **Cloudflare WARP** tourne en mode SOCKS5 (`socks5://127.0.0.1:40000`) sur le VPS ; **seul** le trafic WebSocket Gemini Live y est routé (via `GEMINI_PROXY_URL`, cf. §8) car l'IP du datacenter est bloquée par Google. Supabase, DeepSeek, Stripe et les clients restent en connexion directe.
- **Auto-deploy (webhook Forgejo)** : un push sur `main` déclenche un webhook Forgejo vers `deploy.expria.app` (sous-domaine dédié, proxifié par Caddy vers un listener local `127.0.0.1:9000`). Le listener (`deploy/webhook-listener.mjs`, Node stdlib) vérifie la signature **HMAC-SHA256** (`X-Gitea-Signature`) et l'IP source (allowlist `157.22.190.91`), puis lance `deploy/deploy.sh` (git fast-forward → `npm ci``npm run build``systemctl restart expria-backend`, avec **rollback automatique** si le build ou le health-check échoue). Listener isolé sous l'utilisateur non-root `deploy` (unité `expria-deploy.service`).
### Base de données — Supabase
@ -478,8 +475,9 @@ npx wrangler pages deploy dist --project-name=expria
```
1. Tester localement (npm run test — tous les tests verts)
2. Rejouer le Golden Dataset
3. Commit + push sur GitHub (branche main)
4. Backend : auto-deploy Render déclenché automatiquement
3. Commit + push sur Forgejo (branche main)
4. Backend : auto-deploy déclenché par le webhook Forgejo → VPS Paris
(git pull → npm ci → build → restart systemd, rollback auto si échec)
5. Déployer le frontend : npm run build && npx wrangler pages deploy dist
6. Vérifier les URLs de production (expria.app + api.expria.app)
7. Rejouer le Smoke Test (Groupe Z du Golden Dataset)

View file

@ -6,6 +6,22 @@ Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/).
---
## [Unreleased] — 2026-07-01 — Auto-deploy par webhook Forgejo (VPS Paris)
### Added
- `deploy/webhook-listener.mjs` — listener HTTP **Node stdlib pur** (aucune dépendance externe), bindé sur `127.0.0.1:9000`, path `POST /hooks/deploy`. Vérifie la signature **HMAC-SHA256** du corps brut (header `X-Gitea-Signature`, fallback `X-Forgejo-Signature`) via `crypto.timingSafeEqual`, refuse de démarrer sans `WEBHOOK_SECRET`, ne traite que `ref === refs/heads/main`, cape le body à 5 Mo, ne logge jamais le secret ni la signature. Lance `deploy.sh` sans bloquer la réponse.
- `deploy/deploy.sh` — script de déploiement (`set -euo pipefail`) : verrou `flock` (deploys sérialisés), `git fetch` + `git merge --ff-only origin/main` (refuse un historique divergé), `npm ci`, `npm run build`, `sudo systemctl restart expria-backend.service` (unique commande privilégiée), health-check `GET http://127.0.0.1:4000/`. **Rollback automatique** (`git reset --hard` vers le commit précédent + rebuild + restart) si le build ou le health-check échoue.
- `deploy/expria-deploy.service` — unité systemd du listener, **distincte** de `expria-backend.service`, sous l'utilisateur non-root `deploy`, secret injecté via `EnvironmentFile=/etc/expria/webhook.env`.
### Notes
- Exposition : sous-domaine dédié `deploy.expria.app` proxifié par Caddy vers `127.0.0.1:9000`, avec **allowlist IP** sur l'egress Forgejo Moscou (`157.22.190.91`) + signature HMAC = double garde.
- Sécurité : le listener tourne sous `deploy` (non-root) avec une règle sudoers restreinte à la seule ligne `systemctl restart expria-backend.service`. Secret dédié (≠ token git), hors dépôt, jamais loggé.
- Résout **TD-04** (déploiement manuel). Étapes ops VPS (création user `deploy`, sudoers, DNS `deploy.expria.app`, bloc Caddy, secret, `systemctl enable --now expria-deploy`, config webhook Forgejo) documentées et exécutées hors code.
---
## [Unreleased] — 2026-06-30 — Proxy SOCKS5 (Cloudflare WARP) pour les WS Gemini Live
### Added

View file

@ -44,13 +44,22 @@
## 2. Décisions pragmatiques — à revisiter
### TD-04 — Déploiement manuel (frontend + backend)
### TD-04 — Déploiement manuel (backend)
**Priorité :** 🟢 Mineur
**Statut :** Ouvert — accepté jusqu'aux premiers revenus
**Description :** Cloudflare Pages et Render ne supportent pas l'auto-deploy depuis Codeberg. Le déploiement est manuel (CLI + dashboard).
**À faire :** Migrer vers VPS Hetzner + Coolify pour restaurer l'auto-deploy. Voir ARCHITECTURE.md §9 Phase 2.
**Condition de résolution :** Quand Expria génère ses premiers revenus réguliers.
**Statut :** Résolu — Auto-deploy webhook Forgejo → VPS Paris (2026-07-01)
**Description :** Le déploiement backend était manuel (git pull + build + restart à la main sur le VPS). Résolu par un **webhook auto-deploy custom** : un push sur `main` du dépôt Forgejo (`git.expria.app`) déclenche un webhook vers `deploy.expria.app`, reçu par un listener **Node stdlib** (`deploy/webhook-listener.mjs`) qui vérifie la signature HMAC-SHA256 + l'IP source, puis exécute `deploy/deploy.sh` (fast-forward → `npm ci``npm run build``systemctl restart expria-backend`, avec rollback automatique sur échec). Le listener tourne sous l'utilisateur non-root `deploy` via `expria-deploy.service`. Solution retenue plutôt que la cible historique « VPS Hetzner + Coolify » (jamais implémentée) : le backend est déjà sur VPS Paris (HostVDS) avec systemd + Caddy, un webhook léger suffit.
**Note frontend :** le déploiement frontend (Cloudflare Pages CLI) reste manuel — hors périmètre de cette résolution.
---
### TD-26 — `expria-backend.service` tourne en root
**Priorité :** 🟢 Mineur
**Statut :** Ouvert — introduit/constaté au Sprint auto-deploy (2026-07-01)
**Description :** L'unité systemd `expria-backend.service` sur le VPS Paris définit `User=root`. Le process API (Hono, WebSocket) s'exécute donc avec les pleins privilèges, ce qui n'est pas nécessaire et augmente la surface d'impact en cas de compromission du runtime.
**À faire :** Migrer le service vers un utilisateur dédié non-root (ex. `expria`), ajuster les permissions du checkout `/opt/expria/expria-backend`, du port (< 1024 non requis, le service écoute sur 4000) et du `EnvironmentFile`. Le listener auto-deploy tourne déjà sous `deploy` (non-root) le backend applicatif doit suivre.
**Hors scope :** ce durcissement est indépendant de la mise en place du webhook et sera planifié ultérieurement.
---
@ -323,14 +332,15 @@ live** apparaîtra (ex. T3 Live).
## 5. Historique des résolutions
| ID | Description | Résolu le | Comment |
| ----- | ------------------------------------------- | ---------- | --------------------------- |
| TD-02 | planController.ts complété | 2026-04-16 | Session Stripe |
| TD-03 | stripe.ts complété | 2026-04-16 | Session Stripe |
| TD-14 | Erreurs TS2835 + TS18046 + TS7053 corrigées | 2026-04-17 | Session build Render |
| TD-10 | Analyse des patterns (Premium) livrée | 2026-04-25 | Sprint 3.6c |
| TD-11 | Indice de préparation livré | 2026-04-25 | Sprint 3.6c |
| TD-16 | Bucket Storage abandonné | 2026-04-25 | Sprint 4b — Deepgram direct |
| TD-17 | Limite audio in-memory caduque | 2026-04-25 | Sprint 4b |
| TD-18 | RLS Storage caduque | 2026-04-25 | Sprint 4b |
| TD-13 | Webhook Stripe idempotent | 2026-04-26 | Sprint 5a |
| ID | Description | Résolu le | Comment |
| ----- | ------------------------------------------- | ---------- | ----------------------------- |
| TD-02 | planController.ts complété | 2026-04-16 | Session Stripe |
| TD-03 | stripe.ts complété | 2026-04-16 | Session Stripe |
| TD-14 | Erreurs TS2835 + TS18046 + TS7053 corrigées | 2026-04-17 | Session build Render |
| TD-10 | Analyse des patterns (Premium) livrée | 2026-04-25 | Sprint 3.6c |
| TD-11 | Indice de préparation livré | 2026-04-25 | Sprint 3.6c |
| TD-16 | Bucket Storage abandonné | 2026-04-25 | Sprint 4b — Deepgram direct |
| TD-17 | Limite audio in-memory caduque | 2026-04-25 | Sprint 4b |
| TD-18 | RLS Storage caduque | 2026-04-25 | Sprint 4b |
| TD-13 | Webhook Stripe idempotent | 2026-04-26 | Sprint 5a |
| TD-04 | Auto-deploy webhook Forgejo → VPS Paris | 2026-07-01 | Node stdlib + systemd + Caddy |