Compare commits
8 Commits
docs/norma
...
docs/runbo
| Author | SHA1 | Date | |
|---|---|---|---|
| add688602a | |||
| 3f3c717185 | |||
| b5f32da0c8 | |||
| 6e7ed8e041 | |||
| 90f79a7ee7 | |||
| b5663891a1 | |||
| a74b95e775 | |||
| b78eb4fc7b |
@@ -10,6 +10,15 @@ Si un seul de ces 3 paramètres est faux → on obtient :
|
||||
- 404 / redirect login inattendu
|
||||
- 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)
|
||||
|
||||
- `PUBLIC_GITEA_BASE`
|
||||
|
||||
@@ -83,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
|
||||
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é)
|
||||
|
||||
### DSM 7.3 :
|
||||
|
||||
@@ -43,61 +43,15 @@ Le flow ne doit jamais ouvrir deux onglets.
|
||||
- un seul `a.target="_blank"` (ou équivalent) déclenché
|
||||
- sur click : handler doit neutraliser les propagations parasites
|
||||
|
||||
### Vérification (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
|
||||
## Diagnostic (canonique)
|
||||
|
||||
Doit retourner 0 ligne.
|
||||
Le diagnostic détaillé est centralisé dans `docs/TROUBLESHOOTING.md` pour éviter les doublons.
|
||||
|
||||
## 3) Diagnostic “trace ouverture onglet” (navigateur)
|
||||
- 404 / non autorisé / redirect login :
|
||||
- voir : `TROUBLESHOOTING.md#proposer-404`
|
||||
- cause la plus fréquente : `PUBLIC_GITEA_OWNER/REPO` faux (souvent casse)
|
||||
|
||||
Dans la console, tu peux surcharger temporairement les mécanismes d’ouverture pour tracer :
|
||||
- Double onglet :
|
||||
- voir : `TROUBLESHOOTING.md#proposer-double-onglet`
|
||||
- cause la plus fréquente : double handler (bubbling) ou `window.open` + `a.click()`
|
||||
|
||||
si un window.open survient,
|
||||
|
||||
ou si un a.click target _blank est appelé.
|
||||
|
||||
But : prouver qu’il n’y a qu’un seul événement d’ouverture.
|
||||
|
||||
## 4) URL attendue (forme)
|
||||
|
||||
L’onglet doit ressembler à :
|
||||
|
||||
{PUBLIC_GITEA_BASE}/{OWNER}/{REPO}/issues/new?title=...&body=...
|
||||
|
||||
Important : owner et repo doivent être exactement ceux du repo canonique.
|
||||
|
||||
## 5) Pré-requis d’accès
|
||||
|
||||
L’utilisateur doit être loggé sur Gitea pour accéder à /issues/new
|
||||
|
||||
Si non loggé : redirect vers /user/login (comportement normal)
|
||||
|
||||
## 6) Tests fonctionnels (checklist)
|
||||
|
||||
Ouvrir une page chapitre (ex chapitre 4)
|
||||
|
||||
Clic Proposer (sur un paragraphe)
|
||||
|
||||
Choix 1 puis choix 2
|
||||
|
||||
Vérifier :
|
||||
|
||||
1 seul onglet
|
||||
|
||||
URL du repo correct
|
||||
|
||||
formulaire new issue visible
|
||||
|
||||
title/body pré-remplis (chemin + ancre + texte actuel)
|
||||
|
||||
Créer l’issue → vérifier le traitement CI/runner
|
||||
|
||||
## 7) Pannes typiques + causes
|
||||
|
||||
404 sur issue/new : PUBLIC_GITEA_OWNER/REPO faux (souvent casse)
|
||||
|
||||
2 onglets : double handler (bubbling + ouverture multiple)
|
||||
|
||||
pas de favicon : cache ou absence dans public/ → rebuild
|
||||
|
||||
@@ -11,6 +11,36 @@ Objectif : déployer une nouvelle version du site sur le NAS (DS220+) sans jamai
|
||||
|
||||
> 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
|
||||
Noms & domaines
|
||||
• Site public (prod) : https://archicratie.trans-hands.synology.me
|
||||
@@ -393,3 +423,5 @@ Fix standard (dans le vrai dossier site/) :
|
||||
rm -rf node_modules .astro dist
|
||||
npm ci
|
||||
npm run dev
|
||||
|
||||
</details>
|
||||
|
||||
@@ -4,7 +4,7 @@ Document “pivot” : liens, invariants, conventions, commandes réflexes.
|
||||
|
||||
## 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.
|
||||
- **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).
|
||||
|
||||
@@ -15,6 +15,7 @@ Toujours isoler : **Local**, **Gitea**, **NAS**, **Navigateur**.
|
||||
|
||||
---
|
||||
|
||||
<a id="proposer-404"></a>
|
||||
## 1) “Proposer” ouvre Gitea mais retourne 404 / non autorisé
|
||||
|
||||
### Symptôme
|
||||
@@ -38,6 +39,7 @@ Puis rebuild + restart du container + smoke.
|
||||
|
||||
---
|
||||
|
||||
<a id="proposer-double-onglet"></a>
|
||||
## 2) Double onglet à la validation du flow “Proposer”
|
||||
|
||||
### Symptôme
|
||||
@@ -64,7 +66,9 @@ 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
|
||||
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
|
||||
- Fichier versionné (ex) : `docs/anchor-aliases.json`
|
||||
- Fichier versionné (ex) : `src/anchors/anchor-aliases.json`
|
||||
- Format : `oldId -> newId` par page
|
||||
|
||||
Ex en json :
|
||||
|
||||
@@ -379,6 +379,64 @@ const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? "";
|
||||
const FULL_TEXT_SOFT_LIMIT = 1600;
|
||||
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) =>
|
||||
String(s || "")
|
||||
.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-"]'));
|
||||
|
||||
// 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) {
|
||||
if (p.querySelector(".para-tools")) continue;
|
||||
|
||||
@@ -493,12 +569,16 @@ const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? "";
|
||||
propose.textContent = "Proposer";
|
||||
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 excerpt = raw.length > 420 ? (raw.slice(0, 420) + "…") : raw;
|
||||
|
||||
const issueUrl = buildIssueURL(p.id, raw, excerpt);
|
||||
|
||||
// Lien fallback (si JS casse totalement)
|
||||
// Lien fallback (si JS modal casse totalement)
|
||||
propose.href = issueUrl;
|
||||
|
||||
// ✅ 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.full = raw;
|
||||
|
||||
// ❌ PAS de target=_blank ici
|
||||
// ❌ PAS de rel noopener ici
|
||||
|
||||
row.appendChild(propose);
|
||||
}
|
||||
|
||||
@@ -544,6 +621,22 @@ const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? "";
|
||||
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
|
||||
let lastAuto = 0;
|
||||
function writeLastSeen(id) {
|
||||
|
||||
Reference in New Issue
Block a user