From b275dfdc9cd3139baaf9d4bd67841d8abe486f77 Mon Sep 17 00:00:00 2001 From: ram/haidar <49301219+ramhaidar@users.noreply.github.com> Date: Fri, 6 Feb 2026 20:54:19 +0700 Subject: [PATCH] feat(providers): auto-validate API keys on save - AddApiKeyModal and EditConnectionModal now automatically validate API keys during save - Sets testStatus to 'active' when validation succeeds, removing need for manual Check button - Added saving state to prevent duplicate submissions during validation - Target: provider connection management UX --- .../dashboard/providers/[id]/page.js | 113 ++++++++++++++---- 1 file changed, 89 insertions(+), 24 deletions(-) diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.js b/src/app/(dashboard)/dashboard/providers/[id]/page.js index 4b1eb91..bd483b7 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.js +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.js @@ -66,8 +66,8 @@ export default function ProviderDetailPage() { const fetchConnections = useCallback(async () => { try { const [connectionsRes, nodesRes] = await Promise.all([ - fetch("/api/providers"), - fetch("/api/provider-nodes"), + fetch("/api/providers", { cache: "no-store" }), + fetch("/api/provider-nodes", { cache: "no-store" }), ]); const connectionsData = await connectionsRes.json(); const nodesData = await nodesRes.json(); @@ -76,7 +76,21 @@ export default function ProviderDetailPage() { setConnections(filtered); } if (nodesRes.ok) { - const node = (nodesData.nodes || []).find((entry) => entry.id === providerId) || null; + let node = (nodesData.nodes || []).find((entry) => entry.id === providerId) || null; + + // Newly created compatible nodes can be briefly unavailable on one worker. + // Retry a few times before showing "Provider not found". + if (!node && isCompatible) { + for (let attempt = 0; attempt < 3; attempt += 1) { + await new Promise((resolve) => setTimeout(resolve, 150)); + const retryRes = await fetch("/api/provider-nodes", { cache: "no-store" }); + if (!retryRes.ok) continue; + const retryData = await retryRes.json(); + node = (retryData.nodes || []).find((entry) => entry.id === providerId) || null; + if (node) break; + } + } + setProviderNode(node); } } catch (error) { @@ -1025,6 +1039,7 @@ function AddApiKeyModal({ isOpen, provider, providerName, isCompatible, isAnthro }); const [validating, setValidating] = useState(false); const [validationResult, setValidationResult] = useState(null); + const [saving, setSaving] = useState(false); const handleValidate = async () => { setValidating(true); @@ -1043,13 +1058,38 @@ function AddApiKeyModal({ isOpen, provider, providerName, isCompatible, isAnthro } }; - const handleSubmit = () => { - onSave({ - name: formData.name, - apiKey: formData.apiKey, - priority: formData.priority, - testStatus: validationResult === "success" ? "active" : "unknown", - }); + const handleSubmit = async () => { + if (!provider || !formData.apiKey) return; + + setSaving(true); + try { + let isValid = false; + try { + setValidating(true); + setValidationResult(null); + const res = await fetch("/api/providers/validate", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ provider, apiKey: formData.apiKey }), + }); + const data = await res.json(); + isValid = !!data.valid; + setValidationResult(isValid ? "success" : "failed"); + } catch { + setValidationResult("failed"); + } finally { + setValidating(false); + } + + await onSave({ + name: formData.name, + apiKey: formData.apiKey, + priority: formData.priority, + testStatus: isValid ? "active" : "unknown", + }); + } finally { + setSaving(false); + } }; if (!provider) return null; @@ -1072,7 +1112,7 @@ function AddApiKeyModal({ isOpen, provider, providerName, isCompatible, isAnthro className="flex-1" />