38 lines
1.7 KiB
TypeScript
38 lines
1.7 KiB
TypeScript
import { isSystemProxyBaseUrl, resolveBackendApiUrl, type AiConfig, type ChannelHeader } from "@/stores/use-config-store";
|
|
|
|
type RelayConfig = Pick<AiConfig, "baseUrl" | "apiKey" | "apiFormat"> & { headers?: ChannelHeader[] };
|
|
|
|
export type ChannelRequest = {
|
|
url: string;
|
|
headers: Record<string, string>;
|
|
credentials: RequestCredentials;
|
|
};
|
|
|
|
/** 自定义渠道统一经登录态后端中转,避免依赖第三方服务的浏览器 CORS。 */
|
|
export function channelRequest(config: RelayConfig, upstreamUrl: string, headers: HeadersInit = {}): ChannelRequest {
|
|
const normalizedHeaders = new Headers(headers);
|
|
if (isSystemProxyBaseUrl(config.baseUrl)) {
|
|
return { url: upstreamUrl, headers: Object.fromEntries(normalizedHeaders.entries()), credentials: "include" };
|
|
}
|
|
|
|
const normalizedUpstreamUrl = new URL(upstreamUrl).toString();
|
|
normalizedHeaders.delete("X-Canvas-Upstream-Headers");
|
|
normalizedHeaders.delete("x-goog-api-key");
|
|
normalizedHeaders.set("Authorization", `Bearer ${config.apiKey}`);
|
|
normalizedHeaders.set("X-Canvas-Upstream-URL", normalizedUpstreamUrl);
|
|
normalizedHeaders.set("X-Canvas-Upstream-Format", config.apiFormat === "gemini" ? "gemini" : "openai");
|
|
if (config.headers?.length) normalizedHeaders.set("X-Canvas-Upstream-Headers", encodeChannelHeaders(config.headers));
|
|
return {
|
|
url: resolveBackendApiUrl("/api/ai/custom"),
|
|
headers: Object.fromEntries(normalizedHeaders.entries()),
|
|
credentials: "include",
|
|
};
|
|
}
|
|
|
|
function encodeChannelHeaders(headers: ChannelHeader[]) {
|
|
const bytes = new TextEncoder().encode(JSON.stringify(headers));
|
|
let binary = "";
|
|
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
return btoa(binary);
|
|
}
|