From 82fe289918d723df8428fc0f695f1ca929469f02 Mon Sep 17 00:00:00 2001 From: viettranx Date: Thu, 16 Apr 2026 17:55:26 +0700 Subject: [PATCH] fix(ui): use test-connection endpoint for TTS credential validation Update CredentialsSection to call /v1/tts/test-connection instead of /v1/tts/synthesize - allows testing unsaved credentials before save. --- ui/web/src/pages/tts/hooks/use-tts-config.ts | 35 ++++++++++++++++++- .../tts/sections/credentials-section.tsx | 20 ++++++++--- ui/web/src/pages/tts/tts-page.tsx | 4 +-- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/ui/web/src/pages/tts/hooks/use-tts-config.ts b/ui/web/src/pages/tts/hooks/use-tts-config.ts index e63dfdde..1bb4d53b 100644 --- a/ui/web/src/pages/tts/hooks/use-tts-config.ts +++ b/ui/web/src/pages/tts/hooks/use-tts-config.ts @@ -52,6 +52,22 @@ export interface SynthesizeParams { model_id?: string; } +export interface TestConnectionParams { + provider: string; + api_key?: string; + api_base?: string; + voice_id?: string; + model_id?: string; + group_id?: string; +} + +export interface TestConnectionResult { + success: boolean; + provider?: string; + latency_ms?: number; + error?: string; +} + export function useTtsConfig() { const ws = useWs(); const http = useHttp(); @@ -114,5 +130,22 @@ export function useTtsConfig() { [http], ); - return { tts, loading, saving, error, refresh: invalidate, save, synthesize }; + // Test connection with unsaved credentials — uses ephemeral provider. + const testConnection = useCallback( + async (params: TestConnectionParams): Promise => { + const res = await fetch("/v1/tts/test-connection", { + method: "POST", + headers: { "Content-Type": "application/json", ...http.getAuthHeaders() }, + body: JSON.stringify(params), + }); + const data = (await res.json()) as TestConnectionResult; + if (!res.ok || !data.success) { + throw new Error(data.error || `Test failed (${res.status})`); + } + return data; + }, + [http], + ); + + return { tts, loading, saving, error, refresh: invalidate, save, synthesize, testConnection }; } diff --git a/ui/web/src/pages/tts/sections/credentials-section.tsx b/ui/web/src/pages/tts/sections/credentials-section.tsx index ece0f6eb..6f767da6 100644 --- a/ui/web/src/pages/tts/sections/credentials-section.tsx +++ b/ui/web/src/pages/tts/sections/credentials-section.tsx @@ -13,7 +13,7 @@ import { Label } from "@/components/ui/label"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { toast } from "@/stores/use-toast-store"; import { getProviderDefinition } from "@/data/tts-providers"; -import type { TtsConfig, TtsProviderConfig, SynthesizeParams } from "../hooks/use-tts-config"; +import type { TtsConfig, TtsProviderConfig, TestConnectionParams, TestConnectionResult } from "../hooks/use-tts-config"; interface Props { provider: string; @@ -22,10 +22,10 @@ interface Props { providerKey: keyof Pick, patch: Partial, ) => void; - synthesize: (params: SynthesizeParams) => Promise; + testConnection: (params: TestConnectionParams) => Promise; } -export function CredentialsSection({ provider, draft, onUpdate, synthesize }: Props) { +export function CredentialsSection({ provider, draft, onUpdate, testConnection }: Props) { const { t } = useTranslation("tts"); const [testing, setTesting] = useState(false); @@ -36,8 +36,18 @@ export function CredentialsSection({ provider, draft, onUpdate, synthesize }: Pr const handleTestConnection = async () => { setTesting(true); try { - await synthesize({ text: "Hello, this is a connection test.", provider }); - toast.success(t("testConnection.success", "Connection successful")); + // Build params from draft credentials — test with unsaved config + const cfg = draft[provider as keyof Pick]; + const params: TestConnectionParams = { + provider, + api_key: cfg?.api_key, + api_base: cfg?.api_base || cfg?.base_url, + voice_id: cfg?.voice_id || cfg?.voice, + model_id: cfg?.model_id || cfg?.model, + group_id: (cfg as { group_id?: string })?.group_id, + }; + const result = await testConnection(params); + toast.success(t("testConnection.success", "Connection successful"), `${result.latency_ms}ms`); } catch (err) { const msg = err instanceof Error ? err.message : String(err); toast.error(t("testConnection.failed", "Connection failed"), msg); diff --git a/ui/web/src/pages/tts/tts-page.tsx b/ui/web/src/pages/tts/tts-page.tsx index 75f18da5..6910b0a7 100644 --- a/ui/web/src/pages/tts/tts-page.tsx +++ b/ui/web/src/pages/tts/tts-page.tsx @@ -68,7 +68,7 @@ function modelPatch(provider: string, value: string): [ProviderKey, Partial(tts); @@ -138,7 +138,7 @@ export function TtsPage() { provider={draft.provider} draft={draft} onUpdate={updateProvider} - synthesize={synthesize} + testConnection={testConnection} /> )}