mirror of https://github.com/cppla/ServerStatus
Compare commits
No commits in common. "master" and "1.1.7" have entirely different histories.
|
@ -153,13 +153,11 @@ names_done:
|
||||||
}
|
}
|
||||||
// alarm logic
|
// alarm logic
|
||||||
if(cert->m_aExpireTS>0){
|
if(cert->m_aExpireTS>0){
|
||||||
// 剩余天数: 向下取整 (floor) —— 与 JSON expire_days 保持一致,用于阈值分桶和消息显示
|
int days = (int)((cert->m_aExpireTS - nowt)/86400);
|
||||||
int64_t secsLeft = cert->m_aExpireTS - nowt;
|
int64_t *lastAlarm = NULL; int need=0; int target=0;
|
||||||
int days = (int)(secsLeft/86400);
|
if(days <=7 && days >3){ lastAlarm=&cert->m_aLastAlarm7; target=7; }
|
||||||
int64_t *lastAlarm = NULL; int need=0;
|
else if(days <=3 && days >1){ lastAlarm=&cert->m_aLastAlarm3; target=3; }
|
||||||
if(days <=7 && days >3){ lastAlarm=&cert->m_aLastAlarm7; }
|
else if(days <=1){ lastAlarm=&cert->m_aLastAlarm1; target=1; }
|
||||||
else if(days <=3 && days >1){ lastAlarm=&cert->m_aLastAlarm3; }
|
|
||||||
else if(days <=1){ lastAlarm=&cert->m_aLastAlarm1; }
|
|
||||||
if(lastAlarm && (*lastAlarm==0 || nowt - *lastAlarm > 20*3600)) need=1; // avoid spam, 20h
|
if(lastAlarm && (*lastAlarm==0 || nowt - *lastAlarm > 20*3600)) need=1; // avoid spam, 20h
|
||||||
if(need && strlen(cert->m_aCallback)>0){
|
if(need && strlen(cert->m_aCallback)>0){
|
||||||
CURL *curl = curl_easy_init();
|
CURL *curl = curl_easy_init();
|
||||||
|
@ -168,8 +166,7 @@ names_done:
|
||||||
char timebuf[32];
|
char timebuf[32];
|
||||||
time_t expt = (time_t)cert->m_aExpireTS;
|
time_t expt = (time_t)cert->m_aExpireTS;
|
||||||
strftime(timebuf,sizeof(timebuf),"%Y-%m-%d %H:%M:%S", gmtime(&expt));
|
strftime(timebuf,sizeof(timebuf),"%Y-%m-%d %H:%M:%S", gmtime(&expt));
|
||||||
// 使用 floor(days)
|
snprintf(msg,sizeof(msg),"【SSL证书提醒】%s(%s) 将在 %d 天后(%s UTC) 到期", cert->m_aName, cert->m_aDomain, target, timebuf);
|
||||||
snprintf(msg,sizeof(msg),"【SSL证书提醒】%s(%s) 将在 %d 天后(%s UTC) 到期", cert->m_aName, cert->m_aDomain, days, timebuf);
|
|
||||||
char *enc = curl_easy_escape(curl,msg,0);
|
char *enc = curl_easy_escape(curl,msg,0);
|
||||||
char url[1500]; snprintf(url,sizeof(url),"%s%s", cert->m_aCallback, enc?enc:"");
|
char url[1500]; snprintf(url,sizeof(url),"%s%s", cert->m_aCallback, enc?enc:"");
|
||||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||||
|
|
|
@ -101,8 +101,6 @@ table.data tbody tr:hover{background:rgba(255,255,255,.04)}
|
||||||
/* modal styles */
|
/* modal styles */
|
||||||
.modal-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.55);display:flex;align-items:flex-start;justify-content:center;padding:5vh 1rem;z-index:50;backdrop-filter:blur(4px)}
|
.modal-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.55);display:flex;align-items:flex-start;justify-content:center;padding:5vh 1rem;z-index:50;backdrop-filter:blur(4px)}
|
||||||
.modal-box{position:relative;width:100%;max-width:560px;background:var(--bg-alt);border:1px solid var(--border);border-radius:16px;box-shadow:0 8px 30px -6px rgba(0,0,0,.6);padding:1.25rem 1.35rem;display:flex;flex-direction:column;gap:.9rem;animation:fade .25s ease}
|
.modal-box{position:relative;width:100%;max-width:560px;background:var(--bg-alt);border:1px solid var(--border);border-radius:16px;box-shadow:0 8px 30px -6px rgba(0,0,0,.6);padding:1.25rem 1.35rem;display:flex;flex-direction:column;gap:.9rem;animation:fade .25s ease}
|
||||||
.modal-box.high-load{border-color:rgba(239,68,68,.6);background:linear-gradient(180deg, rgba(239,68,68,.16), rgba(239,68,68,.08)), var(--bg-alt);box-shadow:0 0 0 1px rgba(239,68,68,.38),0 10px 28px -10px rgba(239,68,68,.28)}
|
|
||||||
body:not(.light) .modal-box.high-load{background:linear-gradient(180deg, rgba(239,68,68,.24), rgba(239,68,68,.12)), var(--bg-alt)}
|
|
||||||
.modal-title{margin:0;font-size:16px;font-weight:600;letter-spacing:.5px}
|
.modal-title{margin:0;font-size:16px;font-weight:600;letter-spacing:.5px}
|
||||||
.modal-close{position:absolute;top:10px;right:12px;background:transparent;border:0;color:var(--text-dim);font-size:20px;line-height:1;cursor:pointer;padding:4px;border-radius:8px;transition:var(--trans)}
|
.modal-close{position:absolute;top:10px;right:12px;background:transparent;border:0;color:var(--text-dim);font-size:20px;line-height:1;cursor:pointer;padding:4px;border-radius:8px;transition:var(--trans)}
|
||||||
.modal-close:hover{color:var(--text);background:var(--bg)}
|
.modal-close:hover{color:var(--text);background:var(--bg)}
|
||||||
|
@ -231,14 +229,10 @@ 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{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.offline{opacity:.6;}
|
||||||
.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)), var(--bg-alt);box-shadow:0 0 0 1px rgba(239,68,68,.48),0 6px 18px -6px rgba(239,68,68,.28);}
|
.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{background:rgba(239,68,68,.18) !important;}
|
||||||
table.data tbody tr.high-load:hover{background:rgba(239,68,68,.26) !important;}
|
table.data tbody tr.high-load:hover{background:rgba(239,68,68,.26) !important;}
|
||||||
|
|
||||||
/* SSL 域名告警底色:与高负载相同 */
|
|
||||||
#sslTable td.alert-domain{background:rgba(239,68,68,.18) !important;}
|
|
||||||
#sslTable tr:hover td.alert-domain{background:rgba(239,68,68,.26) !important;}
|
|
||||||
|
|
||||||
/* OS 着色(更明显):
|
/* OS 着色(更明显):
|
||||||
1) 为各 OS 类定义 --os-color 变量
|
1) 为各 OS 类定义 --os-color 变量
|
||||||
2) 行左侧使用 inset box-shadow 画 4px 彩条
|
2) 行左侧使用 inset box-shadow 画 4px 彩条
|
||||||
|
@ -283,10 +277,10 @@ table.data tbody tr[class*="os-"]:hover{background:linear-gradient(180deg, color
|
||||||
.cards .kvlist div{display:flex;flex-direction:column;}
|
.cards .kvlist div{display:flex;flex-direction:column;}
|
||||||
.cards .kvlist span.key{opacity:.6;}
|
.cards .kvlist span.key{opacity:.6;}
|
||||||
.cards .buckets{margin-top:.25rem;}
|
.cards .buckets{margin-top:.25rem;}
|
||||||
/* 证书卡片:域名告警底色(与高负载卡片风格一致) */
|
.cards .expand-btn{position:absolute;top:.5rem;right:.5rem;background:transparent;border:0;color:var(--text-dim);cursor:pointer;font-size:.9rem;padding:.2rem;}
|
||||||
.cards .kvlist .alert-domain{background:rgba(239,68,68,.18);border:1px solid rgba(239,68,68,.35);border-radius:8px;padding:.4rem .5rem;}
|
.cards .expand-btn:focus, .cards .expand-btn:hover{color:var(--text);}
|
||||||
.cards .kvlist .alert-domain .key{opacity:.85}
|
.cards .expand-area{margin-top:.4rem;display:none;animation:fadeIn .25s ease;}
|
||||||
/* 移除移动端卡片展开箭头与展开区域(已按需简化交互) */
|
.cards .card.expanded .expand-area{display:block;}
|
||||||
/* 旧移动端 latency spark 样式移除 */
|
/* 旧移动端 latency spark 样式移除 */
|
||||||
|
|
||||||
/* 简易信号格,用于服务连通性延迟展示 */
|
/* 简易信号格,用于服务连通性延迟展示 */
|
||||||
|
|
|
@ -219,12 +219,13 @@ function renderServersCards(){
|
||||||
const buckets = `<div class=\"buckets\">${bucket(p1)}${bucket(p2)}${bucket(p3)}</div>`;
|
const buckets = `<div class=\"buckets\">${bucket(p1)}${bucket(p2)}${bucket(p3)}</div>`;
|
||||||
// 唯一 key 已附加为 s._key(如需使用)
|
// 唯一 key 已附加为 s._key(如需使用)
|
||||||
const highLoad = online && ( (s.cpu||0)>=90 || (memPct)>=90 || (hddPct)>=90 );
|
const highLoad = online && ( (s.cpu||0)>=90 || (memPct)>=90 || (hddPct)>=90 );
|
||||||
html += `<div class=\"card${online?'':' offline'}${highLoad?' high-load':''}${osClass(s.os)}\" data-idx=\"${idx}\" data-online=\"${online?1:0}\">\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>`;
|
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.innerHTML = html || '<div class="muted" style="font-size:.75rem;text-align:center;padding:1rem;">无数据</div>';
|
||||||
wrap.querySelectorAll('.card').forEach(card=>{
|
wrap.querySelectorAll('.card').forEach(card=>{
|
||||||
const idx = parseInt(card.getAttribute('data-idx'));
|
const idx = parseInt(card.getAttribute('data-idx'));
|
||||||
card.addEventListener('click', ()=>{
|
card.addEventListener('click', e=>{
|
||||||
|
if(e.target.classList.contains('expand-btn')){ card.classList.toggle('expanded'); e.stopPropagation(); return;}
|
||||||
if(card.getAttribute('data-online')!=='1') return; // 离线不弹
|
if(card.getAttribute('data-online')!=='1') return; // 离线不弹
|
||||||
openDetail(idx);
|
openDetail(idx);
|
||||||
});
|
});
|
||||||
|
@ -314,11 +315,9 @@ function renderSSL(){
|
||||||
const cls = c.expire_days<=0? 'err': c.expire_days<=7? 'warn':'ok';
|
const cls = c.expire_days<=0? 'err': c.expire_days<=7? 'warn':'ok';
|
||||||
const status = c.expire_days<=0? '已过期': c.expire_days<=7? '将到期':'正常';
|
const status = c.expire_days<=0? '已过期': c.expire_days<=7? '将到期':'正常';
|
||||||
const dt = c.expire_ts? new Date(c.expire_ts*1000).toISOString().replace('T',' ').replace(/\.\d+Z/,''):'-';
|
const dt = c.expire_ts? new Date(c.expire_ts*1000).toISOString().replace('T',' ').replace(/\.\d+Z/,''):'-';
|
||||||
// 当证书进入警告/错误状态时,高亮域名列底色(与高负载相同的底色)
|
|
||||||
const domainCellCls = (cls !== 'ok') ? 'alert-domain' : '';
|
|
||||||
html += `<tr>
|
html += `<tr>
|
||||||
<td>${c.name||'-'}</td>
|
<td>${c.name||'-'}</td>
|
||||||
<td class="${domainCellCls}">${(c.domain||'').replace(/^https?:\/\//,'')}</td>
|
<td>${(c.domain||'').replace(/^https?:\/\//,'')}</td>
|
||||||
<td>${c.port||443}</td>
|
<td>${c.port||443}</td>
|
||||||
<td><span class="badge ${cls}">${c.expire_days??'-'}</span></td>
|
<td><span class="badge ${cls}">${c.expire_days??'-'}</span></td>
|
||||||
<td>${dt}</td>
|
<td>${dt}</td>
|
||||||
|
@ -337,11 +336,10 @@ function renderSSLCards(){
|
||||||
const cls = c.expire_days<=0? 'err': c.expire_days<=7? 'warn':'ok';
|
const cls = c.expire_days<=0? 'err': c.expire_days<=7? 'warn':'ok';
|
||||||
const status = c.expire_days<=0? '已过期': c.expire_days<=7? '将到期':'正常';
|
const status = c.expire_days<=0? '已过期': c.expire_days<=7? '将到期':'正常';
|
||||||
const dt = c.expire_ts? new Date(c.expire_ts*1000).toISOString().replace('T',' ').replace(/\.\d+Z/,''):'-';
|
const dt = c.expire_ts? new Date(c.expire_ts*1000).toISOString().replace('T',' ').replace(/\.\d+Z/,''):'-';
|
||||||
const domainRowCls = (cls !== 'ok') ? 'alert-domain' : '';
|
|
||||||
html += `<div class="card">
|
html += `<div class="card">
|
||||||
<div class="card-header"><div class="card-title">${c.name||'-'}</div><span class="status-pill ${cls==='err'?'off':'on'}">${status}</span></div>
|
<div class="card-header"><div class="card-title">${c.name||'-'}</div><span class="status-pill ${cls==='err'?'off':'on'}">${status}</span></div>
|
||||||
<div class="kvlist" style="grid-template-columns:repeat(2,minmax(0,1fr));">
|
<div class="kvlist" style="grid-template-columns:repeat(2,minmax(0,1fr));">
|
||||||
<div class="${domainRowCls}"><span class="key">域名</span><span>${(c.domain||'').replace(/^https?:\/\//,'')}</span></div>
|
<div><span class="key">域名</span><span>${(c.domain||'').replace(/^https?:\/\//,'')}</span></div>
|
||||||
<div><span class="key">端口</span><span>${c.port||443}</span></div>
|
<div><span class="key">端口</span><span>${c.port||443}</span></div>
|
||||||
<div><span class="key">剩余(天)</span><span><span class="badge ${cls}">${c.expire_days??'-'}</span></span></div>
|
<div><span class="key">剩余(天)</span><span><span class="badge ${cls}">${c.expire_days??'-'}</span></span></div>
|
||||||
<div><span class="key">到期</span><span>${dt.split(' ')[0]||dt}</span></div>
|
<div><span class="key">到期</span><span>${dt.split(' ')[0]||dt}</span></div>
|
||||||
|
@ -398,7 +396,6 @@ function openDetail(i){
|
||||||
const s = S.servers[i]; if(!s) return;
|
const s = S.servers[i]; if(!s) return;
|
||||||
const box = document.getElementById('detailContent');
|
const box = document.getElementById('detailContent');
|
||||||
const modal = document.getElementById('detailModal');
|
const modal = document.getElementById('detailModal');
|
||||||
const modalBox = modal.querySelector('.modal-box');
|
|
||||||
const osText = osLabel(s.os);
|
const osText = osLabel(s.os);
|
||||||
const titleEl = document.getElementById('detailTitle');
|
const titleEl = document.getElementById('detailTitle');
|
||||||
titleEl.textContent = s.name + ' 详情';
|
titleEl.textContent = s.name + ' 详情';
|
||||||
|
@ -479,9 +476,6 @@ function openDetail(i){
|
||||||
${latencyBlock}
|
${latencyBlock}
|
||||||
`;
|
`;
|
||||||
modal.style.display='flex';
|
modal.style.display='flex';
|
||||||
// 根据高负载阈值(>=90% 任一项)给弹窗加高亮底色
|
|
||||||
const highLoad = (s.cpu||0) >= 90 || memPct >= 90 || hddPct >= 90;
|
|
||||||
if(modalBox){ modalBox.classList.toggle('high-load', highLoad); }
|
|
||||||
document.addEventListener('keydown', escCloseOnce);
|
document.addEventListener('keydown', escCloseOnce);
|
||||||
if(!offline){
|
if(!offline){
|
||||||
drawLatencyChart(key);
|
drawLatencyChart(key);
|
||||||
|
@ -494,14 +488,7 @@ function openDetail(i){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function escCloseOnce(e){ if(e.key==='Escape'){ closeDetail(); } }
|
function escCloseOnce(e){ if(e.key==='Escape'){ closeDetail(); } }
|
||||||
function closeDetail(){
|
function closeDetail(){ const m=document.getElementById('detailModal'); m.style.display='none'; document.removeEventListener('keydown', escCloseOnce); stopDetailAutoUpdate(); }
|
||||||
const m=document.getElementById('detailModal');
|
|
||||||
m.style.display='none';
|
|
||||||
const b = m.querySelector('.modal-box');
|
|
||||||
if(b) b.classList.remove('high-load');
|
|
||||||
document.removeEventListener('keydown', escCloseOnce);
|
|
||||||
stopDetailAutoUpdate();
|
|
||||||
}
|
|
||||||
document.getElementById('detailClose').addEventListener('click', closeDetail);
|
document.getElementById('detailClose').addEventListener('click', closeDetail);
|
||||||
document.getElementById('detailModal').addEventListener('click', e=>{ if(e.target.id==='detailModal') closeDetail(); });
|
document.getElementById('detailModal').addEventListener('click', e=>{ if(e.target.id==='detailModal') closeDetail(); });
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue