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']]); }