Files
wevads-platform/public/cloudflare-setup.php
2026-04-07 03:04:16 +02:00

506 lines
21 KiB
PHP

<?php
require_once('/opt/wevads/config/credentials.php');
/**
* WEVAL Cloudflare Setup
* - Ajouter API keys aux comptes
* - Rapatrier automatiquement les zones/domaines
* - Configurer DNS pour tracking
*/
session_start();
function getDB() {
static $pdo = null;
if ($pdo === null) {
$pdo = get_pdo('adx_system');
}
return $pdo;
}
function cfAPI($endpoint, $email, $apiKey, $method = 'GET', $data = null) {
$ch = curl_init("https://api.cloudflare.com/client/v4" . $endpoint);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-Auth-Email: ' . $email,
'X-Auth-Key: ' . $apiKey,
'Content-Type: application/json'
],
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_TIMEOUT => 30
]);
if ($data) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$resp = curl_exec($ch);
curl_close($ch);
return json_decode($resp, true);
}
// AJAX
if (isset($_REQUEST['action'])) {
header('Content-Type: application/json');
$pdo = getDB();
switch ($_REQUEST['action']) {
case 'accounts':
$stmt = $pdo->query("
SELECT ca.id, ca.email, ca.password,
CASE WHEN ca.api_key IS NOT NULL AND ca.api_key != '' THEN true ELSE false END as has_api,
ca.api_key,
COUNT(cz.id) as zones_count
FROM admin.cloudflare_accounts ca
LEFT JOIN admin.cloudflare_zones cz ON cz.account_id = ca.id
GROUP BY ca.id
ORDER BY ca.id
");
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
break;
case 'test_api':
$email = $_POST['email'] ?? '';
$apiKey = $_POST['api_key'] ?? '';
$result = cfAPI('/user', $email, $apiKey);
echo json_encode([
'success' => $result['success'] ?? false,
'email' => $result['result']['email'] ?? null
]);
break;
case 'save_api':
$id = $_POST['id'] ?? 0;
$apiKey = $_POST['api_key'] ?? '';
$stmt = $pdo->prepare("UPDATE admin.cloudflare_accounts SET api_key = ?, last_api_check = NOW(), api_status = 'valid' WHERE id = ?");
$stmt->execute([$apiKey, $id]);
echo json_encode(['success' => true]);
break;
case 'fetch_zones':
$id = $_POST['id'] ?? 0;
$stmt = $pdo->prepare("SELECT email, api_key FROM admin.cloudflare_accounts WHERE id = ?");
$stmt->execute([$id]);
$acc = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$acc || !$acc['api_key']) {
echo json_encode(['success' => false, 'error' => 'No API key']);
break;
}
$result = cfAPI('/zones?per_page=50', $acc['email'], $acc['api_key']);
if (!$result['success']) {
echo json_encode(['success' => false, 'error' => $result['errors'][0]['message'] ?? 'API Error']);
break;
}
$imported = 0;
foreach ($result['result'] as $zone) {
// Insert into cloudflare_zones
$stmt = $pdo->prepare("
INSERT INTO admin.cloudflare_zones (account_id, zone_id, domain, status)
VALUES (?, ?, ?, ?)
ON CONFLICT (zone_id) DO UPDATE SET domain = EXCLUDED.domain, status = EXCLUDED.status
");
$stmt->execute([$id, $zone['id'], $zone['name'], $zone['status']]);
// Update domains_pool if exists
$stmt = $pdo->prepare("
UPDATE admin.domains_pool
SET zone_id = ?, cf_account_id = ?, provider = 'cloudflare'
WHERE domain = ? OR domain LIKE ?
");
$stmt->execute([$zone['id'], $id, $zone['name'], '%.' . $zone['name']]);
$imported++;
}
// Update account domains count
$stmt = $pdo->prepare("UPDATE admin.cloudflare_accounts SET domains_count = ? WHERE id = ?");
$stmt->execute([$imported, $id]);
echo json_encode(['success' => true, 'imported' => $imported, 'zones' => $result['result']]);
break;
case 'get_zones':
$id = $_POST['id'] ?? 0;
$stmt = $pdo->prepare("SELECT zone_id, domain, status FROM admin.cloudflare_zones WHERE account_id = ?");
$stmt->execute([$id]);
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
break;
case 'update_dns':
$zoneId = $_POST['zone_id'] ?? '';
$domain = $_POST['domain'] ?? '';
$ip = $_POST['ip'] ?? '151.80.235.110';
$accountId = $_POST['account_id'] ?? 0;
$stmt = $pdo->prepare("SELECT email, api_key FROM admin.cloudflare_accounts WHERE id = ?");
$stmt->execute([$accountId]);
$acc = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$acc || !$acc['api_key']) {
echo json_encode(['success' => false, 'error' => 'No API key']);
break;
}
// Get existing A records
$existing = cfAPI("/zones/$zoneId/dns_records?type=A&name=$domain", $acc['email'], $acc['api_key']);
if (!empty($existing['result'])) {
// Update existing
$recordId = $existing['result'][0]['id'];
$result = cfAPI("/zones/$zoneId/dns_records/$recordId", $acc['email'], $acc['api_key'], 'PUT', [
'type' => 'A',
'name' => $domain,
'content' => $ip,
'ttl' => 1,
'proxied' => false
]);
} else {
// Create new
$result = cfAPI("/zones/$zoneId/dns_records", $acc['email'], $acc['api_key'], 'POST', [
'type' => 'A',
'name' => $domain,
'content' => $ip,
'ttl' => 1,
'proxied' => false
]);
}
echo json_encode($result);
break;
case 'create_subdomain':
$zoneId = $_POST['zone_id'] ?? '';
$subdomain = $_POST['subdomain'] ?? 'track';
$ip = $_POST['ip'] ?? '151.80.235.110';
$accountId = $_POST['account_id'] ?? 0;
$stmt = $pdo->prepare("SELECT email, api_key FROM admin.cloudflare_accounts WHERE id = ?");
$stmt->execute([$accountId]);
$acc = $stmt->fetch(PDO::FETCH_ASSOC);
// Get zone name
$stmt = $pdo->prepare("SELECT domain FROM admin.cloudflare_zones WHERE zone_id = ?");
$stmt->execute([$zoneId]);
$zoneName = $stmt->fetchColumn();
$fullDomain = $subdomain . '.' . $zoneName;
$result = cfAPI("/zones/$zoneId/dns_records", $acc['email'], $acc['api_key'], 'POST', [
'type' => 'A',
'name' => $fullDomain,
'content' => $ip,
'ttl' => 1,
'proxied' => false
]);
if ($result['success']) {
// Add to tracking_domains
$pdo->prepare("
INSERT INTO admin.tracking_domains (domain, server_ip, is_primary, status)
VALUES (?, ?, FALSE, 'standby')
ON CONFLICT DO NOTHING
")->execute([$fullDomain, $ip]);
}
echo json_encode(array_merge($result, ['domain' => $fullDomain]));
break;
case 'bulk_fetch':
// Fetch zones for ALL accounts with API keys
$stmt = $pdo->query("SELECT id, email, api_key FROM admin.cloudflare_accounts WHERE api_key IS NOT NULL AND api_key != ''");
$accounts = $stmt->fetchAll(PDO::FETCH_ASSOC);
$results = [];
foreach ($accounts as $acc) {
$zones = cfAPI('/zones?per_page=50', $acc['email'], $acc['api_key']);
if ($zones['success']) {
foreach ($zones['result'] as $zone) {
$stmt = $pdo->prepare("
INSERT INTO admin.cloudflare_zones (account_id, zone_id, domain, status)
VALUES (?, ?, ?, ?)
ON CONFLICT (zone_id) DO UPDATE SET status = EXCLUDED.status
");
$stmt->execute([$acc['id'], $zone['id'], $zone['name'], $zone['status']]);
}
$results[] = ['email' => $acc['email'], 'zones' => count($zones['result'])];
}
}
echo json_encode(['success' => true, 'results' => $results]);
break;
}
exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<title>WEVAL | Cloudflare Setup</title>
<link href="plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="plugins/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<style>
body { background: #0a0e17; color: #f1f5f9; font-family: 'Segoe UI', sans-serif; }
.container-main { max-width: 1400px; margin: 30px auto; padding: 20px; }
.card-dark { background: #1a2332; border: 1px solid #2a3a50; border-radius: 12px; margin-bottom: 20px; }
.card-header { background: #111827; border-bottom: 1px solid #2a3a50; padding: 15px 20px; font-weight: 600; }
.card-body { padding: 20px; }
.form-control-dark { background: #0d1219; border: 1px solid #2a3a50; color: #fff; border-radius: 8px; padding: 10px; }
.form-control-dark:focus { border-color: #00e5ff; outline: none; }
.btn-cyber { padding: 8px 16px; border-radius: 8px; font-weight: 600; border: none; cursor: pointer; margin: 2px; }
.btn-primary-cyber { background: linear-gradient(135deg, #00e5ff, #0891b2); color: #000; }
.btn-success-cyber { background: linear-gradient(135deg, #10b981, #059669); color: #fff; }
.btn-warning-cyber { background: linear-gradient(135deg, #f59e0b, #d97706); color: #000; }
.btn-danger-cyber { background: linear-gradient(135deg, #ef4444, #dc2626); color: #fff; }
table { width: 100%; border-collapse: collapse; }
th { color: #64748b; font-weight: 500; padding: 10px; text-align: left; border-bottom: 1px solid #2a3a50; }
td { padding: 10px; border-bottom: 1px solid #1e293b; font-size: 13px; }
.badge { padding: 4px 10px; border-radius: 12px; font-size: 11px; }
.badge-ok { background: #10b981; color: #fff; }
.badge-warn { background: #f59e0b; color: #000; }
.badge-error { background: #ef4444; color: #fff; }
h1 { color: #00e5ff; margin-bottom: 5px; }
.subtitle { color: #64748b; margin-bottom: 25px; }
code { background: #0d1219; padding: 2px 6px; border-radius: 4px; color: #f59e0b; }
.info-box { background: rgba(0,229,255,0.1); border: 1px solid rgba(0,229,255,0.3); border-radius: 8px; padding: 15px; margin-bottom: 20px; }
.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000; }
.modal-content { background: #1a2332; border-radius: 12px; max-width: 600px; margin: 100px auto; padding: 25px; }
.modal-header { display: flex; justify-content: space-between; margin-bottom: 20px; }
.modal-close { background: none; border: none; color: #fff; font-size: 24px; cursor: pointer; }
.zone-list { max-height: 300px; overflow-y: auto; }
.zone-item { background: #111827; padding: 10px; border-radius: 6px; margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; }
</style>
</head>
<body>
<div class="container-main">
<h1><i class="fa fa-cloud"></i> Cloudflare Setup</h1>
<p class="subtitle">Configurer les API keys et rapatrier les domaines automatiquement</p>
<div class="info-box">
<strong><i class="fa fa-info-circle"></i> Comment obtenir l'API Key:</strong><br>
1. Connectez-vous sur <a href="https://dash.cloudflare.com" target="_blank" style="color:#00e5ff;">dash.cloudflare.com</a><br>
2. Allez dans <strong>Profile → API Tokens → Global API Key → View</strong><br>
3. Copiez la clé et collez-la ici
</div>
<div class="card-dark">
<div class="card-header">
<i class="fa fa-list"></i> Comptes Cloudflare
<div class="float-right">
<button class="btn btn-cyber btn-warning-cyber btn-sm" onclick="bulkFetch()"><i class="fa fa-download"></i> Fetch All Zones</button>
<button class="btn btn-cyber btn-primary-cyber btn-sm" onclick="loadAccounts()"><i class="fa fa-refresh"></i></button>
</div>
</div>
<div class="card-body">
<table>
<thead>
<tr>
<th>ID</th>
<th>Email</th>
<th>Password</th>
<th>API Key</th>
<th>Zones</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="accounts-table"></tbody>
</table>
</div>
</div>
<div class="card-dark">
<div class="card-header"><i class="fa fa-crosshairs"></i> Quick Tracking Setup</div>
<div class="card-body">
<div class="row">
<div class="col-md-3">
<label>Account ID</label>
<input type="number" class="form-control form-control-dark" id="track-account" value="37">
</div>
<div class="col-md-3">
<label>Zone ID</label>
<input type="text" class="form-control form-control-dark" id="track-zone" value="53e067fbc5c532a142222d60f7ecda9d">
</div>
<div class="col-md-3">
<label>Subdomain</label>
<input type="text" class="form-control form-control-dark" id="track-subdomain" value="track">
</div>
<div class="col-md-3">
<label>Tracking IP</label>
<input type="text" class="form-control form-control-dark" id="track-ip" value="151.80.235.110">
</div>
</div>
<button class="btn btn-cyber btn-success-cyber mt-3" onclick="createTracking()">
<i class="fa fa-plus"></i> Create Tracking Subdomain
</button>
<div id="track-result" class="mt-3"></div>
</div>
</div>
</div>
<!-- Modal API Key -->
<div class="modal" id="modal-api">
<div class="modal-content">
<div class="modal-header">
<h4><i class="fa fa-key"></i> Add API Key</h4>
<button class="modal-close" onclick="closeModal()">&times;</button>
</div>
<div id="modal-body">
<p>Email: <strong id="modal-email"></strong></p>
<p>Password: <code id="modal-password"></code></p>
<div class="form-group">
<label>Global API Key</label>
<input type="text" class="form-control form-control-dark" id="modal-apikey" placeholder="Paste API Key here">
</div>
<button class="btn btn-cyber btn-success-cyber" onclick="saveApiKey()"><i class="fa fa-save"></i> Test & Save</button>
<span id="modal-status"></span>
</div>
</div>
</div>
<!-- Modal Zones -->
<div class="modal" id="modal-zones">
<div class="modal-content">
<div class="modal-header">
<h4><i class="fa fa-globe"></i> Zones</h4>
<button class="modal-close" onclick="closeModal()">&times;</button>
</div>
<div class="zone-list" id="zones-list"></div>
</div>
</div>
<script src="plugins/jquery.min.js"></script>
<script>
var currentAccountId = 0;
function loadAccounts() {
$.post('?action=accounts', function(data) {
var html = '';
data.forEach(function(a) {
var apiStatus = a.has_api ? '<span class="badge badge-ok">✓</span>' : '<span class="badge badge-error">✗</span>';
var apiDisplay = a.api_key ? a.api_key.substring(0,10) + '...' : '-';
html += '<tr>';
html += '<td>' + a.id + '</td>';
html += '<td>' + a.email + '</td>';
html += '<td><code>' + (a.password || '-') + '</code></td>';
html += '<td>' + apiStatus + ' <small style="color:#64748b">' + apiDisplay + '</small></td>';
html += '<td>' + a.zones_count + '</td>';
html += '<td>';
html += '<button class="btn btn-cyber btn-primary-cyber btn-sm" onclick="openApiModal(' + a.id + ',\'' + a.email + '\',\'' + (a.password||'') + '\')"><i class="fa fa-key"></i></button> ';
if (a.has_api) {
html += '<button class="btn btn-cyber btn-warning-cyber btn-sm" onclick="fetchZones(' + a.id + ')"><i class="fa fa-download"></i></button> ';
html += '<button class="btn btn-cyber btn-success-cyber btn-sm" onclick="viewZones(' + a.id + ')"><i class="fa fa-globe"></i></button>';
}
html += '</td>';
html += '</tr>';
});
$('#accounts-table').html(html);
});
}
function openApiModal(id, email, password) {
currentAccountId = id;
$('#modal-email').text(email);
$('#modal-password').text(password);
$('#modal-apikey').val('');
$('#modal-status').html('');
$('#modal-api').show();
}
function closeModal() {
$('.modal').hide();
}
function saveApiKey() {
var apiKey = $('#modal-apikey').val().trim();
if (!apiKey) return alert('Enter API Key');
$('#modal-status').html('<i class="fa fa-spinner fa-spin"></i> Testing...');
$.post('?action=test_api', {
email: $('#modal-email').text(),
api_key: apiKey
}, function(r) {
if (r.success) {
$.post('?action=save_api', {id: currentAccountId, api_key: apiKey}, function() {
$('#modal-status').html('<span style="color:#10b981"><i class="fa fa-check"></i> Saved!</span>');
setTimeout(function() {
closeModal();
loadAccounts();
}, 1000);
});
} else {
$('#modal-status').html('<span style="color:#ef4444"><i class="fa fa-times"></i> Invalid API Key</span>');
}
});
}
function fetchZones(id) {
if (!confirm('Fetch all zones for this account?')) return;
$.post('?action=fetch_zones', {id: id}, function(r) {
if (r.success) {
alert('Imported ' + r.imported + ' zones!');
loadAccounts();
} else {
alert('Error: ' + r.error);
}
});
}
function viewZones(id) {
$.post('?action=get_zones', {id: id}, function(data) {
var html = '';
data.forEach(function(z) {
html += '<div class="zone-item">';
html += '<div><strong>' + z.domain + '</strong><br><small style="color:#64748b">' + z.zone_id + '</small></div>';
html += '<span class="badge badge-ok">' + z.status + '</span>';
html += '</div>';
});
$('#zones-list').html(html || '<p>No zones</p>');
$('#modal-zones').show();
});
}
function bulkFetch() {
if (!confirm('Fetch zones for ALL accounts with API keys?')) return;
$.post('?action=bulk_fetch', function(r) {
if (r.success) {
var msg = 'Results:\n';
r.results.forEach(function(x) {
msg += x.email + ': ' + x.zones + ' zones\n';
});
alert(msg);
loadAccounts();
}
});
}
function createTracking() {
$.post('?action=create_subdomain', {
account_id: $('#track-account').val(),
zone_id: $('#track-zone').val(),
subdomain: $('#track-subdomain').val(),
ip: $('#track-ip').val()
}, function(r) {
if (r.success) {
$('#track-result').html('<span style="color:#10b981"><i class="fa fa-check"></i> Created: <strong>' + r.domain + '</strong> → ' + $('#track-ip').val() + '</span>');
} else {
$('#track-result').html('<span style="color:#ef4444"><i class="fa fa-times"></i> Error: ' + (r.errors?.[0]?.message || 'Failed') + '</span>');
}
});
}
$(document).ready(function() {
loadAccounts();
});
</script>
</body>
</html>