mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 20:00:47 +00:00
Enhance token refresh functionality across multiple executors
- Updated refreshCredentials methods in various executors (Antigravity, Base, Default, Github, Kiro) to accept optional proxyOptions for improved proxy handling. - Modified token refresh logic to utilize proxy-aware fetch for better network management. - Enhanced usage retrieval functions to support proxy options, ensuring seamless integration with proxy configurations. - Updated ModelSelectModal and ProviderInfoCard components to incorporate kind filtering for improved user experience in model selection. - Added validation for API keys in the provider validation route, including support for webSearch/webFetch providers.
This commit is contained in:
@@ -1,17 +1,53 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getProviderNodeById } from "@/models";
|
||||
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider, isCustomEmbeddingProvider } from "@/shared/constants/providers";
|
||||
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider, isCustomEmbeddingProvider, AI_PROVIDERS } from "@/shared/constants/providers";
|
||||
import { getDefaultModel } from "open-sse/config/providerModels.js";
|
||||
import { resolveOllamaLocalHost } from "open-sse/config/providers.js";
|
||||
import { PROVIDER_ENDPOINTS } from "@/shared/constants/config";
|
||||
|
||||
// Probe a webSearch/webFetch provider using its searchConfig/fetchConfig.
|
||||
// Returns true if API key is accepted (status !== 401 && !== 403).
|
||||
async function probeWebProvider(provider, apiKey) {
|
||||
const p = AI_PROVIDERS[provider];
|
||||
if (!p) return null;
|
||||
// Skip if provider has dual-purpose (LLM + search), let LLM validate handle it
|
||||
const kinds = p.serviceKinds || ["llm"];
|
||||
const isWebOnly = kinds.every((k) => k === "webSearch" || k === "webFetch");
|
||||
if (!isWebOnly) return null;
|
||||
const cfg = p.searchConfig || p.fetchConfig;
|
||||
if (!cfg) return null;
|
||||
if (cfg.authType === "none") return true; // no-auth (e.g. searxng)
|
||||
|
||||
let url = cfg.baseUrl;
|
||||
const headers = { "Content-Type": "application/json" };
|
||||
let body;
|
||||
|
||||
// Apply auth based on authHeader
|
||||
switch (cfg.authHeader) {
|
||||
case "bearer": headers["Authorization"] = `Bearer ${apiKey}`; break;
|
||||
case "x-api-key": headers["x-api-key"] = apiKey; break;
|
||||
case "x-subscription-token":headers["x-subscription-token"] = apiKey; break;
|
||||
case "key": url += `?key=${encodeURIComponent(apiKey)}&q=ping&cx=test`; break; // google-pse
|
||||
case "api_key": url += `?api_key=${encodeURIComponent(apiKey)}&q=ping&engine=google`; break; // searchapi
|
||||
}
|
||||
|
||||
// Minimal body for POST endpoints; GET sends nothing
|
||||
if (cfg.method === "POST") {
|
||||
body = JSON.stringify({ query: "ping", q: "ping", url: "https://example.com" });
|
||||
}
|
||||
|
||||
const res = await fetch(url, { method: cfg.method, headers, body, signal: AbortSignal.timeout(8000) });
|
||||
return res.status !== 401 && res.status !== 403;
|
||||
}
|
||||
|
||||
// POST /api/providers/validate - Validate API key with provider
|
||||
export async function POST(request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { provider, apiKey, providerSpecificData } = body;
|
||||
|
||||
if (!provider || (!apiKey && provider !== "ollama-local")) {
|
||||
const isNoAuth = AI_PROVIDERS[provider]?.noAuth === true;
|
||||
if (!provider || (!apiKey && provider !== "ollama-local" && !isNoAuth)) {
|
||||
return NextResponse.json({ error: "Provider and API key required" }, { status: 400 });
|
||||
}
|
||||
|
||||
@@ -147,6 +183,15 @@ export async function POST(request) {
|
||||
});
|
||||
}
|
||||
|
||||
// Generic probe for webSearch/webFetch providers (config-driven)
|
||||
const webResult = await probeWebProvider(provider, apiKey);
|
||||
if (webResult !== null) {
|
||||
return NextResponse.json({
|
||||
valid: webResult,
|
||||
error: webResult ? null : "Invalid API key",
|
||||
});
|
||||
}
|
||||
|
||||
switch (provider) {
|
||||
case "openai":
|
||||
const openaiRes = await fetch("https://api.openai.com/v1/models", {
|
||||
@@ -294,7 +339,12 @@ export async function POST(request) {
|
||||
const headers = {};
|
||||
if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
|
||||
const res = await fetch(endpoints[provider], { headers });
|
||||
isValid = res.ok;
|
||||
// xai returns 400 for bad key, 403 for valid-but-no-credit. Other providers use 401.
|
||||
if (provider === "xai") {
|
||||
isValid = res.status === 200 || res.status === 403;
|
||||
} else {
|
||||
isValid = res.ok;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user