mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-06 12:41:25 +02:00
add no auth and passthrough auth
This commit is contained in:
@@ -1651,6 +1651,8 @@ export const aiProviders = pgTable("aiProviders", {
|
||||
| "x-goog-api-key"
|
||||
| "hec"
|
||||
| "cf-aig-authorization"
|
||||
| "none"
|
||||
| "passthrough"
|
||||
>()
|
||||
.notNull(),
|
||||
routingMode: varchar("routingMode")
|
||||
|
||||
@@ -1633,6 +1633,8 @@ export const aiProviders = sqliteTable("aiProviders", {
|
||||
| "x-goog-api-key"
|
||||
| "hec"
|
||||
| "cf-aig-authorization"
|
||||
| "none"
|
||||
| "passthrough"
|
||||
>()
|
||||
.notNull(),
|
||||
routingMode: text("routingMode")
|
||||
|
||||
@@ -14,7 +14,9 @@ export const AI_PROVIDER_AUTH_TYPES = [
|
||||
"x-api-key",
|
||||
"x-goog-api-key",
|
||||
"hec",
|
||||
"cf-aig-authorization"
|
||||
"cf-aig-authorization",
|
||||
"none",
|
||||
"passthrough"
|
||||
] as const;
|
||||
|
||||
export type AiProviderAuthType = (typeof AI_PROVIDER_AUTH_TYPES)[number];
|
||||
@@ -71,6 +73,10 @@ const CONFLICTING_AUTH_HEADERS = [
|
||||
"cf-aig-authorization"
|
||||
] as const;
|
||||
|
||||
export function authTypeRequiresApiKey(authType: AiProviderAuthType): boolean {
|
||||
return authType !== "none" && authType !== "passthrough";
|
||||
}
|
||||
|
||||
export function providerRequiresUpstreamUrl(
|
||||
type: AiProviderType,
|
||||
routingMode: AiProviderRoutingMode = "url"
|
||||
@@ -116,20 +122,26 @@ export function resolveAiProviderCreateFields(input: {
|
||||
const defaults = AI_PROVIDER_DEFAULTS[input.type];
|
||||
return {
|
||||
upstreamUrl: input.upstreamUrl ?? defaults.upstreamUrl,
|
||||
authType: defaults.authType,
|
||||
authType: input.authType ?? defaults.authType,
|
||||
routingMode
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip inbound client auth headers, then set the provider auth header
|
||||
* for the given authType.
|
||||
* Apply provider auth to upstream headers.
|
||||
* - Injected modes: strip client auth headers, then set the provider key.
|
||||
* - none: strip client auth headers, send no auth.
|
||||
* - passthrough: leave client auth headers as-is.
|
||||
*/
|
||||
export function applyAiProviderAuthHeaders(
|
||||
headers: Record<string, string>,
|
||||
authType: AiProviderAuthType,
|
||||
apiKey: string
|
||||
apiKey: string | null
|
||||
): void {
|
||||
if (authType === "passthrough") {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const name of CONFLICTING_AUTH_HEADERS) {
|
||||
for (const key of Object.keys(headers)) {
|
||||
if (key.toLowerCase() === name) {
|
||||
@@ -138,6 +150,14 @@ export function applyAiProviderAuthHeaders(
|
||||
}
|
||||
}
|
||||
|
||||
if (authType === "none") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error(`API key required for authType ${authType}`);
|
||||
}
|
||||
|
||||
switch (authType) {
|
||||
case "bearer":
|
||||
headers["Authorization"] = `Bearer ${apiKey}`;
|
||||
|
||||
@@ -19,7 +19,8 @@ import config from "@server/lib/config";
|
||||
import { decrypt } from "@server/lib/crypto";
|
||||
import {
|
||||
AiProviderAuthType,
|
||||
applyAiProviderAuthHeaders
|
||||
applyAiProviderAuthHeaders,
|
||||
authTypeRequiresApiKey
|
||||
} from "@server/lib/aiProviderDefaults";
|
||||
import {
|
||||
SESSION_COOKIE_NAME,
|
||||
@@ -488,15 +489,6 @@ export async function chatCompletions(
|
||||
|
||||
const { provider } = selection;
|
||||
|
||||
if (!provider.apiKey) {
|
||||
return res.status(HttpCode.INTERNAL_SERVER_ERROR).json({
|
||||
error: { message: "AI provider has no API key configured" }
|
||||
});
|
||||
}
|
||||
|
||||
const secret = config.getRawConfig().server.secret!;
|
||||
const apiKey = decrypt(provider.apiKey, secret);
|
||||
|
||||
const upstreamUrl = provider.upstreamUrl;
|
||||
const authType = provider.authType as AiProviderAuthType;
|
||||
|
||||
@@ -508,6 +500,19 @@ export async function chatCompletions(
|
||||
});
|
||||
}
|
||||
|
||||
let apiKey: string | null = null;
|
||||
if (authTypeRequiresApiKey(authType)) {
|
||||
if (!provider.apiKey) {
|
||||
return res.status(HttpCode.INTERNAL_SERVER_ERROR).json({
|
||||
error: {
|
||||
message: "AI provider has no API key configured"
|
||||
}
|
||||
});
|
||||
}
|
||||
const secret = config.getRawConfig().server.secret!;
|
||||
apiKey = decrypt(provider.apiKey, secret);
|
||||
}
|
||||
|
||||
const targetUrl = `${upstreamUrl.replace(/\/$/, "")}`;
|
||||
|
||||
// Drop hop-by-hop / proxy-only headers. Forwarding Host especially
|
||||
|
||||
@@ -52,12 +52,4 @@ export function refineProviderUpstreamFields(
|
||||
path: ["upstreamUrl"]
|
||||
});
|
||||
}
|
||||
|
||||
if (data.type === "custom" && !data.authType) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: "authType is required for custom providers",
|
||||
path: ["authType"]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user