Files
wevads-platform/scripts/api_domains-pool.php
2026-02-26 04:53:11 +01:00

236 lines
9.8 KiB
PHP
Executable File

<?php
/**
* API Domains Pool - Gestion centralisée des domaines de send
* Sources: Cloudflare, FreeDNS, Namecheap
*/
header('Content-Type: application/json');
$pdo = new PDO("pgsql:host=localhost;dbname=adx_system", "admin", "admin123");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$action = $_GET['action'] ?? $_POST['action'] ?? '';
$input = json_decode(file_get_contents('php://input'), true) ?? [];
// Config Cloudflare
function getCFConfig() {
$file = '/opt/wevads/storage/office365/cloudflare_config.json';
return file_exists($file) ? json_decode(file_get_contents($file), true) : [];
}
// Appel API Cloudflare
function cfApi($endpoint, $method = 'GET', $data = null) {
$config = getCFConfig();
if (!$config['api_key']) return ['success' => false, 'error' => 'No API key'];
$ch = curl_init("https://api.cloudflare.com/client/v4/$endpoint");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Auth-Email: {$config['api_email']}",
"X-Auth-Key: {$config['api_key']}",
"Content-Type: application/json"
],
CURLOPT_TIMEOUT => 30
]);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
} elseif ($method === 'DELETE') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
}
$result = curl_exec($ch);
curl_close($ch);
return json_decode($result, true);
}
switch ($action) {
// === LISTER DOMAINES ===
case 'list':
$status = $_GET['status'] ?? null;
$provider = $_GET['provider'] ?? null;
$limit = min((int)($_GET['limit'] ?? 100), 500);
$sql = "SELECT * FROM admin.domains_pool WHERE 1=1";
$params = [];
if ($status) { $sql .= " AND status = ?"; $params[] = $status; }
if ($provider) { $sql .= " AND provider = ?"; $params[] = $provider; }
$sql .= " ORDER BY created_at DESC LIMIT $limit";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
echo json_encode(['success' => true, 'domains' => $stmt->fetchAll(PDO::FETCH_ASSOC)]);
break;
// === STATS ===
case 'stats':
$stats = $pdo->query("
SELECT
COUNT(*) as total,
COUNT(CASE WHEN status = 'FREE' THEN 1 END) as free,
COUNT(CASE WHEN status = 'ASSIGNED' THEN 1 END) as assigned,
COUNT(CASE WHEN verification = 'VERIFIED' THEN 1 END) as verified,
COUNT(CASE WHEN dns_complete = true THEN 1 END) as dns_complete,
COUNT(CASE WHEN provider = 'cloudflare' THEN 1 END) as cloudflare,
COUNT(CASE WHEN provider = 'freedns' THEN 1 END) as freedns,
COUNT(CASE WHEN provider = 'office365' THEN 1 END) as office365
FROM admin.domains_pool
")->fetch(PDO::FETCH_ASSOC);
echo json_encode(['success' => true, 'stats' => $stats]);
break;
// === SYNC CLOUDFLARE ZONES ===
case 'sync_cloudflare':
$result = cfApi('zones?per_page=50');
if (!$result['success']) {
echo json_encode(['success' => false, 'error' => $result['errors'][0]['message'] ?? 'CF API error']);
break;
}
$synced = 0;
foreach ($result['result'] as $zone) {
// Insérer dans cloudflare_zones
$stmt = $pdo->prepare("INSERT INTO admin.cloudflare_zones (zone_id, domain, status)
VALUES (?, ?, ?) ON CONFLICT (zone_id) DO UPDATE SET status = EXCLUDED.status");
$stmt->execute([$zone['id'], $zone['name'], $zone['status']]);
$synced++;
}
echo json_encode(['success' => true, 'synced' => $synced, 'zones' => $result['result']]);
break;
// === CRÉER SOUS-DOMAINE CLOUDFLARE ===
case 'create_cf_subdomain':
$config = getCFConfig();
$subdomain = $input['subdomain'] ?? substr(md5(rand()), 0, 5);
$baseDomain = $input['base_domain'] ?? $config['base_domain'];
$zoneId = $input['zone_id'] ?? $config['zone_id'];
$fullDomain = "$subdomain.$baseDomain";
// Créer les records DNS pour Office365
$mxTarget = "$fullDomain.mail.protection.outlook.com";
$records = [
['type' => 'MX', 'name' => $fullDomain, 'content' => $mxTarget, 'priority' => 10],
['type' => 'TXT', 'name' => $fullDomain, 'content' => "v=spf1 include:spf.protection.outlook.com -all"],
['type' => 'CNAME', 'name' => "autodiscover.$fullDomain", 'content' => 'autodiscover.outlook.com']
];
$recordIds = [];
$errors = [];
foreach ($records as $rec) {
$res = cfApi("zones/$zoneId/dns_records", 'POST', $rec);
if ($res['success']) {
$recordIds[$rec['type']] = $res['result']['id'];
} else {
$errors[] = $rec['type'] . ': ' . ($res['errors'][0]['message'] ?? 'error');
}
}
if (count($recordIds) >= 2) {
// Insérer dans domains_pool
$stmt = $pdo->prepare("INSERT INTO admin.domains_pool
(domain, zone_id, zone_name, provider, status, verification, mx_content, spf_record, cf_record_ids, created_at)
VALUES (?, ?, ?, 'cloudflare', 'FREE', 'PENDING', ?, ?, ?, NOW())
ON CONFLICT (domain) DO UPDATE SET updated_at = NOW()
RETURNING id");
$stmt->execute([
$fullDomain, $zoneId, $baseDomain, $mxTarget,
"v=spf1 include:spf.protection.outlook.com -all",
json_encode($recordIds)
]);
$id = $stmt->fetchColumn();
echo json_encode(['success' => true, 'domain' => $fullDomain, 'id' => $id, 'records' => $recordIds, 'errors' => $errors]);
} else {
echo json_encode(['success' => false, 'errors' => $errors]);
}
break;
// === ASSIGNER DOMAINE À COMPTE OFFICE ===
case 'assign':
$domainId = $input['domain_id'] ?? 0;
$officeAccountId = $input['office_account_id'] ?? 0;
if (!$domainId || !$officeAccountId) {
echo json_encode(['success' => false, 'error' => 'domain_id and office_account_id required']);
break;
}
$stmt = $pdo->prepare("UPDATE admin.domains_pool
SET office_account_id = ?, status = 'ASSIGNED', updated_at = NOW()
WHERE id = ? AND status = 'FREE'");
$stmt->execute([$officeAccountId, $domainId]);
echo json_encode(['success' => $stmt->rowCount() > 0, 'updated' => $stmt->rowCount()]);
break;
// === LIBÉRER DOMAINE ===
case 'release':
$domainId = $input['domain_id'] ?? 0;
$stmt = $pdo->prepare("UPDATE admin.domains_pool
SET office_account_id = NULL, status = 'FREE', updated_at = NOW()
WHERE id = ?");
$stmt->execute([$domainId]);
echo json_encode(['success' => true, 'released' => $stmt->rowCount()]);
break;
// === SUPPRIMER DOMAINE ===
case 'delete':
$domainId = $input['domain_id'] ?? 0;
// Récupérer les infos pour supprimer de CF
$domain = $pdo->query("SELECT * FROM admin.domains_pool WHERE id = $domainId")->fetch(PDO::FETCH_ASSOC);
if ($domain && $domain['provider'] === 'cloudflare' && $domain['cf_record_ids']) {
$recordIds = json_decode($domain['cf_record_ids'], true);
foreach ($recordIds as $recId) {
cfApi("zones/{$domain['zone_id']}/dns_records/$recId", 'DELETE');
}
}
$stmt = $pdo->prepare("DELETE FROM admin.domains_pool WHERE id = ?");
$stmt->execute([$domainId]);
echo json_encode(['success' => true, 'deleted' => $stmt->rowCount()]);
break;
// === VÉRIFIER DNS COMPLET ===
case 'check_dns':
$domainId = $input['domain_id'] ?? $_GET['domain_id'] ?? 0;
$domain = $pdo->query("SELECT * FROM admin.domains_pool WHERE id = $domainId")->fetch(PDO::FETCH_ASSOC);
if (!$domain) {
echo json_encode(['success' => false, 'error' => 'Domain not found']);
break;
}
$dnsComplete = !empty($domain['mx_content']) && !empty($domain['spf_record']);
$pdo->prepare("UPDATE admin.domains_pool SET dns_complete = ?, last_dns_check = NOW() WHERE id = ?")
->execute([$dnsComplete, $domainId]);
echo json_encode(['success' => true, 'domain' => $domain['domain'], 'dns_complete' => $dnsComplete]);
break;
// === OBTENIR DOMAINE LIBRE POUR ENVOI ===
case 'get_free_for_send':
$officeAccountId = $_GET['office_account_id'] ?? 0;
// Priorité: domaines déjà assignés à ce compte, sinon FREE avec DNS complet
$sql = "SELECT * FROM admin.domains_pool
WHERE (office_account_id = ? OR (status = 'FREE' AND dns_complete = true))
AND verification = 'VERIFIED'
ORDER BY CASE WHEN office_account_id = ? THEN 0 ELSE 1 END,
provider ASC -- freedns first
LIMIT 1";
$stmt = $pdo->prepare($sql);
$stmt->execute([$officeAccountId, $officeAccountId]);
$domain = $stmt->fetch(PDO::FETCH_ASSOC);
echo json_encode(['success' => (bool)$domain, 'domain' => $domain]);
break;
default:
echo json_encode([
'error' => 'Invalid action',
'available' => ['list', 'stats', 'sync_cloudflare', 'create_cf_subdomain', 'assign', 'release', 'delete', 'check_dns', 'get_free_for_send']
]);
}