mirror of
https://github.com//cppla/ServerStatus
synced 2025-09-05 01:12:20 +08:00
update
This commit is contained in:
parent
9cd86ccaa3
commit
68a1d3719f
@ -32,6 +32,7 @@ import json
|
||||
import errno
|
||||
import subprocess
|
||||
import threading
|
||||
import platform
|
||||
if sys.version_info.major == 3:
|
||||
from queue import Queue
|
||||
elif sys.version_info.major == 2:
|
||||
@ -520,6 +521,34 @@ if __name__ == '__main__':
|
||||
array['tcp'], array['udp'], array['process'], array['thread'] = tupd()
|
||||
array['io_read'] = diskIO.get("read")
|
||||
array['io_write'] = diskIO.get("write")
|
||||
# report OS (normalized)
|
||||
try:
|
||||
sysname = platform.system().lower()
|
||||
if sysname.startswith('linux'):
|
||||
os_name = 'linux'
|
||||
# try distro from os-release
|
||||
try:
|
||||
with open('/etc/os-release') as f:
|
||||
for line in f:
|
||||
if line.startswith('ID='):
|
||||
val = line.strip().split('=',1)[1].strip().strip('"')
|
||||
if val: os_name = val
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
elif sysname.startswith('darwin'):
|
||||
os_name = 'darwin'
|
||||
elif sysname.startswith('freebsd'):
|
||||
os_name = 'freebsd'
|
||||
elif sysname.startswith('openbsd'):
|
||||
os_name = 'openbsd'
|
||||
elif sysname.startswith('netbsd'):
|
||||
os_name = 'netbsd'
|
||||
else:
|
||||
os_name = sysname or 'unknown'
|
||||
except Exception:
|
||||
os_name = 'unknown'
|
||||
array['os'] = os_name
|
||||
array['custom'] = "<br>".join(f"{k}\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100:.1f}%</code>" for k, v in monitorServer.items())
|
||||
s.send(byte_str("update " + json.dumps(array) + "\n"))
|
||||
except KeyboardInterrupt:
|
||||
|
@ -32,6 +32,7 @@ import json
|
||||
import errno
|
||||
import psutil
|
||||
import threading
|
||||
import platform
|
||||
if sys.version_info.major == 3:
|
||||
from queue import Queue
|
||||
elif sys.version_info.major == 2:
|
||||
@ -509,7 +510,31 @@ if __name__ == '__main__':
|
||||
array['tcp'], array['udp'], array['process'], array['thread'] = tupd()
|
||||
array['io_read'] = diskIO.get("read")
|
||||
array['io_write'] = diskIO.get("write")
|
||||
array['custom'] = "<br>".join(f"{k}\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100:.2f}%</code>" for k, v in monitorServer.items())
|
||||
# report OS (normalized)
|
||||
try:
|
||||
sysname = platform.system().lower()
|
||||
if sysname.startswith('windows'):
|
||||
os_name = 'windows'
|
||||
elif sysname.startswith('darwin') or 'mac' in sysname:
|
||||
os_name = 'darwin'
|
||||
elif 'bsd' in sysname:
|
||||
os_name = 'bsd'
|
||||
elif sysname.startswith('linux'):
|
||||
# try distro if available
|
||||
os_name = 'linux'
|
||||
try:
|
||||
import distro # optional
|
||||
os_name = distro.id() or 'linux'
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
os_name = sysname or 'unknown'
|
||||
except Exception:
|
||||
os_name = 'unknown'
|
||||
array['os'] = os_name
|
||||
array['custom'] = "<br>".join("{}\t解析: {}\t连接: {}\t下载: {}\t在线率: <code>{:.2f}%</code>".format(
|
||||
k, v.get('dns_time'), v.get('connect_time'), v.get('download_time'), (v.get('online_rate') or 0.0)*100
|
||||
) for k, v in monitorServer.items())
|
||||
s.send(byte_str("update " + json.dumps(array) + "\n"))
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
|
@ -383,6 +383,9 @@ int CMain::HandleMessage(int ClientNetID, char *pMessage)
|
||||
pClient->m_Stats.m_Online6 = rStart["online6"].u.boolean;
|
||||
if(rStart["custom"].type == json_string)
|
||||
str_copy(pClient->m_Stats.m_aCustom, rStart["custom"].u.string.ptr, sizeof(pClient->m_Stats.m_aCustom));
|
||||
// optional OS field from clients
|
||||
if(rStart["os"].type == json_string)
|
||||
str_copy(pClient->m_Stats.m_aOS, rStart["os"].u.string.ptr, sizeof(pClient->m_Stats.m_aOS));
|
||||
|
||||
//copy message for watchdog to analysis
|
||||
WatchdogMessage(ClientNetID,
|
||||
@ -630,7 +633,7 @@ void CMain::JSONUpdateThread(void *pUser)
|
||||
}
|
||||
|
||||
str_format(pBuf, sizeof(aFileBuf) - (pBuf - aFileBuf),
|
||||
"{ \"name\": \"%s\",\"type\": \"%s\",\"host\": \"%s\",\"location\": \"%s\",\"online4\": %s, \"online6\": %s, \"uptime\": \"%s\",\"load_1\": %.2f, \"load_5\": %.2f, \"load_15\": %.2f,\"ping_10010\": %.2f, \"ping_189\": %.2f, \"ping_10086\": %.2f,\"time_10010\": %" PRId64 ", \"time_189\": %" PRId64 ", \"time_10086\": %" PRId64 ", \"tcp_count\": %" PRId64 ", \"udp_count\": %" PRId64 ", \"process_count\": %" PRId64 ", \"thread_count\": %" PRId64 ", \"network_rx\": %" PRId64 ", \"network_tx\": %" PRId64 ", \"network_in\": %" PRId64 ", \"network_out\": %" PRId64 ", \"cpu\": %d, \"memory_total\": %" PRId64 ", \"memory_used\": %" PRId64 ", \"swap_total\": %" PRId64 ", \"swap_used\": %" PRId64 ", \"hdd_total\": %" PRId64 ", \"hdd_used\": %" PRId64 ", \"last_network_in\": %" PRId64 ", \"last_network_out\": %" PRId64 ",\"io_read\": %" PRId64 ", \"io_write\": %" PRId64 ",\"custom\": \"%s\" },\n",
|
||||
"{ \"name\": \"%s\",\"type\": \"%s\",\"host\": \"%s\",\"location\": \"%s\",\"online4\": %s, \"online6\": %s, \"uptime\": \"%s\",\"load_1\": %.2f, \"load_5\": %.2f, \"load_15\": %.2f,\"ping_10010\": %.2f, \"ping_189\": %.2f, \"ping_10086\": %.2f,\"time_10010\": %" PRId64 ", \"time_189\": %" PRId64 ", \"time_10086\": %" PRId64 ", \"tcp_count\": %" PRId64 ", \"udp_count\": %" PRId64 ", \"process_count\": %" PRId64 ", \"thread_count\": %" PRId64 ", \"network_rx\": %" PRId64 ", \"network_tx\": %" PRId64 ", \"network_in\": %" PRId64 ", \"network_out\": %" PRId64 ", \"cpu\": %d, \"memory_total\": %" PRId64 ", \"memory_used\": %" PRId64 ", \"swap_total\": %" PRId64 ", \"swap_used\": %" PRId64 ", \"hdd_total\": %" PRId64 ", \"hdd_used\": %" PRId64 ", \"last_network_in\": %" PRId64 ", \"last_network_out\": %" PRId64 ",\"io_read\": %" PRId64 ", \"io_write\": %" PRId64 ",\"custom\": \"%s\", \"os\": \"%s\" },\n",
|
||||
pClients[i].m_aName,pClients[i].m_aType,pClients[i].m_aHost,pClients[i].m_aLocation,
|
||||
pClients[i].m_Stats.m_Online4 ? "true" : "false",pClients[i].m_Stats.m_Online6 ? "true" : "false",
|
||||
aUptime, pClients[i].m_Stats.m_Load_1, pClients[i].m_Stats.m_Load_5, pClients[i].m_Stats.m_Load_15, pClients[i].m_Stats.m_ping_10010, pClients[i].m_Stats.m_ping_189, pClients[i].m_Stats.m_ping_10086,
|
||||
@ -640,15 +643,17 @@ void CMain::JSONUpdateThread(void *pUser)
|
||||
pClients[i].m_Stats.m_NetworkIN == 0 || pClients[i].m_LastNetworkIN == 0 ? pClients[i].m_Stats.m_NetworkIN : pClients[i].m_LastNetworkIN,
|
||||
pClients[i].m_Stats.m_NetworkOUT == 0 || pClients[i].m_LastNetworkOUT == 0 ? pClients[i].m_Stats.m_NetworkOUT : pClients[i].m_LastNetworkOUT,
|
||||
pClients[i].m_Stats.m_IORead, pClients[i].m_Stats.m_IOWrite,
|
||||
pClients[i].m_Stats.m_aCustom);
|
||||
pClients[i].m_Stats.m_aCustom,
|
||||
pClients[i].m_Stats.m_aOS[0] ? pClients[i].m_Stats.m_aOS : "");
|
||||
pBuf += strlen(pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
// sava network traffic record to json when close client
|
||||
// last_network_in == last network in record, last_network_out == last network out record
|
||||
str_format(pBuf, sizeof(aFileBuf) - (pBuf - aFileBuf), "{ \"name\": \"%s\", \"type\": \"%s\", \"host\": \"%s\", \"location\": \"%s\", \"online4\": false, \"online6\": false, \"last_network_in\": %" PRId64 ", \"last_network_out\": %" PRId64 " },\n",
|
||||
pClients[i].m_aName, pClients[i].m_aType, pClients[i].m_aHost, pClients[i].m_aLocation, pClients[i].m_LastNetworkIN, pClients[i].m_LastNetworkOUT);
|
||||
str_format(pBuf, sizeof(aFileBuf) - (pBuf - aFileBuf), "{ \"name\": \"%s\", \"type\": \"%s\", \"host\": \"%s\", \"location\": \"%s\", \"online4\": false, \"online6\": false, \"last_network_in\": %" PRId64 ", \"last_network_out\": %" PRId64 ", \"os\": \"%s\" },\n",
|
||||
pClients[i].m_aName, pClients[i].m_aType, pClients[i].m_aHost, pClients[i].m_aLocation, pClients[i].m_LastNetworkIN, pClients[i].m_LastNetworkOUT,
|
||||
pClients[i].m_Stats.m_aOS[0] ? pClients[i].m_Stats.m_aOS : "");
|
||||
pBuf += strlen(pBuf);
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,8 @@ class CMain
|
||||
int64_t m_IOWrite;
|
||||
double m_CPU;
|
||||
char m_aCustom[1024];
|
||||
// OS name reported by client (e.g. linux/windows/darwin/freebsd)
|
||||
char m_aOS[64];
|
||||
// Options
|
||||
bool m_Pong;
|
||||
} m_Stats;
|
||||
|
@ -230,9 +230,39 @@ body.light .gauge-half .needle{background:linear-gradient(var(--text),var(--text
|
||||
}
|
||||
.cards .card{border:1px solid var(--border);border-radius:12px;padding:.75rem .85rem;background:linear-gradient(145deg,var(--bg),var(--bg-alt));display:flex;flex-direction:column;gap:.45rem;position:relative;}
|
||||
.cards .card.offline{opacity:.6;}
|
||||
.cards .card.high-load{border-color:rgba(239,68,68,.55);box-shadow:0 0 0 1px rgba(239,68,68,.4),0 4px 16px -4px rgba(239,68,68,.3);}
|
||||
table.data tbody tr.high-load{background:rgba(239,68,68,.10);}
|
||||
table.data tbody tr.high-load:hover{background:rgba(239,68,68,.18);}
|
||||
.cards .card.high-load{border-color:rgba(239,68,68,.6);background:linear-gradient(180deg, rgba(239,68,68,.22), rgba(239,68,68,.12));box-shadow:0 0 0 1px rgba(239,68,68,.48),0 6px 18px -6px rgba(239,68,68,.28);}
|
||||
table.data tbody tr.high-load{background:rgba(239,68,68,.18) !important;}
|
||||
table.data tbody tr.high-load:hover{background:rgba(239,68,68,.26) !important;}
|
||||
|
||||
/* OS 着色(更明显):
|
||||
1) 为各 OS 类定义 --os-color 变量
|
||||
2) 行左侧使用 inset box-shadow 画 4px 彩条
|
||||
3) 行背景叠加轻度渐变以提示 OS
|
||||
*/
|
||||
table.data tbody tr[class*="os-"]{box-shadow:inset 4px 0 0 0 var(--os-color, transparent);background:linear-gradient(180deg, color-mix(in srgb, var(--os-color, transparent) 10%, transparent), transparent 60%);}
|
||||
table.data tbody tr[class*="os-"]:hover{background:linear-gradient(180deg, color-mix(in srgb, var(--os-color, transparent) 16%, transparent), transparent 65%);}
|
||||
.cards .card[class*="os-"]{border-color:color-mix(in srgb, var(--os-color, var(--accent)) 60%, transparent);box-shadow:0 0 0 1px color-mix(in srgb, var(--os-color, var(--accent)) 40%, transparent),0 4px 16px -6px color-mix(in srgb, var(--os-color, #000) 35%, transparent);}
|
||||
|
||||
/* 为常见系统赋色 */
|
||||
.os-linux{--os-color: rgba(16,185,129,.85);} /* emerald */
|
||||
.os-ubuntu{--os-color: rgba(251,146,60,.9);} /* orange */
|
||||
.os-debian{--os-color: rgba(236,72,153,.9);} /* pink */
|
||||
.os-centos{--os-color: rgba(59,130,246,.9);} /* blue */
|
||||
.os-rocky{--os-color: rgba(59,130,246,.9);}
|
||||
.os-almalinux{--os-color: rgba(59,130,246,.9);}
|
||||
.os-rhel{--os-color: rgba(59,130,246,.9);}
|
||||
.os-arch{--os-color: rgba(14,165,233,.9);} /* sky */
|
||||
.os-alpine{--os-color: rgba(2,132,199,.9);} /* blue-600 */
|
||||
.os-fedora{--os-color: rgba(59,130,246,.9);}
|
||||
.os-amazon{--os-color: rgba(245,158,11,.9);} /* amber */
|
||||
.os-suse{--os-color: rgba(34,197,94,.9);} /* green */
|
||||
.os-freebsd{--os-color: rgba(244,63,94,.9);} /* rose */
|
||||
.os-openbsd{--os-color: rgba(244,63,94,.9);}
|
||||
.os-bsd{--os-color: rgba(244,63,94,.9);}
|
||||
.os-darwin{--os-color: rgba(148,163,184,.95);} /* slate */
|
||||
.os-windows{--os-color: rgba(59,130,246,.95);} /* blue */
|
||||
|
||||
/* 已移除徽标叠加与伪元素,仅保留色条 + 渐变背景作为 OS 提示 */
|
||||
|
||||
/* 旧进度条相关样式已清理 */
|
||||
.cards .card-header{display:flex;align-items:center;justify-content:space-between;gap:.5rem;}
|
||||
|
@ -27,6 +27,31 @@ function humanMinKBFromB(bytes){ if(bytes==null||isNaN(bytes)) return '-'; //
|
||||
function humanAgo(ts){ if(!ts) return '-'; const s=Math.floor((Date.now()/1000 - ts)); const m=Math.floor(s/60); return m>0? m+' 分钟前':'几秒前'; }
|
||||
function num(v){ return (typeof v==='number' && !isNaN(v)) ? v : '-'; }
|
||||
|
||||
// 将服务端上报的 os 映射为样式类名(用于为行/卡片着色)
|
||||
function osClass(os){
|
||||
if(!os) return '';
|
||||
const v = String(os).toLowerCase();
|
||||
const pick = (k)=>' os-'+k;
|
||||
if(v.includes('ubuntu')) return pick('ubuntu');
|
||||
if(v.includes('debian')) return pick('debian');
|
||||
if(v.includes('centos')) return pick('centos');
|
||||
if(v.includes('rocky')) return pick('rocky');
|
||||
if(v.includes('alma')) return pick('almalinux');
|
||||
if(v.includes('arch')) return pick('arch');
|
||||
if(v.includes('alpine')) return pick('alpine');
|
||||
if(v.includes('fedora')) return pick('fedora');
|
||||
if(v.includes('rhel') || v.includes('redhat')) return pick('rhel');
|
||||
if(v.includes('suse')) return pick('suse');
|
||||
if(v.includes('amazon')) return pick('amazon');
|
||||
if(v.includes('freebsd')) return pick('freebsd');
|
||||
if(v.includes('openbsd')) return pick('openbsd');
|
||||
if(v.includes('netbsd') || v.includes('bsd')) return pick('bsd');
|
||||
if(v.includes('darwin') || v.includes('mac')) return pick('darwin');
|
||||
if(v.includes('win')) return pick('windows');
|
||||
if(v.includes('linux')) return pick('linux');
|
||||
return pick(v.replace(/[^a-z0-9_-]+/g,'-').slice(0,20));
|
||||
}
|
||||
|
||||
async function fetchData(){
|
||||
try {
|
||||
const r = await fetch('json/stats.json?_='+Date.now());
|
||||
@ -106,7 +131,7 @@ function renderServers(){
|
||||
// 唯一 key 已附加为 s._key(如需使用)
|
||||
const rowCursor = online? 'pointer':'default';
|
||||
const highLoad = online && ( (s.cpu||0)>=90 || (memPct)>=90 || (hddPct)>=90 );
|
||||
html += `<tr data-idx="${idx}" data-online="${online?1:0}" class="row-server${highLoad?' high-load':''}" style="cursor:${rowCursor};${online?'':'opacity:.65;'}">
|
||||
html += `<tr data-idx="${idx}" data-online="${online?1:0}" class="row-server${highLoad?' high-load':''}${osClass(s.os)}" style="cursor:${rowCursor};${online?'':'opacity:.65;'}">
|
||||
<td>${statusPill}</td>
|
||||
<td><span class="${trafficCls}" title="本月下行 | 上行 (≥500GB 触发红黄)"><span class="half in">${monthIn}</span><span class="half out">${monthOut}</span></span></td>
|
||||
<td>${s.name||'-'}</td>
|
||||
@ -176,7 +201,7 @@ function renderServersCards(){
|
||||
const buckets = `<div class=\"buckets\">${bucket(p1)}${bucket(p2)}${bucket(p3)}</div>`;
|
||||
// 唯一 key 已附加为 s._key(如需使用)
|
||||
const highLoad = online && ( (s.cpu||0)>=90 || (memPct)>=90 || (hddPct)>=90 );
|
||||
html += `<div class=\"card${online?'':' offline'}${highLoad?' high-load':''}\" data-idx=\"${idx}\" data-online=\"${online?1:0}\">\n <button class=\"expand-btn\" aria-label=\"展开\">▼</button>\n <div class=\"card-header\">\n <div class=\"card-title\">${s.name||'-'} <span class=\"tag\">${s.location||'-'}</span></div>\n ${pill}\n </div>\n <div class=\"kvlist\">\n <div><span class=\"key\">负载</span><span>${s.load_1==-1?'–':s.load_1?.toFixed(2)}</span></div>\n <div><span class=\"key\">在线</span><span>${s.uptime||'-'}</span></div>\n <div><span class=\"key\">月流量</span><span><span class=\"${trafficCls}\" title=\"本月下行 | 上行 (≥500GB 触发红黄)\"><span class=\"half in\">${monthIn}</span><span class=\"half out\">${monthOut}</span></span></span></div>\n <div><span class=\"key\">网络</span><span>${netNow}</span></div>\n <div><span class=\"key\">总流量</span><span>${netTotal}</span></div>\n <div><span class=\"key\">CPU</span><span>${s.cpu||0}%</span></div>\n <div><span class=\"key\">内存</span><span>${memPct.toFixed(0)}%</span></div>\n <div><span class=\"key\">硬盘</span><span>${hddPct.toFixed(0)}%</span></div>\n </div>\n ${buckets}\n <div class=\"expand-area\">\n <div style=\"font-size:.65rem;opacity:.7;margin-top:.3rem\">${online?'点击卡片可查看详情':'离线,不可查看详情'}</div>\n </div>\n </div>`;
|
||||
html += `<div class=\"card${online?'':' offline'}${highLoad?' high-load':''}${osClass(s.os)}\" data-idx=\"${idx}\" data-online=\"${online?1:0}\">\n <button class=\"expand-btn\" aria-label=\"展开\">▼</button>\n <div class=\"card-header\">\n <div class=\"card-title\">${s.name||'-'} <span class=\"tag\">${s.location||'-'}</span></div>\n ${pill}\n </div>\n <div class=\"kvlist\">\n <div><span class=\"key\">负载</span><span>${s.load_1==-1?'–':s.load_1?.toFixed(2)}</span></div>\n <div><span class=\"key\">在线</span><span>${s.uptime||'-'}</span></div>\n <div><span class=\"key\">月流量</span><span><span class=\"${trafficCls}\" title=\"本月下行 | 上行 (≥500GB 触发红黄)\"><span class=\"half in\">${monthIn}</span><span class=\"half out\">${monthOut}</span></span></span></div>\n <div><span class=\"key\">网络</span><span>${netNow}</span></div>\n <div><span class=\"key\">总流量</span><span>${netTotal}</span></div>\n <div><span class=\"key\">CPU</span><span>${s.cpu||0}%</span></div>\n <div><span class=\"key\">内存</span><span>${memPct.toFixed(0)}%</span></div>\n <div><span class=\"key\">硬盘</span><span>${hddPct.toFixed(0)}%</span></div>\n </div>\n ${buckets}\n <div class=\"expand-area\">\n <div style=\"font-size:.65rem;opacity:.7;margin-top:.3rem\">${online?'点击卡片可查看详情':'离线,不可查看详情'}</div>\n </div>\n </div>`;
|
||||
});
|
||||
wrap.innerHTML = html || '<div class="muted" style="font-size:.75rem;text-align:center;padding:1rem;">无数据</div>';
|
||||
wrap.querySelectorAll('.card').forEach(card=>{
|
||||
|
Loading…
x
Reference in New Issue
Block a user