From 7444eeb5324b91e6e91e00b87e37244b8eefb3ad Mon Sep 17 00:00:00 2001 From: Archicratia Date: Sat, 21 Feb 2026 15:34:47 +0100 Subject: [PATCH] docs: add pro runbooks (deploy/edge/public_site) + annotations spec + start-here v2 --- docs/EDITORIAL-ANNOTATIONS-SPEC.md | 327 +++++++++++++++++++++++++++++ docs/START-HERE.md | 176 ++++++++++++++++ docs/runbooks/DEPLOY-BLUE-GREEN.md | 202 ++++++++++++++++++ docs/runbooks/EDGE-TRAEFIK.md | 147 +++++++++++++ docs/runbooks/ENV-PUBLIC_SITE.md | 114 ++++++++++ 5 files changed, 966 insertions(+) create mode 100644 docs/EDITORIAL-ANNOTATIONS-SPEC.md create mode 100644 docs/START-HERE.md create mode 100644 docs/runbooks/DEPLOY-BLUE-GREEN.md create mode 100644 docs/runbooks/EDGE-TRAEFIK.md create mode 100644 docs/runbooks/ENV-PUBLIC_SITE.md diff --git a/docs/EDITORIAL-ANNOTATIONS-SPEC.md b/docs/EDITORIAL-ANNOTATIONS-SPEC.md new file mode 100644 index 0000000..2dc378c --- /dev/null +++ b/docs/EDITORIAL-ANNOTATIONS-SPEC.md @@ -0,0 +1,327 @@ +# SPEC — Annotations éditoriales (YAML v1) + merge + anti-doublon +> Objectif : permettre aux tickets (Gitea) de déposer “Références / Médias / Commentaires” dans `src/annotations/**`, +> de façon univoque, stable, et sans régression. + +## 0) Contexte et intention +Le site est statique. L’édition collaborative se fait via : +- un mode “proposition” (UI / modal) +- un ticket Gitea (issue) standardisé +- un script d’application côté éditeur (`apply-ticket.mjs` ou équivalent) +- génération d’un YAML d’annotations versionné dans Git + +La donnée d’annotation doit être : +- **audit-able** (Git) +- **merge-able** (sans tout casser) +- **stable** (IDs paragraphes / liens / médias) +- **scalable** (éviter YAML monstrueux à long terme) + +## 1) Arborescence canonique +### 1.1 Un workKey par “ouvrage / section du site” +On veut une univocité entre : +- SiteNav (Méthode, Essai-thèse, Traité, Cas IA, Glossaire, Atlas) +et +- l’arborescence annotations + +Proposition canonique (workKey = route racine) : +- `methode` +- `archicrat-ia` (Essai-thèse ArchiCraT-IA) +- `traite` +- `ia` +- `glossaire` +- `atlas` + +### 1.2 Règle de stockage “v1” +**Par page**, un YAML unique : + +src/annotations//.yml + +Exemples : +- Page : `/archicrat-ia/prologue/` + - slug content = `archicrat-ia/prologue` + - fichier : `src/annotations/archicrat-ia/prologue.yml` + +- Page : `/traite/00-demarrage/` + - fichier : `src/annotations/traite/00-demarrage.yml` + +> Note : “slugSansWorkKey” = la partie après `/`. +> S’il y a des sous-dossiers (chapitres), le chemin reflète la structure : `chapitre-1/section-a.yml` si on choisit du sharding. + +## 2) Question “gros YAML” : page unique vs sharding par paragraphe +### 2.1 Option A (v1 recommandée) : 1 YAML par page +Avantages : +- simple +- peu de fichiers +- diff lisible si volume modéré +- cohérent avec un modèle “annotations par page” + +Inconvénients : +- YAML peut grossir si milliers d’annotations + +### 2.2 Option B (v2 future) : sharding par paragraphe + +src/annotations///.yml + +Avantages : +- fichiers petits +- merges moins conflictuels +Inconvénients : +- plus de fichiers +- tooling plus complexe (indexation + merge multi-fichiers) + +### 2.3 Recommandation de mission (sans casser l’existant) +- On démarre en **Option A**. +- On se garde une migration future (v2) quand le volume réel le justifie. +- On impose dès v1 : **clé unique + merge déterministe + anti-doublon**, ce qui rend la migration future possible. + +## 3) Format YAML v1 (schéma complet) +### 3.1 Top-level +en yaml : + +schema: 1 + +# Optionnel mais recommandé (doit matcher la page) +page: "/" + +meta: + title: "Titre de la page (optionnel)" + updatedAt: "2026-02-21T12:34:56Z" # ISO8601 + updatedBy: "username" # compte editor + source: + kind: "ticket" + id: 123 + url: "https://gitea.../issues/123" + +paras: + "": + references: [] + media: [] + comments: [] + +### 3.2 paras : clé = paraId (ex: p-0-d7974f88) + +Chaque paragraphe peut porter 3 types d’éléments : + +references + +media + +comments + +Règle : si une section est vide, elle peut être [] ou absente. +Mais pour simplifier les merges, on recommande de garder la forme canonique avec []. + +## 4) Formats des items + clés uniques +### 4.1 References +#### 4.1.1 Format + +references: + - id: "ref:doi:10.1234/abcd.efgh" # clé stable (voir 4.1.2) + kind: "doi" # doi | url | isbn | arxiv | hal | other + label: "Titre court" + target: "https://doi.org/10.1234/abcd.efgh" + note: "Pourquoi c’est pertinent (optionnel)" + addedAt: "2026-02-21T12:34:56Z" + addedBy: "username" + +#### 4.1.2 Règle de clé unique (anti-doublon) + +id doit être stable et déterministe : + +doi → ref:doi: + +isbn → ref:isbn: + +url → ref:url: + +Normalisation URL (v1) : au minimum + +trim + +lowercase scheme/host + +retirer trailing slash si non significatif + +conserver query si importante + +#### 4.1.3 Merge / précédence + +Quand on merge deux listes references : + +union par id (clé unique) + +si même id existe des deux côtés : + +conserver kind/target de l’item le plus “riche” (target non vide gagne) + +concat/merge note : + +si notes différentes : garder les deux en les séparant (ex: noteA + "\n---\n" + noteB) + +addedAt : conserver le plus ancien + +addedBy : conserver le premier (ou liste si on veut, mais v1 simple : first) + +### 4.2 Media +#### 4.2.1 Format + +media: + - id: "media:image:sha256:abcd..." # clé stable (voir 4.2.2) + type: "image" # image | video | audio | file + src: "/public/media////" + caption: "Légende (optionnel)" + credit: "Auteur/source (optionnel)" + license: "CC-BY (optionnel)" + addedAt: "2026-02-21T12:34:56Z" + addedBy: "username" + +#### 4.2.2 Règle de clé unique + +id déterministe : + +idéal : hash du fichier (sha256) + +sinon : hash de type + src + +v1 (si on ne calcule pas de hash fichier) : + +media:: + +#### 4.2.3 Merge / précédence + +union par id + +si collision : + +garder src identique (sinon c’est un bug) + +fusionner caption/credit/license selon “non vide gagne” + +addedAt : plus ancien + +### 4.3 Comments +#### 4.3.1 Format + +comments: + - id: "cmt:20260221T123456Z:username:0001" + kind: "comment" # comment | question | objection | todo | validation + text: "Texte du commentaire" + status: "open" # open | resolved + addedAt: "2026-02-21T12:34:56Z" + addedBy: "username" + source: + kind: "ticket" + id: 123 + +#### 4.3.2 Clé unique + +Les commentaires sont “append-only” → id peut être générée (timestamp + user + compteur) + +Anti-doublon : si on ré-applique un ticket, on refuse de dupliquer un id existant. + +#### 4.3.3 Merge / précédence + +union par id + +collisions rares, mais si elles arrivent : + +si textes différents → garder les deux (on renomme l’id du second) + +## 5) Règles globales de merge (résumé) + +Quand on applique un ticket sur un YAML existant : + +vérifier schema == 1 + +vérifier page si présent : + +doit matcher / + +paras : + +créer paras[paraId] si absent + +pour chaque liste (references/media/comments) : + +merge par id (anti-doublon) + +appliquer règles de précédence (non vide gagne / concat note / append-only comments) + +## 6) Table de correspondance “UI ticket → YAML” + +Cette table permet à un successeur IA d’implémenter apply-ticket.mjs sans ambiguïté. + +### 6.1 Champs UI minimaux + +workKey (sélection implicite via page) + +pagePath (ex: /archicrat-ia/prologue/) + +pageSlug (ex: archicrat-ia/prologue) + +paraId (ex: p-0-d7974f88) + +kind : + +reference + +media + +comment + +### 6.2 Mapping exact + +| UI kind | UI champs | YAML cible | +| --------- | ----------------------------------------------------------- | ---------------------------- | +| reference | kind(doi/url/isbn), target, label, note | `paras[paraId].references[]` | +| media | type(image/video/audio/file), src, caption, credit, license | `paras[paraId].media[]` | +| comment | kind(comment/question/objection/todo/validation), text | `paras[paraId].comments[]` | + +### 6.3 Règles de génération d’ID (implémentation) + +reference.id : + +doi : ref:doi:${doi} + +isbn : ref:isbn:${isbn} + +url : ref:url:${normalize(url)} + +media.id : + +media:${type}:${src} + +comment.id : + +cmt:${timestamp}:${user}:${counter} + +## 7) Validation YAML (sanity) + +Avant commit (et en CI) : + +YAML parse OK + +schema OK + +page si présent cohérent + +paras est un mapping + +paraId match pattern : ^p-\d+-[a-f0-9]{8}$ (existant) + +src media pointe dans /public/media/... (ou /media/... si on choisit un alias, mais v1 canon : /public/media/...) + +## 8) Notes de compatibilité + +Les routes “Essai-thèse” ont été migrées vers /archicrat-ia/*. + +Les anciennes routes /archicratie/archicrat-ia/* peuvent exister en legacy, mais la donnée canonique d’annotation doit suivre le workKey final (archicrat-ia). + +## 9) Ce que l’étape 9 devra implémenter + +pipeline : ticket → YAML (apply-ticket) + +index : build-annotations-index + check-annotations + +tooling : détection médias orphelins / liens cassés + +éventuellement : migration vers sharding par paragraphe (v2) si volume réel le justifie \ No newline at end of file diff --git a/docs/START-HERE.md b/docs/START-HERE.md new file mode 100644 index 0000000..6e4f949 --- /dev/null +++ b/docs/START-HERE.md @@ -0,0 +1,176 @@ +# START-HERE — Archicratie / Édition Web (v2) +> Onboarding + exploitation “nickel chrome” (DEV → Gitea → CI → Release → Blue/Green → Edge/SSO) + +## 0) TL;DR (la règle d’or) +- **Gitea = source canonique**. +- **main est protégé** : toute modification passe par **branche → PR → CI → merge**. +- **Le NAS n’est pas la source** : si un hotfix est fait sur NAS, on **backporte** via PR immédiatement. +- **Le site est statique Astro** : la prod sert du HTML (nginx), l’accès est contrôlé au niveau reverse-proxy (Traefik + Authelia). + +## 1) Architecture mentale (ultra simple) +- **DEV (Mac Studio)** : édition + tests + commit + push +- **Gitea** : dépôt canon + PR + CI (CI.yaml) +- **NAS (DS220+)** : déploiement “blue/green” + - `web_blue` (staging upstream) → `127.0.0.1:8081` + - `web_green` (live upstream) → `127.0.0.1:8082` +- **Edge (Traefik)** : route les hosts + - `staging.archicratie...` → 8081 + - `archicratie...` → 8082 + - **Authelia** devant, via middleware `chain-auth@file` + +## 2) Répertoires & conventions (repo) +### 2.1 Contenu canon (édition) +- `src/content/**` : contenu MD / MDX canon (Astro content collections) +- `src/pages/**` : routes Astro (index, [...slug], etc.) +- `src/components/**` : composants UI (SiteNav, TOC, SidePanel, etc.) +- `src/layouts/**` : layouts (EditionLayout, SiteLayout) +- `src/styles/**` : CSS global + +### 2.2 Annotations (pré-Édition “tickets”) +- `src/annotations//.yml` + - Exemple : `src/annotations/archicrat-ia/prologue.yml` +- Objectif : stocker “Références / Médias / Commentaires” par page et par paragraphe (`p-...`). + +### 2.3 Scripts (tooling / build) +- `scripts/inject-anchor-aliases.mjs` : injection aliases dans dist +- `scripts/dedupe-ids-dist.mjs` : retire IDs dupliqués dans dist +- `scripts/build-para-index.mjs` : index paragraphes (postbuild / predev) +- `scripts/build-annotations-index.mjs` : index annotations (postbuild / predev) +- `scripts/check-anchors.mjs` : contrat stabilité d’ancres (CI) +- `scripts/check-annotations*.mjs` : sanity YAML + médias + +> Important : les scripts sont **partie intégrante** de la stabilité (IDs/ancres/indexation). +> On évite “la magie” : tout est scripté + vérifié. + +## 3) Workflow Git “pro” (main protégé) +### 3.1 Cycle standard (toute modif) +en bash : + +git checkout main +git pull --ff-only + +BR="chore/xxx-$(date +%Y%m%d)" +git checkout -b "$BR" + +# dev… +npm i +npm run build +npm run test:anchors + +git add -A +git commit -m "xxx: description claire" +git push -u origin "$BR" + +### 3.2 PR vers main + +Ouvrir PR dans Gitea + +CI doit être verte + +Merge PR → main + +### 3.3 Cas spécial : hotfix prod (NAS) + +On peut faire un hotfix “urgence” en prod/staging si nécessaire… + +MAIS : l’état final doit revenir dans Gitea : branche → PR → CI → merge. + +## 4) Déploiement (NAS) — principe +### 4.1 Release pack + +On génère un pack “reproductible” (source + config + scripts) puis on déploie. + +### 4.2 Blue/Green + +web_blue = staging upstream (8081) + +web_green = live upstream (8082) + +Edge Traefik sélectionne quel host pointe vers quel upstream. + +## 5) Check-list “≤ 10 commandes” (happy path complet) +### 5.1 DEV (Mac) + +git checkout main && git pull --ff-only +git checkout -b chore/my-change-$(date +%Y%m%d) + +npm i +rm -rf .astro node_modules/.vite dist +npm run build +npm run test:anchors +npm run dev + +### 5.2 Push + PR + +git add -A +git commit -m "chore: my change" +git push -u origin chore/my-change-YYYYMMDD +# ouvrir PR dans Gitea + +### 5.3 Déploiement NAS (résumé) + +Voir docs/runbooks/DEPLOY-BLUE-GREEN.md. + +## 6) Problèmes “classiques” + diagnostic rapide +### 6.1 “Le staging ne ressemble pas au local” + +# Comparer upstream direct 8081 vs 8082 : + +curl -sS http://127.0.0.1:8081/ | head -n 2 +curl -sS http://127.0.0.1:8082/ | head -n 2 + +# Vérifier quel routeur edge répond (header diag) : + +curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/ \ + | grep -iE 'HTTP/|location:|x-archi-router' + +# Lire docs/runbooks/EDGE-TRAEFIK.md. + +### 6.2 Canonical incorrect (localhost en prod) + +Cause racine : site dans Astro = PUBLIC_SITE non injecté au build. + +Fix canonique : voir docs/runbooks/ENV-PUBLIC_SITE.md. + +Test : + +curl -sS http://127.0.0.1:8082/ | grep -oE 'rel="canonical" href="[^"]+"' | head -1 + +### 6.3 Contrat “anchors” en échec après migration d’URL + +Quand on déplace des routes (ex: /archicratie/archicrat-ia/* → /archicrat-ia/*), le test d’ancres peut échouer même si les IDs n’ont pas changé, car les pages ont changé de chemin. + +# Procédure safe : + +Backup baseline : + +cp -a tests/anchors-baseline.json /tmp/anchors-baseline.json.bak.$(date +%F-%H%M%S) + +Mettre à jour les clés (chemins) sans toucher aux IDs : + +node - <<'NODE' +import fs from 'fs'; +const p='tests/anchors-baseline.json'; +const j=JSON.parse(fs.readFileSync(p,'utf8')); +const out={}; +for (const [k,v] of Object.entries(j)) { + const nk = k.replace(/^archicratie\/archicrat-ia\//, 'archicrat-ia/'); + out[nk]=v; +} +fs.writeFileSync(p, JSON.stringify(out,null,2)+'\n'); +console.log('updated keys:', Object.keys(j).length, '->', Object.keys(out).length); +NODE + +Re-run : + +npm run test:anchors + +## 7) Ce que l’étape 9 doit faire (orientation) + +Stabiliser le pipeline “tickets → YAML annotations” + +Formaliser la spec YAML + merge + anti-doublon (voir docs/EDITORIAL-ANNOTATIONS-SPEC.md) + +Durcir l’onboarding (ce START-HERE + runbooks) + +Éviter les régressions par tests (anchors / annotations / smoke) \ No newline at end of file diff --git a/docs/runbooks/DEPLOY-BLUE-GREEN.md b/docs/runbooks/DEPLOY-BLUE-GREEN.md new file mode 100644 index 0000000..4e14ba4 --- /dev/null +++ b/docs/runbooks/DEPLOY-BLUE-GREEN.md @@ -0,0 +1,202 @@ +# RUNBOOK — Déploiement Blue/Green (NAS DS220+) +> Objectif : déployer une release **sans casser**, avec rollback immédiat. + +## 0) Portée +Ce runbook décrit le déploiement de l’édition web Archicratie sur NAS (Synology), en mode blue/green : +- `web_blue` : upstream staging → `127.0.0.1:8081` +- `web_green` : upstream live → `127.0.0.1:8082` +- Edge Traefik publie : + - `staging.archicratie.trans-hands.synology.me` → 8081 + - `archicratie.trans-hands.synology.me` → 8082 + +## 1) Pré-requis +- Accès shell NAS (user `archicratia`) + `sudo` +- Docker Compose Synology nécessite souvent : + - `sudo env DOCKER_API_VERSION=1.43 docker compose ...` +- Les fichiers edge Traefik sont dans : + - `/volume2/docker/edge/config/dynamic/` + +## 2) Répertoires canon (NAS) +On considère ces chemins (adapter si besoin, mais rester cohérent) : +- Base : `/volume2/docker/archicratie-web` +- Releases : `/volume2/docker/archicratie-web/releases/YYYYMMDD-HHMMSS/app` +- Symlink actif : `/volume2/docker/archicratie-web/current` → pointe vers le `.../app` actif + +## 3) Garde-fous (AVANT toute action) +### 3.1 Snapshot de l’état actuel +en bash : + +cd /volume2/docker/archicratie-web +ls -la current || true +readlink current || true + +### 3.2 Vérifier l’état live/staging upstream direct + +curl -sSI http://127.0.0.1:8081/ | head -n 12 +curl -sSI http://127.0.0.1:8082/ | head -n 12 + +### 3.3 Vérifier l’état edge (host routing) + +curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/ \ + | grep -iE 'HTTP/|location:|x-archi-router' | head -n 30 + +curl -sSI -H 'Host: archicratie.trans-hands.synology.me' http://127.0.0.1:18080/ \ + | grep -iE 'HTTP/|location:|x-archi-router' | head -n 30 + + Si tu n’es pas authentifié, tu verras un 302 vers auth... : c’est normal. + +## 4) Procédure de déploiement (release pack → nouvelle release) +### 4.1 Déposer le pack + +Hypothèse : tu as un .tgz “release pack” (issu de release-pack.sh) dans incoming/ : + +cd /volume2/docker/archicratie-web +ls -la incoming | tail -n 20 + +### 4.2 Créer un répertoire release + +TS="$(date +%Y%m%d-%H%M%S)" +REL="/volume2/docker/archicratie-web/releases/$TS" +APP="$REL/app" +sudo mkdir -p "$APP" + +### 4.3 Extraire le pack + +PKG="/volume2/docker/archicratie-web/incoming/archicratie-web.tar.gz" # adapter au nom réel +sudo tar -xzf "$PKG" -C "$APP" + +### 4.4 Sanity check (fichiers attendus) + +sudo test -f "$APP/Dockerfile" && echo "OK Dockerfile" +sudo test -f "$APP/docker-compose.yml" && echo "OK compose" +sudo test -f "$APP/astro.config.mjs" && echo "OK astro config" +sudo test -f "$APP/src/layouts/EditionLayout.astro" && echo "OK layout" +sudo test -f "$APP/src/pages/archicrat-ia/index.astro" && echo "OK archicrat-ia index" +sudo test -f "$APP/docs/diagrams/archicratie-web-edition-global-verbatim-v2.svg" && echo "OK diagrams" + +### 4.5 Permissions (crucial sur Synology) + +But : archicratia:users doit pouvoir traverser le parent + lire le contenu. + +sudo chown -R archicratia:users "$REL" +sudo chmod -R u+rwX,g+rX,o-rwx "$REL" +sudo chmod 750 "$REL" "$APP" + +Vérifier : + +ls -ld "$REL" "$APP" +ls -la "$APP" | head + +## 5) Activation : basculer current vers la nouvelle release +### 5.1 Backup du current existant + +cd /volume2/docker/archicratie-web +TS2="$(date +%F-%H%M%S)" + +# on backup "current" (symlink ou dossier) +if [ -e current ] || [ -L current ]; then + sudo mv -f current "current.BAK.$TS2" + echo "✅ backup: current.BAK.$TS2" +fi + +### 5.2 Recréer current (symlink propre) + +sudo ln -s "$APP" current + +ls -la current +readlink current +sudo test -f current/docker-compose.yml && echo "✅ OK: current/docker-compose.yml" + +Si cd current échoue, c’est que current n’est pas un symlink correct OU que le parent n’est pas traversable (permissions). + +## 6) Build & run : (re)construire web_blue/web_green +### 6.1 Vérifier la config compose + +cd /volume2/docker/archicratie-web/current +sudo env DOCKER_API_VERSION=1.43 docker compose -f docker-compose.yml config \ + | grep -nE 'services:|web_blue:|web_green:|context:|dockerfile:|PUBLIC_SITE|REQUIRE_PUBLIC_SITE' \ + | sed -n '1,220p' + +### 6.2 Build propre (recommandé si changement de code/config) + +sudo env DOCKER_API_VERSION=1.43 docker compose build --no-cache web_blue web_green + +### 6.3 Up (force recreate) + +sudo env DOCKER_API_VERSION=1.43 docker compose up -d --force-recreate web_blue web_green + +### 6.4 Vérifier upstream direct (8081/8082) + +curl -sSI http://127.0.0.1:8081/ | head -n 12 +curl -sSI http://127.0.0.1:8082/ | head -n 12 + +## 7) Tests de non-régression (MINIMAL CHECKLIST) + +À exécuter systématiquement après up. + +### 7.1 Upstreams directs + +curl -sSI http://127.0.0.1:8081/ | head -n 12 +curl -sSI http://127.0.0.1:8082/ | head -n 12 + +### 7.2 Canonical (anti “localhost en prod”) + +curl -sS http://127.0.0.1:8081/ | grep -oE 'rel="canonical" href="[^"]+"' | head -n 1 +curl -sS http://127.0.0.1:8082/ | grep -oE 'rel="canonical" href="[^"]+"' | head -n 1 + +Attendu : + +blue (8081) → https://staging.archicratie.../ + +green (8082) → https://archicratie.../ + +### 7.3 Edge routing (Host header + diag) + +curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/ \ + | grep -iE 'HTTP/|location:|x-archi-router' | head -n 30 + +curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/_auth/whoami \ + | grep -iE 'HTTP/|location:|x-archi-router' | head -n 30 + +### 7.4 Smoke UI (manuel) + +Home : lien “Essai-thèse — ArchiCraT-IA” → /archicrat-ia/ + +TOC global : liens /archicrat-ia/* (pas de préfixe /archicratie/archicrat-ia/*) + +Reading-follow/TOC local : scroll ok + +## 8) Rollback (si un seul test est mauvais) + +Objectif : revenir immédiatement à l’état précédent. + +### 8.1 Repointer current sur l’ancien backup + +cd /volume2/docker/archicratie-web +ls -la current.BAK.* | tail -n 5 + +# choisir le plus récent +OLD="current.BAK.YYYY-MM-DD-HHMMSS" +sudo rm -f current +sudo ln -s "$(readlink -f "$OLD")" current 2>/dev/null || sudo ln -s "$(readlink "$OLD")" current + +ls -la current +readlink current + +### 8.2 Rebuild + recreate + +cd /volume2/docker/archicratie-web/current +sudo env DOCKER_API_VERSION=1.43 docker compose build --no-cache web_blue web_green +sudo env DOCKER_API_VERSION=1.43 docker compose up -d --force-recreate web_blue web_green + +### 8.3 Re-tester la checklist (section 7) + +Si rollback OK : investiguer en environnement isolé (staging upstream uniquement, ou release dans un autre current). + +## 9) Notes opérationnelles + +Ne jamais modifier dist/ “à la main” sur NAS. + +Si un hotfix prod est indispensable : documenter et backporter via PR Gitea. + +Le canonical dépend du build : PUBLIC_SITE doit être injecté (voir runbook ENV-PUBLIC_SITE). \ No newline at end of file diff --git a/docs/runbooks/EDGE-TRAEFIK.md b/docs/runbooks/EDGE-TRAEFIK.md new file mode 100644 index 0000000..8118d91 --- /dev/null +++ b/docs/runbooks/EDGE-TRAEFIK.md @@ -0,0 +1,147 @@ +# RUNBOOK — Edge Traefik (routing + SSO Authelia) +> Objectif : comprendre et diagnostiquer rapidement qui route quoi, et pourquoi staging/live peuvent diverger. + +## 0) Portée +Edge Traefik route plusieurs hosts vers des backends locaux (127.0.0.1:*), avec Auth via Authelia. + +Répertoire : +- `/volume2/docker/edge/config/dynamic/` + +Port d’entrée edge : +- `http://127.0.0.1:18080/` (entryPoint `web`) +- Les hosts publics pointent vers cet edge. + +## 1) Fichiers dynamiques (canon) +### 00-smoke.yml +- route `/__smoke` vers le service `smoke_svc` → `127.0.0.1:18081` + +### 10-core.yml +- définit les middlewares : + - `sanitize-remote` + - `authelia` (forwardAuth vers 9091) + - `chain-auth` (chain sanitize-remote + authelia) + +### 20-archicratie-backend.yml +- définit service `archicratie_web` → `127.0.0.1:8082` (live upstream) + +### 21-archicratie-staging.yml +- route staging host vers `127.0.0.1:8081` (staging upstream) +- applique middlewares `diag-staging@file` et `chain-auth@file` +- IMPORTANT : `diag-staging@file` doit exister + +### 22-archicratie-authinfo-staging.yml +- route `/ _auth /` sur staging vers `whoami@file` +- applique `diag-staging-authinfo@file` + `chain-auth@file` +- IMPORTANT : `diag-staging-authinfo@file` doit exister + +### 90-overlay-staging-fix.yml (overlay de diagnostic + fallback) +Rôle : +- **fournir** les middlewares manquants (`diag-staging`, `diag-staging-authinfo`) +- optionnel : fallback route si 21/22 sont cassés +- injecter un header `X-Archi-Router` pour identifier le routeur utilisé + +### 92-overlay-live-fix.yml +- route live host `archicratie.trans-hands.synology.me` → `archicratie_web@file` (8082) +- route `/ _auth/whoami` → `whoami@file` (18081) + +## 2) Diagnostiquer rapidement : quel routeur répond ? +### 2.1 Test “host header” (sans UI) +# en bash : + +curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/ \ + | grep -iE 'HTTP/|location:|x-archi-router' | head -n 30 + +curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/_auth/whoami \ + | grep -iE 'HTTP/|location:|x-archi-router' | head -n 30 + +# Interprétation : + +X-Archi-Router: staging@21 → routeur 21-archicratie-staging.yml OK + +X-Archi-Router: staging-authinfo@22 → routeur authinfo OK + +Si tu vois staging-fallback@90 → tu es tombé sur le fallback 90 (donc 21/22 potentiellement invalides) + +### 2.2 Vérifier l’upstream direct derrière edge + +curl -sSI http://127.0.0.1:8081/ | head -n 12 +curl -sSI http://127.0.0.1:8082/ | head -n 12 + +Si 8081 et 8082 servent des versions différentes : c’est “normal” en blue/green, mais il faut savoir laquelle est censée être staging/live. + +## 3) Diagnostiquer les erreurs Traefik (fichier invalide / middleware manquant) +### 3.1 Grep “level=error” + +sudo docker logs edge-traefik --since 5m | grep -Ei 'level=error|middleware|router|service|yaml' | tail -n 80 + +# Cas typique : + +middleware "diag-staging@file" does not exist +→ 21-archicratie-staging.yml référence un middleware absent. Solution : le définir (souvent dans 90-overlay-staging-fix.yml). + +## 4) Procédure safe de modification (jamais en aveugle) +### 4.1 Backup + +cd /volume2/docker/edge/config/dynamic +TS="$(date +%F-%H%M%S)" +sudo cp -a 90-overlay-staging-fix.yml "90-overlay-staging-fix.yml.bak.$TS" + +### 4.2 Édition (ex : ajouter middlewares diag) + +Faire une modif minimale + +Ne pas casser les règles existantes (Host + PathPrefix) + +Respecter les priorités (voir section 5) + +### 4.3 Reload Traefik + +sudo docker restart edge-traefik + +### 4.4 Tests immédiats + +curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/ \ + | grep -iE 'HTTP/|location:|x-archi-router' + +curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/_auth/whoami \ + | grep -iE 'HTTP/|location:|x-archi-router' + +## 5) Priorités Traefik (le point subtil) + +Traefik choisit le routeur selon : + +la correspondance de règle + +la priority (plus grand gagne) + +en cas d’égalité, l’ordre interne (à éviter) + +### 5.1 Canon pour staging + +21-archicratie-staging.yml : priority 10 + +22-archicratie-authinfo-staging.yml : priority 10000 + +90-overlay-staging-fix.yml : + +fallback host : priority faible (ex: 5) pour ne PAS écraser 21 + +fallback whoami : priority < 10000 (ex: 9000) pour ne PAS écraser 22 + +=> On garde 90 comme filet de sécurité / diag, pas comme “source”. + +## 6) Rollback (si un changement edge casse staging/live) + +cd /volume2/docker/edge/config/dynamic +# choisir le bon backup +sudo mv -f 90-overlay-staging-fix.yml "90-overlay-staging-fix.yml.BAD.$(date +%F-%H%M%S)" +sudo cp -a 90-overlay-staging-fix.yml.bak.YYYY-MM-DD-HHMMSS 90-overlay-staging-fix.yml +sudo docker restart edge-traefik + +Puis re-tests section 2. + +## 7) Remarques + +Les 302 Authelia sont normaux si non authentifié. + +Un 404 “Not Found” depuis edge alors que 8081 répond : souvent routeur manquant / invalidé / middleware absent. \ No newline at end of file diff --git a/docs/runbooks/ENV-PUBLIC_SITE.md b/docs/runbooks/ENV-PUBLIC_SITE.md new file mode 100644 index 0000000..61a6097 --- /dev/null +++ b/docs/runbooks/ENV-PUBLIC_SITE.md @@ -0,0 +1,114 @@ +# RUNBOOK — PUBLIC_SITE (canonical + sitemap) “anti localhost en prod” +> Objectif : ne plus jamais voir `rel="canonical" href="http://localhost:4321/"` en staging/live. + +## 0) Pourquoi c’est critique +Astro génère : +- `` +- `sitemap-index.xml` + +Ces valeurs dépendent de `site` dans `astro.config.mjs`. + +Si `site` vaut `http://localhost:4321` au moment du build Docker, **la prod sortira des canonical faux** : +- SEO / partage / cohérence de navigation impactés +- confusion staging/live + +## 1) Règle canonique +- `astro.config.mjs` : +# en js : + +site: process.env.PUBLIC_SITE ?? "http://localhost:4321" + +# Donc : + +En DEV local : pas besoin de PUBLIC_SITE (fallback ok) + +En build “déploiement” : on DOIT fournir PUBLIC_SITE + +## 2) Exigence “antifragile” +### 2.1 Dockerfile (build stage) + +On injecte PUBLIC_SITE au build et on peut le rendre obligatoire : + +ARG PUBLIC_SITE + +ARG REQUIRE_PUBLIC_SITE=0 + +ENV PUBLIC_SITE=$PUBLIC_SITE + +# garde-fou : + +RUN if [ "$REQUIRE_PUBLIC_SITE" = "1" ] && [ -z "$PUBLIC_SITE" ]; then \ + echo "ERROR: PUBLIC_SITE is required (REQUIRE_PUBLIC_SITE=1)"; exit 1; \ + fi + +=> Si quelqu’un oublie l’URL en prod, le build casse au lieu de produire une release mauvaise. + +## 3) docker-compose : blue/staging vs green/live + +Objectif : injecter deux valeurs différentes, sans bricolage. + +### 3.1 .env (NAS) + +Exemple canonique : + +PUBLIC_SITE_BLUE=https://staging.archicratie.trans-hands.synology.me +PUBLIC_SITE_GREEN=https://archicratie.trans-hands.synology.me + +### 3.2 docker-compose.yml + +web_blue : + +REQUIRE_PUBLIC_SITE: "1" + +PUBLIC_SITE: ${PUBLIC_SITE_BLUE} + +web_green : + +REQUIRE_PUBLIC_SITE: "1" + +PUBLIC_SITE: ${PUBLIC_SITE_GREEN} + +## 4) Tests (obligatoires après build) +### 4.1 Vérifier l’injection dans compose + +sudo env DOCKER_API_VERSION=1.43 docker compose config \ + | grep -nE 'PUBLIC_SITE|REQUIRE_PUBLIC_SITE|web_blue:|web_green:' | sed -n '1,200p' + +### 4.2 Vérifier canonical (upstream direct) + +curl -sS http://127.0.0.1:8081/ | grep -oE 'rel="canonical" href="[^"]+"' | head -n 1 +curl -sS http://127.0.0.1:8082/ | grep -oE 'rel="canonical" href="[^"]+"' | head -n 1 + +# Attendu : + +blue : https://staging.../ + +green : https://archicratie.../ + +## 5) Procédure de correction (si canonical est faux) +### 5.1 Vérifier astro.config.mjs dans la release courante + +cd /volume2/docker/archicratie-web/current +grep -nE 'site:\s*process\.env\.PUBLIC_SITE' astro.config.mjs + +### 5.2 Vérifier que Dockerfile exporte PUBLIC_SITE + +grep -nE 'ARG PUBLIC_SITE|ENV PUBLIC_SITE|REQUIRE_PUBLIC_SITE' Dockerfile + +### 5.3 Vérifier .env et compose + +grep -nE 'PUBLIC_SITE_BLUE|PUBLIC_SITE_GREEN' .env +grep -nE 'PUBLIC_SITE|REQUIRE_PUBLIC_SITE' docker-compose.yml + +### 5.4 Rebuild + recreate + +sudo env DOCKER_API_VERSION=1.43 docker compose build --no-cache web_blue web_green +sudo env DOCKER_API_VERSION=1.43 docker compose up -d --force-recreate web_blue web_green + +Puis tests section 4. + +## 6) Notes + +Cette mécanique doit être backportée dans Gitea (source canonique), sinon ça re-cassera au prochain pack. + +En DEV local, conserver le fallback http://localhost:4321 est utile et normal. \ No newline at end of file