clean up providers ui

This commit is contained in:
miloschwartz
2026-08-03 16:06:28 -04:00
parent 0d9a01a066
commit 209272f69c
17 changed files with 1412 additions and 962 deletions
@@ -135,7 +135,9 @@ export async function createAiProvider(
.returning();
return response<CreateOrEditAiProviderResponse>(res, {
data: { provider: toPublicAiProvider(provider) },
data: {
provider: toPublicAiProvider(provider, { includeApiKey: true })
},
success: true,
error: false,
message: "AI provider created successfully",
+3 -1
View File
@@ -67,7 +67,9 @@ export async function getAiProvider(
}
return response<GetAiProviderResponse>(res, {
data: { provider: toPublicAiProvider(provider) },
data: {
provider: toPublicAiProvider(provider, { includeApiKey: true })
},
success: true,
error: false,
message: "AI provider retrieved successfully",
+22 -2
View File
@@ -6,8 +6,12 @@ import {
type AiProviderRoutingMode,
type AiProviderType
} from "@server/lib/aiProviderDefaults";
import { decrypt } from "@server/lib/crypto";
import config from "@server/lib/config";
export type AiProviderPublic = Omit<AiProvider, "apiKey"> & {
/** Decrypted API key. Only included on get/create/update of a single provider. */
apiKey?: string | null;
effectiveUpstreamUrl: string | null;
effectiveAuthType: AiProviderAuthType | null;
};
@@ -36,8 +40,11 @@ export type CreateOrEditAiModelResponse = {
model: AiModel;
};
export function toPublicAiProvider(provider: AiProvider): AiProviderPublic {
const { apiKey: _apiKey, ...rest } = provider;
export function toPublicAiProvider(
provider: AiProvider,
options?: { includeApiKey?: boolean }
): AiProviderPublic {
const { apiKey: encryptedApiKey, ...rest } = provider;
const resolved = resolveAiProviderConfig({
type: provider.type as AiProviderType,
upstreamUrl: provider.upstreamUrl,
@@ -45,8 +52,21 @@ export function toPublicAiProvider(provider: AiProvider): AiProviderPublic {
routingMode: provider.routingMode as AiProviderRoutingMode | null
});
let apiKey: string | null | undefined;
if (options?.includeApiKey) {
if (encryptedApiKey) {
apiKey = decrypt(
encryptedApiKey,
config.getRawConfig().server.secret!
);
} else {
apiKey = null;
}
}
return {
...rest,
...(options?.includeApiKey ? { apiKey } : {}),
effectiveUpstreamUrl: resolved.upstreamUrl,
effectiveAuthType: resolved.authType
};
+14 -2
View File
@@ -125,7 +125,10 @@ export async function updateAiProvider(
? body.upstreamUrl
: existing.upstreamUrl;
const nextAuthType =
body.authType !== undefined ? body.authType : existing.authType;
body.authType !== undefined
? body.authType
: (existing.authType ??
(providerType === "custom" ? "bearer" : null));
const validation = z
.object({
@@ -178,6 +181,13 @@ export async function updateAiProvider(
}
if (body.authType !== undefined) {
updateData.authType = body.authType;
} else if (
providerType === "custom" &&
!existing.authType &&
nextAuthType
) {
// Backfill required authType for custom providers created without one
updateData.authType = nextAuthType;
}
if (body.apiKey !== undefined) {
@@ -193,7 +203,9 @@ export async function updateAiProvider(
.returning();
return response<CreateOrEditAiProviderResponse>(res, {
data: { provider: toPublicAiProvider(provider) },
data: {
provider: toPublicAiProvider(provider, { includeApiKey: true })
},
success: true,
error: false,
message: "AI provider updated successfully",
+1 -1
View File
@@ -75,7 +75,7 @@ export function refineProviderUpstreamFields(
});
}
if (data.type === "custom" && routingMode === "url" && !data.authType) {
if (data.type === "custom" && !data.authType) {
ctx.addIssue({
code: "custom",
message: "authType is required for custom providers",