254 lines
12 KiB
PHP
Executable File
254 lines
12 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* Provider Manager API — ALL providers, ALL accounts, ALL features
|
|
* Combines: hamid_providers + ia_provider_accounts + factory config
|
|
*/
|
|
header('Content-Type: application/json');
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
|
header('Access-Control-Allow-Headers: Content-Type');
|
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; }
|
|
|
|
$input = json_decode(file_get_contents('php://input'), true) ?: [];
|
|
$action = $_GET['action'] ?? $input['action'] ?? 'all_providers';
|
|
|
|
try {
|
|
$pdo = new PDO('pgsql:host=127.0.0.1;dbname=adx_system','admin','admin123');
|
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
} catch (Exception $e) { die(json_encode(['error'=>'DB fail'])); }
|
|
|
|
switch ($action) {
|
|
|
|
case 'all_providers':
|
|
// Merge hamid_providers + ia_provider_accounts into ONE unified list
|
|
$hamid = $pdo->query("SELECT provider_name, model, api_url, api_key, is_active, priority,
|
|
COALESCE(requests_total,0) as requests_total, last_used, last_error
|
|
FROM admin.hamid_providers ORDER BY priority")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$multi = $pdo->query("SELECT provider_name,
|
|
COUNT(*) as total_accounts,
|
|
COUNT(*) FILTER (WHERE api_key NOT LIKE '%PENDING%' AND LENGTH(api_key)>10) as real_keys,
|
|
COUNT(*) FILTER (WHERE status='active') as active_accounts,
|
|
COUNT(*) FILTER (WHERE status='error') as error_accounts,
|
|
COUNT(*) FILTER (WHERE api_key LIKE '%PENDING%') as pending_keys,
|
|
SUM(COALESCE(requests_total,0)) as total_requests,
|
|
MAX(model) as model, MAX(api_url) as api_url, MAX(signup_url) as signup_url, MAX(plan_type) as plan_type
|
|
FROM admin.ia_provider_accounts
|
|
GROUP BY provider_name ORDER BY provider_name")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Build unified map
|
|
$all = [];
|
|
$hamidMap = [];
|
|
foreach ($hamid as $h) {
|
|
$hamidMap[$h['provider_name']] = $h;
|
|
$all[$h['provider_name']] = [
|
|
'name' => $h['provider_name'],
|
|
'model' => $h['model'],
|
|
'api_url' => $h['api_url'],
|
|
'has_key' => !empty($h['api_key']) && strlen($h['api_key']) > 10,
|
|
'is_active' => $h['is_active'],
|
|
'priority' => $h['priority'],
|
|
'requests' => $h['requests_total'],
|
|
'last_used' => $h['last_used'],
|
|
'last_error' => $h['last_error'],
|
|
'source' => 'hamid',
|
|
'signup_url' => null,
|
|
'plan_type' => 'free',
|
|
'multi_accounts' => 0,
|
|
'real_keys' => 0,
|
|
'pending_keys' => 0,
|
|
'active_accounts' => 0
|
|
];
|
|
}
|
|
foreach ($multi as $m) {
|
|
$name = $m['provider_name'];
|
|
if (isset($all[$name])) {
|
|
$all[$name]['multi_accounts'] = (int)$m['total_accounts'];
|
|
$all[$name]['real_keys'] = (int)$m['real_keys'];
|
|
$all[$name]['pending_keys'] = (int)$m['pending_keys'];
|
|
$all[$name]['active_accounts'] = (int)$m['active_accounts'];
|
|
$all[$name]['signup_url'] = $m['signup_url'];
|
|
$all[$name]['plan_type'] = $m['plan_type'] ?: 'free';
|
|
} else {
|
|
$all[$name] = [
|
|
'name' => $name,
|
|
'model' => $m['model'],
|
|
'api_url' => $m['api_url'],
|
|
'has_key' => (int)$m['real_keys'] > 0,
|
|
'is_active' => (int)$m['active_accounts'] > 0,
|
|
'priority' => 99,
|
|
'requests' => (int)$m['total_requests'],
|
|
'last_used' => null,
|
|
'last_error' => null,
|
|
'source' => 'multi',
|
|
'signup_url' => $m['signup_url'],
|
|
'plan_type' => $m['plan_type'] ?: 'free',
|
|
'multi_accounts' => (int)$m['total_accounts'],
|
|
'real_keys' => (int)$m['real_keys'],
|
|
'pending_keys' => (int)$m['pending_keys'],
|
|
'active_accounts' => (int)$m['active_accounts']
|
|
];
|
|
}
|
|
}
|
|
|
|
// Sort: active first, then by name
|
|
usort($all, function($a, $b) {
|
|
if ($a['is_active'] !== $b['is_active']) return $b['is_active'] <=> $a['is_active'];
|
|
if ($a['has_key'] !== $b['has_key']) return $b['has_key'] <=> $a['has_key'];
|
|
return strcmp($a['name'], $b['name']);
|
|
});
|
|
|
|
$stats = [
|
|
'total_providers' => count($all),
|
|
'active' => count(array_filter($all, fn($p) => $p['is_active'])),
|
|
'with_keys' => count(array_filter($all, fn($p) => $p['has_key'])),
|
|
'free' => count(array_filter($all, fn($p) => $p['plan_type'] === 'free')),
|
|
'paid' => count(array_filter($all, fn($p) => $p['plan_type'] !== 'free')),
|
|
'total_accounts' => array_sum(array_column($all, 'multi_accounts')),
|
|
'total_real_keys' => array_sum(array_column($all, 'real_keys')),
|
|
'total_pending' => array_sum(array_column($all, 'pending_keys'))
|
|
];
|
|
|
|
echo json_encode(['status'=>'success', 'providers'=>array_values($all), 'stats'=>$stats]);
|
|
break;
|
|
|
|
case 'accounts':
|
|
$provider = $_GET['provider'] ?? $input['provider'] ?? '';
|
|
if (!$provider) { echo json_encode(['error'=>'provider required']); break; }
|
|
$stmt = $pdo->prepare("SELECT ia.id, ia.provider_name, ia.account_email, ia.model,
|
|
ia.status, ia.api_key IS NOT NULL AND LENGTH(ia.api_key)>10 AND ia.api_key NOT LIKE '%PENDING%' as has_real_key,
|
|
ia.requests_total, ia.tokens_used_today, ia.last_used, ia.last_error, ia.persona_id,
|
|
p.first_name, p.last_name
|
|
FROM admin.ia_provider_accounts ia
|
|
LEFT JOIN admin.personas p ON ia.persona_id = p.id
|
|
WHERE ia.provider_name = ? OR ia.provider_name LIKE ?
|
|
ORDER BY ia.status DESC, ia.id LIMIT 50");
|
|
$stmt->execute([$provider, $provider.'%']);
|
|
$accounts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
echo json_encode(['status'=>'success', 'provider'=>$provider, 'accounts'=>$accounts, 'count'=>count($accounts)]);
|
|
break;
|
|
|
|
case 'save_key':
|
|
$provider = $input['provider'] ?? '';
|
|
$key = $input['api_key'] ?? '';
|
|
$target = $input['target'] ?? 'hamid'; // hamid or account_id
|
|
$accountId = intval($input['account_id'] ?? 0);
|
|
|
|
if (!$key) { echo json_encode(['error'=>'api_key required']); break; }
|
|
|
|
$saved = [];
|
|
if ($target === 'hamid' && $provider) {
|
|
$stmt = $pdo->prepare("UPDATE admin.hamid_providers SET api_key=?, is_active=true, last_error=NULL WHERE provider_name=?");
|
|
$stmt->execute([$key, $provider]);
|
|
$saved[] = "hamid_providers: $provider";
|
|
}
|
|
if ($accountId > 0) {
|
|
$stmt = $pdo->prepare("UPDATE admin.ia_provider_accounts SET api_key=?, status='active', last_error=NULL WHERE id=?");
|
|
$stmt->execute([$key, $accountId]);
|
|
$saved[] = "ia_provider_accounts: #$accountId";
|
|
}
|
|
echo json_encode(['status'=>'success', 'saved'=>$saved]);
|
|
break;
|
|
|
|
case 'test_key':
|
|
$provider = $input['provider'] ?? '';
|
|
$key = $input['api_key'] ?? '';
|
|
$model = $input['model'] ?? '';
|
|
$url = $input['api_url'] ?? '';
|
|
|
|
if (!$key) {
|
|
// Try to get key from DB
|
|
$row = $pdo->prepare("SELECT api_key, model, api_url FROM admin.hamid_providers WHERE provider_name=? AND api_key IS NOT NULL");
|
|
$row->execute([$provider]);
|
|
$r = $row->fetch(PDO::FETCH_ASSOC);
|
|
if ($r) { $key=$r['api_key']; $model=$model?:$r['model']; $url=$url?:$r['api_url']; }
|
|
}
|
|
if (!$key) { echo json_encode(['error'=>'No key to test']); break; }
|
|
|
|
// Build request based on provider
|
|
$start = microtime(true);
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
|
|
if (stripos($provider, 'gemini') !== false) {
|
|
curl_setopt($ch, CURLOPT_URL, "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key={$key}");
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['contents'=>[['parts'=>[['text'=>'say OK']]]],'generationConfig'=>['maxOutputTokens'=>5]]));
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
} elseif (stripos($provider, 'claude') !== false) {
|
|
curl_setopt($ch, CURLOPT_URL, $url ?: 'https://api.anthropic.com/v1/messages');
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['model'=>$model,'max_tokens'=>5,'messages'=>[['role'=>'user','content'=>'say OK']]]));
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json','x-api-key: '.$key,'anthropic-version: 2023-06-01']);
|
|
} elseif (stripos($provider, 'cohere') !== false) {
|
|
curl_setopt($ch, CURLOPT_URL, $url ?: 'https://api.cohere.ai/v1/chat');
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['model'=>$model,'message'=>'say OK']));
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json','Authorization: Bearer '.$key]);
|
|
} else {
|
|
curl_setopt($ch, CURLOPT_URL, $url ?: 'https://api.openai.com/v1/chat/completions');
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['model'=>$model,'messages'=>[['role'=>'user','content'=>'say OK']],'max_tokens'=>5]));
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json','Authorization: Bearer '.$key]);
|
|
}
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
$resp = curl_exec($ch);
|
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
$latency = round((microtime(true) - $start) * 1000);
|
|
|
|
$ok = $code === 200;
|
|
$data = json_decode($resp, true);
|
|
$error = '';
|
|
if (!$ok) {
|
|
$error = $data['error']['message'] ?? $data['detail'] ?? "HTTP $code";
|
|
}
|
|
|
|
// Update DB status
|
|
if ($ok) {
|
|
$pdo->prepare("UPDATE admin.hamid_providers SET last_error=NULL, is_active=true WHERE provider_name=?")->execute([$provider]);
|
|
} else {
|
|
$pdo->prepare("UPDATE admin.hamid_providers SET last_error=?, is_active=false WHERE provider_name=?")->execute([substr($error,0,100), $provider]);
|
|
}
|
|
|
|
echo json_encode(['status'=>$ok?'success':'error', 'provider'=>$provider, 'http_code'=>$code,
|
|
'latency_ms'=>$latency, 'error'=>$error, 'response'=>$ok?substr($resp,0,200):'']);
|
|
break;
|
|
|
|
case 'add_to_hamid':
|
|
// Add a provider from ia_provider_accounts to hamid_providers
|
|
$name = $input['provider_name'] ?? '';
|
|
$model = $input['model'] ?? '';
|
|
$url = $input['api_url'] ?? '';
|
|
$key = $input['api_key'] ?? '';
|
|
$priority = intval($input['priority'] ?? 12);
|
|
|
|
if (!$name) { echo json_encode(['error'=>'provider_name required']); break; }
|
|
|
|
// Check if already exists
|
|
$exists = $pdo->prepare("SELECT id FROM admin.hamid_providers WHERE provider_name=?");
|
|
$exists->execute([$name]);
|
|
if ($exists->fetch()) {
|
|
$pdo->prepare("UPDATE admin.hamid_providers SET model=COALESCE(NULLIF(?,'''),model), api_url=COALESCE(NULLIF(?,'''),api_url), api_key=COALESCE(NULLIF(?,'''),api_key), is_active=true, priority=? WHERE provider_name=?")
|
|
->execute([$model, $url, $key, $priority, $name]);
|
|
} else {
|
|
$pdo->prepare("INSERT INTO admin.hamid_providers (provider_name, model, api_url, api_key, is_active, priority) VALUES (?,?,?,?,true,?)")
|
|
->execute([$name, $model, $url, $key, $priority]);
|
|
}
|
|
echo json_encode(['status'=>'success', 'message'=>"$name added/updated in hamid_providers"]);
|
|
break;
|
|
|
|
case 'test_all':
|
|
// Quick connectivity test on all hamid providers with keys
|
|
$providers = $pdo->query("SELECT provider_name FROM admin.hamid_providers WHERE api_key IS NOT NULL AND LENGTH(api_key)>10 ORDER BY priority")->fetchAll(PDO::FETCH_COLUMN);
|
|
$results = [];
|
|
foreach ($providers as $pn) {
|
|
// Recursive self-call for test
|
|
$r = json_decode(file_get_contents("http://127.0.0.1:{$_SERVER['SERVER_PORT']}/api/provider-manager.php?action=test_key&provider=".urlencode($pn)), true);
|
|
$results[] = ['provider'=>$pn, 'ok'=>($r['status']??'')=='success', 'latency'=>$r['latency_ms']??0, 'error'=>$r['error']??''];
|
|
}
|
|
echo json_encode(['status'=>'success', 'results'=>$results]);
|
|
break;
|
|
|
|
default:
|
|
echo json_encode(['error'=>'Unknown action', 'actions'=>['all_providers','accounts','save_key','test_key','add_to_hamid','test_all']]);
|
|
}
|