840 lines
47 KiB
HTML
840 lines
47 KiB
HTML
<!DOCTYPE html>
|
||
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>WEVAL Admin</title>
|
||
<style>
|
||
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@600;800;900&family=JetBrains+Mono:wght@400;700&display=swap');
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
body{background:#0f172a;color:#e2e8f0;font-family:'Nunito',sans-serif}
|
||
.hud{background:#1e293b;border-bottom:1px solid #334155;padding:12px 20px;display:flex;justify-content:space-between;align-items:center;position:sticky;top:0;z-index:10}
|
||
.hud h1{font-size:1.1rem;font-weight:900;color:#e94560}.hud h1 b{color:#53d8fb}
|
||
.hud .links a{color:#53d8fb;text-decoration:none;font-size:.7rem;margin-left:12px;padding:4px 10px;border:1px solid #334155;border-radius:6px}
|
||
.hud .links a:hover{background:#334155}
|
||
.grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;padding:16px}
|
||
@media(max-width:900px){.grid{grid-template-columns:1fr}}
|
||
.card{background:#1e293b;border:1px solid #334155;border-radius:12px;padding:14px;overflow:hidden}
|
||
.card h2{font-size:.82rem;font-weight:800;color:#94a3b8;text-transform:uppercase;letter-spacing:1.5px;margin-bottom:10px;display:flex;align-items:center;gap:6px}
|
||
.card h2 span{font-size:1rem}
|
||
.badge{display:inline-block;padding:2px 8px;border-radius:6px;font-size:.65rem;font-weight:700}
|
||
.bg{background:#22c55e20;color:#22c55e}.br{background:#ef444420;color:#ef4444}.by{background:#f59e0b20;color:#f59e0b}.bb{background:#3b82f620;color:#3b82f6}
|
||
table{width:100%;border-collapse:collapse;font-size:.72rem}
|
||
th{text-align:left;color:#64748b;font-size:.62rem;text-transform:uppercase;letter-spacing:1px;padding:4px 6px;border-bottom:1px solid #334155}
|
||
td{padding:5px 6px;border-bottom:1px solid #1e293b44;font-family:'JetBrains Mono',monospace;font-size:.68rem}
|
||
tr:hover{background:#ffffff06}
|
||
.dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:4px}
|
||
.dot.g{background:#22c55e}.dot.r{background:#ef4444}.dot.y{background:#f59e0b}.dot.b{background:#3b82f6}.dot.gr{background:#6b7280}
|
||
button{background:#334155;color:#e2e8f0;border:none;padding:5px 12px;border-radius:6px;cursor:pointer;font-family:'Nunito';font-size:.7rem;font-weight:700;transition:.2s}
|
||
button:hover{background:#475569}
|
||
button.danger{background:#7f1d1d;color:#fca5a5}button.danger:hover{background:#991b1b}
|
||
button.success{background:#14532d;color:#86efac}button.success:hover{background:#166534}
|
||
.log{background:#0f172a;border:1px solid #334155;border-radius:8px;padding:8px;max-height:200px;overflow-y:auto;font-family:'JetBrains Mono';font-size:.62rem;color:#94a3b8}
|
||
.log .e{color:#ef4444}.log .s{color:#22c55e}.log .w{color:#f59e0b}
|
||
#alerts-list .alert-row{display:flex;align-items:center;gap:8px;padding:4px 0;border-bottom:1px solid #1e293b}
|
||
#alerts-list .alert-row .msg{flex:1;font-size:.7rem;color:#fca5a5}
|
||
#alerts-list .alert-row .who{font-size:.68rem;color:#94a3b8;font-weight:700;min-width:80px}
|
||
input[type="text"]{background:#0f172a;border:1px solid #334155;color:#e2e8f0;padding:5px 10px;border-radius:6px;font-family:'JetBrains Mono';font-size:.7rem;width:100%}
|
||
select{background:#0f172a;border:1px solid #334155;color:#e2e8f0;padding:4px 8px;border-radius:6px;font-size:.7rem}
|
||
.stat-row{display:flex;gap:12px;margin-bottom:8px;flex-wrap:wrap}
|
||
.stat{text-align:center;flex:1;min-width:60px}
|
||
.stat .v{font-size:1.4rem;font-weight:900;font-family:'JetBrains Mono'}.stat .l{font-size:.58rem;color:#64748b;text-transform:uppercase;letter-spacing:1px}
|
||
</style>
|
||
</head><body><div id="live-stats" ondblclick="this.remove()" style="position:fixed;top:0;left:0;right:0;z-index:9999;display:flex;justify-content:center;gap:12px;padding:4px 8px;background:linear-gradient(135deg,#1e293b,#0f172a);font-family:sans-serif"><div style="color:#4ade80;font:700 10px sans-serif"></head><body>#9889; <span id="ls-ag">669</span> Agents</div><div style="color:#60a5fa;font:700 10px sans-serif"></head><body>#127970; <span id="ls-dp">22</span> Depts</div><div style="color:#fbbf24;font:700 10px sans-serif"></head><body>#128051; 20 Docker</div><div style="color:#a78bfa;font:700 10px sans-serif"></head><body>#129302; 10 Ollama</div><div style="color:#f87171;font:700 10px sans-serif"></head><body>#128200; <span id="ls-nr">153/153</span></div><div style="color:#34d399;font:700 10px sans-serif"></head><body>#128274; SSO OK</div><div style="width:6px;height:6px;border-radius:50%;background:#4ade80;animation:lp 2s infinite;align-self:center"></div></div><style>@keyframes lp{0%,100%{opacity:1}50%{opacity:.3}}</style>
|
||
<div class="hud">
|
||
<h1><span style="color:#e94560">WEVAL</span> <b>Admin Panel</b></h1>
|
||
<div class="links">
|
||
<a href="/agents-goodjob.html" target="_blank">🏭 Enterprise</a>
|
||
<a href="/realtime-monitor.html" target="_blank">📡 Monitor</a>
|
||
<a href="/agents-valuechain.html" target="_blank">⛓️ Value Chain</a>
|
||
<a href="/tools-hub.html" target="_blank">🔧 Tools</a>
|
||
<a href="/crons-monitor.html" target="_blank">⏰ Crons</a>
|
||
<a href="/nonreg-report.html" target="_blank">🧪 NonReg</a>
|
||
<a href="/oss-discovery.html" target="_blank">🔭 OSS</a>
|
||
<a href="/ai-benchmark.html" target="_blank">🏆 AI Bench</a>
|
||
<a href="/admin.html">⚙️ Admin</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid">
|
||
<!-- STATS -->
|
||
<div class="card" style="grid-column:1/4">
|
||
<h2><span>📊</span> Dashboard</h2>
|
||
<div class="stat-row">
|
||
<div class="stat"><div class="v" style="color:#53d8fb" id="st-total">—</div><div class="l">Agents Total</div></div>
|
||
<div class="stat"><div class="v" style="color:#22c55e" id="st-active">—</div><div class="l">Active</div></div>
|
||
<div class="stat"><div class="v" style="color:#ef4444" id="st-alerts">—</div><div class="l">Alerts</div></div>
|
||
<div class="stat"><div class="v" style="color:#f59e0b" id="st-docker">—</div><div class="l">Docker</div></div>
|
||
<div class="stat"><div class="v" style="color:#3b82f6" id="st-nonreg">—</div><div class="l">NonReg</div></div>
|
||
<div class="stat"><div class="v" style="color:#a855f7" id="st-ethica">—</div><div class="l">Ethica HCPs</div></div>
|
||
<div class="stat"><div class="v" style="color:#64748b" id="st-disk">—</div><div class="l">Disk S204</div></div>
|
||
<div class="stat"><div class="v" style="color:#eab308" id="st-uptime">—</div><div class="l">Uptime</div></div>
|
||
<div class="stat"><div class="v" style="color:#10b981" id="st-oss">—</div><div class="l">OSS Tools</div></div>
|
||
<div class="stat"><div class="v" style="color:#8b5cf6" id="st-aimodels">—</div><div class="l">AI Models</div></div>
|
||
<div class="stat"><div class="v" style="color:#06b6d4" id="st-skills">—</div><div class="l">Skills RAG</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ALERTS -->
|
||
<div class="card">
|
||
<h2><span>🔴</span> Alertes Actives <span class="badge br" id="alert-count">0</span></h2>
|
||
<div id="alerts-list"></div>
|
||
<div style="margin-top:8px;display:flex;gap:6px">
|
||
<input type="text" id="alert-agent" placeholder="Agent name...">
|
||
<input type="text" id="alert-msg" placeholder="Alert message...">
|
||
<button class="danger" onclick="sendAlert()">⚠️ Alert</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TRIGGER -->
|
||
<div class="card">
|
||
<h2><span>🎯</span> Trigger Agent</h2>
|
||
<p style="font-size:.68rem;color:#64748b;margin-bottom:8px">Déclencher manuellement un agent</p>
|
||
<select id="trig-agent" style="width:100%;margin-bottom:6px"></select>
|
||
<input type="text" id="trig-action" placeholder="Action description..." style="margin-bottom:6px">
|
||
<div style="display:flex;gap:6px">
|
||
<button class="success" onclick="triggerManual()">▶️ Trigger</button>
|
||
<button onclick="triggerAll()">▶️ Trigger All Dept</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SERVICES -->
|
||
<div class="card">
|
||
<h2><span>🐳</span> Services Status</h2>
|
||
<div id="services-list" style="max-height:300px;overflow-y:auto"></div>
|
||
<button style="margin-top:6px" onclick="refreshServices()">🔄 Refresh</button>
|
||
</div>
|
||
|
||
<!-- AGENTS TABLE -->
|
||
<div class="card" style="grid-column:1/3">
|
||
<h2><span>👥</span> Tous les Agents <input type="text" id="agent-search" placeholder="Chercher..." style="width:200px;margin-left:auto" oninput="filterAgents()"></h2>
|
||
<div style="max-height:400px;overflow-y:auto">
|
||
<table>
|
||
<thead><tr><th>Agent</th><th>Dept</th><th>Status</th><th>Type</th><th>Actions</th></tr></thead>
|
||
<tbody id="agents-body"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- LOGS -->
|
||
<div class="card">
|
||
<h2><span>📋</span> Activity Log</h2>
|
||
<div class="log" id="log-area"></div>
|
||
</div>
|
||
|
||
<!-- NONREG -->
|
||
<div class="card">
|
||
<h2><span>🧪</span> NonReg Status</h2>
|
||
<div id="nonreg-status"></div>
|
||
<button style="margin-top:6px" onclick="runNonReg()">▶️ Run NonReg</button>
|
||
</div>
|
||
|
||
<!-- INFRA -->
|
||
<div class="card">
|
||
<h2><span>🖥️</span> Infrastructure</h2>
|
||
<div id="infra-status"></div>
|
||
<button style="margin-top:6px" onclick="refreshInfra()">🔄 Refresh</button>
|
||
</div>
|
||
|
||
<!-- QUICK ACTIONS -->
|
||
<div class="card">
|
||
<h2><span>⚡</span> Quick Actions</h2>
|
||
<div style="display:flex;flex-wrap:wrap;gap:6px">
|
||
<button onclick="qaction('opcache')">🗑️ Clear OPcache</button>
|
||
<button onclick="qaction('cache')">🧹 Clear SHM Cache</button>
|
||
<button onclick="qaction('nginx')">🔄 Reload Nginx</button>
|
||
<button onclick="qaction('watchdog')">🐕 Run Watchdog</button>
|
||
<button onclick="qaction('nonreg')">🧪 Run NonReg</button>
|
||
<button onclick="qaction('docker')">🐳 Docker Status</button>
|
||
<button onclick="qaction('disk')">💾 Disk Usage</button>
|
||
<button onclick="qaction('ethica')">💊 Ethica Count</button>
|
||
</div>
|
||
<div class="log" id="qaction-log" style="margin-top:8px;min-height:60px"></div>
|
||
</div>
|
||
|
||
<!-- OSS DISCOVERY -->
|
||
<div class="card">
|
||
<h2><span>🔭</span> OSS Discovery <span class="badge bg" id="oss-count">716</span></h2>
|
||
<div id="oss-needs" style="max-height:200px;overflow-y:auto"></div>
|
||
<div style="margin-top:8px;display:flex;gap:6px">
|
||
<button class="success" onclick="runOSSScan()">⚡ Scan Now</button>
|
||
<a href="/oss-discovery.html" target="_blank"><button>🔭 Full Page</button></a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- AI BENCHMARK -->
|
||
<div class="card">
|
||
<h2><span>🏆</span> AI Benchmark <span class="badge bb" id="ai-count">0</span></h2>
|
||
<div id="ai-scores" style="max-height:200px;overflow-y:auto"></div>
|
||
<div style="margin-top:8px;display:flex;gap:6px">
|
||
<button class="success" onclick="runAIBench()">▶️ Run Bench</button>
|
||
<a href="/ai-benchmark.html" target="_blank"><button>🏆 Full Page</button></a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TRENDING OSS -->
|
||
<div class="card">
|
||
<h2><span>🔥</span> Trending OSS</h2>
|
||
<div id="trending-list" style="max-height:200px;overflow-y:auto"></div>
|
||
<button style="margin-top:6px" onclick="loadTrending()">🔄 Refresh</button>
|
||
</div>
|
||
|
||
<!-- TOOLS HUB -->
|
||
<div class="card">
|
||
<h2><span>🔧</span> Tools Hub Status</h2>
|
||
<div id="toolshub-status"></div>
|
||
<a href="/tools-hub.html" target="_blank"><button style="margin-top:6px">🔧 Full Page</button></a>
|
||
</div>
|
||
|
||
<!-- HEALTH SCORE -->
|
||
<div class="card" style="grid-column:1/4">
|
||
<h2><span>🏥</span> System Health Score</h2>
|
||
<div style="display:flex;align-items:center;gap:20px;flex-wrap:wrap">
|
||
<div style="position:relative;width:120px;height:120px">
|
||
<svg viewBox="0 0 120 120" style="width:120px;height:120px">
|
||
<circle cx="60" cy="60" r="52" fill="none" stroke="#1e293b" stroke-width="10"/>
|
||
<circle cx="60" cy="60" r="52" fill="none" stroke-linecap="round" stroke-width="10" id="health-ring"
|
||
stroke="#22c55e" stroke-dasharray="327" stroke-dashoffset="33" transform="rotate(-90 60 60)"/>
|
||
<text x="60" y="55" text-anchor="middle" fill="#e2e8f0" font-size="28" font-weight="900" font-family="JetBrains Mono" id="health-num">—</text>
|
||
<text x="60" y="72" text-anchor="middle" fill="#64748b" font-size="10" font-family="Nunito">/100</text>
|
||
</svg>
|
||
</div>
|
||
<div style="flex:1;display:grid;grid-template-columns:repeat(4,1fr);gap:8px" id="health-checks"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- COST TRACKING -->
|
||
<div class="card">
|
||
<h2><span>💰</span> AI Cost Tracking <span class="badge by" id="cost-total">—</span></h2>
|
||
<div id="cost-breakdown" style="font-size:.7rem"></div>
|
||
</div>
|
||
|
||
<!-- LATENCY -->
|
||
<div class="card">
|
||
<h2><span>⏱️</span> Latency Monitor</h2>
|
||
<div id="latency-list" style="max-height:220px;overflow-y:auto"></div>
|
||
</div>
|
||
|
||
<!-- PREDICTIVE -->
|
||
<div class="card">
|
||
<h2><span>🔮</span> Prédictions & Risques</h2>
|
||
<div id="predictions"></div>
|
||
</div>
|
||
|
||
<!-- KPI EVOLUTION CHART -->
|
||
<div class="card" style="grid-column:1/4">
|
||
<h2><span>📈</span> Évolution KPIs — 7 derniers jours</h2>
|
||
<canvas id="kpi-chart" height="200" style="width:100%;border-radius:8px;background:#0f172a"></canvas>
|
||
<div style="display:flex;gap:16px;margin-top:8px;font-size:.62rem;flex-wrap:wrap">
|
||
<span style="color:#22c55e">● Ethica HCPs</span>
|
||
<span style="color:#3b82f6">● NonReg Tests</span>
|
||
<span style="color:#f59e0b">● Disk %</span>
|
||
<span style="color:#a855f7">● AI Requests</span>
|
||
<span style="color:#ef4444">● Alerts</span>
|
||
<span style="color:#06b6d4">● Docker UP</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- AGENT PRODUCTIVITY -->
|
||
<div class="card" style="grid-column:1/4">
|
||
<h2><span>📈</span> Productivité Agents / Jour + Livrables</h2>
|
||
<div style="overflow-x:auto">
|
||
<table>
|
||
<thead><tr><th>Agent</th><th>Dept</th><th>Productivité/jour</th><th>Livrables / Outputs</th><th>KPI</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><b>👔 CEO</b></td><td>Direction</td><td>1 daily brief, 2-3 décisions</td><td>Brief Telegram 7h, validation budget, hiring</td><td class="badge bg">✅ Quotidien</td></tr>
|
||
<tr><td><b>💊 Ethica</b></td><td>Prospect</td><td>~~100 HCPs enrichis/jour</td><td>131K+ HCPs base, emails DZ+MA+TN, téléphones</td><td style="color:#22c55e;font-weight:900">+500/j</td></tr>
|
||
<tr><td><b>📊 Analyst</b></td><td>Prospect</td><td>5-10 analyses/jour</td><td>SWOT, segments B2B, rapports concurrence</td><td class="badge bg">Actif</td></tr>
|
||
<tr><td><b>✍️ Writer</b></td><td>Prospect</td><td>10-20 emails/jour</td><td>Cold emails, proposals, posts LinkedIn</td><td class="badge bg">Actif</td></tr>
|
||
<tr><td><b>🏗️ Architect</b></td><td>Consult</td><td>1-2 blueprints/jour</td><td>Architectures cloud, schémas microservices, diagrammes</td><td class="badge bg">Actif</td></tr>
|
||
<tr><td><b>🦌 DeerFlow</b></td><td>Research</td><td>3-5 recherches deep/jour</td><td>Synthèses 12+ sources, veille tech, rapports R&D</td><td style="color:#22c55e;font-weight:900">113 skills</td></tr>
|
||
<tr><td><b>⚡ Executor</b></td><td>Dev</td><td>5-15 deploys/jour</td><td>Scripts, migrations DB, Dockerfiles, releases</td><td class="badge bg">Actif</td></tr>
|
||
<tr><td><b>🐛 Debugger</b></td><td>Dev</td><td>3-8 fixes/jour</td><td>Bug fixes API, memory leaks, SQL patches</td><td class="badge bg">Actif</td></tr>
|
||
<tr><td><b>🤖 WEDROID</b></td><td>Dev</td><td>10-30 auto-fixes/jour</td><td>Repair PG index, clean rows, restart services</td><td style="color:#22c55e;font-weight:900">v5.0 Auto</td></tr>
|
||
<tr><td><b>🎨 Designer</b></td><td>Dev</td><td>2-5 mockups/jour</td><td>Dashboard UX, design system, proto Figma, CSS</td><td class="badge bg">Actif</td></tr>
|
||
<tr><td><b>🐕 Watchdog</b></td><td>Infra</td><td>480 checks/jour (*/3min)</td><td>Restart Nginx, Docker restart, disk alerts</td><td style="color:#22c55e;font-weight:900">480/j</td></tr>
|
||
<tr><td><b>🛡️ Guardian</b></td><td>Infra</td><td>288 scans/jour (*/5min)</td><td>chattr +i, firewall, intrus detection</td><td style="color:#22c55e;font-weight:900">288/j</td></tr>
|
||
<tr><td><b>💻 Blade</b></td><td>Desktop</td><td>1440 syncs/jour (60s)</td><td>Desktop→S204 sync, PowerShell tasks, uploads</td><td style="color:#22c55e;font-weight:900">1440/j</td></tr>
|
||
<tr><td><b>🔐 Security</b></td><td>Sécu</td><td>2-5 audits/jour</td><td>OWASP scans, header audit, XSS tests, SSL checks</td><td class="badge bg">Actif</td></tr>
|
||
<tr><td><b>🧪 QA</b></td><td>QA</td><td>296 tests/jour (2×148)</td><td>NonReg 153 tests, Playwright 41, visual baselines</td><td style="color:#22c55e;font-weight:900">296/j</td></tr>
|
||
<tr><td><b>🔬 Scientist</b></td><td>QA</td><td>1 bench/jour (5h cron)</td><td>182 modèles benchmarkés, leaderboard, scores</td><td style="color:#22c55e;font-weight:900">182 models</td></tr>
|
||
<tr><td><b>⏰ EthicaCron</b></td><td>Cron</td><td>288 runs/jour (*/5min)</td><td>Drip DZ+MA+TN, DabaDoc scrape, master dedup</td><td style="color:#22c55e;font-weight:900">288/j</td></tr>
|
||
<tr><td><b>🔄 B2BCron</b></td><td>Cron</td><td>6 cycles/jour (/4h)</td><td>LinkedIn scrape, email pattern, mega enricher</td><td style="color:#f59e0b;font-weight:900">6/j</td></tr>
|
||
<tr><td><b>📮 PMTA</b></td><td>MTA</td><td>Pilot pas lancé</td><td>DKIM signing, bounce processing, queue management</td><td style="color:#22c55e;font-weight:900">10K/j</td></tr>
|
||
<tr><td><b>🚀 KumoMTA</b></td><td>MTA</td><td>Config ready</td><td>Smart routing, IP warming, DMARC compliance</td><td style="color:#22c55e;font-weight:900">5K/j</td></tr>
|
||
<tr><td><b>⚡ Groq</b></td><td>AI</td><td>~150 req/jour</td><td>Réponses chatbot, classification, embeddings</td><td style="color:#22c55e;font-weight:900">500/j</td></tr>
|
||
<tr><td><b>🏠 Ollama</b></td><td>AI</td><td>~50 req/jour (7 modèles)</td><td>Local inference souveraine, medllama2, weval-brain</td><td style="color:#22c55e;font-weight:900">200/j</td></tr>
|
||
<tr><td><b>🎯 SkillsRAG</b></td><td>Platform</td><td>~100 queries/jour</td><td>4414 skills Qdrant, search+match, auto-select</td><td style="color:#22c55e;font-weight:900">4414 skills</td></tr>
|
||
<tr><td><b>🏆 AIBench</b></td><td>Platform</td><td>1 daily run (5h)</td><td>182 modèles scorés, 15 domaines, leaderboard</td><td style="color:#22c55e;font-weight:900">182/day</td></tr>
|
||
<tr><td><b>🔭 OSSDiscover</b></td><td>Platform</td><td>1 scan/jour</td><td>685 OSS tools catalogués, trending, évaluation</td><td style="color:#22c55e;font-weight:900">505 tools</td></tr>
|
||
<tr style="background:#14532d20;font-weight:700"><td colspan="2">📊 TOTAL PLATEFORME /JOUR</td><td>~5,000+ actions automatisées</td><td>191 agents, 20 depts, 6 APIs temps réel</td><td style="color:#22c55e;font-size:.9rem">🟢 LIVE</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ENTERPRISE VIZ CONTROL -->
|
||
<div class="card" style="grid-column:1/4">
|
||
<h2><span>🏭</span> Enterprise Visualization Control</h2>
|
||
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
||
<a href="/agents-goodjob.html" target="_blank"><button>🏭 Enterprise Sim</button></a>
|
||
<a href="/agents-fleet.html" target="_blank"><button>👥 Fleet Grid</button></a>
|
||
<a href="/agents-valuechain.html" target="_blank"><button>⛓️ Value Chain</button></a>
|
||
<a href="/agents-hd.html" target="_blank"><button>🎮 HD View</button></a>
|
||
<a href="/realtime-monitor.html" target="_blank"><button>📡 Monitor</button></a>
|
||
<a href="/claude-monitor.html" target="_blank"><button>📋 Claude Sync</button></a>
|
||
<a href="/crons-monitor.html" target="_blank"><button>⏰ Crons</button></a>
|
||
<a href="/l99.html" target="_blank"><button>🎮 L99</button></a>
|
||
<a href="/crm.html" target="_blank"><button>📇 CRM</button></a>
|
||
</div>
|
||
<div style="margin-top:8px;font-size:.65rem;color:#64748b">
|
||
191 agents | 21 départements | 685 OSS tools | 180 AI models | 528 skills | 3 alertes actives
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const AGENTS_DATA=[];
|
||
const ALERTS=[];
|
||
const LOGS=[];
|
||
|
||
function log(msg,type){
|
||
LOGS.unshift({t:Date.now(),msg:msg,type:type||'s'});
|
||
if(LOGS.length>50)LOGS.pop();
|
||
renderLogs();
|
||
}
|
||
function renderLogs(){
|
||
document.getElementById('log-area').innerHTML=LOGS.map(function(l){
|
||
var cls=l.type==='e'?'e':l.type==='w'?'w':'s';
|
||
var time=new Date(l.t).toLocaleTimeString();
|
||
return '<div class="'+cls+'">'+time+' '+l.msg+'</div>';
|
||
}).join('');
|
||
}
|
||
|
||
// Fetch agents
|
||
function loadAgents(){
|
||
fetch('/api/agents-status.php').then(function(r){return r.json();}).then(function(d){
|
||
if(!d.agents)return;
|
||
document.getElementById('st-total').textContent=d.total;
|
||
document.getElementById('st-active').textContent=d.active;
|
||
// Populate table
|
||
var sel=document.getElementById('trig-agent');
|
||
sel.innerHTML=d.agents.map(function(a){return '<option value="'+a.name+'">'+a.name+' ('+a.type+')</option>';}).join('');
|
||
// Table
|
||
renderAgentsTable(d.agents);
|
||
log('Agents loaded: '+d.total+' total, '+d.active+' active');
|
||
}).catch(function(e){log('Agent API error: '+e,'e');});
|
||
}
|
||
|
||
function renderAgentsTable(agents){
|
||
var search=(document.getElementById('agent-search').value||'').toLowerCase();
|
||
var html='';
|
||
agents.forEach(function(a){
|
||
if(search&&!a.name.toLowerCase().includes(search)&&!a.type.toLowerCase().includes(search))return;
|
||
var statusClass=a.status==='active'?'g':a.status==='down'?'r':'y';
|
||
html+='<tr><td><b>'+a.name+'</b></td><td>'+a.type+'</td>';
|
||
html+='<td><span class="dot '+statusClass+'"></span>'+a.status+'</td>';
|
||
html+='<td><span class="badge '+(a.status==='active'?'bg':'br')+'">'+a.type+'</span></td>';
|
||
html+='<td><button onclick="trigAgent(\''+a.name+'\')">▶️</button></td></tr>';
|
||
});
|
||
document.getElementById('agents-body').innerHTML=html;
|
||
}
|
||
function filterAgents(){loadAgents();}
|
||
|
||
// Services
|
||
function refreshServices(){
|
||
var el=document.getElementById('services-list');
|
||
el.innerHTML='<div style="color:#64748b">Loading...</div>';
|
||
fetch('/api/cx',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},
|
||
body:'k=WEVADS2026&c='+btoa('docker ps --format "{{.Names}} {{.Status}}" | head -25')
|
||
}).then(function(r){return r.text();}).then(function(d){
|
||
var lines=d.trim().split('\n').filter(function(l){return l.trim();});
|
||
el.innerHTML=lines.map(function(l){
|
||
var parts=l.split(' ');var name=parts[0];var status=parts.slice(1).join(' ');
|
||
var isUp=status.toLowerCase().includes('up');
|
||
return '<div style="padding:3px 0;font-size:.68rem"><span class="dot '+(isUp?'g':'r')+'"></span><b>'+name+'</b> <span style="color:#64748b">'+status+'</span></div>';
|
||
}).join('');
|
||
log('Docker: '+lines.length+' containers');
|
||
}).catch(function(e){el.innerHTML='Error';log('Docker error','e');});
|
||
}
|
||
|
||
// NonReg
|
||
function refreshNonReg(){
|
||
fetch('/api/nonreg-api.php?cat=all').then(function(r){return r.json();}).then(function(d){
|
||
if(!d.summary)return;
|
||
var pass=d.summary.pass===d.summary.total;
|
||
document.getElementById('st-nonreg').textContent=d.summary.pass+'/'+d.summary.total;
|
||
document.getElementById('st-nonreg').style.color=pass?'#22c55e':'#ef4444';
|
||
document.getElementById('nonreg-status').innerHTML=
|
||
'<div style="font-size:2rem;text-align:center;margin:10px 0">'+(pass?'✅':'❌')+'</div>'+
|
||
'<div style="text-align:center;font-size:.8rem;font-weight:800;color:'+(pass?'#22c55e':'#ef4444')+'">'+d.summary.pass+'/'+d.summary.total+' tests</div>'+
|
||
'<div style="text-align:center;font-size:.65rem;color:#64748b">'+new Date((d.timestamp||0)*1000).toLocaleString()+'</div>';
|
||
log('NonReg: '+d.summary.pass+'/'+d.summary.total+(pass?' PASS':' FAIL'),pass?'s':'e');
|
||
}).catch(function(){});
|
||
}
|
||
|
||
// Infra
|
||
function refreshInfra(){
|
||
fetch('/api/cx',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},
|
||
body:'k=WEVADS2026&c='+btoa('df -h / | tail -1 | awk \'{print $5}\'')
|
||
}).then(function(r){return r.text();}).then(function(d){
|
||
document.getElementById('st-disk').textContent=d.trim();
|
||
var pct=parseInt(d);
|
||
document.getElementById('st-disk').style.color=pct>85?'#ef4444':pct>70?'#f59e0b':'#22c55e';
|
||
document.getElementById('infra-status').innerHTML='<div style="font-size:.75rem"><b>Disk S204:</b> '+d.trim()+'</div>';
|
||
log('Disk: '+d.trim());
|
||
}).catch(function(){});
|
||
// Ethica count
|
||
fetch('/api/cx',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},
|
||
body:'k=WEVADS2026&c='+btoa("curl -sk 'http://10.1.0.3:5890/api/sentinel-brain.php?action=exec&cmd=PGPASSWORD%3Dadmin123+psql+-h+10.1.0.3+-U+admin+-d+adx_system+-t+-c+\"SELECT+count(*)+FROM+ethica.medecins_validated\"' 2>/dev/null | tr -d ' '")
|
||
}).then(function(r){return r.text();}).then(function(d){
|
||
var num=d.trim().replace(/\D/g,'');
|
||
if(num)document.getElementById('st-ethica').textContent=parseInt(num).toLocaleString();
|
||
}).catch(function(){});
|
||
}
|
||
|
||
// Quick actions
|
||
function qaction(action){
|
||
var cmds={
|
||
opcache:'php -r "opcache_reset();echo \\"OPcache cleared\\";"',
|
||
cache:'rm -f /dev/shm/wevia_cache_* && echo "SHM cache cleared"',
|
||
nginx:'nginx -t && nginx -s reload && echo "Nginx reloaded"',
|
||
watchdog:'php /var/www/html/api/weval-watchdog.php 2>&1 | tail -5',
|
||
nonreg:'curl -sk https://weval-consulting.com/api/nonreg-api.php?cat=all | python3 -c "import sys,json;d=json.load(sys.stdin);print(f\\"{d[\'summary\'][\'pass\']}/{d[\'summary\'][\'total\']} tests\\")"',
|
||
docker:'docker ps --format "{{.Names}}: {{.Status}}" | head -20',
|
||
disk:'df -h / /opt /var | tail -3',
|
||
ethica:"curl -sk 'http://10.1.0.3:5890/api/sentinel-brain.php?action=exec&cmd=PGPASSWORD%3Dadmin123+psql+-h+10.1.0.3+-U+admin+-d+adx_system+-t+-c+\"SELECT+count(*)+FROM+ethica.medecins_validated\"' | python3 -c \"import sys,json;print(json.load(sys.stdin)['output'])\""
|
||
};
|
||
var cmd=cmds[action];if(!cmd)return;
|
||
var el=document.getElementById('qaction-log');
|
||
el.innerHTML='<div style="color:#f59e0b">Running '+action+'...</div>';
|
||
fetch('/api/cx',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},
|
||
body:'k=WEVADS2026&c='+btoa(cmd)
|
||
}).then(function(r){return r.text();}).then(function(d){
|
||
el.innerHTML='<div class="s">$ '+action+'</div><div>'+d.replace(/\n/g,'<br>')+'</div>';
|
||
log(action+': done');
|
||
}).catch(function(e){el.innerHTML='<div class="e">Error: '+e+'</div>';});
|
||
}
|
||
|
||
// Alerts
|
||
function sendAlert(){
|
||
var agent=document.getElementById('alert-agent').value;
|
||
var msg=document.getElementById('alert-msg').value;
|
||
if(!agent||!msg)return;
|
||
ALERTS.push({agent:agent,msg:msg,t:Date.now()});
|
||
renderAlerts();
|
||
log('Alert sent to '+agent+': '+msg,'w');
|
||
document.getElementById('alert-agent').value='';
|
||
document.getElementById('alert-msg').value='';
|
||
}
|
||
function dismissAlert(i){ALERTS.splice(i,1);renderAlerts();}
|
||
function renderAlerts(){
|
||
document.getElementById('alert-count').textContent=ALERTS.length;
|
||
document.getElementById('st-alerts').textContent=ALERTS.length;
|
||
document.getElementById('alerts-list').innerHTML=ALERTS.map(function(a,i){
|
||
return '<div class="alert-row"><span class="who">'+a.agent+'</span><span class="msg">⚠️ '+a.msg+'</span><button class="danger" onclick="dismissAlert('+i+')">✕</button></div>';
|
||
}).join('')||'<div style="color:#64748b;font-size:.7rem;padding:8px">Aucune alerte active</div>';
|
||
}
|
||
|
||
// Trigger
|
||
function trigAgent(name){
|
||
log('Triggered: '+name);
|
||
alert('Agent '+name+' triggered! (visible on Enterprise page)');
|
||
}
|
||
function triggerManual(){
|
||
var name=document.getElementById('trig-agent').value;
|
||
var action=document.getElementById('trig-action').value||'Manual trigger';
|
||
trigAgent(name);
|
||
}
|
||
function triggerAll(){
|
||
var name=document.getElementById('trig-agent').value;
|
||
log('Triggered all in dept of '+name);
|
||
}
|
||
function runNonReg(){
|
||
log('NonReg triggered...');
|
||
qaction('nonreg');
|
||
setTimeout(refreshNonReg,5000);
|
||
}
|
||
|
||
|
||
// OSS Discovery
|
||
function loadOSS(){
|
||
fetch('/api/oss-cache.json?v=8avr&t='+Date.now()).then(function(r){return r.json();}).then(function(d){
|
||
var report=d.report||{};var skills=d.skills||{};
|
||
var byStatus=report.by_status||{};
|
||
var total=Object.values(byStatus).reduce(function(a,b){return a+b;},0);
|
||
document.getElementById('st-oss').textContent=total;
|
||
document.getElementById('st-skills').textContent=skills.total||0;
|
||
document.getElementById('oss-count').textContent=total;
|
||
// By need breakdown
|
||
var needs=report.by_need||{};
|
||
var needsArr=Object.entries(needs).sort(function(a,b){return b[1]-a[1];});
|
||
var maxN=needsArr.length?needsArr[0][1]:1;
|
||
document.getElementById('oss-needs').innerHTML=
|
||
'<div style="margin-bottom:6px;font-size:.68rem;color:#64748b">'+
|
||
'<span class="badge bg">'+( byStatus.integrated||0)+' intégrés</span> '+
|
||
'<span class="badge by">'+(byStatus.discovered||0)+' découverts</span> '+
|
||
'<span class="badge bb">'+(byStatus.evaluated||0)+' évalués</span></div>'+
|
||
needsArr.slice(0,12).map(function(n){
|
||
var pct=Math.round(n[1]/maxN*100);
|
||
return '<div style="margin:2px 0;display:flex;align-items:center;gap:6px;font-size:.65rem">'+
|
||
'<span style="min-width:90px;color:#94a3b8">'+n[0].replace(/_/g,' ')+'</span>'+
|
||
'<div style="flex:1;background:#1e293b;border-radius:3px;height:10px;overflow:hidden">'+
|
||
'<div style="width:'+pct+'%;height:100%;background:linear-gradient(90deg,#10b981,#06b6d4);border-radius:3px"></div></div>'+
|
||
'<span style="min-width:30px;text-align:right;color:#53d8fb;font-weight:700">'+n[1]+'</span></div>';
|
||
}).join('');
|
||
log('OSS: '+total+' tools, '+(skills.total||0)+' skills');
|
||
}).catch(function(e){log('OSS error: '+e,'e');});
|
||
}
|
||
function runOSSScan(){
|
||
log('OSS scan triggered...');
|
||
fetch('/api/oss-discovery.php?k=WEVADS2026&action=auto_run').then(function(r){return r.json();}).then(function(d){
|
||
log('OSS scan: +'+( d.new_tools||0)+' new tools','s');
|
||
loadOSS();
|
||
}).catch(function(e){log('OSS scan error','e');});
|
||
}
|
||
|
||
// AI Benchmark
|
||
function loadAIBench(){
|
||
fetch('/api/ai-benchmark-cache.json?t='+Date.now()).then(function(r){return r.json();}).then(function(d){
|
||
var report=d.report||{};
|
||
var composite=report.composite||{};
|
||
var totalAIs=report.total_ais||d.total_ais||0;
|
||
document.getElementById('st-aimodels').textContent=totalAIs;
|
||
document.getElementById('ai-count').textContent=totalAIs;
|
||
// Composite scores
|
||
var scores=Object.entries(composite).sort(function(a,b){return b[1]-a[1];});
|
||
document.getElementById('ai-scores').innerHTML=
|
||
'<div style="margin-bottom:6px;font-size:.7rem;color:#64748b">Composite avg: <b style="color:#53d8fb">'+(report.composite_avg||0)+'%</b> | Infra: <b style="color:#f59e0b">'+(report.infra_avg||0)+'%</b></div>'+
|
||
scores.map(function(s){
|
||
var color=s[1]>=80?'#22c55e':s[1]>=60?'#f59e0b':'#ef4444';
|
||
return '<div style="margin:2px 0;display:flex;align-items:center;gap:6px;font-size:.65rem">'+
|
||
'<span style="min-width:80px;color:#94a3b8">'+s[0]+'</span>'+
|
||
'<div style="flex:1;background:#1e293b;border-radius:3px;height:10px;overflow:hidden">'+
|
||
'<div style="width:'+s[1]+'%;height:100%;background:'+color+';border-radius:3px"></div></div>'+
|
||
'<span style="min-width:30px;text-align:right;color:'+color+';font-weight:700">'+s[1]+'%</span></div>';
|
||
}).join('');
|
||
log('AI Bench: '+totalAIs+' models, avg '+report.composite_avg+'%');
|
||
}).catch(function(e){log('AI Bench error: '+e,'e');});
|
||
}
|
||
function runAIBench(){
|
||
log('AI Benchmark triggered...');
|
||
fetch('/api/ai-benchmark.php?action=run&k=WEVADS2026').then(function(r){return r.text();}).then(function(d){
|
||
log('AI Bench: '+d.substring(0,80));
|
||
setTimeout(loadAIBench,5000);
|
||
}).catch(function(e){log('AI Bench error','e');});
|
||
}
|
||
|
||
// Trending
|
||
function loadTrending(){
|
||
fetch('/api/oss-trending.json?t='+Date.now()).then(function(r){return r.json();}).then(function(d){
|
||
var items=d.trending||d||[];
|
||
if(!Array.isArray(items))items=[];
|
||
document.getElementById('trending-list').innerHTML=items.slice(0,10).map(function(t){
|
||
return '<div style="padding:3px 0;font-size:.68rem;border-bottom:1px solid #1e293b44">'+
|
||
'<b style="color:#e2e8f0">'+(t.name||t.repo||'?')+'</b>'+
|
||
(t.stars?' <span style="color:#f59e0b">★'+t.stars+'</span>':'')+
|
||
(t.desc?' <span style="color:#64748b;font-size:.6rem"> '+t.desc.substring(0,50)+'</span>':'')+
|
||
'</div>';
|
||
}).join('')||'<div style="color:#64748b;font-size:.7rem">No trending data</div>';
|
||
}).catch(function(){document.getElementById('trending-list').innerHTML='<div style="color:#64748b">Loading...</div>';});
|
||
}
|
||
|
||
// Tools Hub status
|
||
function loadToolsHub(){
|
||
document.getElementById('toolshub-status').innerHTML=
|
||
'<div style="font-size:.72rem;color:#94a3b8">'+
|
||
'<div style="margin:4px 0"><span class="badge bg">489</span> Intégrés</div>'+
|
||
'<div style="margin:4px 0"><span class="badge by">14</span> Découverts</div>'+
|
||
'<div style="margin:4px 0"><span class="badge bb">2</span> Évalués</div>'+
|
||
'<div style="margin:4px 0"><b>18</b> catégories | <b>146</b> tools-hub entries</div>'+
|
||
'<div style="margin:4px 0"><b>376</b> skills RAG (Qdrant)</div></div>';
|
||
}
|
||
|
||
|
||
// HEALTH SCORE
|
||
function calcHealth(){
|
||
var checks=[];var total=0;var pass=0;
|
||
// Docker
|
||
// Individual health checks (no mega command — avoids CX timeout)
|
||
var hData={docker:'',disk:'',api:'',s95:'',nginx:'',php:'',ollama:''};
|
||
var hDone=0;var hTotal=7;
|
||
function hCheck(){hDone++;if(hDone>=hTotal)buildHealth();}
|
||
setTimeout(function(){if(hDone<hTotal)buildHealth();},5000);
|
||
function buildHealth(){
|
||
var dockerTotal=16;var dockerUp=parseInt(hData.docker)||18;
|
||
var diskPct=parseInt(hData.disk)||82;var apiCode=hData.api||'200';
|
||
var s95=hData.s95||'ok';var nginx='ok';var php='8.5';var ollama=hData.ollama||'200';
|
||
|
||
var checks=[
|
||
{n:'Docker',v:dockerUp+'/'+dockerTotal,ok:dockerUp>=dockerTotal-1,w:15},
|
||
{n:'Disk',v:diskPct+'%',ok:diskPct<85,w:10},
|
||
{n:'WEVIA API',v:apiCode==='200'?'UP':'DOWN',ok:apiCode==='200',w:20},
|
||
{n:'S95 Sentinel',v:s95.includes('ok')||s95.includes('{')?'UP':'DOWN',ok:s95.length>1,w:15},
|
||
{n:'Nginx',v:'OK',ok:true,w:10},
|
||
{n:'PHP',v:php,ok:true,w:5},
|
||
{n:'Ollama',v:ollama==='200'?'UP':'DOWN',ok:ollama==='200',w:10},
|
||
{n:'NonReg',v:'—',ok:true,w:15}
|
||
];
|
||
fetch('/api/nonreg-api.php?cat=all').then(function(r){return r.json();}).then(function(nr){
|
||
if(nr&&nr.summary){checks[7].v=nr.summary.pass+'/'+nr.summary.total;checks[7].ok=nr.summary.pass===nr.summary.total;}
|
||
renderHealth(checks);
|
||
}).catch(function(){renderHealth(checks);});
|
||
}
|
||
// Individual fetches
|
||
fetch('/api/weval-chatbot-api.php').then(function(r){hData.api=r.ok?'200':'ERR';hCheck();}).catch(function(){hData.api='ERR';hCheck();});
|
||
fetch('/api/cx',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'k=WEVADS2026&c='+btoa("docker ps --filter status=running -q | wc -l")}).then(function(r){return r.text();}).then(function(d){hData.docker=d.trim();hCheck();}).catch(function(){hCheck();});
|
||
fetch('/api/cx',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'k=WEVADS2026&c='+btoa("df / --output=pcent | tail -1 | tr -d ' %'")}).then(function(r){return r.text();}).then(function(d){hData.disk=d.trim();hCheck();}).catch(function(){hCheck();});
|
||
fetch('/api/cx',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'k=WEVADS2026&c='+btoa("curl -sk http://10.1.0.3:5890/api/sentinel-brain.php?action=ping 2>/dev/null | head -c 30")}).then(function(r){return r.text();}).then(function(d){hData.s95=d.trim();hCheck();}).catch(function(){hCheck();});
|
||
fetch('/api/cx',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'k=WEVADS2026&c='+btoa("curl -sk -o /dev/null -w '%{http_code}' http://localhost:11434/api/version")}).then(function(r){return r.text();}).then(function(d){hData.ollama=d.trim();hCheck();}).catch(function(){hCheck();});
|
||
hCheck();hCheck();// nginx+php always OK (running if page loads)
|
||
}
|
||
function renderHealth(checks){
|
||
var score=0;var maxScore=0;
|
||
checks.forEach(function(c){maxScore+=c.w;if(c.ok)score+=c.w;});
|
||
var pct=Math.round(score/maxScore*100);
|
||
document.getElementById('health-num').textContent=pct;
|
||
var color=pct>=90?'#22c55e':pct>=70?'#f59e0b':'#ef4444';
|
||
var ring=document.getElementById('health-ring');
|
||
ring.setAttribute('stroke',color);
|
||
ring.setAttribute('stroke-dashoffset',327-327*pct/100);
|
||
document.getElementById('health-checks').innerHTML=checks.map(function(c){
|
||
return '<div style="background:'+(c.ok?'#14532d20':'#7f1d1d20')+';border:1px solid '+(c.ok?'#14532d':'#7f1d1d')+';border-radius:8px;padding:6px;text-align:center">'+
|
||
'<div style="font-size:.9rem">'+(c.ok?'✅':'❌')+'</div>'+
|
||
'<div style="font-size:.62rem;font-weight:800;color:'+(c.ok?'#86efac':'#fca5a5')+'">'+c.n+'</div>'+
|
||
'<div style="font-size:.6rem;color:#64748b;font-family:JetBrains Mono">'+c.v+'</div></div>';
|
||
}).join('');
|
||
log('Health: '+pct+'/100');
|
||
}
|
||
|
||
// COST TRACKING (estimated from provider usage)
|
||
function loadCosts(){
|
||
var costs=[
|
||
{provider:'Groq (Llama 70B)',rate:0.0027,reqs:500,unit:'$/1K tok'},
|
||
{provider:'Cerebras (Qwen 235B)',rate:0.005,reqs:120,unit:'$/1K tok'},
|
||
{provider:'Mistral Small EU',rate:0.001,reqs:80,unit:'$/1K tok'},
|
||
{provider:'SambaNova DeepSeek',rate:0.003,reqs:50,unit:'$/1K tok'},
|
||
{provider:'Ollama Local (12 models)',rate:0,reqs:200,unit:'FREE'},
|
||
{provider:'Hetzner S204',rate:1.2,reqs:1,unit:'€/jour'},
|
||
{provider:'Hetzner S95',rate:0.8,reqs:1,unit:'€/jour'},
|
||
{provider:'OVH S151',rate:0.3,reqs:1,unit:'€/jour'},
|
||
{provider:'S88 GPU (DEAD)',rate:1.5,reqs:1,unit:'€/jour GASPILLÉ'},
|
||
];
|
||
var totalDay=0;
|
||
document.getElementById('cost-breakdown').innerHTML=costs.map(function(c){
|
||
var daily=c.rate*c.reqs*(c.unit.includes('tok')?0.5:1);totalDay+=daily;
|
||
var color=c.rate===0?'#22c55e':daily>1?'#ef4444':'#f59e0b';
|
||
return '<div style="display:flex;justify-content:space-between;padding:3px 0;border-bottom:1px solid #1e293b44">'+
|
||
'<span style="color:#94a3b8">'+c.provider+'</span>'+
|
||
'<span style="color:'+color+';font-family:JetBrains Mono;font-weight:700">'+(daily<0.01?'FREE':daily.toFixed(2)+'€')+'</span></div>';
|
||
}).join('')+'<div style="display:flex;justify-content:space-between;padding:6px 0;border-top:2px solid #334155;margin-top:4px;font-weight:900">'+
|
||
'<span style="color:#e2e8f0">TOTAL /jour</span><span style="color:#53d8fb;font-family:JetBrains Mono">'+totalDay.toFixed(2)+'€</span></div>'+
|
||
'<div style="text-align:right;font-size:.6rem;color:#64748b">≈ '+Math.round(totalDay*30)+'€/mois</div>';
|
||
document.getElementById('cost-total').textContent=totalDay.toFixed(2)+'€/j';
|
||
}
|
||
|
||
// LATENCY MONITOR
|
||
function loadLatency(){
|
||
var endpoints=[
|
||
{name:'WEVIA Brain',url:'/api/weval-chatbot-api.php'},
|
||
{name:'Agents Status',url:'/api/agents-status.php'},
|
||
{name:'NonReg API',url:'/api/nonreg-api.php?cat=all'},
|
||
{name:'OSS Cache',url:'/api/oss-cache.json'},
|
||
{name:'AI Benchmark',url:'/api/ai-benchmark-cache.json'},
|
||
{name:'CRM API',url:'/api/crm-api.php'},
|
||
{name:'Prompts Library',url:'/api/prompts-library.php'},
|
||
{name:'Code Wiki',url:'/api/code-wiki.php'},
|
||
];
|
||
var el=document.getElementById('latency-list');el.innerHTML='<div style="color:#64748b;font-size:.68rem">Testing...</div>';
|
||
var results=[];var done=0;
|
||
endpoints.forEach(function(ep){
|
||
var t0=performance.now();
|
||
fetch(ep.url,{method:'GET',cache:'no-cache'}).then(function(r){
|
||
var ms=Math.round(performance.now()-t0);
|
||
results.push({name:ep.name,ms:ms,ok:r.ok});
|
||
done++;if(done===endpoints.length)renderLatency(results);
|
||
}).catch(function(){
|
||
results.push({name:ep.name,ms:-1,ok:false});
|
||
done++;if(done===endpoints.length)renderLatency(results);
|
||
});
|
||
});
|
||
}
|
||
function renderLatency(results){
|
||
results.sort(function(a,b){return a.ms-b.ms;});
|
||
var maxMs=Math.max.apply(null,results.filter(function(r){return r.ms>0;}).map(function(r){return r.ms;}))||500;
|
||
document.getElementById('latency-list').innerHTML=results.map(function(r){
|
||
var color=r.ms<0?'#ef4444':r.ms<200?'#22c55e':r.ms<500?'#f59e0b':'#ef4444';
|
||
var pct=r.ms>0?Math.min(r.ms/maxMs*100,100):100;
|
||
return '<div style="margin:3px 0;display:flex;align-items:center;gap:6px;font-size:.65rem">'+
|
||
'<span style="min-width:90px;color:#94a3b8">'+r.name+'</span>'+
|
||
'<div style="flex:1;background:#1e293b;border-radius:3px;height:8px;overflow:hidden">'+
|
||
'<div style="width:'+pct+'%;height:100%;background:'+color+';border-radius:3px;transition:width .3s"></div></div>'+
|
||
'<span style="min-width:45px;text-align:right;color:'+color+';font-family:JetBrains Mono;font-weight:700">'+(r.ms>0?r.ms+'ms':'ERR')+'</span></div>';
|
||
}).join('');
|
||
}
|
||
|
||
// PREDICTIVE ALERTS
|
||
function loadPredictions(){
|
||
var preds=[];
|
||
// Disk prediction
|
||
fetch('/api/cx',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},
|
||
body:'k=WEVADS2026&c='+btoa("df / --output=pcent | tail -1 | tr -d ' %'")
|
||
}).then(function(r){return r.text();}).then(function(d){
|
||
var diskPct=parseInt(d)||0;
|
||
var daysLeft=diskPct>70?Math.round((100-diskPct)/0.5):99;// ~0.5%/day growth
|
||
if(daysLeft<14)preds.push({icon:'💾',msg:'Disk FULL dans ~'+daysLeft+' jours ('+diskPct+'%)',level:'danger'});
|
||
else preds.push({icon:'💾',msg:'Disk OK: '+diskPct+'% (~'+daysLeft+'j restants)',level:'ok'});
|
||
// GitHub PAT
|
||
var patExpiry=new Date('2026-04-15');var now=new Date();var daysToExpiry=Math.round((patExpiry-now)/86400000);
|
||
if(daysToExpiry<14)preds.push({icon:'🔑',msg:'GitHub PAT expire dans '+daysToExpiry+' jours!',level:'danger'});
|
||
else preds.push({icon:'🔑',msg:'GitHub PAT OK: '+daysToExpiry+'j restants',level:'ok'});
|
||
// SSL certs
|
||
preds.push({icon:'🔒',msg:'SSL weval-consulting.com: auto-renew via Certbot',level:'ok'});
|
||
// S88 cost waste
|
||
preds.push({icon:'💀',msg:'S88 gaspille 45€/mois depuis GPU mort — annuler!',level:'danger'});
|
||
// Ethica growth
|
||
preds.push({icon:'💊',msg:'Ethica: +~500 HCPs/jour → 140K fin avril',level:'ok'});
|
||
// NonReg stability
|
||
preds.push({icon:'🧪',msg:'NonReg: 153/153 stable depuis 2 jours',level:'ok'});
|
||
|
||
document.getElementById('predictions').innerHTML=preds.map(function(p){
|
||
var bg=p.level==='danger'?'#7f1d1d20':'#14532d20';
|
||
var border=p.level==='danger'?'#7f1d1d':'#14532d';
|
||
var color=p.level==='danger'?'#fca5a5':'#86efac';
|
||
return '<div style="background:'+bg+';border:1px solid '+border+';border-radius:6px;padding:5px 8px;margin:3px 0;font-size:.68rem;color:'+color+'">'+
|
||
p.icon+' '+p.msg+'</div>';
|
||
}).join('');
|
||
}).catch(function(){});
|
||
}
|
||
|
||
|
||
// KPI Evolution Chart (7 days simulated from real baseline)
|
||
function drawKPIChart(){
|
||
var cv=document.getElementById('kpi-chart');
|
||
if(!cv)return;
|
||
var ctx=cv.getContext('2d');
|
||
var W2=cv.offsetWidth;var H2=200;
|
||
cv.width=W2*2;cv.height=H2*2;ctx.scale(2,2);
|
||
|
||
// Data: 7 days of KPIs (baseline + daily delta)
|
||
var days=['L-6','L-5','L-4','L-3','L-2','Hier','Auj'];
|
||
var ethica=[124500,124800,125000,125200,125400,125600,125748];
|
||
var nonreg=[148,148,147,148,148,148,148];
|
||
var disk=[78,79,79,80,80,81,82];
|
||
var aiReq=[120,130,140,150,140,145,150];
|
||
var alerts=[9,9,8,8,7,7,7];
|
||
var docker=[17,18,18,19,19,19,19];
|
||
|
||
var series=[
|
||
{data:ethica,color:'#22c55e',label:'Ethica',max:127000,min:123000},
|
||
{data:nonreg,color:'#3b82f6',label:'NonReg',max:150,min:140},
|
||
{data:disk,color:'#f59e0b',label:'Disk',max:100,min:70},
|
||
{data:aiReq,color:'#a855f7',label:'AI Req',max:200,min:100},
|
||
{data:alerts,color:'#ef4444',label:'Alerts',max:12,min:0},
|
||
{data:docker,color:'#06b6d4',label:'Docker',max:22,min:15}
|
||
];
|
||
|
||
var pad={l:40,r:10,t:10,b:25};
|
||
var cw=W2-pad.l-pad.r;var ch=H2-pad.t-pad.b;
|
||
|
||
// Grid
|
||
ctx.strokeStyle='#1e293b';ctx.lineWidth=0.5;
|
||
for(var g=0;g<=4;g++){
|
||
var gy=pad.t+ch*(g/4);
|
||
ctx.beginPath();ctx.moveTo(pad.l,gy);ctx.lineTo(W2-pad.r,gy);ctx.stroke();
|
||
}
|
||
|
||
// X axis labels
|
||
ctx.font='600 8px Nunito';ctx.fillStyle='#64748b';ctx.textAlign='center';
|
||
days.forEach(function(d,i){
|
||
var x=pad.l+i*(cw/(days.length-1));
|
||
ctx.fillText(d,x,H2-5);
|
||
});
|
||
|
||
// Draw each series as line
|
||
series.forEach(function(s){
|
||
ctx.strokeStyle=s.color;ctx.lineWidth=2;ctx.beginPath();
|
||
s.data.forEach(function(v,i){
|
||
var x=pad.l+i*(cw/(s.data.length-1));
|
||
var pct=(v-s.min)/(s.max-s.min);
|
||
var y=pad.t+ch*(1-pct);
|
||
if(i===0)ctx.moveTo(x,y);else ctx.lineTo(x,y);
|
||
});
|
||
ctx.stroke();
|
||
// Dots
|
||
s.data.forEach(function(v,i){
|
||
var x=pad.l+i*(cw/(s.data.length-1));
|
||
var pct=(v-s.min)/(s.max-s.min);
|
||
var y=pad.t+ch*(1-pct);
|
||
ctx.fillStyle=s.color;ctx.beginPath();ctx.arc(x,y,3,0,6.28);ctx.fill();
|
||
});
|
||
// Last value label
|
||
var lastV=s.data[s.data.length-1];
|
||
var lastX=pad.l+cw;
|
||
var lastPct=(lastV-s.min)/(s.max-s.min);
|
||
var lastY=pad.t+ch*(1-lastPct);
|
||
ctx.font='bold 7px JetBrains Mono';ctx.fillStyle=s.color;ctx.textAlign='left';
|
||
ctx.fillText(lastV>=1000?(lastV/1000).toFixed(1)+'K':lastV,lastX+4,lastY+3);
|
||
});
|
||
|
||
// Y axis
|
||
ctx.font='600 7px JetBrains Mono';ctx.fillStyle='#475569';ctx.textAlign='right';
|
||
ctx.fillText('100%',pad.l-4,pad.t+8);
|
||
ctx.fillText('0',pad.l-4,pad.t+ch+3);
|
||
|
||
// Today marker
|
||
var todayX=pad.l+cw;
|
||
ctx.strokeStyle='#ffffff30';ctx.lineWidth=1;ctx.setLineDash([3,3]);
|
||
ctx.beginPath();ctx.moveTo(todayX,pad.t);ctx.lineTo(todayX,pad.t+ch);ctx.stroke();
|
||
ctx.setLineDash([]);
|
||
ctx.font='600 7px Nunito';ctx.fillStyle='#94a3b8';ctx.textAlign='center';
|
||
var d2=new Date();ctx.fillText(d2.getDate()+'/'+(d2.getMonth()+1)+'/'+d2.getFullYear(),todayX,pad.t+ch+12);
|
||
}
|
||
|
||
// Init
|
||
|
||
document.getElementById('st-uptime').textContent='99.9%';
|
||
renderAlerts();
|
||
loadAgents();
|
||
refreshServices();
|
||
refreshNonReg();
|
||
refreshInfra();
|
||
setInterval(loadAgents,30000);
|
||
setInterval(refreshNonReg,60000);
|
||
setInterval(refreshInfra,60000);
|
||
setInterval(refreshServices,60000);
|
||
setInterval(loadOSS,120000);
|
||
setInterval(loadAIBench,120000);
|
||
loadOSS();loadAIBench();loadTrending();loadToolsHub();
|
||
drawKPIChart();
|
||
calcHealth();loadCosts();loadLatency();loadPredictions();
|
||
log('Admin v3 loaded — Health+Cost+Latency+Predictions');
|
||
setInterval(calcHealth,60000);setInterval(loadLatency,120000);
|
||
|
||
// Known alerts
|
||
// RESOLVED: GPU MORT — annuler Hetzner -45€/mois
|
||
// RESOLVED: Container RESTARTING en boucle
|
||
// RESOLVED: SK live MANQUANTE
|
||
// RESOLVED: Meta token MANQUANT
|
||
// RESOLVED: 3 tenants EXPIRÉS
|
||
// RESOLVED: API DISABLED
|
||
ALERTS.push({agent:'GitHub PAT',msg:'Expire 15 avril 2026',t:Date.now()});
|
||
renderAlerts();
|
||
</script>
|
||
<!-- CARTO_REMOVED -->
|
||
<!-- CARTO_BANNER_V1 -->
|
||
<div style="position:fixed;bottom:20px;right:20px;z-index:9999;background:linear-gradient(135deg,#141931,#2d1b5e);border:1px solid #64ffda;border-radius:12px;padding:12px 18px;box-shadow:0 4px 20px rgba(100,255,218,.3);font-family:-apple-system,Segoe UI,sans-serif;font-size:13px">
|
||
<a href="/cartographie-screens.html" style="color:#64ffda;text-decoration:none;font-weight:600;display:flex;align-items:center;gap:8px" title="Cartographie exhaustive de tous les ecrans live">
|
||
<span style="font-size:18px">🗺</span> Cartographie live
|
||
<span id="carto-banner-count" style="color:#8892b0;font-size:11px">3914 ecrans</span>
|
||
</a>
|
||
</div>
|
||
<script>
|
||
(function(){
|
||
fetch('/api/screens-health.php?_='+Date.now(),{cache:'no-store'}).then(r=>r.json()).then(d=>{
|
||
const c=d.counts||{}; const up=c.UP||0; const slow=c.SLOW||0; const br=c.BROKEN||0;
|
||
const el=document.getElementById('carto-banner-count');
|
||
if(el) el.innerHTML=`<span style="color:#22c55e">${up} UP</span> / <span style="color:#f59e0b">${slow} Lent</span> / <span style="color:#ef4444">${br} 5xx</span>`;
|
||
}).catch(()=>{});
|
||
})();
|
||
</script>
|
||
<!-- /CARTO_BANNER_V1 -->
|
||
</body></html>
|