/**
* 系统设置 · 备份恢复
*/
(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);