| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>系统监控 - PlateRecApp</title>
- <style>
- *{margin:0;padding:0;box-sizing:border-box}
- body{background:#0f172a;color:#e2e8f0;font-family:'Segoe UI',system-ui,sans-serif;min-height:100vh}
- .header{background:#1e293b;padding:16px 24px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #334155}
- .header h1{font-size:20px;font-weight:600;color:#f8fafc}
- .header a{color:#60a5fa;text-decoration:none;font-size:14px}
- .nav{display:flex;gap:16px;align-items:center}
- .nav a{padding:6px 12px;border-radius:6px;transition:background .2s}
- .nav a:hover{background:#334155}
- .container{max-width:1200px;margin:0 auto;padding:20px}
- .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px;margin-bottom:20px}
- .card{background:#1e293b;border-radius:12px;padding:20px;border:1px solid #334155}
- .card-title{font-size:13px;color:#94a3b8;text-transform:uppercase;letter-spacing:1px;margin-bottom:12px}
- .card-value{font-size:32px;font-weight:700;margin-bottom:4px}
- .card-sub{font-size:13px;color:#64748b}
- .progress-bar{height:8px;background:#334155;border-radius:4px;margin-top:8px;overflow:hidden}
- .progress-fill{height:100%;border-radius:4px;transition:width .5s}
- .green{color:#4ade80}.yellow{color:#facc15}.red{color:#f87171}
- .bg-green{background:#4ade80}.bg-yellow{background:#facc15}.bg-red{background:#f87171}
- .info-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;font-size:14px}
- .info-grid .label{color:#94a3b8}.info-grid .val{color:#e2e8f0;text-align:right}
- .alert-banner{background:#7f1d1d;border:1px solid #dc2626;border-radius:8px;padding:12px 16px;margin-bottom:16px;display:none;color:#fca5a5;font-size:14px}
- .refresh-info{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;font-size:13px;color:#64748b}
- @media (max-width: 768px) {
- .header { flex-direction: column; gap: 8px; padding: 12px 16px; }
- .header h1 { font-size: 16px; }
- .nav { flex-wrap: wrap; gap: 8px; justify-content: center; }
- .nav a { font-size: 12px; padding: 4px 8px; }
- .container { padding: 12px; }
- .grid { grid-template-columns: 1fr; gap: 12px; }
- .card { padding: 14px; }
- .card-value { font-size: 24px; }
- .info-grid { grid-template-columns: 1fr; gap: 4px; }
- .info-grid .val { text-align: left; }
- .refresh-info { flex-direction: column; gap: 8px; text-align: center; }
- }
- @media (max-width: 480px) {
- .card-value { font-size: 20px; }
- .card-title { font-size: 11px; }
- }
- </style>
- </head>
- <body>
- <div class="header">
- <h1>🖥️ 系统监控</h1>
- <div class="nav">
- <a href="/">监控台</a>
- <a href="/video">视频</a>
- <a href="/config">配置</a>
- <a href="/system" style="background:#334155">系统</a>
- <a href="/frpc">frpc</a>
- <a href="/ssh-keys">SSH</a>
- <a href="/logs">日志</a>
- <a href="/help">帮助</a>
- </div>
- </div>
- <div class="container">
- <div id="alert-banner" class="alert-banner"></div>
- <div class="refresh-info">
- <span>自动刷新: 每 <span id="interval">2</span> 秒</span>
- <span>最后更新: <span id="last-update">--</span></span>
- </div>
- <div class="grid">
- <div class="card">
- <div class="card-title">CPU 使用率</div>
- <div class="card-value" id="cpu-usage">--%</div>
- <div class="card-sub"><span id="cpu-cores">-</span> 核 | 负载: <span id="cpu-load">-</span></div>
- <div class="progress-bar"><div class="progress-fill bg-green" id="cpu-bar" style="width:0%"></div></div>
- </div>
- <div class="card">
- <div class="card-title">内存使用</div>
- <div class="card-value" id="mem-usage">--%</div>
- <div class="card-sub"><span id="mem-detail">-</span></div>
- <div class="progress-bar"><div class="progress-fill bg-green" id="mem-bar" style="width:0%"></div></div>
- </div>
- <div class="card">
- <div class="card-title">CPU 温度</div>
- <div class="card-value" id="cpu-temp">--°C</div>
- <div class="card-sub">告警阈值: <span id="temp-threshold">-</span>°C</div>
- <div class="progress-bar"><div class="progress-fill bg-green" id="temp-bar" style="width:0%"></div></div>
- </div>
- <div class="card">
- <div class="card-title">磁盘使用</div>
- <div class="card-value" id="disk-usage">--%</div>
- <div class="card-sub"><span id="disk-detail">-</span></div>
- <div class="progress-bar"><div class="progress-fill bg-green" id="disk-bar" style="width:0%"></div></div>
- </div>
- </div>
- <div class="grid">
- <div class="card">
- <div class="card-title">系统信息</div>
- <div class="info-grid">
- <span class="label">主机名</span><span class="val" id="hostname">-</span>
- <span class="label">内核版本</span><span class="val" id="kernel">-</span>
- <span class="label">运行时间</span><span class="val" id="uptime">-</span>
- <span class="label">进程PID</span><span class="val" id="pid">-</span>
- <span class="label">进程内存</span><span class="val" id="proc-rss">-</span>
- </div>
- </div>
- <div class="card">
- <div class="card-title">告警阈值</div>
- <div class="info-grid">
- <span class="label">CPU 告警</span><span class="val" id="cpu-threshold">-</span>
- <span class="label">温度告警</span><span class="val" id="temp-threshold2">-</span>
- </div>
- </div>
- </div>
- </div>
- <script>
- 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;});}
- function getColor(pct){return pct<60?'green':pct<85?'yellow':'red'}
- function updateBar(id,pct,max){
- let bar=document.getElementById(id);
- bar.style.width=Math.min(pct/max*100,100)+'%';
- bar.className='progress-fill bg-'+getColor(pct);
- }
- function update(){
- authFetch('/api/system/status').then(r=>r.json()).then(d=>{
- // CPU
- document.getElementById('cpu-usage').textContent=d.cpu.usage.toFixed(1)+'%';
- document.getElementById('cpu-usage').className='card-value '+getColor(d.cpu.usage);
- document.getElementById('cpu-cores').textContent=d.cpu.cores;
- document.getElementById('cpu-load').textContent=d.cpu.load1.toFixed(2)+' / '+d.cpu.load5.toFixed(2)+' / '+d.cpu.load15.toFixed(2);
- updateBar('cpu-bar',d.cpu.usage,100);
- // Memory
- document.getElementById('mem-usage').textContent=d.memory.percent.toFixed(1)+'%';
- document.getElementById('mem-usage').className='card-value '+getColor(d.memory.percent);
- document.getElementById('mem-detail').textContent=d.memory.used+' MB / '+d.memory.total+' MB (可用 '+d.memory.available+' MB)';
- updateBar('mem-bar',d.memory.percent,100);
- // Temperature
- if(d.temperature.available){
- document.getElementById('cpu-temp').textContent=d.temperature.value.toFixed(1)+'°C';
- let tc=d.temperature.value>d.temperature.alert_threshold?'red':d.temperature.value>60?'yellow':'green';
- document.getElementById('cpu-temp').className='card-value '+tc;
- updateBar('temp-bar',d.temperature.value,d.temperature.alert_threshold*1.3);
- }else{
- document.getElementById('cpu-temp').textContent='不可用';
- document.getElementById('cpu-temp').className='card-value';
- }
- document.getElementById('temp-threshold').textContent=d.temperature.alert_threshold;
- // Disk
- document.getElementById('disk-usage').textContent=d.disk.percent.toFixed(1)+'%';
- document.getElementById('disk-usage').className='card-value '+getColor(d.disk.percent);
- document.getElementById('disk-detail').textContent=d.disk.used+' GB / '+d.disk.total+' GB (可用 '+d.disk.available+' GB)';
- updateBar('disk-bar',d.disk.percent,100);
- // System
- document.getElementById('hostname').textContent=d.system.hostname;
- document.getElementById('kernel').textContent=d.system.kernel;
- document.getElementById('uptime').textContent=d.system.uptime;
- document.getElementById('pid').textContent=d.process.pid;
- document.getElementById('proc-rss').textContent=d.process.rss_mb+' MB';
- document.getElementById('cpu-threshold').textContent=d.thresholds.cpu_alert+'%';
- document.getElementById('temp-threshold2').textContent=d.thresholds.temp_alert+'°C';
- // Alerts
- let alerts=[];
- if(d.cpu.usage>d.thresholds.cpu_alert) alerts.push('⚠️ CPU使用率 '+d.cpu.usage.toFixed(1)+'% 超过阈值 '+d.thresholds.cpu_alert+'%');
- 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');
- let ab=document.getElementById('alert-banner');
- if(alerts.length){ab.style.display='block';ab.innerHTML=alerts.join('<br>')}else{ab.style.display='none'}
- document.getElementById('last-update').textContent=new Date().toLocaleTimeString();
- }).catch(e=>console.error(e));
- }
- update();
- setInterval(update,2000);
- </script>
- </body>
- </html>
|