409 lines
22 KiB
HTML
Executable File
409 lines
22 KiB
HTML
Executable File
<?php include_once("/opt/wevads-arsenal/public/api/wevads-metrics.php"); ?>
|
||
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>WEVADS - SENTINEL V5 — WEVADS Command Control</title>
|
||
<style>
|
||
:root{--bg:#060a14;--s:#0c1220;--s2:#111827;--b:#1e293b;--t:#e2e8f0;--d:#64748b;--cy:#22d3ee;--gn:#34d399;--am:#fbbf24;--rd:#f87171;--pu:#a78bfa;--bl:#60a5fa;--m:'JetBrains Mono',monospace;--f:'DM Sans',sans-serif}
|
||
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
body{background:#060a14;color:#e2e8f0;font-family:'Segoe UI',system-ui,sans-serif;overflow-x:hidden}
|
||
.topbar{background:linear-gradient(135deg,#0a0e1a 0%,#1a1040 100%);padding:12px 24px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #1e293b;position:sticky;top:0;z-index:100}
|
||
.topbar h1{font-size:18px;background:linear-gradient(90deg,#818cf8,#a78bfa,#c084fc);-webkit-background-clip:text;-webkit-text-fill-color:transparent;font-weight:700;letter-spacing:1px}
|
||
.overall{padding:6px 20px;border-radius:20px;font-weight:700;font-size:13px;text-transform:uppercase;letter-spacing:2px}
|
||
.overall.HEALTHY{background:#059669;color:#fff;box-shadow:0 0 20px #05966966}
|
||
.overall.WARNING{background:#d97706;color:#fff;box-shadow:0 0 20px #d9770666}
|
||
.overall.CRITICAL{background:#dc2626;color:#fff;box-shadow:0 0 20px #dc262666;animation:pulse-red 1s infinite}
|
||
@keyframes pulse-red{0%,100%{opacity:1}50%{opacity:.7}}
|
||
.live-dot{width:8px;height:8px;border-radius:50%;background:#22c55e;display:inline-block;margin-right:6px;animation:blink 2s infinite}
|
||
@keyframes blink{0%,100%{opacity:1}50%{opacity:.3}}
|
||
.meta{color:#64748b;font-size:12px}
|
||
.grid{display:grid;gap:16px;padding:16px;grid-template-columns:repeat(auto-fit,minmax(380px,1fr))}
|
||
.card{background:linear-gradient(135deg,#0f172a,#1e1b4b);border:1px solid #1e293b;border-radius:12px;padding:16px;transition:all .3s}
|
||
.card:hover{border-color:#6366f1;box-shadow:0 0 25px #6366f133}
|
||
.card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
|
||
.card-title{font-size:14px;font-weight:600;color:#a5b4fc}
|
||
.badge{padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600}
|
||
.badge.ok{background:#05966622;color:#34d399;border:1px solid #05966644}
|
||
.badge.warn{background:#d9770622;color:#fbbf24;border:1px solid #d9770644}
|
||
.badge.critical{background:#dc262622;color:#f87171;border:1px solid #dc262644}
|
||
.badge.error{background:#dc262622;color:#f87171;border:1px solid #dc262644}
|
||
.kpi-row{display:flex;gap:10px;flex-wrap:wrap;margin:8px 0}
|
||
.kpi{background:#0a0e1a;padding:10px 14px;border-radius:8px;border:1px solid #1e293b;flex:1;min-width:80px;text-align:center}
|
||
.kpi .val{font-size:20px;font-weight:700;color:#f1f5f9}
|
||
.kpi .lbl{font-size:10px;color:#64748b;text-transform:uppercase;margin-top:2px}
|
||
.api-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:6px;margin-top:8px}
|
||
.api-item{background:#0a0e1a;padding:6px 10px;border-radius:6px;border:1px solid #1e293b;font-size:11px;display:flex;align-items:center;gap:6px}
|
||
.api-item .dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}
|
||
.api-item .dot.ok{background:#22c55e}
|
||
.api-item .dot.error{background:#ef4444}
|
||
.db-table{width:100%;border-collapse:collapse;margin-top:8px}
|
||
.db-table td{padding:6px 10px;border-bottom:1px solid #1e293b;font-size:12px}
|
||
.db-table td:first-child{color:#94a3b8}
|
||
.db-table td:last-child{text-align:right;font-weight:600;color:#c4b5fd}
|
||
.screen-list{max-height:350px;overflow-y:auto;margin-top:8px}
|
||
.screen-item{display:flex;align-items:center;justify-content:space-between;padding:5px 8px;border-bottom:1px solid #1e293b11;font-size:11px}
|
||
.screen-item:hover{background:#1e293b33}
|
||
.screen-item .name{color:#cbd5e1}
|
||
.screen-item .info{color:#64748b;font-size:10px}
|
||
.screen-item .dot{width:6px;height:6px;border-radius:50%;flex-shrink:0;margin-right:6px}
|
||
.screen-item .dot.ok{background:#22c55e}
|
||
.screen-item .dot.disconnected{background:#f59e0b}
|
||
.screen-item .dot.critical{background:#ef4444}
|
||
.screen-item .dot.warn{background:#f59e0b}
|
||
.infra-row{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-top:8px}
|
||
.infra-item{background:#0a0e1a;padding:10px;border-radius:8px;border:1px solid #1e293b}
|
||
.infra-item .lbl{font-size:10px;color:#64748b;text-transform:uppercase}
|
||
.infra-item .val{font-size:14px;font-weight:600;color:#e2e8f0;margin-top:2px}
|
||
.btn-row{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}
|
||
.btn{padding:6px 16px;border-radius:6px;border:1px solid #4f46e5;background:#4f46e522;color:#a5b4fc;cursor:pointer;font-size:12px;font-weight:500;transition:all .2s}
|
||
.btn:hover{background:#4f46e5;color:#fff}
|
||
.btn.danger{border-color:#dc2626;color:#f87171;background:#dc262622}
|
||
.btn.danger:hover{background:#dc2626;color:#fff}
|
||
.btn.success{border-color:#059669;color:#34d399;background:#05966922}
|
||
.btn.success:hover{background:#059669;color:#fff}
|
||
.progress-bar{height:6px;background:#1e293b;border-radius:3px;overflow:hidden;margin-top:6px}
|
||
.progress-fill{height:100%;border-radius:3px;transition:width .5s}
|
||
.log-area{background:#0a0e1a;border:1px solid #1e293b;border-radius:8px;padding:10px;margin-top:8px;max-height:200px;overflow-y:auto;font-family:'Fira Code',monospace;font-size:11px;color:#94a3b8;line-height:1.6}
|
||
.log-line{border-bottom:1px solid #1e293b11;padding:2px 0}
|
||
.log-line .ts{color:#6366f1}
|
||
.log-line .ok{color:#22c55e}
|
||
.log-line .err{color:#ef4444}
|
||
.log-line .warn{color:#f59e0b}
|
||
.full-width{grid-column:1/-1}
|
||
::-webkit-scrollbar{width:6px}
|
||
::-webkit-scrollbar-track{background:#0a0e1a}
|
||
::-webkit-scrollbar-thumb{background:#4f46e5;border-radius:3px}
|
||
.tab-bar{display:flex;gap:4px;margin-bottom:12px;border-bottom:1px solid #1e293b;padding-bottom:8px}
|
||
.tab{padding:6px 16px;border-radius:6px 6px 0 0;cursor:pointer;font-size:12px;color:#64748b;transition:all .2s}
|
||
.tab.active{background:#4f46e533;color:#a5b4fc;border-bottom:2px solid #6366f1}
|
||
.tab:hover{color:#e2e8f0}
|
||
.wv-status{position:fixed;top:12px;right:140px;z-index:9998;background:rgba(52,211,153,.15);border:1px solid #34d399;border-radius:12px;padding:3px 10px;color:#34d399;font-size:10px;font-weight:700;font-family:'JetBrains Mono',monospace}
|
||
</style>
|
||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=DM+Sans:wght@400;600;700&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="wevads-global.css?v1770777318">
|
||
</head>
|
||
<body>
|
||
|
||
|
||
<div class="topbar">
|
||
<div style="display:flex;align-items:center;gap:16px">
|
||
<h1>🛡️ SENTINEL V5</h1>
|
||
<span class="meta"><span class="live-dot"></span>LIVE MONITORING</span>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:16px">
|
||
<span id="elapsed" class="meta"></span>
|
||
<span id="timestamp" class="meta"></span>
|
||
<span id="overall" class="overall HEALTHY">CHECKING...</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid">
|
||
<!-- CARD 1: SCREENS -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">📺 SCREENS</span>
|
||
<span id="screens-badge" class="badge ok">OK</span>
|
||
</div>
|
||
<div class="kpi-row">
|
||
<div class="kpi"><div class="val" id="scr-total">-</div><div class="lbl">Total</div></div>
|
||
<div class="kpi"><div class="val" id="scr-connected">-</div><div class="lbl">API Connected</div></div>
|
||
<div class="kpi"><div class="val" id="scr-disconnected">-</div><div class="lbl">Disconnected</div></div>
|
||
<div class="kpi"><div class="val" id="scr-php">-</div><div class="lbl">Raw PHP</div></div>
|
||
</div>
|
||
<div class="progress-bar"><div class="progress-fill" id="scr-progress" style="width:0%;background:linear-gradient(90deg,#6366f1,#22c55e)"></div></div>
|
||
<div class="btn-row">
|
||
<button class="btn" onclick="loadScreens()">📋 Liste complète</button>
|
||
<button class="btn success" onclick="runGuardian()">🔍 Guardian Scan</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CARD 2: CRITICAL APIs -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">⚡ APIs CRITIQUES</span>
|
||
<span id="apis-badge" class="badge ok">OK</span>
|
||
</div>
|
||
<div class="kpi-row">
|
||
<div class="kpi"><div class="val" id="api-total">-</div><div class="lbl">Total APIs</div></div>
|
||
<div class="kpi"><div class="val" id="api-ok">-</div><div class="lbl">Critical OK</div></div>
|
||
<div class="kpi"><div class="val" id="api-err">-</div><div class="lbl">Errors</div></div>
|
||
</div>
|
||
<div id="api-grid" class="api-grid"></div>
|
||
</div>
|
||
|
||
<!-- CARD 3: DATABASE -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">🗄️ DATABASE</span>
|
||
<span id="db-badge" class="badge ok">OK</span>
|
||
</div>
|
||
<div class="kpi-row">
|
||
<div class="kpi"><div class="val" id="db-tables">-</div><div class="lbl">Tables</div></div>
|
||
<div class="kpi"><div class="val" id="db-size">-</div><div class="lbl">Size</div></div>
|
||
</div>
|
||
<table class="db-table" id="db-data"></table>
|
||
</div>
|
||
|
||
<!-- CARD 4: INFRASTRUCTURE -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">🏗️ INFRASTRUCTURE</span>
|
||
<span id="infra-badge" class="badge ok">OK</span>
|
||
</div>
|
||
<div class="infra-row">
|
||
<div class="infra-item"><div class="lbl">Hetzner</div><div class="val" id="inf-hetzner">-</div></div>
|
||
<div class="infra-item"><div class="lbl">OVH Tracking</div><div class="val" id="inf-ovh">-</div></div>
|
||
<div class="infra-item"><div class="lbl">Disque</div><div class="val" id="inf-disk">-</div></div>
|
||
<div class="infra-item"><div class="lbl">Load</div><div class="val" id="inf-load">-</div></div>
|
||
<div class="infra-item"><div class="lbl">Mémoire</div><div class="val" id="inf-mem">-</div></div>
|
||
<div class="infra-item"><div class="lbl">Ports</div><div class="val">5821 / 5890</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CARD 5: VAULT -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">🔒 VAULT / PROTECTION</span>
|
||
<span id="vault-badge" class="badge ok">OK</span>
|
||
</div>
|
||
<div class="kpi-row">
|
||
<div class="kpi"><div class="val" id="vault-gold">-</div><div class="lbl">Gold Files</div></div>
|
||
<div class="kpi"><div class="val" id="vault-check">-</div><div class="lbl">Checksums</div></div>
|
||
</div>
|
||
<div class="btn-row">
|
||
<button class="btn" onclick="verifyVault()">🔐 Vérifier intégrité</button>
|
||
<button class="btn success" onclick="goldSync()">📦 Sync Gold</button>
|
||
</div>
|
||
<div id="vault-log" class="log-area" style="display:none"></div>
|
||
</div>
|
||
|
||
<!-- CARD 6: ADS PLATFORMS -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">📢 ADS & REVENUE</span>
|
||
<span id="ads-badge" class="badge ok">OK</span>
|
||
</div>
|
||
<div class="kpi-row" id="ads-kpis"></div>
|
||
<div class="btn-row">
|
||
<button class="btn" onclick="window.open('ads-commander.html','_self')">📊 Ads Commander</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CARD 7: FULL SCREEN LIST - full width -->
|
||
<div class="card full-width" id="screen-list-card" style="display:none">
|
||
<div class="card-header">
|
||
<span class="card-title">📺 ALL SCREENS — Point par Point</span>
|
||
<button class="btn" onclick="document.getElementById('screen-list-card').style.display='none'">✕ Fermer</button>
|
||
</div>
|
||
<div class="tab-bar">
|
||
<div class="tab active" onclick="filterScreens('all',this)">Tous</div>
|
||
<div class="tab" onclick="filterScreens('ok',this)">✅ OK</div>
|
||
<div class="tab" onclick="filterScreens('disconnected',this)">⚠️ Disconnected</div>
|
||
<div class="tab" onclick="filterScreens('critical',this)">❌ Critical</div>
|
||
</div>
|
||
<div id="screen-list" class="screen-list"></div>
|
||
</div>
|
||
|
||
<!-- CARD 8: LIVE LOG - full width -->
|
||
<div class="card full-width">
|
||
<div class="card-header">
|
||
<span class="card-title">📜 SENTINEL LOG</span>
|
||
<span class="meta" id="log-count">0 events</span>
|
||
</div>
|
||
<div id="sentinel-log" class="log-area"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const API = '/api/sentinel-v5.php';
|
||
let allScreens = [];
|
||
let logLines = [];
|
||
|
||
function log(msg, level) {
|
||
const ts = new Date().toLocaleTimeString();
|
||
const cls = level || 'ok';
|
||
logLines.unshift({ts, msg, cls});
|
||
if(logLines.length > 100) logLines.pop();
|
||
renderLog();
|
||
}
|
||
|
||
function renderLog() {
|
||
const el = document.getElementById('sentinel-log');
|
||
el.innerHTML = logLines.map(l =>
|
||
'<div class="log-line"><span class="ts">[' + l.ts + ']</span> <span class="' + l.cls + '">' + l.msg + '</span></div>'
|
||
).join('');
|
||
document.getElementById('log-count').textContent = logLines.length + ' events';
|
||
}
|
||
|
||
async function fullCheck() {
|
||
try {
|
||
log('Starting full system check...', 'ok');
|
||
const r = await fetch(API + '?action=full_check');
|
||
const d = await r.json();
|
||
|
||
// Overall
|
||
const ov = document.getElementById('overall');
|
||
ov.textContent = d.overall;
|
||
ov.className = 'overall ' + d.overall;
|
||
document.getElementById('elapsed').textContent = d.elapsed_ms + 'ms';
|
||
document.getElementById('timestamp').textContent = d.timestamp;
|
||
|
||
// Screens
|
||
const sc = d.checks.screens;
|
||
document.getElementById('screens-badge').className = 'badge ' + sc.status;
|
||
document.getElementById('screens-badge').textContent = sc.status.toUpperCase();
|
||
document.getElementById('scr-total').textContent = sc.total;
|
||
document.getElementById('scr-connected').textContent = sc.api_connected;
|
||
document.getElementById('scr-disconnected').textContent = sc.disconnected;
|
||
document.getElementById('scr-php').textContent = sc.raw_php;
|
||
document.getElementById('scr-progress').style.width = Math.round(sc.api_connected/sc.total*100) + '%';
|
||
log('Screens: ' + sc.api_connected + '/' + sc.total + ' connected, ' + sc.raw_php + ' raw PHP', sc.raw_php > 0 ? 'err' : 'ok');
|
||
|
||
// APIs
|
||
const ap = d.checks.apis;
|
||
document.getElementById('apis-badge').className = 'badge ' + ap.status;
|
||
document.getElementById('apis-badge').textContent = ap.status.toUpperCase();
|
||
document.getElementById('api-total').textContent = ap.total;
|
||
document.getElementById('api-ok').textContent = ap.critical_ok;
|
||
document.getElementById('api-err').textContent = ap.critical_err;
|
||
document.getElementById('api-grid').innerHTML = ap.details.map(a =>
|
||
'<div class="api-item"><div class="dot ' + a.status + '"></div>' + a.api.replace('.php','') + '</div>'
|
||
).join('');
|
||
log('APIs: ' + ap.critical_ok + '/' + (ap.critical_ok+ap.critical_err) + ' critical OK', ap.critical_err > 0 ? 'err' : 'ok');
|
||
|
||
// Database
|
||
const db = d.checks.database;
|
||
document.getElementById('db-badge').className = 'badge ' + db.status;
|
||
document.getElementById('db-badge').textContent = db.status.toUpperCase();
|
||
document.getElementById('db-tables').textContent = db.tables;
|
||
document.getElementById('db-size').textContent = db.db_size;
|
||
const dbData = db.data || {};
|
||
document.getElementById('db-data').innerHTML = Object.entries(dbData).map(([k,v]) =>
|
||
'<tr><td>' + k.replace(/_/g,' ') + '</td><td>' + (typeof v==='number'?v.toLocaleString():v) + '</td></tr>'
|
||
).join('');
|
||
log('DB: ' + db.tables + ' tables, ' + db.db_size + ' — ' + (dbData.contacts||0).toLocaleString() + ' contacts', 'ok');
|
||
|
||
// Infrastructure
|
||
const inf = d.checks.infrastructure;
|
||
document.getElementById('infra-badge').className = 'badge ' + inf.status;
|
||
document.getElementById('infra-badge').textContent = inf.status.toUpperCase();
|
||
document.getElementById('inf-hetzner').textContent = inf.hetzner;
|
||
document.getElementById('inf-hetzner').style.color = '#22c55e';
|
||
document.getElementById('inf-ovh').textContent = inf.ovh_tracking;
|
||
document.getElementById('inf-ovh').style.color = inf.ovh_tracking === 'online' ? '#22c55e' : '#ef4444';
|
||
document.getElementById('inf-disk').textContent = inf.disk_usage;
|
||
document.getElementById('inf-load').textContent = inf.load;
|
||
document.getElementById('inf-mem').textContent = inf.memory;
|
||
log('Infra: Hetzner=' + inf.hetzner + ' OVH=' + inf.ovh_tracking + ' Disk=' + inf.disk_usage + ' Load=' + inf.load, inf.status === 'ok' ? 'ok' : 'warn');
|
||
|
||
// Vault
|
||
const vt = d.checks.vault;
|
||
document.getElementById('vault-badge').className = 'badge ' + vt.status;
|
||
document.getElementById('vault-badge').textContent = vt.status.toUpperCase();
|
||
document.getElementById('vault-gold').textContent = vt.gold_files;
|
||
document.getElementById('vault-check').textContent = '✅';
|
||
log('Vault: ' + vt.gold_files + ' gold files protected', 'ok');
|
||
|
||
// Ads
|
||
const ads = d.checks.ads;
|
||
document.getElementById('ads-badge').className = 'badge ' + ads.status;
|
||
document.getElementById('ads-badge').textContent = ads.status.toUpperCase();
|
||
document.getElementById('ads-kpis').innerHTML = '<div class="kpi"><div class="val">' + (ads.flux_api?'✅':'❌') + '</div><div class="lbl">Flux API</div></div>';
|
||
|
||
log('✅ Full check complete: ' + d.overall + ' in ' + d.elapsed_ms + 'ms', d.overall === 'HEALTHY' ? 'ok' : 'warn');
|
||
|
||
} catch(e) {
|
||
log('❌ Check failed: ' + e.message, 'err');
|
||
document.getElementById('overall').textContent = 'ERROR';
|
||
document.getElementById('overall').className = 'overall CRITICAL';
|
||
}
|
||
}
|
||
|
||
async function loadScreens() {
|
||
document.getElementById('screen-list-card').style.display = 'block';
|
||
log('Loading screen details...', 'ok');
|
||
try {
|
||
const r = await fetch(API + '?action=screens');
|
||
const d = await r.json();
|
||
allScreens = d.screens;
|
||
filterScreens('all', document.querySelector('.tab.active'));
|
||
log('Loaded ' + d.total + ' screens', 'ok');
|
||
} catch(e) {
|
||
log('Failed to load screens: ' + e.message, 'err');
|
||
}
|
||
}
|
||
|
||
function filterScreens(filter, tab) {
|
||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||
if(tab) tab.classList.add('active');
|
||
|
||
const filtered = filter === 'all' ? allScreens : allScreens.filter(s => s.status === filter);
|
||
const el = document.getElementById('screen-list');
|
||
el.innerHTML = filtered.map(s => {
|
||
const sz = s.size > 1024 ? Math.round(s.size/1024) + 'KB' : s.size + 'B';
|
||
const icons = [];
|
||
if(s.api) icons.push('🔗API');
|
||
if(s.theme) icons.push('🎨');
|
||
if(s.php) icons.push('⚠️PHP');
|
||
if(s.alerts > 3) icons.push('🔔' + s.alerts);
|
||
return '<div class="screen-item" onclick="window.open(\'' + s.name + '\',\'_blank\')" style="cursor:pointer">' +
|
||
'<div style="display:flex;align-items:center"><div class="dot ' + s.status + '"></div><span class="name">' + s.name + '</span></div>' +
|
||
'<div style="display:flex;align-items:center;gap:8px"><span class="info">' + icons.join(' ') + '</span><span class="info">' + sz + '</span></div></div>';
|
||
}).join('');
|
||
}
|
||
|
||
async function runGuardian() {
|
||
log('Running Guardian scan...', 'ok');
|
||
try {
|
||
const r = await fetch('/api/guardian-scan.php?action=scan');
|
||
const d = await r.json();
|
||
log('Guardian: ' + d.checked + ' files checked, ' + d.issues_count + ' issues, health=' + d.health, d.issues_count > 0 ? 'warn' : 'ok');
|
||
if(d.issues && d.issues.length > 0) {
|
||
d.issues.forEach(i => log(' Issue: ' + i.file + ' — ' + i.type + ' (' + i.severity + ')', 'warn'));
|
||
}
|
||
} catch(e) { log('Guardian error: ' + e.message, 'err'); }
|
||
}
|
||
|
||
async function verifyVault() {
|
||
const vl = document.getElementById('vault-log');
|
||
vl.style.display = 'block';
|
||
vl.innerHTML = '<div class="log-line"><span class="ts">[' + new Date().toLocaleTimeString() + ']</span> Verifying vault integrity...</div>';
|
||
log('Vault integrity check started...', 'ok');
|
||
try {
|
||
const r = await fetch('/api/arsenal-health.php?action=status');
|
||
const d = await r.json();
|
||
vl.innerHTML += '<div class="log-line"><span class="ok">Gold files: ' + d.gold_files + '</span></div>';
|
||
vl.innerHTML += '<div class="log-line"><span class="ok">HTML screens: ' + d.html_screens + '</span></div>';
|
||
vl.innerHTML += '<div class="log-line"><span class="ok">API endpoints: ' + d.api_endpoints + '</span></div>';
|
||
vl.innerHTML += '<div class="log-line"><span class="ok">DB size: ' + d.db_size + '</span></div>';
|
||
vl.innerHTML += '<div class="log-line"><span class="ok">✅ Vault integrity verified</span></div>';
|
||
log('Vault OK: ' + d.gold_files + ' gold, ' + d.html_screens + ' HTML, ' + d.api_endpoints + ' APIs', 'ok');
|
||
} catch(e) {
|
||
vl.innerHTML += '<div class="log-line"><span class="err">❌ ' + e.message + '</span></div>';
|
||
log('Vault check failed: ' + e.message, 'err');
|
||
}
|
||
}
|
||
|
||
async function goldSync() {
|
||
log('Gold sync initiated...', 'ok');
|
||
try {
|
||
const r = await fetch('/api/healing.php?action=run_check');
|
||
const d = await r.json();
|
||
log('Healing check: ' + JSON.stringify(d), 'ok');
|
||
} catch(e) { log('Sync error: ' + e.message, 'err'); }
|
||
}
|
||
|
||
// Auto-refresh
|
||
fullCheck();
|
||
setInterval(fullCheck, 15000);
|
||
</script>
|
||
<script src="arsenal-common.js?v1770778169">
|
||
<?php include("/opt/wevads-arsenal/public/universal-drill.html"); ?>
|
||
</body>
|
||
</html>
|
||
</script>
|