var cpuChart = null; var memChart = null; var diskChart = null; var sysStatusTimer = null; var resizeHandler = null; var sysAbortController = null; function getSystemInfo() { sysAbortController && sysAbortController.abort(); sysAbortController = new AbortController(); fetch("/api/system/info", { signal: sysAbortController.signal }) .then(function (r) { if (!r.ok) throw new Error("HTTP " + r.status); return r.json(); }) .then(function (d) { if (!cpuChart || !memChart || !diskChart) { return; } cpuChart.setOption({ graphic: [ { type: "text", left: "center", top: "75%", style: { text: (d.cpu_cores || "--") + "核 " + (d.cpu_freq || "--") + "MHz 温度" + (d.cpu_temp || "--"), fill: "#aaa", fontSize: 15, }, }, ], series: [ { data: [ { value: Number((d.cpu_usage != null ? d.cpu_usage : 0).toFixed(1)) }, ], }, ], }); memChart.setOption({ graphic: [ { type: "text", left: "center", top: "75%", style: { text: (d.mem_used || "--") + " / " + (d.mem_total || "--"), fill: "#aaa", fontSize: 15, }, }, ], series: [{ data: [{ value: d.mem_percent != null ? d.mem_percent : 0 }] }], }); diskChart.setOption({ title: { text: d.disk_percent + "%", subtext: (d.disk_used || "--") + " / " + (d.disk_total || "--"), left: "center", top: "38%", textStyle: { fontSize: 38, fontWeight: "bold", }, subtextStyle: { fontSize: 15, }, }, series: [ { data: [ { value: d.disk_percent != null ? d.disk_percent : 0, name: "已使用" }, { value: 100 - d.disk_percent, name: "剩余" }, ], }, ], }); }) .catch(function (e) { if (e.name !== "AbortError") { console.error("system info failed:", e); } }); } function gaugeOption() { return { series: [ { type: "gauge", radius: "95%", center: ["50%", "55%"], min: 0, max: 100, startAngle: 210, endAngle: -30, axisLine: { lineStyle: { width: 22, }, }, progress: { show: true, width: 22, }, pointer: { show: false }, axisTick: { show: false }, splitLine: { show: false }, axisLabel: { show: false }, detail: { fontSize: 42, fontWeight: "bold", offsetCenter: [0, "0%"], formatter: "{value}%", }, title: { show: false }, data: [{ value: 0 }], }, ], }; } function getLoadInfo() { fetch("/api/system/loadAverage") .then(function (r) { if (!r.ok) { throw new Error("HTTP " + r.status); } return r.json(); }) .then(function (data) { ["load1m", "load5m", "load15m", "runQueue", "bootTime", "uptime"].forEach( function (id) { return setText(id, data[id], "page1"); } ); }) .catch(function (e) { console.error("load info failed:", e); ["load1m", "load5m", "load15m", "runQueue", "bootTime", "uptime"].forEach( function (id) { return setText(id, null, "page1"); } ); }); } function getServiceInfo() { fetch("/api/system/serviceStatus") .then(function (r) { if (!r.ok) { throw new Error("HTTP " + r.status); } return r.json(); }) .then(function (data) { [ "var1", "var2", "var3", "var4", "var5", "var6", "var7", "var8", "var9", "var10", "var11", "var22", "var33", "var44", "var55", "var66", "var77", "var88", "var99", ].forEach(function (id) { return setText(id, data[id], "page1"); }); }) .catch(function (e) { console.error("getAllServiceStatus() failed:", e); [ "var1", "var2", "var3", "var4", "var5", "var6", "var7", "var8", "var9", "var10", "var11", "var22", "var33", "var44", "var55", "var66", "var77", "var88", "var99", ].forEach(function (id) { return setText(id, null, "page1"); }); }); } function startSysStatusRefresh() { if (sysStatusTimer) return; getSystemInfo(); getLoadInfo(); getServiceInfo(); sysStatusTimer = setInterval(function () { getSystemInfo(); getLoadInfo(); getServiceInfo(); }, 5000); } function stopSysStatusRefresh() { if (sysStatusTimer) { clearInterval(sysStatusTimer); sysStatusTimer = null; } } function resizeSysCharts() { cpuChart && cpuChart.resize(); memChart && memChart.resize(); diskChart && diskChart.resize(); } var serviceNames = { "yfkj-networkd.service": "设备自动联网", "yfkj-timesyncd.service": "系统时间同步", "yfkj-gnss.service": "地理坐标定位", "yfkj-camera-capture.service": "相机图像采集", "yfkj-sshd-mqtt-bridge.service": "远程运维通道", "yfkj-app-install.service": "应用安装卸载", "yfkj-web-ui.service": "本地配置管理", "yfkj-frp-client.service": "公网配置管理", "yfkj-upgrade.service": "开机自动升级", }; function switchServiceVersion(service, action) { var name = serviceNames[service] || service; var msg = action === "factory" ? "确认将【" + name + "】恢复到出厂版本吗?\n\n警告: 切换过程中服务会自动重启,请勿断开电源!" : "确认将【" + name + "】回退到上一版本吗?\n\n警告: 切换过程中服务会自动重启,请勿断开电源!"; if (!confirm(msg)) { return; } var password = prompt("请输入操作密码:"); if (password === null) { return; } password = password.trim(); fetch("/api/service/version/switch", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ service: service, action: action, password: password, }), }) .then(function (r) { return r.json(); }) .then(function (d) { alert( "【" + name + "】\n\n" + (d.status == "ok" ? "版本切换成功" : "版本切换失败:" + d.message) ); }) .catch(function (e) { alert("【" + name + "】\n\n请求失败:" + e.message); }); } function switchAutoUpgrade(event, action) { var enable = action.startsWith("enable"); var msg = enable ? "确认要启用 【开机自动升级】 吗?" : "确认要关闭 【开机自动升级】 吗?"; if (!confirm(msg)) { return; } var password = prompt("请输入操作密码:"); if (password === null) { return; } password = password.trim(); var btn = event.currentTarget; btn.disabled = true; fetch("/api/service/control", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ name: "yfkj-upgrade.service", action: action, secret: password, }), }) .then(function (r) { if (!r.ok) { return r.text().then(function (t) { throw new Error(t); }); } return r.json(); }) .then(function (d) { if (d.ok) { alert("【开机自动升级】\n\n" + (enable ? "已启用" : "已禁用")); } else { alert("操作失败"); } }) .catch(function (e) { alert("【开机自动升级】\n\n请求失败: " + e.message); }) .finally(function () { btn.disabled = false; }); } function downloadUpgradeLogFile(event) { var btn = event.currentTarget; btn.disabled = true; fetch("/api/upgrade/logfile") .then(function (r) { if (!r.ok) { throw new Error("HTTP " + r.status); } var disposition = r.headers.get("Content-Disposition"); var filename = "upgrade.log.tar.gz"; if (disposition) { var match = disposition.match(/filename="?([^";]+)"?/); if (match) { filename = match[1]; } } return r.blob().then(function (blob) { return { blob: blob, filename: filename }; }); }) .then(function (res) { downloadBlobFile(res.blob, res.filename); alert("下载成功"); }) .catch(function (e) { console.error("download failed:", e); alert("下载失败: " + e.message); }) .finally(function () { btn.disabled = false; btn.textContent = "下载升级日志"; }); } function startPublicAccess(event) { if (!confirm("确定开启公网访问吗?")) { return; } var password = prompt("请输入操作密码:"); if (password === null) { return; } password = password.trim(); var btn = event.currentTarget; btn.disabled = true; fetch("/api/service/control", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ name: "yfkj-frp-client.service", action: "start", secret: password, }), }) .then(function (r) { if (!r.ok) { return r.text().then(function (t) { throw new Error(t); }); } return r.json(); }) .then(function (d) { alert(d.ok ? "开启公网访问成功" : "开启公网访问失败"); }) .catch(function (e) { alert("开启公网访问失败: " + e.message); }) .finally(function () { btn.disabled = false; }); } function stopPublicAccess(event) { if (!confirm("确定关闭公网访问吗?")) { return; } var password = prompt("请输入操作密码:"); if (password === null) { return; } password = password.trim(); var btn = event.currentTarget; btn.disabled = true; fetch("/api/service/control", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ name: "yfkj-frp-client.service", action: "stop", secret: password, }), }) .then(function (r) { if (!r.ok) { return r.text().then(function (t) { throw new Error(t); }); } return r.json(); }) .then(function (d) { alert(d.ok ? "关闭公网访问成功" : "关闭公网访问失败"); }) .catch(function (e) { alert("关闭公网访问失败: " + e.message); }) .finally(function () { btn.disabled = false; }); } function initChart(id, option) { var dom = document.getElementById(id); if (!dom) { return null; } var chart = echarts.init(dom); chart.setOption(option); return chart; } function initPage1() { cpuChart = initChart("cpu-chart", gaugeOption()); memChart = initChart("mem-chart", gaugeOption()); diskChart = initChart("disk-chart", { title: { text: "0%", subtext: "-- / --", left: "center", top: "38%", textStyle: { fontSize: 38, fontWeight: "bold", }, subtextStyle: { fontSize: 15, }, }, series: [ { type: "pie", radius: ["65%", "85%"], label: { show: false }, data: [ { value: 0, name: "已使用" }, { value: 100, name: "剩余" }, ], }, ], }); resizeHandler = resizeSysCharts; window.addEventListener("resize", resizeHandler); waitChartResize(); startSysStatusRefresh(); } function waitChartResize(count) { if (count === undefined) { count = 0; } resizeSysCharts(); var dom = document.getElementById("cpu-chart"); if (dom && dom.clientWidth > 0 && dom.clientHeight > 0) { return; } dom = document.getElementById("mem-chart"); if (dom && dom.clientWidth > 0 && dom.clientHeight > 0) { return; } dom = document.getElementById("disk-chart"); if (dom && dom.clientWidth > 0 && dom.clientHeight > 0) { return; } if (count < 50) { setTimeout(function () { waitChartResize(count + 1); }, 100); } } function exitPage1() { sysAbortController && sysAbortController.abort(); sysAbortController = null; stopSysStatusRefresh(); if (resizeHandler) { window.removeEventListener("resize", resizeHandler); resizeHandler = null; } cpuChart && cpuChart.dispose(); memChart && memChart.dispose(); diskChart && diskChart.dispose(); cpuChart = null; memChart = null; diskChart = null; }