(function () { "use strict"; async function api(url, body, method) { var res = await fetch(url, { method: method || "POST", headers: { "content-type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), }); var json = null; try { json = await res.json(); } catch (e) {} if (!res.ok || (json && json.ok === false)) { if (res.status === 401) { location.href = "/login"; return new Promise(function () {}); } throw new Error((json && json.error && json.error.message) || "请求失败(" + res.status + ")"); } return json; } function toast(msg) { var el = document.querySelector(".toast"); if (!el) return; el.textContent = msg; el.classList.add("show"); clearTimeout(el._t); el._t = setTimeout(function () { el.classList.remove("show"); }, 2600); } function refresh() { setTimeout(function () { location.reload(); }, 500); } document.querySelectorAll(".admin-nav button[data-section]").forEach(function (btn) { btn.addEventListener("click", function () { document.querySelectorAll(".admin-nav button").forEach(function (b) { b.setAttribute("aria-current", "false"); }); btn.setAttribute("aria-current", "true"); var id = btn.getAttribute("data-section"); document.querySelectorAll(".admin-main > section").forEach(function (s) { s.classList.toggle("active", s.id === id); }); }); }); document.querySelectorAll(".tree-row[data-tree]").forEach(function (row) { row.addEventListener("click", function (e) { if (e.target.closest("button[data-tag-action]")) return; var target = document.getElementById(row.getAttribute("data-tree")); if (!target) return; var open = target.classList.toggle("open"); row.setAttribute("aria-expanded", open ? "true" : "false"); }); }); document.addEventListener("click", async function (e) { var btn = e.target.closest("[data-action]"); if (!btn) return; var action = btn.getAttribute("data-action"); try { if (action === "tag-add") { var parentId = btn.getAttribute("data-parent-id"); var name = prompt(parentId ? "新增子标签名称:" : "新增顶层标签组名称:"); if (!name) return; await api("/api/tags", { name: name, parentId: parentId ? Number(parentId) : null }); toast("标签已创建"); refresh(); } if (action === "tag-rename") { var name2 = prompt("重命名为:", btn.getAttribute("data-name")); if (!name2 || name2 === btn.getAttribute("data-name")) return; await api("/api/tags/" + btn.getAttribute("data-id"), { name: name2 }, "PATCH"); toast("已重命名"); refresh(); } if (action === "tag-move") { var target3 = prompt("移动到哪个标签之下?填其 ID(留空移到顶层):\n" + btn.getAttribute("data-ids")); if (target3 === null) return; await api("/api/tags/" + btn.getAttribute("data-id"), { parentId: target3.trim() ? Number(target3.trim()) : null }, "PATCH"); toast("已移动"); refresh(); } if (action === "tag-delete") { if (!confirm("删除该标签?其子标签将上移一级,关联关系解除。")) return; await api("/api/tags/" + btn.getAttribute("data-id"), undefined, "DELETE"); toast("已删除"); refresh(); } if (action === "entry-status") { await api("/api/entries/" + btn.getAttribute("data-id") + "/status", { status: btn.getAttribute("data-status") }, "PATCH"); toast("状态已更新"); refresh(); } if (action === "entry-delete") { if (!confirm("物理删除该条目?此操作不可恢复。")) return; await api("/api/entries/" + btn.getAttribute("data-id"), undefined, "DELETE"); toast("已删除"); refresh(); } if (action === "comment-delete") { if (!confirm("删除该评论?")) return; await api("/api/comments/" + btn.getAttribute("data-id"), undefined, "DELETE"); toast("已删除"); refresh(); } if (action === "user-disable") { var status = btn.getAttribute("data-status") === "active" ? "disabled" : "active"; await api("/api/admin/users/" + btn.getAttribute("data-id") + "/status", { status: status }, "PATCH"); toast("账号状态已更新"); refresh(); } } catch (err) { toast(err.message); } }); var userAddForm = document.getElementById("user-add-form"); if (userAddForm) { userAddForm.addEventListener("submit", function (e) { e.preventDefault(); api("/api/admin/users", { username: userAddForm.username.value.trim(), password: userAddForm.password.value, role: userAddForm.role.value, }) .then(function () { toast("账号已创建"); refresh(); }) .catch(function (err) { toast(err.message); }); }); } var aiKeyForm = document.getElementById("ai-key-form"); if (aiKeyForm) { aiKeyForm.addEventListener("submit", function (e) { e.preventDefault(); api("/api/admin/ai-key", { key: aiKeyForm.key.value.trim() }, "PUT") .then(function () { toast("AI 录入密钥已更新"); refresh(); }) .catch(function (err) { toast(err.message); }); }); } document.querySelectorAll("form.source-config").forEach(function (form) { form.addEventListener("submit", function (e) { e.preventDefault(); var id = form.getAttribute("data-id"); api("/api/admin/sources/" + id, { name: form.name.value, url: form.url.value, adapter: form.adapter.value, cron_expr: form.cron_expr.value, enabled: form.enabled.checked, }, "PATCH") .then(function () { toast("内容源已保存"); refresh(); }) .catch(function (err) { toast(err.message); }); }); }); document.querySelectorAll("[data-source-run]").forEach(function (btn) { btn.addEventListener("click", async function () { var id = btn.getAttribute("data-source-run"); btn.disabled = true; try { var json = await api("/api/admin/sources/" + id + "/run", {}); var s = json.data; toast(s.error ? "拉取失败:" + s.error : "完成:发现 " + s.found + " · 新增 " + s.created + " · 跳过 " + s.skipped); if (!s.error) refresh(); } catch (err) { toast(err.message); } finally { btn.disabled = false; } }); }); document.querySelectorAll("[data-source-runs]").forEach(function (btn) { btn.addEventListener("click", async function () { var box = document.getElementById("runs-" + btn.getAttribute("data-source-runs")); if (!box) return; if (!box.hidden) { box.hidden = true; return; } try { var json = await api("/api/admin/sources/" + btn.getAttribute("data-source-runs") + "/runs", undefined, "GET"); box.textContent = JSON.stringify(json.data, null, 2).slice(0, 3000); box.hidden = false; } catch (err) { toast(err.message); } }); }); var sourceAddForm = document.getElementById("source-add-form"); if (sourceAddForm) { sourceAddForm.addEventListener("submit", function (e) { e.preventDefault(); api("/api/admin/sources", { name: sourceAddForm.name.value.trim(), url: sourceAddForm.url.value.trim(), adapter: sourceAddForm.adapter.value, cron_expr: sourceAddForm.cron_expr.value.trim(), }) .then(function () { toast("拉取源已创建"); refresh(); }) .catch(function (err) { toast(err.message); }); }); } })();