Add niceid to ui and api for providers

This commit is contained in:
Owen
2026-08-14 09:43:03 -04:00
parent f0f5e9219b
commit 5d37f44241
18 changed files with 219 additions and 75 deletions
@@ -1,5 +1,5 @@
import { Request, Response, NextFunction } from "express";
import { aiProviders, apiKeyOrg, db } from "@server/db";
import { AiProvider, aiProviders, apiKeyOrg, db } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
@@ -13,7 +13,8 @@ export async function verifyApiKeyAiProviderAccess(
try {
const apiKey = req.apiKey;
const providerIdRaw = getFirstString(req.params.providerId);
const providerId = Number.parseInt(providerIdRaw ?? "", 10);
const niceId = getFirstString(req.params.niceId);
const orgIdParam = getFirstString(req.params.orgId);
if (!apiKey) {
return next(
@@ -21,23 +22,42 @@ export async function verifyApiKeyAiProviderAccess(
);
}
if (Number.isNaN(providerId)) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Invalid provider ID")
);
}
let provider: AiProvider | undefined;
const [provider] = await db
.select()
.from(aiProviders)
.where(eq(aiProviders.providerId, providerId))
.limit(1);
if (niceId && orgIdParam) {
const [providerRes] = await db
.select()
.from(aiProviders)
.where(
and(
eq(aiProviders.niceId, niceId),
eq(aiProviders.orgId, orgIdParam)
)
)
.limit(1);
provider = providerRes;
} else {
const providerId = Number.parseInt(providerIdRaw ?? "", 10);
if (Number.isNaN(providerId)) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Invalid provider ID")
);
}
const [providerRes] = await db
.select()
.from(aiProviders)
.where(eq(aiProviders.providerId, providerId))
.limit(1);
provider = providerRes;
}
if (!provider) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`AI provider with ID ${providerId} not found`
`AI provider with ID ${providerIdRaw || niceId} not found`
)
);
}
+33 -13
View File
@@ -1,5 +1,5 @@
import { Request, Response, NextFunction } from "express";
import { aiProviders, db, userOrgs } from "@server/db";
import { AiProvider, aiProviders, db, userOrgs } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
@@ -15,7 +15,8 @@ export async function verifyAiProviderAccess(
try {
const userId = req.user!.userId;
const providerIdRaw = getFirstString(req.params.providerId);
const providerId = Number.parseInt(providerIdRaw ?? "", 10);
const niceId = getFirstString(req.params.niceId);
const orgIdParam = getFirstString(req.params.orgId);
if (!userId) {
return next(
@@ -23,23 +24,42 @@ export async function verifyAiProviderAccess(
);
}
if (Number.isNaN(providerId)) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Invalid provider ID")
);
}
let provider: AiProvider | undefined;
const [provider] = await db
.select()
.from(aiProviders)
.where(eq(aiProviders.providerId, providerId))
.limit(1);
if (niceId && orgIdParam) {
const [providerRes] = await db
.select()
.from(aiProviders)
.where(
and(
eq(aiProviders.niceId, niceId),
eq(aiProviders.orgId, orgIdParam)
)
)
.limit(1);
provider = providerRes;
} else {
const providerId = Number.parseInt(providerIdRaw ?? "", 10);
if (Number.isNaN(providerId)) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Invalid provider ID")
);
}
const [providerRes] = await db
.select()
.from(aiProviders)
.where(eq(aiProviders.providerId, providerId))
.limit(1);
provider = providerRes;
}
if (!provider) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`AI provider with ID ${providerId} not found`
`AI provider with ID ${providerIdRaw || niceId} not found`
)
);
}
+65 -13
View File
@@ -2,26 +2,78 @@ import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { aiProviders, db } from "@server/db";
import response from "@server/lib/response";
import stoi from "@server/lib/stoi";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import { eq } from "drizzle-orm";
import { and, eq } from "drizzle-orm";
import type { GetAiProviderResponse } from "@server/routers/aiProvider/types";
import { toPublicAiProvider } from "@server/routers/aiProvider/types";
const paramsSchema = z.strictObject({
providerId: z.coerce.number().int().positive()
providerId: z
.string()
.optional()
.transform(stoi)
.pipe(z.int().positive().optional())
.optional(),
niceId: z.string().optional(),
orgId: z.string().optional()
});
async function query(providerId?: number, niceId?: string, orgId?: string) {
if (providerId) {
const [res] = await db
.select()
.from(aiProviders)
.where(eq(aiProviders.providerId, providerId))
.limit(1);
return res;
} else if (niceId && orgId) {
const [res] = await db
.select()
.from(aiProviders)
.where(
and(
eq(aiProviders.niceId, niceId),
eq(aiProviders.orgId, orgId)
)
)
.limit(1);
return res;
}
}
registry.registerPath({
method: "get",
path: "/ai-provider/{providerId}",
description: "Get an AI provider by ID.",
tags: [OpenAPITags.AiProvider],
request: {
params: paramsSchema
params: z.object({
providerId: z.string()
})
},
responses: {
200: {
description: "Successful response"
}
}
});
registry.registerPath({
method: "get",
path: "/org/{orgId}/ai-provider/{niceId}",
description:
"Get an AI provider by orgId and niceId. NiceId is a readable ID for the provider and unique on a per org basis.",
tags: [OpenAPITags.AiProvider],
request: {
params: z.object({
orgId: z.string(),
niceId: z.string()
})
},
responses: {
200: {
@@ -46,22 +98,22 @@ export async function getAiProvider(
);
}
const { providerId } = parsedParams.data;
const { providerId, niceId, orgId } = parsedParams.data;
const [provider] =
req.aiProvider && req.aiProvider.providerId === providerId
? [req.aiProvider]
: await db
.select()
.from(aiProviders)
.where(eq(aiProviders.providerId, providerId))
.limit(1);
const provider =
req.aiProvider &&
(req.aiProvider.providerId === providerId ||
(niceId &&
req.aiProvider.niceId === niceId &&
req.aiProvider.orgId === orgId))
? req.aiProvider
: await query(providerId, niceId, orgId);
if (!provider) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`AI provider with ID ${providerId} not found`
`AI provider with ID ${providerId || niceId} not found`
)
);
}
+7
View File
@@ -1589,6 +1589,13 @@ authenticated.get(
verifyUserHasAction(ActionsEnum.getAiProvider),
aiProvider.getAiProvider
);
authenticated.get(
"/org/:orgId/ai-provider/:niceId",
verifyOrgAccess,
verifyAiProviderAccess,
verifyUserHasAction(ActionsEnum.getAiProvider),
aiProvider.getAiProvider
);
authenticated.put(
"/ai-provider/:providerId/target",
+7
View File
@@ -1624,6 +1624,13 @@ authenticated.get(
verifyApiKeyHasAction(ActionsEnum.getAiProvider),
aiProvider.getAiProvider
);
authenticated.get(
"/org/:orgId/ai-provider/:niceId",
verifyApiKeyOrgAccess,
verifyApiKeyAiProviderAccess,
verifyApiKeyHasAction(ActionsEnum.getAiProvider),
aiProvider.getAiProvider
);
authenticated.put(
"/ai-provider/:providerId/target",