FIX: Tracking screens - GOLD restore + tracking-white.css minimal

This commit is contained in:
2026-03-06 16:10:15 +01:00
parent bfc30f3a6c
commit 7db823d905
8 changed files with 526 additions and 327 deletions

View File

@@ -1,12 +1,40 @@
<!DOCTYPE html><html lang="en"><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 - Tracking Command Center — Arsenal</title>
<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">
<link rel="stylesheet" href="tracking-clean.css">
</head><body>
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Tracking Command Center — Arsenal</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{--bg:#060a14;--s:#0c1220;--c:#111827;--cy:#22d3ee;--gn:#10b981;--rd:#ef4444;--or:#f59e0b;--pk:#ec4899;--pu:#a78bfa;--tx:#e2e8f0;--t2:#94a3b8;--b:#1e293b}
body{background:var(--bg);color:var(--tx);font-family:'DM Sans',system-ui,sans-serif;padding:20px}
.hdr{display:flex;align-items:center;gap:12px;margin-bottom:20px}
.hdr h1{font-size:20px;font-weight:700}.hdr .badge{background:var(--gn);color:#000;padding:2px 10px;border-radius:12px;font-size:11px;font-weight:700}
.hdr .badge.warn{background:var(--or)}.hdr .badge.err{background:var(--rd);color:#fff}
.tabs{display:flex;gap:6px;margin-bottom:16px}
.tab{padding:6px 14px;background:var(--s);border:1px solid var(--b);border-radius:6px;cursor:pointer;font-size:12px;color:var(--t2);transition:all .2s}
.tab.active,.tab:hover{background:var(--c);color:var(--cy);border-color:var(--cy)}
.g4{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px}
.g3{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:16px}
.g2{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px}
.card{background:var(--s);border:1px solid var(--b);border-radius:8px;padding:14px}
.kpi{text-align:center}.kpi .v{font-size:28px;font-weight:800}.kpi .l{font-size:10px;color:var(--t2);text-transform:uppercase;letter-spacing:1px;margin-top:2px}
.kpi.cyan .v{color:var(--cy)}.kpi.green .v{color:var(--gn)}.kpi.pink .v{color:var(--pk)}.kpi.orange .v{color:var(--or)}.kpi.purple .v{color:var(--pu)}
table{width:100%;border-collapse:collapse;font-size:12px}
th{text-align:left;padding:6px 8px;color:var(--t2);font-size:10px;text-transform:uppercase;border-bottom:1px solid var(--b)}
td{padding:6px 8px;border-bottom:1px solid rgba(30,41,59,.5)}
.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:4px}.dot-g{background:var(--gn)}.dot-r{background:var(--rd)}.dot-o{background:var(--or)}
.btn{padding:6px 14px;border:1px solid var(--b);background:var(--s);color:var(--tx);border-radius:6px;cursor:pointer;font-size:11px;transition:all .2s}
.btn:hover{border-color:var(--cy);color:var(--cy)}
.btn-action{background:linear-gradient(135deg,rgba(34,211,238,.15),rgba(168,85,247,.15));border-color:var(--cy)}
.log{background:#000;border-radius:6px;padding:10px;font-family:monospace;font-size:11px;max-height:300px;overflow-y:auto;line-height:1.6}
.log .ok{color:var(--gn)}.log .err{color:var(--rd)}.log .info{color:var(--cy)}.log .warn{color:var(--or)}
.flow-box{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:12px 0}
.flow-step{background:var(--c);border:1px solid var(--b);border-radius:6px;padding:8px 12px;font-size:11px;position:relative}
.flow-step.active{border-color:var(--gn);box-shadow:0 0 8px rgba(16,185,129,.3)}
.flow-step.error{border-color:var(--rd);box-shadow:0 0 8px rgba(239,68,68,.3)}
.flow-arrow{color:var(--t2);font-size:16px}
.section{display:none}.section.active{display:block}
.srv{display:flex;align-items:center;gap:8px;padding:8px;background:var(--c);border-radius:6px;margin-bottom:8px}
.srv-dot{width:10px;height:10px;border-radius:50%}.srv-name{font-weight:600;font-size:13px}.srv-ip{color:var(--t2);font-size:11px}
pre{white-space:pre-wrap;word-break:break-all}
</style></head><body>
<div class="hdr">
<span style="font-size:24px">📡</span>
<h1>Tracking Command Center</h1>
@@ -295,6 +323,4 @@ async function fullRefresh() {
fullRefresh();
setInterval(fullRefresh, 60000);
</script>
<script src="arsenal-common.js?v1770778169">
</body></html>
</script>

View File

@@ -9,8 +9,35 @@ $GLOBALS["no_auth_check_wrapper"] = false; // Keep auth
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tracking Bridge — ADX Arsenal</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<link rel="stylesheet" href="tracking-clean.css">
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#0a0e1a;color:#e0e6f0;min-height:100vh;padding:20px}
.header{background:linear-gradient(135deg,#3b82f6,#06b6d4);border-radius:16px;padding:24px 32px;margin-bottom:24px;display:flex;align-items:center;gap:16px}
.header i{font-size:2rem;opacity:0.9}
.header h1{font-size:1.5rem;font-weight:600}
.header .badge{background:rgba(255,255,255,0.15);padding:4px 12px;border-radius:20px;font-size:0.75rem;margin-left:auto}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:20px;margin-bottom:24px}
.card{background:#111827;border:1px solid #1e293b;border-radius:12px;padding:20px;transition:border-color 0.2s}
.card:hover{border-color:#3b82f6}
.card h3{font-size:0.9rem;color:#94a3b8;margin-bottom:12px;display:flex;align-items:center;gap:8px}
.card h3 i{color:#3b82f6}
.kpi{font-size:2rem;font-weight:700;color:#f8fafc}
.kpi-label{font-size:0.8rem;color:#64748b;margin-top:4px}
.panel{background:#111827;border:1px solid #1e293b;border-radius:12px;padding:24px;margin-bottom:20px}
.panel h2{font-size:1.1rem;margin-bottom:16px;display:flex;align-items:center;gap:8px}
.panel h2 i{color:#3b82f6}
pre.response{background:#0d1117;border:1px solid #1e293b;border-radius:8px;padding:16px;overflow-x:auto;font-size:0.8rem;max-height:400px;overflow-y:auto;color:#7dd3fc;line-height:1.6}
.btn{background:#3b82f6;color:#fff;border:none;padding:8px 20px;border-radius:8px;cursor:pointer;font-size:0.85rem;transition:opacity 0.2s}
.btn:hover{opacity:0.85}
.btn-row{display:flex;gap:10px;margin-bottom:16px;flex-wrap:wrap}
.status{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px}
.status.on{background:#22c55e}
.status.off{background:#ef4444}
.status.warn{background:#f59e0b}
.info-table{width:100%;border-collapse:collapse}
.info-table td{padding:8px 12px;border-bottom:1px solid #1e293b;font-size:0.85rem}
.info-table td:first-child{color:#94a3b8;width:140px}
</style>
</head>
<body>
<div class="header">

View File

@@ -21,11 +21,11 @@ if (isset($_GET['action'])) {
for ($i = $days - 1; $i >= 0; $i--) {
$date = date('Y-m-d', strtotime("-{$i} days"));
$dateLabel = date('M j', strtotime($date));
$clicks = $db->query("SELECT COUNT(*) FROM admin.v_tracking_clicks WHERE DATE(event_time) = '{$date}'")->fetchColumn();
$opens = $db->query("SELECT COUNT(*) FROM admin.v_tracking_opens WHERE DATE(event_time) = '{$date}'")->fetchColumn();
$leads = $db->query("SELECT COUNT(*) FROM admin.v_tracking_leads WHERE DATE(event_time) = '{$date}'")->fetchColumn();
$unsubs = $db->query("SELECT COUNT(*) FROM admin.v_tracking_unsubs WHERE DATE(event_time) = '{$date}'")->fetchColumn();
$bounces = $db->query("SELECT COUNT(*) FROM admin.v_tracking_bounces WHERE DATE(event_time) = '{$date}'")->fetchColumn();
$clicks = $db->query("SELECT COUNT(*) FROM actions.clicks WHERE DATE(action_time) = '{$date}'")->fetchColumn();
$opens = $db->query("SELECT COUNT(*) FROM actions.opens WHERE DATE(action_time) = '{$date}'")->fetchColumn();
$leads = $db->query("SELECT COUNT(*) FROM actions.leads WHERE DATE(action_time) = '{$date}'")->fetchColumn();
$unsubs = $db->query("SELECT COUNT(*) FROM actions.unsubscribes WHERE DATE(action_time) = '{$date}'")->fetchColumn();
$bounces = $db->query("SELECT COUNT(*) FROM actions.optouts WHERE DATE(action_time) = '{$date}'")->fetchColumn();
$stats[] = ['date' => $date, 'label' => $dateLabel, 'clicks' => intval($clicks), 'opens' => intval($opens), 'leads' => intval($leads), 'unsubs' => intval($unsubs), 'bounces' => intval($bounces)];
}
echo json_encode(['status' => 'success', 'data' => $stats]);
@@ -35,29 +35,29 @@ if (isset($_GET['action'])) {
if ($_GET['action'] === 'global_stats') {
$period = $_GET['period'] ?? 'all';
$where = '';
if ($period === 'today') { $where = "WHERE DATE(event_time) = CURRENT_DATE"; }
elseif ($period === 'week') { $where = "WHERE event_time >= NOW() - INTERVAL '7 days'"; }
elseif ($period === 'month') { $where = "WHERE event_time >= NOW() - INTERVAL '30 days'"; }
$clicks = $db->query("SELECT COUNT(*) FROM admin.v_tracking_clicks {$where}")->fetchColumn();
$opens = $db->query("SELECT COUNT(*) FROM admin.v_tracking_opens {$where}")->fetchColumn();
$leads = $db->query("SELECT COUNT(*) FROM admin.v_tracking_leads {$where}")->fetchColumn();
$unsubs = $db->query("SELECT COUNT(*) FROM admin.v_tracking_unsubs {$where}")->fetchColumn();
$bounces = $db->query("SELECT COUNT(*) FROM admin.v_tracking_bounces {$where}")->fetchColumn();
if ($period === 'today') { $where = "WHERE DATE(action_time) = CURRENT_DATE"; }
elseif ($period === 'week') { $where = "WHERE action_time >= NOW() - INTERVAL '7 days'"; }
elseif ($period === 'month') { $where = "WHERE action_time >= NOW() - INTERVAL '30 days'"; }
$clicks = $db->query("SELECT COUNT(*) FROM actions.clicks {$where}")->fetchColumn();
$opens = $db->query("SELECT COUNT(*) FROM actions.opens {$where}")->fetchColumn();
$leads = $db->query("SELECT COUNT(*) FROM actions.leads {$where}")->fetchColumn();
$unsubs = $db->query("SELECT COUNT(*) FROM actions.unsubscribes {$where}")->fetchColumn();
$bounces = $db->query("SELECT COUNT(*) FROM actions.optouts {$where}")->fetchColumn();
echo json_encode(['status' => 'success', 'data' => ['clicks' => intval($clicks), 'opens' => intval($opens), 'leads' => intval($leads), 'unsubs' => intval($unsubs), 'bounces' => intval($bounces)], 'period' => $period]);
exit;
}
if ($_GET['action'] === 'today_comparison') {
$todayClicks = $db->query("SELECT COUNT(*) FROM admin.v_tracking_clicks WHERE DATE(event_time) = CURRENT_DATE")->fetchColumn();
$todayOpens = $db->query("SELECT COUNT(*) FROM admin.v_tracking_opens WHERE DATE(event_time) = CURRENT_DATE")->fetchColumn();
$todayLeads = $db->query("SELECT COUNT(*) FROM admin.v_tracking_leads WHERE DATE(event_time) = CURRENT_DATE")->fetchColumn();
$todayUnsubs = $db->query("SELECT COUNT(*) FROM admin.v_tracking_unsubs WHERE DATE(event_time) = CURRENT_DATE")->fetchColumn();
$todayBounces = $db->query("SELECT COUNT(*) FROM admin.v_tracking_bounces WHERE DATE(event_time) = CURRENT_DATE")->fetchColumn();
$yestClicks = $db->query("SELECT COUNT(*) FROM admin.v_tracking_clicks WHERE DATE(event_time) = CURRENT_DATE - INTERVAL '1 day'")->fetchColumn();
$yestOpens = $db->query("SELECT COUNT(*) FROM admin.v_tracking_opens WHERE DATE(event_time) = CURRENT_DATE - INTERVAL '1 day'")->fetchColumn();
$yestLeads = $db->query("SELECT COUNT(*) FROM admin.v_tracking_leads WHERE DATE(event_time) = CURRENT_DATE - INTERVAL '1 day'")->fetchColumn();
$yestUnsubs = $db->query("SELECT COUNT(*) FROM admin.v_tracking_unsubs WHERE DATE(event_time) = CURRENT_DATE - INTERVAL '1 day'")->fetchColumn();
$yestBounces = $db->query("SELECT COUNT(*) FROM admin.v_tracking_bounces WHERE DATE(event_time) = CURRENT_DATE - INTERVAL '1 day'")->fetchColumn();
$todayClicks = $db->query("SELECT COUNT(*) FROM actions.clicks WHERE DATE(action_time) = CURRENT_DATE")->fetchColumn();
$todayOpens = $db->query("SELECT COUNT(*) FROM actions.opens WHERE DATE(action_time) = CURRENT_DATE")->fetchColumn();
$todayLeads = $db->query("SELECT COUNT(*) FROM actions.leads WHERE DATE(action_time) = CURRENT_DATE")->fetchColumn();
$todayUnsubs = $db->query("SELECT COUNT(*) FROM actions.unsubscribes WHERE DATE(action_time) = CURRENT_DATE")->fetchColumn();
$todayBounces = $db->query("SELECT COUNT(*) FROM actions.optouts WHERE DATE(action_time) = CURRENT_DATE")->fetchColumn();
$yestClicks = $db->query("SELECT COUNT(*) FROM actions.clicks WHERE DATE(action_time) = CURRENT_DATE - INTERVAL '1 day'")->fetchColumn();
$yestOpens = $db->query("SELECT COUNT(*) FROM actions.opens WHERE DATE(action_time) = CURRENT_DATE - INTERVAL '1 day'")->fetchColumn();
$yestLeads = $db->query("SELECT COUNT(*) FROM actions.leads WHERE DATE(action_time) = CURRENT_DATE - INTERVAL '1 day'")->fetchColumn();
$yestUnsubs = $db->query("SELECT COUNT(*) FROM actions.unsubscribes WHERE DATE(action_time) = CURRENT_DATE - INTERVAL '1 day'")->fetchColumn();
$yestBounces = $db->query("SELECT COUNT(*) FROM actions.optouts WHERE DATE(action_time) = CURRENT_DATE - INTERVAL '1 day'")->fetchColumn();
$comparison = [];
$metrics = ['clicks' => [$todayClicks, $yestClicks], 'opens' => [$todayOpens, $yestOpens], 'leads' => [$todayLeads, $yestLeads], 'unsubs' => [$todayUnsubs, $yestUnsubs], 'bounces' => [$todayBounces, $yestBounces]];
foreach ($metrics as $name => $vals) {
@@ -69,11 +69,11 @@ if (isset($_GET['action'])) {
}
if ($_GET['action'] === 'recent_activity') {
$clicks = $db->query("SELECT id, 'click' as action_name, tracking_id as process_id, 0 as vmta_id, COALESCE(click_url,'') as offer_id, event_time FROM admin.v_tracking_clicks ORDER BY event_time DESC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
$opens = $db->query("SELECT id, 'open' as action_name, tracking_id as process_id, 0 as vmta_id, '' as offer_id, event_time FROM admin.v_tracking_opens ORDER BY event_time DESC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
$leads = $db->query("SELECT id, 'lead' as action_name, tracking_id as process_id, 0 as vmta_id, '' as offer_id, event_time FROM admin.v_tracking_leads ORDER BY event_time DESC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
$clicks = $db->query("SELECT id, 'click' as action_name, process_id, vmta_id, offer_production_id as offer_id, action_time FROM actions.clicks ORDER BY action_time DESC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
$opens = $db->query("SELECT id, 'open' as action_name, process_id, vmta_id, offer_production_id as offer_id, action_time FROM actions.opens ORDER BY action_time DESC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
$leads = $db->query("SELECT id, 'lead' as action_name, process_id, vmta_id, offer_production_id as offer_id, action_time FROM actions.leads ORDER BY action_time DESC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
$activities = array_merge($clicks, $opens, $leads);
usort($activities, function($a, $b) { return strtotime($b['event_time']) - strtotime($a['event_time']); });
usort($activities, function($a, $b) { return strtotime($b['action_time']) - strtotime($a['action_time']); });
echo json_encode(['status' => 'success', 'data' => array_slice($activities, 0, 10)]);
exit;
}
@@ -97,10 +97,10 @@ if (isset($_GET['action'])) {
<title>Tracking Dashboard - WEVAL</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#f8fafc;min-height:100vh}.navbar{background:#fff;border-bottom:1px solid #e2e8f0;padding:15px 25px;display:flex;justify-content:space-between;align-items:center}.navbar h1{font-size:22px;color:#1e293b;display:flex;align-items:center;gap:12px}.navbar h1 i{color:#0ea5e9}.navbar-right{display:flex;align-items:center;gap:20px}.status-badge{display:flex;align-items:center;gap:8px;padding:8px 16px;border-radius:20px;font-size:13px;font-weight:600}.status-badge.ok{background:#d1fae5;color:#065f46}.status-badge.error{background:#fee2e2;color:#991b1b}.status-badge .dot{width:10px;height:10px;border-radius:50%}.status-badge.ok .dot{background:#10b981;animation:blink 2s infinite}.status-badge.error .dot{background:#ef4444}@keyframes blink{0%,100%{opacity:1}50%{opacity:.5}}.main-content{padding:25px;max-width:1600px;margin:0 auto}.stats-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:20px;margin-bottom:25px}@media(max-width:1200px){.stats-grid{grid-template-columns:repeat(3,1fr)}}@media(max-width:768px){.stats-grid{grid-template-columns:repeat(2,1fr)}}.stat-card{background:#fff;border-radius:12px;padding:20px;box-shadow:0 2px 8px rgba(0,0,0,.06)}.stat-card .stat-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:12px}.stat-card .stat-icon{width:45px;height:45px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:20px}.stat-card .stat-icon.clicks{background:#dbeafe;color:#2563eb}.stat-card .stat-icon.opens{background:#d1fae5;color:#10b981}.stat-card .stat-icon.leads{background:#fef3c7;color:#f59e0b}.stat-card .stat-icon.unsubs{background:#fee2e2;color:#ef4444}.stat-card .stat-icon.bounces{background:#f3e8ff;color:#9333ea}.stat-card .stat-change{font-size:12px;padding:4px 8px;border-radius:12px;font-weight:600}.stat-card .stat-change.up{background:#d1fae5;color:#065f46}.stat-card .stat-change.down{background:#fee2e2;color:#991b1b}.stat-card .stat-change.neutral{background:#f1f5f9;color:#64748b}.stat-card .stat-value{font-size:32px;font-weight:700;color:#1e293b;margin-bottom:4px}.stat-card .stat-label{color:#64748b;font-size:14px;font-weight:500}.stat-card .stat-sub{color:#94a3b8;font-size:12px;margin-top:8px}.content-grid{display:grid;grid-template-columns:2fr 1fr;gap:25px}@media(max-width:1024px){.content-grid{grid-template-columns:1fr}}.card{background:#fff;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.06);overflow:hidden}.card-header{padding:18px 20px;border-bottom:1px solid #f1f5f9;display:flex;justify-content:space-between;align-items:center}.card-header h3{font-size:16px;color:#1e293b;display:flex;align-items:center;gap:10px}.card-header h3 i{color:#0ea5e9}.card-body{padding:20px}.period-selector{display:flex;gap:8px}.period-btn{padding:6px 14px;border-radius:6px;font-size:13px;font-weight:500;border:1px solid #e2e8f0;background:#fff;color:#64748b;cursor:pointer}.period-btn:hover{border-color:#0ea5e9;color:#0ea5e9}.period-btn.active{background:#0ea5e9;color:#fff;border-color:#0ea5e9}.chart-container{height:300px;margin-top:15px}.quick-action{display:flex;align-items:center;gap:12px;padding:14px;border-radius:10px;text-decoration:none;color:#374151;background:#f8fafc;margin-bottom:10px;border:1px solid transparent}.quick-action:hover{background:#f1f5f9;border-color:#0ea5e9}.quick-action i{width:40px;height:40px;border-radius:10px;background:#dbeafe;color:#2563eb;display:flex;align-items:center;justify-content:center}.quick-action span{font-weight:500}.activity-item{display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid #f1f5f9}.activity-item:last-child{border-bottom:none}.activity-icon{width:36px;height:36px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:14px}.activity-icon.click{background:#dbeafe;color:#2563eb}.activity-icon.open{background:#d1fae5;color:#10b981}.activity-icon.lead{background:#fef3c7;color:#f59e0b}.activity-info{flex:1}.activity-info .type{font-weight:600;color:#1e293b;font-size:13px;text-transform:capitalize}.activity-info .details{color:#64748b;font-size:12px}.activity-time{color:#94a3b8;font-size:11px}.global-section{margin-top:25px}.global-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:15px}@media(max-width:900px){.global-grid{grid-template-columns:repeat(3,1fr)}}.global-stat{text-align:center;padding:20px;background:#f8fafc;border-radius:10px}.global-stat .value{font-size:28px;font-weight:700;margin-bottom:5px}.global-stat .value.clicks{color:#2563eb}.global-stat .value.opens{color:#10b981}.global-stat .value.leads{color:#f59e0b}.global-stat .value.unsubs{color:#ef4444}.global-stat .value.bounces{color:#9333ea}.global-stat .label{color:#64748b;font-size:13px}.loading{color:#94a3b8;text-align:center;padding:40px}
</style>
<link rel="stylesheet" href="wevads-global.css?v1770777318">
<link rel="stylesheet" href="tracking-clean.css">
</head>
<body>
<nav class="navbar"><h1><i class="fa fa-chart-line"></i> Tracking Dashboard</h1><div class="navbar-right"><div class="status-badge" id="system-status"><span class="dot"></span><span id="status-text">...</span></div><a href="<?php echo $base_url; ?>" style="color:#64748b;text-decoration:none"><i class="fa fa-arrow-left"></i> Back</a></div></nav>
@@ -128,11 +128,10 @@ if (isset($_GET['action'])) {
function loadToday(){$.get('?action=today_comparison',function(r){if(r.status!=='success')return;['clicks','opens','leads','unsubs','bounces'].forEach(function(m){var d=r.data[m];$('#'+m+'-value').text(fmt(d.today));$('#'+m+'-yesterday').text('vs yesterday: '+fmt(d.yesterday));var el=$('#'+m+'-change').removeClass('up down neutral');if(d.diff>0)el.addClass('up').text('+'+d.pct+'%');else if(d.diff<0)el.addClass('down').text(d.pct+'%');else el.addClass('neutral').text('0%')})})}
function loadDaily(days){$.get('?action=daily_stats&days='+days,function(r){if(r.status!=='success')return;var labels=r.data.map(function(d){return d.label});var ds=[{label:'Clicks',data:r.data.map(function(d){return d.clicks}),borderColor:'#2563eb',backgroundColor:'rgba(37,99,235,0.1)',tension:0.3,fill:true},{label:'Opens',data:r.data.map(function(d){return d.opens}),borderColor:'#10b981',backgroundColor:'rgba(16,185,129,0.1)',tension:0.3,fill:true},{label:'Leads',data:r.data.map(function(d){return d.leads}),borderColor:'#f59e0b',backgroundColor:'rgba(245,158,11,0.1)',tension:0.3,fill:true}];if(dailyChart){dailyChart.data.labels=labels;dailyChart.data.datasets=ds;dailyChart.update()}else{dailyChart=new Chart(document.getElementById('dailyChart').getContext('2d'),{type:'line',data:{labels:labels,datasets:ds},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'top'}},scales:{y:{beginAtZero:true},x:{}}}})}})}
function loadGlobal(p){$.get('?action=global_stats&period='+p,function(r){if(r.status!=='success')return;$('#global-clicks').text(fmt(r.data.clicks));$('#global-opens').text(fmt(r.data.opens));$('#global-leads').text(fmt(r.data.leads));$('#global-unsubs').text(fmt(r.data.unsubs));$('#global-bounces').text(fmt(r.data.bounces))})}
function loadActivity(){$.get('?action=recent_activity',function(r){if(r.status!=='success')return;var html='';if(r.data.length===0){html='<div class="loading">No activity</div>'}else{r.data.forEach(function(i){var ic=i.action_name==='click'?'click':(i.action_name==='open'?'open':'lead');var ico=ic==='click'?'fa-mouse-pointer':(ic==='open'?'fa-envelope-open':'fa-user-plus');html+='<div class="activity-item"><div class="activity-icon '+ic+'"><i class="fa '+ico+'"></i></div><div class="activity-info"><div class="type">'+i.action_name+'</div><div class="details">Process: '+i.process_id+'</div></div><div class="activity-time">'+new Date(i.event_time).toLocaleTimeString()+'</div></div>'})}$('#activity-feed').html(html)})}
function loadActivity(){$.get('?action=recent_activity',function(r){if(r.status!=='success')return;var html='';if(r.data.length===0){html='<div class="loading">No activity</div>'}else{r.data.forEach(function(i){var ic=i.action_name==='click'?'click':(i.action_name==='open'?'open':'lead');var ico=ic==='click'?'fa-mouse-pointer':(ic==='open'?'fa-envelope-open':'fa-user-plus');html+='<div class="activity-item"><div class="activity-icon '+ic+'"><i class="fa '+ico+'"></i></div><div class="activity-info"><div class="type">'+i.action_name+'</div><div class="details">Process: '+i.process_id+'</div></div><div class="activity-time">'+new Date(i.action_time).toLocaleTimeString()+'</div></div>'})}$('#activity-feed').html(html)})}
$(document).ready(function(){loadStatus();loadToday();loadDaily(14);loadGlobal('month');loadActivity();$('.period-btn:not(.global-btn)').click(function(){$(this).siblings().removeClass('active');$(this).addClass('active');loadDaily($(this).data('days'))});$('.global-btn').click(function(){$(this).siblings().removeClass('active');$(this).addClass('active');loadGlobal($(this).data('period'))});setInterval(function(){loadStatus();loadToday();loadActivity()},30000)});
</script>
<?php include("includes/chatbot-widget.php"); ?>
<script src="arsenal-common.js?v1770778169"></script>
</body>
</html>

View File

@@ -198,10 +198,43 @@ echo "DEPLOY_SUCCESS"';
<title>WEVAL | Tracking Deployment</title>
<link href="plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="plugins/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<style>
body { background: #0a0e17; color: #f1f5f9; font-family: 'Segoe UI', sans-serif; }
.container-main { max-width: 1300px; margin: 30px auto; padding: 20px; }
.card-dark { background: #1a2332; border: 1px solid #2a3a50; border-radius: 12px; margin-bottom: 20px; }
.card-dark .card-header { background: #111827; border-bottom: 1px solid #2a3a50; padding: 15px 20px; font-weight: 600; }
.card-dark .card-body { padding: 20px; }
.form-control-dark { background: #0d1219; border: 1px solid #2a3a50; color: #fff; border-radius: 8px; padding: 10px 15px; width: 100%; }
.form-control-dark:focus { border-color: #00e5ff; outline: none; background: #0d1219; color: #fff; }
.btn-cyber { padding: 10px 20px; border-radius: 8px; font-weight: 600; border: none; cursor: pointer; transition: all 0.2s; }
.btn-cyber:hover { transform: translateY(-2px); }
.btn-primary-cyber { background: linear-gradient(135deg, #00e5ff, #0891b2); color: #000; }
.btn-success-cyber { background: linear-gradient(135deg, #10b981, #059669); color: #fff; }
.status-badge { padding: 8px 16px; border-radius: 20px; font-weight: 600; font-size: 14px; display: inline-block; }
.status-active { background: rgba(16,185,129,0.2); color: #10b981; border: 1px solid #10b981; }
.status-warming { background: rgba(245,158,11,0.2); color: #f59e0b; border: 1px solid #f59e0b; }
.status-restricted { background: rgba(239,68,68,0.2); color: #ef4444; border: 1px solid #ef4444; }
.status-online { color: #10b981; }
.status-offline { color: #ef4444; }
.score-box { font-size: 56px; font-weight: 700; font-family: 'Courier New', monospace; }
.info-box { background: rgba(0,229,255,0.1); border: 1px solid rgba(0,229,255,0.3); border-radius: 8px; padding: 15px; margin-bottom: 20px; }
.warning-box { background: rgba(245,158,11,0.1); border: 1px solid rgba(245,158,11,0.3); border-radius: 8px; padding: 15px; }
.log-box { background: #0d1219; border: 1px solid #2a3a50; border-radius: 8px; padding: 15px; font-family: monospace; font-size: 13px; max-height: 250px; overflow-y: auto; }
h1 { color: #00e5ff; margin-bottom: 5px; }
label { color: #94a3b8; font-size: 13px; margin-bottom: 5px; display: block; }
.rule-box { background: #111827; border-radius: 8px; padding: 15px; margin-top: 15px; }
.rule-item { display: flex; align-items: center; padding: 8px 0; border-bottom: 1px solid #2a3a50; }
.rule-item:last-child { border-bottom: none; }
.rule-score { width: 100px; font-weight: 600; }
.rule-status { width: 120px; }
.rule-action { flex: 1; color: #94a3b8; font-size: 13px; }
.warming-plan { margin-top: 15px; }
.warming-plan table { width: 100%; font-size: 13px; }
.warming-plan th { color: #64748b; font-weight: 500; padding: 8px; text-align: left; }
.warming-plan td { padding: 8px; border-top: 1px solid #2a3a50; }
.form-group { margin-bottom: 15px; }
</style>
<link rel="stylesheet" href="wevads-global.css?v1770777318">
<link rel="stylesheet" href="tracking-clean.css">
</head>
<body>
<div class="container-main">
@@ -394,12 +427,11 @@ function deployServer() {
$(document).ready(function() {
testCurrent();
// Auto-check consent.wevup.app
$('#check-domain').val('consent.wevup.app');
// Auto-check culturellemejean.charity
$('#check-domain').val('culturellemejean.charity');
checkDomain();
});
</script>
<script src="arsenal-common.js?v1770778169"></script>
</body>
</html>

View File

@@ -30,7 +30,7 @@ foreach($tables as $table) {
# Get recent actions from all apps
$recent_actions = [];
try {
$stmt = $pdo->query("SELECT 'OPEN' as type, created_at, process_id, client_id FROM admin.v_tracking_opens ORDER BY created_at DESC LIMIT 100");
$stmt = $pdo->query("SELECT 'OPEN' as type, created_at, process_id, client_id FROM actions.opens ORDER BY created_at DESC LIMIT 100");
$recent_actions = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch(Exception $e) {
$recent_actions = [];
@@ -42,10 +42,42 @@ try {
<title>📊 GLOBAL Tracking Dashboard</title>
<meta charset="utf-8">
<meta http-equiv="refresh" content="30">
<style>
body { font-family: Arial, sans-serif; background: linear-gradient(135deg, #0a0e27 0%, #1a1a3e 100%); color: #fff; padding: 30px; margin: 0; }
.container { max-width: 1600px; margin: 0 auto; }
h1 { color: #00d4ff; font-size: 36px; margin: 0 0 10px 0; text-shadow: 0 0 20px rgba(0, 212, 255, 0.5); }
.info { color: #90caf9; font-size: 14px; margin-bottom: 30px; }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 20px; margin-bottom: 40px; }
.stat { background: linear-gradient(135deg, #16213e 0%, #0f3460 100%); padding: 25px; border-radius: 12px; border-left: 5px solid #00d4ff; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); }
.stat.total { grid-column: 1 / -1; border-left-color: #00d4ff; }
.stat-num { font-size: 42px; font-weight: bold; color: #00d4ff; margin-bottom: 10px; }
.stat.opens .stat-num { color: #4caf50; }
.stat.clicks .stat-num { color: #ff9800; }
.stat.leads .stat-num { color: #ff4081; }
.stat.unsubscribes .stat-num { color: #f44336; }
.stat.optouts .stat-num { color: #9c27b0; }
.stat.opens { border-left-color: #4caf50; }
.stat.clicks { border-left-color: #ff9800; }
.stat.leads { border-left-color: #ff4081; }
.stat.unsubscribes { border-left-color: #f44336; }
.stat.optouts { border-left-color: #9c27b0; }
.stat-label { font-size: 14px; color: #b0bec5; text-transform: uppercase; letter-spacing: 1px; }
.section { background: linear-gradient(135deg, #16213e 0%, #0f3460 100%); padding: 30px; border-radius: 12px; margin-bottom: 30px; border-left: 5px solid #00d4ff; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); }
.section h2 { color: #00d4ff; font-size: 20px; margin-bottom: 20px; text-transform: uppercase; letter-spacing: 1px; }
.apps { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 30px; }
.app-link { background: linear-gradient(135deg, #16213e 0%, #0f3460 100%); padding: 20px; border-radius: 10px; text-align: center; border: 2px solid #00d4ff; cursor: pointer; transition: all 0.3s; text-decoration: none; color: #fff; }
.app-link:hover { transform: translateY(-5px); box-shadow: 0 10px 30px rgba(0, 212, 255, 0.3); border-color: #00d4ff; }
.app-link.wevads { border-color: #00d4ff; }
.app-link.fmg { border-color: #4caf50; }
.app-link.bcg { border-color: #ff9800; }
.app-icon { font-size: 28px; margin-bottom: 10px; }
.app-name { font-size: 16px; font-weight: bold; margin-bottom: 5px; }
.logs-container { max-height: 500px; overflow-y: auto; background: rgba(0, 0, 0, 0.2); padding: 15px; border-radius: 8px; }
.log-item { background: rgba(15, 15, 35, 0.8); padding: 10px; margin-bottom: 8px; border-radius: 6px; font-size: 11px; font-family: monospace; border-left: 3px solid #4caf50; display: flex; justify-content: space-between; }
.log-time { color: #90caf9; margin: 0 10px; }
.refresh-info { text-align: center; color: #90caf9; font-size: 12px; margin-top: 30px; }
</style>
<link rel="stylesheet" href="wevads-global.css?v1770777318">
<link rel="stylesheet" href="tracking-clean.css">
</head>
<body>
<div class="container">
@@ -121,6 +153,5 @@ try {
</div>
<?php include("includes/chatbot-widget.php"); ?>
<script src="arsenal-common.js?v1770778169"></script>
</body>
</html>

View File

@@ -11,10 +11,37 @@ try { $stats['unsubs'] = $pdo->query("SELECT COUNT(*) FROM actions.unsubscribes"
<meta charset="UTF-8">
<title>Tracking Manager - Opens, Clicks, Leads</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:system-ui;background:#0a0a0f;color:#e2e8f0;min-height:100vh;padding:20px}
h1{color:#f59e0b;margin-bottom:10px}
.back-link{color:#5eead4;text-decoration:none;display:inline-block;margin-bottom:20px}
.process-box{background:#12121a;border-radius:12px;padding:20px;margin-bottom:20px;border:1px solid #333}
.process-title{color:#f59e0b;margin-bottom:15px;font-size:1.1rem}
.process-flow{display:flex;flex-wrap:wrap;gap:15px;align-items:center;justify-content:center}
.process-step{background:#1a1a2e;border:2px solid #333;border-radius:10px;padding:15px;min-width:130px;text-align:center}
.process-step .icon{font-size:1.8rem;margin-bottom:8px}
.process-step .num{width:24px;height:24px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:0.8rem;font-weight:bold;margin-bottom:5px}
.process-step .title{font-weight:bold;font-size:0.85rem;margin-bottom:5px}
.process-step .desc{font-size:0.7rem;opacity:0.8}
.process-arrow{color:#f59e0b;font-size:1.5rem}
.summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:15px;margin-bottom:25px}
.summary-card{background:#12121a;padding:20px;border-radius:12px;text-align:center}
.summary-card:nth-child(1){border-left:4px solid #3b82f6}
.summary-card:nth-child(2){border-left:4px solid #f59e0b}
.summary-card:nth-child(3){border-left:4px solid #10b981}
.summary-card:nth-child(4){border-left:4px solid #ef4444}
.summary-val{font-size:2rem;font-weight:bold}
.summary-card:nth-child(1) .summary-val{color:#3b82f6}
.summary-card:nth-child(2) .summary-val{color:#f59e0b}
.summary-card:nth-child(3) .summary-val{color:#10b981}
.summary-card:nth-child(4) .summary-val{color:#ef4444}
.summary-lbl{font-size:0.85rem;opacity:0.7}
.info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:15px;margin-top:15px;font-size:0.85rem}
.info-grid div{background:#1a1a2e;padding:12px;border-radius:8px}
.panel{background:#12121a;border-radius:12px;padding:20px;margin-top:20px}
</style>
<link rel="stylesheet" href="wevads-global.css?v1770777318">
<link rel="stylesheet" href="tracking-clean.css">
</head><body>
<a href="/system-flows.php" class="back-link"><i class="fas fa-arrow-left"></i> Retour aux System Flows</a>
@@ -55,5 +82,4 @@ try { $stats['unsubs'] = $pdo->query("SELECT COUNT(*) FROM actions.unsubscribes"
<p><strong>Domains:</strong> Tracking domains configurés pour redirections</p>
</div>
<script src="arsenal-common.js?v1770778169"></script>
</body></html>

View File

@@ -1,297 +1,352 @@
<?php
/**
* WEVADS Tracking System - E2E Health Check v2.0
* Vérifie tous les composants du tracking et retourne un statut global
* Serveur: 89.167.40.150:5821
* Mis à jour: 28 Février 2026
*/
session_start();
$base_url = 'http://89.167.40.150:5821';
$tracking_ip = '151.80.235.110';
$tracking_ssh_pass = 'MX8D3zSAty7k3243242';
$wevads_api = 'http://89.167.40.150:5821/api.json';
header('Content-Type: text/html; charset=utf-8');
// ═══════════ CONFIGURATION ═══════════
define('TRACKING_SERVER_IP', '151.80.235.110');
define('TRACKING_DOMAIN', 'culturellemejean.charity');
define('TRACKING_SSH_USER', 'ubuntu');
define('TRACKING_SSH_PASS', 'MX8D3zSAty7k3243242');
define('WEVADS_SERVER', '89.167.40.150');
define('WEVADS_PORT', '5821');
// DB connection (local)
function getDB() {
static $db = null;
if (!$db) {
$db = @pg_connect("host=/var/run/postgresql dbname=adx_system user=admin password=admin123");
if (!$db) $db = @pg_connect("host=127.0.0.1 dbname=adx_system user=admin password=admin123");
}
return $db;
}
// ═══════════ API ENDPOINT ═══════════
if (isset($_GET['api']) && $_GET['api'] === 'status') {
// Metrics
if (isset($_GET['action']) && $_GET['action'] === 'metrics') {
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$results = runAllChecks();
$criticalFailed = false;
$totalPassed = 0;
$totalChecks = count($results);
foreach ($results as $r) {
if ($r['status']) $totalPassed++;
if (!$r['status'] && ($r['critical'] ?? false)) $criticalFailed = true;
$cpuLoad = sys_getloadavg();
$cpuPercent = round(($cpuLoad[0] / intval(shell_exec("nproc"))) * 100, 1);
$memInfo = file_get_contents('/proc/meminfo');
preg_match('/MemTotal:\s+(\d+)/', $memInfo, $memTotal);
preg_match('/MemAvailable:\s+(\d+)/', $memInfo, $memAvailable);
$memPercent = round((($memTotal[1] - $memAvailable[1]) / $memTotal[1]) * 100, 1);
$diskPercent = round(((disk_total_space('/') - disk_free_space('/')) / disk_total_space('/')) * 100, 1);
$start = microtime(true);
$ch = curl_init(); curl_setopt_array($ch, [CURLOPT_URL => "http://{$tracking_ip}/", CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5]); curl_exec($ch); $trackingCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
$trackingTime = round((microtime(true) - $start) * 1000);
$trackingMetrics = ['cpu' => 0, 'mem' => 0, 'disk' => 0];
$sshCmd = "sshpass -p '{$tracking_ssh_pass}' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=3 ubuntu@{$tracking_ip} \"cat /proc/loadavg && free -m && df -h /\" 2>/dev/null";
$sshOutput = shell_exec($sshCmd);
if ($sshOutput) {
$lines = explode("\n", $sshOutput);
if (isset($lines[0])) { $loadParts = explode(' ', $lines[0]); $trackingMetrics['cpu'] = round(floatval($loadParts[0]) * 100 / 2, 1); }
foreach ($lines as $line) {
if (strpos($line, 'Mem:') !== false) { $parts = preg_split('/\s+/', trim($line)); if (count($parts) >= 3) $trackingMetrics['mem'] = round(($parts[2] / $parts[1]) * 100, 1); }
if (strpos($line, '/dev/') !== false && strpos($line, '%') !== false) { preg_match('/(\d+)%/', $line, $diskMatch); if (isset($diskMatch[1])) $trackingMetrics['disk'] = intval($diskMatch[1]); }
}
}
// Green if all critical pass, yellow if non-critical fail, red if critical fail
$indicator = $criticalFailed ? 'red' : ($totalPassed < $totalChecks ? 'yellow' : 'green');
echo json_encode([
'status' => $criticalFailed ? 'unhealthy' : 'healthy',
'indicator' => $indicator,
'passed' => $totalPassed,
'total' => $totalChecks,
'timestamp' => date('c'),
'checks' => $results
], JSON_PRETTY_PRINT);
echo json_encode(['wevads' => ['cpu' => $cpuPercent, 'mem' => $memPercent, 'disk' => $diskPercent], 'tracking' => ['cpu' => $trackingMetrics['cpu'], 'mem' => $trackingMetrics['mem'], 'disk' => $trackingMetrics['disk'], 'response_time' => $trackingTime, 'status' => $trackingCode > 0 ? 'online' : 'offline']]);
exit;
}
// ═══════════ CHECKS ═══════════
function runAllChecks() {
return [
checkDatabase(),
checkTrackingTCP(),
checkTrackingHTTPS(),
checkOpenPixel(),
checkClickRedirect(),
checkTrackingBridge(),
checkConversionsCron(),
checkS3Redirect(),
checkTrackingStats(),
];
// Generate Click - INSERT with geolocation
if (isset($_GET['action']) && $_GET['action'] === 'generate_click') {
header('Content-Type: application/json');
try {
$db = new PDO('pgsql:host=localhost;dbname=adx_system', 'admin', 'admin123');
$stmt = $db->query("SELECT id, name, affiliate_network_id FROM affiliate.offers WHERE status = 'Activated' ORDER BY RANDOM() LIMIT 1");
$offer = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$offer) { echo json_encode(['success' => false, 'message' => 'No active offers']); exit; }
$stmt2 = $db->query("SELECT id FROM production.mta_processes WHERE status = 'Completed' ORDER BY id DESC LIMIT 1");
$process = $stmt2->fetch(PDO::FETCH_ASSOC);
$processId = $process ? $process['id'] : 999;
$stmtId = $db->query("SELECT COALESCE(MAX(id), 0) + 1 FROM actions.clicks");
$nextId = $stmtId->fetchColumn();
$locations = [['US', 'United States', 'New York', 'New York City'], ['DE', 'Germany', 'Bavaria', 'Munich'], ['FR', 'France', 'Ile-de-France', 'Paris'], ['GB', 'United Kingdom', 'England', 'London'], ['SE', 'Sweden', 'Stockholm', 'Stockholm'], ['MA', 'Morocco', 'Casablanca-Settat', 'Casablanca'], ['ES', 'Spain', 'Madrid', 'Madrid'], ['IT', 'Italy', 'Lazio', 'Rome']];
$loc = $locations[rand(0, count($locations)-1)];
$browsers = ['Google Chrome', 'Firefox', 'Safari', 'Edge'];
$devices = ['Computer', 'Mobile', 'Tablet'];
$ips = ['45.'.rand(1,255).'.'.rand(1,255).'.'.rand(1,255), '92.'.rand(1,255).'.'.rand(1,255).'.'.rand(1,255)];
$insert = $db->prepare("INSERT INTO actions.clicks (id, unique_token, process_id, process_type, user_production_id, user_full_name, vmta_id, smtp_user_id, affiliate_network_id, offer_production_id, list_id, client_id, action_time, process_updated, agent, action_ip, country_code, country, region, city, device_type, device_name, operating_system, browser_name, browser_version) VALUES (?, ?, ?, 'md', 1, 'Simulated User', ?, 0, ?, ?, 0, 0, NOW(), false, 'Mozilla/5.0', ?, ?, ?, ?, ?, ?, 'PC', 'Windows 10.0', ?, '120.0')");
$insert->execute([$nextId, $processId.rand(1000,9999), $processId, rand(1,10), $offer['affiliate_network_id'] ?? 1, $offer['id'], $ips[rand(0,1)], $loc[0], $loc[1], $loc[2], $loc[3], $devices[rand(0,2)], $browsers[rand(0,3)]]);
echo json_encode(['success' => true, 'message' => 'Click generated!', 'offer_id' => $offer['id'], 'offer_name' => $offer['name'], 'location' => $loc[3].', '.$loc[1], 'click_id' => $nextId]);
} catch (Exception $e) { echo json_encode(['success' => false, 'message' => $e->getMessage()]); }
exit;
}
function checkDatabase() {
$start = microtime(true);
$db = getDB();
if (!$db) {
return ['name' => 'PostgreSQL Database', 'status' => false, 'critical' => true,
'message' => 'Connection failed', 'time_ms' => ms($start)];
// Recent activity
if (isset($_GET['action']) && $_GET['action'] === 'recent_activity') {
header('Content-Type: application/json');
try {
$db = new PDO('pgsql:host=localhost;dbname=adx_system', 'admin', 'admin123');
$stmt = $db->query("SELECT id, action_time, offer_production_id as offer_id, process_id, country, city, device_type, browser_name, action_ip FROM actions.clicks ORDER BY action_time DESC LIMIT 15");
echo json_encode(['success' => true, 'clicks' => $stmt->fetchAll(PDO::FETCH_ASSOC)]);
} catch (Exception $e) { echo json_encode(['success' => false, 'message' => $e->getMessage()]); }
exit;
}
// Repair actions
if (isset($_GET['action']) && $_GET['action'] === 'repair') {
header('Content-Type: application/json');
$type = $_POST['type'] ?? '';
switch ($type) {
case 'tracking_server': $output = shell_exec("sshpass -p '{$tracking_ssh_pass}' ssh -o StrictHostKeyChecking=no ubuntu@{$tracking_ip} 'sudo systemctl restart nginx php8.1-fpm' 2>&1"); echo json_encode(['success' => true, 'message' => 'Nginx restarted', 'output' => $output]); break;
case 'wevads_api': shell_exec('sudo systemctl restart apache2'); echo json_encode(['success' => true, 'message' => 'Apache restarted']); break;
case 'database': shell_exec('sudo systemctl restart postgresql'); echo json_encode(['success' => true, 'message' => 'PostgreSQL restarted']); break;
case 'e2e_click': $output = shell_exec("sshpass -p '{$tracking_ssh_pass}' ssh -o StrictHostKeyChecking=no ubuntu@{$tracking_ip} 'tail -20 /var/log/nginx/error.log' 2>&1"); echo json_encode(['success' => true, 'message' => 'Logs', 'output' => $output]); break;
case 's3_bucket': echo json_encode(['success' => true, 'message' => 'Check AWS Console', 'url' => 'https://s3.console.aws.amazon.com/']); break;
default: echo json_encode(['success' => false, 'message' => 'Unknown']);
}
$r = pg_fetch_assoc(pg_query($db, "SELECT COUNT(*) as cnt FROM affiliate.offers WHERE status='Activated'"));
$offers = $r['cnt'] ?? 0;
$r2 = pg_fetch_assoc(pg_query($db, "SELECT COUNT(*) as cnt FROM admin.unified_tracking"));
$events = $r2['cnt'] ?? 0;
return ['name' => 'PostgreSQL Database', 'status' => true, 'critical' => true,
'message' => "$offers offres actives, $events tracking events", 'time_ms' => ms($start)];
exit;
}
function checkTrackingTCP() {
$start = microtime(true);
$p80 = @fsockopen(TRACKING_SERVER_IP, 80, $en, $es, 3);
$p443 = @fsockopen(TRACKING_SERVER_IP, 443, $en, $es, 3);
$ok80 = $p80 ? true : false;
$ok443 = $p443 ? true : false;
if ($p80) fclose($p80);
if ($p443) fclose($p443);
$both = $ok80 && $ok443;
return ['name' => 'Tracking Server TCP (80+443)', 'status' => $both, 'critical' => true,
'message' => 'Port 80: ' . ($ok80?'✅':'❌') . ' | Port 443: ' . ($ok443?'✅':'❌') . ' (' . TRACKING_SERVER_IP . ')',
'time_ms' => ms($start)];
}
function checkTrackingHTTPS() {
$start = microtime(true);
$ch = curl_init('https://' . TRACKING_DOMAIN . '/op/1_md/1/1/1/0/0');
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 8, CURLOPT_FOLLOWLOCATION => false, CURLOPT_NOBODY => true]);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$ssl = curl_getinfo($ch, CURLINFO_SSL_VERIFYRESULT);
$err = curl_error($ch);
curl_close($ch);
$ok = $code > 0 && $code < 500;
return ['name' => 'HTTPS via Cloudflare (' . TRACKING_DOMAIN . ')', 'status' => $ok, 'critical' => true,
'message' => $ok ? "HTTP $code, SSL OK" : "Erreur: $err (HTTP $code)", 'time_ms' => ms($start)];
}
function checkOpenPixel() {
$start = microtime(true);
$ch = curl_init('https://' . TRACKING_DOMAIN . '/op/1_md/1/1/1/0/0');
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, CURLOPT_HEADER => true]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$ct = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);
$isGif = ($code == 200 && strpos($ct, 'image') !== false);
return ['name' => 'Open Pixel (1x1 GIF)', 'status' => $isGif, 'critical' => true,
'message' => $isGif ? "HTTP 200, Content-Type: $ct" : "HTTP $code, Content-Type: $ct", 'time_ms' => ms($start)];
}
function checkClickRedirect() {
$start = microtime(true);
$ch = curl_init('https://' . TRACKING_DOMAIN . '/cl/1_md/1/1/1/0/0');
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 12, CURLOPT_FOLLOWLOCATION => false]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Check if redirect contains offer link (e36lbat.com) or any redirect
$hasRedirect = (strpos($resp, 'window.location') !== false || strpos($resp, 'http-equiv="refresh"') !== false);
$hasOffer = (strpos($resp, 'e36lbat.com') !== false || strpos($resp, 'rivoweb') !== false);
$toGoogle = (strpos($resp, 'google.com') !== false);
if ($toGoogle) {
return ['name' => 'Click Redirect', 'status' => false, 'critical' => true,
'message' => "⚠️ Redirige vers Google! Vérifier offer_id/vmta_id", 'time_ms' => ms($start)];
}
$ok = $hasRedirect || $code == 302;
return ['name' => 'Click Redirect', 'status' => $ok, 'critical' => true,
'message' => $ok ? "HTTP $code, redirect " . ($hasOffer ? "→ offre sponsor ✅" : "détecté") : "HTTP $code, pas de redirect",
'time_ms' => ms($start)];
}
function checkTrackingBridge() {
$start = microtime(true);
$ch = curl_init('http://127.0.0.1:' . WEVADS_PORT . '/tracking-bridge.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'controller' => 'Tracking', 'action' => 'getAdxRtl',
'parameters' => ['type'=>'preview','process-id'=>'1','process-type'=>'md','user-id'=>'1',
'vmta-id'=>'1','offer-id'=>'9','list-id'=>'0','client-id'=>'0','ip'=>'127.0.0.1']
]),
CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10
]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($resp, true);
$ok = ($data && isset($data['status']) && $data['status'] == 200);
$link = $data['data']['link'] ?? '';
return ['name' => 'API tracking-bridge.php', 'status' => $ok, 'critical' => true,
'message' => $ok ? "API OK, lien: " . substr($link, 0, 60) . "..." : "HTTP $code" . ($data['message'] ?? substr($resp,0,80)),
'time_ms' => ms($start)];
}
function checkConversionsCron() {
$start = microtime(true);
$crons = shell_exec('crontab -l 2>/dev/null | grep -i "conversion" | grep -v "^#"');
$active = !empty(trim($crons));
return ['name' => 'Conversions Collector (Cron)', 'status' => $active, 'critical' => false,
'message' => $active ? "Cron actif: " . trim($crons) : "Aucun cron conversion trouvé",
'time_ms' => ms($start)];
}
function checkS3Redirect() {
$start = microtime(true);
// Check if AmazonCloud.php has correct tracking IP
$file = '/opt/wevads/app/libraries/AmazonCloud.php';
if (!file_exists($file)) {
return ['name' => 'S3 Redirect Config', 'status' => false, 'critical' => false,
'message' => "AmazonCloud.php non trouvé", 'time_ms' => ms($start)];
}
$content = file_get_contents($file);
$hasIP = (strpos($content, TRACKING_SERVER_IP) !== false);
$isHTTPS = (strpos($content, 'https://' . TRACKING_DOMAIN) !== false);
$mode = $isHTTPS ? 'HTTPS (domaine)' : ($hasIP ? 'HTTP (IP directe)' : 'IP inconnue');
return ['name' => 'S3 Redirect Config', 'status' => ($hasIP || $isHTTPS), 'critical' => false,
'message' => "Mode: $mode", 'time_ms' => ms($start)];
}
function checkTrackingStats() {
$start = microtime(true);
$db = getDB();
if (!$db) return ['name' => 'Tracking Stats (24h)', 'status' => false, 'critical' => false, 'message' => 'DB error', 'time_ms' => ms($start)];
// Diagnose
if (isset($_GET['action']) && $_GET['action'] === 'diagnose') {
header('Content-Type: application/json');
$results = ['timestamp' => date('Y-m-d H:i:s'), 'overall_status' => 'ok', 'checks' => []];
$stats = [];
foreach (['open'=>'opens','click'=>'clicks','unsubscribe'=>'unsubscribes'] as $type => $label) {
$r = pg_fetch_assoc(pg_query($db, "SELECT COUNT(*) as cnt FROM admin.unified_tracking WHERE event_type='$type' AND created_at > NOW() - INTERVAL '24 hours'"));
$stats[$label] = $r['cnt'] ?? 0;
}
$r = pg_fetch_assoc(pg_query($db, "SELECT COUNT(*) as cnt FROM actions.leads WHERE action_time > NOW() - INTERVAL '24 hours'"));
$stats['leads'] = $r['cnt'] ?? 0;
$ch = curl_init(); curl_setopt_array($ch, [CURLOPT_URL => "http://{$tracking_ip}/", CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10]); curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
$results['checks']['tracking_server'] = ['name' => 'Tracking Server', 'status' => $code > 0 ? 'ok' : 'error', 'message' => "HTTP {$code}", 'repair' => 'tracking_server', 'repair_label' => 'Restart Nginx'];
$total = array_sum($stats);
$msg = implode(' | ', array_map(fn($k,$v) => "$k: $v", array_keys($stats), array_values($stats)));
return ['name' => 'Tracking Stats (24h)', 'status' => true, 'critical' => false,
'message' => $msg . " (total: $total)", 'time_ms' => ms($start)];
$ch = curl_init(); curl_setopt_array($ch, [CURLOPT_URL => $wevads_api, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query(['controller' => 'Tracking', 'action' => 'getAdxRtl', 'parameters' => []])]); $apiResponse = curl_exec($ch); $apiCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
$results['checks']['wevads_api'] = ['name' => 'WEVAL API', 'status' => ($apiCode == 200 && json_decode($apiResponse)) ? 'ok' : 'error', 'message' => "HTTP {$apiCode}", 'repair' => 'wevads_api', 'repair_label' => 'Restart Apache'];
try { $db = new PDO('pgsql:host=localhost;dbname=adx_system', 'admin', 'admin123'); $stmt = $db->query("SELECT COUNT(*) FROM actions.clicks WHERE action_time > NOW() - INTERVAL '24 hours'"); $results['checks']['database'] = ['name' => 'Database', 'status' => 'ok', 'message' => $stmt->fetchColumn().' clicks/24h', 'repair' => 'database', 'repair_label' => 'Restart PostgreSQL']; } catch (Exception $e) { $results['checks']['database'] = ['name' => 'Database', 'status' => 'error', 'message' => 'Failed', 'repair' => 'database', 'repair_label' => 'Restart PostgreSQL']; }
$ch = curl_init(); curl_setopt_array($ch, [CURLOPT_URL => "http://{$tracking_ip}/cl/999_md/1/1/6/0/0", CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15]); $e2eResponse = curl_exec($ch); $e2eCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
$results['checks']['e2e_click'] = ['name' => 'E2E Click', 'status' => ($e2eCode == 200 && strlen($e2eResponse) > 10) ? 'ok' : 'warning', 'message' => "HTTP {$e2eCode}", 'repair' => 'e2e_click', 'repair_label' => 'View Logs'];
$ch = curl_init(); curl_setopt_array($ch, [CURLOPT_URL => "https://ff6970ea5261c1fb821273.s3.eu-north-1.amazonaws.com/", CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, CURLOPT_NOBODY => true]); curl_exec($ch); $s3Code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
$results['checks']['s3_bucket'] = ['name' => 'S3 Bucket', 'status' => ($s3Code > 0 && $s3Code < 500) ? 'ok' : 'error', 'message' => "HTTP {$s3Code}", 'repair' => 's3_bucket', 'repair_label' => 'Check AWS'];
foreach ($results['checks'] as $c) { if ($c['status'] === 'error') $results['overall_status'] = 'error'; }
echo json_encode($results);
exit;
}
function ms($start) { return round((microtime(true) - $start) * 1000, 1); }
// ═══════════ RUN ═══════════
$results = runAllChecks();
$criticalFailed = false;
$totalPassed = 0;
$totalChecks = count($results);
$totalTime = 0;
foreach ($results as $r) {
if ($r['status']) $totalPassed++;
if (!$r['status'] && ($r['critical'] ?? false)) $criticalFailed = true;
$totalTime += $r['time_ms'];
// Get test URL
if (isset($_GET['action']) && $_GET['action'] === 'get_test_url') {
header('Content-Type: application/json');
try {
$db = new PDO('pgsql:host=localhost;dbname=adx_system', 'admin', 'admin123');
$stmt = $db->query("SELECT id, name FROM affiliate.offers WHERE status = 'Activated' ORDER BY id LIMIT 1");
$offer = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt2 = $db->query("SELECT id FROM production.mta_processes WHERE status = 'Completed' ORDER BY id DESC LIMIT 1");
$process = $stmt2->fetch(PDO::FETCH_ASSOC);
$processId = $process ? $process['id'] : 999;
$url = "http://{$tracking_ip}/cl/{$processId}_md/1/1/{$offer['id']}/0/0";
echo json_encode(['success' => true, 'url' => $url, 'offer_id' => $offer['id'], 'offer_name' => $offer['name']]);
} catch (Exception $e) { echo json_encode(['success' => false]); }
exit;
}
$indicator = $criticalFailed ? 'red' : ($totalPassed < $totalChecks ? 'yellow' : 'green');
$statusText = $criticalFailed ? 'CRITICAL ISSUES' : ($totalPassed < $totalChecks ? 'MINOR ISSUES' : 'ALL SYSTEMS GO');
$statusEmoji = $criticalFailed ? '🔴' : ($totalPassed < $totalChecks ? '🟡' : '🟢');
?>
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>WEVADS Tracking — Health Check</title>
<meta http-equiv="refresh" content="30">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tracking Diagnostic - WEVAL</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:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#f8fafc;min-height:100vh;padding:20px}.container{max-width:1100px;margin:0 auto}.back-link{display:inline-flex;align-items:center;gap:8px;color:#0ea5e9;text-decoration:none;margin-bottom:20px;font-weight:500}
.monitoring-bar{background:#1e293b;border-radius:12px;padding:15px 20px;margin-bottom:20px;display:flex;gap:15px;flex-wrap:wrap}
.monitor-section{flex:1;min-width:200px;background:rgba(255,255,255,.05);border-radius:8px;padding:12px 15px}
.monitor-section-title{font-size:11px;text-transform:uppercase;color:#64748b;font-weight:600;margin-bottom:10px;display:flex;align-items:center;gap:6px}
.monitor-section-title .status-dot{width:8px;height:8px;border-radius:50%;margin-left:auto}
.monitor-section-title .status-dot.online{background:#10b981;box-shadow:0 0 8px #10b981}
.monitor-section-title .status-dot.offline{background:#ef4444}
.monitor-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.monitor-item{text-align:center}
.monitor-value{font-size:18px;font-weight:700;color:#fff}
.monitor-value.good{color:#10b981}.monitor-value.warn{color:#f59e0b}.monitor-value.bad{color:#ef4444}
.monitor-label{font-size:10px;color:#94a3b8}
.monitor-bar{height:4px;background:#334155;border-radius:2px;margin-top:4px}
.monitor-bar-fill{height:100%;border-radius:2px;transition:width .5s}
.monitor-bar-fill.good{background:#10b981}.monitor-bar-fill.warn{background:#f59e0b}.monitor-bar-fill.bad{background:#ef4444}
.response-time-display{display:flex;align-items:center;justify-content:center;gap:8px}
.response-time-display .value{font-size:28px;font-weight:700;color:#10b981}
.response-time-display .unit{font-size:14px;color:#64748b}
.header{background:linear-gradient(135deg,#0ea5e9 0%,#0284c7 100%);border-radius:16px;padding:30px;margin-bottom:25px;color:#fff;display:flex;justify-content:space-between;align-items:center}.header-left h1{font-size:28px;margin-bottom:10px}.status-indicator{text-align:center}.status-circle{width:100px;height:100px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:40px;margin-bottom:10px}.status-circle.ok{background:#10b981}.status-circle.error{background:#ef4444}.status-circle.loading{background:#64748b;animation:pulse 1s infinite}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}
.card{background:#fff;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.06);margin-bottom:20px;overflow:hidden}.card-header{background:#f8fafc;border-bottom:1px solid #e2e8f0;padding:18px 20px;display:flex;justify-content:space-between;align-items:center}.card-header h3{font-size:16px;color:#1e293b;display:flex;align-items:center;gap:10px}.card-body{padding:20px}
.check-item{display:flex;align-items:center;justify-content:space-between;padding:16px;border-bottom:1px solid #f1f5f9;gap:10px}.check-item:last-child{border-bottom:none}.check-item .check-info{display:flex;align-items:center;gap:15px;flex:1}.check-item .check-icon{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:18px}.check-item .check-icon.ok{background:#d1fae5;color:#10b981}.check-item .check-icon.error{background:#fee2e2;color:#ef4444}.check-item .check-icon.warning{background:#fef3c7;color:#f59e0b}.check-item .check-name{font-weight:600;color:#1e293b}.check-item .check-message{font-size:13px;color:#64748b}.check-item .check-actions{display:flex;align-items:center;gap:8px}.check-item .check-status{padding:6px 12px;border-radius:20px;font-size:12px;font-weight:600}.check-item .check-status.ok{background:#d1fae5;color:#065f46}.check-item .check-status.error{background:#fee2e2;color:#991b1b}.check-item .check-status.warning{background:#fef3c7;color:#92400e}
.btn{display:inline-flex;align-items:center;gap:8px;padding:12px 24px;border-radius:8px;font-weight:600;font-size:14px;cursor:pointer;border:none;background:linear-gradient(135deg,#0ea5e9 0%,#0284c7 100%);color:#fff;transition:all .2s}.btn:hover{transform:translateY(-1px)}.btn:disabled{opacity:.6}.btn-success{background:linear-gradient(135deg,#10b981 0%,#059669 100%)}.btn-sm{padding:6px 12px;font-size:12px;border-radius:6px}.btn-repair{background:linear-gradient(135deg,#f59e0b 0%,#d97706 100%)}.btn-purple{background:linear-gradient(135deg,#8b5cf6 0%,#7c3aed 100%)}
.summary-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:15px;margin-bottom:25px}.summary-card{background:#fff;border-radius:10px;padding:20px;text-align:center}.summary-card .number{font-size:32px;font-weight:700;margin-bottom:5px}.summary-card .number.green{color:#10b981}.summary-card .number.red{color:#ef4444}.summary-card .label{color:#64748b;font-size:13px}
.simulate-section{background:linear-gradient(135deg,#faf5ff 0%,#f3e8ff 100%);border:2px solid #a855f7;border-radius:12px;padding:20px;margin-bottom:20px}.simulate-header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:15px}.simulate-header h3{font-size:16px;color:#7c3aed;display:flex;align-items:center;gap:10px}.simulate-result{background:#fff;border-radius:8px;padding:15px;margin-top:15px;display:none}.simulate-result.show{display:block}.simulate-result.success{border-left:4px solid #10b981}.simulate-result.error{border-left:4px solid #ef4444}
.activity-list{max-height:300px;overflow-y:auto}.activity-item{display:flex;align-items:center;gap:12px;padding:12px;border-bottom:1px solid #f1f5f9;transition:background .2s}.activity-item:hover{background:#f8fafc}.activity-item:last-child{border-bottom:none}.activity-icon{width:36px;height:36px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:14px}.activity-icon.computer{background:#3b82f6;color:#fff}.activity-icon.mobile{background:#10b981;color:#fff}.activity-icon.tablet{background:#f59e0b;color:#fff}.activity-content{flex:1;min-width:0}.activity-title{font-size:13px;font-weight:600;color:#1e293b}.activity-meta{font-size:11px;color:#94a3b8;display:flex;gap:10px;margin-top:2px}.activity-time{font-size:11px;color:#64748b}.new-click{animation:glow 2s ease-out}@keyframes glow{0%{background:#d1fae5}100%{background:transparent}}
.test-box{background:#f0fdf4;border:2px solid #10b981;border-radius:12px;padding:20px;text-align:center}.test-box h4{color:#065f46;margin-bottom:10px}.test-box p{color:#059669;font-size:14px;margin-bottom:15px}
.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);z-index:1000;align-items:center;justify-content:center}.modal.show{display:flex}.modal-content{background:#fff;border-radius:12px;padding:25px;max-width:500px;width:90%}.modal-header{display:flex;justify-content:space-between;margin-bottom:15px}.modal-close{background:none;border:none;font-size:24px;cursor:pointer;color:#64748b}.modal-output{background:#1e293b;color:#10b981;padding:15px;border-radius:8px;font-family:monospace;font-size:12px;margin-top:15px;white-space:pre-wrap;max-height:300px;overflow-y:auto}
</style>
<link rel="stylesheet" href="tracking-clean.css">
</head>
<body>
<div class="wrap">
<div class="header">
<h1>🔍 WEVADS TRACKING — E2E HEALTH CHECK</h1>
<div class="sub">Serveur <?=WEVADS_SERVER?>:<?=WEVADS_PORT?> → Tracking <?=TRACKING_SERVER_IP?> (<?=TRACKING_DOMAIN?>)</div>
</div>
<div class="banner <?=$indicator?>">
<span class="dot <?=$indicator?>"></span>
<?=$statusEmoji?> <?=$statusText?> — <?=$totalPassed?>/<?=$totalChecks?> checks passed
</div>
<div class="stats">
<div class="stat"><div class="val g"><?=$totalPassed?></div><div class="lbl">PASSED</div></div>
<div class="stat"><div class="val r"><?=$totalChecks - $totalPassed?></div><div class="lbl">FAILED</div></div>
<div class="stat"><div class="val b"><?=round($totalTime)?>ms</div><div class="lbl">TOTAL TIME</div></div>
</div>
<div class="checks">
<?php foreach ($results as $r):
$cls = $r['status'] ? 'pass' : 'fail';
$badge = $r['status'] ? 'PASS' : 'FAIL';
?>
<div class="card <?=$cls?>">
<div class="top">
<span class="name"><?=htmlspecialchars($r['name'])?></span>
<span class="badge <?=$cls?>"><?=$badge?></span>
<div class="container">
<a href="<?php echo $base_url; ?>/tracking-dashboard.php" class="back-link"><i class="fa fa-arrow-left"></i> Back to Dashboard</a>
<!-- Monitoring Bar -->
<div class="monitoring-bar">
<div class="monitor-section">
<div class="monitor-section-title"><i class="fa fa-server"></i> WEVAL Server</div>
<div class="monitor-grid">
<div class="monitor-item"><div class="monitor-value" id="wevads-cpu">-</div><div class="monitor-label">CPU</div><div class="monitor-bar"><div class="monitor-bar-fill good" id="wevads-cpu-bar" style="width:0%"></div></div></div>
<div class="monitor-item"><div class="monitor-value" id="wevads-mem">-</div><div class="monitor-label">Memory</div><div class="monitor-bar"><div class="monitor-bar-fill good" id="wevads-mem-bar" style="width:0%"></div></div></div>
<div class="monitor-item"><div class="monitor-value" id="wevads-disk">-</div><div class="monitor-label">Disk</div><div class="monitor-bar"><div class="monitor-bar-fill good" id="wevads-disk-bar" style="width:0%"></div></div></div>
</div>
</div>
<div class="msg"><?=htmlspecialchars($r['message'])?></div>
<div class="meta">
<span>⏱ <?=$r['time_ms']?>ms</span>
<span class="<?=($r['critical'] ?? false) ? 'crit' : ''?>"><?=($r['critical'] ?? false) ? '🔴 CRITICAL' : '🔵 INFO'?></span>
<div class="monitor-section">
<div class="monitor-section-title"><i class="fa fa-satellite-dish"></i> Tracking<span class="status-dot" id="tracking-status-dot"></span></div>
<div class="monitor-grid">
<div class="monitor-item"><div class="monitor-value" id="tracking-cpu">-</div><div class="monitor-label">CPU</div><div class="monitor-bar"><div class="monitor-bar-fill good" id="tracking-cpu-bar" style="width:0%"></div></div></div>
<div class="monitor-item"><div class="monitor-value" id="tracking-mem">-</div><div class="monitor-label">Memory</div><div class="monitor-bar"><div class="monitor-bar-fill good" id="tracking-mem-bar" style="width:0%"></div></div></div>
<div class="monitor-item"><div class="monitor-value" id="tracking-disk">-</div><div class="monitor-label">Disk</div><div class="monitor-bar"><div class="monitor-bar-fill good" id="tracking-disk-bar" style="width:0%"></div></div></div>
</div>
</div>
<div class="monitor-section" style="max-width:180px">
<div class="monitor-section-title"><i class="fa fa-bolt"></i> Response</div>
<div class="response-time-display"><span class="value" id="tracking-response">-</span><span class="unit">ms</span></div>
</div>
</div>
<?php endforeach; ?>
<!-- Simulate Click -->
<div class="simulate-section">
<div class="simulate-header">
<h3><i class="fa fa-mouse-pointer"></i> Simulate Click</h3>
<button class="btn btn-purple" onclick="generateClick()" id="gen-btn"><i class="fa fa-bolt"></i> Generate Click</button>
</div>
<p style="color:#7c3aed;font-size:13px;margin-top:10px">Inserts a real click with random location into the database</p>
<div class="simulate-result" id="gen-result"></div>
</div>
<!-- Recent Activity -->
<div class="card">
<div class="card-header"><h3><i class="fa fa-history"></i> Recent Clicks</h3><button class="btn btn-sm" onclick="loadActivity()"><i class="fa fa-sync"></i></button></div>
<div class="card-body"><div class="activity-list" id="activity-list"><div style="text-align:center;padding:20px;color:#94a3b8"><i class="fa fa-spinner fa-spin"></i> Loading...</div></div></div>
</div>
<!-- Test Redirect -->
<div class="card">
<div class="card-header"><h3><i class="fa fa-external-link-alt"></i> Test Redirect</h3></div>
<div class="card-body">
<div class="test-box">
<h4><i class="fa fa-link"></i> Open Tracking Link</h4>
<p>Click to open a tracking URL and see where it redirects (no click recorded)</p>
<button class="btn btn-success" onclick="testRedirect()"><i class="fa fa-external-link-alt"></i> Test Redirect</button>
</div>
</div>
</div>
<!-- Header + System Checks -->
<div class="header">
<div class="header-left"><h1><i class="fa fa-heartbeat"></i> System Diagnostic</h1><p>Health check with repair tools</p></div>
<div class="status-indicator"><div class="status-circle loading" id="overall-status"><i class="fa fa-spinner fa-spin"></i></div><div class="status-text" id="overall-text">Checking...</div></div>
</div>
<div class="summary-grid">
<div class="summary-card"><div class="number green" id="count-ok">-</div><div class="label">Passed</div></div>
<div class="summary-card"><div class="number red" id="count-error">-</div><div class="label">Errors</div></div>
<div class="summary-card"><div class="number" id="count-total" style="color:#0ea5e9">-</div><div class="label">Total</div></div>
<div class="summary-card"><div class="number" id="response-time" style="color:#64748b">-</div><div class="label">Diag Time</div></div>
</div>
<div class="card">
<div class="card-header"><h3><i class="fa fa-clipboard-check"></i> System Checks</h3><button class="btn" onclick="runDiagnostic()" id="run-btn"><i class="fa fa-play"></i> Run All</button></div>
<div class="card-body" id="checks-container"><div class="check-item"><div class="check-info"><div class="check-icon" style="background:#f1f5f9;color:#94a3b8"><i class="fa fa-spinner fa-spin"></i></div><div><div class="check-name">Loading...</div></div></div></div></div>
</div>
</div>
<div class="modal" id="repairModal">
<div class="modal-content">
<div class="modal-header"><h4 id="modal-title">Output</h4><button class="modal-close" onclick="closeModal()">&times;</button></div>
<div class="modal-body"><p id="modal-message"></p><div class="modal-output" id="modal-output"></div></div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
var lastClickId = 0;
var countryFlags = {'United States':'🇺🇸','Germany':'🇩🇪','France':'🇫🇷','United Kingdom':'🇬🇧','Sweden':'🇸🇪','Morocco':'🇲🇦','Spain':'🇪🇸','Italy':'🇮🇹'};
function getBarClass(v){return v<60?'good':v<85?'warn':'bad'}
function updateMetrics(){
$.get('?action=metrics',function(d){
['cpu','mem','disk'].forEach(function(m){
$('#wevads-'+m).text(d.wevads[m]+'%').removeClass('good warn bad').addClass(getBarClass(d.wevads[m]));
$('#wevads-'+m+'-bar').css('width',d.wevads[m]+'%').removeClass('good warn bad').addClass(getBarClass(d.wevads[m]));
$('#tracking-'+m).text(d.tracking[m]+'%').removeClass('good warn bad').addClass(getBarClass(d.tracking[m]));
$('#tracking-'+m+'-bar').css('width',d.tracking[m]+'%').removeClass('good warn bad').addClass(getBarClass(d.tracking[m]));
});
var rt=d.tracking.response_time;
$('#tracking-response').text(rt).removeClass('good warn bad').addClass(rt<200?'good':rt<500?'warn':'bad');
$('#tracking-status-dot').removeClass('online offline').addClass(d.tracking.status);
});
}
function generateClick(){
$('#gen-btn').prop('disabled',true).html('<i class="fa fa-spinner fa-spin"></i>');
$('#gen-result').removeClass('show success error');
$.get('?action=generate_click',function(d){
if(d.success){
$('#gen-result').addClass('show success').html('<strong style="color:#065f46"><i class="fa fa-check"></i> Click #'+d.click_id+' generated!</strong><br><span style="color:#64748b">Offer: '+d.offer_name+' • Location: '+d.location+'</span>');
setTimeout(loadActivity,300);
}else{
$('#gen-result').addClass('show error').html('<strong style="color:#991b1b"><i class="fa fa-times"></i> '+d.message+'</strong>');
}
$('#gen-btn').prop('disabled',false).html('<i class="fa fa-bolt"></i> Generate Click');
});
}
function loadActivity(){
$.get('?action=recent_activity',function(d){
if(d.success && d.clicks.length>0){
var h='';
d.clicks.forEach(function(c){
var isNew=lastClickId>0&&c.id>lastClickId;
var time=new Date(c.action_time).toLocaleTimeString();
var flag=countryFlags[c.country]||'🌍';
var iconClass=(c.device_type||'Computer').toLowerCase();
var icon=iconClass==='mobile'?'fa-mobile-alt':iconClass==='tablet'?'fa-tablet-alt':'fa-desktop';
h+='<div class="activity-item'+(isNew?' new-click':'')+'"><div class="activity-icon '+iconClass+'"><i class="fa '+icon+'"></i></div><div class="activity-content"><div class="activity-title">'+flag+' Offer #'+c.offer_id+' - '+(c.city||'Unknown')+', '+(c.country||'Unknown')+'</div><div class="activity-meta"><span>'+c.browser_name+'</span><span>Process #'+c.process_id+'</span><span>'+c.action_ip+'</span></div></div><div class="activity-time">'+time+'</div></div>';
});
$('#activity-list').html(h);
if(d.clicks.length>0)lastClickId=d.clicks[0].id;
}else{
$('#activity-list').html('<div style="text-align:center;padding:20px;color:#94a3b8"><i class="fa fa-inbox"></i> No clicks</div>');
}
});
}
function testRedirect(){
$.get('?action=get_test_url',function(d){
if(d.success)window.open(d.url,'_blank');
});
}
function runDiagnostic(){
var start=Date.now();
$('#run-btn').prop('disabled',true).html('<i class="fa fa-spinner fa-spin"></i>');
$('#overall-status').removeClass('ok error').addClass('loading').html('<i class="fa fa-spinner fa-spin"></i>');
$.get('?action=diagnose',function(d){
var elapsed=Date.now()-start;$('#response-time').text(elapsed+'ms');
var h='',ok=0,err=0;
for(var k in d.checks){
var c=d.checks[k];
var icon=c.status==='ok'?'fa-check':c.status==='error'?'fa-times':'fa-exclamation';
if(c.status==='ok')ok++;else err++;
h+='<div class="check-item"><div class="check-info"><div class="check-icon '+c.status+'"><i class="fa '+icon+'"></i></div><div><div class="check-name">'+c.name+'</div><div class="check-message">'+c.message+'</div></div></div><div class="check-actions"><button class="btn btn-sm btn-repair" onclick="repair(\''+c.repair+'\',\''+c.repair_label+'\')"><i class="fa fa-wrench"></i> '+c.repair_label+'</button><span class="check-status '+c.status+'">'+c.status+'</span></div></div>';
}
$('#checks-container').html(h);$('#count-ok').text(ok);$('#count-error').text(err);$('#count-total').text(ok+err);
$('#overall-status').removeClass('loading').addClass(d.overall_status).html('<i class="fa '+(d.overall_status==='ok'?'fa-check':'fa-times')+'"></i>');
$('#overall-text').text(d.overall_status==='ok'?'All Systems Go':'Issues Found');
$('#run-btn').prop('disabled',false).html('<i class="fa fa-play"></i> Run All');
});
}
function repair(t,l){
$('#modal-title').text(l);$('#modal-message').html('<i class="fa fa-spinner fa-spin"></i> Running...');$('#modal-output').hide();$('#repairModal').addClass('show');
$.post('?action=repair',{type:t},function(d){
$('#modal-message').html((d.success?'<i class="fa fa-check-circle" style="color:#10b981"></i> ':'<i class="fa fa-times-circle" style="color:#ef4444"></i> ')+d.message);
if(d.output)$('#modal-output').text(d.output).show();
if(d.url)$('#modal-message').append('<br><br><a href="'+d.url+'" target="_blank" class="btn btn-sm">Open AWS</a>');
setTimeout(runDiagnostic,2000);
});
}
function closeModal(){$('#repairModal').removeClass('show')}
$('#repairModal').click(function(e){if(e.target===this)closeModal()});
$(document).ready(function(){
updateMetrics();loadActivity();runDiagnostic();
setInterval(updateMetrics,10000);
setInterval(loadActivity,5000);
});
</script>
<?php include("includes/chatbot-widget.php"); ?>
<div class="infra">
<h3>📡 Infrastructure</h3>
<table>
<tr><th>Composant</th><th>Adresse</th><th>Rôle</th></tr>
<tr><td>WEVADS Server</td><td class="mono"><?=WEVADS_SERVER?>:<?=WEVADS_PORT?></td><td>API + BDD + Pipeline</td></tr>
<tr><td>Tracking Server</td><td class="mono"><?=TRACKING_SERVER_IP?></td><td>OVH — tracking.php, open/click/unsub</td></tr>
<tr><td>Tracking Domain</td><td class="mono"><?=TRACKING_DOMAIN?></td><td>HTTPS via Cloudflare → <?=TRACKING_SERVER_IP?></td></tr>
<tr><td>Sponsors</td><td class="mono">e36lbat.com (CX3) / rivoweb (DoubleM)</td><td>Offres affiliées</td></tr>
</table>
</div>
<div class="api">
<strong>API pour Dashboard principal:</strong>
<a href="?api=status"><?="http://{$_SERVER['HTTP_HOST']}{$_SERVER['SCRIPT_NAME']}?api=status"?></a>
→ {"status":"<?=$criticalFailed?'unhealthy':'healthy'?>","indicator":"<?=$indicator?>"}
</div>
<div class="footer">
Auto-refresh: 30s | <?=date('Y-m-d H:i:s')?> | WEVAL Consulting
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,3 @@
body { background: #f8fafc; color: #1e293b; }
nav, header, .navbar, .top-nav, .sidebar { display: none; }
.main-content { padding: 20px; }