diff --git a/packages/backend-core/src/errors/errors.ts b/packages/backend-core/src/errors/errors.ts index 79aa330de43..2d205f6277b 100644 --- a/packages/backend-core/src/errors/errors.ts +++ b/packages/backend-core/src/errors/errors.ts @@ -97,45 +97,15 @@ export class NotImplementedError extends HTTPError { } // LICENSING - -export class UsageLimitError extends HTTPError { - limitName: string - - constructor(message: string, limitName: string) { - super(message, 400, ErrorCode.USAGE_LIMIT_EXCEEDED) - this.limitName = limitName - } - - getPublicError() { - return { - limitName: this.limitName, - } +export class FeatureDisabledError extends Error { + constructor(feature: string) { + super(`Feature disabled: '${feature}'`) } } -export class FeatureDisabledError extends HTTPError { - featureName: string - - constructor(message: string, featureName: string) { - super(message, 400, ErrorCode.FEATURE_DISABLED) - this.featureName = featureName - } - - getPublicError() { - return { - featureName: this.featureName, - } - } -} - -// AUTH - -export class InvalidAPIKeyError extends BudibaseError { - constructor() { - super( - "Invalid API key - may need re-generated, or user doesn't exist", - ErrorCode.INVALID_API_KEY - ) +export class UsageLimitError extends Error { + constructor(limitName: string) { + super(`Usage limit exceeded: '${limitName}'`) } } diff --git a/packages/backend-core/src/middleware/authenticated.ts b/packages/backend-core/src/middleware/authenticated.ts index e413db2bf62..0d910262085 100644 --- a/packages/backend-core/src/middleware/authenticated.ts +++ b/packages/backend-core/src/middleware/authenticated.ts @@ -16,12 +16,12 @@ import env from "../environment" import { Ctx, EndpointMatcher, - ErrorCode, + APIWarningCode, LoginMethod, SessionCookie, User, } from "@budibase/types" -import { InvalidAPIKeyError } from "../errors" +import { InvalidAPIKeyWarning } from "../warnings" import tracer from "dd-trace" import type { Middleware, Next } from "koa" @@ -91,7 +91,7 @@ async function checkApiKey( }), } } else { - throw new InvalidAPIKeyError() + throw new InvalidAPIKeyWarning() } }) } @@ -242,7 +242,7 @@ export function authenticated( // invalid token, clear the cookie if (err?.name === "JsonWebTokenError") { clearCookie(ctx, Cookie.Auth) - } else if (err?.code === ErrorCode.INVALID_API_KEY) { + } else if (err?.code === APIWarningCode.INVALID_API_KEY) { ctx.throw(403, err.message) } // allow configuring for public access diff --git a/packages/backend-core/src/warnings/index.ts b/packages/backend-core/src/warnings/index.ts new file mode 100644 index 00000000000..8e9763624d1 --- /dev/null +++ b/packages/backend-core/src/warnings/index.ts @@ -0,0 +1 @@ +export * from "./warnings" diff --git a/packages/backend-core/src/warnings/warnings.ts b/packages/backend-core/src/warnings/warnings.ts new file mode 100644 index 00000000000..5d0e72d75de --- /dev/null +++ b/packages/backend-core/src/warnings/warnings.ts @@ -0,0 +1,54 @@ +// BASE + +import { APIWarningCode } from "@budibase/types" + +export abstract class APIWarning extends Error { + code: string + + constructor(message: string, code: string) { + super(message) + this.code = code + } + + protected getPublicWarning?(): any +} + +// AUTH + +export class InvalidAPIKeyWarning extends APIWarning { + constructor() { + super("Invalid API key", APIWarningCode.INVALID_API_KEY) + } +} + +// LICENSING + +export class UsageLimitWarning extends APIWarning { + limitName: string + + constructor(message: string, limitName: string) { + super(message, APIWarningCode.USAGE_LIMIT_EXCEEDED) + this.limitName = limitName + } + + getPublicWarning() { + return { + limitName: this.limitName, + } + } +} + +export class FeatureDisabledWarning extends APIWarning { + featureName: string + + constructor(message: string, featureName: string) { + super(message, APIWarningCode.FEATURE_DISABLED) + this.featureName = featureName + } + + getPublicWarning() { + return { + featureName: this.featureName, + } + } +} diff --git a/packages/builder/src/components/common/CodeEditor/AIGen.svelte b/packages/builder/src/components/common/CodeEditor/AIGen.svelte index e3843bbb2af..c3c1ac0a44f 100644 --- a/packages/builder/src/components/common/CodeEditor/AIGen.svelte +++ b/packages/builder/src/components/common/CodeEditor/AIGen.svelte @@ -3,7 +3,7 @@ import { createEventDispatcher } from "svelte" import { API } from "@/api" - import { ErrorCode, type EnrichedBinding } from "@budibase/types" + import { APIWarningCode, type EnrichedBinding } from "@budibase/types" import analytics, { Events } from "@/analytics" import AiInput from "../ai/AIInput.svelte" @@ -51,7 +51,7 @@ } catch (e: any) { console.error(e) - if ("code" in e && e.code === ErrorCode.USAGE_LIMIT_EXCEEDED) { + if ("code" in e && e.code === APIWarningCode.USAGE_LIMIT_EXCEEDED) { notifications.error( "Monthly usage limit reached. We're exploring options to expand this soon. Questions? Contact support@budibase.com" ) diff --git a/packages/server/src/api/controllers/workspace.ts b/packages/server/src/api/controllers/workspace.ts index d18c51b4e41..20530372712 100644 --- a/packages/server/src/api/controllers/workspace.ts +++ b/packages/server/src/api/controllers/workspace.ts @@ -25,7 +25,7 @@ import { DeleteWorkspaceResponse, DuplicateWorkspaceRequest, DuplicateWorkspaceResponse, - ErrorCode, + APIWarningCode, FetchAppDefinitionResponse, FetchAppPackageResponse, FetchPublishedAppsResponse, @@ -638,7 +638,7 @@ async function workspacePostCreate( return quotas.addRows(rowCount) }) } catch (err: any) { - if (err.code && err.code === ErrorCode.USAGE_LIMIT_EXCEEDED) { + if (err.code && err.code === APIWarningCode.USAGE_LIMIT_EXCEEDED) { // this import resulted in row usage exceeding the quota // delete the app // skip pre and post-steps as no rows have been added to quotas yet diff --git a/packages/types/src/api/web/errors.ts b/packages/types/src/api/web/errors.ts index 9e448701ad3..7ef37d928be 100644 --- a/packages/types/src/api/web/errors.ts +++ b/packages/types/src/api/web/errors.ts @@ -6,8 +6,5 @@ export interface APIError { } export enum ErrorCode { - USAGE_LIMIT_EXCEEDED = "usage_limit_exceeded", - FEATURE_DISABLED = "feature_disabled", - INVALID_API_KEY = "invalid_api_key", HTTP = "http", } diff --git a/packages/types/src/api/web/index.ts b/packages/types/src/api/web/index.ts index 57ef85760cf..e38231857a2 100644 --- a/packages/types/src/api/web/index.ts +++ b/packages/types/src/api/web/index.ts @@ -6,6 +6,7 @@ export * from "./cookies" export * from "./debug" export * from "./dev" export * from "./errors" +export * from "./warnings" export * from "./global" export * from "./pagination" export * from "./plugins" diff --git a/packages/types/src/api/web/warnings.ts b/packages/types/src/api/web/warnings.ts new file mode 100644 index 00000000000..82edc3939c7 --- /dev/null +++ b/packages/types/src/api/web/warnings.ts @@ -0,0 +1,10 @@ +export interface APIWarning { + message: string + status: number +} + +export enum APIWarningCode { + USAGE_LIMIT_EXCEEDED = "usage_limit_exceeded", + FEATURE_DISABLED = "feature_disabled", + INVALID_API_KEY = "invalid_api_key", +} diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 591b4bab2cc..2b9baa06f09 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -30,7 +30,7 @@ import { DeleteInviteUsersResponse, DeleteUserResponse, EditUserPermissionsResponse, - ErrorCode, + APIWarningCode, FetchUsersResponse, FindUserResponse, GetUserInvitesResponse, @@ -614,7 +614,7 @@ export const inviteAccept = async ( } ) } catch (err: any) { - if (err.code === ErrorCode.USAGE_LIMIT_EXCEEDED) { + if (err.code === APIWarningCode.USAGE_LIMIT_EXCEEDED) { // explicitly re-throw limit exceeded errors ctx.throw(400, err) }