support sending capability specific error codes

This commit is contained in:
miloschwartz
2026-08-12 12:34:25 -04:00
parent 115c3cbf07
commit 93cba1d098
4 changed files with 175 additions and 17 deletions
+8 -8
View File
@@ -366,10 +366,6 @@ async function resolveTarget(host: string): Promise<ResolvedTarget | null> {
.where(eq(resourceAiModels.resourceId, resourceRow.resourceId))
]);
if (attachmentRows.length === 0) {
return null;
}
return {
resourceId: resourceRow.resourceId,
siteResourceId: null,
@@ -427,10 +423,6 @@ async function resolveTarget(host: string): Promise<ResolvedTarget | null> {
)
]);
if (attachmentRows.length === 0) {
return null;
}
return {
resourceId: null,
siteResourceId: siteResourceRow.siteResourceId,
@@ -756,6 +748,14 @@ export async function handleAiGatewayProxy(
});
}
if (attachments.length === 0) {
return res.status(HttpCode.FORBIDDEN).json({
error: {
message: "No AI providers configured for this resource"
}
});
}
const capableAttachments = attachments.filter((a) =>
providerHasCapability(a.provider.capabilities, capability)
);
+36 -1
View File
@@ -48,6 +48,11 @@ import { z } from "zod";
import { fromError } from "zod-validation-error";
import { getCountryCodeForIp } from "@server/lib/geoip";
import { getAsnForIp } from "@server/lib/asn";
import {
buildInferenceAuthClientError,
type ClientErrorResponse
} from "@server/lib/aiGatewayAuthError";
import { resolveAiCapabilityFromPath } from "@server/lib/aiCapabilities";
import { verifyPassword } from "@server/auth/password";
import {
checkOrgAccessPolicy,
@@ -89,6 +94,8 @@ type BasicUserData = {
role: string | null;
};
export type { ClientErrorResponse };
export type VerifyUserResponse = {
valid: boolean;
headerAuthChallenged?: boolean;
@@ -96,8 +103,27 @@ export type VerifyUserResponse = {
userData?: BasicUserData;
pangolinVersion?: string;
dontStripSession?: boolean;
clientError?: ClientErrorResponse;
};
function notAllowedWithClientError(
res: Response,
clientError: ClientErrorResponse
) {
const data = {
data: {
valid: false,
clientError,
pangolinVersion: APP_VERSION
},
success: true,
error: false,
message: "Access denied",
status: HttpCode.OK
};
return response<VerifyUserResponse>(res, data);
}
export async function verifyResourceSession(
req: Request,
res: Response,
@@ -408,7 +434,16 @@ export async function verifyResourceSession(
parsedBody.data
);
return notAllowed(res, redirectPath, resource.orgId);
// Browsers go to the resource auth / API key page. API clients get
// a capability-shaped JSON auth error instead of a redirect.
if (clientIsBrowser) {
return notAllowed(res, redirectPath, resource.orgId);
}
return notAllowedWithClientError(
res,
buildInferenceAuthClientError(resolveAiCapabilityFromPath(path))
);
}
// check for access token in headers