715 lines
37 KiB
PHP
Executable File
715 lines
37 KiB
PHP
Executable File
<?php
|
|
error_reporting(0);
|
|
ini_set("display_errors",0);
|
|
/**
|
|
* CEO PILOTAGE GLOBAL
|
|
* Vision complète + Contrôle + Winning Auto-Invalidation
|
|
*/
|
|
header('Content-Type: text/html; charset=utf-8');
|
|
|
|
$db = new PDO("pgsql:host=127.0.0.1;dbname=adx_system", "admin", "admin123");
|
|
|
|
// ==================== STATS GLOBALES ====================
|
|
$stats = [
|
|
// Volumes
|
|
'total_contacts' => $db->query("SELECT COUNT(*) FROM admin.send_contacts")->fetchColumn(),
|
|
'today_sent' => $db->query("SELECT COALESCE(SUM(mails_sent), 0) FROM admin.send_configs WHERE DATE(started_at) = CURRENT_DATE")->fetchColumn(),
|
|
|
|
// Accounts
|
|
'office_total' => $db->query("SELECT COUNT(*) FROM admin.office_accounts")->fetchColumn(),
|
|
'office_ready' => $db->query("SELECT COUNT(*) FROM admin.office_accounts WHERE status IN ('Configured', 'Active')")->fetchColumn(),
|
|
'office_blocked' => $db->query("SELECT COUNT(*) FROM admin.office_accounts WHERE blocked_status IS NOT NULL AND blocked_status != ''")->fetchColumn(),
|
|
|
|
// Servers
|
|
'servers_total' => $db->query("SELECT COUNT(*) FROM mta.servers WHERE status = 'active'")->fetchColumn(),
|
|
'servers_running' => $db->query("SELECT COUNT(*) FROM mta.servers WHERE provider_status = 'running'")->fetchColumn(),
|
|
|
|
// Warmup
|
|
'warmup_ready' => $db->query("SELECT COUNT(*) FROM admin.warmup_status WHERE status = 'ready'")->fetchColumn(),
|
|
'warmup_active' => $db->query("SELECT COUNT(*) FROM admin.warmup_status WHERE status = 'warming'")->fetchColumn(),
|
|
'warmup_burned' => $db->query("SELECT COUNT(*) FROM admin.warmup_status WHERE status = 'burned'")->fetchColumn(),
|
|
|
|
// Winning
|
|
'winning_active' => $db->query("SELECT COUNT(*) FROM admin.winning_configs WHERE status = 'active'")->fetchColumn(),
|
|
'winning_invalidated' => $db->query("SELECT COUNT(*) FROM admin.winning_configs WHERE status = 'invalidated'")->fetchColumn(),
|
|
|
|
// Sends
|
|
'sends_running' => $db->query("SELECT COUNT(*) FROM admin.send_configs WHERE status = 'running'")->fetchColumn(),
|
|
'sends_today' => $db->query("SELECT COUNT(*) FROM admin.send_configs WHERE DATE(created_at) = CURRENT_DATE")->fetchColumn(),
|
|
|
|
// Alerts
|
|
'alerts_critical' => $db->query("SELECT COUNT(*) FROM admin.send_alerts WHERE severity = 'critical' AND acknowledged = FALSE")->fetchColumn(),
|
|
'alerts_warning' => $db->query("SELECT COUNT(*) FROM admin.send_alerts WHERE severity = 'warning' AND acknowledged = FALSE")->fetchColumn(),
|
|
|
|
// IPs
|
|
'ips_clean' => $db->query("SELECT COUNT(*) FROM admin.ip_monitoring WHERE status = 'clean'")->fetchColumn(),
|
|
'ips_listed' => $db->query("SELECT COUNT(*) FROM admin.ip_monitoring WHERE status = 'listed'")->fetchColumn(),
|
|
];
|
|
|
|
// ==================== WINNING PERFORMANCE ====================
|
|
$winningPerformance = $db->query("SELECT
|
|
w.id, w.name, w.isp, w.country, w.inbox_rate as original_inbox,
|
|
w.usage_count,
|
|
COALESCE(AVG(m.inbox_rate), 0) as current_avg_inbox,
|
|
COUNT(m.id) as recent_uses,
|
|
CASE
|
|
WHEN COALESCE(AVG(m.inbox_rate), 0) < 60 THEN 'CRITICAL'
|
|
WHEN COALESCE(AVG(m.inbox_rate), 0) < 70 THEN 'WARNING'
|
|
ELSE 'OK'
|
|
END as health
|
|
FROM admin.winning_configs w
|
|
LEFT JOIN admin.send_configs c ON c.winning_config_id = w.id AND c.created_at > NOW() - INTERVAL '7 days'
|
|
LEFT JOIN admin.inbox_monitoring m ON m.send_config_id = c.id
|
|
WHERE w.status = 'active'
|
|
GROUP BY w.id
|
|
ORDER BY current_avg_inbox ASC")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// ==================== ECONOMIC SUMMARY ====================
|
|
$economics = $db->query("SELECT
|
|
DATE(created_at) as date,
|
|
SUM(volume_sent) as volume,
|
|
SUM(server_cost) as cost,
|
|
AVG(avg_inbox_rate) as avg_inbox,
|
|
AVG(stability_score) as stability
|
|
FROM admin.send_economics
|
|
WHERE created_at > NOW() - INTERVAL '7 days'
|
|
GROUP BY DATE(created_at)
|
|
ORDER BY date DESC")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// ==================== WEVAL MIND STATUS ====================
|
|
$hamidProviders = $db->query("SELECT * FROM admin.ai_providers WHERE status = 'active' ORDER BY priority")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// ==================== RECENT ALERTS ====================
|
|
$alerts = $db->query("SELECT * FROM admin.send_alerts WHERE acknowledged = FALSE ORDER BY severity DESC, created_at DESC LIMIT 10")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// ==================== ACTIVE SENDS ====================
|
|
$activeSends = $db->query("SELECT c.*,
|
|
s.name as sponsor_name,
|
|
(SELECT inbox_rate FROM admin.inbox_monitoring WHERE send_config_id = c.id ORDER BY check_time DESC LIMIT 1) as last_inbox
|
|
FROM admin.send_configs c
|
|
LEFT JOIN admin.sponsors s ON c.sponsor_id = s.id
|
|
WHERE c.status IN ('running', 'paused')
|
|
ORDER BY c.started_at DESC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// ==================== API ACTIONS ====================
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
header('Content-Type: application/json');
|
|
$action = $_POST['action'] ?? '';
|
|
|
|
switch ($action) {
|
|
case 'invalidate_winning':
|
|
$winning_id = intval($_POST['winning_id']);
|
|
$reason = $_POST['reason'] ?? 'manual';
|
|
|
|
$db->prepare("UPDATE admin.winning_configs SET status = 'invalidated',
|
|
notes = COALESCE(notes, '') || ' [Invalidated: ' || ? || ' at ' || NOW() || ']'
|
|
WHERE id = ?")->execute([$reason, $winning_id]);
|
|
|
|
// Log
|
|
$db->prepare("INSERT INTO admin.orchestrator_log (source, action, data, status) VALUES ('ceo-pilotage', 'invalidate_winning', ?, 'success')")
|
|
->execute([json_encode(['winning_id' => $winning_id, 'reason' => $reason])]);
|
|
|
|
echo json_encode(['success' => true]);
|
|
exit;
|
|
|
|
case 'auto_invalidate_check':
|
|
// Check all winning configs and invalidate poor performers
|
|
$poor = $db->query("SELECT w.id, w.name, AVG(m.inbox_rate) as avg_inbox
|
|
FROM admin.winning_configs w
|
|
JOIN admin.send_configs c ON c.winning_config_id = w.id AND c.created_at > NOW() - INTERVAL '7 days'
|
|
JOIN admin.inbox_monitoring m ON m.send_config_id = c.id
|
|
WHERE w.status = 'active'
|
|
GROUP BY w.id
|
|
HAVING AVG(m.inbox_rate) < 60 OR COUNT(m.id) >= 3")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$invalidated = [];
|
|
foreach ($poor as $w) {
|
|
if ($w['avg_inbox'] < 60) {
|
|
$db->prepare("UPDATE admin.winning_configs SET status = 'invalidated' WHERE id = ?")->execute([$w['id']]);
|
|
$invalidated[] = $w;
|
|
}
|
|
}
|
|
|
|
echo json_encode(['success' => true, 'invalidated' => $invalidated, 'count' => count($invalidated)]);
|
|
exit;
|
|
|
|
case 'emergency_stop_all':
|
|
// Stop ALL running sends and servers
|
|
$running = $db->query("SELECT id, server_ids FROM admin.send_configs WHERE status = 'running'")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
foreach ($running as $send) {
|
|
$db->prepare("UPDATE admin.send_configs SET status = 'emergency_stopped' WHERE id = ?")->execute([$send['id']]);
|
|
|
|
$serverIds = trim($send['server_ids'], '{}');
|
|
if ($serverIds) {
|
|
$db->exec("UPDATE mta.servers SET provider_status = 'stopped' WHERE id IN ($serverIds)");
|
|
}
|
|
}
|
|
|
|
// Alert
|
|
$db->prepare("INSERT INTO admin.send_alerts (send_config_id, alert_type, message, severity) VALUES (0, 'emergency_stop', 'EMERGENCY STOP ALL - Triggered by CEO', 'critical')")
|
|
->execute();
|
|
|
|
echo json_encode(['success' => true, 'stopped' => count($running)]);
|
|
exit;
|
|
|
|
case 'test_hamid_capability':
|
|
$capability = $_POST['capability']; // text, image, voice
|
|
$provider = $_POST['provider'] ?? 'groq';
|
|
|
|
// Test based on capability
|
|
$result = testHamidCapability($capability, $provider);
|
|
echo json_encode($result);
|
|
exit;
|
|
|
|
case 'refresh_stats':
|
|
// Return fresh stats
|
|
echo json_encode([
|
|
'success' => true,
|
|
'sends_running' => $db->query("SELECT COUNT(*) FROM admin.send_configs WHERE status = 'running'")->fetchColumn(),
|
|
'servers_running' => $db->query("SELECT COUNT(*) FROM mta.servers WHERE provider_status = 'running'")->fetchColumn(),
|
|
'alerts_critical' => $db->query("SELECT COUNT(*) FROM admin.send_alerts WHERE severity = 'critical' AND acknowledged = FALSE")->fetchColumn(),
|
|
]);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
function testHamidCapability($capability, $provider) {
|
|
$ch = curl_init("http://127.0.0.1:5821/hamid-api.php");
|
|
|
|
$params = ['action' => 'chat', 'provider' => $provider];
|
|
|
|
switch ($capability) {
|
|
case 'text':
|
|
$params['message'] = 'Réponds OK si tu fonctionnes.';
|
|
break;
|
|
case 'image':
|
|
$params['message'] = 'Décris cette image test.';
|
|
$params['image'] = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
|
|
break;
|
|
case 'voice':
|
|
$params['action'] = 'transcribe';
|
|
$params['audio'] = ''; // Would need actual audio
|
|
break;
|
|
}
|
|
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
return [
|
|
'success' => $httpCode == 200,
|
|
'capability' => $capability,
|
|
'provider' => $provider,
|
|
'response' => json_decode($response, true)
|
|
];
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>CEO Pilotage Global - WEVAL/WEVAL SEND</title>
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: 'Segoe UI', system-ui, sans-serif; background: linear-gradient(135deg, #0a0a0f 0%, #1a1a2e 100%); min-height: 100vh; color: #e2e8f0; }
|
|
|
|
.header { background: rgba(15, 23, 42, 0.95); padding: 12px 25px; border-bottom: 3px solid #f97316; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 100; }
|
|
.logo { display: flex; align-items: center; gap: 15px; }
|
|
.logo h1 { font-size: 20px; background: linear-gradient(135deg, #f97316, #eab308); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
|
.logo .status { display: flex; gap: 10px; margin-left: 20px; }
|
|
.logo .status-dot { width: 10px; height: 10px; border-radius: 50%; animation: pulse 2s infinite; }
|
|
.logo .status-dot.green { background: #22c55e; }
|
|
.logo .status-dot.red { background: #ef4444; }
|
|
.logo .status-dot.orange { background: #f97316; }
|
|
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
|
|
|
|
.emergency-btn { padding: 10px 20px; background: linear-gradient(135deg, #dc2626, #b91c1c); border: none; border-radius: 8px; color: #fff; font-weight: 700; cursor: pointer; display: flex; align-items: center; gap: 8px; }
|
|
.emergency-btn:hover { transform: scale(1.05); }
|
|
|
|
.container { max-width: 1920px; margin: 0 auto; padding: 20px; }
|
|
|
|
/* Grid Layout */
|
|
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin-bottom: 20px; }
|
|
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-bottom: 20px; }
|
|
.grid-2 { display: grid; grid-template-columns: 2fr 1fr; gap: 20px; }
|
|
|
|
/* Stat Cards */
|
|
.stat-card { background: rgba(30, 41, 59, 0.9); border-radius: 12px; padding: 18px; border: 1px solid rgba(99, 102, 241, 0.2); position: relative; overflow: hidden; }
|
|
.stat-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: linear-gradient(90deg, #6366f1, #8b5cf6); }
|
|
.stat-card.green::before { background: linear-gradient(90deg, #22c55e, #16a34a); }
|
|
.stat-card.orange::before { background: linear-gradient(90deg, #f97316, #ea580c); }
|
|
.stat-card.red::before { background: linear-gradient(90deg, #ef4444, #dc2626); }
|
|
.stat-card .icon { position: absolute; top: 15px; right: 15px; font-size: 28px; opacity: 0.2; }
|
|
.stat-card .value { font-size: 32px; font-weight: 700; color: #fff; }
|
|
.stat-card .label { font-size: 11px; color: #94a3b8; margin-top: 5px; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
.stat-card .sub { font-size: 11px; color: #64748b; margin-top: 8px; }
|
|
|
|
/* Section Cards */
|
|
.card { background: rgba(30, 41, 59, 0.9); border: 1px solid rgba(99, 102, 241, 0.2); border-radius: 12px; padding: 20px; margin-bottom: 20px; }
|
|
.card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 12px; border-bottom: 1px solid rgba(99, 102, 241, 0.15); }
|
|
.card-header h3 { color: #a5b4fc; font-size: 14px; display: flex; align-items: center; gap: 10px; }
|
|
.card-header .badge { padding: 3px 10px; border-radius: 12px; font-size: 10px; font-weight: 600; }
|
|
.card-header .badge.green { background: rgba(34, 197, 94, 0.2); color: #4ade80; }
|
|
.card-header .badge.red { background: rgba(239, 68, 68, 0.2); color: #f87171; }
|
|
.card-header .badge.orange { background: rgba(249, 115, 22, 0.2); color: #fb923c; }
|
|
|
|
/* Winning Table */
|
|
.winning-table { width: 100%; border-collapse: collapse; }
|
|
.winning-table th, .winning-table td { padding: 10px; text-align: left; border-bottom: 1px solid rgba(99, 102, 241, 0.1); }
|
|
.winning-table th { font-size: 10px; color: #64748b; text-transform: uppercase; }
|
|
.winning-table tr:hover { background: rgba(99, 102, 241, 0.05); }
|
|
.winning-table .health-badge { padding: 3px 8px; border-radius: 8px; font-size: 10px; font-weight: 600; }
|
|
.winning-table .health-badge.OK { background: rgba(34, 197, 94, 0.2); color: #4ade80; }
|
|
.winning-table .health-badge.WARNING { background: rgba(249, 115, 22, 0.2); color: #fb923c; }
|
|
.winning-table .health-badge.CRITICAL { background: rgba(239, 68, 68, 0.2); color: #f87171; }
|
|
|
|
/* WEVAL MIND Capabilities */
|
|
.capabilities-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
|
|
.capability-card { background: rgba(15, 23, 42, 0.8); padding: 15px; border-radius: 10px; text-align: center; border: 1px solid rgba(99, 102, 241, 0.2); }
|
|
.capability-card .icon { font-size: 30px; margin-bottom: 10px; }
|
|
.capability-card .name { font-size: 12px; font-weight: 600; }
|
|
.capability-card .status { font-size: 10px; margin-top: 5px; }
|
|
.capability-card.ok { border-color: rgba(34, 197, 94, 0.5); }
|
|
.capability-card.ok .icon { color: #4ade80; }
|
|
.capability-card.warning { border-color: rgba(249, 115, 22, 0.5); }
|
|
.capability-card.warning .icon { color: #fb923c; }
|
|
|
|
/* Alerts */
|
|
.alert-item { padding: 12px 15px; border-radius: 8px; margin-bottom: 8px; display: flex; align-items: center; gap: 12px; font-size: 13px; }
|
|
.alert-item.critical { background: rgba(239, 68, 68, 0.15); border-left: 4px solid #ef4444; }
|
|
.alert-item.warning { background: rgba(249, 115, 22, 0.15); border-left: 4px solid #f97316; }
|
|
.alert-item .time { margin-left: auto; font-size: 10px; color: #64748b; }
|
|
|
|
/* Active Sends */
|
|
.send-item { background: rgba(15, 23, 42, 0.8); border-radius: 10px; padding: 15px; margin-bottom: 10px; border-left: 4px solid #22c55e; }
|
|
.send-item.paused { border-left-color: #f97316; }
|
|
.send-item .header { display: flex; justify-content: space-between; margin-bottom: 8px; }
|
|
.send-item .name { font-weight: 600; }
|
|
.send-item .inbox-rate { font-size: 20px; font-weight: 700; }
|
|
.send-item .inbox-rate.good { color: #4ade80; }
|
|
.send-item .inbox-rate.warning { color: #fb923c; }
|
|
.send-item .inbox-rate.bad { color: #f87171; }
|
|
|
|
/* Buttons */
|
|
.btn { padding: 8px 15px; border: none; border-radius: 6px; cursor: pointer; font-size: 11px; font-weight: 600; display: inline-flex; align-items: center; gap: 6px; transition: all 0.2s; }
|
|
.btn-primary { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: #fff; }
|
|
.btn-danger { background: linear-gradient(135deg, #ef4444, #dc2626); color: #fff; }
|
|
.btn-warning { background: linear-gradient(135deg, #f97316, #ea580c); color: #fff; }
|
|
.btn-outline { background: transparent; border: 1px solid rgba(99, 102, 241, 0.5); color: #a5b4fc; }
|
|
.btn:hover { transform: translateY(-1px); }
|
|
|
|
/* Quick Links */
|
|
.quick-links { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; }
|
|
.quick-link { padding: 15px; background: rgba(15, 23, 42, 0.8); border: 1px solid rgba(99, 102, 241, 0.2); border-radius: 10px; text-align: center; text-decoration: none; color: #a5b4fc; transition: all 0.2s; }
|
|
.quick-link:hover { border-color: #f97316; background: rgba(249, 115, 22, 0.1); }
|
|
.quick-link i { font-size: 24px; display: block; margin-bottom: 8px; }
|
|
.quick-link span { font-size: 10px; }
|
|
|
|
/* n8n Workflows */
|
|
.workflow-item { display: flex; align-items: center; gap: 12px; padding: 10px; background: rgba(15, 23, 42, 0.8); border-radius: 8px; margin-bottom: 8px; }
|
|
.workflow-item .status-dot { width: 8px; height: 8px; border-radius: 50%; }
|
|
.workflow-item .status-dot.active { background: #22c55e; }
|
|
.workflow-item .status-dot.inactive { background: #64748b; }
|
|
.workflow-item .name { flex: 1; font-size: 12px; }
|
|
</style>
|
|
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<div class="logo">
|
|
<h1><i class="fas fa-crown"></i> CEO PILOTAGE GLOBAL</h1>
|
|
<div class="status">
|
|
<span class="status-dot <?= $stats['sends_running'] > 0 ? 'green' : 'orange' ?>" title="Sends"></span>
|
|
<span class="status-dot <?= $stats['servers_running'] > 0 ? 'green' : 'orange' ?>" title="Servers"></span>
|
|
<span class="status-dot <?= $stats['alerts_critical'] == 0 ? 'green' : 'red' ?>" title="Alerts"></span>
|
|
</div>
|
|
</div>
|
|
<button class="emergency-btn" onclick="emergencyStopAll()">
|
|
<i class="fas fa-power-off"></i> EMERGENCY STOP ALL
|
|
</button>
|
|
</div>
|
|
|
|
<div class="container">
|
|
<!-- Stats Row 1: Core Metrics -->
|
|
<div class="grid-4">
|
|
<div class="stat-card">
|
|
<i class="fas fa-paper-plane icon"></i>
|
|
<div class="value"><?= $stats['sends_running'] ?></div>
|
|
<div class="label">Sends Running</div>
|
|
<div class="sub"><?= $stats['sends_today'] ?> créés aujourd'hui</div>
|
|
</div>
|
|
<div class="stat-card green">
|
|
<i class="fas fa-server icon"></i>
|
|
<div class="value"><?= $stats['servers_running'] ?>/<?= $stats['servers_total'] ?></div>
|
|
<div class="label">Servers Running</div>
|
|
<div class="sub">€<?= number_format($stats['servers_running'] * 0.02, 2) ?>/h</div>
|
|
</div>
|
|
<div class="stat-card <?= $stats['alerts_critical'] > 0 ? 'red' : 'green' ?>">
|
|
<i class="fas fa-bell icon"></i>
|
|
<div class="value"><?= $stats['alerts_critical'] ?></div>
|
|
<div class="label">Alertes Critiques</div>
|
|
<div class="sub"><?= $stats['alerts_warning'] ?> warnings</div>
|
|
</div>
|
|
<div class="stat-card orange">
|
|
<i class="fas fa-envelope icon"></i>
|
|
<div class="value"><?= number_format($stats['today_sent']) ?></div>
|
|
<div class="label">Mails Aujourd'hui</div>
|
|
<div class="sub"><?= number_format($stats['total_contacts']) ?> contacts total</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats Row 2: Infrastructure -->
|
|
<div class="grid-4">
|
|
<div class="stat-card">
|
|
<i class="fab fa-microsoft icon"></i>
|
|
<div class="value"><?= $stats['office_ready'] ?>/<?= $stats['office_total'] ?></div>
|
|
<div class="label">Office Ready</div>
|
|
<div class="sub"><?= $stats['office_blocked'] ?> bloqués</div>
|
|
</div>
|
|
<div class="stat-card green">
|
|
<i class="fas fa-fire icon"></i>
|
|
<div class="value"><?= $stats['warmup_ready'] ?></div>
|
|
<div class="label">IPs Warmup Ready</div>
|
|
<div class="sub"><?= $stats['warmup_active'] ?> en warmup, <?= $stats['warmup_burned'] ?> burned</div>
|
|
</div>
|
|
<div class="stat-card <?= $stats['ips_listed'] > 0 ? 'red' : 'green' ?>">
|
|
<i class="fas fa-shield-alt icon"></i>
|
|
<div class="value"><?= $stats['ips_clean'] ?></div>
|
|
<div class="label">IPs Clean</div>
|
|
<div class="sub"><?= $stats['ips_listed'] ?> blacklistées</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<i class="fas fa-trophy icon"></i>
|
|
<div class="value"><?= $stats['winning_active'] ?></div>
|
|
<div class="label">Winning Configs</div>
|
|
<div class="sub"><?= $stats['winning_invalidated'] ?> invalidées</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid-2">
|
|
<!-- LEFT COLUMN -->
|
|
<div>
|
|
<!-- WINNING CONFIGS -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-trophy"></i> Winning Configs Performance</h3>
|
|
<button class="btn btn-warning" onclick="autoInvalidateCheck()">
|
|
<i class="fas fa-sync"></i> Auto-Check & Invalidate
|
|
</button>
|
|
</div>
|
|
|
|
<?php if (empty($winningPerformance)): ?>
|
|
<div style="text-align:center;color:#64748b;padding:30px;">Aucune winning config active</div>
|
|
<?php else: ?>
|
|
<table class="winning-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Config</th>
|
|
<th>ISP/Country</th>
|
|
<th>Original Inbox</th>
|
|
<th>Current Avg</th>
|
|
<th>Uses (7j)</th>
|
|
<th>Health</th>
|
|
<th>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($winningPerformance as $w): ?>
|
|
<tr>
|
|
<td><?= htmlspecialchars($w['name']) ?></td>
|
|
<td><?= $w['isp'] ?> / <?= $w['country'] ?></td>
|
|
<td><?= $w['original_inbox'] ?>%</td>
|
|
<td style="font-weight:700;color:<?= $w['current_avg_inbox'] < 70 ? '#f87171' : '#4ade80' ?>"><?= round($w['current_avg_inbox'], 1) ?>%</td>
|
|
<td><?= $w['recent_uses'] ?></td>
|
|
<td><span class="health-badge <?= $w['health'] ?>"><?= $w['health'] ?></span></td>
|
|
<td>
|
|
<?php if ($w['health'] != 'OK'): ?>
|
|
<button class="btn btn-danger" onclick="invalidateWinning(<?= $w['id'] ?>, 'low_performance')">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- ACTIVE SENDS -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-paper-plane"></i> Sends Actifs</h3>
|
|
<a href="/deliverads/send-control.php" class="btn btn-outline">Voir tout →</a>
|
|
</div>
|
|
|
|
<?php foreach ($activeSends as $send):
|
|
$inboxClass = $send['last_inbox'] >= 80 ? 'good' : ($send['last_inbox'] >= 70 ? 'warning' : 'bad');
|
|
?>
|
|
<div class="send-item <?= $send['status'] ?>">
|
|
<div class="header">
|
|
<span class="name"><?= htmlspecialchars($send['name']) ?></span>
|
|
<span class="inbox-rate <?= $inboxClass ?>"><?= $send['last_inbox'] ?? '?' ?>%</span>
|
|
</div>
|
|
<div style="font-size:11px;color:#64748b;">
|
|
<?= $send['sponsor_name'] ?> | Drop <?= $send['current_drop'] ?>/<?= $send['drops_count'] ?> | <?= $send['servers_needed'] ?> servers
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
|
|
<?php if (empty($activeSends)): ?>
|
|
<div style="text-align:center;color:#64748b;padding:30px;">Aucun send actif</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- WEVAL MIND CAPABILITIES -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-brain"></i> WEVAL MIND Capabilities</h3>
|
|
<span class="badge green"><?= count($hamidProviders) ?> Providers</span>
|
|
</div>
|
|
|
|
<div class="capabilities-grid">
|
|
<div class="capability-card ok" onclick="testCapability('text')">
|
|
<div class="icon">📝</div>
|
|
<div class="name">Text</div>
|
|
<div class="status">✅ All Providers</div>
|
|
</div>
|
|
<div class="capability-card ok" onclick="testCapability('image')">
|
|
<div class="icon">🖼️</div>
|
|
<div class="name">Image/Vision</div>
|
|
<div class="status">✅ Gemini, Claude</div>
|
|
</div>
|
|
<div class="capability-card warning" onclick="testCapability('video')">
|
|
<div class="icon">🎬</div>
|
|
<div class="name">Video</div>
|
|
<div class="status">⚠️ Gemini 1.5 Pro</div>
|
|
</div>
|
|
<div class="capability-card ok" onclick="testCapability('voice')">
|
|
<div class="icon">🎤</div>
|
|
<div class="name">Voice/Whisper</div>
|
|
<div class="status">✅ Groq Whisper</div>
|
|
</div>
|
|
<div class="capability-card ok">
|
|
<div class="icon">📊</div>
|
|
<div class="name">Analysis</div>
|
|
<div class="status">✅ All Providers</div>
|
|
</div>
|
|
<div class="capability-card ok">
|
|
<div class="icon">🔮</div>
|
|
<div class="name">Prediction</div>
|
|
<div class="status">✅ Claude, Gemini</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="margin-top:15px;">
|
|
<h4 style="font-size:12px;color:#94a3b8;margin-bottom:10px;">Providers Actifs:</h4>
|
|
<div style="display:flex;flex-wrap:wrap;gap:8px;">
|
|
<?php foreach ($hamidProviders as $p): ?>
|
|
<span style="padding:5px 12px;background:rgba(34,197,94,0.1);border:1px solid rgba(34,197,94,0.3);border-radius:15px;font-size:11px;"><?= $p['name'] ?></span>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RIGHT COLUMN -->
|
|
<div>
|
|
<!-- ALERTS -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-bell"></i> Alertes Récentes</h3>
|
|
<span class="badge <?= $stats['alerts_critical'] > 0 ? 'red' : 'green' ?>"><?= $stats['alerts_critical'] + $stats['alerts_warning'] ?></span>
|
|
</div>
|
|
|
|
<?php foreach ($alerts as $alert): ?>
|
|
<div class="alert-item <?= $alert['severity'] ?>">
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
<span><?= htmlspecialchars($alert['message']) ?></span>
|
|
<span class="time"><?= date('H:i', strtotime($alert['created_at'])) ?></span>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
|
|
<?php if (empty($alerts)): ?>
|
|
<div style="text-align:center;color:#4ade80;padding:20px;">
|
|
<i class="fas fa-check-circle" style="font-size:30px;"></i>
|
|
<div style="margin-top:10px;">Aucune alerte</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- N8N WORKFLOWS -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-robot"></i> n8n Workflows</h3>
|
|
<a href="http://89.167.40.150:5678" target="_blank" class="btn btn-outline">Ouvrir n8n →</a>
|
|
</div>
|
|
|
|
<div class="workflow-item">
|
|
<span class="status-dot active"></span>
|
|
<span class="name">Office Setup Automation</span>
|
|
<span style="font-size:10px;color:#64748b;">/webhook/office-setup</span>
|
|
</div>
|
|
<div class="workflow-item">
|
|
<span class="status-dot active"></span>
|
|
<span class="name">IP Warmup Manager</span>
|
|
<span style="font-size:10px;color:#64748b;">/webhook/warmup</span>
|
|
</div>
|
|
<div class="workflow-item">
|
|
<span class="status-dot active"></span>
|
|
<span class="name">Alert Response</span>
|
|
<span style="font-size:10px;color:#64748b;">/webhook/alert</span>
|
|
</div>
|
|
<div class="workflow-item">
|
|
<span class="status-dot active"></span>
|
|
<span class="name">Inbox Monitor</span>
|
|
<span style="font-size:10px;color:#64748b;">/webhook/inbox-check</span>
|
|
</div>
|
|
<div class="workflow-item">
|
|
<span class="status-dot active"></span>
|
|
<span class="name">Server Auto-Stop</span>
|
|
<span style="font-size:10px;color:#64748b;">/webhook/server-stop</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- QUICK LINKS -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-bolt"></i> Accès Rapide</h3>
|
|
</div>
|
|
<div class="quick-links">
|
|
<a href="/deliverads/send-control.php" class="quick-link">
|
|
<i class="fas fa-paper-plane"></i>
|
|
<span>Send Control</span>
|
|
</a>
|
|
<a href="/deliverads/process-hub.php" class="quick-link">
|
|
<i class="fas fa-cogs"></i>
|
|
<span>Process Hub</span>
|
|
</a>
|
|
<a href="/hamid-fullscreen.php" class="quick-link">
|
|
<i class="fas fa-brain"></i>
|
|
<span>WEVAL MIND</span>
|
|
</a>
|
|
<a href="/office-workflow.php" class="quick-link">
|
|
<i class="fab fa-microsoft"></i>
|
|
<span>Office VNC</span>
|
|
</a>
|
|
<a href="/deliverads/domain-hub.php" class="quick-link">
|
|
<i class="fas fa-globe"></i>
|
|
<span>Domain Hub</span>
|
|
</a>
|
|
<a href="/system-monitoring.php" class="quick-link">
|
|
<i class="fas fa-chart-line"></i>
|
|
<span>Monitoring</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ECONOMICS SUMMARY -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-dollar-sign"></i> Economics (7 jours)</h3>
|
|
</div>
|
|
|
|
<?php if (!empty($economics)): ?>
|
|
<table style="width:100%;font-size:11px;">
|
|
<thead>
|
|
<tr style="color:#64748b;">
|
|
<th>Date</th>
|
|
<th>Volume</th>
|
|
<th>Coût</th>
|
|
<th>Inbox</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($economics as $e): ?>
|
|
<tr>
|
|
<td><?= date('d/m', strtotime($e['date'])) ?></td>
|
|
<td><?= number_format($e['volume']) ?></td>
|
|
<td>€<?= number_format($e['cost'], 2) ?></td>
|
|
<td style="color:<?= $e['avg_inbox'] >= 70 ? '#4ade80' : '#f87171' ?>"><?= round($e['avg_inbox'], 1) ?>%</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php else: ?>
|
|
<div style="text-align:center;color:#64748b;padding:20px;">Pas de données</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
async function emergencyStopAll() {
|
|
if (!confirm('⚠️ EMERGENCY STOP ALL?\n\nCela va:\n- Arrêter TOUS les sends\n- Stopper TOUS les serveurs\n\nContinuer?')) return;
|
|
|
|
const fd = new FormData();
|
|
fd.append('action', 'emergency_stop_all');
|
|
|
|
const res = await fetch('', {method: 'POST', body: fd});
|
|
const data = await res.json();
|
|
|
|
alert(data.success ? `✅ ${data.stopped} sends arrêtés` : '❌ Erreur');
|
|
location.reload();
|
|
}
|
|
|
|
async function invalidateWinning(id, reason) {
|
|
if (!confirm('Invalider cette winning config?')) return;
|
|
|
|
const fd = new FormData();
|
|
fd.append('action', 'invalidate_winning');
|
|
fd.append('winning_id', id);
|
|
fd.append('reason', reason);
|
|
|
|
await fetch('', {method: 'POST', body: fd});
|
|
location.reload();
|
|
}
|
|
|
|
async function autoInvalidateCheck() {
|
|
const fd = new FormData();
|
|
fd.append('action', 'auto_invalidate_check');
|
|
|
|
const res = await fetch('', {method: 'POST', body: fd});
|
|
const data = await res.json();
|
|
|
|
if (data.count > 0) {
|
|
alert(`${data.count} winning configs invalidées pour mauvaise performance`);
|
|
location.reload();
|
|
} else {
|
|
alert('Toutes les winning configs sont OK');
|
|
}
|
|
}
|
|
|
|
async function testCapability(cap) {
|
|
const fd = new FormData();
|
|
fd.append('action', 'test_hamid_capability');
|
|
fd.append('capability', cap);
|
|
fd.append('provider', cap === 'image' ? 'gemini' : 'groq');
|
|
|
|
const res = await fetch('', {method: 'POST', body: fd});
|
|
const data = await res.json();
|
|
|
|
alert(data.success ? `✅ ${cap} fonctionne!` : `❌ ${cap} error`);
|
|
}
|
|
|
|
// Auto-refresh every 30s
|
|
setInterval(() => {
|
|
fetch('?action=refresh_stats')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
console.log('Stats refreshed', data);
|
|
});
|
|
}, 30000);
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|