Files
wevads-platform/public/process-supervision.php
2026-04-07 03:04:16 +02:00

278 lines
15 KiB
PHP

<?php
require_once('/opt/wevads/config/credentials.php');
session_start();
$pdo = get_pdo('adx_system');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Drill-down API
if (isset($_GET['drill'])) {
header('Content-Type: application/json');
$data = [];
try {
switch($_GET['drill']) {
// Infrastructure
case 'servers': $data = $pdo->query("SELECT id, name, main_ip, host_name, status, created_date FROM admin.mta_servers ORDER BY id DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC); break;
case 'vmtas': $data = $pdo->query("SELECT v.id, v.name, v.domain, v.ip, v.status, m.name as server FROM admin.servers_vmtas v JOIN admin.mta_servers m ON v.mta_server_id = m.id LIMIT 20")->fetchAll(PDO::FETCH_ASSOC); break;
// Campaigns
case 'campaigns': $data = $pdo->query("SELECT id, name, status, total_sent, total_opens, total_clicks FROM admin.campaigns ORDER BY id DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC); break;
case 'campaigns_active': $data = $pdo->query("SELECT id, name, status, total_sent FROM admin.campaigns WHERE status IN ('sending','active') LIMIT 20")->fetchAll(PDO::FETCH_ASSOC); break;
// Office 365
case 'office_accounts': $data = $pdo->query("SELECT id, admin_email, status, created_at FROM admin.office_accounts ORDER BY id DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC); break;
case 'office_domains': $data = $pdo->query("SELECT id, domain_name, verification_status, account_id FROM admin.office_domains ORDER BY id DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC); break;
// Security
case 'security_events': $data = $pdo->query("SELECT * FROM admin.security_events ORDER BY created_at DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC); break;
case 'blacklist': $data = $pdo->query("SELECT * FROM admin.blacklist_checks ORDER BY checked_at DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC); break;
// Brain
case 'brain_tests': $data = $pdo->query("SELECT * FROM admin.brain_test_results ORDER BY id DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC); break;
case 'brain_winners': $data = $pdo->query("SELECT * FROM admin.brain_winners ORDER BY id DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC); break;
}
} catch(Exception $e) { $data = [['error' => $e->getMessage()]]; }
echo json_encode($data);
exit;
}
// Stats
$stats = [
'servers' => $pdo->query("SELECT COUNT(*) FROM admin.mta_servers WHERE status='Activated'")->fetchColumn(),
'vmtas' => $pdo->query("SELECT COUNT(*) FROM admin.servers_vmtas")->fetchColumn(),
'campaigns_active' => $pdo->query("SELECT COUNT(*) FROM admin.campaigns WHERE status IN ('sending','active')")->fetchColumn(),
'emails_sent' => $pdo->query("SELECT COALESCE(SUM(total_sent),0) FROM admin.campaigns")->fetchColumn(),
'opens' => $pdo->query("SELECT COALESCE(SUM(total_opens),0) FROM admin.campaigns")->fetchColumn(),
'clicks' => $pdo->query("SELECT COALESCE(SUM(total_clicks),0) FROM admin.campaigns")->fetchColumn(),
'office_accounts' => $pdo->query("SELECT COUNT(*) FROM admin.office_accounts")->fetchColumn(),
'office_verified' => $pdo->query("SELECT COUNT(*) FROM admin.office_domains WHERE verification_status='Verified'")->fetchColumn(),
'security_events' => $pdo->query("SELECT COUNT(*) FROM admin.security_events")->fetchColumn(),
];
// Security Check
$security = ['status' => 'OK', 'score' => 100, 'alerts' => []];
$brute = shell_exec("grep 'Failed password' /var/log/auth.log 2>/dev/null | awk '{print \$(NF-3)}' | sort | uniq -c | sort -rn | head -3");
if ($brute) {
foreach (array_filter(explode("\n", trim($brute))) as $line) {
if (preg_match('/^\s*(\d+)\s+(.+)$/', $line, $m) && intval($m[1]) >= 10) {
$security['alerts'][] = ['type' => 'BRUTE_FORCE', 'ip' => $m[2], 'attempts' => $m[1]];
$security['score'] -= 20;
}
}
}
if ($security['score'] < 80) $security['status'] = 'WARNING';
if ($security['score'] < 50) $security['status'] = 'CRITICAL';
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8"><title>Process Supervision - WEVADS</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
body{background:linear-gradient(135deg,#0f172a,#1e293b)}
.card{background:rgba(255,255,255,0.05);backdrop-filter:blur(10px);transition:all 0.3s}
.card:hover{transform:translateY(-3px);box-shadow:0 10px 30px rgba(0,0,0,0.3)}
.flow-step{position:relative;padding-left:30px}
.flow-step::before{content:'';position:absolute;left:10px;top:0;bottom:0;width:2px;background:linear-gradient(180deg,#3b82f6,#10b981)}
.flow-step::after{content:'';position:absolute;left:6px;top:8px;width:10px;height:10px;background:#3b82f6;border-radius:50%}
.drill{cursor:pointer;transition:all 0.2s}.drill:hover{background:rgba(59,130,246,0.2)}
</style>
</head>
<body class="text-white min-h-screen p-4">
<div class="container mx-auto max-w-7xl">
<!-- Header -->
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold"><i class="fas fa-tachometer-alt mr-3 text-blue-400"></i>Process Supervision</h1>
<div class="flex gap-2">
<span class="px-3 py-1 rounded text-sm <?=$security['status']==='OK'?'bg-green-600':($security['status']==='WARNING'?'bg-yellow-600':'bg-red-600')?>">
<i class="fas fa-shield-alt mr-1"></i>Security: <?=$security['score']?>%
</span>
<a href="/system-flows.php" class="bg-gray-600 px-3 py-1 rounded text-sm">System Flows</a>
</div>
</div>
<!-- Security Alert -->
<?php if(!empty($security['alerts'])): ?>
<div class="bg-red-900/50 border border-red-500 rounded-lg p-4 mb-6">
<h3 class="font-bold text-red-400 mb-2"><i class="fas fa-exclamation-triangle mr-2"></i>Alertes Sécurité</h3>
<?php foreach($security['alerts'] as $a): ?>
<div class="flex justify-between items-center bg-red-800/30 p-2 rounded mb-1">
<span><i class="fas fa-user-secret mr-2"></i><?=$a['type']?>: <?=$a['ip']?> (<?=$a['attempts']?> tentatives)</span>
<button onclick="banIP('<?=$a['ip']?>')" class="bg-red-600 px-2 py-1 rounded text-xs">Bannir</button>
</div>
<?php endforeach; ?>
<p class="text-xs text-red-300 mt-2"><i class="fas fa-info-circle mr-1"></i>Fail2ban config: port 49222. Vérifier que SSH utilise ce port.</p>
</div>
<?php endif; ?>
<!-- Stats Row -->
<div class="grid grid-cols-6 gap-3 mb-6">
<div class="card rounded-lg p-3 text-center drill" onclick="showDrill('servers')">
<div class="text-2xl font-bold text-blue-400"><?=$stats['servers']?></div>
<div class="text-xs text-gray-400">MTA Servers</div>
</div>
<div class="card rounded-lg p-3 text-center drill" onclick="showDrill('vmtas')">
<div class="text-2xl font-bold text-green-400"><?=$stats['vmtas']?></div>
<div class="text-xs text-gray-400">VMTAs</div>
</div>
<div class="card rounded-lg p-3 text-center drill" onclick="showDrill('campaigns_active')">
<div class="text-2xl font-bold text-yellow-400"><?=$stats['campaigns_active']?></div>
<div class="text-xs text-gray-400">Campagnes Actives</div>
</div>
<div class="card rounded-lg p-3 text-center drill" onclick="showDrill('campaigns')">
<div class="text-2xl font-bold text-purple-400"><?=number_format($stats['emails_sent'])?></div>
<div class="text-xs text-gray-400">Emails Envoyés</div>
</div>
<div class="card rounded-lg p-3 text-center">
<div class="text-2xl font-bold text-cyan-400"><?=number_format($stats['opens'])?></div>
<div class="text-xs text-gray-400">Opens</div>
</div>
<div class="card rounded-lg p-3 text-center">
<div class="text-2xl font-bold text-pink-400"><?=number_format($stats['clicks'])?></div>
<div class="text-xs text-gray-400">Clicks</div>
</div>
</div>
<!-- Main Process Flow -->
<div class="grid grid-cols-3 gap-6">
<!-- Process Flow Column -->
<div class="col-span-2 card rounded-xl p-5">
<h2 class="text-lg font-bold mb-4"><i class="fas fa-project-diagram mr-2 text-blue-400"></i>Email Delivery Flow</h2>
<div class="space-y-4">
<div class="flow-step">
<div class="bg-blue-900/30 p-3 rounded-lg drill" onclick="showDrill('office_accounts')">
<div class="flex justify-between"><span class="font-bold">1. Office 365 Setup</span><span class="text-blue-400"><?=$stats['office_accounts']?> comptes</span></div>
<p class="text-xs text-gray-400 mt-1">Tenant recovery → License test → Azure credentials</p>
</div>
</div>
<div class="flow-step">
<div class="bg-green-900/30 p-3 rounded-lg drill" onclick="showDrill('office_domains')">
<div class="flex justify-between"><span class="font-bold">2. Domain Config</span><span class="text-green-400"><?=$stats['office_verified']?> vérifiés</span></div>
<p class="text-xs text-gray-400 mt-1">FreeDNS creation → Add to O365 → DNS verification</p>
</div>
</div>
<div class="flow-step">
<div class="bg-purple-900/30 p-3 rounded-lg drill" onclick="showDrill('servers')">
<div class="flex justify-between"><span class="font-bold">3. MTA Infrastructure</span><span class="text-purple-400"><?=$stats['servers']?> serveurs</span></div>
<p class="text-xs text-gray-400 mt-1">Huawei provisioning → PMTA install → VMTA config</p>
</div>
</div>
<div class="flow-step">
<div class="bg-yellow-900/30 p-3 rounded-lg drill" onclick="showDrill('brain_tests')">
<div class="flex justify-between"><span class="font-bold">4. Brain Analysis</span><span class="text-yellow-400">Testing</span></div>
<p class="text-xs text-gray-400 mt-1">Config testing → ISP analysis → Winner selection</p>
</div>
</div>
<div class="flow-step">
<div class="bg-cyan-900/30 p-3 rounded-lg drill" onclick="showDrill('campaigns_active')">
<div class="flex justify-between"><span class="font-bold">5. Campaign Execution</span><span class="text-cyan-400"><?=$stats['campaigns_active']?> actives</span></div>
<p class="text-xs text-gray-400 mt-1">Send list → Creative → PMTA routing → Tracking</p>
</div>
</div>
<div class="flow-step" style="padding-left:0">
<div class="bg-emerald-900/30 p-3 rounded-lg">
<div class="flex justify-between"><span class="font-bold">6. Results</span><span class="text-emerald-400"><?=number_format($stats['emails_sent'])?> sent</span></div>
<p class="text-xs text-gray-400 mt-1">Opens: <?=number_format($stats['opens'])?> | Clicks: <?=number_format($stats['clicks'])?> | Rate: <?=$stats['emails_sent']>0?round($stats['opens']/$stats['emails_sent']*100,1):0?>%</p>
</div>
</div>
</div>
</div>
<!-- Quick Actions & Security -->
<div class="space-y-4">
<div class="card rounded-xl p-4">
<h3 class="font-bold mb-3"><i class="fas fa-bolt mr-2 text-yellow-400"></i>Actions Rapides</h3>
<div class="space-y-2">
<a href="/huawei-create-n8n.php" class="block bg-blue-600 hover:bg-blue-500 p-2 rounded text-center text-sm">+ Créer Serveur Huawei</a>
<a href="/office365-process.php" class="block bg-green-600 hover:bg-green-500 p-2 rounded text-center text-sm">Office 365 Workflow</a>
<a href="/security-report.php" class="block bg-red-600 hover:bg-red-500 p-2 rounded text-center text-sm">Rapport Sécurité</a>
</div>
</div>
<div class="card rounded-xl p-4">
<h3 class="font-bold mb-3"><i class="fas fa-shield-alt mr-2 text-red-400"></i>Sécurité</h3>
<div class="space-y-2 text-sm">
<div class="flex justify-between"><span>Fail2ban</span><span class="text-green-400">Actif</span></div>
<div class="flex justify-between"><span>SSH Port</span><span class="text-yellow-400">49222</span></div>
<div class="flex justify-between drill" onclick="showDrill('security_events')"><span>Événements</span><span class="text-blue-400"><?=$stats['security_events']?></span></div>
<div class="flex justify-between drill" onclick="showDrill('blacklist')"><span>Blacklist Checks</span><span class="text-purple-400">→</span></div>
</div>
</div>
<div class="card rounded-xl p-4">
<h3 class="font-bold mb-3"><i class="fas fa-question-circle mr-2 text-blue-400"></i>Diagnostic</h3>
<div class="text-xs text-gray-400 space-y-1">
<p>• Campagnes avec 0 envois = pas de send queue</p>
<p>• Fail2ban port 49222 ≠ SSH réel ?</p>
<p>• daily_stats vide → utiliser campaigns</p>
</div>
</div>
</div>
</div>
<!-- Drill Modal -->
<div id="drillModal" class="hidden fixed inset-0 bg-black/80 z-50 flex items-center justify-center p-4">
<div class="bg-gray-800 rounded-xl w-full max-w-4xl max-h-[80vh] overflow-hidden">
<div class="flex justify-between items-center p-4 border-b border-gray-700">
<h3 id="drillTitle" class="text-lg font-bold"></h3>
<button onclick="closeDrill()" class="text-2xl">&times;</button>
</div>
<div id="drillContent" class="p-4 overflow-auto max-h-[60vh]"></div>
</div>
</div>
</div>
<script>
async function showDrill(type) {
const titles = {
'servers': 'MTA Servers', 'vmtas': 'VMTAs', 'campaigns': 'Toutes Campagnes',
'campaigns_active': 'Campagnes Actives', 'office_accounts': 'Comptes Office 365',
'office_domains': 'Domaines Office', 'security_events': 'Événements Sécurité',
'blacklist': 'Blacklist Checks', 'brain_tests': 'Brain Tests', 'brain_winners': 'Brain Winners'
};
document.getElementById('drillTitle').textContent = titles[type] || type;
document.getElementById('drillContent').innerHTML = '<div class="text-center py-8"><i class="fas fa-spinner fa-spin text-2xl"></i></div>';
document.getElementById('drillModal').classList.remove('hidden');
try {
const res = await fetch('?drill=' + type);
const data = await res.json();
if (!data.length) { document.getElementById('drillContent').innerHTML = '<p class="text-center text-gray-400">Aucune donnée</p>'; return; }
let html = '<table class="w-full text-sm"><thead><tr class="bg-gray-700">';
Object.keys(data[0]).forEach(k => html += '<th class="p-2 text-left">' + k + '</th>');
html += '</tr></thead><tbody>';
data.forEach(row => {
html += '<tr class="border-b border-gray-700 hover:bg-gray-700/50">';
Object.entries(row).forEach(([k,v]) => {
let cls = '';
if (v === 'Activated' || v === 'completed' || v === 'Verified') cls = 'text-green-400';
if (v === 'sending' || v === 'active' || v === 'Pending') cls = 'text-yellow-400';
if (v === 'failed' || v === 'CRITICAL' || v === 'HIGH') cls = 'text-red-400';
html += '<td class="p-2 ' + cls + '">' + (v || '-') + '</td>';
});
html += '</tr>';
});
html += '</tbody></table>';
document.getElementById('drillContent').innerHTML = html;
} catch(e) { document.getElementById('drillContent').innerHTML = '<p class="text-red-400">' + e.message + '</p>'; }
}
function closeDrill() { document.getElementById('drillModal').classList.add('hidden'); }
function banIP(ip) {
if(confirm('Bannir ' + ip + ' ?')) {
fetch('/security-report.php', {method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded'}, body:'action=block_ip&ip='+ip})
.then(r=>r.json()).then(d=>alert(d.message)).catch(e=>alert('Erreur'));
}
}
</script>
</body>
</html>