Files
crypto_okx_hub/static/settings_backup.js
T

213 lines
7.0 KiB
JavaScript

(function (global) {
function escapeHtml(s) {
return String(s)
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
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 setText(id, msg, isErr) {
const el = document.getElementById(id);
if (!el) return;
el.textContent = msg || "";
el.style.color = isErr ? "var(--bad)" : "";
}
function renderRows(tbodyId, items, downloadHrefFn, restoreAttr) {
const body = document.getElementById(tbodyId);
if (!body) return;
if (!items || !items.length) {
body.innerHTML = '<tr><td colspan="4" class="muted">暂无备份</td></tr>';
return;
}
body.innerHTML = items
.map(function (it) {
const name = it.name || "";
const ops =
'<a class="btn btn-sm" href="' +
downloadHrefFn(name) +
'">下载</a>' +
(restoreAttr
? ' <button type="button" class="btn btn-sm btn-secondary" data-restore-name="' +
encodeURIComponent(name) +
'">恢复</button>'
: "");
return (
"<tr>" +
"<td><code>" +
escapeHtml(name) +
"</code></td>" +
"<td>" +
escapeHtml(it.mtime_bj || "—") +
"</td>" +
"<td>" +
escapeHtml(fmtSize(it.size)) +
"</td>" +
"<td style=\"white-space:nowrap\">" +
ops +
"</td>" +
"</tr>"
);
})
.join("");
}
async function loadHubList() {
const body = document.getElementById("hub-backup-body");
if (body) body.innerHTML = '<tr><td colspan="4" class="muted">加载中…</td></tr>';
try {
const r = await fetch("/api/settings/backup/hub/list", { credentials: "same-origin" });
const j = await r.json();
if (!j.ok) throw new Error(j.msg || "加载失败");
const dir = document.getElementById("hub-backup-dir");
if (dir && j.backup_dir) dir.textContent = j.backup_dir;
renderRows(
"hub-backup-body",
j.items || [],
function (name) {
return "/api/settings/backup/hub/download/" + encodeURIComponent(name);
},
true
);
} catch (e) {
if (body) {
body.innerHTML =
'<tr><td colspan="4" class="muted">' + escapeHtml(e.message || "加载失败") + "</td></tr>";
}
}
}
async function createHubBackup() {
setText("hub-backup-status", "备份中…");
try {
const r = await fetch("/api/settings/backup/hub/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 || "备份失败");
setText("hub-backup-status", "已生成 " + (j.name || ""));
await loadHubList();
} catch (e) {
setText("hub-backup-status", e.message || "备份失败", true);
}
}
async function restoreHub(name) {
if (!name) return;
if (!confirm("确定恢复该中控备份?\n将覆盖 .env 与 hub.db,建议恢复后重启。")) return;
setText("hub-backup-status", "恢复中…");
try {
const r = await fetch("/api/settings/backup/hub/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 || "恢复失败");
setText("hub-backup-status", j.msg || "恢复完成");
} catch (e) {
setText("hub-backup-status", e.message || "恢复失败", true);
}
}
async function loadInstances() {
const sel = document.getElementById("client-backup-instance");
if (!sel) return;
try {
const r = await fetch("/api/settings/backup/instances", { credentials: "same-origin" });
const j = await r.json();
if (!j.ok) throw new Error(j.msg || "加载实例失败");
const items = j.items || [];
sel.innerHTML =
'<option value="">选择客户端</option>' +
items
.map(function (it) {
return (
'<option value="' +
escapeHtml(String(it.id)) +
'">' +
escapeHtml(it.name || "#" + it.id) +
(it.enabled ? "" : " (停用)") +
"</option>"
);
})
.join("");
} catch (e) {
setText("client-backup-status", e.message || "加载实例失败", true);
}
}
async function loadClientList() {
const sel = document.getElementById("client-backup-instance");
const body = document.getElementById("client-backup-body");
const iid = sel && sel.value;
if (!iid) {
if (body) body.innerHTML = '<tr><td colspan="4" class="muted">请选择客户端</td></tr>';
return;
}
if (body) body.innerHTML = '<tr><td colspan="4" class="muted">加载中…</td></tr>';
setText("client-backup-status", "");
try {
const r = await fetch("/api/settings/backup/client/" + encodeURIComponent(iid) + "/list", {
credentials: "same-origin",
});
const j = await r.json();
if (!j.ok) throw new Error(j.msg || "加载失败");
renderRows(
"client-backup-body",
j.items || [],
function (name) {
return (
"/api/settings/backup/client/" +
encodeURIComponent(iid) +
"/download/" +
encodeURIComponent(name)
);
},
false
);
} catch (e) {
if (body) {
body.innerHTML =
'<tr><td colspan="4" class="muted">' + escapeHtml(e.message || "加载失败") + "</td></tr>";
}
setText("client-backup-status", e.message || "加载失败", true);
}
}
function boot() {
const createBtn = document.getElementById("hub-backup-create");
const hubRefresh = document.getElementById("hub-backup-refresh");
const clientRefresh = document.getElementById("client-backup-refresh");
const sel = document.getElementById("client-backup-instance");
if (createBtn) createBtn.addEventListener("click", createHubBackup);
if (hubRefresh) hubRefresh.addEventListener("click", loadHubList);
if (clientRefresh) clientRefresh.addEventListener("click", loadClientList);
if (sel) sel.addEventListener("change", loadClientList);
const hubBody = document.getElementById("hub-backup-body");
if (hubBody) {
hubBody.addEventListener("click", function (ev) {
const btn = ev.target.closest("[data-restore-name]");
if (!btn) return;
void restoreHub(decodeURIComponent(btn.getAttribute("data-restore-name") || ""));
});
}
void loadHubList();
void loadInstances();
}
global.HubSettingsBackup = { boot: boot };
})(window);