Compare commits
4 Commits
archicrati
...
chore/dock
| Author | SHA1 | Date | |
|---|---|---|---|
| bb4610f49d | |||
| b5f32da0c8 | |||
| 6e7ed8e041 | |||
| 90f79a7ee7 |
@@ -7,7 +7,8 @@ WORKDIR /app
|
|||||||
ENV npm_config_update_notifier=false \
|
ENV npm_config_update_notifier=false \
|
||||||
npm_config_audit=false \
|
npm_config_audit=false \
|
||||||
npm_config_fund=false \
|
npm_config_fund=false \
|
||||||
npm_config_progress=false
|
npm_config_progress=false \
|
||||||
|
ASTRO_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
# (Optionnel mais propre) git + certificats
|
# (Optionnel mais propre) git + certificats
|
||||||
RUN apt-get -o Acquire::Retries=5 -o Acquire::ForceIPv4=true update \
|
RUN apt-get -o Acquire::Retries=5 -o Acquire::ForceIPv4=true update \
|
||||||
@@ -16,7 +17,8 @@ RUN apt-get -o Acquire::Retries=5 -o Acquire::ForceIPv4=true update \
|
|||||||
|
|
||||||
# Déps d’abord (cache Docker)
|
# Déps d’abord (cache Docker)
|
||||||
COPY package.json package-lock.json ./
|
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
|
# Sources
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|||||||
@@ -379,6 +379,60 @@ 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(() => ({
|
||||||
|
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) =>
|
const quoteBlock = (s) =>
|
||||||
String(s || "")
|
String(s || "")
|
||||||
.split(/\r?\n/)
|
.split(/\r?\n/)
|
||||||
@@ -493,6 +547,10 @@ 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 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 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;
|
||||||
|
|
||||||
@@ -506,9 +564,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 +599,20 @@ 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
|
||||||
|
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
|
// Auto-checkpoint
|
||||||
let lastAuto = 0;
|
let lastAuto = 0;
|
||||||
function writeLastSeen(id) {
|
function writeLastSeen(id) {
|
||||||
|
|||||||
Reference in New Issue
Block a user