211 lines
16 KiB
JavaScript
211 lines
16 KiB
JavaScript
/* WEVAL Enterprise Management — Live KPI Injector v2
|
||
Premium CEO Dashboard · 17 avril 2026
|
||
ENRICHIT sans écraser. Auto-refresh 60s.
|
||
*/
|
||
(function(){
|
||
const CACHE_URL = '/api/em-kpi-cache.json';
|
||
const FALLBACK = {
|
||
s204:{load:2.57,cpu_cores:8,ram_total_mb:31335,ram_free_mb:24989,disk_pct:"80%",fpm_workers:102,docker_containers:19},
|
||
s95:{load:5.01,disk_pct:"89%",status:"UP",ram_total_mb:15610,ram_free_mb:11532},
|
||
pmta:[{name:"SER6",status:"DOWN"},{name:"SER7",status:"DOWN"},{name:"SER8",status:"DOWN"},{name:"SER9",status:"DOWN"}],
|
||
assets:{html_pages:227,php_apis:559,wiki_entries:1399,vault_doctrines:32},
|
||
tools:{total:422},
|
||
sovereign:{status:"UP",active:13,total:13,primary:"Cerebras-fast",cost:"0\u20ac",
|
||
providers:["Cerebras-fast","Cerebras-think","Groq","Cloudflare-AI","Gemini","SambaNova","NVIDIA-NIM","Mistral","Groq-OSS","HF-Space","HF-Router","OpenRouter","GitHub-Models"]},
|
||
ethica:{total_hcps:146694,with_email:110120,with_phone:141112,gap_email:36574,pct_email:75.1,pct_phone:96.2,
|
||
by_country:[{country:"DZ",t:"107320",e:"78114"},{country:"MA",t:"19426",e:"14892"},{country:"TN",t:"17364",e:"14816"}]},
|
||
services:[{name:"DeerFlow",port:3002,status:"UP"},{name:"Qdrant",port:6333,status:"UP"},{name:"Ollama",port:11434,status:"UP"},{name:"Redis",port:6379,status:"UP"},{name:"Sovereign",port:4000,status:"UP"},{name:"SearXNG",port:8080,status:"UP"}],
|
||
whisper:{binary:"COMPILED",model:"142MB"},
|
||
git:{head:"17avr-6sigma",dirty:0,status:"CLEAN"},
|
||
nonreg:{total:153,passed:153,score:"100%"},
|
||
health:{score:6,max:6,pct:100},
|
||
grand_total:2658,
|
||
docker:[{name:"gitea",status:"Up"},{name:"qdrant",status:"Up"},{name:"redis",status:"Up"},{name:"searxng",status:"Up"},{name:"uptime-kuma",status:"Up"},{name:"vaultwarden",status:"Up"},{name:"prometheus",status:"Up"},{name:"loki",status:"Up"},{name:"listmonk",status:"Up"},{name:"deerflow",status:"Up"},{name:"n8n",status:"Up"},{name:"mattermost",status:"Up"},{name:"twenty",status:"Up"},{name:"langfuse",status:"Up"},{name:"plausible",status:"Up"},{name:"node-exporter",status:"Up"},{name:"twenty-redis",status:"Up"},{name:"plausible-db",status:"Up"},{name:"deerflow-api",status:"Up"}]
|
||
};
|
||
|
||
async function load(){
|
||
let d;
|
||
try {
|
||
const r = await fetch(CACHE_URL, {cache:'no-cache'});
|
||
d = await r.json();
|
||
} catch(e) {
|
||
d = FALLBACK;
|
||
}
|
||
|
||
// Update stat cards
|
||
const el=id=>document.getElementById(id);
|
||
if(el('statPages'))el('statPages').textContent=d.assets?.html_pages||'–';
|
||
if(el('statApis'))el('statApis').textContent=d.assets?.php_apis||'–';
|
||
if(el('statWiki'))el('statWiki').textContent=d.assets?.wiki_entries||'–';
|
||
if(el('statVault'))el('statVault').textContent=d.assets?.vault_doctrines||'–';
|
||
if(el('statSvc'))el('statSvc').textContent=(d.services?.filter(s=>s.status==='UP').length||0);
|
||
if(el('statIntents'))el('statIntents').textContent='20+';
|
||
if(el('statGrand'))el('statGrand').textContent=(d.grand_total||0).toLocaleString();
|
||
if(el('statHealth'))el('statHealth').textContent=(d.health?.pct||100)+'%';
|
||
|
||
// Inject Live Dashboard section
|
||
let sec = document.getElementById('liveCeoDash');
|
||
if(!sec){
|
||
sec = document.createElement('div');
|
||
sec.id = 'liveCeoDash';
|
||
const heroStats = document.querySelector('.hero-stats');
|
||
if(heroStats && heroStats.parentNode){
|
||
heroStats.parentNode.insertBefore(sec, heroStats.nextSibling);
|
||
} else { document.querySelector('.dash,.container,.main')?.appendChild(sec); }
|
||
}
|
||
sec.innerHTML = render(d);
|
||
}
|
||
|
||
function render(d){
|
||
const diskPct = parseInt(d.s204?.disk_pct)||0;
|
||
const diskClass = diskPct<80?'ld-bar-ok':diskPct<90?'ld-bar-warn':'ld-bar-danger';
|
||
const ramPct = Math.round(((d.s204?.ram_total_mb||1)-(d.s204?.ram_free_mb||0))/(d.s204?.ram_total_mb||1)*100);
|
||
const s95diskPct = parseInt(d.s95?.disk_pct)||0;
|
||
const s95diskClass = s95diskPct<80?'ld-bar-ok':s95diskPct<90?'ld-bar-warn':'ld-bar-danger';
|
||
|
||
const providers = (d.sovereign?.providers||[]).map(p=>`<span class="ld-pill-ia">${p}</span>`).join('');
|
||
const services = (d.services||[]).map(s=>`<div class="ld-svc-item ${s.status==='UP'?'ld-up':'ld-down'}"><div class="ld-svc-dot ${s.status==='UP'?'ld-dot-g':'ld-dot-r'}"></div><div class="ld-svc-name">${s.name}</div><div class="ld-svc-port">:${s.port}</div></div>`).join('');
|
||
const pmta = (d.pmta||[]).map(p=>`<span class="ld-pill-${p.status==='UP'?'up':'down'}">${p.name}</span>`).join('');
|
||
const docker = (d.docker||[]).map(c=>`<div class="ld-dock"><span class="ld-dock-n">${c.name}</span><span class="ld-dock-s">${c.status}</span></div>`).join('');
|
||
|
||
const maxHcp = Math.max(...(d.ethica?.by_country||[]).map(c=>parseInt(c.t)||0),1);
|
||
const countries = (d.ethica?.by_country||[]).map(c=>{
|
||
const flag = c.country==='DZ'?'\u{1F1E9}\u{1F1FF}':c.country==='MA'?'\u{1F1F2}\u{1F1E6}':c.country==='TN'?'\u{1F1F9}\u{1F1F3}':'\u{1F30D}';
|
||
const barW = Math.round((parseInt(c.t)||0)/maxHcp*100);
|
||
const barC = c.country==='DZ'?'ld-bar-dz':c.country==='MA'?'ld-bar-ma':'ld-bar-tn';
|
||
return `<div class="ld-country"><span class="ld-cflag">${flag}</span><span class="ld-cname">${c.country}</span><div class="ld-cbar-w"><div class="ld-cbar ${barC}" style="width:${barW}%"></div></div><span class="ld-cval">${parseInt(c.t).toLocaleString()}</span><span class="ld-ceml">${parseInt(c.e).toLocaleString()}</span></div>`;
|
||
}).join('');
|
||
|
||
return `<style>
|
||
#liveCeoDash{margin:16px 0;font-family:'DM Sans',system-ui,sans-serif}
|
||
.ld-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:12px 0}
|
||
.ld-grid2{grid-template-columns:repeat(2,1fr)}
|
||
.ld-card{background:rgba(14,18,32,0.85);border:1px solid rgba(255,255,255,0.06);border-radius:14px;padding:16px;backdrop-filter:blur(20px)}
|
||
.ld-title{font-size:11px;color:rgba(255,255,255,0.35);text-transform:uppercase;letter-spacing:1px;margin-bottom:10px;display:flex;align-items:center;gap:6px}
|
||
.ld-dot-g{width:6px;height:6px;border-radius:50%;background:#10b981;box-shadow:0 0 8px #10b981;display:inline-block}
|
||
.ld-dot-r{width:6px;height:6px;border-radius:50%;background:#ef4444;box-shadow:0 0 8px #ef4444;display:inline-block}
|
||
.ld-big{font-size:32px;font-weight:700;font-family:'JetBrains Mono',monospace;color:#10b981;line-height:1}
|
||
.ld-sub{font-size:12px;color:rgba(255,255,255,0.5);margin-top:4px}
|
||
.ld-bar-wrap{height:8px;background:rgba(255,255,255,0.06);border-radius:4px;overflow:hidden;margin-top:6px}
|
||
.ld-bar-fill{height:100%;border-radius:4px;transition:width .8s}
|
||
.ld-bar-ok{background:linear-gradient(90deg,#10b981,#059669)}
|
||
.ld-bar-warn{background:linear-gradient(90deg,#f59e0b,#d97706)}
|
||
.ld-bar-danger{background:linear-gradient(90deg,#ef4444,#dc2626)}
|
||
.ld-bar-label{display:flex;justify-content:space-between;font-size:11px;margin-top:8px}
|
||
.ld-bar-label span:first-child{color:rgba(255,255,255,0.3)}
|
||
.ld-bar-label span:last-child{font-family:'JetBrains Mono',monospace}
|
||
.ld-pill-ia{display:inline-block;padding:2px 6px;border-radius:4px;font-size:10px;margin:1px;background:rgba(139,92,246,0.1);color:#a78bfa;border:1px solid rgba(139,92,246,0.2);font-family:'JetBrains Mono',monospace}
|
||
.ld-pill-up{display:inline-block;padding:2px 7px;border-radius:5px;font-size:10px;margin:1px;background:rgba(16,185,129,0.1);color:#10b981;border:1px solid rgba(16,185,129,0.2)}
|
||
.ld-pill-down{display:inline-block;padding:2px 7px;border-radius:5px;font-size:10px;margin:1px;background:rgba(239,68,68,0.1);color:#ef4444;border:1px solid rgba(239,68,68,0.2)}
|
||
.ld-svc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:6px}
|
||
.ld-svc-item{background:rgba(255,255,255,0.02);border:1px solid rgba(255,255,255,0.06);border-radius:8px;padding:8px;text-align:center}
|
||
.ld-svc-item.ld-up{border-color:rgba(16,185,129,0.2)}
|
||
.ld-svc-item.ld-down{border-color:rgba(239,68,68,0.2)}
|
||
.ld-svc-dot{width:5px;height:5px;border-radius:50%;margin:0 auto 4px}
|
||
.ld-svc-name{font-size:10px;font-weight:600;color:#fff}
|
||
.ld-svc-port{font-size:9px;font-family:'JetBrains Mono',monospace;color:rgba(255,255,255,0.3)}
|
||
.ld-country{display:flex;align-items:center;gap:6px;padding:4px 0}
|
||
.ld-cflag{font-size:16px;width:22px}
|
||
.ld-cname{font-size:12px;font-weight:600;width:22px;color:#fff}
|
||
.ld-cbar-w{flex:1;height:8px;background:rgba(255,255,255,0.06);border-radius:4px;overflow:hidden}
|
||
.ld-cbar{height:100%;border-radius:4px}
|
||
.ld-bar-dz{background:linear-gradient(90deg,#10b981,#22d3ee)}
|
||
.ld-bar-ma{background:linear-gradient(90deg,#60a5fa,#a78bfa)}
|
||
.ld-bar-tn{background:linear-gradient(90deg,#f472b6,#a78bfa)}
|
||
.ld-cval{font-size:12px;font-family:'JetBrains Mono',monospace;color:rgba(255,255,255,0.6);width:58px;text-align:right}
|
||
.ld-ceml{font-size:11px;color:rgba(255,255,255,0.3);width:55px;text-align:right}
|
||
.ld-dock{display:flex;justify-content:space-between;padding:3px 0;border-bottom:1px solid rgba(255,255,255,0.03);font-size:11px}
|
||
.ld-dock-n{font-family:'JetBrains Mono',monospace;color:#fff}
|
||
.ld-dock-s{color:#10b981}
|
||
.ld-nr{font-size:48px;font-weight:700;font-family:'JetBrains Mono',monospace;color:#10b981;text-align:center;line-height:1}
|
||
.ld-metric-box{background:rgba(255,255,255,0.02);border:1px solid rgba(255,255,255,0.06);border-radius:10px;padding:14px;text-align:center}
|
||
.ld-metric-val{font-size:22px;font-weight:700;font-family:'JetBrains Mono',monospace}
|
||
.ld-metric-lbl{font-size:9px;color:rgba(255,255,255,0.3);text-transform:uppercase;letter-spacing:1px;margin-top:3px}
|
||
.ld-section-head{font-size:13px;color:rgba(255,255,255,0.4);margin:16px 0 8px;display:flex;align-items:center;gap:8px}
|
||
.ld-live-badge{padding:3px 10px;border-radius:10px;font-size:10px;font-weight:600;background:rgba(16,185,129,0.12);color:#10b981;border:1px solid rgba(16,185,129,0.25)}
|
||
.ld-ts{text-align:right;font-size:10px;color:rgba(255,255,255,0.2);margin-top:8px;font-family:'JetBrains Mono',monospace}
|
||
@media(max-width:1200px){.ld-grid{grid-template-columns:1fr 1fr}}
|
||
@media(max-width:768px){.ld-grid,.ld-grid2{grid-template-columns:1fr}}
|
||
</style>
|
||
<div class="ld-section-head">\u{1F4CA} Live Infrastructure <span class="ld-live-badge">\u{1F534} LIVE</span></div>
|
||
<div class="ld-grid">
|
||
<div class="ld-card">
|
||
<div class="ld-title"><span class="ld-dot-g"></span> S204 — WEVIA + ETHICA</div>
|
||
<div class="ld-big">${d.s204?.load||'?'}</div>
|
||
<div class="ld-sub">Load avg \u00b7 ${d.s204?.cpu_cores||8} CPU \u00b7 ${Math.round((d.s204?.ram_total_mb||0)/1024)} GB RAM</div>
|
||
<div class="ld-sub">RAM libre: ${(d.s204?.ram_free_mb||0).toLocaleString()} MB \u00b7 FPM: ${d.s204?.fpm_workers||'?'} \u00b7 Docker: ${d.s204?.docker_containers||'?'}</div>
|
||
<div class="ld-bar-label"><span>Disk</span><span style="color:${diskPct<80?'#10b981':diskPct<90?'#f59e0b':'#ef4444'}">${d.s204?.disk_pct||'?'}</span></div>
|
||
<div class="ld-bar-wrap"><div class="ld-bar-fill ${diskClass}" style="width:${diskPct}%"></div></div>
|
||
<div class="ld-bar-label"><span>RAM</span><span style="color:#10b981">${ramPct}%</span></div>
|
||
<div class="ld-bar-wrap"><div class="ld-bar-fill ld-bar-ok" style="width:${ramPct}%"></div></div>
|
||
</div>
|
||
<div class="ld-card">
|
||
<div class="ld-title"><span class="${d.s95?.status==='UP'?'ld-dot-g':'ld-dot-r'}"></span> S95 — WEVADS + ARSENAL</div>
|
||
<div class="ld-big">${d.s95?.load||'?'}</div>
|
||
<div class="ld-sub">Load avg \u00b7 ${Math.round((d.s95?.ram_total_mb||0)/1024)} GB RAM</div>
|
||
<div class="ld-bar-label"><span>Disk</span><span style="color:${s95diskPct<80?'#10b981':s95diskPct<90?'#f59e0b':'#ef4444'}">${d.s95?.disk_pct||'?'}</span></div>
|
||
<div class="ld-bar-wrap"><div class="ld-bar-fill ${s95diskClass}" style="width:${s95diskPct}%"></div></div>
|
||
<div class="ld-sub" style="margin-top:10px">PMTA ECS (4 serveurs)</div>
|
||
<div style="margin-top:4px">${pmta}</div>
|
||
</div>
|
||
<div class="ld-card" style="display:flex;flex-direction:column;align-items:center;justify-content:center">
|
||
<div class="ld-title"><span class="ld-dot-g"></span> NONREG PLAYWRIGHT</div>
|
||
<div class="ld-nr">${d.nonreg?.passed||0}/${d.nonreg?.total||0}</div>
|
||
<div class="ld-sub">Score Playwright \u00b7 ${d.nonreg?.score||'?'} \u00b7 6\u03c3</div>
|
||
<svg width="110" height="110" viewBox="0 0 120 120" style="margin-top:12px">
|
||
<circle cx="60" cy="60" r="50" fill="none" stroke="rgba(255,255,255,0.06)" stroke-width="7"/>
|
||
<circle cx="60" cy="60" r="50" fill="none" stroke="#10b981" stroke-width="7" stroke-dasharray="314" stroke-dashoffset="0" stroke-linecap="round" transform="rotate(-90 60 60)"/>
|
||
<text x="60" y="58" text-anchor="middle" fill="#10b981" font-size="22" font-weight="700" font-family="JetBrains Mono,monospace">${d.health?.pct||100}</text>
|
||
<text x="60" y="72" text-anchor="middle" fill="rgba(255,255,255,0.3)" font-size="9">HEALTH %</text>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
<div class="ld-grid">
|
||
<div class="ld-card">
|
||
<div class="ld-title"><span class="ld-dot-g"></span> SOVEREIGN IA CASCADE \u00b7 0\u20ac</div>
|
||
<div style="font-size:26px;font-weight:700;font-family:JetBrains Mono,monospace;color:#a78bfa">${d.sovereign?.active||0}<span style="color:rgba(255,255,255,0.3);font-size:14px">/${d.sovereign?.total||0}</span></div>
|
||
<div class="ld-sub">Providers actifs \u00b7 Primary: ${d.sovereign?.primary||'?'}</div>
|
||
<div style="margin-top:8px">${providers}</div>
|
||
</div>
|
||
<div class="ld-card">
|
||
<div class="ld-title"><span class="ld-dot-g"></span> ETHICA B2B — MAGHREB</div>
|
||
<div style="font-size:26px;font-weight:700;font-family:JetBrains Mono,monospace">${(d.ethica?.total_hcps||0).toLocaleString()}</div>
|
||
<div class="ld-sub">${(d.ethica?.with_email||0).toLocaleString()} emails (${d.ethica?.pct_email||0}%) \u00b7 ${(d.ethica?.with_phone||0).toLocaleString()} phones (${d.ethica?.pct_phone||0}%)</div>
|
||
<div style="margin-top:10px">${countries}</div>
|
||
<div class="ld-sub" style="margin-top:6px">Gap: ${(d.ethica?.gap_email||0).toLocaleString()} emails manquants</div>
|
||
</div>
|
||
<div class="ld-card">
|
||
<div class="ld-title"><span class="ld-dot-g"></span> SERVICES LIVE</div>
|
||
<div class="ld-svc-grid">${services}</div>
|
||
<div style="margin-top:12px">
|
||
<div class="ld-sub"><b style="color:#fff">Git:</b> ${d.git?.head||'?'} \u00b7 <span style="color:${d.git?.status==='CLEAN'?'#10b981':'#f59e0b'}">${d.git?.status||'?'}</span></div>
|
||
<div class="ld-sub"><b style="color:#fff">Crons:</b> ${d.crons?.active||'?'} actifs \u00b7 <b style="color:#fff">Whisper:</b> ${d.whisper?.binary||'?'}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="ld-grid ld-grid2">
|
||
<div class="ld-card">
|
||
<div class="ld-title"><span class="ld-dot-g"></span> DOCKER \u00b7 ${d.docker?.length||0} CONTAINERS</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0 16px">${docker}</div>
|
||
</div>
|
||
<div class="ld-card">
|
||
<div class="ld-title"><span class="ld-dot-g"></span> M\u00c9TRIQUES CL\u00c9S</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">
|
||
<div class="ld-metric-box"><div class="ld-metric-val" style="color:#a78bfa">0\u20ac</div><div class="ld-metric-lbl">Co\u00fbt IA mensuel</div></div>
|
||
<div class="ld-metric-box"><div class="ld-metric-val" style="color:#60a5fa">20/20</div><div class="ld-metric-lbl">WEVIA Intents 6\u03c3</div></div>
|
||
<div class="ld-metric-box"><div class="ld-metric-val" style="color:#f472b6">${d.ethica?.pct_email||75}%</div><div class="ld-metric-lbl">Email coverage</div></div>
|
||
<div class="ld-metric-box"><div class="ld-metric-val" style="color:#22d3ee">${d.whisper?.model||'142MB'}</div><div class="ld-metric-lbl">Whisper model</div></div>
|
||
</div>
|
||
<div style="margin-top:10px;padding:10px;background:rgba(255,255,255,0.02);border-radius:8px;border:1px solid rgba(255,255,255,0.05)">
|
||
<div style="font-size:10px;color:rgba(255,255,255,0.3);margin-bottom:4px">ARCHITECTURE</div>
|
||
<div style="font-size:11px;color:rgba(255,255,255,0.5);line-height:1.6">S204 (8cpu/32gb) \u2192 WEVIA+Ethica+Docker<br>S95 (16gb) \u2192 WEVADS+Arsenal+PG<br>4\u00d7 PMTA ECS Huawei<br>Sovereign \u2192 13 LLMs gratuits</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="ld-ts">Derni\u00e8re MAJ: ${new Date().toLocaleString('fr-FR')} \u00b7 Auto-refresh 60s</div>`;
|
||
}
|
||
|
||
load();
|
||
setInterval(load, 60000);
|
||
})();
|