From a90a87355bcb43004cd1c5be66764397d6f93020 Mon Sep 17 00:00:00 2001 From: "dayuan.jiang" Date: Thu, 11 Jun 2026 13:14:05 +0900 Subject: [PATCH] fix: allow testing unsaved providers in admin panel The test button previously looked up credentials by providerId in the saved settings, so testing a newly added (unsaved) provider failed with 'Unknown provider or model'. The test endpoint now accepts the client's current provider state; newly typed secrets are used as-is and masked markers are resolved against the stored values, so testing works both before and after saving. --- app/[lang]/admin/page.tsx | 6 +---- app/api/admin/test-model/route.ts | 37 ++++++++++++++++++------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/app/[lang]/admin/page.tsx b/app/[lang]/admin/page.tsx index 091bd57..6045be1 100644 --- a/app/[lang]/admin/page.tsx +++ b/app/[lang]/admin/page.tsx @@ -384,7 +384,7 @@ function ProviderDetail({ try { const data = await adminFetch("/api/admin/test-model", password, { method: "POST", - body: JSON.stringify({ providerId: provider.id, modelId }), + body: JSON.stringify({ provider, modelId }), }) setTestResults((prev) => ({ ...prev, @@ -693,10 +693,6 @@ function ProviderDetail({ )} -

- Note: Test runs against saved credentials — save your - changes first. -

diff --git a/app/api/admin/test-model/route.ts b/app/api/admin/test-model/route.ts index 9b93c5f..4d6e3d3 100644 --- a/app/api/admin/test-model/route.ts +++ b/app/api/admin/test-model/route.ts @@ -1,45 +1,52 @@ import { POST as validateModel } from "@/app/api/validate-model/route" import { checkAdminAuth } from "@/lib/admin/auth" -import { loadAdminProviders } from "@/lib/admin/providers" +import { + AdminProviderSchema, + loadAdminProviders, + mergeSecrets, +} from "@/lib/admin/providers" export const runtime = "nodejs" export const dynamic = "force-dynamic" -// Test a stored admin provider's model. The client only has masked -// secrets, so credentials are filled in server-side from settings.json -// and passed to the existing validate-model handler. +// Test a model with the client's CURRENT provider state (which may be +// unsaved). Secret fields arrive either as plaintext (newly typed) or as +// masked {isSet} markers, which are resolved against settings.json — so +// testing works both before and after saving. export async function POST(req: Request) { const authError = checkAdminAuth(req) if (authError) return authError - let body: { providerId?: string; modelId?: string } + let body: { provider?: unknown; modelId?: string } try { body = await req.json() } catch { return Response.json({ error: "Invalid JSON body" }, { status: 400 }) } - const stored = loadAdminProviders().find((p) => p.id === body.providerId) - if (!stored || !body.modelId) { + const parsed = AdminProviderSchema.safeParse(body.provider) + if (!parsed.success || !body.modelId) { return Response.json( - { valid: false, error: "Unknown provider or model" }, + { valid: false, error: "Invalid provider or model" }, { status: 400 }, ) } + const [resolved] = mergeSecrets([parsed.data], loadAdminProviders()) + return validateModel( new Request(new URL("/api/validate-model", req.url), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ - provider: stored.provider, - apiKey: stored.apiKey, - baseUrl: stored.baseUrl, + provider: resolved.provider, + apiKey: resolved.apiKey, + baseUrl: resolved.baseUrl, modelId: body.modelId, - awsAccessKeyId: stored.awsAccessKeyId, - awsSecretAccessKey: stored.awsSecretAccessKey, - awsRegion: stored.awsRegion, - vertexApiKey: stored.vertexApiKey, + awsAccessKeyId: resolved.awsAccessKeyId, + awsSecretAccessKey: resolved.awsSecretAccessKey, + awsRegion: resolved.awsRegion, + vertexApiKey: resolved.vertexApiKey, }), }), )