Fix bug
This commit is contained in:
@@ -1,5 +1,37 @@
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
// Fetch with timeout wrapper
|
||||
const fetchWithTimeout = (url, options, timeout = 10000) => {
|
||||
return Promise.race([
|
||||
fetch(url, options),
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error("Request timeout")), timeout)
|
||||
)
|
||||
]);
|
||||
};
|
||||
|
||||
// Validate URL format
|
||||
const isValidUrl = (url) => {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Parse error details for user-friendly messages
|
||||
const getErrorMessage = (error) => {
|
||||
if (error.cause?.code === "ECONNREFUSED") return "Connection refused - provider node offline or unreachable";
|
||||
if (error.cause?.code === "ENOTFOUND") return "DNS lookup failed - invalid domain or network issue";
|
||||
if (error.cause?.code === "ETIMEDOUT") return "Connection timeout - provider node too slow";
|
||||
if (error.message.includes("timeout")) return "Request timeout (>10s) - provider node not responding";
|
||||
if (error.cause?.code === "CERT_HAS_EXPIRED") return "SSL certificate expired";
|
||||
if (error.cause?.code === "UNABLE_TO_VERIFY_LEAF_SIGNATURE") return "SSL certificate verification failed";
|
||||
if (error.cause?.code) return `Network error: ${error.cause.code}`;
|
||||
return "Network connection failed - check URL and network connectivity";
|
||||
};
|
||||
|
||||
// POST /api/provider-nodes/validate - Validate API key against base URL
|
||||
export async function POST(request) {
|
||||
try {
|
||||
@@ -10,6 +42,11 @@ export async function POST(request) {
|
||||
return NextResponse.json({ error: "Base URL and API key required" }, { status: 400 });
|
||||
}
|
||||
|
||||
// Validate URL format
|
||||
if (!isValidUrl(baseUrl)) {
|
||||
return NextResponse.json({ error: "Invalid URL format" }, { status: 400 });
|
||||
}
|
||||
|
||||
// Anthropic Compatible Validation
|
||||
if (type === "anthropic-compatible") {
|
||||
// Robustly construct URL: remove trailing slash, and remove trailing /messages if user added it
|
||||
@@ -21,7 +58,7 @@ export async function POST(request) {
|
||||
// Use /models endpoint for validation as many compatible providers support it (like OpenAI)
|
||||
const modelsUrl = `${normalizedBase}/models`;
|
||||
|
||||
const res = await fetch(modelsUrl, {
|
||||
const res = await fetchWithTimeout(modelsUrl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"x-api-key": apiKey,
|
||||
@@ -30,18 +67,27 @@ export async function POST(request) {
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json({ valid: res.ok, error: res.ok ? null : "Invalid API key" });
|
||||
return NextResponse.json({ valid: res.ok, error: res.ok ? null : "Invalid API key or unauthorized" });
|
||||
}
|
||||
|
||||
// OpenAI Compatible Validation (Default)
|
||||
const modelsUrl = `${baseUrl.replace(/\/$/, "")}/models`;
|
||||
const res = await fetch(modelsUrl, {
|
||||
const res = await fetchWithTimeout(modelsUrl, {
|
||||
headers: { "Authorization": `Bearer ${apiKey}` },
|
||||
});
|
||||
|
||||
return NextResponse.json({ valid: res.ok, error: res.ok ? null : "Invalid API key" });
|
||||
return NextResponse.json({ valid: res.ok, error: res.ok ? null : "Invalid API key or unauthorized" });
|
||||
} catch (error) {
|
||||
console.log("Error validating provider node:", error);
|
||||
return NextResponse.json({ error: "Validation failed" }, { status: 500 });
|
||||
const errorMessage = getErrorMessage(error);
|
||||
console.error("Error validating provider node:", {
|
||||
message: error.message,
|
||||
cause: error.cause,
|
||||
code: error.cause?.code,
|
||||
userMessage: errorMessage
|
||||
});
|
||||
return NextResponse.json({
|
||||
valid: false,
|
||||
error: errorMessage
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user