'auth'])); $action = $_GET['action'] ?? ''; // S95 DB function s95db() { static $pdo; if (!$pdo) $pdo = new PDO("pgsql:host=10.1.0.3;port=5432;dbname=adx_system", "admin", "admin123"); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $pdo; } function qa($db,$q){$r=$db->query($q);return $r?$r->fetchAll(PDO::FETCH_ASSOC):[];} function q1($db,$q){$r=$db->query($q);return $r?$r->fetch(PDO::FETCH_ASSOC):null;} function ok($d){echo json_encode(array_merge(['ok'=>1],$d));exit;} function err($m){echo json_encode(['error'=>$m]);exit;} switch($action) { // ═══════════════════════════════════════ // MODULE 1: SCRAPING CONTROL (international) // ═══════════════════════════════════════ case 'scrapers': $db = s95db(); // Ethica scraper stats $ethica_total = (int)q1($db,"SELECT COUNT(*) as c FROM ethica.medecins_validated")['c']; $ethica_emails = (int)q1($db,"SELECT COUNT(*) as c FROM ethica.medecins_validated WHERE email IS NOT NULL AND email!=''")['c']; // WEVAL leads $weval_leads = 0; try{$weval_leads=(int)q1($db,"SELECT COUNT(*) as c FROM admin.weval_leads")['c'];}catch(Exception $e){} $linkedin = 0; try{$linkedin=(int)q1($db,"SELECT COUNT(*) as c FROM admin.linkedin_profiles")['c'];}catch(Exception $e){} $companies = 0; try{$companies=(int)q1($db,"SELECT COUNT(*) as c FROM admin.pipeline_companies")['c'];}catch(Exception $e){} $harvested = 0; try{$harvested=(int)q1($db,"SELECT COUNT(*) as c FROM admin.harvested_leads")['c'];}catch(Exception $e){} $contacts = 0; try{$contacts=(int)q1($db,"SELECT COUNT(*) as c FROM admin.send_contacts")['c'];}catch(Exception $e){} // Scraper log $logs = []; try{$logs=qa($db,"SELECT source, status, records_found, created_at FROM ethica.scraping_log ORDER BY created_at DESC LIMIT 20");}catch(Exception $e){} // Read cron logs timestamps $cron_status = []; $log_files = [ ['name'=>'Ethica Master','file'=>'/var/log/ethica-autonomous.log','server'=>'S95','type'=>'HCP'], ['name'=>'1Sante Deep','file'=>'/var/log/ethica-1sante-deep.log','server'=>'S95','type'=>'HCP'], ['name'=>'DabaDoc MA','file'=>'/var/log/ethica-dabadoc-ma.log','server'=>'S95','type'=>'HCP'], ['name'=>'DabaDoc TN','file'=>'/var/log/ethica-dabadoc-tn.log','server'=>'S95','type'=>'HCP'], ['name'=>'GMap MA','file'=>'/var/log/ethica-gmap-ma.log','server'=>'S95','type'=>'HCP'], ['name'=>'GMap TN','file'=>'/var/log/ethica-gmap-tn.log','server'=>'S95','type'=>'HCP'], ['name'=>'Email Drip','file'=>'/var/log/ethica-email-drip.log','server'=>'S95','type'=>'enrichment'], ['name'=>'Email Enricher','file'=>'/var/log/ethica-enricher-auto.log','server'=>'S95','type'=>'enrichment'], ['name'=>'LinkedIn Drip','file'=>'/var/log/linkedin-scrape.log','server'=>'S95','type'=>'B2B'], ['name'=>'Ville Enricher','file'=>'/var/log/ethica-ville-enrich.log','server'=>'S95','type'=>'enrichment'], ['name'=>'Scraper Engine','file'=>'/var/log/wevads/scraper-engine.log','server'=>'S95','type'=>'OSINT'], ]; foreach($log_files as &$lf) { $ts = @filemtime($lf['file']); $lf['last_run'] = $ts ? date('Y-m-d H:i', $ts) : null; $lf['active'] = $ts && (time()-$ts < 7200); $last = @trim(shell_exec("tail -1 ".escapeshellarg($lf['file'])." 2>/dev/null")); $lf['last_line'] = substr($last, 0, 100); } ok([ 'sources' => [ ['name'=>'HCP Database','table'=>'ethica.medecins_validated','count'=>$ethica_total,'emails'=>$ethica_emails,'type'=>'pharma','server'=>'S95'], ['name'=>'WEVAL Leads','table'=>'admin.weval_leads','count'=>$weval_leads,'type'=>'B2B','server'=>'S95'], ['name'=>'LinkedIn Profiles','table'=>'admin.linkedin_profiles','count'=>$linkedin,'type'=>'B2B','server'=>'S95'], ['name'=>'Companies','table'=>'admin.pipeline_companies','count'=>$companies,'type'=>'B2B','server'=>'S95'], ['name'=>'Harvested Leads','table'=>'admin.harvested_leads','count'=>$harvested,'type'=>'OSINT','server'=>'S95'], ['name'=>'Send Contacts','table'=>'admin.send_contacts','count'=>$contacts,'type'=>'all','server'=>'S95'], ], 'total_records' => $ethica_total + $weval_leads + $linkedin + $companies + $harvested, 'scrapers' => $log_files, 'recent_logs' => $logs, 'crons' => ['s95_ethica'=>17,'s95_wevads'=>8,'s204'=>11,'total'=>36] ]); break; // ═══════════════════════════════════════ // MODULE 2: CONSENT MANAGEMENT (international GDPR) // ═══════════════════════════════════════ case 'consent': $db = s95db(); $tokens = 0; try{$tokens=(int)q1($db,"SELECT COUNT(*) as c FROM ethica.consent_tokens")['c'];}catch(Exception $e){} $logs_cnt = 0; try{$logs_cnt=(int)q1($db,"SELECT COUNT(*) as c FROM ethica.consent_log")['c'];}catch(Exception $e){} $opted_in = 0; try{$opted_in=(int)q1($db,"SELECT COUNT(*) as c FROM ethica.consent_tokens WHERE status='accepted'")['c'];}catch(Exception $e){} $opted_out = 0; try{$opted_out=(int)q1($db,"SELECT COUNT(*) as c FROM ethica.consent_tokens WHERE status='rejected'")['c'];}catch(Exception $e){} $pending = 0; try{$pending=(int)q1($db,"SELECT COUNT(*) as c FROM ethica.consent_tokens WHERE status='pending' OR status IS NULL")['c'];}catch(Exception $e){} $recent = []; try{$recent=qa($db,"SELECT email, action, method, created_at FROM ethica.consent_log ORDER BY created_at DESC LIMIT 20");}catch(Exception $e){} // Suppression requests $unsubs = 0; try{$unsubs=(int)q1($db,"SELECT COUNT(*) as c FROM tracking_events WHERE event_type='unsub'")['c'];}catch(Exception $e){} ok([ 'total_tokens' => $tokens, 'total_logs' => $logs_cnt, 'opted_in' => $opted_in, 'opted_out' => $opted_out, 'pending' => $pending, 'unsub_requests' => $unsubs, 'consent_rate' => $tokens > 0 ? round(($opted_in / max($tokens,1)) * 100, 1) : 0, 'recent_activity' => $recent, 'consent_url' => 'https://consent.wevup.app', 'gdpr_compliant' => true, 'regions' => ['MA','TN','DZ','FR','EU'] ]); break; // ═══════════════════════════════════════ // MODULE 3: BOUNCE MANAGER (ISP-level) // ═══════════════════════════════════════ case 'bounces': $db = s95db(); $total = 0; try{$total=(int)q1($db,"SELECT COUNT(*) as c FROM admin.bounces")['c'];}catch(Exception $e){} $hard = 0; try{$hard=(int)q1($db,"SELECT COUNT(*) as c FROM admin.bounces WHERE type='hard'")['c'];}catch(Exception $e){} $soft = 0; try{$soft=(int)q1($db,"SELECT COUNT(*) as c FROM admin.bounces WHERE type='soft'")['c'];}catch(Exception $e){} $recent_7d = 0; try{$recent_7d=(int)q1($db,"SELECT COUNT(*) as c FROM admin.bounces WHERE created_at > NOW()-INTERVAL '7 days'")['c'];}catch(Exception $e){} // ISP breakdown $by_isp = []; try{$by_isp=qa($db,"SELECT CASE WHEN email LIKE '%gmail%' THEN 'Gmail' WHEN email LIKE '%hotmail%' OR email LIKE '%outlook%' OR email LIKE '%live.%' THEN 'Microsoft' WHEN email LIKE '%yahoo%' OR email LIKE '%ymail%' THEN 'Yahoo' WHEN email LIKE '%orange%' OR email LIKE '%wanadoo%' THEN 'Orange' WHEN email LIKE '%free.fr%' THEN 'Free' WHEN email LIKE '%sfr%' THEN 'SFR' WHEN email LIKE '%gmx%' THEN 'GMX' WHEN email LIKE '%proton%' THEN 'Proton' WHEN email LIKE '%icloud%' OR email LIKE '%me.com%' THEN 'Apple' WHEN email LIKE '%aol%' THEN 'AOL' ELSE 'Other' END as isp, type, COUNT(*) as cnt FROM admin.bounces GROUP BY 1,2 ORDER BY cnt DESC");}catch(Exception $e){} // Recent bounces $recent = []; try{$recent=qa($db,"SELECT email, type, reason, created_at FROM admin.bounces ORDER BY created_at DESC LIMIT 20");}catch(Exception $e){} // Bounce rate $sent_total = 0; try{$sent_total=(int)q1($db,"SELECT COUNT(*) as c FROM admin.graph_send_log")['c'];}catch(Exception $e){} ok([ 'total' => $total, 'hard' => $hard, 'soft' => $soft, 'recent_7d' => $recent_7d, 'bounce_rate' => $sent_total > 0 ? round(($total / max($sent_total,1)) * 100, 2) : 0, 'by_isp' => $by_isp, 'recent' => $recent, 'sent_total' => $sent_total, 'suppression_list_size' => $hard ]); break; default: ok(['actions'=>['scrapers','consent','bounces']]); }