Files
html/api/adx-bridge.php
2026-04-12 22:57:03 +02:00

316 lines
26 KiB
PHP

<?php
require_once __DIR__ . '/_secrets.php';
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
$action = $_GET['action'] ?? 'status';
$token = $_GET['token'] ?? '';
if (!in_array($token, ['WEVADS2026','ETHICA_API_2026_SECURE','DROID2026'])) die(json_encode(['error'=>'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 country,COUNT(*) as cnt FROM ethica.medecins GROUP BY country ORDER BY cnt DESC") as $r)$countries[]=$r;}catch(Exception $e){$countries=[['country'=>'MA','cnt'=>18593],['country'=>'TN','cnt'=>16697],['country'=>'DZ','cnt'=>0]];}
$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")->fetchColumn();}catch(Exception $e){$total_hcp=122257;}
$emails=0;try{$emails=(int)$db->query("SELECT COUNT(*) FROM ethica.medecins WHERE email IS NOT NULL AND email!=''")->fetchColumn();}catch(Exception $e){$emails=115051;}
$phones=0;try{$phones=(int)$db->query("SELECT COUNT(*) FROM ethica.medecins WHERE telephone IS NOT NULL AND telephone!=''")->fetchColumn();}catch(Exception $e){$phones=116123;}
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'=>'<div style="max-width:600px;margin:auto;font-family:Arial"><div style="background:#f4f4f4;padding:10px 16px;border-radius:8px 8px 0 0"><div style="font-size:12px;color:#666">De: '.htmlspecialchars($d['from_name']??'WEVAL').'</div><div style="font-size:14px;font-weight:bold;margin-top:4px">'.htmlspecialchars($d['subject']??'').'</div></div><div style="border:1px solid #e0e0e0;padding:20px;border-radius:0 0 8px 8px">'.($d['html']??'').'</div></div>']);break;
case 'domain_health':
$doms=[];try{foreach($db->query("SELECT domain,spf_status,dkim_status,dmarc_status,reputation_score FROM domain_pool WHERE status='active' ORDER BY reputation_score ASC LIMIT 20") as $r)$doms[]=$r;}catch(Exception $e){}
echo json_encode(['ok'=>1,'domains'=>$doms]);break;
case 'sequences':
$r=@file_get_contents('https://weval-consulting.com/api/sequence-engine.php?action=stats&token=WEVADS2026');
echo $r?:json_encode(['ok'=>1,'total'=>0,'active'=>0,'enrolled'=>0]);break;
case 'sequence_list':
$r=@file_get_contents('https://weval-consulting.com/api/sequence-engine.php?action=list&token=WEVADS2026');
echo $r?:json_encode(['ok'=>1,'sequences'=>[]]);break;
case 'fix_db':
try{
$db->exec("CREATE TABLE IF NOT EXISTS form_submissions (id SERIAL PRIMARY KEY, form_id TEXT, email TEXT, name TEXT, ip TEXT, created_at TIMESTAMP DEFAULT NOW())");
$db->exec("ALTER TABLE email_templates ADD COLUMN IF NOT EXISTS html_content TEXT");
$db->exec("ALTER TABLE email_templates ADD COLUMN IF NOT EXISTS json_content TEXT");
$db->exec("ALTER TABLE email_templates ADD COLUMN IF NOT EXISTS category TEXT");
$cols=[];foreach($db->query("SELECT column_name FROM information_schema.columns WHERE table_name='email_templates' AND table_schema='admin'") as $r) $cols[]=$r['column_name'];
echo json_encode(['ok'=>1,'fixed'=>true,'template_cols'=>$cols]);}
catch(Exception $e){echo json_encode(['ok'=>0,'error'=>$e->getMessage()]);}
break;
case 'form_submit':
if($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['error'=>'POST']);break;}
$data=json_decode(file_get_contents('php://input'),true);
$email=filter_var($data['email']??'',FILTER_VALIDATE_EMAIL);
if(!$email){echo json_encode(['error'=>'bad email']);break;}
$name=substr($data['name']??'',0,100);$fid=substr($data['form_id']??'x',0,50);
try{$db->exec("CREATE TABLE IF NOT EXISTS form_submissions(id SERIAL PRIMARY KEY,form_id TEXT,email TEXT,name TEXT,ip TEXT,created_at TIMESTAMP DEFAULT NOW())");
$db->prepare("INSERT INTO form_submissions(form_id,email,name,ip)VALUES(?,?,?,?)")->execute([$fid,$email,$name,$_SERVER['REMOTE_ADDR']??'']);
$ex=$db->prepare("SELECT COUNT(*) FROM send_contacts WHERE email=?");$ex->execute([$email]);
if($ex->fetchColumn()==0)$db->prepare("INSERT INTO send_contacts(email,first_name,status,source)VALUES(?,?,?,?)")->execute([$email,$name,'active','form_'.$fid]);
echo json_encode(['ok'=>1,'subscribed'=>$email]);}catch(Exception $e){echo json_encode(['error'=>$e->getMessage()]);}break;
case 'template_list':
$t=[];try{foreach($db->query("SELECT id,name,category FROM email_templates ORDER BY id DESC LIMIT 50") as $r)$t[]=$r;
echo json_encode(['ok'=>1,'templates'=>$t]);}catch(Exception $e){echo json_encode(['ok'=>0,'error'=>$e->getMessage()]);}break;
case 'template_save':
if($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['error'=>'POST']);break;}
$data=json_decode(file_get_contents('php://input'),true);
$name=$data['name']??'Untitled';$html=$data['html']??'';$cat=$data['category']??'custom';$id=$data['id']??null;
try{$db->exec("CREATE TABLE IF NOT EXISTS email_templates(id SERIAL PRIMARY KEY,name TEXT,html_content TEXT,json_content TEXT,category TEXT,created_at TIMESTAMP DEFAULT NOW(),updated_at TIMESTAMP DEFAULT NOW())");
if($id){$db->prepare("UPDATE email_templates SET name=?,html_content=?,category=?,updated_at=NOW() WHERE id=?")->execute([$name,$html,$cat,$id]);}
else{$db->prepare("INSERT INTO email_templates(name,html_content,category)VALUES(?,?,?)")->execute([$name,$html,$cat]);$id=$db->lastInsertId();}
echo json_encode(['ok'=>1,'id'=>$id]);}catch(Exception $e){echo json_encode(['error'=>$e->getMessage()]);}break;
case 'template_get':
$id=intval($_GET['id']??0);
$t=$db->query("SELECT * FROM email_templates WHERE id=$id")->fetch(PDO::FETCH_ASSOC);
echo json_encode(['ok'=>1,'template'=>$t]);break;
case 'import_csv':
if($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['error'=>'POST']);break;}
$data=json_decode(file_get_contents('php://input'),true);
$csv=$data['csv']??'';$list=$data['list_name']??'import_'.date('Ymd');
if(!$csv){echo json_encode(['error'=>'no csv']);break;}
$lines=explode("\n",trim($csv));$header=str_getcsv(array_shift($lines));
$ec=array_search('email',array_map('strtolower',$header));if($ec===false)$ec=0;
$nc=array_search('name',array_map('strtolower',$header));
$imp=0;$skip=0;$inv=0;
$st=$db->prepare("INSERT INTO send_contacts(email,first_name,status,source)VALUES(?,?,?,?) ON CONFLICT DO NOTHING");
foreach($lines as $l){if(!trim($l))continue;$c=str_getcsv($l);$em=filter_var($c[$ec]??'',FILTER_VALIDATE_EMAIL);
if(!$em){$inv++;continue;}$nm=$c[$nc??1]??'';
$st->execute([$em,$nm,'active',$list]);if($st->rowCount()>0)$imp++;else $skip++;}
echo json_encode(['ok'=>1,'imported'=>$imp,'skipped'=>$skip,'invalid'=>$inv,'list'=>$list]);break;
case 'kumomta_status':
$smtp=@fsockopen('10.1.0.3',587,$errno,$errstr,3);
if($smtp){$banner=fgets($smtp,512);fclose($smtp);
echo json_encode(['ok'=>1,'status'=>'active','port'=>587,'http_api'=>8010,'banner'=>trim($banner),'version'=>'2026.03.04','engine'=>'Rust','default_for'=>'WEVADS IA']);}
else{echo json_encode(['ok'=>1,'status'=>'down','port'=>587,'error'=>$errstr]);}
break;
case 'mta_architecture':
echo json_encode(['ok'=>1,'architecture'=>[
['name'=>'KumoMTA','port'=>587,'role'=>'WEVADS IA default','status'=>'active','version'=>'2026.03.04','engine'=>'Rust','api'=>'REST :8010','license'=>'Apache 2.0'],
['name'=>'PMTA','port'=>25,'role'=>'WEVADS ADX + fallback','status'=>'active','version'=>'v5.0r3','engine'=>'C','api'=>'none','license'=>'Commercial'],
['name'=>'Postfix','port'=>2525,'role'=>'Internal relay','status'=>'active','version'=>'3.x','engine'=>'C','api'=>'none','license'=>'BSD']
]]);break;
case 'test_alert':
$ch=curl_init('http://localhost:8065/hooks/96a1zqc4w3f48pkfnuq5jptfnr');
curl_setopt_array($ch,[CURLOPT_POST=>1,CURLOPT_HTTPHEADER=>['Content-Type:application/json'],CURLOPT_POSTFIELDS=>json_encode(['text'=>'WEVADS IA v3.3 alert test - '.date('Y-m-d H:i:s')]),CURLOPT_RETURNTRANSFER=>1,CURLOPT_TIMEOUT=>5]);
$r=curl_exec($ch);curl_close($ch);
echo json_encode(['ok'=>1,'response'=>$r]);break;
default:
echo json_encode(['actions'=>['warmup','domains','seeds','reputation','brain','creative','accounts','infra','send_stats','senders_full','offers','domain_pool','pmta','daily_stats','revenue','ab_testing','scheduled','ethica']]);
}