system.html 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>系统监控 - PlateRecApp</title>
  7. <style>
  8. *{margin:0;padding:0;box-sizing:border-box}
  9. body{background:#0f172a;color:#e2e8f0;font-family:'Segoe UI',system-ui,sans-serif;min-height:100vh}
  10. .header{background:#1e293b;padding:16px 24px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #334155}
  11. .header h1{font-size:20px;font-weight:600;color:#f8fafc}
  12. .header a{color:#60a5fa;text-decoration:none;font-size:14px}
  13. .nav{display:flex;gap:16px;align-items:center}
  14. .nav a{padding:6px 12px;border-radius:6px;transition:background .2s}
  15. .nav a:hover{background:#334155}
  16. .container{max-width:1200px;margin:0 auto;padding:20px}
  17. .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px;margin-bottom:20px}
  18. .card{background:#1e293b;border-radius:12px;padding:20px;border:1px solid #334155}
  19. .card-title{font-size:13px;color:#94a3b8;text-transform:uppercase;letter-spacing:1px;margin-bottom:12px}
  20. .card-value{font-size:32px;font-weight:700;margin-bottom:4px}
  21. .card-sub{font-size:13px;color:#64748b}
  22. .progress-bar{height:8px;background:#334155;border-radius:4px;margin-top:8px;overflow:hidden}
  23. .progress-fill{height:100%;border-radius:4px;transition:width .5s}
  24. .green{color:#4ade80}.yellow{color:#facc15}.red{color:#f87171}
  25. .bg-green{background:#4ade80}.bg-yellow{background:#facc15}.bg-red{background:#f87171}
  26. .info-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;font-size:14px}
  27. .info-grid .label{color:#94a3b8}.info-grid .val{color:#e2e8f0;text-align:right}
  28. .alert-banner{background:#7f1d1d;border:1px solid #dc2626;border-radius:8px;padding:12px 16px;margin-bottom:16px;display:none;color:#fca5a5;font-size:14px}
  29. .refresh-info{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;font-size:13px;color:#64748b}
  30. @media (max-width: 768px) {
  31. .header { flex-direction: column; gap: 8px; padding: 12px 16px; }
  32. .header h1 { font-size: 16px; }
  33. .nav { flex-wrap: wrap; gap: 8px; justify-content: center; }
  34. .nav a { font-size: 12px; padding: 4px 8px; }
  35. .container { padding: 12px; }
  36. .grid { grid-template-columns: 1fr; gap: 12px; }
  37. .card { padding: 14px; }
  38. .card-value { font-size: 24px; }
  39. .info-grid { grid-template-columns: 1fr; gap: 4px; }
  40. .info-grid .val { text-align: left; }
  41. .refresh-info { flex-direction: column; gap: 8px; text-align: center; }
  42. }
  43. @media (max-width: 480px) {
  44. .card-value { font-size: 20px; }
  45. .card-title { font-size: 11px; }
  46. }
  47. </style>
  48. </head>
  49. <body>
  50. <div class="header">
  51. <h1>🖥️ 系统监控</h1>
  52. <div class="nav">
  53. <a href="/">监控台</a>
  54. <a href="/video">视频</a>
  55. <a href="/config">配置</a>
  56. <a href="/system" style="background:#334155">系统</a>
  57. <a href="/frpc">frpc</a>
  58. <a href="/ssh-keys">SSH</a>
  59. <a href="/logs">日志</a>
  60. <a href="/help">帮助</a>
  61. </div>
  62. </div>
  63. <div class="container">
  64. <div id="alert-banner" class="alert-banner"></div>
  65. <div class="refresh-info">
  66. <span>自动刷新: 每 <span id="interval">2</span> 秒</span>
  67. <span>最后更新: <span id="last-update">--</span></span>
  68. </div>
  69. <div class="grid">
  70. <div class="card">
  71. <div class="card-title">CPU 使用率</div>
  72. <div class="card-value" id="cpu-usage">--%</div>
  73. <div class="card-sub"><span id="cpu-cores">-</span> 核 | 负载: <span id="cpu-load">-</span></div>
  74. <div class="progress-bar"><div class="progress-fill bg-green" id="cpu-bar" style="width:0%"></div></div>
  75. </div>
  76. <div class="card">
  77. <div class="card-title">内存使用</div>
  78. <div class="card-value" id="mem-usage">--%</div>
  79. <div class="card-sub"><span id="mem-detail">-</span></div>
  80. <div class="progress-bar"><div class="progress-fill bg-green" id="mem-bar" style="width:0%"></div></div>
  81. </div>
  82. <div class="card">
  83. <div class="card-title">CPU 温度</div>
  84. <div class="card-value" id="cpu-temp">--°C</div>
  85. <div class="card-sub">告警阈值: <span id="temp-threshold">-</span>°C</div>
  86. <div class="progress-bar"><div class="progress-fill bg-green" id="temp-bar" style="width:0%"></div></div>
  87. </div>
  88. <div class="card">
  89. <div class="card-title">磁盘使用</div>
  90. <div class="card-value" id="disk-usage">--%</div>
  91. <div class="card-sub"><span id="disk-detail">-</span></div>
  92. <div class="progress-bar"><div class="progress-fill bg-green" id="disk-bar" style="width:0%"></div></div>
  93. </div>
  94. </div>
  95. <div class="grid">
  96. <div class="card">
  97. <div class="card-title">系统信息</div>
  98. <div class="info-grid">
  99. <span class="label">主机名</span><span class="val" id="hostname">-</span>
  100. <span class="label">内核版本</span><span class="val" id="kernel">-</span>
  101. <span class="label">运行时间</span><span class="val" id="uptime">-</span>
  102. <span class="label">进程PID</span><span class="val" id="pid">-</span>
  103. <span class="label">进程内存</span><span class="val" id="proc-rss">-</span>
  104. </div>
  105. </div>
  106. <div class="card">
  107. <div class="card-title">告警阈值</div>
  108. <div class="info-grid">
  109. <span class="label">CPU 告警</span><span class="val" id="cpu-threshold">-</span>
  110. <span class="label">温度告警</span><span class="val" id="temp-threshold2">-</span>
  111. </div>
  112. </div>
  113. </div>
  114. </div>
  115. <script>
  116. function getCookie(n){var m=document.cookie.match(new RegExp("(^| )"+n+"=([^;]+)"));return m?m[2]:"";}function authFetch(u,o){o=o||{};var h=o.headers||{};if(!(h instanceof Headers)){var nh=new Headers();for(var k in h)nh.set(k,h[k]);h=nh;}if(!h.has("X-CSRF-Token")){var t=getCookie("csrf_token");if(t)h.set("X-CSRF-Token",t);}o.headers=h;return fetch(u,o).then(function(r){if(r.status===401){window.location.href="/login?redirect="+encodeURIComponent(window.location.pathname);return new Promise(function(){});}return r;});}
  117. function getColor(pct){return pct<60?'green':pct<85?'yellow':'red'}
  118. function updateBar(id,pct,max){
  119. let bar=document.getElementById(id);
  120. bar.style.width=Math.min(pct/max*100,100)+'%';
  121. bar.className='progress-fill bg-'+getColor(pct);
  122. }
  123. function update(){
  124. authFetch('/api/system/status').then(r=>r.json()).then(d=>{
  125. // CPU
  126. document.getElementById('cpu-usage').textContent=d.cpu.usage.toFixed(1)+'%';
  127. document.getElementById('cpu-usage').className='card-value '+getColor(d.cpu.usage);
  128. document.getElementById('cpu-cores').textContent=d.cpu.cores;
  129. document.getElementById('cpu-load').textContent=d.cpu.load1.toFixed(2)+' / '+d.cpu.load5.toFixed(2)+' / '+d.cpu.load15.toFixed(2);
  130. updateBar('cpu-bar',d.cpu.usage,100);
  131. // Memory
  132. document.getElementById('mem-usage').textContent=d.memory.percent.toFixed(1)+'%';
  133. document.getElementById('mem-usage').className='card-value '+getColor(d.memory.percent);
  134. document.getElementById('mem-detail').textContent=d.memory.used+' MB / '+d.memory.total+' MB (可用 '+d.memory.available+' MB)';
  135. updateBar('mem-bar',d.memory.percent,100);
  136. // Temperature
  137. if(d.temperature.available){
  138. document.getElementById('cpu-temp').textContent=d.temperature.value.toFixed(1)+'°C';
  139. let tc=d.temperature.value>d.temperature.alert_threshold?'red':d.temperature.value>60?'yellow':'green';
  140. document.getElementById('cpu-temp').className='card-value '+tc;
  141. updateBar('temp-bar',d.temperature.value,d.temperature.alert_threshold*1.3);
  142. }else{
  143. document.getElementById('cpu-temp').textContent='不可用';
  144. document.getElementById('cpu-temp').className='card-value';
  145. }
  146. document.getElementById('temp-threshold').textContent=d.temperature.alert_threshold;
  147. // Disk
  148. document.getElementById('disk-usage').textContent=d.disk.percent.toFixed(1)+'%';
  149. document.getElementById('disk-usage').className='card-value '+getColor(d.disk.percent);
  150. document.getElementById('disk-detail').textContent=d.disk.used+' GB / '+d.disk.total+' GB (可用 '+d.disk.available+' GB)';
  151. updateBar('disk-bar',d.disk.percent,100);
  152. // System
  153. document.getElementById('hostname').textContent=d.system.hostname;
  154. document.getElementById('kernel').textContent=d.system.kernel;
  155. document.getElementById('uptime').textContent=d.system.uptime;
  156. document.getElementById('pid').textContent=d.process.pid;
  157. document.getElementById('proc-rss').textContent=d.process.rss_mb+' MB';
  158. document.getElementById('cpu-threshold').textContent=d.thresholds.cpu_alert+'%';
  159. document.getElementById('temp-threshold2').textContent=d.thresholds.temp_alert+'°C';
  160. // Alerts
  161. let alerts=[];
  162. if(d.cpu.usage>d.thresholds.cpu_alert) alerts.push('⚠️ CPU使用率 '+d.cpu.usage.toFixed(1)+'% 超过阈值 '+d.thresholds.cpu_alert+'%');
  163. if(d.temperature.available&&d.temperature.value>d.temperature.alert_threshold) alerts.push('⚠️ CPU温度 '+d.temperature.value.toFixed(1)+'°C 超过阈值 '+d.temperature.alert_threshold+'°C');
  164. let ab=document.getElementById('alert-banner');
  165. if(alerts.length){ab.style.display='block';ab.innerHTML=alerts.join('<br>')}else{ab.style.display='none'}
  166. document.getElementById('last-update').textContent=new Date().toLocaleTimeString();
  167. }).catch(e=>console.error(e));
  168. }
  169. update();
  170. setInterval(update,2000);
  171. </script>
  172. </body>
  173. </html>