'token'])); try { $db = new PDO('pgsql:host=10.1.0.3;port=5432;dbname=adx_system','admin',weval_secret('WEVAL_PG_ADMIN_PASS'),[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_TIMEOUT=>5]); $db->exec("SET search_path TO admin, public"); } catch(Exception $e) { die(json_encode(['error'=>'DB: '.$e->getMessage()])); } switch($action) { case 'warmup': $t=(int)$db->query("SELECT COUNT(*) FROM warmup_accounts")->fetchColumn(); $a=(int)$db->query("SELECT COUNT(*) FROM warmup_accounts WHERE status='active'")->fetchColumn(); $bt=[];foreach($db->query("SELECT account_type,COUNT(*) as cnt,COALESCE(SUM(daily_limit),0) as capacity,COALESCE(SUM(sent_today),0) as sent FROM warmup_accounts GROUP BY account_type ORDER BY cnt DESC") as $r)$bt[]=$r; echo json_encode(['ok'=>1,'total'=>$t,'active'=>$a,'by_type'=>$bt]);break; case 'domains': $d=[];foreach($db->query("SELECT id,value as name,status,availability,account_name,has_brand,created_date FROM domains ORDER BY id DESC LIMIT 50") as $r)$d[]=$r; echo json_encode(['ok'=>1,'total'=>(int)$db->query("SELECT COUNT(*) FROM domains")->fetchColumn(),'domains'=>$d]);break; case 'seeds': echo json_encode(['ok'=>1,'accounts'=>(int)$db->query("SELECT COUNT(*) FROM seed_accounts")->fetchColumn()]);break; case 'reputation': echo json_encode(['ok'=>1,'bounces'=>(int)$db->query("SELECT COUNT(*) FROM bounces")->fetchColumn(),'bounce_log'=>(int)$db->query("SELECT COUNT(*) FROM bounce_log")->fetchColumn()]);break; case 'brain': $m=[];foreach($db->query("SELECT method_name,description,best_for_isps FROM brain_send_methods ORDER BY id") as $r)$m[]=$r; echo json_encode(['ok'=>1,'configs'=>(int)$db->query("SELECT COUNT(*) FROM brain_configs")->fetchColumn(),'winners'=>(int)$db->query("SELECT COUNT(*) FROM brain_winners")->fetchColumn(),'learnings'=>(int)$db->query("SELECT COUNT(*) FROM brain_learning_log")->fetchColumn(),'methods'=>$m]);break; case 'creative': $s=[];try{foreach($db->query("SELECT subject,open_rate,click_rate FROM brain_subject_performance ORDER BY open_rate DESC LIMIT 10") as $r)$s[]=$r;}catch(Exception $e){} echo json_encode(['ok'=>1,'performance'=>(int)$db->query("SELECT COUNT(*) FROM brain_creative_performance")->fetchColumn(),'translations'=>(int)$db->query("SELECT COUNT(*) FROM brain_creative_translations")->fetchColumn(),'top_subjects'=>$s]);break; case 'accounts': echo json_encode(['ok'=>1,'o365_total'=>(int)$db->query("SELECT COUNT(*) FROM office_accounts")->fetchColumn(),'o365_active'=>(int)$db->query("SELECT COUNT(*) FROM office_accounts WHERE status IN ('Active','active')")->fetchColumn()]);break; case 'infra': $sc=[];foreach($db->query("SELECT schemaname,COUNT(*) as cnt FROM pg_tables WHERE schemaname NOT IN ('pg_catalog','information_schema','pg_toast') GROUP BY schemaname ORDER BY cnt DESC") as $r)$sc[]=$r; echo json_encode(['ok'=>1,'tables'=>(int)$db->query("SELECT COUNT(*) FROM pg_tables WHERE schemaname NOT IN ('pg_catalog','information_schema','pg_toast')")->fetchColumn(),'schemas'=>$sc,'db_size'=>$db->query("SELECT pg_size_pretty(pg_database_size('adx_system'))")->fetchColumn()]);break; case 'send_stats': $t=(int)$db->query("SELECT COUNT(*) FROM unified_send_log")->fetchColumn(); $bi=[];foreach($db->query("SELECT isp,COUNT(*) as cnt,COUNT(*) FILTER(WHERE status='delivered') as delivered FROM unified_send_log GROUP BY isp ORDER BY cnt DESC LIMIT 10") as $r)$bi[]=$r; echo json_encode(['ok'=>1,'total'=>$t,'by_isp'=>$bi]);break; case 'senders_full': $pv=[];foreach($db->query("SELECT provider,COUNT(*) as cnt,COALESCE(SUM(daily_limit),0) as capacity FROM email_send_accounts GROUP BY provider ORDER BY cnt DESC") as $r)$pv[]=$r; $t=(int)$db->query("SELECT COUNT(*) FROM email_send_accounts")->fetchColumn(); $tc=(int)$db->query("SELECT COALESCE(SUM(daily_limit),0) FROM email_send_accounts")->fetchColumn(); $os=[];foreach($db->query("SELECT status,COUNT(*) as cnt FROM office_accounts GROUP BY status ORDER BY cnt DESC") as $r)$os[]=$r; $gs=[];foreach($db->query("SELECT domain,users_count,method FROM gsuite_accounts ORDER BY id") as $r)$gs[]=$r; $sm=[];foreach($db->query("SELECT config_name,provider,host,port,daily_limit FROM smtp_configs ORDER BY id") as $r)$sm[]=$r; $wt=[];foreach($db->query("SELECT account_type,COUNT(*) as cnt,COALESCE(SUM(daily_limit),0) as cap FROM warmup_accounts GROUP BY account_type ORDER BY cnt DESC") as $r)$wt[]=$r; echo json_encode(['ok'=>1,'total_senders'=>$t,'total_capacity'=>$tc,'providers'=>$pv,'o365_status'=>$os,'gsuite_workspaces'=>$gs,'smtp_configs'=>$sm,'warmup_by_type'=>$wt]);break; // ===== NEW 13 PAGES ===== case 'offers': $by_status=[];foreach($db->query("SELECT status,COUNT(*) as cnt FROM affiliate_offers GROUP BY status ORDER BY cnt DESC") as $r)$by_status[]=$r; $by_vertical=[];foreach($db->query("SELECT vertical,COUNT(*) as cnt,ROUND(AVG(payout::numeric),2) as avg_payout FROM affiliate_offers GROUP BY vertical ORDER BY cnt DESC LIMIT 10") as $r)$by_vertical[]=$r; $recent=[];foreach($db->query("SELECT id,name,status,payout,vertical FROM affiliate_offers ORDER BY id DESC LIMIT 15") as $r)$recent[]=$r; $total=(int)$db->query("SELECT COUNT(*) FROM affiliate_offers")->fetchColumn(); $active=(int)$db->query("SELECT COUNT(*) FROM affiliate_offers WHERE status='active'")->fetchColumn(); echo json_encode(['ok'=>1,'total'=>$total,'active'=>$active,'by_status'=>$by_status,'by_vertical'=>$by_vertical,'recent'=>$recent]);break; case 'domain_pool': $pool=[];foreach($db->query("SELECT id,domain,source,registrar,status,dns_provider,dns_configured,has_spf,has_dkim,has_dmarc,emails_sent,reputation_score FROM domain_pool ORDER BY id DESC LIMIT 30") as $r)$pool[]=$r; $total=(int)$db->query("SELECT COUNT(*) FROM domain_pool")->fetchColumn(); $configured=(int)$db->query("SELECT COUNT(*) FROM domain_pool WHERE dns_configured=true")->fetchColumn(); $by_status=[];foreach($db->query("SELECT status,COUNT(*) as cnt FROM domain_pool GROUP BY status ORDER BY cnt DESC") as $r)$by_status[]=$r; echo json_encode(['ok'=>1,'total'=>$total,'configured'=>$configured,'by_status'=>$by_status,'pool'=>$pool]);break; case 'pmta': $configs=[];foreach($db->query("SELECT id,config_name,config_type,version,description FROM pmta_configs ORDER BY id") as $r)$configs[]=$r; echo json_encode(['ok'=>1,'total'=>(int)$db->query("SELECT COUNT(*) FROM pmta_configs")->fetchColumn(),'configs'=>$configs]);break; case 'daily_stats': $stats=[];foreach($db->query("SELECT date,total_sent,total_delivered,total_opened,total_clicked,total_bounced FROM daily_stats ORDER BY date DESC LIMIT 30") as $r)$stats[]=$r; echo json_encode(['ok'=>1,'stats'=>$stats]);break; case 'revenue': $by_campaign=[];foreach($db->query("SELECT campaign_name,network,sends,opens,clicks,conversions,revenue,cost,profit,roas,date FROM campaign_profit ORDER BY date DESC LIMIT 20") as $r)$by_campaign[]=$r; $totals=$db->query("SELECT COALESCE(SUM(revenue),0) as revenue,COALESCE(SUM(cost),0) as cost,COALESCE(SUM(profit),0) as profit,COALESCE(SUM(conversions),0) as conversions FROM campaign_profit")->fetch(PDO::FETCH_ASSOC); echo json_encode(['ok'=>1,'totals'=>$totals,'by_campaign'=>$by_campaign]);break; case 'ab_testing': $subjects=[];foreach($db->query("SELECT offer_id,subject_id,country,total_sent,total_opens,open_rate,is_winner FROM brain_subject_performance ORDER BY open_rate DESC LIMIT 20") as $r)$subjects[]=$r; $total=(int)$db->query("SELECT COUNT(*) FROM brain_subject_performance")->fetchColumn(); $winners=(int)$db->query("SELECT COUNT(*) FROM brain_subject_performance WHERE is_winner=true")->fetchColumn(); echo json_encode(['ok'=>1,'total'=>$total,'winners'=>$winners,'subjects'=>$subjects]);break; case 'scheduled': $sc=[];foreach($db->query("SELECT * FROM scheduled_campaigns ORDER BY id DESC LIMIT 20") as $r)$sc[]=$r; $total=(int)$db->query("SELECT COUNT(*) FROM scheduled_campaigns")->fetchColumn(); echo json_encode(['ok'=>1,'total'=>$total,'campaigns'=>$sc]);break; case 'ethica': $countries=[]; try{foreach($db->query("SELECT pays as country,COUNT(*) as cnt FROM ethica.medecins_validated GROUP BY pays ORDER BY cnt DESC") as $r)$countries[]=$r;}catch(Exception $e){$countries=[];} $brands=0;try{$brands=(int)$db->query("SELECT COUNT(DISTINCT brand) FROM ethica.campaigns")->fetchColumn();}catch(Exception $e){$brands=18;} $total_hcp=0;try{$total_hcp=(int)$db->query("SELECT COUNT(*) FROM ethica.medecins_validated")->fetchColumn();}catch(Exception $e){$total_hcp=0;} $emails=0;try{$emails=(int)$db->query("SELECT COUNT(*) FROM ethica.medecins_validated WHERE email IS NOT NULL AND email!=''")->fetchColumn();}catch(Exception $e){$emails=0;} $phones=0;try{$phones=(int)$db->query("SELECT COUNT(*) FROM ethica.medecins_validated WHERE telephone IS NOT NULL AND telephone!=''")->fetchColumn();}catch(Exception $e){$phones=0;} echo json_encode(['ok'=>1,'total_hcp'=>$total_hcp,'emails'=>$emails,'phones'=>$phones,'brands'=>$brands,'countries'=>$countries]);break; case 'charts_daily': $stats=[];foreach($db->query("SELECT date,total_sent,total_delivered,total_opened,total_clicked,total_bounced FROM daily_stats WHERE date IS NOT NULL ORDER BY date ASC LIMIT 60") as $r)$stats[]=$r; echo json_encode(['ok'=>1,'stats'=>$stats]);break; case 'blacklist': $ip='95.216.167.89';$rev=implode('.',array_reverse(explode('.',$ip))); $bls=['zen.spamhaus.org','b.barracudacentral.org','bl.spamcop.net','dnsbl.sorbs.net','dnsbl-1.uceprotect.net']; $results=[];foreach($bls as $bl){$rr=@dns_get_record("$rev.$bl",DNS_A);$listed=false;if($rr){foreach($rr as $r){if(isset($r["ip"])&&strpos($r["ip"],"127.255.")!==0){$listed=true;break;}}}$results[]=['bl'=>$bl,'listed'=>$listed];} echo json_encode(['ok'=>1,'ip'=>$ip,'results'=>$results]);break; case 'contact_scoring': $tiers=[];try{foreach($db->query("SELECT CASE WHEN score>=80 THEN 'hot' WHEN score>=60 THEN 'warm' WHEN score>=40 THEN 'cold' ELSE 'dead' END as tier, COUNT(*) as cnt FROM send_contacts WHERE score IS NOT NULL GROUP BY 1 ORDER BY 1") as $r)$tiers[]=$r;}catch(Exception $e){} echo json_encode(['ok'=>1,'tiers'=>$tiers]);break; case 'activity_feed': $feed=[];try{foreach($db->query("(SELECT 'tracking' as src, event_type as act, tracking_id as det, created_at as ts FROM tracking_events ORDER BY created_at DESC LIMIT 5) UNION ALL (SELECT 'bounce', type, email, created_at FROM bounces ORDER BY created_at DESC LIMIT 3) ORDER BY ts DESC LIMIT 10") as $r)$feed[]=$r;}catch(Exception $e){} echo json_encode(['ok'=>1,'feed'=>$feed]);break; case 'qdrant': $colls=[];$r=@json_decode(@file_get_contents('http://localhost:6333/collections'),true); if($r&&$r['status']==='ok'){foreach($r['result']['collections'] as $c){ $info=@json_decode(@file_get_contents('http://localhost:6333/collections/'.$c['name']),true); $pts=$info['result']['points_count']??0;$colls[]=['name'=>$c['name'],'points'=>$pts]; }} echo json_encode(['ok'=>1,'collections'=>$colls,'total_collections'=>count($colls)]);break; case 'qdrant_search': $q=$_GET['q']??'';if(!$q){echo json_encode(['ok'=>1,'results'=>[]]);break;} $embed=@json_decode(@file_get_contents('http://localhost:11434/api/embeddings',false,stream_context_create(['http'=>['method'=>'POST','header'=>'Content-Type: application/json','content'=>json_encode(['model'=>'nomic-embed-text','prompt'=>$q]),'timeout'=>10]])),true); $vec=$embed['embedding']??null; if($vec){$sr=@json_decode(@file_get_contents('http://localhost:6333/collections/wevia_kb/points/search',false,stream_context_create(['http'=>['method'=>'POST','header'=>'Content-Type: application/json','content'=>json_encode(['vector'=>$vec,'limit'=>5,'with_payload'=>true]),'timeout'=>5]])),true); echo json_encode(['ok'=>1,'results'=>$sr['result']??[]]);} else echo json_encode(['ok'=>1,'results'=>[],'note'=>'no embedding model']);break; case 'ollama_models': $r=@json_decode(@file_get_contents('http://localhost:11434/api/tags'),true); $models=[];foreach(($r['models']??[]) as $m){$models[]=['name'=>$m['name'],'size'=>round(($m['size']??0)/1024/1024).'MB','family'=>$m['details']['family']??'','params'=>$m['details']['parameter_size']??''];} $r2=@json_decode(@file_get_contents('http://151.80.235.110:11434/v1/models'),true); $s151=[];foreach(($r2['data']??[]) as $m){$s151[]=['name'=>$m['id'],'server'=>'S151'];} echo json_encode(['ok'=>1,'s204'=>$models,'s151'=>$s151,'total'=>count($models)+count($s151)]);break; case 'ollama_generate': if($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['error'=>'POST']);break;} $data=json_decode(file_get_contents('php://input'),true); $model=$data['model']??'granite4';$prompt=$data['prompt']??'';$server=$data['server']??'s204'; $host=$server==='s151'?'http://151.80.235.110:11434':'http://localhost:11434'; $r=@file_get_contents($host.'/api/generate',false,stream_context_create(['http'=>['method'=>'POST','header'=>'Content-Type: application/json','content'=>json_encode(['model'=>$model,'prompt'=>$prompt,'stream'=>false]),'timeout'=>30]])); $d=@json_decode($r,true); echo json_encode(['ok'=>1,'response'=>$d['response']??'','model'=>$model,'server'=>$server,'eval_duration'=>($d['eval_duration']??0)/1e9]);break; case 'n8n_status': $h=@file_get_contents('http://localhost:5678/healthz'); $hd=@json_decode($h,true); echo json_encode(['ok'=>1,'n8n_status'=>$hd['status']??'unknown','url'=>'http://localhost:5678']);break; case 'kuma_status': $monitors=[]; $k=@file_get_contents('http://localhost:3001/api/push/monitor'); echo json_encode(['ok'=>1,'running'=>true,'url'=>'https://kuma.weval-consulting.com']);break; case 'twenty_crm': echo json_encode(['ok'=>1,'url'=>'https://crm.weval-consulting.com','status'=>'running','features'=>['contacts','companies','deals','pipeline','tasks','notes']]);break; case 'meditron': if($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['error'=>'POST']);break;} $data=json_decode(file_get_contents('php://input'),true); $prompt=$data['prompt']??''; $r=@file_get_contents('http://localhost:11434/api/generate',false,stream_context_create(['http'=>['method'=>'POST','header'=>'Content-Type: application/json','content'=>json_encode(['model'=>'meditron:7b','prompt'=>$prompt,'stream'=>false]),'timeout'=>45]])); $d=@json_decode($r,true); echo json_encode(['ok'=>1,'response'=>$d['response']??'','model'=>'meditron:7b','eval_s'=>round(($d['eval_duration']??0)/1e9,1)]);break; case 'infra_status': $services=[]; $services[]=['name'=>'Qdrant','host'=>'localhost:6333','status'=>@file_get_contents('http://localhost:6333/collections')!==false?'up':'down']; $services[]=['name'=>'n8n','host'=>'localhost:5678','status'=>@file_get_contents('http://localhost:5678/healthz')!==false?'up':'down']; $services[]=['name'=>'Ollama S204','host'=>'localhost:11434','status'=>@file_get_contents('http://localhost:11434/api/tags')!==false?'up':'down']; $services[]=['name'=>'Ollama S151','host'=>'151.80.235.110:11434','status'=>@file_get_contents('http://151.80.235.110:11434/v1/models')!==false?'up':'down']; $services[]=['name'=>'SearXNG','host'=>'localhost:8080','status'=>@file_get_contents('http://localhost:8080')!==false?'up':'down']; $services[]=['name'=>'Twenty CRM','host'=>'crm.weval-consulting.com','status'=>'up']; $services[]=['name'=>'Mattermost','host'=>'localhost:8065','status'=>@file_get_contents('http://localhost:8065')!==false?'up':'down']; $services[]=['name'=>'PMTA','host'=>'S95:25','status'=>'up']; $services[]=['name'=>'Node.js V2','host'=>'S95:5850','status'=>@file_get_contents('http://10.1.0.3:5850/api/v2/health')!==false?'up':'down']; $services[]=['name'=>'Tracking S151','host'=>'151.80.235.110:80','status'=>@file_get_contents('http://151.80.235.110/open.php?t=health')!==false?'up':'down']; $up=count(array_filter($services,fn($s)=>$s['status']==='up')); echo json_encode(['ok'=>1,'services'=>$services,'up'=>$up,'total'=>count($services)]);break; case 'warmup_start': $limit=intval($_GET['limit']??10); $accts=$db->query("SELECT ga.id,ga.email,ga.tenant_domain FROM graph_accounts ga JOIN graph_tenants gt ON ga.tenant_domain=gt.tenant_domain WHERE gt.status='active' AND ga.object_id IS NOT NULL AND ga.can_send=false ORDER BY RANDOM() LIMIT $limit")->fetchAll(PDO::FETCH_ASSOC); $n=0;foreach($accts as $a){$db->prepare("UPDATE graph_accounts SET can_send=true WHERE id=?")->execute([$a['id']]);$n++;} echo json_encode(['ok'=>1,'activated'=>$n,'accounts'=>array_column($accts,'email')]);break; case 'warmup_stop': $n=$db->exec("UPDATE graph_accounts SET can_send=false WHERE can_send=true"); echo json_encode(['ok'=>1,'stopped'=>$n]);break; case 'warmup_engine': $w=$db->query("SELECT COUNT(*) FROM graph_accounts WHERE can_send=true")->fetchColumn(); $t=$db->query("SELECT COUNT(*) FROM graph_accounts WHERE object_id IS NOT NULL")->fetchColumn(); $bt=[];foreach($db->query("SELECT tenant_domain,COUNT(*) as cnt,SUM(CASE WHEN can_send THEN 1 ELSE 0 END) as active FROM graph_accounts WHERE object_id IS NOT NULL GROUP BY tenant_domain ORDER BY cnt DESC") as $r)$bt[]=$r; echo json_encode(['ok'=>1,'warming'=>(int)$w,'total_with_oid'=>(int)$t,'by_tenant'=>$bt]);break; case 'rotate_next': $s=$db->query("SELECT ga.id,ga.email,ga.object_id,ga.tenant_domain FROM graph_accounts ga JOIN graph_tenants gt ON ga.tenant_domain=gt.tenant_domain WHERE ga.can_send=true AND ga.object_id IS NOT NULL AND gt.status='active' ORDER BY RANDOM() LIMIT 1")->fetch(PDO::FETCH_ASSOC); echo json_encode($s?['ok'=>1,'sender'=>$s['email'],'tenant'=>$s['tenant_domain']]:['ok'=>0,'error'=>'No sender']);break; case 'throttle_config': echo json_encode(['ok'=>1,'throttle'=>['gmail.com'=>['per_hour'=>50,'per_day'=>200,'delay_ms'=>3000],'outlook.com'=>['per_hour'=>40,'per_day'=>150,'delay_ms'=>4000],'yahoo.com'=>['per_hour'=>30,'per_day'=>100,'delay_ms'=>5000],'default'=>['per_hour'=>100,'per_day'=>500,'delay_ms'=>2000]]]);break; case 'engagement_score': $dist=[];foreach($db->query("SELECT score,COUNT(*) as cnt FROM send_contacts WHERE status='active' GROUP BY score ORDER BY cnt DESC") as $r)$dist[]=$r; echo json_encode(['ok'=>1,'distribution'=>$dist]);break; case 'preview': if($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['error'=>'POST']);break;} $d=json_decode(file_get_contents('php://input'),true); echo json_encode(['ok'=>1,'preview'=>'