148 lines
4.2 KiB
TypeScript
148 lines
4.2 KiB
TypeScript
|
|
import { supabase } from '@/integrations/supabase/client';
|
|
|
|
interface ApiKey {
|
|
id: string;
|
|
name: string;
|
|
key: string;
|
|
is_primary: boolean;
|
|
}
|
|
|
|
class ApiKeyManager {
|
|
private static instance: ApiKeyManager;
|
|
private apiKeys: ApiKey[] = [];
|
|
private lastFetch: number = 0;
|
|
private readonly CACHE_DURATION = 5 * 60 * 1000; // 5 minutos
|
|
|
|
private constructor() {}
|
|
|
|
static getInstance(): ApiKeyManager {
|
|
if (!ApiKeyManager.instance) {
|
|
ApiKeyManager.instance = new ApiKeyManager();
|
|
}
|
|
return ApiKeyManager.instance;
|
|
}
|
|
|
|
private async fetchApiKeys(): Promise<void> {
|
|
const now = Date.now();
|
|
if (now - this.lastFetch < this.CACHE_DURATION && this.apiKeys.length > 0) {
|
|
return; // Usar cache
|
|
}
|
|
|
|
try {
|
|
const { data, error } = await (supabase as any)
|
|
.from('api_keys')
|
|
.select('*')
|
|
.order('is_primary', { ascending: false })
|
|
.order('created_at', { ascending: true });
|
|
|
|
if (error) throw error;
|
|
|
|
this.apiKeys = data || [];
|
|
this.lastFetch = now;
|
|
} catch (error) {
|
|
console.error('Erro ao buscar chaves API:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getApiKeyWithFallback(): Promise<string | null> {
|
|
await this.fetchApiKeys();
|
|
|
|
if (this.apiKeys.length === 0) {
|
|
console.warn('Nenhuma chave API configurada');
|
|
return null;
|
|
}
|
|
|
|
// Retorna a chave principal primeiro, depois as secundárias
|
|
const primaryKey = this.apiKeys.find(key => key.is_primary);
|
|
if (primaryKey) {
|
|
return primaryKey.key;
|
|
}
|
|
|
|
// Se não há chave principal, retorna a primeira disponível
|
|
return this.apiKeys[0]?.key || null;
|
|
}
|
|
|
|
async getAllApiKeys(): Promise<string[]> {
|
|
await this.fetchApiKeys();
|
|
return this.apiKeys.map(key => key.key);
|
|
}
|
|
|
|
// Método para fazer requests com fallback automático
|
|
async makeRequestWithFallback<T>(
|
|
requestFn: (apiKey: string) => Promise<T>,
|
|
options: {
|
|
retries?: number;
|
|
timeout?: number;
|
|
} = {}
|
|
): Promise<T> {
|
|
const { retries = 1, timeout = 30000 } = options;
|
|
await this.fetchApiKeys();
|
|
|
|
if (this.apiKeys.length === 0) {
|
|
throw new Error('Nenhuma chave API configurada');
|
|
}
|
|
|
|
const keys = [...this.apiKeys].sort((a, b) => {
|
|
if (a.is_primary && !b.is_primary) return -1;
|
|
if (!a.is_primary && b.is_primary) return 1;
|
|
return 0;
|
|
});
|
|
|
|
let lastError: Error | null = null;
|
|
|
|
for (const apiKeyObj of keys) {
|
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
try {
|
|
console.log(`Tentando chave: ${apiKeyObj.name} (tentativa ${attempt + 1})`);
|
|
|
|
const timeoutPromise = new Promise<never>((_, reject) =>
|
|
setTimeout(() => reject(new Error('Timeout na requisição')), timeout)
|
|
);
|
|
|
|
const result = await Promise.race([
|
|
requestFn(apiKeyObj.key),
|
|
timeoutPromise
|
|
]);
|
|
|
|
console.log(`Sucesso com chave: ${apiKeyObj.name}`);
|
|
return result;
|
|
|
|
} catch (error: any) {
|
|
lastError = error;
|
|
const isAuthError = error?.status === 401 || error?.status === 403;
|
|
const isNetworkError = error?.message?.includes('Timeout') ||
|
|
error?.message?.includes('network') ||
|
|
error?.code === 'ECONNREFUSED';
|
|
|
|
console.warn(`Falha com chave ${apiKeyObj.name}:`, error?.message);
|
|
|
|
// Se é erro de autenticação, não tentar novamente com a mesma chave
|
|
if (isAuthError) {
|
|
console.log(`Chave ${apiKeyObj.name} inválida, tentando próxima...`);
|
|
break;
|
|
}
|
|
|
|
// Se é erro de rede e ainda há tentativas, aguarda um pouco
|
|
if (isNetworkError && attempt < retries) {
|
|
await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new Error(
|
|
`Todas as chaves API falharam. Último erro: ${lastError?.message || 'Erro desconhecido'}`
|
|
);
|
|
}
|
|
|
|
// Limpar cache (útil após atualizações nas chaves)
|
|
clearCache(): void {
|
|
this.apiKeys = [];
|
|
this.lastFetch = 0;
|
|
}
|
|
}
|
|
|
|
export const apiKeyManager = ApiKeyManager.getInstance();
|