Compare commits
24 Commits
docs/ops-r
...
docs/RUNBO
| Author | SHA1 | Date | |
|---|---|---|---|
| ab63511d81 | |||
| e5d831cb61 | |||
| cab9e9cf2d | |||
| c7704ada8a | |||
| 010601be63 | |||
| add688602a | |||
| 3f3c717185 | |||
| b5f32da0c8 | |||
| 6e7ed8e041 | |||
| 90f79a7ee7 | |||
| b5663891a1 | |||
| a74b95e775 | |||
| b78eb4fc7b | |||
| 80c047369f | |||
| d7c158a0fc | |||
| 30f0ef4164 | |||
| d2963673c9 | |||
| d59e10dfc6 | |||
| 214f930e56 | |||
| 9e903607bb | |||
| 8b7cfdfd48 | |||
| c12b6015ab | |||
| f7f6b8f770 | |||
| ae8ec42349 |
3
.env
3
.env
@@ -1,3 +0,0 @@
|
|||||||
PUBLIC_GITEA_BASE=https://gitea.archicratie.trans-hands.synology.me
|
|
||||||
PUBLIC_GITEA_OWNER=Archicratia
|
|
||||||
PUBLIC_GITEA_REPO=archicratie-edition
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
PUBLIC_GITEA_BASE=https://gitea.archicratie.trans-hands.synology.me
|
|
||||||
PUBLIC_GITEA_OWNER=Archicratia
|
|
||||||
PUBLIC_GITEA_REPO=archicratie-edition
|
|
||||||
PUBLIC_SITE=https://archicratie.trans-hands.synology.me
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
FORGE_API=http://192.168.1.20:3000
|
|
||||||
FORGE_BASE=https://gitea.archicratie.trans-hands.synology.me
|
|
||||||
FORGE_TOKEN=aW73wpfJ4MiN2!3UU69qL*vWF9$9V7f@2
|
|
||||||
PUBLIC_GITEA_OWNER=Archicratia
|
|
||||||
PUBLIC_GITEA_REPO=archicratie-edition
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
PUBLIC_SITE=https://archicratie.trans-hands.synology.me
|
|
||||||
PUBLIC_RELEASE=0.1.0
|
|
||||||
|
|
||||||
PUBLIC_GITEA_BASE=https://gitea.archicratie.trans-hands.synology.me
|
|
||||||
PUBLIC_GITEA_OWNER=Archicratia
|
|
||||||
PUBLIC_GITEA_REPO=archicratie-edition
|
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
# `docs/CONFIG-ENV.md`
|
|
||||||
|
|
||||||
```md
|
|
||||||
# CONFIG-ENV — variables, priorités, injection build
|
# CONFIG-ENV — variables, priorités, injection build
|
||||||
|
|
||||||
## 0) Ce que la prod doit garantir
|
## 0) Ce que la prod doit garantir
|
||||||
@@ -13,6 +10,15 @@ Si un seul de ces 3 paramètres est faux → on obtient :
|
|||||||
- 404 / redirect login inattendu
|
- 404 / redirect login inattendu
|
||||||
- ou un repo/owner incorrect
|
- ou un repo/owner incorrect
|
||||||
|
|
||||||
|
|
||||||
|
# Diagnostic — “Proposer” (résumé)
|
||||||
|
|
||||||
|
**Symptôme :** clic “Proposer” → 404 / login / mauvais repo
|
||||||
|
**Cause la plus fréquente :** `PUBLIC_GITEA_OWNER` (casse sensible) ou `PUBLIC_GITEA_REPO` faux.
|
||||||
|
|
||||||
|
➡️ Procédure complète (pas-à-pas + commandes) : voir `docs/TROUBLESHOOTING.md#proposer-404`.
|
||||||
|
|
||||||
|
|
||||||
## 1) Variables utilisées (publique, côté build Astro)
|
## 1) Variables utilisées (publique, côté build Astro)
|
||||||
|
|
||||||
- `PUBLIC_GITEA_BASE`
|
- `PUBLIC_GITEA_BASE`
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
# Déploiement production (Synology DS220+ / DSM 7.3) — Astro → Nginx statique
|
# Déploiement production (Synology DS220+ / DSM 7.3) — Astro → Nginx statique
|
||||||
|
|
||||||
|
> ✅ **CANONIQUE** — Procédure de référence “prod DS220+ / DSM 7.3”.
|
||||||
|
|
||||||
|
> Toute modif de déploiement doit être faite **ici**, via PR sur Gitea/main (pas d’édition à la main en prod).
|
||||||
|
|
||||||
|
> Périmètre : build Docker (Node→Nginx), blue/green 8081/8082, Reverse Proxy DSM, smoke, rollback.
|
||||||
|
|
||||||
|
> Dépendances critiques : variables PUBLIC_GITEA_* (sinon “Proposer” part en 404/login loop).
|
||||||
|
|
||||||
|
> Voir aussi : OPS-REFERENCE.md (index), OPS_COCKPIT.md (checklist), TROUBLESHOOTING.md (incidents).
|
||||||
|
|
||||||
Dernière mise à jour : 2026-02-01
|
Dernière mise à jour : 2026-02-01
|
||||||
|
|
||||||
Ce document décrit une mise en place stable sur NAS :
|
Ce document décrit une mise en place stable sur NAS :
|
||||||
@@ -73,6 +83,17 @@ for f in .env .env.local .env.production .env.production.local; do
|
|||||||
[ -f "$f" ] && echo "---- $f" && grep -nE '^PUBLIC_GITEA_(BASE|OWNER|REPO)=' "$f" || true
|
[ -f "$f" ] && echo "---- $f" && grep -nE '^PUBLIC_GITEA_(BASE|OWNER|REPO)=' "$f" || true
|
||||||
done
|
done
|
||||||
|
|
||||||
|
En cas d’échec :
|
||||||
|
- 404 / login loop / mauvais repo → `docs/TROUBLESHOOTING.md#proposer-404`
|
||||||
|
- double onglet → `docs/TROUBLESHOOTING.md#proposer-double-onglet`
|
||||||
|
|
||||||
|
## Diagnostic — “Proposer” (résumé)
|
||||||
|
|
||||||
|
**Symptôme :** clic “Proposer” → 404 / login / mauvais repo
|
||||||
|
**Cause la plus fréquente :** `PUBLIC_GITEA_OWNER` (casse sensible) ou `PUBLIC_GITEA_REPO` faux.
|
||||||
|
|
||||||
|
➡️ Procédure complète (pas-à-pas + commandes) : voir `docs/TROUBLESHOOTING.md#proposer-404`.
|
||||||
|
|
||||||
## 5) Reverse Proxy DSM (le point clé)
|
## 5) Reverse Proxy DSM (le point clé)
|
||||||
|
|
||||||
### DSM 7.3 :
|
### DSM 7.3 :
|
||||||
|
|||||||
57
docs/FEATURE-PROPOSER.md
Normal file
57
docs/FEATURE-PROPOSER.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# FEATURE — “Proposer” (édition par paragraphe → issue Gitea)
|
||||||
|
|
||||||
|
Dernière mise à jour : 2026-02-01
|
||||||
|
|
||||||
|
Cette feature permet à un lecteur de proposer une correction/amélioration d’un paragraphe, en générant une issue pré-remplie dans Gitea.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0) Objectif fonctionnel
|
||||||
|
Depuis une page chapitre :
|
||||||
|
1) clic sur **Proposer** sur un paragraphe
|
||||||
|
2) choix #1 (type)
|
||||||
|
3) choix #2 (state/catégorie selon UI)
|
||||||
|
4) ouverture d’un seul onglet vers Gitea : `/issues/new?...`
|
||||||
|
5) issue pré-remplie avec :
|
||||||
|
- chemin / URL / ancre
|
||||||
|
- texte actuel (citation)
|
||||||
|
- champs “Proposition / Justification”
|
||||||
|
6) l’utilisateur valide, et le runner/CI traite.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1) Dépendances de configuration (critique)
|
||||||
|
|
||||||
|
Le lien Gitea est construit à partir de variables publiques injectées au build Astro :
|
||||||
|
|
||||||
|
- `PUBLIC_GITEA_BASE` (ex: `https://gitea.archicratie.trans-hands.synology.me`)
|
||||||
|
- `PUBLIC_GITEA_OWNER` (**casse sensible**, ex: `Archicratia`)
|
||||||
|
- `PUBLIC_GITEA_REPO` (ex: `archicratie-edition`)
|
||||||
|
|
||||||
|
### Symptômes si mauvais
|
||||||
|
- mauvais repo → 404
|
||||||
|
- redirect login inattendu
|
||||||
|
- création d’issues impossible
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2) Contrat “une seule ouverture d’onglet”
|
||||||
|
Le flow ne doit jamais ouvrir deux onglets.
|
||||||
|
|
||||||
|
### Contrat
|
||||||
|
- pas de `window.open(...)`
|
||||||
|
- un seul `a.target="_blank"` (ou équivalent) déclenché
|
||||||
|
- sur click : handler doit neutraliser les propagations parasites
|
||||||
|
|
||||||
|
## Diagnostic (canonique)
|
||||||
|
|
||||||
|
Le diagnostic détaillé est centralisé dans `docs/TROUBLESHOOTING.md` pour éviter les doublons.
|
||||||
|
|
||||||
|
- 404 / non autorisé / redirect login :
|
||||||
|
- voir : `TROUBLESHOOTING.md#proposer-404`
|
||||||
|
- cause la plus fréquente : `PUBLIC_GITEA_OWNER/REPO` faux (souvent casse)
|
||||||
|
|
||||||
|
- Double onglet :
|
||||||
|
- voir : `TROUBLESHOOTING.md#proposer-double-onglet`
|
||||||
|
- cause la plus fréquente : double handler (bubbling) ou `window.open` + `a.click()`
|
||||||
|
|
||||||
@@ -1,6 +1,46 @@
|
|||||||
OPS — Déploiement Archicratie Web Edition (Mac Studio → DS220+)
|
# OPS — Déploiement Archicratie Web Edition (Mac Studio → DS220+)
|
||||||
Objectif : déployer une nouvelle version du site sur le NAS (DS220+) sans jamais casser la prod, en utilisant un schéma blue/green piloté par DSM Reverse Proxy, avec une procédure robuste même quand docker compose build est instable sur le NAS.
|
Objectif : déployer une nouvelle version du site sur le NAS (DS220+) sans jamais casser la prod, en utilisant un schéma blue/green piloté par DSM Reverse Proxy, avec une procédure robuste même quand docker compose build est instable sur le NAS.
|
||||||
|
|
||||||
|
> 🟧 **LEGACY / HISTORIQUE** — Ce document n’est plus la source de vérité.
|
||||||
|
|
||||||
|
> Référence actuelle : docs/DEPLOY_PROD_SYNOLOGY_DS220.md (canonique).
|
||||||
|
|
||||||
|
> Statut : gelé (on n’édite plus que pour ajouter un lien vers le canonique, si nécessaire).
|
||||||
|
|
||||||
|
> Raison : doublon → risque de divergence → risque d’erreur en prod.
|
||||||
|
|
||||||
|
> Si tu lis ceci pour déployer : stop → ouvre le canonique.
|
||||||
|
|
||||||
|
> ⚠️ LEGACY — Ne pas suivre pour déployer.
|
||||||
|
> Doc conservé pour historique.
|
||||||
|
> Canon : `DEPLOY_PROD_SYNOLOGY_DS220.md` + `OPS-SYNC-TRIPLE-SOURCE.md`.
|
||||||
|
|
||||||
|
## Pourquoi ce doc existe encore
|
||||||
|
|
||||||
|
- Historique : il capture des repères (domaines, ports, logique blue/green) tels qu’ils ont été consolidés pendant la phase d’implémentation.
|
||||||
|
- Sécurité : éviter la divergence documentaire (un seul pas-à-pas officiel).
|
||||||
|
- Maintenance : si tu dois déployer, tu suis le canonique ; ici tu ne viens que pour comprendre “d’où ça vient”.
|
||||||
|
|
||||||
|
## Ce qu’il faut faire aujourd’hui (canonique)
|
||||||
|
|
||||||
|
➡️ Déploiement = `docs/DEPLOY_PROD_SYNOLOGY_DS220.md` (procédure détaillée, à jour).
|
||||||
|
|
||||||
|
## Schéma (résumé, sans commandes)
|
||||||
|
|
||||||
|
- Ne jamais toucher au slot live.
|
||||||
|
- Construire/tester sur l’autre slot.
|
||||||
|
- Smoke test.
|
||||||
|
- Bascule DSM Reverse Proxy (8081 ↔ 8082).
|
||||||
|
- Rollback DSM si besoin.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
> 🚫 NE PAS UTILISER POUR PROD — ARCHIVE UNIQUEMENT
|
||||||
|
|
||||||
|
<summary>Archive — ancien pas-à-pas (NE PAS SUIVRE)</summary>
|
||||||
|
|
||||||
|
> ⚠️ Archive. Ce contenu est conservé pour mémoire.
|
||||||
|
|
||||||
## 0) Repères essentiels
|
## 0) Repères essentiels
|
||||||
Noms & domaines
|
Noms & domaines
|
||||||
• Site public (prod) : https://archicratie.trans-hands.synology.me
|
• Site public (prod) : https://archicratie.trans-hands.synology.me
|
||||||
@@ -383,3 +423,5 @@ Fix standard (dans le vrai dossier site/) :
|
|||||||
rm -rf node_modules .astro dist
|
rm -rf node_modules .astro dist
|
||||||
npm ci
|
npm ci
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Document “pivot” : liens, invariants, conventions, commandes réflexes.
|
|||||||
|
|
||||||
## 0) Invariants (à ne pas casser)
|
## 0) Invariants (à ne pas casser)
|
||||||
|
|
||||||
- **Source de vérité Git** : `origin/main` sur :contentReference[oaicite:0]{index=0}.
|
- **Source de vérité Git** : origin/main (repo Archicratia/archicratie-edition sur Gitea).
|
||||||
- **Prod** : conteneur `archicratie-web-*` (nginx) derrière reverse proxy DSM.
|
- **Prod** : conteneur `archicratie-web-*` (nginx) derrière reverse proxy DSM.
|
||||||
- **Config “Proposer”** : dépend de `PUBLIC_GITEA_BASE`, `PUBLIC_GITEA_OWNER`, `PUBLIC_GITEA_REPO` injectés au build.
|
- **Config “Proposer”** : dépend de `PUBLIC_GITEA_BASE`, `PUBLIC_GITEA_OWNER`, `PUBLIC_GITEA_REPO` injectés au build.
|
||||||
- **Branches** : `main` = travail ; `master` = legacy/compat (alignée mais protégée).
|
- **Branches** : `main` = travail ; `master` = legacy/compat (alignée mais protégée).
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
# OPS Runbook — Archicratie Web (NAS Synology DS220 + Gitea)
|
# OPS Runbook — Archicratie Web (NAS Synology DS220 + Gitea)
|
||||||
|
|
||||||
|
> 🟦 **ALIAS (résumé)** — Runbook 1-page pour opérer vite sans se tromper.
|
||||||
|
|
||||||
|
> La procédure détaillée **canonique** est : docs/DEPLOY_PROD_SYNOLOGY_DS220.md.
|
||||||
|
|
||||||
|
> Source Git : Gitea/main ; déploiement = rebuild depuis main ; pas de hotfix non versionné.
|
||||||
|
|
||||||
|
> Déploiement : build sur slot inactif → smoke → bascule DSM → rollback si besoin.
|
||||||
|
|
||||||
|
> Incidents connus : voir docs/TROUBLESHOOTING.md.
|
||||||
|
|
||||||
## 0. Objectif
|
## 0. Objectif
|
||||||
Ce document décrit la procédure **exacte** pour :
|
Ce document décrit la procédure **exacte** pour :
|
||||||
- maintenir un état cohérent entre **Local (Mac Studio)**, **Gitea**, **NAS (prod)** ;
|
- maintenir un état cohérent entre **Local (Mac Studio)**, **Gitea**, **NAS (prod)** ;
|
||||||
|
|||||||
122
docs/OPS-SYNC-TRIPLE-SOURCE.md
Normal file
122
docs/OPS-SYNC-TRIPLE-SOURCE.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# OPS-SYNC-TRIPLE-SOURCE — Mac Studio / Gitea / NAS (prod)
|
||||||
|
|
||||||
|
Dernière mise à jour : 2026-02-01
|
||||||
|
|
||||||
|
Ce document décrit la synchronisation **sans ambiguïté** entre :
|
||||||
|
- **Local (Mac Studio)** : édition / écriture / préparation PR
|
||||||
|
- **Gitea** : **vérité canonique** (branche `main`)
|
||||||
|
- **NAS Synology DS220+** : déploiement (blue/green) à partir de `main`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0) Invariants (à ne jamais violer)
|
||||||
|
|
||||||
|
1) **Gitea `main` = source de vérité.**
|
||||||
|
2) Le NAS ne doit pas “inventer” du code : pas d’édition manuelle non versionnée en prod (sauf hotfix temporaire immédiatement reporté dans une PR).
|
||||||
|
3) Le bouton “Proposer” dépend de `PUBLIC_GITEA_*` : une valeur fausse → 404 / redirect login / mauvais repo.
|
||||||
|
4) Les secrets (tokens) **ne doivent jamais** entrer dans le repo : `.env*` ignorés, token injecté uniquement via variable d’environnement locale/CI.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1) Topologie réelle (ce que nous avons)
|
||||||
|
|
||||||
|
### 1.1 Local (Mac Studio)
|
||||||
|
- Dev et documentation.
|
||||||
|
- Git complet.
|
||||||
|
- On fait : branches, commits, push, PR, merge.
|
||||||
|
|
||||||
|
### 1.2 Gitea
|
||||||
|
- Repo canonique : `Archicratia/archicratie-edition`.
|
||||||
|
- `main` = défaut + protégée.
|
||||||
|
- Toute modif arrive via PR.
|
||||||
|
|
||||||
|
### 1.3 NAS (prod)
|
||||||
|
- Chemin canonique :
|
||||||
|
- `/volume2/docker/archicratie-web/releases/<timestamp>/app`
|
||||||
|
- `/volume2/docker/archicratie-web/current` → symlink vers la release active
|
||||||
|
- Blue/Green :
|
||||||
|
- `web_blue` sur `127.0.0.1:8081`
|
||||||
|
- `web_green` sur `127.0.0.1:8082`
|
||||||
|
- Reverse proxy DSM : bascule 8081 ↔ 8082.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2) Règle d’or : qui écrit quoi, où ?
|
||||||
|
|
||||||
|
### 2.1 Toute écriture “source” se fait sur Mac Studio
|
||||||
|
- Code Astro
|
||||||
|
- Scripts
|
||||||
|
- Docs `docs/*.md`
|
||||||
|
- `.gitignore`
|
||||||
|
|
||||||
|
### 2.2 Gitea ne reçoit que via PR
|
||||||
|
- Push sur branche feature/docs
|
||||||
|
- PR → CI → merge
|
||||||
|
|
||||||
|
### 2.3 NAS ne fait que :
|
||||||
|
- `git reset --hard origin/main` (alignement)
|
||||||
|
- build image + restart slot blue/green
|
||||||
|
- smoke test
|
||||||
|
- bascule reverse proxy DSM
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3) Procédure standard (la seule à utiliser)
|
||||||
|
|
||||||
|
### Étape A — Mac Studio → Gitea (PR)
|
||||||
|
1) `git checkout -b feat/...` ou `docs/...`
|
||||||
|
2) commits propres et atomiques
|
||||||
|
3) `git push -u origin <branch>`
|
||||||
|
4) PR dans Gitea → CI OK → merge dans `main`
|
||||||
|
|
||||||
|
### Étape B — NAS : aligner `current` sur `origin/main`
|
||||||
|
Sur NAS, git n’est pas forcément installé : on utilise un conteneur git.
|
||||||
|
en sh :
|
||||||
|
|
||||||
|
APP="/volume2/docker/archicratie-web/current"
|
||||||
|
U_ID="$(id -u)"; G_ID="$(id -g)"
|
||||||
|
|
||||||
|
sudo docker run --rm --network host \
|
||||||
|
-u "$U_ID:$G_ID" -e HOME=/tmp \
|
||||||
|
-v "$APP":/repo -w /repo \
|
||||||
|
--entrypoint sh alpine/git -lc '
|
||||||
|
set -eu
|
||||||
|
git config --global --add safe.directory /repo
|
||||||
|
git config http.sslVerify false
|
||||||
|
|
||||||
|
git fetch origin --prune
|
||||||
|
git checkout -B main
|
||||||
|
git reset --hard origin/main
|
||||||
|
git status -sb
|
||||||
|
'
|
||||||
|
|
||||||
|
### Étape C — NAS : rebuild du slot inactif + smoke + bascule
|
||||||
|
|
||||||
|
Rebuild de l’image (slot inactif recommandé).
|
||||||
|
|
||||||
|
docker compose up -d --force-recreate --no-build web_green (ou blue)
|
||||||
|
|
||||||
|
smoke test via script ou curl
|
||||||
|
|
||||||
|
bascule DSM vers le port du slot actif
|
||||||
|
|
||||||
|
## 4) Checkpoints rapides (sanity)
|
||||||
|
### 4.1 Vérifier que NAS = origin/main
|
||||||
|
|
||||||
|
git rev-parse --short HEAD sur NAS (via alpine/git)
|
||||||
|
|
||||||
|
doit égaler origin/main.
|
||||||
|
|
||||||
|
### 4.2 Vérifier “Proposer” (points minimum)
|
||||||
|
|
||||||
|
PUBLIC_GITEA_OWNER=Archicratia (casse sensible)
|
||||||
|
|
||||||
|
PUBLIC_GITEA_REPO=archicratie-edition
|
||||||
|
|
||||||
|
Flow : Proposer → choix 1 → choix 2 → onglet Gitea /issues/new?... OK
|
||||||
|
|
||||||
|
## 5) Rollback
|
||||||
|
|
||||||
|
DSM reverse proxy : repasser sur l’autre port (8081/8082).
|
||||||
|
|
||||||
|
En cas de code cassé : réaligner NAS sur origin/main précédent (tag/release) ou repointer /current vers une release précédente.
|
||||||
122
docs/RUNBOOK-PR-AUTO-GITEA.md
Normal file
122
docs/RUNBOOK-PR-AUTO-GITEA.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# RUNBOOK — Créer une Demande d’ajout (PR) “automatique” depuis un push (Gitea)
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
Pousser une branche depuis le Mac vers Gitea et obtenir le workflow standard :
|
||||||
|
1) branche dédiée
|
||||||
|
2) push
|
||||||
|
3) suggestion “Nouvelle demande d’ajout” (bandeau vert) OU lien terminal
|
||||||
|
4) création PR via UI
|
||||||
|
5) merge (main protégé)
|
||||||
|
|
||||||
|
> Important : Gitea ne crée pas une PR automatiquement.
|
||||||
|
> Il affiche une *suggestion* (bandeau vert) ou imprime un lien “Create a new pull request” lors du push.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pré-check (obligatoire, 10 secondes)
|
||||||
|
en bash :
|
||||||
|
|
||||||
|
git status -sb
|
||||||
|
git fetch origin --prune
|
||||||
|
git branch --show-current
|
||||||
|
|
||||||
|
Procédure standard (zéro surprise)
|
||||||
|
### 1) Se remettre propre sur main
|
||||||
|
|
||||||
|
git checkout main
|
||||||
|
git pull --ff-only
|
||||||
|
|
||||||
|
### 2) Créer une branche AVANT de modifier / ajouter des fichiers
|
||||||
|
|
||||||
|
git switch -c docs/<YYYY-MM-DD>-<sujet-court>
|
||||||
|
|
||||||
|
### 3) Ajouter/modifier tes fichiers dans docs/
|
||||||
|
|
||||||
|
Exemple :
|
||||||
|
|
||||||
|
docs/auth-stack.md
|
||||||
|
|
||||||
|
docs/runbook-....
|
||||||
|
|
||||||
|
### 4) Vérifier ce qui va partir
|
||||||
|
|
||||||
|
git status -sb
|
||||||
|
git diff
|
||||||
|
|
||||||
|
### 5) Commit
|
||||||
|
|
||||||
|
git add docs/
|
||||||
|
git commit -m "docs: <résumé clair>"
|
||||||
|
|
||||||
|
### 6) Vérifier que ta branche a bien des commits “devant” main (SINON pas de PR possible)
|
||||||
|
|
||||||
|
git fetch origin
|
||||||
|
git log --oneline origin/main..HEAD
|
||||||
|
|
||||||
|
Si ça n’affiche rien : tu n’as rien à proposer (branche identique à main).
|
||||||
|
|
||||||
|
### 7) Push (méthode la plus robuste)
|
||||||
|
|
||||||
|
git push -u origin HEAD
|
||||||
|
|
||||||
|
### 8) Créer la PR (2 chemins fiables)
|
||||||
|
# Chemin A — le plus simple : utiliser le lien imprimé dans le terminal
|
||||||
|
|
||||||
|
Après le push, Gitea affiche généralement :
|
||||||
|
“Create a new pull request for '<ta-branche>': <URL>”
|
||||||
|
➡️ Ouvre cette URL, clique “Créer la demande d’ajout”.
|
||||||
|
|
||||||
|
# Chemin B — via l’UI Gitea (si tu veux le bandeau vert)
|
||||||
|
|
||||||
|
Va sur le dépôt
|
||||||
|
|
||||||
|
Onglet “Demandes d’ajout”
|
||||||
|
|
||||||
|
Clique “Nouvelle demande d’ajout”
|
||||||
|
|
||||||
|
Source branch = ta branche, Target = main
|
||||||
|
|
||||||
|
Créer
|
||||||
|
|
||||||
|
## Pourquoi le bandeau vert peut ne PAS apparaître (et ce que ça signifie)
|
||||||
|
|
||||||
|
Ta branche est identique à main
|
||||||
|
|
||||||
|
# Diagnostic :
|
||||||
|
|
||||||
|
git fetch origin
|
||||||
|
git diff --name-status origin/main..HEAD
|
||||||
|
|
||||||
|
Si vide => normal, pas de suggestion.
|
||||||
|
|
||||||
|
Tu n’es pas sur la bonne branche
|
||||||
|
|
||||||
|
# Diagnostic :
|
||||||
|
|
||||||
|
git branch --show-current
|
||||||
|
|
||||||
|
Tu regardes l’UI au mauvais endroit
|
||||||
|
|
||||||
|
Solution : utilise le bouton “Nouvelle demande d’ajout” ou le lien du terminal (chemin A).
|
||||||
|
|
||||||
|
Anti-bêtise (optionnel mais recommandé)
|
||||||
|
Empêcher de commit sur main par erreur (hook local)
|
||||||
|
|
||||||
|
# Créer .git/hooks/pre-commit :
|
||||||
|
|
||||||
|
#!/bin/sh
|
||||||
|
b="$(git branch --show-current)"
|
||||||
|
if [ "$b" = "main" ]; then
|
||||||
|
echo "❌ Refus: commit interdit sur main. Crée une branche."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
Puis :
|
||||||
|
|
||||||
|
chmod +x .git/hooks/pre-commit
|
||||||
|
|
||||||
|
## Rappel : main protégé
|
||||||
|
|
||||||
|
Si main est protégé, tu ne merges PAS par git push origin main.
|
||||||
|
Tu merges via la PR (UI), après CI verte.
|
||||||
|
|
||||||
217
docs/TROUBLESHOOTING.md
Normal file
217
docs/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
# TROUBLESHOOTING — Archicratie Web / NAS / Gitea
|
||||||
|
|
||||||
|
Dernière mise à jour : 2026-02-01
|
||||||
|
|
||||||
|
Ce document liste les symptômes rencontrés et les remèdes **concrets**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0) Réflexe unique
|
||||||
|
Toujours isoler : **Local**, **Gitea**, **NAS**, **Navigateur**.
|
||||||
|
|
||||||
|
- Si ça marche sur `127.0.0.1:8082` mais pas sur le domaine → proxy/cache.
|
||||||
|
- Si ça marche après login Gitea mais pas via “Proposer” → variables `PUBLIC_GITEA_*`.
|
||||||
|
- Si push refusé → branch protection (normal).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="proposer-404"></a>
|
||||||
|
## 1) “Proposer” ouvre Gitea mais retourne 404 / non autorisé
|
||||||
|
|
||||||
|
### Symptôme
|
||||||
|
Nouvel onglet :
|
||||||
|
- 404 Not Found / “n’existe pas ou pas autorisé”
|
||||||
|
- ou redirect `/user/login`
|
||||||
|
|
||||||
|
### Cause la plus fréquente
|
||||||
|
URL pointe vers **mauvais owner/repo** (casse sensible) :
|
||||||
|
- `archicratia/archicratie-web` au lieu de `Archicratia/archicratie-edition`
|
||||||
|
|
||||||
|
### Diagnostic
|
||||||
|
Sur NAS (ou dans le HTML généré), vérifier l’URL ouverte :
|
||||||
|
- doit contenir : `/Archicratia/archicratie-edition/issues/new`
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
Dans `.env` de build prod (NAS) :
|
||||||
|
- `PUBLIC_GITEA_OWNER=Archicratia`
|
||||||
|
- `PUBLIC_GITEA_REPO=archicratie-edition`
|
||||||
|
Puis rebuild + restart du container + smoke.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="proposer-double-onglet"></a>
|
||||||
|
## 2) Double onglet à la validation du flow “Proposer”
|
||||||
|
|
||||||
|
### Symptôme
|
||||||
|
Deux onglets s’ouvrent au moment de valider (après choix 1 / choix 2).
|
||||||
|
|
||||||
|
### Causes possibles
|
||||||
|
- handler JS déclenché deux fois (bubbling)
|
||||||
|
- présence d’un `window.open` + `a.click()` simultanément
|
||||||
|
- bouton “Proposer” est un `<a target=_blank>` et un autre handler ouvre aussi.
|
||||||
|
|
||||||
|
### Diagnostic rapide (devtools navigateur)
|
||||||
|
Chercher `window.open` dans la page générée :
|
||||||
|
- la commande doit retourner 0 lignes.
|
||||||
|
|
||||||
|
Sur NAS :
|
||||||
|
en sh :
|
||||||
|
|
||||||
|
curl -fsS http://127.0.0.1:8082/archicratie/archicrat-ia/chapitre-4/ > /tmp/page.html
|
||||||
|
grep -n "window.open" /tmp/page.html | head
|
||||||
|
|
||||||
|
Fix
|
||||||
|
|
||||||
|
garder un seul mécanisme d’ouverture
|
||||||
|
|
||||||
|
sur click : preventDefault() + stopImmediatePropagation()
|
||||||
|
|
||||||
|
<a id="Favicon-504-erreurs"></a>
|
||||||
|
## 3) Favicon 504 / erreurs console sur favicon
|
||||||
|
|
||||||
|
# Symptôme
|
||||||
|
|
||||||
|
Console navigateur : GET /favicon.ico 504
|
||||||
|
|
||||||
|
# Cause fréquente
|
||||||
|
|
||||||
|
Cache du navigateur (ancienne erreur conservée).
|
||||||
|
|
||||||
|
# Diagnostic
|
||||||
|
|
||||||
|
Comparer :
|
||||||
|
|
||||||
|
curl -I http://127.0.0.1:8082/favicon.ico
|
||||||
|
|
||||||
|
curl -kI https://<domaine>/favicon.ico
|
||||||
|
|
||||||
|
Si curl = 200 et navigateur = 504 → cache.
|
||||||
|
|
||||||
|
# Fix
|
||||||
|
|
||||||
|
Désactiver cache dans l’onglet Réseau (devtools)
|
||||||
|
|
||||||
|
hard refresh
|
||||||
|
|
||||||
|
vérifier droits fichiers dans dist/
|
||||||
|
|
||||||
|
## 4) Sur NAS : git: command not found
|
||||||
|
# Symptôme
|
||||||
|
|
||||||
|
git fetch impossible sur le NAS.
|
||||||
|
|
||||||
|
# Cause
|
||||||
|
|
||||||
|
Git non installé sur DSM shell.
|
||||||
|
|
||||||
|
# Fix standard (recommandé)
|
||||||
|
|
||||||
|
Utiliser un conteneur git :
|
||||||
|
|
||||||
|
APP="/volume2/docker/archicratie-web/current"
|
||||||
|
U_ID="$(id -u)"; G_ID="$(id -g)"
|
||||||
|
|
||||||
|
sudo docker run --rm --network host \
|
||||||
|
-u "$U_ID:$G_ID" -e HOME=/tmp \
|
||||||
|
-v "$APP":/repo -w /repo \
|
||||||
|
--entrypoint sh alpine/git -lc '
|
||||||
|
set -eu
|
||||||
|
git config --global --add safe.directory /repo
|
||||||
|
git config http.sslVerify false
|
||||||
|
git fetch origin --prune
|
||||||
|
git status -sb
|
||||||
|
'
|
||||||
|
|
||||||
|
## 5) Git : “dubious ownership in repository”
|
||||||
|
# Symptôme
|
||||||
|
|
||||||
|
fatal: detected dubious ownership
|
||||||
|
|
||||||
|
# Fix
|
||||||
|
|
||||||
|
Dans le conteneur git (ou machine locale) :
|
||||||
|
git config --global --add safe.directory /repo
|
||||||
|
|
||||||
|
## 6) Git : non-fast-forward au push
|
||||||
|
# Symptôme
|
||||||
|
|
||||||
|
rejected (non-fast-forward)
|
||||||
|
|
||||||
|
# Cause
|
||||||
|
|
||||||
|
Ta branche locale est en retard vs remote.
|
||||||
|
|
||||||
|
# Fix
|
||||||
|
|
||||||
|
En général :
|
||||||
|
|
||||||
|
on fait une PR depuis une branche
|
||||||
|
|
||||||
|
ou on rebase/merge origin/main avant push
|
||||||
|
|
||||||
|
Sur une branche de travail :
|
||||||
|
git fetch origin
|
||||||
|
git rebase origin/main
|
||||||
|
# ou
|
||||||
|
git merge origin/main
|
||||||
|
|
||||||
|
## 7) Gitea : “Not allowed to push to protected branch main”
|
||||||
|
# Symptôme
|
||||||
|
|
||||||
|
pre-receive hook declined
|
||||||
|
|
||||||
|
# Cause
|
||||||
|
|
||||||
|
Protection de branche (normal/attendu).
|
||||||
|
|
||||||
|
# Fix
|
||||||
|
|
||||||
|
Push sur une branche
|
||||||
|
|
||||||
|
Ouvrir PR
|
||||||
|
|
||||||
|
Merger via UI Gitea
|
||||||
|
|
||||||
|
## 8) Docker build : BuildKit / buildx / API version
|
||||||
|
# Symptômes typiques
|
||||||
|
|
||||||
|
the --network option requires BuildKit
|
||||||
|
|
||||||
|
BuildKit is enabled but the buildx component is missing
|
||||||
|
|
||||||
|
client version ... too new. Maximum supported API version ...
|
||||||
|
|
||||||
|
# Fix “robuste” (principe)
|
||||||
|
|
||||||
|
installer buildx si nécessaire
|
||||||
|
|
||||||
|
si DSM/docker API ancienne : définir DOCKER_API_VERSION=<compatible> (selon ton environnement)
|
||||||
|
|
||||||
|
garder le build en --network host si nécessaire
|
||||||
|
|
||||||
|
## 9) Container Manager / Docker : “database is locked” (logging driver db)
|
||||||
|
# Symptôme
|
||||||
|
|
||||||
|
failed to initialize logging driver : database is locked
|
||||||
|
|
||||||
|
# Cause
|
||||||
|
|
||||||
|
Le driver de logs Docker est db (Synology) et sa DB est verrouillée.
|
||||||
|
|
||||||
|
# Fix rapide
|
||||||
|
|
||||||
|
Redémarrer “Container Manager” depuis le centre de paquets DSM.
|
||||||
|
|
||||||
|
Vérifier que le conteneur redémarre ensuite.
|
||||||
|
|
||||||
|
## 10) Checklist “tout marche”
|
||||||
|
|
||||||
|
curl -I http://127.0.0.1:8082/ => 200
|
||||||
|
|
||||||
|
curl -kI https://<domaine>/ => 200
|
||||||
|
|
||||||
|
PUBLIC_GITEA_* corrects
|
||||||
|
|
||||||
|
“Proposer” : 1 onglet, pas de 404, issue pré-remplie
|
||||||
|
|
||||||
|
CI passe sur PR merge
|
||||||
@@ -63,7 +63,7 @@ Si l’ID exact n’existe plus :
|
|||||||
But : éviter les “liens morts” historiques quand une régénération d’IDs a eu lieu.
|
But : éviter les “liens morts” historiques quand une régénération d’IDs a eu lieu.
|
||||||
|
|
||||||
Limite : c’est un fallback de dernier recours (moins déterministe qu’un alias explicite).
|
Limite : c’est un fallback de dernier recours (moins déterministe qu’un alias explicite).
|
||||||
Le mécanisme recommandé reste : `docs/anchor-aliases.json` + injection au build.
|
Le mécanisme recommandé reste : `src/anchors/anchor-aliases.json` + injection au build.
|
||||||
|
|
||||||
_______________________________________
|
_______________________________________
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ Les IDs d’ancres générés (ou dérivés) peuvent changer :
|
|||||||
|
|
||||||
## 2) Le mapping d’alias
|
## 2) Le mapping d’alias
|
||||||
|
|
||||||
- Fichier versionné (ex) : `docs/anchor-aliases.json`
|
- Fichier versionné (ex) : `src/anchors/anchor-aliases.json`
|
||||||
- Format : `oldId -> newId` par page
|
- Format : `oldId -> newId` par page
|
||||||
|
|
||||||
Ex en json :
|
Ex en json :
|
||||||
|
|||||||
201
docs/auth-stack.md
Normal file
201
docs/auth-stack.md
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
# Auth Stack — LLDAP + Authelia + Redis (DSM 7.3 / Synology DS220+)
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
Fournir une pile d’authentification robuste (anti-lockout) pour protéger des services web via reverse-proxy :
|
||||||
|
- Annuaire utilisateurs : **LLDAP**
|
||||||
|
- Portail / SSO / MFA : **Authelia**
|
||||||
|
- Cache/sessions (optionnel selon config) : **Redis**
|
||||||
|
- Exposition publique : **Reverse proxy** (Synology / Nginx / Traefik) vers Authelia
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Composants
|
||||||
|
- **LLDAP**
|
||||||
|
- UI admin (HTTP) : `127.0.0.1:17170`
|
||||||
|
- LDAP : `127.0.0.1:3890`
|
||||||
|
- Base : sqlite dans `/volume2/docker/auth/data/lldap`
|
||||||
|
|
||||||
|
- **Authelia**
|
||||||
|
- API/portal : `127.0.0.1:9091`
|
||||||
|
- Stockage : sqlite dans `/volume2/docker/auth/data/authelia/db.sqlite3`
|
||||||
|
- Accès externe : via reverse proxy -> `https://auth.<domaine>`
|
||||||
|
|
||||||
|
- **Redis**
|
||||||
|
- Local uniquement : `127.0.0.1:6379`
|
||||||
|
- (peut servir plus tard à sessions/rate-limit selon config)
|
||||||
|
|
||||||
|
### Exposition réseau (principe de sécurité)
|
||||||
|
- Tous les services **bindés sur 127.0.0.1** (loopback NAS)
|
||||||
|
- Seul le **reverse proxy** expose `https://auth.<domaine>` vers `127.0.0.1:9091`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fichiers de référence
|
||||||
|
|
||||||
|
### 1) docker-compose.auth.yml
|
||||||
|
- Déploie redis + lldap + authelia.
|
||||||
|
- Recommandation DSM : **network_mode: host** + bind sur localhost.
|
||||||
|
- Supprime les aléas “bridge + DNS + subnets”
|
||||||
|
- Évite les timeouts LDAP sporadiques.
|
||||||
|
|
||||||
|
### 2) /volume2/docker/auth/compose/.env
|
||||||
|
Variables attendues :
|
||||||
|
|
||||||
|
#### LLDAP
|
||||||
|
- `LLDAP_JWT_SECRET=...` (random 32+)
|
||||||
|
- `LLDAP_KEY_SEED=...` (random 32+)
|
||||||
|
- `LLDAP_LDAP_USER_PASS=...` (mot de passe admin LLDAP)
|
||||||
|
|
||||||
|
#### Authelia
|
||||||
|
- `AUTHELIA_JWT_SECRET=...` (utilisé ici comme source pour reset_password)
|
||||||
|
- `AUTHELIA_SESSION_SECRET=...`
|
||||||
|
- `AUTHELIA_STORAGE_ENCRYPTION_KEY=...`
|
||||||
|
|
||||||
|
> Ne jamais committer `.env`. Stocker dans DSM / secrets.
|
||||||
|
|
||||||
|
### 3) /volume2/docker/auth/config/authelia/configuration.yml
|
||||||
|
- LDAP address en mode robuste : `ldap://127.0.0.1:3890`
|
||||||
|
- Cookie domain : `archicratie.trans-hands.synology.me`
|
||||||
|
- `authelia_url` : `https://auth.archicratie.trans-hands.synology.me`
|
||||||
|
- `default_redirection_url` : service principal (ex: gitea)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Procédures opératoires
|
||||||
|
|
||||||
|
### Restart safe (redémarrage propre)
|
||||||
|
en bash :
|
||||||
|
cd /volume2/docker/auth/compose
|
||||||
|
sudo docker compose --env-file .env -f docker-compose.auth.yml down --remove-orphans
|
||||||
|
sudo docker compose --env-file .env -f docker-compose.auth.yml up -d --force-recreate
|
||||||
|
|
||||||
|
### Tests santé (sans dépendances DSM)
|
||||||
|
curl -fsS http://127.0.0.1:17170/ >/dev/null && echo "LLDAP UI OK"
|
||||||
|
curl -fsS http://127.0.0.1:9091/api/health && echo "AUTHELIA LOCAL OK"
|
||||||
|
curl -kfsS https://auth.archicratie.trans-hands.synology.me/api/health && echo "AUTHELIA HTTPS OK"
|
||||||
|
|
||||||
|
### Test TCP LDAP :
|
||||||
|
sudo docker run --rm --network host nicolaka/netshoot:latest sh -lc 'nc -vz -w2 127.0.0.1 3890'
|
||||||
|
|
||||||
|
### Rotate secrets (rotation)
|
||||||
|
|
||||||
|
# Principes :
|
||||||
|
|
||||||
|
Rotation = redémarrage forcé d’Authelia (sessions invalidées)
|
||||||
|
|
||||||
|
Rotation de LLDAP_KEY_SEED est sensible : peut affecter chiffrement des mots de passe.
|
||||||
|
|
||||||
|
# Procédure conseillée :
|
||||||
|
|
||||||
|
Sauvegarder DBs :
|
||||||
|
|
||||||
|
/volume2/docker/auth/data/lldap/users.db
|
||||||
|
|
||||||
|
/volume2/docker/auth/data/authelia/db.sqlite3
|
||||||
|
|
||||||
|
Changer d’abord secrets Authelia (AUTHELIA_SESSION_SECRET, AUTHELIA_STORAGE_ENCRYPTION_KEY)
|
||||||
|
|
||||||
|
docker compose up -d --force-recreate authelia
|
||||||
|
|
||||||
|
Vérifier /api/health + login.
|
||||||
|
|
||||||
|
Reset admin LLDAP (break-glass)
|
||||||
|
|
||||||
|
# Si tu perds le mot de passe admin :
|
||||||
|
|
||||||
|
Activer temporairement LLDAP_FORCE_LDAP_USER_PASS_RESET=true dans l’environnement LLDAP
|
||||||
|
|
||||||
|
Redémarrer LLDAP une seule fois
|
||||||
|
|
||||||
|
Désactiver immédiatement après.
|
||||||
|
|
||||||
|
⚠️ Ne jamais laisser ce flag en permanence : il force le reset à chaque boot.
|
||||||
|
|
||||||
|
## Checklist anti-lockout (indispensable)
|
||||||
|
### 1) Accès direct local (bypass)
|
||||||
|
|
||||||
|
LLDAP UI accessible en local : http://127.0.0.1:17170
|
||||||
|
|
||||||
|
Authelia health local : http://127.0.0.1:9091/api/health
|
||||||
|
|
||||||
|
### 2) Règle Authelia : domaine auth en bypass
|
||||||
|
|
||||||
|
Dans configuration.yml :
|
||||||
|
access_control:
|
||||||
|
rules:
|
||||||
|
- domain: "auth.<domaine>"
|
||||||
|
policy: bypass
|
||||||
|
|
||||||
|
But : pouvoir charger le portail même si les règles des autres domaines cassent.
|
||||||
|
|
||||||
|
### 3) Route de secours reverse-proxy
|
||||||
|
|
||||||
|
Prévoir une route non protégée (ou protégée différemment) pour pouvoir corriger :
|
||||||
|
|
||||||
|
ex: https://admin.<domaine>/ ou un vhost interne LAN-only.
|
||||||
|
|
||||||
|
### 4) Fenêtre privée pour tester
|
||||||
|
|
||||||
|
Toujours tester login/authelia dans un onglet privé pour éviter cookies “fantômes”.
|
||||||
|
|
||||||
|
## Troubleshooting (ce qu’on a rencontré et résolu)
|
||||||
|
### A) YAML/Compose cassé (tabs, doublons)
|
||||||
|
|
||||||
|
# Symptômes :
|
||||||
|
|
||||||
|
mapping key "ports" already defined
|
||||||
|
|
||||||
|
found character that cannot start any token
|
||||||
|
|
||||||
|
# Fix :
|
||||||
|
|
||||||
|
supprimer tabs
|
||||||
|
|
||||||
|
supprimer doublons (volumes/ports/networks)
|
||||||
|
|
||||||
|
valider : docker compose ... config
|
||||||
|
|
||||||
|
### B) Substitution foireuse des variables dans healthcheck
|
||||||
|
|
||||||
|
# Problème :
|
||||||
|
|
||||||
|
$VAR évalué par compose au parse-time
|
||||||
|
|
||||||
|
# Fix :
|
||||||
|
|
||||||
|
utiliser $$VAR dans CMD-SHELL si nécessaire.
|
||||||
|
|
||||||
|
### C) /config monté read-only
|
||||||
|
|
||||||
|
# Symptômes :
|
||||||
|
|
||||||
|
chown: /config/... Read-only file system
|
||||||
|
|
||||||
|
# Fix :
|
||||||
|
|
||||||
|
monter /config en :rw si Authelia doit écrire des backups/keys.
|
||||||
|
|
||||||
|
### D) Timeouts LDAP aléatoires en bridge
|
||||||
|
|
||||||
|
# Symptômes :
|
||||||
|
|
||||||
|
dial tcp <ip>:3890: i/o timeout
|
||||||
|
|
||||||
|
IP Docker “surprise” (subnet 192.168.32.0/20 etc.)
|
||||||
|
|
||||||
|
# Fix robuste DSM :
|
||||||
|
|
||||||
|
passer en network_mode: host + bind 127.0.0.1
|
||||||
|
|
||||||
|
Authelia -> ldap://127.0.0.1:3890
|
||||||
|
|
||||||
|
### E) “Authelia OK mais Gitea redemande login”
|
||||||
|
|
||||||
|
# Normal :
|
||||||
|
|
||||||
|
tant que Gitea n’est pas configuré en OIDC vers Authelia, ce n’est pas du SSO.
|
||||||
|
|
||||||
|
Authelia protège l’accès, mais ne crée pas de session Gitea.
|
||||||
|
|
||||||
67
docs/gitea-pr-main-protege.md
Normal file
67
docs/gitea-pr-main-protege.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# Workflow Git/Gitea — main protégé (PR only)
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
Éviter toute casse de `main` : on travaille **toujours** via branche + Pull Request.
|
||||||
|
|
||||||
|
## 1) Démarrer propre (local)
|
||||||
|
en bash :
|
||||||
|
|
||||||
|
git fetch origin --prune
|
||||||
|
git checkout main
|
||||||
|
git reset --hard origin/main
|
||||||
|
git clean -fd
|
||||||
|
|
||||||
|
## 2) Créer une branche
|
||||||
|
|
||||||
|
git checkout -b fix/ma-modif
|
||||||
|
|
||||||
|
## 3) Modifier, tester, commit
|
||||||
|
|
||||||
|
npm test
|
||||||
|
git add -A
|
||||||
|
git commit -m "Mon changement"
|
||||||
|
|
||||||
|
## 4) Push (création branche distante)
|
||||||
|
|
||||||
|
git push -u origin fix/ma-modif
|
||||||
|
|
||||||
|
## 5) Créer la Pull Request (UI Gitea)
|
||||||
|
|
||||||
|
Gitea → repository → Pull Requests → New Pull Request
|
||||||
|
|
||||||
|
base : main
|
||||||
|
compare : fix/ma-modif
|
||||||
|
|
||||||
|
Si “je ne vois pas de PR”
|
||||||
|
|
||||||
|
Vérifie d’abord qu’il y a un diff réel :
|
||||||
|
|
||||||
|
git log --oneline origin/main..HEAD
|
||||||
|
|
||||||
|
Si la commande ne sort rien : ta branche ne contient aucun commit différent → PR inutile/invisible.
|
||||||
|
|
||||||
|
## 6) Conflits
|
||||||
|
|
||||||
|
Ne merge pas en local vers main (push refusé si main protégé).
|
||||||
|
On met à jour la branche de PR :
|
||||||
|
|
||||||
|
Option A (simple) : merge main dans la branche
|
||||||
|
|
||||||
|
git fetch origin
|
||||||
|
git merge origin/main
|
||||||
|
# résoudre conflits
|
||||||
|
npm test
|
||||||
|
git push
|
||||||
|
|
||||||
|
Option B (plus propre) : rebase
|
||||||
|
|
||||||
|
git fetch origin
|
||||||
|
git rebase origin/main
|
||||||
|
# résoudre conflits, puis:
|
||||||
|
npm test
|
||||||
|
git push --force-with-lease
|
||||||
|
|
||||||
|
## 7) Merge
|
||||||
|
|
||||||
|
Toujours depuis l’UI de la Pull Request (ou via un mainteneur).
|
||||||
|
|
||||||
69
docs/proposer-whoami-gate.md
Normal file
69
docs/proposer-whoami-gate.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# “Proposer” protégé par groupe (whoami / editors)
|
||||||
|
|
||||||
|
## But
|
||||||
|
Le bouton **Proposer** (création d’issue Gitea pré-remplie) doit être :
|
||||||
|
- visible **uniquement** pour les membres du groupe `editors`,
|
||||||
|
- **absent** pour les autres utilisateurs,
|
||||||
|
- robuste (fail-closed), mais **non-collant** (pas de “bloqué” après un échec transitoire).
|
||||||
|
|
||||||
|
## Pré-requis (build-time)
|
||||||
|
Les variables publiques Astro doivent être injectées au build :
|
||||||
|
- `PUBLIC_GITEA_BASE`
|
||||||
|
- `PUBLIC_GITEA_OWNER`
|
||||||
|
- `PUBLIC_GITEA_REPO`
|
||||||
|
|
||||||
|
Si une seule manque → `giteaReady=false` → Proposer est désactivé.
|
||||||
|
|
||||||
|
### Vérification NAS (slots blue/green)
|
||||||
|
Exemple :
|
||||||
|
- blue : http://127.0.0.1:8081/...
|
||||||
|
- green : http://127.0.0.1:8082/...
|
||||||
|
|
||||||
|
Commande (ex) :
|
||||||
|
`curl -sS http://127.0.0.1:8081/archicratie/archicrat-ia/chapitre-4/ | grep -n "const GITEA_" | head`
|
||||||
|
|
||||||
|
## Signal d’auth (runtime) : `/_auth/whoami`
|
||||||
|
Le site appelle `/_auth/whoami` (same-origin) pour récupérer :
|
||||||
|
- `Remote-User`
|
||||||
|
- `Remote-Groups`
|
||||||
|
Ces headers sont injectés par la chaîne edge (Traefik → Authelia forward-auth).
|
||||||
|
|
||||||
|
### Appel robuste
|
||||||
|
- cache-bust : `?_=${Date.now()}`
|
||||||
|
- `cache: "no-store"`
|
||||||
|
- `credentials: "include"`
|
||||||
|
|
||||||
|
### Critère
|
||||||
|
`groups.includes("editors")`
|
||||||
|
|
||||||
|
## Comportement attendu (UX)
|
||||||
|
- utilisateur editors : le bouton “Proposer” est visible, ouvre la modal, puis ouvre Gitea.
|
||||||
|
- utilisateur non editors : le bouton “Proposer” n’existe pas (retiré du DOM).
|
||||||
|
|
||||||
|
## Pièges connus
|
||||||
|
1) Tester en direct 8081/8082 ne reflète pas toujours la chaîne Traefik+Authelia.
|
||||||
|
2) Un gate “collant” peut rester OFF si l’échec est mis en cache trop agressivement.
|
||||||
|
3) Si “Proposer” est caché via `style.display="none"`, il faut le réafficher via `style.display=""` (pas via `hidden=false`).
|
||||||
|
|
||||||
|
## Debug rapide (console navigateur)
|
||||||
|
en js :
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const r = await fetch("/_auth/whoami?_=" + Date.now(), {
|
||||||
|
credentials: "include",
|
||||||
|
cache: "no-store",
|
||||||
|
redirect: "follow",
|
||||||
|
});
|
||||||
|
const t = await r.text();
|
||||||
|
const groups = (t.match(/^Remote-Groups:\s*(.*)$/mi)?.[1] || "")
|
||||||
|
.split(",").map(s => s.trim()).filter(Boolean);
|
||||||
|
console.log({ ok: r.ok, status: r.status, groups, raw: t.slice(0, 220) + "..." });
|
||||||
|
})();
|
||||||
|
|
||||||
|
## Définition “done”
|
||||||
|
|
||||||
|
Archicratia (editors) voit Proposer et peut ouvrir un ticket.
|
||||||
|
|
||||||
|
s-FunX (non editors) ne voit pas Proposer.
|
||||||
|
|
||||||
|
Les deux slots blue/green injectent les constantes Gitea dans le HTML.
|
||||||
82
docs/runbook-deploiement-web-blue-green.md
Normal file
82
docs/runbook-deploiement-web-blue-green.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Runbook — Déploiement Archicratie Web Édition (Blue/Green)
|
||||||
|
|
||||||
|
## Arborescence NAS (repère)
|
||||||
|
- `/volume2/docker/archicratie-web/current/` : état courant (Dockerfile, docker-compose.yml, dist buildé en image)
|
||||||
|
- `/volume2/docker/archicratie-web/releases/` : historiques éventuels
|
||||||
|
- `/volume2/docker/edge/` : Traefik + config dynamique
|
||||||
|
|
||||||
|
> Important : les commandes `docker compose -f ...` doivent viser le **docker-compose.yml présent dans `current/`**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pré-requis Synology
|
||||||
|
Sur NAS, les commandes ont été exécutées avec :
|
||||||
|
en bash
|
||||||
|
sudo env DOCKER_API_VERSION=1.43 docker ...
|
||||||
|
|
||||||
|
(contexte DSM / compat API)
|
||||||
|
|
||||||
|
### 1) Variables de build (Gitea)
|
||||||
|
|
||||||
|
Dans /volume2/docker/archicratie-web/current créer/maintenir :
|
||||||
|
|
||||||
|
cat > .env <<'EOF'
|
||||||
|
PUBLIC_GITEA_BASE=https://gitea.archicratie.trans-hands.synology.me
|
||||||
|
PUBLIC_GITEA_OWNER=Archicratia
|
||||||
|
PUBLIC_GITEA_REPO=archicratie-edition
|
||||||
|
EOF
|
||||||
|
|
||||||
|
### 2) Build images (blue + green) — méthode robuste
|
||||||
|
|
||||||
|
cd /volume2/docker/archicratie-web/current
|
||||||
|
|
||||||
|
sudo env DOCKER_API_VERSION=1.43 docker compose -f docker-compose.yml build --no-cache web_blue web_green
|
||||||
|
|
||||||
|
Puis recréer les conteneurs sans rebuild :
|
||||||
|
|
||||||
|
sudo env DOCKER_API_VERSION=1.43 docker compose -f docker-compose.yml up -d --force-recreate --no-build web_blue web_green
|
||||||
|
|
||||||
|
### 3) Vérifier que les deux slots sont OK
|
||||||
|
|
||||||
|
curl -sS -D- http://127.0.0.1:8081/ | head -n 12
|
||||||
|
curl -sS -D- http://127.0.0.1:8082/ | head -n 12
|
||||||
|
|
||||||
|
Attendu :
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
|
||||||
|
Server: nginx/...
|
||||||
|
|
||||||
|
### 4) Traefik : s’assurer qu’un seul backend est actif
|
||||||
|
|
||||||
|
Fichier :
|
||||||
|
/volume2/docker/edge/config/dynamic/20-archicratie-backend.yml
|
||||||
|
|
||||||
|
Attendu : une seule URL (8081 OU 8082)
|
||||||
|
|
||||||
|
http:
|
||||||
|
services:
|
||||||
|
archicratie_web:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://127.0.0.1:8081"
|
||||||
|
|
||||||
|
### 5) Smoke via Traefik (entrée réelle)
|
||||||
|
|
||||||
|
curl -sS -H 'Host: archicratie.trans-hands.synology.me' http://127.0.0.1:18080/ | head -n 20
|
||||||
|
|
||||||
|
Attendu :
|
||||||
|
|
||||||
|
si non loggé : 302 vers Authelia
|
||||||
|
|
||||||
|
si loggé : HTML du site
|
||||||
|
|
||||||
|
### 6) Piège classique : conflit de nom de conteneur
|
||||||
|
|
||||||
|
Si :
|
||||||
|
Conflict. The container name "/archicratie-web-blue" is already in use...
|
||||||
|
|
||||||
|
Faire :
|
||||||
|
|
||||||
|
sudo docker rm -f archicratie-web-blue
|
||||||
|
sudo env DOCKER_API_VERSION=1.43 docker compose -f docker-compose.yml up -d --force-recreate --no-build web_blue
|
||||||
71
docs/runbook-gitea-branches-pr.md
Normal file
71
docs/runbook-gitea-branches-pr.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# Runbook — Gitea : Branches, PR, Merge (sans se faire piéger)
|
||||||
|
|
||||||
|
## Règle n°1 (hyper importante)
|
||||||
|
Une PR n’apparaît dans Gitea que si la branche contient **au moins 1 commit différent de `main`**.
|
||||||
|
|
||||||
|
Symptôme typique :
|
||||||
|
- `git push -u origin fix/xxx`
|
||||||
|
- et tu vois : `Total 0 ...`
|
||||||
|
→ ça veut dire : **aucun nouveau commit** → la branche est identique à main → pas de vraie PR à proposer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow “propre” (pas à pas)
|
||||||
|
### 1) Remettre `main` propre
|
||||||
|
en bash
|
||||||
|
|
||||||
|
git checkout main
|
||||||
|
git pull --ff-only
|
||||||
|
|
||||||
|
### 2) Créer une branche de travail
|
||||||
|
|
||||||
|
git checkout -b fix/mon-fix
|
||||||
|
|
||||||
|
### 3) Faire un changement réel
|
||||||
|
|
||||||
|
Modifier le fichier (ex : src/layouts/EditionLayout.astro)
|
||||||
|
|
||||||
|
Vérifier :
|
||||||
|
|
||||||
|
git status -sb
|
||||||
|
|
||||||
|
→ doit montrer un fichier modifié.
|
||||||
|
|
||||||
|
### 4) Tester
|
||||||
|
|
||||||
|
npm test
|
||||||
|
|
||||||
|
### 5) Commit
|
||||||
|
|
||||||
|
git add src/layouts/EditionLayout.astro
|
||||||
|
git commit -m "Fix: ..."
|
||||||
|
|
||||||
|
### 6) Push
|
||||||
|
|
||||||
|
git push -u origin fix/mon-fix
|
||||||
|
|
||||||
|
### 7) Créer la PR dans l’UI Gitea
|
||||||
|
|
||||||
|
# Aller dans Pull Requests
|
||||||
|
|
||||||
|
# New Pull Request
|
||||||
|
|
||||||
|
Base : main
|
||||||
|
|
||||||
|
Compare : fix/mon-fix
|
||||||
|
|
||||||
|
Branch protection (si “Not allowed to push to protected branch main”)
|
||||||
|
|
||||||
|
# C’est normal si main est protégé :
|
||||||
|
|
||||||
|
On ne pousse jamais directement sur main.
|
||||||
|
|
||||||
|
On merge via PR (UI), avec un compte autorisé.
|
||||||
|
|
||||||
|
Si Gitea refuse de merger automatiquement :
|
||||||
|
|
||||||
|
soit tu actives le réglage côté Gitea “manual merge detection” (admin),
|
||||||
|
|
||||||
|
soit tu fais le merge localement MAIS tu ne pourras pas pousser sur main si la protection l’interdit.
|
||||||
|
|
||||||
|
Conclusion : la voie “pro” = PR + merge UI.
|
||||||
67
docs/runbook-proposer-gitea.md
Normal file
67
docs/runbook-proposer-gitea.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# Runbook — Bouton “Proposer” (site → Gitea issue) + Gate Authelia
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
Permettre une proposition de correction éditoriale depuis un paragraphe du site, en créant une *issue* Gitea pré-remplie, uniquement pour les membres du groupe `editors`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pré-requis
|
||||||
|
- Traefik (edge) en front
|
||||||
|
- Authelia (forwardAuth) opérationnel
|
||||||
|
- Router `/_auth/whoami` exposé (whoami)
|
||||||
|
- Variables `PUBLIC_GITEA_*` injectées au build du site
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Vérification rapide (navigateur)
|
||||||
|
### 1) Qui suis-je ? (groupes)
|
||||||
|
Dans la console :
|
||||||
|
en js :
|
||||||
|
await fetch("/_auth/whoami?_=" + Date.now(), {
|
||||||
|
credentials: "include",
|
||||||
|
cache: "no-store",
|
||||||
|
}).then(r => r.text());
|
||||||
|
|
||||||
|
Attendu (extraits) :
|
||||||
|
|
||||||
|
Remote-User: <login>
|
||||||
|
|
||||||
|
Remote-Groups: ...,editors,... pour un éditeur
|
||||||
|
|
||||||
|
### 2) Le bouton existe ?
|
||||||
|
document.querySelectorAll(".para-propose").length
|
||||||
|
|
||||||
|
> 0 si editors
|
||||||
|
|
||||||
|
0 si non-editor
|
||||||
|
|
||||||
|
## Vérification côté NAS (build vars)
|
||||||
|
### 1) Blue et Green contiennent les constantes ?
|
||||||
|
|
||||||
|
P="/archicratie/archicrat-ia/chapitre-4/"
|
||||||
|
|
||||||
|
curl -sS "http://127.0.0.1:8081$P" | grep -n "const GITEA_BASE" | head -n 2
|
||||||
|
curl -sS "http://127.0.0.1:8082$P" | grep -n "const GITEA_BASE" | head -n 2
|
||||||
|
|
||||||
|
### 2) Si une des deux est vide → rebuild propre
|
||||||
|
|
||||||
|
Dans /volume2/docker/archicratie-web/current :
|
||||||
|
|
||||||
|
cat > .env <<'EOF'
|
||||||
|
PUBLIC_GITEA_BASE=https://gitea.archicratie.trans-hands.synology.me
|
||||||
|
PUBLIC_GITEA_OWNER=Archicratia
|
||||||
|
PUBLIC_GITEA_REPO=archicratie-edition
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sudo env DOCKER_API_VERSION=1.43 docker compose -f docker-compose.yml build --no-cache web_blue web_green
|
||||||
|
sudo env DOCKER_API_VERSION=1.43 docker compose -f docker-compose.yml up -d --force-recreate --no-build web_blue web_green
|
||||||
|
|
||||||
|
## Dépannage (si Proposer “disparaît”)
|
||||||
|
|
||||||
|
Vérifier groupes via /_auth/whoami
|
||||||
|
|
||||||
|
Vérifier const GITEA_BASE via curl sur le slot actif
|
||||||
|
|
||||||
|
Vérifier que Traefik sert bien le slot actif (grep via curl -H Host: ... http://127.0.0.1:18080/...)
|
||||||
|
|
||||||
|
Ouvrir la console : vérifier qu’aucune erreur JS n’empêche l’injection des outils paragraphe
|
||||||
@@ -379,6 +379,64 @@ const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? "";
|
|||||||
const FULL_TEXT_SOFT_LIMIT = 1600;
|
const FULL_TEXT_SOFT_LIMIT = 1600;
|
||||||
const URL_HARD_LIMIT = 6500;
|
const URL_HARD_LIMIT = 6500;
|
||||||
|
|
||||||
|
// ✅ Gate par groupe (Traefik→Authelia→LLDAP via /_auth/whoami)
|
||||||
|
const WHOAMI_PATH = "/_auth/whoami";
|
||||||
|
const PROPOSE_REQUIRED_GROUP = "editors";
|
||||||
|
|
||||||
|
let _authInfoPromise = null;
|
||||||
|
|
||||||
|
function parseWhoamiLine(text, key) {
|
||||||
|
const re = new RegExp(`^${key}:\\s*(.*)$`, "mi");
|
||||||
|
const m = String(text || "").match(re);
|
||||||
|
return (m?.[1] ?? "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAuthInfo() {
|
||||||
|
if (_authInfoPromise) return _authInfoPromise;
|
||||||
|
|
||||||
|
_authInfoPromise = (async () => {
|
||||||
|
const res = await fetch(`${WHOAMI_PATH}?_=${Date.now()}`, {
|
||||||
|
credentials: "include",
|
||||||
|
cache: "no-store",
|
||||||
|
redirect: "follow",
|
||||||
|
});
|
||||||
|
|
||||||
|
const text = await res.text().catch(() => "");
|
||||||
|
|
||||||
|
const groups = parseWhoamiLine(text, "Remote-Groups")
|
||||||
|
.split(",")
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
return {
|
||||||
|
ok: Boolean(res && res.ok),
|
||||||
|
user: parseWhoamiLine(text, "Remote-User"),
|
||||||
|
name: parseWhoamiLine(text, "Remote-Name"),
|
||||||
|
email: parseWhoamiLine(text, "Remote-Email"),
|
||||||
|
groups,
|
||||||
|
raw: text,
|
||||||
|
};
|
||||||
|
})().catch((err) => {
|
||||||
|
console.warn("[proposer] whoami fetch failed", err);
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
user: "",
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
groups: [],
|
||||||
|
raw: "",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return _authInfoPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Promise unique : est-on editor ?
|
||||||
|
// ⚠️ On reste fail-closed, mais NON destructif (on ne supprime pas sur erreur réseau)
|
||||||
|
const isEditorP = giteaReady
|
||||||
|
? getAuthInfo().then((info) => info.groups.includes(PROPOSE_REQUIRED_GROUP))
|
||||||
|
: Promise.resolve(false);
|
||||||
|
|
||||||
const quoteBlock = (s) =>
|
const quoteBlock = (s) =>
|
||||||
String(s || "")
|
String(s || "")
|
||||||
.split(/\r?\n/)
|
.split(/\r?\n/)
|
||||||
@@ -447,6 +505,24 @@ const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? "";
|
|||||||
// ==========================
|
// ==========================
|
||||||
const paras = Array.from(document.querySelectorAll('.reading p[id^="p-"]'));
|
const paras = Array.from(document.querySelectorAll('.reading p[id^="p-"]'));
|
||||||
|
|
||||||
|
// Petit helper : fail-closed mais réversible (hide ≠ remove)
|
||||||
|
function hidePropose(el) {
|
||||||
|
try {
|
||||||
|
el.hidden = true;
|
||||||
|
el.style.display = "none";
|
||||||
|
el.setAttribute("aria-hidden", "true");
|
||||||
|
el.setAttribute("tabindex", "-1");
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
function showPropose(el) {
|
||||||
|
try {
|
||||||
|
el.hidden = false;
|
||||||
|
el.style.display = "";
|
||||||
|
el.removeAttribute("aria-hidden");
|
||||||
|
el.removeAttribute("tabindex");
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
for (const p of paras) {
|
for (const p of paras) {
|
||||||
if (p.querySelector(".para-tools")) continue;
|
if (p.querySelector(".para-tools")) continue;
|
||||||
|
|
||||||
@@ -493,12 +569,16 @@ const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? "";
|
|||||||
propose.textContent = "Proposer";
|
propose.textContent = "Proposer";
|
||||||
propose.setAttribute("aria-label", "Proposer une correction sur Gitea");
|
propose.setAttribute("aria-label", "Proposer une correction sur Gitea");
|
||||||
|
|
||||||
|
// ✅ fail-closed (mais NON destructif)
|
||||||
|
propose.dataset.requiresGroup = PROPOSE_REQUIRED_GROUP;
|
||||||
|
hidePropose(propose);
|
||||||
|
|
||||||
const raw = (p.textContent || "").trim().replace(/\s+/g, " ");
|
const raw = (p.textContent || "").trim().replace(/\s+/g, " ");
|
||||||
const excerpt = raw.length > 420 ? (raw.slice(0, 420) + "…") : raw;
|
const excerpt = raw.length > 420 ? (raw.slice(0, 420) + "…") : raw;
|
||||||
|
|
||||||
const issueUrl = buildIssueURL(p.id, raw, excerpt);
|
const issueUrl = buildIssueURL(p.id, raw, excerpt);
|
||||||
|
|
||||||
// Lien fallback (si JS casse totalement)
|
// Lien fallback (si JS modal casse totalement)
|
||||||
propose.href = issueUrl;
|
propose.href = issueUrl;
|
||||||
|
|
||||||
// ✅ Marqueurs pour ProposeModal (interception 2 étapes)
|
// ✅ Marqueurs pour ProposeModal (interception 2 étapes)
|
||||||
@@ -506,9 +586,6 @@ const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? "";
|
|||||||
propose.dataset.url = issueUrl;
|
propose.dataset.url = issueUrl;
|
||||||
propose.dataset.full = raw;
|
propose.dataset.full = raw;
|
||||||
|
|
||||||
// ❌ PAS de target=_blank ici
|
|
||||||
// ❌ PAS de rel noopener ici
|
|
||||||
|
|
||||||
row.appendChild(propose);
|
row.appendChild(propose);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,6 +621,22 @@ const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? "";
|
|||||||
p.appendChild(tools);
|
p.appendChild(tools);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ Après insertion : on autorise Proposer seulement si groupe editors
|
||||||
|
// - ok=false => remove (pas d’UI “Proposer” pour les non-éditeurs)
|
||||||
|
// - erreur fetch => on garde HIDDEN (non destructif) ; un reload pourra réussir
|
||||||
|
if (giteaReady) {
|
||||||
|
isEditorP.then((ok) => {
|
||||||
|
const els = document.querySelectorAll(".para-propose");
|
||||||
|
for (const el of els) {
|
||||||
|
if (ok) showPropose(el);
|
||||||
|
else el.remove();
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.warn("[proposer] gate failed; keeping Proposer hidden", err);
|
||||||
|
document.querySelectorAll(".para-propose").forEach((el) => hidePropose(el));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-checkpoint
|
// Auto-checkpoint
|
||||||
let lastAuto = 0;
|
let lastAuto = 0;
|
||||||
function writeLastSeen(id) {
|
function writeLastSeen(id) {
|
||||||
|
|||||||
Reference in New Issue
Block a user