Compare commits

..

4 Commits

Author SHA1 Message Date
bb4610f49d Docker: enable BuildKit cache for npm ci
All checks were successful
CI / build-and-anchors (push) Successful in 1m32s
SMOKE / smoke (push) Successful in 10s
2026-02-12 11:20:27 +01:00
b5f32da0c8 Merge pull request 'Gate 'Proposer' to editors via /_auth/whoami' (#74) from feat/proposer-editors-only into main
All checks were successful
CI / build-and-anchors (push) Successful in 1m29s
SMOKE / smoke (push) Successful in 14s
Reviewed-on: #74
2026-02-12 09:47:03 +01:00
6e7ed8e041 Gate 'Proposer' to editors via /_auth/whoami
All checks were successful
CI / build-and-anchors (push) Successful in 1m57s
SMOKE / smoke (push) Successful in 13s
2026-02-12 09:46:30 +01:00
90f79a7ee7 Merge pull request 'Supprimer docs/SESSION_BILAN_CI_RUNNER_DNS_2026-01.md' (#71) from archicratia-patch-1 into main
All checks were successful
CI / build-and-anchors (push) Successful in 1m36s
SMOKE / smoke (push) Successful in 24s
Reviewed-on: #71
2026-02-02 12:13:12 +01:00
2 changed files with 77 additions and 6 deletions

View File

@@ -7,16 +7,18 @@ WORKDIR /app
ENV npm_config_update_notifier=false \
npm_config_audit=false \
npm_config_fund=false \
npm_config_progress=false
npm_config_progress=false \
ASTRO_TELEMETRY_DISABLED=1
# (Optionnel mais propre) git + certificats
RUN apt-get -o Acquire::Retries=5 -o Acquire::ForceIPv4=true update \
&& apt-get install -y --no-install-recommends ca-certificates git \
&& rm -rf /var/lib/apt/lists/*
&& rm -rf /var/lib/apt/lists/*
# Déps dabord (cache Docker)
COPY package.json package-lock.json ./
RUN npm ci --no-audit --no-fund
RUN --mount=type=cache,target=/root/.npm \
npm ci --no-audit --no-fund
# Sources
COPY . .

View File

@@ -379,6 +379,60 @@ 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(() => ({
ok: false,
user: "",
name: "",
email: "",
groups: [],
raw: "",
}));
return _authInfoPromise;
}
// Promise unique : est-on editor ?
const isEditorP = giteaReady
? getAuthInfo().then((info) => info.groups.includes(PROPOSE_REQUIRED_GROUP)).catch(() => false)
: Promise.resolve(false);
const quoteBlock = (s) =>
String(s || "")
.split(/\r?\n/)
@@ -493,6 +547,10 @@ const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? "";
propose.textContent = "Proposer";
propose.setAttribute("aria-label", "Proposer une correction sur Gitea");
// ✅ fail-closed DUR : inline-style (ne peut pas être overridé par ton CSS)
propose.style.display = "none";
propose.dataset.requiresGroup = PROPOSE_REQUIRED_GROUP;
const raw = (p.textContent || "").trim().replace(/\s+/g, " ");
const excerpt = raw.length > 420 ? (raw.slice(0, 420) + "…") : raw;
@@ -506,9 +564,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 +599,20 @@ const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? "";
p.appendChild(tools);
}
// ✅ Après insertion : on autorise Proposer seulement si groupe editors
if (giteaReady) {
isEditorP.then((ok) => {
const els = document.querySelectorAll(".para-propose");
for (const el of els) {
if (ok) el.style.display = "";
else el.remove();
}
}).catch(() => {
// fail-closed
document.querySelectorAll(".para-propose").forEach((el) => el.remove());
});
}
// Auto-checkpoint
let lastAuto = 0;
function writeLastSeen(id) {