288 lines
18 KiB
HTML
288 lines
18 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||
<title>WEVAL Enterprise — AI Operations Command Center</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;600;700;800&display=swap" rel="stylesheet">
|
||
<style>
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
:root{--bg:#0a0e17;--sf:#111827;--sfh:#1f2937;--bd:#1f2937;--ac:#3b82f6;--ok:#10b981;--wa:#f59e0b;--er:#ef4444;--tx:#f9fafb;--tm:#9ca3af;--td:#6b7280;--pu:#8b5cf6;--te:#14b8a6;--pk:#ec4899}
|
||
body{background:var(--bg);color:var(--tx);font-family:'Inter',sans-serif;min-height:100vh}
|
||
.mono{font-family:'JetBrains Mono',monospace}
|
||
header{padding:16px 32px;border-bottom:1px solid var(--bd);display:flex;align-items:center;justify-content:space-between;background:var(--sf)}
|
||
.logo{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,var(--ac),var(--pu));display:flex;align-items:center;justify-content:center;font-weight:800;font-size:16px}
|
||
.badge{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border-radius:6px;font-size:11px;font-weight:700}
|
||
.badge::before{content:'';width:6px;height:6px;border-radius:50%}
|
||
.badge-ok{background:#10b98115;color:var(--ok)}.badge-ok::before{background:var(--ok)}
|
||
.badge-er{background:#ef444415;color:var(--er)}.badge-er::before{background:var(--er)}
|
||
.badge-wa{background:#f59e0b15;color:var(--wa)}.badge-wa::before{background:var(--wa)}
|
||
.badge-pu{background:#8b5cf615;color:var(--pu)}.badge-pu::before{background:var(--pu)}
|
||
.badge-te{background:#14b8a615;color:var(--te)}.badge-te::before{background:var(--te)}
|
||
.badge-ac{background:#3b82f615;color:var(--ac)}.badge-ac::before{background:var(--ac)}
|
||
.main{padding:24px 32px;max-width:1440px;margin:0 auto}
|
||
.tabs{display:flex;gap:2px;background:var(--sfh);border-radius:10px;padding:3px;margin-bottom:24px}
|
||
.tab{flex:1;padding:8px 16px;border-radius:8px;border:none;cursor:pointer;font-size:13px;font-weight:600;background:transparent;color:var(--td);transition:all .2s}
|
||
.tab.on{background:var(--sf);color:var(--tx);box-shadow:0 2px 8px #0004}
|
||
.card{background:var(--sf);border-radius:16px;border:1px solid var(--bd);padding:20px 24px;position:relative;overflow:hidden}
|
||
.card.glow{box-shadow:0 0 40px #3b82f620}
|
||
.g2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
|
||
.g3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
|
||
.g4{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px}
|
||
.g5{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px}
|
||
.g6{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:16px}
|
||
.metric .lbl{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:1.5px;color:var(--td);margin-bottom:8px}
|
||
.metric .val{font-size:32px;font-weight:800;line-height:1;font-family:'JetBrains Mono',monospace}
|
||
.metric .sub{font-size:12px;color:var(--tm);margin-top:6px}
|
||
.metric .trend{position:absolute;top:16px;right:20px;font-size:12px;font-weight:700}
|
||
.svc{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid var(--bd)}
|
||
.svc .dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
||
.svc .nm{font-weight:600;font-size:14px;flex:1}
|
||
.svc .pt{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--td)}
|
||
.alert{display:flex;gap:12px;padding:12px 16px;border-radius:10px;margin-bottom:8px}
|
||
.alert .ti{font-weight:700;font-size:13px}
|
||
.alert .ms{font-size:12px;color:var(--tm)}
|
||
.srv-card{padding:16px;border-radius:12px;background:var(--sfh);border:1px solid var(--bd);text-align:center}
|
||
.srv-card.dead{border-color:#ef444440}
|
||
.bar{width:100%;height:8px;border-radius:8px;background:var(--sfh);overflow:hidden}
|
||
.bar-fill{height:100%;border-radius:8px;transition:width 1s ease}
|
||
.footer{text-align:center;padding:32px 0 16px;font-size:12px;color:var(--td)}
|
||
.scroll{max-height:420px;overflow-y:auto}
|
||
.cron-grid{display:grid;grid-template-columns:100px 1fr;gap:6px 16px;font-family:'JetBrains Mono',monospace;font-size:12px}
|
||
.dock-item{padding:10px 14px;border-radius:10px;background:var(--sfh)}
|
||
.dock-item.ko{background:#ef444410;border:1px solid #ef444430}
|
||
h3{font-size:14px;font-weight:700;margin-bottom:16px}
|
||
@media(max-width:900px){.g6,.g5{grid-template-columns:repeat(3,minmax(0,1fr))}.g4{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<div style="display:flex;align-items:center;gap:16px">
|
||
<div class="logo">W</div>
|
||
<div>
|
||
<div style="font-size:18px;font-weight:800;letter-spacing:-.5px">WEVAL Enterprise</div>
|
||
<div style="font-size:11px;color:var(--td)">AI Operations Command Center</div>
|
||
</div>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:16px">
|
||
<span class="badge badge-ok" id="uptime-badge">—</span>
|
||
<span class="badge badge-er" id="alert-badge">—</span>
|
||
<span class="mono" style="font-size:13px;color:var(--td)" id="clock">—</span>
|
||
</div>
|
||
</header>
|
||
<div style="padding:6px 28px;border-bottom:1px solid #1f2937;display:flex;gap:6px;flex-wrap:wrap;background:rgba(17,24,39,.8);backdrop-filter:blur(8px)">
|
||
<a href="/l99-saas.html" style="padding:4px 12px;border-radius:6px;font-size:10px;font-weight:600;text-decoration:none;background:#1f2937;color:#9ca3af">L99 Mission Control</a>
|
||
<a href="/realtime-monitor.html" style="padding:4px 12px;border-radius:6px;font-size:10px;font-weight:600;text-decoration:none;background:#1f2937;color:#9ca3af">Realtime Monitor</a>
|
||
<a href="/agents-goodjob.html" style="padding:4px 12px;border-radius:6px;font-size:10px;font-weight:600;text-decoration:none;background:#1f2937;color:#9ca3af">Enterprise Viz</a>
|
||
<a href="/admin-saas.html" style="padding:4px 12px;border-radius:6px;font-size:10px;font-weight:700;text-decoration:none;background:#3b82f6;color:#0a0e17">Admin SaaS</a>
|
||
<a href="/sovereign-claude.html" style="padding:4px 12px;border-radius:6px;font-size:10px;font-weight:600;text-decoration:none;background:#1f2937;color:#9ca3af">Sovereign Claude</a>
|
||
<a href="/crons-monitor.html" style="padding:4px 12px;border-radius:6px;font-size:10px;font-weight:600;text-decoration:none;background:#1f2937;color:#9ca3af">Crons</a>
|
||
<a href="/cyber-monitor.html" style="padding:4px 12px;border-radius:6px;font-size:10px;font-weight:600;text-decoration:none;background:#1f2937;color:#9ca3af">Cyber</a>
|
||
</div>
|
||
|
||
<div class="main">
|
||
<div class="tabs" id="tabs"></div>
|
||
<div id="content"></div>
|
||
<div class="footer">WEVAL Consulting — Sovereign AI Operations Platform — weval-consulting.com</div>
|
||
</div>
|
||
|
||
<script>
|
||
const TABS=['Overview','Agents','Pipelines','Infrastructure','Alerts'];
|
||
let activeTab='Overview';
|
||
let DATA={};
|
||
|
||
// Clock
|
||
setInterval(()=>{document.getElementById('clock').textContent=new Date().toLocaleTimeString('fr-FR')},1000);
|
||
|
||
// Fetch live data
|
||
async function fetchData(){
|
||
try{
|
||
const [ag,sync,nr]=await Promise.all([
|
||
fetch('/api/agents-status.php').then(r=>r.json()).catch(()=>({})),
|
||
fetch('/api/enterprise-sync.php').then(r=>r.json()).catch(()=>({})),
|
||
fetch('/api/nonreg-api.php?cat=all').then(r=>r.json()).catch(()=>({}))
|
||
]);
|
||
DATA={agents:ag,sync:sync,nonreg:nr};
|
||
render();
|
||
}catch(e){render();}
|
||
}
|
||
|
||
const SERVICES=[
|
||
{n:'Nginx',p:':80/:443',s:'up',t:'system'},{n:'Sovereign API',p:':4000',s:'up',t:'systemd'},
|
||
{n:'Paperclip',p:':3100',s:'up',t:'systemd'},{n:'DeerFlow',p:':3002/:3003',s:'up',t:'systemd'},
|
||
{n:'Ollama 12 models',p:':11435',s:'up',t:'systemd'},{n:'OpenWebUI',p:':8281',s:'up',t:'docker'},
|
||
{n:'Flowise',p:':3033',s:'up',t:'docker'},{n:'n8n',p:':5678',s:'up',t:'docker'},
|
||
{n:'Twenty CRM',p:':3000',s:'up',t:'docker'},{n:'Mattermost',p:':8065',s:'up',t:'docker'},
|
||
{n:'SearXNG',p:':8080',s:'up',t:'docker'},{n:'Qdrant',p:':6333',s:'up',t:'docker'},
|
||
{n:'Plausible',p:':8000',s:'up',t:'docker'},{n:'Authentik SSO',p:':9000',s:'up',t:'docker'},
|
||
{n:'Vaultwarden',p:':8222',s:'up',t:'docker'},{n:'Uptime Kuma',p:':3001',s:'up',t:'docker'},
|
||
{n:'ClickHouse',p:':8123',s:'up',t:'docker'},{n:'Loki',p:':18821',s:'down',t:'docker'},
|
||
{n:'PMTA',p:':25',s:'up',t:'system'},{n:'KumoMTA',p:':587',s:'up',t:'system'},
|
||
{n:'CrowdSec',p:'—',s:'up',t:'systemd'},{n:'Fail2Ban',p:'—',s:'up',t:'systemd'},
|
||
{n:'Blade Sentinel',p:'*/60s',s:'up',t:'agent'}
|
||
];
|
||
|
||
const ALERTS=[
|
||
{ti:'GitHub PAT',ms:'Expire 15 avril 2026 — renouveler.',sv:'warning'},
|
||
{ti:'OVH SMS',ms:'Identifiants OVH manquants pour Ethica SMS.',sv:'warning'},
|
||
{ti:'ListMonk',ms:'Docker container à déployer sur S95.',sv:'info'}
|
||
];
|
||
|
||
const SERVERS=[
|
||
{n:'S204',ip:'204.168.152.13',ports:48,role:'Primary web/AI',s:'up'},
|
||
{n:'S95',ip:'95.216.167.89',ports:36,role:'Email/DB',s:'up'},
|
||
{n:'S151',ip:'151.80.235.110',ports:7,role:'Tracking relay',s:'up'},
|
||
{n:'Blade',ip:'41.251.x.x',ports:0,role:'Desktop agent',s:'up'},
|
||
{n:'S88',ip:'88.198.4.195',ports:0,role:'GPU (DEAD)',s:'down'}
|
||
];
|
||
|
||
const ROLES=[
|
||
{r:'Engineer',c:67,cl:'var(--ac)'},{r:'General',c:26,cl:'var(--pu)'},{r:'DevOps',c:18,cl:'var(--te)'},
|
||
{r:'QA',c:15,cl:'var(--ok)'},{r:'Researcher',c:10,cl:'var(--pk)'},{r:'PM',c:7,cl:'var(--wa)'},
|
||
{r:'Designer',c:3,cl:'#f97316'},{r:'C-Suite',c:4,cl:'var(--er)'}
|
||
];
|
||
|
||
function badge(text,type){return `<span class="badge badge-${type}">${text}</span>`}
|
||
function metric(lbl,val,sub,color,trend){
|
||
let t=trend?`<div class="trend" style="color:${trend>0?'var(--ok)':'var(--er)'}">${trend>0?'▲':'▼'} ${Math.abs(trend)}%</div>`:'';
|
||
return `<div class="card metric"><div class="lbl">${lbl}</div><div class="val" style="color:${color||'var(--ac)'}">${val}</div>${sub?`<div class="sub">${sub}</div>`:''}${t}</div>`;
|
||
}
|
||
|
||
function render(){
|
||
// Tabs
|
||
document.getElementById('tabs').innerHTML=TABS.map(t=>`<button class="tab${t===activeTab?' on':''}" onclick="activeTab='${t}';render()">${t}</button>`).join('');
|
||
|
||
// Header badges
|
||
const up=SERVICES.filter(s=>s.s==='up').length;
|
||
const crit=ALERTS.filter(a=>a.sv==='critical').length;
|
||
document.getElementById('uptime-badge').textContent=((up/SERVICES.length)*100).toFixed(1)+'% uptime';
|
||
document.getElementById('uptime-badge').className='badge badge-ok';
|
||
document.getElementById('alert-badge').textContent=crit+' critical';
|
||
document.getElementById('alert-badge').className='badge badge-'+(crit>0?'er':'ok');
|
||
|
||
const totalAgents=DATA.agents?.total||5023;
|
||
const pcAgents=DATA.sync?.totals?.agents||150;
|
||
const nrPass=DATA.nonreg?.summary?.pass||148;
|
||
const nrTotal=DATA.nonreg?.summary?.total||148;
|
||
|
||
let h='';
|
||
|
||
if(activeTab==='Overview'){
|
||
h+=`<div class="g6" style="margin-bottom:24px">`;
|
||
h+=metric('Agents',pcAgents,'Paperclip fleet','var(--ac)',50);
|
||
h+=metric('Services',SERVICES.length,up+' UP / '+(SERVICES.length-up)+' KO','var(--ok)');
|
||
h+=metric('Ports','91','4 machines','var(--pu)');
|
||
h+=metric('NonReg',nrPass+'/'+nrTotal,'100% PASS','var(--ok)');
|
||
h+=metric('Health','94%','8 checks','var(--ok)');
|
||
h+=metric('Cost','4.9€','/jour ≈ 147€/mois','var(--wa)');
|
||
h+=`</div>`;
|
||
// Two col
|
||
h+=`<div class="g2" style="margin-bottom:24px;grid-template-columns:2fr 1fr">`;
|
||
h+=`<div class="card"><h3>Services (${SERVICES.length})</h3><div class="scroll">`;
|
||
SERVICES.forEach(s=>{
|
||
const bc=s.t==='docker'?'badge-te':s.t==='systemd'?'badge-pu':s.t==='agent'?'badge-ac':'badge-ok';
|
||
h+=`<div class="svc"><span class="dot" style="background:${s.s==='up'?'var(--ok)':'var(--er)'}"></span><span class="nm">${s.n}</span><span class="pt">${s.p}</span>${badge(s.t,bc.replace('badge-',''))}</div>`;
|
||
});
|
||
h+=`</div></div>`;
|
||
h+=`<div class="card"><h3>Alerts (${ALERTS.length})</h3><div class="scroll">`;
|
||
ALERTS.forEach(a=>{
|
||
const c=a.sv==='critical'?'var(--er)':a.sv==='warning'?'var(--wa)':'var(--td)';
|
||
const ic=a.sv==='critical'?'⛔':a.sv==='warning'?'⚠':'ℹ';
|
||
h+=`<div class="alert" style="background:${c}08;border:1px solid ${c}30"><span style="font-size:18px">${ic}</span><div><div class="ti" style="color:${c}">${a.ti}</div><div class="ms">${a.ms}</div></div></div>`;
|
||
});
|
||
h+=`</div></div></div>`;
|
||
// Servers
|
||
h+=`<div class="card"><h3>Infrastructure (${SERVERS.length} machines / 91 ports)</h3><div class="g5">`;
|
||
SERVERS.forEach(s=>{
|
||
h+=`<div class="srv-card${s.s==='down'?' dead':''}"><div style="font-weight:700;font-size:15px">${s.n}</div><div class="mono" style="font-size:11px;color:var(--td)">${s.ip}</div><div style="font-size:12px;color:var(--tm);margin:6px 0">${s.role}</div>${badge(s.s==='up'?'Online':'Offline',s.s==='up'?'ok':'er')}${s.ports?`<div class="mono" style="font-size:28px;font-weight:800;color:var(--ac);margin-top:8px">${s.ports}</div><div style="font-size:11px;color:var(--td)">ports</div>`:''}</div>`;
|
||
});
|
||
h+=`</div></div>`;
|
||
}
|
||
|
||
else if(activeTab==='Agents'){
|
||
h+=`<div class="g4" style="margin-bottom:24px">`;
|
||
h+=metric('Total agents',pcAgents,'','var(--ac)');
|
||
h+=metric('Instructions','505','3.7 MB','var(--pu)');
|
||
h+=metric('Skills','528','DeerFlow catalog','var(--te)');
|
||
h+=metric('L99 tests','693','96 layers','var(--ok)');
|
||
h+=`</div>`;
|
||
h+=`<div class="card" style="margin-bottom:20px"><h3>Distribution by role</h3>`;
|
||
ROLES.forEach(r=>{
|
||
const pct=((r.c/150)*100).toFixed(0);
|
||
h+=`<div style="display:flex;align-items:center;gap:12px;margin-bottom:10px"><span style="width:80px;font-size:13px;font-weight:600;color:${r.cl}">${r.r}</span><div style="flex:1"><div class="bar"><div class="bar-fill" style="width:${pct}%;background:${r.cl}"></div></div></div><span class="mono" style="font-size:14px;font-weight:700;width:30px;text-align:right">${r.c}</span></div>`;
|
||
});
|
||
h+=`</div>`;
|
||
h+=`<div class="card"><h3>Instruction files (top 8)</h3><div class="g4">`;
|
||
[['SOUL.md',150,'var(--ac)'],['CLAUDE_CODE.md',150,'var(--pu)'],['AGENTS.md',40,'var(--te)'],['ECC_INSTRUCTIONS.md',36,'var(--ok)'],['SKILLS.md',15,'var(--wa)'],['STATUS.md',10,'var(--pk)'],['SOVEREIGN_WIRING.md',9,'var(--er)'],['COGNITIVE_SKILL.md',9,'#f97316']].forEach(([n,c,cl])=>{
|
||
h+=`<div style="padding:12px;border-radius:10px;background:var(--sfh)"><div style="font-size:11px;color:var(--td)">${n}</div><div class="mono" style="font-size:20px;font-weight:800;color:${cl}">${c}</div></div>`;
|
||
});
|
||
h+=`</div></div>`;
|
||
}
|
||
|
||
else if(activeTab==='Pipelines'){
|
||
h+=`<div class="g4" style="margin-bottom:24px">`;
|
||
h+=metric('n8n','5','3 OK / 2 legacy','var(--ac)');
|
||
h+=metric('Flowise','1','chatflow','var(--te)');
|
||
h+=metric('DeerFlow','528','skills','var(--pu)');
|
||
h+=metric('Crons','50+','S204 + S95','var(--ok)');
|
||
h+=`</div>`;
|
||
h+=`<div class="g2" style="margin-bottom:20px">`;
|
||
h+=`<div class="card"><h3>n8n Workflows</h3>`;
|
||
[['WEVIA Health Monitor','active'],['WEVIA AutoLearn v2','active'],['WEVIA Error Monitor v2','active'],['WEVIA AutoLearn v1','KO'],['WEVIA Error Monitor v1','KO']].forEach(([n,s])=>{
|
||
h+=`<div class="svc"><span class="dot" style="background:${s==='active'?'var(--ok)':'var(--er)'}"></span><span class="nm">${n}</span><span style="font-size:11px;color:${s==='active'?'var(--ok)':'var(--er)'}">${s}</span></div>`;
|
||
});
|
||
h+=`</div>`;
|
||
h+=`<div class="card"><h3>Workflow Engines</h3>`;
|
||
[['n8n',':5678','5 workflows'],['Flowise',':3033','1 chatflow'],['DeerFlow',':3002','528 skills'],['Paperclip',':3100','150 agents']].forEach(([n,p,d])=>{
|
||
h+=`<div class="svc">${badge('up','ok')}<span class="nm">${n}</span><span class="pt">${p}</span><span style="font-size:11px;color:var(--tm)">${d}</span></div>`;
|
||
});
|
||
h+=`</div></div>`;
|
||
h+=`<div class="card"><h3>Top cron schedules</h3><div class="cron-grid">`;
|
||
[['*/3min','weval-watchdog.php'],['*/5min','infra-guardian + SSO + blade'],['*/2h','wevia-autolearn.py'],['*/4h','auto-delist + B2B'],['6h+18h','nonreg-master.py (153 tests)'],['daily 4h','oss-discovery + claude-sync'],['daily 5h','ethica-autonomous'],['daily 7h','daily-brief TG + WEVIA Life'],['weekly','nuclei + baselines + enrich']].forEach(([s,d])=>{
|
||
h+=`<span style="color:var(--ac)">${s}</span><span style="color:var(--tm)">${d}</span>`;
|
||
});
|
||
h+=`</div></div>`;
|
||
}
|
||
|
||
else if(activeTab==='Infrastructure'){
|
||
h+=`<div class="g5" style="margin-bottom:24px">`;
|
||
SERVERS.forEach(s=>{
|
||
h+=`<div class="card${s.n==='S204'?' glow':''}" style="text-align:center"><div style="font-size:20px;font-weight:800">${s.n}</div><div class="mono" style="font-size:11px;color:var(--td)">${s.ip}</div>${badge(s.s==='up'?'Online':'Dead',s.s==='up'?'ok':'er')}<div style="font-size:12px;color:var(--tm);margin-top:8px">${s.role}</div>${s.ports?`<div class="mono" style="font-size:28px;font-weight:800;color:var(--ac);margin-top:8px">${s.ports}</div><div style="font-size:11px;color:var(--td)">ports</div>`:''}</div>`;
|
||
});
|
||
h+=`</div>`;
|
||
h+=`<div class="card"><h3>Docker — S204 (19 containers)</h3><div class="g4">`;
|
||
[['Authentik SSO',':9000','up'],['Plausible',':8000','up'],['Mattermost',':8065','up'],['SearXNG',':8080','up'],['n8n',':5678','up'],['Flowise',':3033','up'],['OpenWebUI',':8281','up'],['Twenty',':3000','up'],['Qdrant',':6333','up'],['Vaultwarden',':8222','up'],['Uptime Kuma',':3001','up'],['ClickHouse',':8123','up'],['MiroFish',':3050','up'],['Redis',':6379','up'],['PG (3)',':5432-34','up'],['Loki',':18821','down']].forEach(([n,p,s])=>{
|
||
h+=`<div class="dock-item${s==='down'?' ko':''}"><div style="display:flex;align-items:center;gap:6px;margin-bottom:4px"><span class="dot" style="width:6px;height:6px;border-radius:50%;background:${s==='up'?'var(--ok)':'var(--er)'}"></span><span style="font-weight:600;font-size:13px">${n}</span></div><span class="mono" style="font-size:11px;color:var(--td)">${p}</span></div>`;
|
||
});
|
||
h+=`</div></div>`;
|
||
}
|
||
|
||
else if(activeTab==='Alerts'){
|
||
const cr=ALERTS.filter(a=>a.sv==='critical').length;
|
||
const wa=ALERTS.filter(a=>a.sv==='warning').length;
|
||
const inf=ALERTS.length-cr-wa;
|
||
h+=`<div class="g3" style="margin-bottom:24px">`;
|
||
h+=metric('Critical',cr,'','var(--er)');
|
||
h+=metric('Warning',wa,'','var(--wa)');
|
||
h+=metric('Info',inf,'','var(--td)');
|
||
h+=`</div>`;
|
||
h+=`<div class="card"><h3>All alerts</h3>`;
|
||
ALERTS.forEach(a=>{
|
||
const c=a.sv==='critical'?'var(--er)':a.sv==='warning'?'var(--wa)':'var(--td)';
|
||
const ic=a.sv==='critical'?'⛔':a.sv==='warning'?'⚠':'ℹ';
|
||
h+=`<div class="alert" style="background:${c}08;border:1px solid ${c}30"><span style="font-size:18px">${ic}</span><div><div class="ti" style="color:${c}">${a.ti}</div><div class="ms">${a.ms}</div></div></div>`;
|
||
});
|
||
h+=`</div>`;
|
||
}
|
||
|
||
document.getElementById('content').innerHTML=h;
|
||
}
|
||
|
||
// Init
|
||
fetchData();
|
||
setInterval(fetchData,60000);
|
||
render();
|
||
</script>
|
||
</body>
|
||
</html> |