import { NextRequest, NextResponse } from 'next/server'; import { auth } from '@/auth'; import { getEffectiveTier } from '@/lib/entitlements'; import { isByokProviderAllowed } from '@/lib/byok'; import { fetchLiveModelsForProvider, type FetchModelsResult } from '@/lib/ai/models-list'; import { VALID_PROVIDERS, type AiGatewayProvider } from '@/lib/ai/router'; /** * GET /api/user/api-keys/verify?provider=&key=&baseUrl= * * Verifies that the user's API key is valid by attempting to fetch models. * Returns validity status and available models if successful. * * Key is only considered VALID if we can fetch models from the actual provider API. * Fallback to hardcoded suggestions is NOT considered valid verification. */ export async function GET(request: NextRequest) { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const tier = await getEffectiveTier(session.user.id); if (tier === 'BASIC') { return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); } const { searchParams } = request.nextUrl; const provider = searchParams.get('provider') as AiGatewayProvider; const apiKey = searchParams.get('key'); const baseUrl = searchParams.get('baseUrl') ?? undefined; if (!provider || !apiKey) { return NextResponse.json({ error: 'Missing parameters' }, { status: 400 }); } if (!VALID_PROVIDERS.has(provider)) { return NextResponse.json({ error: 'Invalid provider' }, { status: 400 }); } if (!isByokProviderAllowed(tier, provider)) { return NextResponse.json({ error: 'Tier restricted' }, { status: 403 }); } try { const result: FetchModelsResult = await fetchLiveModelsForProvider(provider, apiKey, baseUrl); console.log(`[verify] ${provider}: fromApi=${result.fromApi}, models=${result.models.length}`, result.models); // Only consider key valid if we got models from the REAL API (not fallbacks) // Exception: Anthropic/Google/custom don't guarantee public /models endpoints, so we accept fallbacks for them const acceptsFallbacks = provider === 'anthropic' || provider === 'anthropic_custom' || provider === 'custom_anthropic' || provider === 'google' || provider === 'minimax' || provider === 'custom' || provider === 'custom_openai'; const isValid = result.models.length > 0 && (result.fromApi || acceptsFallbacks); return NextResponse.json({ valid: isValid, models: result.models, fromApi: result.fromApi, message: isValid ? result.fromApi ? `${result.models.length} modèle(s) trouvé(s) via l'API ${provider}` : `${result.models.length} modèle(s) suggéré(s) pour ${provider}` : 'Aucun modèle trouvé - vérifiez votre clé API' }); } catch (error) { const message = error instanceof Error ? error.message : 'Clé API invalide'; console.error(`[verify] ${provider} failed:`, error); return NextResponse.json({ valid: false, models: [], fromApi: false, message }, { status: 200 }); // Return 200 with valid:false for UI handling } }