page1.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. var cpuChart = null;
  2. var memChart = null;
  3. var diskChart = null;
  4. var sysStatusTimer = null;
  5. var resizeHandler = null;
  6. var sysAbortController = null;
  7. function getSystemInfo() {
  8. sysAbortController && sysAbortController.abort();
  9. sysAbortController = new AbortController();
  10. fetch("/api/system/info", { signal: sysAbortController.signal })
  11. .then(function (r) {
  12. if (!r.ok) throw new Error("HTTP " + r.status);
  13. return r.json();
  14. })
  15. .then(function (d) {
  16. if (!cpuChart || !memChart || !diskChart) {
  17. return;
  18. }
  19. cpuChart.setOption({
  20. graphic: [
  21. {
  22. type: "text",
  23. left: "center",
  24. top: "75%",
  25. style: {
  26. text:
  27. (d.cpu_cores || "--") +
  28. "核 " +
  29. (d.cpu_freq || "--") +
  30. "MHz 温度" +
  31. (d.cpu_temp || "--"),
  32. fill: "#aaa",
  33. fontSize: 15,
  34. },
  35. },
  36. ],
  37. series: [
  38. {
  39. data: [
  40. { value: Number((d.cpu_usage != null ? d.cpu_usage : 0).toFixed(1)) },
  41. ],
  42. },
  43. ],
  44. });
  45. memChart.setOption({
  46. graphic: [
  47. {
  48. type: "text",
  49. left: "center",
  50. top: "75%",
  51. style: {
  52. text: (d.mem_used || "--") + " / " + (d.mem_total || "--"),
  53. fill: "#aaa",
  54. fontSize: 15,
  55. },
  56. },
  57. ],
  58. series: [{ data: [{ value: d.mem_percent != null ? d.mem_percent : 0 }] }],
  59. });
  60. diskChart.setOption({
  61. title: {
  62. text: d.disk_percent + "%",
  63. subtext: (d.disk_used || "--") + " / " + (d.disk_total || "--"),
  64. left: "center",
  65. top: "38%",
  66. textStyle: {
  67. fontSize: 38,
  68. fontWeight: "bold",
  69. },
  70. subtextStyle: {
  71. fontSize: 15,
  72. },
  73. },
  74. series: [
  75. {
  76. data: [
  77. { value: d.disk_percent != null ? d.disk_percent : 0, name: "已使用" },
  78. { value: 100 - d.disk_percent, name: "剩余" },
  79. ],
  80. },
  81. ],
  82. });
  83. })
  84. .catch(function (e) {
  85. if (e.name !== "AbortError") {
  86. console.error("system info failed:", e);
  87. }
  88. });
  89. }
  90. function gaugeOption() {
  91. return {
  92. series: [
  93. {
  94. type: "gauge",
  95. radius: "95%",
  96. center: ["50%", "55%"],
  97. min: 0,
  98. max: 100,
  99. startAngle: 210,
  100. endAngle: -30,
  101. axisLine: {
  102. lineStyle: {
  103. width: 22,
  104. },
  105. },
  106. progress: {
  107. show: true,
  108. width: 22,
  109. },
  110. pointer: { show: false },
  111. axisTick: { show: false },
  112. splitLine: { show: false },
  113. axisLabel: { show: false },
  114. detail: {
  115. fontSize: 42,
  116. fontWeight: "bold",
  117. offsetCenter: [0, "0%"],
  118. formatter: "{value}%",
  119. },
  120. title: { show: false },
  121. data: [{ value: 0 }],
  122. },
  123. ],
  124. };
  125. }
  126. function getLoadInfo() {
  127. fetch("/api/system/loadAverage")
  128. .then(function (r) {
  129. if (!r.ok) {
  130. throw new Error("HTTP " + r.status);
  131. }
  132. return r.json();
  133. })
  134. .then(function (data) {
  135. ["load1m", "load5m", "load15m", "runQueue", "bootTime", "uptime"].forEach(
  136. function (id) {
  137. return setText(id, data[id], "page1");
  138. }
  139. );
  140. })
  141. .catch(function (e) {
  142. console.error("load info failed:", e);
  143. ["load1m", "load5m", "load15m", "runQueue", "bootTime", "uptime"].forEach(
  144. function (id) {
  145. return setText(id, null, "page1");
  146. }
  147. );
  148. });
  149. }
  150. function getServiceInfo() {
  151. fetch("/api/system/serviceStatus")
  152. .then(function (r) {
  153. if (!r.ok) {
  154. throw new Error("HTTP " + r.status);
  155. }
  156. return r.json();
  157. })
  158. .then(function (data) {
  159. [
  160. "var1",
  161. "var2",
  162. "var3",
  163. "var4",
  164. "var5",
  165. "var6",
  166. "var7",
  167. "var8",
  168. "var9",
  169. "var10",
  170. "var11",
  171. "var22",
  172. "var33",
  173. "var44",
  174. "var55",
  175. "var66",
  176. "var77",
  177. "var88",
  178. "var99",
  179. ].forEach(function (id) {
  180. return setText(id, data[id], "page1");
  181. });
  182. })
  183. .catch(function (e) {
  184. console.error("getAllServiceStatus() failed:", e);
  185. [
  186. "var1",
  187. "var2",
  188. "var3",
  189. "var4",
  190. "var5",
  191. "var6",
  192. "var7",
  193. "var8",
  194. "var9",
  195. "var10",
  196. "var11",
  197. "var22",
  198. "var33",
  199. "var44",
  200. "var55",
  201. "var66",
  202. "var77",
  203. "var88",
  204. "var99",
  205. ].forEach(function (id) {
  206. return setText(id, null, "page1");
  207. });
  208. });
  209. }
  210. function startSysStatusRefresh() {
  211. if (sysStatusTimer) return;
  212. getSystemInfo();
  213. getLoadInfo();
  214. getServiceInfo();
  215. sysStatusTimer = setInterval(function () {
  216. getSystemInfo();
  217. getLoadInfo();
  218. getServiceInfo();
  219. }, 5000);
  220. }
  221. function stopSysStatusRefresh() {
  222. if (sysStatusTimer) {
  223. clearInterval(sysStatusTimer);
  224. sysStatusTimer = null;
  225. }
  226. }
  227. function resizeSysCharts() {
  228. cpuChart && cpuChart.resize();
  229. memChart && memChart.resize();
  230. diskChart && diskChart.resize();
  231. }
  232. var serviceNames = {
  233. "yfkj-networkd.service": "设备自动联网",
  234. "yfkj-timesyncd.service": "系统时间同步",
  235. "yfkj-gnss.service": "地理坐标定位",
  236. "yfkj-camera-capture.service": "相机图像采集",
  237. "yfkj-sshd-mqtt-bridge.service": "远程运维通道",
  238. "yfkj-app-install.service": "应用安装卸载",
  239. "yfkj-web-ui.service": "本地配置管理",
  240. "yfkj-frp-client.service": "公网配置管理",
  241. "yfkj-upgrade.service": "开机自动升级",
  242. };
  243. function switchServiceVersion(service, action) {
  244. var name = serviceNames[service] || service;
  245. var msg =
  246. action === "factory"
  247. ? "确认将【" +
  248. name +
  249. "】恢复到出厂版本吗?\n\n警告: 切换过程中服务会自动重启,请勿断开电源!"
  250. : "确认将【" +
  251. name +
  252. "】回退到上一版本吗?\n\n警告: 切换过程中服务会自动重启,请勿断开电源!";
  253. if (!confirm(msg)) {
  254. return;
  255. }
  256. var password = prompt("请输入操作密码:");
  257. if (password === null) {
  258. return;
  259. }
  260. password = password.trim();
  261. fetch("/api/service/version/switch", {
  262. method: "POST",
  263. headers: {
  264. "Content-Type": "application/json",
  265. },
  266. body: JSON.stringify({
  267. service: service,
  268. action: action,
  269. password: password,
  270. }),
  271. })
  272. .then(function (r) {
  273. return r.json();
  274. })
  275. .then(function (d) {
  276. alert(
  277. "【" +
  278. name +
  279. "】\n\n" +
  280. (d.status == "ok" ? "版本切换成功" : "版本切换失败:" + d.message)
  281. );
  282. })
  283. .catch(function (e) {
  284. alert("【" + name + "】\n\n请求失败:" + e.message);
  285. });
  286. }
  287. function switchAutoUpgrade(event, action) {
  288. var enable = action.startsWith("enable");
  289. var msg = enable ? "确认要启用 【开机自动升级】 吗?" : "确认要关闭 【开机自动升级】 吗?";
  290. if (!confirm(msg)) {
  291. return;
  292. }
  293. var password = prompt("请输入操作密码:");
  294. if (password === null) {
  295. return;
  296. }
  297. password = password.trim();
  298. var btn = event.currentTarget;
  299. btn.disabled = true;
  300. fetch("/api/service/control", {
  301. method: "POST",
  302. headers: {
  303. "Content-Type": "application/json",
  304. },
  305. body: JSON.stringify({
  306. name: "yfkj-upgrade.service",
  307. action: action,
  308. secret: password,
  309. }),
  310. })
  311. .then(function (r) {
  312. if (!r.ok) {
  313. return r.text().then(function (t) {
  314. throw new Error(t);
  315. });
  316. }
  317. return r.json();
  318. })
  319. .then(function (d) {
  320. if (d.ok) {
  321. alert("【开机自动升级】\n\n" + (enable ? "已启用" : "已禁用"));
  322. } else {
  323. alert("操作失败");
  324. }
  325. })
  326. .catch(function (e) {
  327. alert("【开机自动升级】\n\n请求失败: " + e.message);
  328. })
  329. .finally(function () {
  330. btn.disabled = false;
  331. });
  332. }
  333. function downloadUpgradeLogFile(event) {
  334. var btn = event.currentTarget;
  335. btn.disabled = true;
  336. fetch("/api/upgrade/logfile")
  337. .then(function (r) {
  338. if (!r.ok) {
  339. throw new Error("HTTP " + r.status);
  340. }
  341. var disposition = r.headers.get("Content-Disposition");
  342. var filename = "upgrade.log.tar.gz";
  343. if (disposition) {
  344. var match = disposition.match(/filename="?([^";]+)"?/);
  345. if (match) {
  346. filename = match[1];
  347. }
  348. }
  349. return r.blob().then(function (blob) {
  350. return { blob: blob, filename: filename };
  351. });
  352. })
  353. .then(function (res) {
  354. downloadBlobFile(res.blob, res.filename);
  355. alert("下载成功");
  356. })
  357. .catch(function (e) {
  358. console.error("download failed:", e);
  359. alert("下载失败: " + e.message);
  360. })
  361. .finally(function () {
  362. btn.disabled = false;
  363. btn.textContent = "下载升级日志";
  364. });
  365. }
  366. function startPublicAccess(event) {
  367. if (!confirm("确定开启公网访问吗?")) {
  368. return;
  369. }
  370. var password = prompt("请输入操作密码:");
  371. if (password === null) {
  372. return;
  373. }
  374. password = password.trim();
  375. var btn = event.currentTarget;
  376. btn.disabled = true;
  377. fetch("/api/service/control", {
  378. method: "POST",
  379. headers: {
  380. "Content-Type": "application/json",
  381. },
  382. body: JSON.stringify({
  383. name: "yfkj-frp-client.service",
  384. action: "start",
  385. secret: password,
  386. }),
  387. })
  388. .then(function (r) {
  389. if (!r.ok) {
  390. return r.text().then(function (t) {
  391. throw new Error(t);
  392. });
  393. }
  394. return r.json();
  395. })
  396. .then(function (d) {
  397. alert(d.ok ? "开启公网访问成功" : "开启公网访问失败");
  398. })
  399. .catch(function (e) {
  400. alert("开启公网访问失败: " + e.message);
  401. })
  402. .finally(function () {
  403. btn.disabled = false;
  404. });
  405. }
  406. function stopPublicAccess(event) {
  407. if (!confirm("确定关闭公网访问吗?")) {
  408. return;
  409. }
  410. var password = prompt("请输入操作密码:");
  411. if (password === null) {
  412. return;
  413. }
  414. password = password.trim();
  415. var btn = event.currentTarget;
  416. btn.disabled = true;
  417. fetch("/api/service/control", {
  418. method: "POST",
  419. headers: {
  420. "Content-Type": "application/json",
  421. },
  422. body: JSON.stringify({
  423. name: "yfkj-frp-client.service",
  424. action: "stop",
  425. secret: password,
  426. }),
  427. })
  428. .then(function (r) {
  429. if (!r.ok) {
  430. return r.text().then(function (t) {
  431. throw new Error(t);
  432. });
  433. }
  434. return r.json();
  435. })
  436. .then(function (d) {
  437. alert(d.ok ? "关闭公网访问成功" : "关闭公网访问失败");
  438. })
  439. .catch(function (e) {
  440. alert("关闭公网访问失败: " + e.message);
  441. })
  442. .finally(function () {
  443. btn.disabled = false;
  444. });
  445. }
  446. function initChart(id, option) {
  447. var dom = document.getElementById(id);
  448. if (!dom) {
  449. return null;
  450. }
  451. var chart = echarts.init(dom);
  452. chart.setOption(option);
  453. return chart;
  454. }
  455. function initPage1() {
  456. cpuChart = initChart("cpu-chart", gaugeOption());
  457. memChart = initChart("mem-chart", gaugeOption());
  458. diskChart = initChart("disk-chart", {
  459. title: {
  460. text: "0%",
  461. subtext: "-- / --",
  462. left: "center",
  463. top: "38%",
  464. textStyle: {
  465. fontSize: 38,
  466. fontWeight: "bold",
  467. },
  468. subtextStyle: {
  469. fontSize: 15,
  470. },
  471. },
  472. series: [
  473. {
  474. type: "pie",
  475. radius: ["65%", "85%"],
  476. label: { show: false },
  477. data: [
  478. { value: 0, name: "已使用" },
  479. { value: 100, name: "剩余" },
  480. ],
  481. },
  482. ],
  483. });
  484. resizeHandler = resizeSysCharts;
  485. window.addEventListener("resize", resizeHandler);
  486. waitChartResize();
  487. startSysStatusRefresh();
  488. }
  489. function waitChartResize(count) {
  490. if (count === undefined) {
  491. count = 0;
  492. }
  493. resizeSysCharts();
  494. var dom = document.getElementById("cpu-chart");
  495. if (dom && dom.clientWidth > 0 && dom.clientHeight > 0) {
  496. return;
  497. }
  498. dom = document.getElementById("mem-chart");
  499. if (dom && dom.clientWidth > 0 && dom.clientHeight > 0) {
  500. return;
  501. }
  502. dom = document.getElementById("disk-chart");
  503. if (dom && dom.clientWidth > 0 && dom.clientHeight > 0) {
  504. return;
  505. }
  506. if (count < 50) {
  507. setTimeout(function () {
  508. waitChartResize(count + 1);
  509. }, 100);
  510. }
  511. }
  512. function exitPage1() {
  513. sysAbortController && sysAbortController.abort();
  514. sysAbortController = null;
  515. stopSysStatusRefresh();
  516. if (resizeHandler) {
  517. window.removeEventListener("resize", resizeHandler);
  518. resizeHandler = null;
  519. }
  520. cpuChart && cpuChart.dispose();
  521. memChart && memChart.dispose();
  522. diskChart && diskChart.dispose();
  523. cpuChart = null;
  524. memChart = null;
  525. diskChart = null;
  526. }