mirror of
https://github.com/decolua/9router.git
synced 2026-05-08 12:01:28 +00:00
fix: add Azure validation and make Organization required
- Add Azure case to /api/providers/validate that sends a test chat completion with api-key header and organization - Pass Azure-specific data (endpoint, deployment, apiVersion, org) from Add modal to validate endpoint - Make Organization field required (needed for billing) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,13 +25,19 @@ export default function AddApiKeyModal({ isOpen, provider, providerName, isCompa
|
|||||||
const [validationResult, setValidationResult] = useState(null);
|
const [validationResult, setValidationResult] = useState(null);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
|
||||||
|
const validatePayload = () => {
|
||||||
|
const payload = { provider, apiKey: formData.apiKey };
|
||||||
|
if (isAzure) payload.providerSpecificData = azureData;
|
||||||
|
return payload;
|
||||||
|
};
|
||||||
|
|
||||||
const handleValidate = async () => {
|
const handleValidate = async () => {
|
||||||
setValidating(true);
|
setValidating(true);
|
||||||
try {
|
try {
|
||||||
const res = await fetch("/api/providers/validate", {
|
const res = await fetch("/api/providers/validate", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ provider, apiKey: formData.apiKey }),
|
body: JSON.stringify(validatePayload()),
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setValidationResult(data.valid ? "success" : "failed");
|
setValidationResult(data.valid ? "success" : "failed");
|
||||||
@@ -54,7 +60,7 @@ export default function AddApiKeyModal({ isOpen, provider, providerName, isCompa
|
|||||||
const res = await fetch("/api/providers/validate", {
|
const res = await fetch("/api/providers/validate", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ provider, apiKey: formData.apiKey }),
|
body: JSON.stringify(validatePayload()),
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
isValid = !!data.valid;
|
isValid = !!data.valid;
|
||||||
@@ -144,7 +150,7 @@ export default function AddApiKeyModal({ isOpen, provider, providerName, isCompa
|
|||||||
placeholder="2024-10-01-preview"
|
placeholder="2024-10-01-preview"
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label="Organization (Optional)"
|
label="Organization"
|
||||||
value={azureData.organization}
|
value={azureData.organization}
|
||||||
onChange={(e) => setAzureData({ ...azureData, organization: e.target.value })}
|
onChange={(e) => setAzureData({ ...azureData, organization: e.target.value })}
|
||||||
placeholder="Organization ID"
|
placeholder="Organization ID"
|
||||||
@@ -182,7 +188,7 @@ export default function AddApiKeyModal({ isOpen, provider, providerName, isCompa
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button onClick={handleSubmit} fullWidth disabled={!formData.name || !formData.apiKey || (isAzure && (!azureData.azureEndpoint || !azureData.deployment)) || saving}>
|
<Button onClick={handleSubmit} fullWidth disabled={!formData.name || !formData.apiKey || (isAzure && (!azureData.azureEndpoint || !azureData.deployment || !azureData.organization)) || saving}>
|
||||||
{saving ? "Saving..." : "Save"}
|
{saving ? "Saving..." : "Save"}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={onClose} variant="ghost" fullWidth>
|
<Button onClick={onClose} variant="ghost" fullWidth>
|
||||||
|
|||||||
@@ -62,6 +62,35 @@ export async function POST(request) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (provider === "azure") {
|
||||||
|
const { providerSpecificData } = body;
|
||||||
|
const endpoint = (providerSpecificData?.azureEndpoint || "").replace(/\/$/, "");
|
||||||
|
const deployment = providerSpecificData?.deployment || "gpt-4";
|
||||||
|
const apiVersion = providerSpecificData?.apiVersion || "2024-10-01-preview";
|
||||||
|
const organization = providerSpecificData?.organization;
|
||||||
|
|
||||||
|
const url = `${endpoint}/openai/deployments/${deployment}/chat/completions?api-version=${apiVersion}`;
|
||||||
|
const headers = {
|
||||||
|
"api-key": apiKey,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
if (organization) headers["OpenAI-Organization"] = organization;
|
||||||
|
|
||||||
|
const azureRes = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify({
|
||||||
|
messages: [{ role: "user", content: "test" }],
|
||||||
|
max_tokens: 1,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
isValid = azureRes.status !== 401 && azureRes.status !== 403;
|
||||||
|
return NextResponse.json({
|
||||||
|
valid: isValid,
|
||||||
|
error: isValid ? null : "Invalid API key or Azure configuration",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case "openai":
|
case "openai":
|
||||||
const openaiRes = await fetch("https://api.openai.com/v1/models", {
|
const openaiRes = await fetch("https://api.openai.com/v1/models", {
|
||||||
|
|||||||
@@ -215,11 +215,11 @@ export default function EditConnectionModal({ isOpen, connection, proxyPools, on
|
|||||||
hint="Azure OpenAI API version to use"
|
hint="Azure OpenAI API version to use"
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label="Organization (Optional)"
|
label="Organization"
|
||||||
value={azureData.organization}
|
value={azureData.organization}
|
||||||
onChange={(e) => setAzureData({ ...azureData, organization: e.target.value })}
|
onChange={(e) => setAzureData({ ...azureData, organization: e.target.value })}
|
||||||
placeholder="Organization ID"
|
placeholder="Organization ID"
|
||||||
hint="Optional: Your Azure organization ID"
|
hint="Required for billing"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user