/** * 系统设置 · 备份恢复 */ (function (global) { function statusEl() { return document.getElementById("backup-status"); } function setStatus(msg, isErr) { const el = statusEl(); if (!el) return; el.textContent = msg || ""; el.style.color = isErr ? "#fb7185" : ""; } function fmtSize(n) { const v = Number(n) || 0; if (v < 1024) return v + " B"; if (v < 1024 * 1024) return (v / 1024).toFixed(1) + " KB"; return (v / (1024 * 1024)).toFixed(2) + " MB"; } function escapeHtml(s) { return String(s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } async function loadList() { const body = document.getElementById("backup-list-body"); if (!body) return; body.innerHTML = '加载中…'; try { const r = await fetch("/api/settings/backup/list", { credentials: "same-origin" }); const j = await r.json(); if (!j.ok) throw new Error(j.msg || "加载失败"); const dir = document.getElementById("backup-dir-label"); if (dir && j.backup_dir) dir.textContent = j.backup_dir; const items = j.items || []; if (!items.length) { body.innerHTML = '暂无备份'; return; } body.innerHTML = items .map(function (it) { const name = escapeHtml(it.name || ""); return ( "" + "" + name + "" + "" + escapeHtml(it.mtime_bj || "—") + "" + "" + escapeHtml(fmtSize(it.size)) + "" + "" + '下载 ' + '' + "" + "" ); }) .join(""); } catch (e) { body.innerHTML = '' + escapeHtml(e.message || "加载失败") + ""; } } async function createBackup() { setStatus("备份中…"); try { const r = await fetch("/api/settings/backup/create", { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: "{}", }); const j = await r.json(); if (!j.ok) throw new Error(j.msg || "备份失败"); setStatus("已生成 " + (j.name || "备份")); await loadList(); } catch (e) { setStatus(e.message || "备份失败", true); } } async function restoreBackup(name) { if (!name) return; if (!confirm("确定用该备份恢复?\n将覆盖 .env、数据库与复盘图片,建议恢复后重启。")) return; setStatus("恢复中…"); try { const r = await fetch("/api/settings/backup/restore", { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: name }), }); const j = await r.json(); if (!j.ok) throw new Error(j.msg || "恢复失败"); setStatus(j.msg || "恢复完成"); } catch (e) { setStatus(e.message || "恢复失败", true); } } function boot() { const root = document.querySelector("[data-backup-root]"); if (!root || root.dataset.bound === "1") return; root.dataset.bound = "1"; const createBtn = document.getElementById("backup-create-btn"); const refreshBtn = document.getElementById("backup-refresh-btn"); if (createBtn) createBtn.addEventListener("click", createBackup); if (refreshBtn) refreshBtn.addEventListener("click", loadList); root.addEventListener("click", function (ev) { const btn = ev.target.closest("[data-backup-restore]"); if (!btn) return; const name = decodeURIComponent(btn.getAttribute("data-backup-restore") || ""); void restoreBackup(name); }); void loadList(); } global.InstanceBackup = { boot: boot, loadList: loadList }; })(typeof window !== "undefined" ? window : globalThis);