526 lines
22 KiB
PHP
Executable File
526 lines
22 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* FreeDNS Factory — Full automation with captcha solving
|
|
* Endpoint: /api/freedns.php
|
|
* Called by N8N workflow "FreeDNS Auto Creator (5 domains/account)"
|
|
*
|
|
* Actions: check_accounts_needed, create_account, create_domains,
|
|
* configure_dns_records, verify_propagation, stats, list_domains
|
|
*/
|
|
require_once("/opt/wevads/config/credentials.php");
|
|
header('Content-Type: application/json');
|
|
header('Access-Control-Allow-Origin: *');
|
|
|
|
$action = $_GET['action'] ?? $_POST['action'] ?? json_decode(file_get_contents('php://input'),true)['action'] ?? 'stats';
|
|
$db = get_pg("adx_system");
|
|
if(!$db){echo json_encode(['status'=>'error','message'=>'DB error']);exit;}
|
|
pg_query($db,"SET search_path TO admin");
|
|
|
|
// FreeDNS base URL
|
|
define('FREEDNS_BASE','https://freedns.afraid.org');
|
|
// Config from DB
|
|
$cfg = [];
|
|
$r = pg_query($db,"SELECT config_key,config_value FROM admin.freedns_config");
|
|
while($row=pg_fetch_assoc($r)) $cfg[$row['config_key']] = $row['config_value'];
|
|
|
|
$DOMAINS_PER_ACCOUNT = (int)($cfg['domains_per_account'] ?? 5);
|
|
$TRACKING_IP = $cfg['tracking_ip'] ?? '151.80.235.110';
|
|
$BAD_TLDS = explode(',', $cfg['bad_tlds'] ?? '.ru,.tk,.xyz');
|
|
$PREFERRED_TLDS = explode(',', $cfg['preferred_tlds'] ?? '.com,.net,.org');
|
|
$MAX_HOSTS = (int)($cfg['max_hosts_threshold'] ?? 50000);
|
|
|
|
// ======= CAPTCHA SOLVER =======
|
|
function solveCaptcha($siteKey, $pageUrl, $type='recaptcha_v2') {
|
|
global $db;
|
|
// Get active captcha service with API key
|
|
$r = pg_query($db,"SELECT name,api_key,api_url FROM admin.captcha_services WHERE status='active' AND api_key IS NOT NULL AND api_key!='' ORDER BY priority LIMIT 1");
|
|
$svc = pg_fetch_assoc($r);
|
|
if(!$svc) return ['error'=>'No captcha service configured with API key'];
|
|
|
|
$name = $svc['name'];
|
|
$key = $svc['api_key'];
|
|
|
|
if($name === '2captcha') {
|
|
return solve2Captcha($key, $siteKey, $pageUrl, $type);
|
|
} elseif($name === 'anticaptcha') {
|
|
return solveAntiCaptcha($key, $siteKey, $pageUrl, $type);
|
|
} elseif($name === 'capsolver') {
|
|
return solveCapsolver($key, $siteKey, $pageUrl, $type);
|
|
}
|
|
return ['error'=>"Unknown captcha service: $name"];
|
|
}
|
|
|
|
function solve2Captcha($apiKey, $siteKey, $pageUrl, $type) {
|
|
// Step 1: Submit task
|
|
$params = [
|
|
'key' => $apiKey,
|
|
'method' => 'userrecaptcha',
|
|
'googlekey' => $siteKey,
|
|
'pageurl' => $pageUrl,
|
|
'json' => 1
|
|
];
|
|
$ch = curl_init('https://2captcha.com/in.php?' . http_build_query($params));
|
|
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_TIMEOUT=>30]);
|
|
$resp = json_decode(curl_exec($ch), true);
|
|
curl_close($ch);
|
|
if(($resp['status'] ?? 0) != 1) return ['error'=>'2captcha submit failed: '.($resp['request']??'unknown')];
|
|
$taskId = $resp['request'];
|
|
|
|
// Step 2: Poll for result (max 120s)
|
|
for($i=0; $i<24; $i++) {
|
|
sleep(5);
|
|
$ch = curl_init("https://2captcha.com/res.php?key={$apiKey}&action=get&id={$taskId}&json=1");
|
|
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_TIMEOUT=>15]);
|
|
$resp = json_decode(curl_exec($ch), true);
|
|
curl_close($ch);
|
|
if(($resp['status'] ?? 0) == 1) return ['token'=>$resp['request']];
|
|
if(($resp['request'] ?? '') !== 'CAPCHA_NOT_READY') return ['error'=>'2captcha: '.($resp['request']??'failed')];
|
|
}
|
|
return ['error'=>'2captcha timeout'];
|
|
}
|
|
|
|
function solveAntiCaptcha($apiKey, $siteKey, $pageUrl, $type) {
|
|
$payload = json_encode([
|
|
'clientKey' => $apiKey,
|
|
'task' => [
|
|
'type' => 'RecaptchaV2TaskProxyless',
|
|
'websiteURL' => $pageUrl,
|
|
'websiteKey' => $siteKey
|
|
]
|
|
]);
|
|
$ch = curl_init('https://api.anti-captcha.com/createTask');
|
|
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_POST=>1, CURLOPT_POSTFIELDS=>$payload, CURLOPT_HTTPHEADER=>['Content-Type: application/json'], CURLOPT_TIMEOUT=>30]);
|
|
$resp = json_decode(curl_exec($ch), true);
|
|
curl_close($ch);
|
|
if(($resp['errorId'] ?? 1) != 0) return ['error'=>'anticaptcha: '.($resp['errorDescription']??'submit failed')];
|
|
$taskId = $resp['taskId'];
|
|
|
|
for($i=0; $i<24; $i++) {
|
|
sleep(5);
|
|
$ch = curl_init('https://api.anti-captcha.com/getTaskResult');
|
|
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_POST=>1, CURLOPT_POSTFIELDS=>json_encode(['clientKey'=>$apiKey,'taskId'=>$taskId]), CURLOPT_HTTPHEADER=>['Content-Type: application/json'], CURLOPT_TIMEOUT=>15]);
|
|
$resp = json_decode(curl_exec($ch), true);
|
|
curl_close($ch);
|
|
if(($resp['status'] ?? '') === 'ready') return ['token'=>$resp['solution']['gRecaptchaResponse']];
|
|
if(($resp['status'] ?? '') !== 'processing') return ['error'=>'anticaptcha: '.($resp['errorDescription']??'failed')];
|
|
}
|
|
return ['error'=>'anticaptcha timeout'];
|
|
}
|
|
|
|
function solveCapsolver($apiKey, $siteKey, $pageUrl, $type) {
|
|
$payload = json_encode([
|
|
'clientKey' => $apiKey,
|
|
'task' => [
|
|
'type' => 'ReCaptchaV2TaskProxyLess',
|
|
'websiteURL' => $pageUrl,
|
|
'websiteKey' => $siteKey
|
|
]
|
|
]);
|
|
$ch = curl_init('https://api.capsolver.com/createTask');
|
|
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_POST=>1, CURLOPT_POSTFIELDS=>$payload, CURLOPT_HTTPHEADER=>['Content-Type: application/json'], CURLOPT_TIMEOUT=>30]);
|
|
$resp = json_decode(curl_exec($ch), true);
|
|
curl_close($ch);
|
|
if(($resp['errorId'] ?? 1) != 0) return ['error'=>'capsolver: '.($resp['errorDescription']??'submit failed')];
|
|
$taskId = $resp['taskId'];
|
|
|
|
for($i=0; $i<24; $i++) {
|
|
sleep(5);
|
|
$ch = curl_init('https://api.capsolver.com/getTaskResult');
|
|
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_POST=>1, CURLOPT_POSTFIELDS=>json_encode(['clientKey'=>$apiKey,'taskId'=>$taskId]), CURLOPT_HTTPHEADER=>['Content-Type: application/json'], CURLOPT_TIMEOUT=>15]);
|
|
$resp = json_decode(curl_exec($ch), true);
|
|
curl_close($ch);
|
|
if(($resp['status'] ?? '') === 'ready') return ['token'=>$resp['solution']['gRecaptchaResponse']];
|
|
if(($resp['status'] ?? '') !== 'processing') return ['error'=>'capsolver: '.($resp['errorDescription']??'failed')];
|
|
}
|
|
return ['error'=>'capsolver timeout'];
|
|
}
|
|
|
|
// ======= FreeDNS HTTP Client =======
|
|
function freednsRequest($url, $cookie='', $post=null) {
|
|
$ch = curl_init($url);
|
|
$opts = [
|
|
CURLOPT_RETURNTRANSFER => 1,
|
|
CURLOPT_FOLLOWLOCATION => 1,
|
|
CURLOPT_TIMEOUT => 30,
|
|
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
CURLOPT_HEADER => 1,
|
|
CURLOPT_SSL_VERIFYPEER => false,
|
|
];
|
|
if($cookie) $opts[CURLOPT_COOKIE] = $cookie;
|
|
if($post !== null) {
|
|
$opts[CURLOPT_POST] = 1;
|
|
$opts[CURLOPT_POSTFIELDS] = is_array($post) ? http_build_query($post) : $post;
|
|
}
|
|
curl_setopt_array($ch, $opts);
|
|
$resp = curl_exec($ch);
|
|
$hsize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|
$headers = substr($resp, 0, $hsize);
|
|
$body = substr($resp, $hsize);
|
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
// Extract cookies
|
|
preg_match_all('/Set-Cookie:\s*([^;]+)/i', $headers, $m);
|
|
$cookies = implode('; ', $m[1] ?? []);
|
|
|
|
return ['code'=>$code, 'body'=>$body, 'cookies'=>$cookies, 'headers'=>$headers];
|
|
}
|
|
|
|
// ======= ACTIONS =======
|
|
switch($action) {
|
|
|
|
case 'check_accounts_needed':
|
|
$r = pg_query($db,"SELECT COUNT(*) as c FROM admin.freedns_accounts WHERE status='active'");
|
|
$cnt = (int)pg_fetch_assoc($r)['c'];
|
|
$r2 = pg_query($db,"SELECT COUNT(*) as c FROM admin.freedns_domains WHERE status='active'");
|
|
$doms = (int)pg_fetch_assoc($r2)['c'];
|
|
// Need more accounts if we have less than 3 active or < 15 active domains
|
|
$need = ($cnt < 3 || $doms < 15);
|
|
echo json_encode([
|
|
'status'=>'success',
|
|
'accounts'=>$cnt,
|
|
'domains'=>$doms,
|
|
'need_more'=>$need,
|
|
'target_accounts'=>max(3, $cnt),
|
|
'target_domains'=>max(15, $doms)
|
|
]);
|
|
break;
|
|
|
|
case 'create_account':
|
|
// Step 1: Get signup page to find captcha sitekey
|
|
$signup = freednsRequest(FREEDNS_BASE . '/signup/');
|
|
$body = $signup['body'];
|
|
$cookie = $signup['cookies'];
|
|
|
|
// Extract reCAPTCHA sitekey
|
|
$siteKey = '';
|
|
if(preg_match('/data-sitekey="([^"]+)"/', $body, $m)) {
|
|
$siteKey = $m[1];
|
|
} elseif(preg_match('/recaptcha.*?sitekey.*?["\']([0-9A-Za-z_-]+)["\']/', $body, $m)) {
|
|
$siteKey = $m[1];
|
|
}
|
|
|
|
// Generate random credentials
|
|
$user = 'wvd_' . substr(md5(uniqid(mt_rand(),true)),0,8);
|
|
$pass = bin2hex(random_bytes(8));
|
|
$email = $user . '@' . ['yopmail.com','guerrillamail.com','tempail.com'][mt_rand(0,2)];
|
|
|
|
// Step 2: Solve captcha if present
|
|
$captchaToken = '';
|
|
if($siteKey) {
|
|
$result = solveCaptcha($siteKey, FREEDNS_BASE . '/signup/');
|
|
if(isset($result['error'])) {
|
|
echo json_encode(['status'=>'error','message'=>'Captcha failed: '.$result['error'],'sitekey'=>$siteKey]);
|
|
break;
|
|
}
|
|
$captchaToken = $result['token'];
|
|
}
|
|
|
|
// Step 3: Submit registration
|
|
$postData = [
|
|
'username' => $user,
|
|
'password' => $pass,
|
|
'password2' => $pass,
|
|
'email' => $email,
|
|
'action' => 'signup',
|
|
];
|
|
if($captchaToken) {
|
|
$postData['g-recaptcha-response'] = $captchaToken;
|
|
}
|
|
|
|
$resp = freednsRequest(FREEDNS_BASE . '/signup/submit.php', $cookie, $postData);
|
|
|
|
// Check success
|
|
$success = (strpos($resp['body'],'activation') !== false || strpos($resp['body'],'success') !== false || $resp['code'] == 302);
|
|
|
|
if($success || strpos($resp['body'],'already') === false) {
|
|
// Store account
|
|
$u = pg_escape_string($db, $user);
|
|
$p = pg_escape_string($db, $pass);
|
|
$e = pg_escape_string($db, $email);
|
|
$ck = pg_escape_string($db, $resp['cookies'] ?: $cookie);
|
|
pg_query($db,"INSERT INTO admin.freedns_accounts(username,password,cookie,domains_count,max_domains,status) VALUES('$u','$p','$ck',0,$DOMAINS_PER_ACCOUNT,'pending_activation')");
|
|
|
|
echo json_encode([
|
|
'status'=>'success',
|
|
'message'=>'Account created, pending activation',
|
|
'username'=>$user,
|
|
'email'=>$email,
|
|
'captcha_solved'=>!empty($captchaToken),
|
|
'http_code'=>$resp['code']
|
|
]);
|
|
} else {
|
|
echo json_encode([
|
|
'status'=>'error',
|
|
'message'=>'Registration failed',
|
|
'http_code'=>$resp['code'],
|
|
'snippet'=>substr(strip_tags($resp['body']),0,300)
|
|
]);
|
|
}
|
|
break;
|
|
|
|
case 'activate_account':
|
|
$uid = (int)($_GET['account_id'] ?? $_POST['account_id'] ?? 0);
|
|
if(!$uid) { echo json_encode(['error'=>'account_id required']); break; }
|
|
|
|
$r = pg_query($db,"SELECT * FROM admin.freedns_accounts WHERE id=$uid");
|
|
$acc = pg_fetch_assoc($r);
|
|
if(!$acc) { echo json_encode(['error'=>'Account not found']); break; }
|
|
|
|
// Login to get fresh cookie
|
|
$loginResp = freednsRequest(FREEDNS_BASE . '/zc.php?from=L2luZGV4Lmh0bWw=', '', [
|
|
'username' => $acc['username'],
|
|
'password' => $acc['password'],
|
|
'submit' => 'Login',
|
|
'action' => 'auth'
|
|
]);
|
|
|
|
if($loginResp['cookies']) {
|
|
$ck = pg_escape_string($db, $loginResp['cookies']);
|
|
pg_query($db,"UPDATE admin.freedns_accounts SET cookie='$ck', status='active', last_login=NOW() WHERE id=$uid");
|
|
echo json_encode(['status'=>'success','message'=>'Account activated','username'=>$acc['username']]);
|
|
} else {
|
|
echo json_encode(['status'=>'error','message'=>'Login failed','http_code'=>$loginResp['code']]);
|
|
}
|
|
break;
|
|
|
|
case 'create_domains':
|
|
$count = (int)($_GET['count'] ?? $_POST['count'] ?? $DOMAINS_PER_ACCOUNT);
|
|
$accountId = (int)($_GET['account_id'] ?? $_POST['account_id'] ?? 0);
|
|
|
|
// Get active account with capacity
|
|
$where = $accountId ? "AND id=$accountId" : "";
|
|
$r = pg_query($db,"SELECT * FROM admin.freedns_accounts WHERE status='active' AND domains_count < max_domains $where ORDER BY domains_count ASC LIMIT 1");
|
|
$acc = pg_fetch_assoc($r);
|
|
if(!$acc) { echo json_encode(['status'=>'error','message'=>'No active FreeDNS account with capacity']); break; }
|
|
|
|
// Step 1: Get list of available shared domains
|
|
$page = freednsRequest(FREEDNS_BASE . '/domain/registry/', $acc['cookie']);
|
|
$body = $page['body'];
|
|
|
|
// Parse available domains - look for high-reputation ones
|
|
$available = [];
|
|
// FreeDNS registry page lists domains with host counts
|
|
if(preg_match_all('/<tr[^>]*>.*?<td[^>]*>(.*?)<\/td>.*?<td[^>]*>(\d+)<\/td>/si', $body, $matches, PREG_SET_ORDER)) {
|
|
foreach($matches as $m) {
|
|
$dom = trim(strip_tags($m[1]));
|
|
$hosts = (int)$m[2];
|
|
if(!$dom || !$hosts) continue;
|
|
|
|
// Filter: skip bad TLDs
|
|
$skip = false;
|
|
foreach($BAD_TLDS as $bad) {
|
|
if(substr($dom, -strlen(trim($bad))) === trim($bad)) { $skip = true; break; }
|
|
}
|
|
if($skip) continue;
|
|
|
|
// Prefer domains with moderate hosts (not too new, not too crowded)
|
|
$score = 0;
|
|
foreach($PREFERRED_TLDS as $pref) {
|
|
if(substr($dom, -strlen(trim($pref))) === trim($pref)) $score += 50;
|
|
}
|
|
if($hosts > 100 && $hosts < $MAX_HOSTS) $score += 30;
|
|
if($hosts > 1000) $score += 20;
|
|
|
|
$available[] = ['domain'=>$dom, 'hosts'=>$hosts, 'score'=>$score];
|
|
}
|
|
}
|
|
|
|
// Sort by score desc
|
|
usort($available, fn($a,$b) => $b['score'] - $a['score']);
|
|
|
|
// Also try parsing links format
|
|
if(empty($available)) {
|
|
if(preg_match_all('/href="[^"]*domain_id=(\d+)[^"]*"[^>]*>([^<]+)</', $body, $matches, PREG_SET_ORDER)) {
|
|
foreach($matches as $m) {
|
|
$available[] = ['domain'=>trim($m[2]), 'domain_id'=>$m[1], 'hosts'=>0, 'score'=>50];
|
|
}
|
|
}
|
|
}
|
|
|
|
$created = [];
|
|
$limit = min($count, count($available), $DOMAINS_PER_ACCOUNT - $acc['domains_count']);
|
|
|
|
for($i=0; $i<$limit; $i++) {
|
|
$base = $available[$i]['domain'];
|
|
$sub = 'mx' . substr(md5(uniqid(mt_rand(),true)),0,6);
|
|
$subdomain = $sub . '.' . $base;
|
|
|
|
// Create subdomain via FreeDNS API
|
|
$createResp = freednsRequest(FREEDNS_BASE . '/subdomain/save.php', $acc['cookie'], [
|
|
'type' => 'A',
|
|
'subdomain' => $sub,
|
|
'domain_id' => $available[$i]['domain_id'] ?? '',
|
|
'address' => $TRACKING_IP,
|
|
'ttl' => '',
|
|
'wildcard' => '',
|
|
'action' => 'save'
|
|
]);
|
|
|
|
// Extract freedns_id from response
|
|
$fid = '';
|
|
if(preg_match('/edit\.php\?data_id=(\d+)/', $createResp['body'], $m)) {
|
|
$fid = $m[1];
|
|
}
|
|
|
|
// Store in DB
|
|
$aid = $acc['id'];
|
|
$d = pg_escape_string($db, $subdomain);
|
|
$bd = pg_escape_string($db, $base);
|
|
$fi = pg_escape_string($db, $fid);
|
|
pg_query($db,"INSERT INTO admin.freedns_domains(account_id,domain,freedns_id,domain_type,base_domain,status) VALUES($aid,'$d','$fi','subdomain','$bd','active') ON CONFLICT DO NOTHING");
|
|
|
|
$created[] = ['subdomain'=>$subdomain, 'base'=>$base, 'freedns_id'=>$fid];
|
|
}
|
|
|
|
// Update account domain count
|
|
pg_query($db,"UPDATE admin.freedns_accounts SET domains_count = (SELECT COUNT(*) FROM admin.freedns_domains WHERE account_id={$acc['id']}) WHERE id={$acc['id']}");
|
|
|
|
echo json_encode([
|
|
'status'=>'success',
|
|
'created'=>count($created),
|
|
'domains'=>$created,
|
|
'available_bases'=>count($available),
|
|
'account'=>$acc['username'],
|
|
'top_bases'=>array_slice(array_map(fn($a)=>$a['domain'].' ('.$a['hosts'].' hosts)', $available),0,10)
|
|
]);
|
|
break;
|
|
|
|
case 'configure_dns_records':
|
|
// Configure MX, SPF, DKIM, DMARC for all domains without records
|
|
$r = pg_query($db,"SELECT fd.id, fd.domain, fd.freedns_id, fd.account_id, fa.cookie
|
|
FROM admin.freedns_domains fd
|
|
JOIN admin.freedns_accounts fa ON fd.account_id=fa.id
|
|
WHERE fd.status='active' AND NOT EXISTS(SELECT 1 FROM admin.freedns_records fr WHERE fr.domain_id=fd.id AND fr.record_type='MX')
|
|
LIMIT 10");
|
|
|
|
$configured = [];
|
|
while($dom = pg_fetch_assoc($r)) {
|
|
$did = $dom['id'];
|
|
$domain = $dom['domain'];
|
|
$cookie = $dom['cookie'];
|
|
$records = [];
|
|
|
|
// MX record → tracking server
|
|
$mxResp = freednsRequest(FREEDNS_BASE . '/subdomain/save.php', $cookie, [
|
|
'type'=>'MX', 'subdomain'=>'', 'domain_id'=>$dom['freedns_id'],
|
|
'address'=>$TRACKING_IP, 'ttl'=>'', 'mx'=>10, 'action'=>'save'
|
|
]);
|
|
$records[] = 'MX';
|
|
|
|
// TXT SPF
|
|
$spfResp = freednsRequest(FREEDNS_BASE . '/subdomain/save.php', $cookie, [
|
|
'type'=>'TXT', 'subdomain'=>'', 'domain_id'=>$dom['freedns_id'],
|
|
'address'=>"v=spf1 ip4:$TRACKING_IP ip4:89.167.1.139 ~all",
|
|
'ttl'=>'', 'action'=>'save'
|
|
]);
|
|
$records[] = 'SPF';
|
|
|
|
// TXT DMARC
|
|
$dmarcResp = freednsRequest(FREEDNS_BASE . '/subdomain/save.php', $cookie, [
|
|
'type'=>'TXT', 'subdomain'=>'_dmarc', 'domain_id'=>$dom['freedns_id'],
|
|
'address'=>'v=DMARC1; p=none; rua=mailto:dmarc@'.$domain,
|
|
'ttl'=>'', 'action'=>'save'
|
|
]);
|
|
$records[] = 'DMARC';
|
|
|
|
// Store records
|
|
foreach($records as $rec) {
|
|
pg_query($db,"INSERT INTO admin.freedns_records(domain_id,record_type,name,value) VALUES($did,'$rec','$domain','configured') ON CONFLICT DO NOTHING");
|
|
}
|
|
|
|
$configured[] = ['domain'=>$domain, 'records'=>$records];
|
|
}
|
|
|
|
echo json_encode(['status'=>'success','configured'=>count($configured),'domains'=>$configured]);
|
|
break;
|
|
|
|
case 'verify_propagation':
|
|
$r = pg_query($db,"SELECT fd.id, fd.domain FROM admin.freedns_domains fd WHERE fd.status='active' LIMIT 20");
|
|
$results = [];
|
|
while($dom = pg_fetch_assoc($r)) {
|
|
$dns = dns_get_record($dom['domain'], DNS_A);
|
|
$mx = dns_get_record($dom['domain'], DNS_MX);
|
|
$txt = dns_get_record($dom['domain'], DNS_TXT);
|
|
$propagated = !empty($dns);
|
|
$results[] = [
|
|
'domain'=>$dom['domain'],
|
|
'a_records'=>count($dns),
|
|
'mx_records'=>count($mx),
|
|
'txt_records'=>count($txt),
|
|
'propagated'=>$propagated
|
|
];
|
|
if($propagated) {
|
|
pg_query($db,"UPDATE admin.freedns_domains SET status='propagated' WHERE id={$dom['id']}");
|
|
}
|
|
}
|
|
echo json_encode(['status'=>'success','checked'=>count($results),'results'=>$results]);
|
|
break;
|
|
|
|
case 'sync_to_pool':
|
|
// Sync propagated FreeDNS domains to main domains_pool
|
|
$r = pg_query($db,"SELECT fd.domain, fd.base_domain FROM admin.freedns_domains fd WHERE fd.status IN ('active','propagated') AND NOT EXISTS(SELECT 1 FROM admin.domains_pool dp WHERE dp.domain=fd.domain)");
|
|
$synced = 0;
|
|
while($dom = pg_fetch_assoc($r)) {
|
|
$d = pg_escape_string($db, $dom['domain']);
|
|
$bd = pg_escape_string($db, $dom['base_domain']);
|
|
pg_query($db,"INSERT INTO admin.domains_pool(domain,status,provider,dns_provider,has_spf,has_dkim,has_dmarc) VALUES('$d','FREE','freedns','freedns',true,false,true) ON CONFLICT DO NOTHING");
|
|
$synced++;
|
|
}
|
|
echo json_encode(['status'=>'success','synced_to_pool'=>$synced]);
|
|
break;
|
|
|
|
case 'stats':
|
|
$acc = pg_fetch_assoc(pg_query($db,"SELECT COUNT(*) as total, COUNT(*) FILTER(WHERE status='active') as active FROM admin.freedns_accounts"));
|
|
$dom = pg_fetch_assoc(pg_query($db,"SELECT COUNT(*) as total, COUNT(*) FILTER(WHERE status='active') as active, COUNT(*) FILTER(WHERE status='propagated') as propagated FROM admin.freedns_domains"));
|
|
$rec = pg_fetch_assoc(pg_query($db,"SELECT COUNT(*) as total FROM admin.freedns_records"));
|
|
$cap = pg_fetch_assoc(pg_query($db,"SELECT COUNT(*) as total, COUNT(*) FILTER(WHERE status='active' AND api_key IS NOT NULL AND api_key!='') as ready FROM admin.captcha_services"));
|
|
$pool = pg_fetch_assoc(pg_query($db,"SELECT COUNT(*) as c FROM admin.domains_pool WHERE provider='freedns'"));
|
|
|
|
echo json_encode([
|
|
'status'=>'success',
|
|
'accounts'=>['total'=>(int)$acc['total'],'active'=>(int)$acc['active']],
|
|
'domains'=>['total'=>(int)$dom['total'],'active'=>(int)$dom['active'],'propagated'=>(int)$dom['propagated']],
|
|
'records'=>(int)$rec['total'],
|
|
'captcha_services'=>['total'=>(int)$cap['total'],'ready'=>(int)$cap['ready']],
|
|
'in_domains_pool'=>(int)$pool['c'],
|
|
'config'=>$cfg,
|
|
'health'=>((int)$cap['ready'] > 0) ? 'READY' : 'NEEDS_CAPTCHA_KEY'
|
|
]);
|
|
break;
|
|
|
|
case 'list_domains':
|
|
$limit = min((int)($_GET['limit'] ?? 50), 100);
|
|
$r = pg_query($db,"SELECT fd.*, fa.username as account FROM admin.freedns_domains fd JOIN admin.freedns_accounts fa ON fd.account_id=fa.id ORDER BY fd.created_at DESC LIMIT $limit");
|
|
$rows = [];
|
|
while($row=pg_fetch_assoc($r)) $rows[] = $row;
|
|
echo json_encode(['status'=>'success','data'=>$rows,'count'=>count($rows)]);
|
|
break;
|
|
|
|
case 'set_captcha_key':
|
|
$service = pg_escape_string($db, $_POST['service'] ?? $_GET['service'] ?? '');
|
|
$apiKey = pg_escape_string($db, $_POST['api_key'] ?? $_GET['api_key'] ?? '');
|
|
if(!$service || !$apiKey) { echo json_encode(['error'=>'service and api_key required']); break; }
|
|
pg_query($db,"UPDATE admin.captcha_services SET api_key='$apiKey', status='active' WHERE name='$service'");
|
|
echo json_encode(['status'=>'success','message'=>"Captcha key set for $service"]);
|
|
break;
|
|
|
|
default:
|
|
echo json_encode([
|
|
'status'=>'success',
|
|
'service'=>'freedns-factory',
|
|
'actions'=>[
|
|
'stats'=>'Dashboard stats',
|
|
'check_accounts_needed'=>'Check if more accounts needed (called by N8N)',
|
|
'create_account'=>'Create new FreeDNS account with captcha solving',
|
|
'activate_account'=>'Login and activate account (account_id required)',
|
|
'create_domains'=>'Create subdomains on high-rep bases (count, account_id optional)',
|
|
'configure_dns_records'=>'Set MX/SPF/DMARC on domains without records',
|
|
'verify_propagation'=>'Check DNS propagation',
|
|
'sync_to_pool'=>'Sync FreeDNS domains to main domains_pool',
|
|
'list_domains'=>'List all FreeDNS domains',
|
|
'set_captcha_key'=>'Configure captcha service (service=2captcha&api_key=xxx)'
|
|
]
|
|
]);
|
|
}
|