mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 22:32:12 +03:00
* chore: remove 9 dead/unreachable free providers Verified via HTTP probe — API endpoints return 000/404/empty: - freetheai, enally, replicate, lepton, poolside, nomic - astraflow, petals, nanobanana (phantom: catalog but no registry) Also removed from: providerRegistry.ts, validation.ts, staticModels.ts, imageValidation.ts, open-sse/config/petals.ts * chore: remove dead astraflow providers Remove astraflow and astraflow-cn (UCloud) — API endpoints unreachable. Remaining dead providers (enally, freetheai, nanobanana, replicate, lepton, petals, poolside, nomic) have working main sites but dead API endpoints — need API keys. Will remove in follow-up. * chore: remove 9 dead/unreachable free providers Removed: freetheai, enally, replicate, lepton, poolside, nomic, astraflow, petals, nanobanana All verified as dead via live API probes (000/404/empty responses). Cleaned from providers.ts, providerRegistry.ts, validation.ts, staticModels.ts, and imageValidation.ts. --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
161 lines
4.2 KiB
TypeScript
161 lines
4.2 KiB
TypeScript
import { getImageProvider } from "@omniroute/open-sse/config/imageRegistry";
|
|
|
|
import { getProviderOutboundGuard } from "@/shared/network/outboundUrlGuard";
|
|
import {
|
|
SAFE_OUTBOUND_FETCH_PRESETS,
|
|
SafeOutboundFetchError,
|
|
getSafeOutboundFetchErrorStatus,
|
|
safeOutboundFetch,
|
|
} from "@/shared/network/safeOutboundFetch";
|
|
|
|
const IMAGE_PROVIDER_VALIDATION_ENDPOINTS: Record<
|
|
string,
|
|
{ baseUrl?: string; path: string; method?: string }
|
|
> = {
|
|
"fal-ai": {
|
|
baseUrl: "https://api.fal.ai",
|
|
path: "/v1/models?limit=1",
|
|
},
|
|
"stability-ai": {
|
|
path: "/v1/user/account",
|
|
},
|
|
"black-forest-labs": {
|
|
path: "/v1/credits",
|
|
},
|
|
recraft: {
|
|
path: "/v1/users/me",
|
|
},
|
|
topaz: {
|
|
path: "/account/v1/credits/balance",
|
|
},
|
|
};
|
|
|
|
function normalizeBaseUrl(baseUrl: string) {
|
|
return (baseUrl || "").trim().replace(/\/$/, "");
|
|
}
|
|
|
|
function applyCustomUserAgent(headers: Record<string, string>, providerSpecificData: any = {}) {
|
|
const customUserAgent =
|
|
typeof providerSpecificData?.customUserAgent === "string"
|
|
? providerSpecificData.customUserAgent.trim()
|
|
: "";
|
|
if (customUserAgent) {
|
|
headers["user-agent"] = customUserAgent;
|
|
}
|
|
return headers;
|
|
}
|
|
|
|
function toValidationErrorResult(error: unknown) {
|
|
const message = error instanceof Error ? error.message : String(error || "Validation failed");
|
|
const statusCode = getSafeOutboundFetchErrorStatus(error);
|
|
|
|
return {
|
|
valid: false,
|
|
error: message || "Validation failed",
|
|
unsupported: false,
|
|
...(statusCode ? { statusCode } : {}),
|
|
...(error instanceof SafeOutboundFetchError && error.code === "TIMEOUT"
|
|
? { timeout: true }
|
|
: {}),
|
|
...(statusCode === 400 ? { securityBlocked: true } : {}),
|
|
};
|
|
}
|
|
|
|
function buildImageProviderValidationHeaders(
|
|
imageProvider: any,
|
|
apiKey: string,
|
|
providerSpecificData: any = {}
|
|
) {
|
|
const headers: Record<string, string> = {
|
|
Accept: "application/json",
|
|
};
|
|
|
|
if (apiKey) {
|
|
switch (String(imageProvider?.authHeader || "").toLowerCase()) {
|
|
case "bearer":
|
|
headers.Authorization = `Bearer ${apiKey}`;
|
|
break;
|
|
case "key":
|
|
headers.Authorization = `Key ${apiKey}`;
|
|
break;
|
|
case "x-key":
|
|
headers["x-key"] = apiKey;
|
|
break;
|
|
case "x-api-key":
|
|
headers["X-API-Key"] = apiKey;
|
|
break;
|
|
case "none":
|
|
break;
|
|
default:
|
|
headers.Authorization = `Bearer ${apiKey}`;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return applyCustomUserAgent(headers, providerSpecificData);
|
|
}
|
|
|
|
async function validationRead(url: string, init: RequestInit) {
|
|
return safeOutboundFetch(url, {
|
|
...SAFE_OUTBOUND_FETCH_PRESETS.validationRead,
|
|
guard: getProviderOutboundGuard(),
|
|
...init,
|
|
});
|
|
}
|
|
|
|
export async function validateImageProviderApiKey({
|
|
provider,
|
|
apiKey,
|
|
providerSpecificData = {},
|
|
}: any) {
|
|
const imageProvider = getImageProvider(provider);
|
|
const validationConfig = IMAGE_PROVIDER_VALIDATION_ENDPOINTS[provider];
|
|
|
|
if (!imageProvider || !validationConfig) {
|
|
return { valid: false, error: "Provider validation not supported", unsupported: true };
|
|
}
|
|
|
|
try {
|
|
const baseUrl = normalizeBaseUrl(
|
|
providerSpecificData?.baseUrl || validationConfig.baseUrl || imageProvider.baseUrl
|
|
);
|
|
const url = `${baseUrl}${validationConfig.path}`;
|
|
const response = await validationRead(url, {
|
|
method: validationConfig.method || "GET",
|
|
headers: buildImageProviderValidationHeaders(imageProvider, apiKey, providerSpecificData),
|
|
});
|
|
|
|
if (response.ok) {
|
|
return { valid: true, error: null, method: "image-provider" };
|
|
}
|
|
|
|
if (response.status === 401 || response.status === 403) {
|
|
return { valid: false, error: "Invalid API key", method: "image-provider" };
|
|
}
|
|
|
|
if (response.status === 429) {
|
|
return {
|
|
valid: false,
|
|
error: "Validation rate limited (429)",
|
|
method: "image-provider",
|
|
};
|
|
}
|
|
|
|
if (response.status >= 500) {
|
|
return {
|
|
valid: false,
|
|
error: `Provider unavailable (${response.status})`,
|
|
method: "image-provider",
|
|
};
|
|
}
|
|
|
|
return {
|
|
valid: false,
|
|
error: `Validation failed: ${response.status}`,
|
|
method: "image-provider",
|
|
};
|
|
} catch (error: any) {
|
|
return toValidationErrorResult(error);
|
|
}
|
|
}
|