feat: intégration TikTak #197-200 + scoped tokens, webhooks filtrés, listes mtime/size ; fix e2e-server BUG-112
This commit is contained in:
+33
-5
@@ -1486,14 +1486,20 @@ function renderWebhooksUI(webhooks) {
|
||||
const list = document.getElementById("webhooks-list");
|
||||
if (!list) return;
|
||||
if (!webhooks.length) { list.innerHTML = '<div class="config-description">Aucun webhook configuré.</div>'; return; }
|
||||
list.innerHTML = webhooks.map(w => `
|
||||
list.innerHTML = webhooks.map(w => {
|
||||
const filters = [];
|
||||
if (w.vault) filters.push("vault:" + w.vault);
|
||||
if (w.path_prefix) filters.push("⇢ " + w.path_prefix);
|
||||
return `
|
||||
<div class="webhook-item">
|
||||
<span class="webhook-name">${escapeHtml(w.name)}</span>
|
||||
<span class="webhook-url">${escapeHtml(w.url)}</span>
|
||||
<span class="webhook-events">${(w.events||[]).join(", ")}</span>
|
||||
${filters.length ? `<span class="webhook-filter">${escapeHtml(filters.join(" · "))}</span>` : ""}
|
||||
<button class="webhook-delete" data-id="${w.id}">✕</button>
|
||||
</div>
|
||||
`).join("");
|
||||
`;
|
||||
}).join("");
|
||||
list.querySelectorAll(".webhook-delete").forEach(b => b.addEventListener("click", async () => {
|
||||
await api(`/api/webhooks/${b.dataset.id}`, { method: "DELETE" });
|
||||
loadWebhooksUI();
|
||||
@@ -1503,8 +1509,13 @@ document.addEventListener("click", function(e) {
|
||||
if (e.target.id === "webhook-add-btn") {
|
||||
const name = document.getElementById("webhook-name-input").value.trim();
|
||||
const url = document.getElementById("webhook-url-input").value.trim();
|
||||
const vaultEl = document.getElementById("webhook-vault-input");
|
||||
const prefixEl = document.getElementById("webhook-prefix-input");
|
||||
if (!url) { showToast(t("config.url_required"), "error"); return; }
|
||||
api("/api/webhooks", { method: "POST", body: JSON.stringify({ name: name || "Webhook", url, events: ["file_created","file_deleted","file_modified","file_renamed"] }) }).then(() => { loadWebhooksUI(); document.getElementById("webhook-name-input").value = ""; document.getElementById("webhook-url-input").value = ""; }).catch(err => showToast(err.message, "error"));
|
||||
const body = { name: name || "Webhook", url, events: ["file_created","file_deleted","file_modified","file_renamed"] };
|
||||
if (vaultEl && vaultEl.value.trim()) body.vault = vaultEl.value.trim();
|
||||
if (prefixEl && prefixEl.value.trim()) body.path_prefix = prefixEl.value.trim();
|
||||
api("/api/webhooks", { method: "POST", body: JSON.stringify(body) }).then(() => { loadWebhooksUI(); document.getElementById("webhook-name-input").value = ""; document.getElementById("webhook-url-input").value = ""; if (vaultEl) vaultEl.value = ""; if (prefixEl) prefixEl.value = ""; }).catch(err => showToast(err.message, "error"));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1546,6 +1557,12 @@ function renderTokensUI(tokens) {
|
||||
const status = expired
|
||||
? '<span class="token-badge token-badge-expired">' + escapeHtml(t("config.token_status_expired")) + "</span>"
|
||||
: '<span class="token-badge token-badge-active">' + escapeHtml(t("config.token_status_active")) + "</span>";
|
||||
// #199 : portée affichée (pleine vs fichiers seuls + vaults).
|
||||
const scopeLabel = (tok.scope || "full") === "files" ? t("config.token_scope_files") : t("config.token_scope_full");
|
||||
const scopeBadge = '<span class="token-badge token-badge-scope">' + escapeHtml(scopeLabel) + "</span>";
|
||||
const vaultsLabel = (tok.vaults && tok.vaults.length)
|
||||
? escapeHtml(tok.vaults.join(", "))
|
||||
: escapeHtml(t("config.token_vaults_all"));
|
||||
const meta = [
|
||||
t("config.token_created") + " " + _formatTokenDate(tok.created_at),
|
||||
t("config.token_expires") + " " + _tokenExpiryLabel(tok),
|
||||
@@ -1553,8 +1570,9 @@ function renderTokensUI(tokens) {
|
||||
].join(" · ");
|
||||
return '<div class="token-item" data-jti="' + escapeHtml(tok.jti) + '">' +
|
||||
'<span class="token-name">' + escapeHtml(tok.name) + "</span>" +
|
||||
status +
|
||||
status + scopeBadge +
|
||||
'<span class="token-meta">' + escapeHtml(meta) + "</span>" +
|
||||
'<span class="token-meta token-vaults">🔒 ' + vaultsLabel + "</span>" +
|
||||
'<button class="token-delete" data-jti="' + escapeHtml(tok.jti) + '" data-name="' + escapeHtml(tok.name) + '" title="' + escapeHtml(t("config.token_revoke")) + '">✕</button>' +
|
||||
"</div>";
|
||||
}).join("");
|
||||
@@ -1582,13 +1600,23 @@ function bindTokenEvents() {
|
||||
if (!name) { showToast(t("config.token_name_required"), "error"); return; }
|
||||
createBtn.disabled = true;
|
||||
try {
|
||||
const res = await api("/api/auth/tokens", { method: "POST", body: JSON.stringify({ name, expiry }) });
|
||||
// #199 : portée limitée optionnelle (vaults + périmètre fichiers).
|
||||
const scopeEl = document.getElementById("token-scope-select");
|
||||
const vaultsEl = document.getElementById("token-vaults-input");
|
||||
const body = { name, expiry };
|
||||
if (scopeEl && scopeEl.value === "files") body.scope = "files";
|
||||
if (vaultsEl && vaultsEl.value.trim()) {
|
||||
body.vaults = vaultsEl.value.split(",").map(s => s.trim()).filter(Boolean);
|
||||
}
|
||||
const res = await api("/api/auth/tokens", { method: "POST", body: JSON.stringify(body) });
|
||||
const area = document.getElementById("token-secret-area");
|
||||
const ta = document.getElementById("token-secret-value");
|
||||
ta.value = res.token;
|
||||
area.classList.remove("hidden");
|
||||
ta.select();
|
||||
document.getElementById("token-name-input").value = "";
|
||||
const _vaultsEl = document.getElementById("token-vaults-input");
|
||||
if (_vaultsEl) _vaultsEl.value = "";
|
||||
showToast(t("config.token_created_toast"), "success");
|
||||
loadTokensUI();
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user