refactor(core): Reorganize error hierarchy in cli package (no-changelog) (#7839)

Ensure all errors in `cli` inherit from `ApplicationError` to continue
normalizing all the errors we report to Sentry

Follow-up to: https://github.com/n8n-io/n8n/pull/7820
This commit is contained in:
Iván Ovejero 2023-11-28 10:19:27 +01:00 committed by GitHub
parent 38f24a6184
commit 1c6178759c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 346 additions and 297 deletions

View file

@ -11,13 +11,14 @@ import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import * as Db from '@/Db';
import type { N8nInstanceType, IExternalHooksClass } from '@/Interfaces';
import { ExternalHooks } from '@/ExternalHooks';
import { send, sendErrorResponse, ServiceUnavailableError } from '@/ResponseHelper';
import { send, sendErrorResponse } from '@/ResponseHelper';
import { rawBodyReader, bodyParser, corsMiddleware } from '@/middlewares';
import { TestWebhooks } from '@/TestWebhooks';
import { WaitingWebhooks } from '@/WaitingWebhooks';
import { webhookRequestHandler } from '@/WebhookHelpers';
import { generateHostInstanceId } from './databases/utils/generators';
import { Logger } from '@/Logger';
import { ServiceUnavailableError } from './errors/response-errors/service-unavailable.error';
export abstract class AbstractServer {
protected logger: Logger;

View file

@ -42,7 +42,6 @@ import type {
WebhookAccessControlOptions,
WebhookRequest,
} from '@/Interfaces';
import * as ResponseHelper from '@/ResponseHelper';
import * as WebhookHelpers from '@/WebhookHelpers';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
@ -68,6 +67,7 @@ import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.reposi
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee';
import { ActivationErrorsService } from '@/ActivationErrors.service';
import { NotFoundError } from './errors/response-errors/not-found.error';
const WEBHOOK_PROD_UNREGISTERED_HINT =
"The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)";
@ -165,9 +165,7 @@ export class ActiveWorkflowRunner implements IWebhookManager {
});
if (workflowData === null) {
throw new ResponseHelper.NotFoundError(
`Could not find workflow with id "${webhook.workflowId}"`,
);
throw new NotFoundError(`Could not find workflow with id "${webhook.workflowId}"`);
}
const workflow = new Workflow({
@ -196,7 +194,7 @@ export class ActiveWorkflowRunner implements IWebhookManager {
const workflowStartNode = workflow.getNode(webhookData.node);
if (workflowStartNode === null) {
throw new ResponseHelper.NotFoundError('Could not find node to process webhook.');
throw new NotFoundError('Could not find node to process webhook.');
}
return new Promise((resolve, reject) => {
@ -252,7 +250,7 @@ export class ActiveWorkflowRunner implements IWebhookManager {
const webhook = await this.webhookService.findWebhook(httpMethod, path);
if (webhook === null) {
throw new ResponseHelper.NotFoundError(
throw new NotFoundError(
webhookNotFoundErrorMessage(path, httpMethod),
WEBHOOK_PROD_UNREGISTERED_HINT,
);

View file

@ -32,7 +32,6 @@ import type {
INodeTypes,
IWorkflowExecuteAdditionalData,
ICredentialTestFunctions,
Severity,
} from 'n8n-workflow';
import {
ICredentialsHelper,
@ -55,6 +54,7 @@ import { isObjectLiteral } from './utils';
import { Logger } from '@/Logger';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { CredentialNotFoundError } from './errors/credential-not-found.error';
const { OAUTH2_CREDENTIAL_TEST_SUCCEEDED, OAUTH2_CREDENTIAL_TEST_FAILED } = RESPONSE_ERROR_MESSAGES;
@ -87,15 +87,6 @@ const mockNodeTypes: INodeTypes = {
},
};
class CredentialNotFoundError extends Error {
severity: Severity;
constructor(credentialId: string, credentialType: string) {
super(`Credential with ID "${credentialId}" does not exist for type "${credentialType}".`);
this.severity = 'warning';
}
}
@Service()
export class CredentialsHelper extends ICredentialsHelper {
constructor(

View file

@ -1,9 +1,10 @@
import { Authorized, Get, Post, RestController } from '@/decorators';
import { ExternalSecretsRequest } from '@/requests';
import { NotFoundError } from '@/ResponseHelper';
import { Response } from 'express';
import { Service } from 'typedi';
import { ProviderNotFoundError, ExternalSecretsService } from './ExternalSecrets.service.ee';
import { ExternalSecretsService } from './ExternalSecrets.service.ee';
import { ExternalSecretsProviderNotFoundError } from '@/errors/external-secrets-provider-not-found.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
@Service()
@Authorized(['global', 'owner'])
@ -22,7 +23,7 @@ export class ExternalSecretsController {
try {
return this.secretsService.getProvider(providerName);
} catch (e) {
if (e instanceof ProviderNotFoundError) {
if (e instanceof ExternalSecretsProviderNotFoundError) {
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
}
throw e;
@ -41,7 +42,7 @@ export class ExternalSecretsController {
}
return result;
} catch (e) {
if (e instanceof ProviderNotFoundError) {
if (e instanceof ExternalSecretsProviderNotFoundError) {
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
}
throw e;
@ -54,7 +55,7 @@ export class ExternalSecretsController {
try {
await this.secretsService.saveProviderSettings(providerName, req.body, req.user.id);
} catch (e) {
if (e instanceof ProviderNotFoundError) {
if (e instanceof ExternalSecretsProviderNotFoundError) {
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
}
throw e;
@ -68,7 +69,7 @@ export class ExternalSecretsController {
try {
await this.secretsService.saveProviderConnected(providerName, req.body.connected);
} catch (e) {
if (e instanceof ProviderNotFoundError) {
if (e instanceof ExternalSecretsProviderNotFoundError) {
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
}
throw e;
@ -88,7 +89,7 @@ export class ExternalSecretsController {
}
return { updated: resp };
} catch (e) {
if (e instanceof ProviderNotFoundError) {
if (e instanceof ExternalSecretsProviderNotFoundError) {
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
}
throw e;

View file

@ -5,12 +5,7 @@ import type { IDataObject } from 'n8n-workflow';
import { deepCopy } from 'n8n-workflow';
import Container, { Service } from 'typedi';
import { ExternalSecretsManager } from './ExternalSecretsManager.ee';
export class ProviderNotFoundError extends Error {
constructor(public providerName: string) {
super(undefined);
}
}
import { ExternalSecretsProviderNotFoundError } from '@/errors/external-secrets-provider-not-found.error';
@Service()
export class ExternalSecretsService {
@ -18,7 +13,7 @@ export class ExternalSecretsService {
const providerAndSettings =
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
if (!providerAndSettings) {
throw new ProviderNotFoundError(providerName);
throw new ExternalSecretsProviderNotFoundError(providerName);
}
const { provider, settings } = providerAndSettings;
return {
@ -110,7 +105,7 @@ export class ExternalSecretsService {
const providerAndSettings =
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
if (!providerAndSettings) {
throw new ProviderNotFoundError(providerName);
throw new ExternalSecretsProviderNotFoundError(providerName);
}
const { settings } = providerAndSettings;
const newData = this.unredact(data, settings.settings);
@ -121,7 +116,7 @@ export class ExternalSecretsService {
const providerAndSettings =
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
if (!providerAndSettings) {
throw new ProviderNotFoundError(providerName);
throw new ExternalSecretsProviderNotFoundError(providerName);
}
await Container.get(ExternalSecretsManager).setProviderConnected(providerName, connected);
return this.getProvider(providerName);
@ -135,7 +130,7 @@ export class ExternalSecretsService {
const providerAndSettings =
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
if (!providerAndSettings) {
throw new ProviderNotFoundError(providerName);
throw new ExternalSecretsProviderNotFoundError(providerName);
}
const { settings } = providerAndSettings;
const newData = this.unredact(data, settings.settings);
@ -146,7 +141,7 @@ export class ExternalSecretsService {
const providerAndSettings =
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
if (!providerAndSettings) {
throw new ProviderNotFoundError(providerName);
throw new ExternalSecretsProviderNotFoundError(providerName);
}
return Container.get(ExternalSecretsManager).updateProvider(providerName);
}

View file

@ -11,7 +11,6 @@ import { Container } from 'typedi';
import { Like } from 'typeorm';
import config from '@/config';
import type { ExecutionPayload, ICredentialsDb, IWorkflowDb } from '@/Interfaces';
import * as ResponseHelper from '@/ResponseHelper';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import type { TagEntity } from '@db/entities/TagEntity';
@ -20,6 +19,7 @@ import type { UserUpdatePayload } from '@/requests';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { ExecutionRepository } from '@db/repositories/execution.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { BadRequestError } from './errors/response-errors/bad-request.error';
/**
* Returns the base URL n8n is reachable from
@ -109,7 +109,7 @@ export async function validateEntity(
.join(' | ');
if (errorMessages) {
throw new ResponseHelper.BadRequestError(errorMessages);
throw new BadRequestError(errorMessages);
}
}

View file

@ -29,13 +29,14 @@ import {
isLdapCurrentAuthenticationMethod,
setCurrentAuthenticationMethod,
} from '@/sso/ssoHelpers';
import { BadRequestError, InternalServerError } from '../ResponseHelper';
import { RoleService } from '@/services/role.service';
import { Logger } from '@/Logger';
import { UserRepository } from '@db/repositories/user.repository';
import { SettingsRepository } from '@db/repositories/settings.repository';
import { AuthProviderSyncHistoryRepository } from '@db/repositories/authProviderSyncHistory.repository';
import { AuthIdentityRepository } from '@db/repositories/authIdentity.repository';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
/**
* Check whether the LDAP feature is disabled in the instance

View file

@ -24,14 +24,6 @@ type FeatureReturnType = Partial<
} & { [K in NumericLicenseFeature]: number } & { [K in BooleanLicenseFeature]: boolean }
>;
export class FeatureNotLicensedError extends Error {
constructor(feature: (typeof LICENSE_FEATURES)[keyof typeof LICENSE_FEATURES]) {
super(
`Your license does not allow for ${feature}. To enable ${feature}, please upgrade to a license that supports this feature.`,
);
}
}
@Service()
export class License {
private manager: LicenseManager | undefined;

View file

@ -12,14 +12,7 @@ import { LoadNodesAndCredentials } from './LoadNodesAndCredentials';
import { join, dirname } from 'path';
import { readdir } from 'fs/promises';
import type { Dirent } from 'fs';
class UnrecognizedNodeError extends Error {
severity = 'warning';
constructor(nodeType: string) {
super(`Unrecognized node type: ${nodeType}".`);
}
}
import { UnrecognizedNodeTypeError } from './errors/unrecognized-node-type.error';
@Service()
export class NodeTypes implements INodeTypes {
@ -75,7 +68,7 @@ export class NodeTypes implements INodeTypes {
return loadedNodes[type];
}
throw new UnrecognizedNodeError(type);
throw new UnrecognizedNodeTypeError(type);
}
async getNodeTranslationPath({

View file

@ -12,76 +12,7 @@ import type {
IWorkflowDb,
} from '@/Interfaces';
import { inDevelopment } from '@/constants';
/**
* Special Error which allows to return also an error code and http status code
*/
abstract class ResponseError extends Error {
/**
* Creates an instance of ResponseError.
* Must be used inside a block with `ResponseHelper.send()`.
*/
constructor(
message: string,
// The HTTP status code of response
readonly httpStatusCode: number,
// The error code in the response
readonly errorCode: number = httpStatusCode,
// The error hint the response
readonly hint: string | undefined = undefined,
) {
super(message);
this.name = 'ResponseError';
}
}
export class BadRequestError extends ResponseError {
constructor(message: string, errorCode?: number) {
super(message, 400, errorCode);
}
}
export class AuthError extends ResponseError {
constructor(message: string, errorCode?: number) {
super(message, 401, errorCode);
}
}
export class UnauthorizedError extends ResponseError {
constructor(message: string, hint: string | undefined = undefined) {
super(message, 403, 403, hint);
}
}
export class NotFoundError extends ResponseError {
constructor(message: string, hint: string | undefined = undefined) {
super(message, 404, 404, hint);
}
}
export class ConflictError extends ResponseError {
constructor(message: string, hint: string | undefined = undefined) {
super(message, 409, 409, hint);
}
}
export class UnprocessableRequestError extends ResponseError {
constructor(message: string, hint: string | undefined = undefined) {
super(message, 422, 422, hint);
}
}
export class InternalServerError extends ResponseError {
constructor(message: string, errorCode = 500) {
super(message, 500, errorCode);
}
}
export class ServiceUnavailableError extends ResponseError {
constructor(message: string, errorCode = 503) {
super(message, 503, errorCode);
}
}
import { ResponseError } from './errors/response-errors/abstract/response.error';
export function sendSuccessResponse(
res: Response,

View file

@ -117,6 +117,8 @@ import { OrchestrationController } from './controllers/orchestration.controller'
import { WorkflowHistoryController } from './workflows/workflowHistory/workflowHistory.controller.ee';
import { InvitationController } from './controllers/invitation.controller';
import { CollaborationService } from './collaboration/collaboration.service';
import { BadRequestError } from './errors/response-errors/bad-request.error';
import { NotFoundError } from './errors/response-errors/not-found.error';
const exec = promisify(callbackExec);
@ -466,9 +468,7 @@ export class Server extends AbstractServer {
userId: req.user.id,
});
throw new ResponseHelper.BadRequestError(
`Workflow with ID "${workflowId}" could not be found.`,
);
throw new BadRequestError(`Workflow with ID "${workflowId}" could not be found.`);
}
return this.activeWorkflowRunner.getActivationError(workflowId);
@ -491,7 +491,7 @@ export class Server extends AbstractServer {
const parameters = toHttpNodeParameters(curlCommand);
return ResponseHelper.flattenObject(parameters, 'parameters');
} catch (e) {
throw new ResponseHelper.BadRequestError('Invalid cURL command');
throw new BadRequestError('Invalid cURL command');
}
},
),
@ -624,7 +624,7 @@ export class Server extends AbstractServer {
const sharedWorkflowIds = await getSharedWorkflowIds(req.user);
if (!sharedWorkflowIds.length) {
throw new ResponseHelper.NotFoundError('Execution not found');
throw new NotFoundError('Execution not found');
}
const fullExecutionData = await Container.get(ExecutionRepository).findSingleExecution(
@ -637,7 +637,7 @@ export class Server extends AbstractServer {
);
if (!fullExecutionData) {
throw new ResponseHelper.NotFoundError('Execution not found');
throw new NotFoundError('Execution not found');
}
if (config.getEnv('executions.mode') === 'queue') {

View file

@ -20,9 +20,9 @@ import type {
} from '@/Interfaces';
import { Push } from '@/push';
import { NodeTypes } from '@/NodeTypes';
import * as ResponseHelper from '@/ResponseHelper';
import * as WebhookHelpers from '@/WebhookHelpers';
import { webhookNotFoundErrorMessage } from './utils';
import { NotFoundError } from './errors/response-errors/not-found.error';
const WEBHOOK_TEST_UNREGISTERED_HINT =
"Click the 'Execute workflow' button on the canvas, then try again. (In test mode, the webhook only works for one call after you click this button)";
@ -80,7 +80,7 @@ export class TestWebhooks implements IWebhookManager {
if (webhookData === undefined) {
// The requested webhook is not registered
const methods = await this.getWebhookMethods(path);
throw new ResponseHelper.NotFoundError(
throw new NotFoundError(
webhookNotFoundErrorMessage(path, httpMethod, methods),
WEBHOOK_TEST_UNREGISTERED_HINT,
);
@ -108,7 +108,7 @@ export class TestWebhooks implements IWebhookManager {
if (testWebhookData[webhookKey] === undefined) {
// The requested webhook is not registered
const methods = await this.getWebhookMethods(path);
throw new ResponseHelper.NotFoundError(
throw new NotFoundError(
webhookNotFoundErrorMessage(path, httpMethod, methods),
WEBHOOK_TEST_UNREGISTERED_HINT,
);
@ -121,7 +121,7 @@ export class TestWebhooks implements IWebhookManager {
// get additional data
const workflowStartNode = workflow.getNode(webhookData.node);
if (workflowStartNode === null) {
throw new ResponseHelper.NotFoundError('Could not find node to process webhook.');
throw new NotFoundError('Could not find node to process webhook.');
}
return new Promise(async (resolve, reject) => {
@ -168,10 +168,7 @@ export class TestWebhooks implements IWebhookManager {
const webhookMethods = this.activeWebhooks.getWebhookMethods(path);
if (!webhookMethods.length) {
// The requested webhook is not registered
throw new ResponseHelper.NotFoundError(
webhookNotFoundErrorMessage(path),
WEBHOOK_TEST_UNREGISTERED_HINT,
);
throw new NotFoundError(webhookNotFoundErrorMessage(path), WEBHOOK_TEST_UNREGISTERED_HINT);
}
return webhookMethods;

View file

@ -2,7 +2,6 @@ import { In } from 'typeorm';
import { compare, genSaltSync, hash } from 'bcryptjs';
import { Container } from 'typedi';
import * as ResponseHelper from '@/ResponseHelper';
import type { WhereClause } from '@/Interfaces';
import type { User } from '@db/entities/User';
import { MAX_PASSWORD_LENGTH, MIN_PASSWORD_LENGTH } from '@db/entities/User';
@ -11,6 +10,7 @@ import { License } from '@/License';
import { getWebhookBaseUrl } from '@/WebhookHelpers';
import { RoleService } from '@/services/role.service';
import { UserRepository } from '@db/repositories/user.repository';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
export function isSharingEnabled(): boolean {
return Container.get(License).isSharingEnabled();
@ -43,7 +43,7 @@ export function generateUserInviteUrl(inviterId: string, inviteeId: string): str
// TODO: Enforce at model level
export function validatePassword(password?: string): string {
if (!password) {
throw new ResponseHelper.BadRequestError('Password is mandatory');
throw new BadRequestError('Password is mandatory');
}
const hasInvalidLength =
@ -70,7 +70,7 @@ export function validatePassword(password?: string): string {
message.push('Password must contain at least 1 uppercase letter.');
}
throw new ResponseHelper.BadRequestError(message.join(' '));
throw new BadRequestError(message.join(' '));
}
return password;

View file

@ -2,7 +2,6 @@ import { NodeHelpers, Workflow } from 'n8n-workflow';
import { Service } from 'typedi';
import type express from 'express';
import * as ResponseHelper from '@/ResponseHelper';
import * as WebhookHelpers from '@/WebhookHelpers';
import { NodeTypes } from '@/NodeTypes';
import type {
@ -15,6 +14,8 @@ import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'
import { ExecutionRepository } from '@db/repositories/execution.repository';
import { OwnershipService } from './services/ownership.service';
import { Logger } from '@/Logger';
import { ConflictError } from './errors/response-errors/conflict.error';
import { NotFoundError } from './errors/response-errors/not-found.error';
@Service()
export class WaitingWebhooks implements IWebhookManager {
@ -43,11 +44,11 @@ export class WaitingWebhooks implements IWebhookManager {
});
if (!execution) {
throw new ResponseHelper.NotFoundError(`The execution "${executionId} does not exist.`);
throw new NotFoundError(`The execution "${executionId} does not exist.`);
}
if (execution.finished || execution.data.resultData.error) {
throw new ResponseHelper.ConflictError(`The execution "${executionId} has finished already.`);
throw new ConflictError(`The execution "${executionId} has finished already.`);
}
const lastNodeExecuted = execution.data.resultData.lastNodeExecuted as string;
@ -79,12 +80,12 @@ export class WaitingWebhooks implements IWebhookManager {
try {
workflowOwner = await this.ownershipService.getWorkflowOwnerCached(workflowData.id!);
} catch (error) {
throw new ResponseHelper.NotFoundError('Could not find workflow');
throw new NotFoundError('Could not find workflow');
}
const workflowStartNode = workflow.getNode(lastNodeExecuted);
if (workflowStartNode === null) {
throw new ResponseHelper.NotFoundError('Could not find node to process webhook.');
throw new NotFoundError('Could not find node to process webhook.');
}
const additionalData = await WorkflowExecuteAdditionalData.getBase(workflowOwner.id);
@ -103,7 +104,7 @@ export class WaitingWebhooks implements IWebhookManager {
// If no data got found it means that the execution can not be started via a webhook.
// Return 404 because we do not want to give any data if the execution exists or not.
const errorMessage = `The workflow for execution "${executionId}" does not contain a waiting webhook with a matching path/method.`;
throw new ResponseHelper.NotFoundError(errorMessage);
throw new NotFoundError(errorMessage);
}
const runExecutionData = execution.data;

View file

@ -63,6 +63,9 @@ import { OwnershipService } from './services/ownership.service';
import { parseBody } from './middlewares';
import { WorkflowsService } from './workflows/workflows.services';
import { Logger } from './Logger';
import { NotFoundError } from './errors/response-errors/not-found.error';
import { InternalServerError } from './errors/response-errors/internal-server.error';
import { UnprocessableRequestError } from './errors/response-errors/unprocessable.error';
const pipeline = promisify(stream.pipeline);
@ -237,7 +240,7 @@ export async function executeWebhook(
if (nodeType === undefined) {
const errorMessage = `The type of the webhook node "${workflowStartNode.name}" is not known`;
responseCallback(new Error(errorMessage), {});
throw new ResponseHelper.InternalServerError(errorMessage);
throw new InternalServerError(errorMessage);
}
const additionalKeys: IWorkflowDataProxyAdditionalKeys = {
@ -254,7 +257,7 @@ export async function executeWebhook(
try {
user = await Container.get(OwnershipService).getWorkflowOwnerCached(workflowData.id);
} catch (error) {
throw new ResponseHelper.NotFoundError('Cannot find workflow');
throw new NotFoundError('Cannot find workflow');
}
}
@ -294,7 +297,7 @@ export async function executeWebhook(
// that something does not resolve properly.
const errorMessage = `The response mode '${responseMode}' is not valid!`;
responseCallback(new Error(errorMessage), {});
throw new ResponseHelper.InternalServerError(errorMessage);
throw new InternalServerError(errorMessage);
}
// Add the Response and Request so that this data can be accessed in the node
@ -781,13 +784,13 @@ export async function executeWebhook(
responseCallback(new Error('There was a problem executing the workflow'), {});
}
throw new ResponseHelper.InternalServerError(e.message);
throw new InternalServerError(e.message);
});
}
return executionId;
} catch (e) {
const error =
e instanceof ResponseHelper.UnprocessableRequestError
e instanceof UnprocessableRequestError
? e
: new Error('There was a problem executing the workflow', { cause: e });
if (didSendResponse) throw error;

View file

@ -4,11 +4,12 @@ import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants';
import type { JwtPayload, JwtToken } from '@/Interfaces';
import type { User } from '@db/entities/User';
import config from '@/config';
import * as ResponseHelper from '@/ResponseHelper';
import { License } from '@/License';
import { Container } from 'typedi';
import { UserRepository } from '@db/repositories/user.repository';
import { JwtService } from '@/services/jwt.service';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
import { AuthError } from '@/errors/response-errors/auth.error';
export function issueJWT(user: User): JwtToken {
const { id, email, password } = user;
@ -26,7 +27,7 @@ export function issueJWT(user: User): JwtToken {
!user.isOwner &&
!isWithinUsersLimit
) {
throw new ResponseHelper.UnauthorizedError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);
throw new UnauthorizedError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);
}
if (password) {
payload.password = createHash('sha256')
@ -63,7 +64,7 @@ export async function resolveJwtContent(jwtPayload: JwtPayload): Promise<User> {
// currently only LDAP users during synchronization
// can be set to disabled
if (user?.disabled) {
throw new ResponseHelper.AuthError('Unauthorized');
throw new AuthError('Unauthorized');
}
if (!user || jwtPayload.password !== passwordHash || user.email !== jwtPayload.email) {

View file

@ -1,10 +1,10 @@
import type { User } from '@db/entities/User';
import { compareHash } from '@/UserManagement/UserManagementHelper';
import * as ResponseHelper from '@/ResponseHelper';
import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks';
import { isLdapLoginEnabled } from '@/Ldap/helpers';
import { UserRepository } from '@db/repositories/user.repository';
import { AuthError } from '@/errors/response-errors/auth.error';
export const handleEmailLogin = async (
email: string,
@ -27,7 +27,7 @@ export const handleEmailLogin = async (
user_id: user.id,
});
throw new ResponseHelper.AuthError('Reset your password to gain access to the instance.');
throw new AuthError('Reset your password to gain access to the instance.');
}
return undefined;

View file

@ -23,7 +23,7 @@ import { EDITOR_UI_DIST_DIR, LICENSE_FEATURES } from '@/constants';
import { eventBus } from '@/eventbus';
import { BaseCommand } from './BaseCommand';
import { InternalHooks } from '@/InternalHooks';
import { License, FeatureNotLicensedError } from '@/License';
import { License } from '@/License';
import type { IConfig } from '@oclif/config';
import { SingleMainSetup } from '@/services/orchestration/main/SingleMainSetup';
import { OrchestrationHandlerMainService } from '@/services/orchestration/main/orchestration.handler.main.service';
@ -31,6 +31,7 @@ import { PruningService } from '@/services/pruning.service';
import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee';
import { SettingsRepository } from '@db/repositories/settings.repository';
import { ExecutionRepository } from '@db/repositories/execution.repository';
import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error';
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
const open = require('open');

View file

@ -40,6 +40,7 @@ import type { IConfig } from '@oclif/config';
import { OrchestrationHandlerWorkerService } from '@/services/orchestration/worker/orchestration.handler.worker.service';
import { OrchestrationWorkerService } from '@/services/orchestration/worker/orchestration.worker.service';
import type { WorkerJobStatusSummary } from '../services/orchestration/worker/types';
import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error';
export class Worker extends BaseCommand {
static description = '\nStarts a n8n worker';
@ -413,7 +414,7 @@ export class Worker extends BaseCommand {
await connection.query('SELECT 1');
} catch (e) {
this.logger.error('No Database connection!', e as Error);
const error = new ResponseHelper.ServiceUnavailableError('No Database connection!');
const error = new ServiceUnavailableError('No Database connection!');
return ResponseHelper.sendErrorResponse(res, error);
}
@ -424,7 +425,7 @@ export class Worker extends BaseCommand {
await Worker.jobQueue.ping();
} catch (e) {
this.logger.error('No Redis connection!', e as Error);
const error = new ResponseHelper.ServiceUnavailableError('No Redis connection!');
const error = new ServiceUnavailableError('No Redis connection!');
return ResponseHelper.sendErrorResponse(res, error);
}

View file

@ -1,11 +1,6 @@
import { NotStringArrayError } from '@/errors/not-string-array.error';
import type { SchemaObj } from 'convict';
class NotStringArrayError extends Error {
constructor(env: string) {
super(`${env} is not a string array.`);
}
}
export const ensureStringArray = (values: string[], { env }: SchemaObj<string>) => {
if (!env) throw new Error(`Missing env: ${env}`);

View file

@ -2,12 +2,6 @@ import validator from 'validator';
import { In } from 'typeorm';
import { Service } from 'typedi';
import { Authorized, Get, Post, RestController } from '@/decorators';
import {
AuthError,
BadRequestError,
InternalServerError,
UnauthorizedError,
} from '@/ResponseHelper';
import { issueCookie, resolveJwt } from '@/auth/jwt';
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants';
import { Request, Response } from 'express';
@ -27,6 +21,10 @@ import { License } from '@/License';
import { UserService } from '@/services/user.service';
import { MfaService } from '@/Mfa/mfa.service';
import { Logger } from '@/Logger';
import { AuthError } from '@/errors/response-errors/auth.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
@Service()
@RestController()

View file

@ -8,12 +8,13 @@ import {
} from '@/constants';
import { Authorized, Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
import { NodeRequest } from '@/requests';
import { BadRequestError, InternalServerError } from '@/ResponseHelper';
import type { InstalledPackages } from '@db/entities/InstalledPackages';
import type { CommunityPackages } from '@/Interfaces';
import { InternalHooks } from '@/InternalHooks';
import { Push } from '@/push';
import { CommunityPackagesService } from '@/services/communityPackages.service';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
const {
PACKAGE_NOT_INSTALLED,

View file

@ -12,7 +12,7 @@ import { Authorized, Get, Middleware, RestController } from '@/decorators';
import { getBase } from '@/WorkflowExecuteAdditionalData';
import { DynamicNodeParametersService } from '@/services/dynamicNodeParameters.service';
import { DynamicNodeParametersRequest } from '@/requests';
import { BadRequestError } from '@/ResponseHelper';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
const assertMethodName: RequestHandler = (req, res, next) => {
const { methodName } = req.query as DynamicNodeParametersRequest.BaseRequest['query'];

View file

@ -1,7 +1,6 @@
import { In } from 'typeorm';
import Container, { Service } from 'typedi';
import { Authorized, NoAuthRequired, Post, RestController } from '@/decorators';
import { BadRequestError, UnauthorizedError } from '@/ResponseHelper';
import { issueCookie } from '@/auth/jwt';
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
import { Response } from 'express';
@ -16,6 +15,8 @@ import { hashPassword, validatePassword } from '@/UserManagement/UserManagementH
import { PostHogClient } from '@/posthog';
import type { User } from '@/databases/entities/User';
import validator from 'validator';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
@Service()
@RestController('/invitations')

View file

@ -4,9 +4,9 @@ import { getLdapConfig, getLdapSynchronizations, updateLdapConfig } from '@/Ldap
import { LdapService } from '@/Ldap/LdapService.ee';
import { LdapSync } from '@/Ldap/LdapSync.ee';
import { LdapConfiguration } from '@/Ldap/types';
import { BadRequestError } from '@/ResponseHelper';
import { NON_SENSIBLE_LDAP_CONFIG_PROPERTIES } from '@/Ldap/constants';
import { InternalHooks } from '@/InternalHooks';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
@Authorized(['global', 'owner'])
@RestController('/ldap')

View file

@ -5,7 +5,6 @@ import { Service } from 'typedi';
import { randomBytes } from 'crypto';
import { Authorized, Delete, Get, Patch, Post, RestController } from '@/decorators';
import { compareHash, hashPassword, validatePassword } from '@/UserManagement/UserManagementHelper';
import { BadRequestError } from '@/ResponseHelper';
import { validateEntity } from '@/GenericHelpers';
import { issueCookie } from '@/auth/jwt';
import type { User } from '@db/entities/User';
@ -21,6 +20,7 @@ import { UserService } from '@/services/user.service';
import { Logger } from '@/Logger';
import { ExternalHooks } from '@/ExternalHooks';
import { InternalHooks } from '@/InternalHooks';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
@Service()
@Authorized()

View file

@ -1,8 +1,8 @@
import { Service } from 'typedi';
import { Authorized, Delete, Get, Post, RestController } from '@/decorators';
import { AuthenticatedRequest, MFA } from '@/requests';
import { BadRequestError } from '@/ResponseHelper';
import { MfaService } from '@/Mfa/mfa.service';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
@Service()
@Authorized()

View file

@ -9,12 +9,13 @@ import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.
import type { ICredentialsDb } from '@/Interfaces';
import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';
import type { OAuthRequest } from '@/requests';
import { BadRequestError, NotFoundError } from '@/ResponseHelper';
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
import { CredentialsHelper } from '@/CredentialsHelper';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
import { Logger } from '@/Logger';
import { ExternalHooks } from '@/ExternalHooks';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
@Service()
export abstract class AbstractOAuthController {

View file

@ -8,8 +8,10 @@ import { createHmac } from 'crypto';
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
import { Authorized, Get, RestController } from '@/decorators';
import { OAuthRequest } from '@/requests';
import { NotFoundError, sendErrorResponse, ServiceUnavailableError } from '@/ResponseHelper';
import { sendErrorResponse } from '@/ResponseHelper';
import { AbstractOAuthController } from './abstractOAuth.controller';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error';
interface OAuth1CredentialData {
signatureMethod: 'HMAC-SHA256' | 'HMAC-SHA512' | 'HMAC-SHA1';

View file

@ -1,7 +1,6 @@
import validator from 'validator';
import { validateEntity } from '@/GenericHelpers';
import { Authorized, Post, RestController } from '@/decorators';
import { BadRequestError } from '@/ResponseHelper';
import { hashPassword, validatePassword } from '@/UserManagement/UserManagementHelper';
import { issueCookie } from '@/auth/jwt';
import { Response } from 'express';
@ -12,6 +11,7 @@ import { SettingsRepository } from '@db/repositories/settings.repository';
import { PostHogClient } from '@/posthog';
import { UserService } from '@/services/user.service';
import { Logger } from '@/Logger';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
@Authorized(['global', 'owner'])
@RestController('/owner')

View file

@ -5,13 +5,6 @@ import { IsNull, Not } from 'typeorm';
import validator from 'validator';
import { Get, Post, RestController } from '@/decorators';
import {
BadRequestError,
InternalServerError,
NotFoundError,
UnauthorizedError,
UnprocessableRequestError,
} from '@/ResponseHelper';
import {
getInstanceBaseUrl,
hashPassword,
@ -29,6 +22,11 @@ import { MfaService } from '@/Mfa/mfa.service';
import { Logger } from '@/Logger';
import { ExternalHooks } from '@/ExternalHooks';
import { InternalHooks } from '@/InternalHooks';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error';
const throttle = rateLimit({
windowMs: 5 * 60 * 1000, // 5 minutes

View file

@ -2,9 +2,9 @@ import { Request, Response, NextFunction } from 'express';
import config from '@/config';
import { Authorized, Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
import { TagService } from '@/services/tag.service';
import { BadRequestError } from '@/ResponseHelper';
import { TagsRequest } from '@/requests';
import { Service } from 'typedi';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
@Authorized()
@RestController('/tags')

View file

@ -3,9 +3,10 @@ import { ICredentialTypes } from 'n8n-workflow';
import { join } from 'path';
import { access } from 'fs/promises';
import { Authorized, Get, RestController } from '@/decorators';
import { BadRequestError, InternalServerError } from '@/ResponseHelper';
import { Config } from '@/config';
import { NODES_BASE_DIR } from '@/constants';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
export const CREDENTIAL_TRANSLATIONS_DIR = 'n8n-nodes-base/dist/credentials/translations';
export const NODE_HEADERS_PATH = join(NODES_BASE_DIR, 'dist/nodes/headers');

View file

@ -4,7 +4,6 @@ import { User } from '@db/entities/User';
import { SharedCredentials } from '@db/entities/SharedCredentials';
import { SharedWorkflow } from '@db/entities/SharedWorkflow';
import { Authorized, Delete, Get, RestController, Patch } from '@/decorators';
import { BadRequestError, NotFoundError, UnauthorizedError } from '@/ResponseHelper';
import { ListQuery, UserRequest, UserSettingsUpdatePayload } from '@/requests';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import { IExternalHooksClass, IInternalHooksClass } from '@/Interfaces';
@ -17,6 +16,9 @@ import { RoleService } from '@/services/role.service';
import { UserService } from '@/services/user.service';
import { listQueryMiddleware } from '@/middlewares';
import { Logger } from '@/Logger';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
@Authorized()
@RestController('/users')

View file

@ -7,9 +7,9 @@ import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.reposi
import { WorkflowStatisticsRepository } from '@db/repositories/workflowStatistics.repository';
import { ExecutionRequest } from '@/requests';
import { whereClause } from '@/UserManagement/UserManagementHelper';
import { NotFoundError } from '@/ResponseHelper';
import type { IWorkflowStatisticsDataLoaded } from '@/Interfaces';
import { Logger } from '@/Logger';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
interface WorkflowStatisticsData<T> {
productionSuccess: T;

View file

@ -11,6 +11,9 @@ import { OwnershipService } from '@/services/ownership.service';
import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks';
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
export const EECredentialsController = express.Router();
@ -40,7 +43,7 @@ EECredentialsController.get(
)) as CredentialsEntity;
if (!credential) {
throw new ResponseHelper.NotFoundError(
throw new NotFoundError(
'Could not load the credential. If you think this is an error, ask the owner to share it with you again',
);
}
@ -48,7 +51,7 @@ EECredentialsController.get(
const userSharing = credential.shared?.find((shared) => shared.user.id === req.user.id);
if (!userSharing && req.user.globalRole.name !== 'owner') {
throw new ResponseHelper.UnauthorizedError('Forbidden.');
throw new UnauthorizedError('Forbidden.');
}
credential = Container.get(OwnershipService).addOwnedByAndSharedWith(credential);
@ -82,7 +85,7 @@ EECredentialsController.post(
const sharing = await EECredentials.getSharing(req.user, credentialId);
if (!ownsCredential) {
if (!sharing) {
throw new ResponseHelper.UnauthorizedError('Forbidden');
throw new UnauthorizedError('Forbidden');
}
const decryptedData = EECredentials.decrypt(sharing.credentials);
@ -115,12 +118,12 @@ EECredentialsController.put(
!Array.isArray(shareWithIds) ||
!shareWithIds.every((userId) => typeof userId === 'string')
) {
throw new ResponseHelper.BadRequestError('Bad request');
throw new BadRequestError('Bad request');
}
const { ownsCredential, credential } = await EECredentials.isOwned(req.user, credentialId);
if (!ownsCredential || !credential) {
throw new ResponseHelper.UnauthorizedError('Forbidden');
throw new UnauthorizedError('Forbidden');
}
let amountRemoved: number | null = null;

View file

@ -14,6 +14,7 @@ import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks';
import { listQueryMiddleware } from '@/middlewares';
import { Logger } from '@/Logger';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
export const credentialsController = express.Router();
credentialsController.use('/', EECredentialsController);
@ -60,9 +61,7 @@ credentialsController.get(
const sharing = await CredentialsService.getSharing(req.user, credentialId, ['credentials']);
if (!sharing) {
throw new ResponseHelper.NotFoundError(
`Credential with ID "${credentialId}" could not be found.`,
);
throw new NotFoundError(`Credential with ID "${credentialId}" could not be found.`);
}
const { credentials: credential } = sharing;
@ -145,7 +144,7 @@ credentialsController.patch(
userId: req.user.id,
},
);
throw new ResponseHelper.NotFoundError(
throw new NotFoundError(
'Credential to be updated not found. You can only update credentials owned by you',
);
}
@ -165,9 +164,7 @@ credentialsController.patch(
const responseData = await CredentialsService.update(credentialId, newCredentialData);
if (responseData === null) {
throw new ResponseHelper.NotFoundError(
`Credential ID "${credentialId}" could not be found to be updated.`,
);
throw new NotFoundError(`Credential ID "${credentialId}" could not be found to be updated.`);
}
// Remove the encrypted data as it is not needed in the frontend
@ -197,7 +194,7 @@ credentialsController.delete(
userId: req.user.id,
},
);
throw new ResponseHelper.NotFoundError(
throw new NotFoundError(
'Credential to be deleted not found. You can only removed credentials owned by you',
);
}

View file

@ -12,11 +12,11 @@ import { SourceControlPreferencesService } from './sourceControlPreferences.serv
import type { SourceControlPreferences } from './types/sourceControlPreferences';
import type { SourceControlledFile } from './types/sourceControlledFile';
import { SOURCE_CONTROL_API_ROOT, SOURCE_CONTROL_DEFAULT_BRANCH } from './constants';
import { BadRequestError } from '@/ResponseHelper';
import type { ImportResult } from './types/importResult';
import { InternalHooks } from '../../InternalHooks';
import { getRepoType } from './sourceControlHelper.ee';
import { SourceControlGetStatus } from './types/sourceControlGetStatus';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
@Service()
@RestController(`/${SOURCE_CONTROL_API_ROOT}`)

View file

@ -17,7 +17,6 @@ import {
import { SourceControlGitService } from './sourceControlGit.service.ee';
import type { PushResult } from 'simple-git';
import { SourceControlExportService } from './sourceControlExport.service.ee';
import { BadRequestError } from '@/ResponseHelper';
import type { ImportResult } from './types/importResult';
import type { SourceControlPushWorkFolder } from './types/sourceControlPushWorkFolder';
import type { SourceControllPullOptions } from './types/sourceControlPullWorkFolder';
@ -35,6 +34,7 @@ import type { ExportableCredential } from './types/exportableCredential';
import { InternalHooks } from '@/InternalHooks';
import { TagRepository } from '@db/repositories/tag.repository';
import { Logger } from '@/Logger';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
@Service()
export class SourceControlService {

View file

@ -1,14 +1,14 @@
import { Container, Service } from 'typedi';
import * as ResponseHelper from '@/ResponseHelper';
import { VariablesRequest } from '@/requests';
import { Authorized, Delete, Get, Licensed, Patch, Post, RestController } from '@/decorators';
import {
VariablesService,
VariablesLicenseError,
VariablesValidationError,
} from './variables.service.ee';
import { VariablesService } from './variables.service.ee';
import { Logger } from '@/Logger';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { VariableValidationError } from '@/errors/variable-validation.error';
import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error';
@Service()
@Authorized()
@ -31,17 +31,17 @@ export class VariablesController {
this.logger.info('Attempt to update a variable blocked due to lack of permissions', {
userId: req.user.id,
});
throw new ResponseHelper.UnauthorizedError('Unauthorized');
throw new UnauthorizedError('Unauthorized');
}
const variable = req.body;
delete variable.id;
try {
return await Container.get(VariablesService).create(variable);
} catch (error) {
if (error instanceof VariablesLicenseError) {
throw new ResponseHelper.BadRequestError(error.message);
} else if (error instanceof VariablesValidationError) {
throw new ResponseHelper.BadRequestError(error.message);
if (error instanceof VariableCountLimitReachedError) {
throw new BadRequestError(error.message);
} else if (error instanceof VariableValidationError) {
throw new BadRequestError(error.message);
}
throw error;
}
@ -52,7 +52,7 @@ export class VariablesController {
const id = req.params.id;
const variable = await Container.get(VariablesService).getCached(id);
if (variable === null) {
throw new ResponseHelper.NotFoundError(`Variable with id ${req.params.id} not found`);
throw new NotFoundError(`Variable with id ${req.params.id} not found`);
}
return variable;
}
@ -66,17 +66,17 @@ export class VariablesController {
id,
userId: req.user.id,
});
throw new ResponseHelper.UnauthorizedError('Unauthorized');
throw new UnauthorizedError('Unauthorized');
}
const variable = req.body;
delete variable.id;
try {
return await Container.get(VariablesService).update(id, variable);
} catch (error) {
if (error instanceof VariablesLicenseError) {
throw new ResponseHelper.BadRequestError(error.message);
} else if (error instanceof VariablesValidationError) {
throw new ResponseHelper.BadRequestError(error.message);
if (error instanceof VariableCountLimitReachedError) {
throw new BadRequestError(error.message);
} else if (error instanceof VariableValidationError) {
throw new BadRequestError(error.message);
}
throw error;
}
@ -90,7 +90,7 @@ export class VariablesController {
id,
userId: req.user.id,
});
throw new ResponseHelper.UnauthorizedError('Unauthorized');
throw new UnauthorizedError('Unauthorized');
}
await this.variablesService.delete(id);

View file

@ -6,9 +6,8 @@ import { canCreateNewVariable } from './enviromentHelpers';
import { CacheService } from '@/services/cache.service';
import { VariablesRepository } from '@db/repositories/variables.repository';
import type { DeepPartial } from 'typeorm';
export class VariablesLicenseError extends Error {}
export class VariablesValidationError extends Error {}
import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error';
import { VariableValidationError } from '@/errors/variable-validation.error';
@Service()
export class VariablesService {
@ -59,19 +58,19 @@ export class VariablesService {
validateVariable(variable: Omit<Variables, 'id'>): void {
if (variable.key.length > 50) {
throw new VariablesValidationError('key cannot be longer than 50 characters');
throw new VariableValidationError('key cannot be longer than 50 characters');
}
if (variable.key.replace(/[A-Za-z0-9_]/g, '').length !== 0) {
throw new VariablesValidationError('key can only contain characters A-Za-z0-9_');
throw new VariableValidationError('key can only contain characters A-Za-z0-9_');
}
if (variable.value?.length > 255) {
throw new VariablesValidationError('value cannot be longer than 255 characters');
throw new VariableValidationError('value cannot be longer than 255 characters');
}
}
async create(variable: Omit<Variables, 'id'>): Promise<Variables> {
if (!canCreateNewVariable(await this.getCount())) {
throw new VariablesLicenseError('Variables limit reached');
throw new VariableCountLimitReachedError('Variables limit reached');
}
this.validateVariable(variable);

View file

@ -0,0 +1,10 @@
import { ApplicationError, type Severity } from 'n8n-workflow';
export class CredentialNotFoundError extends ApplicationError {
severity: Severity;
constructor(credentialId: string, credentialType: string) {
super(`Credential with ID "${credentialId}" does not exist for type "${credentialType}".`);
this.severity = 'warning';
}
}

View file

@ -0,0 +1,7 @@
import { ApplicationError } from 'n8n-workflow';
export class ExternalSecretsProviderNotFoundError extends ApplicationError {
constructor(public providerName: string) {
super(`External secrets provider not found: ${providerName}`);
}
}

View file

@ -0,0 +1,10 @@
import type { LICENSE_FEATURES } from '@/constants';
import { ApplicationError } from 'n8n-workflow';
export class FeatureNotLicensedError extends ApplicationError {
constructor(feature: (typeof LICENSE_FEATURES)[keyof typeof LICENSE_FEATURES]) {
super(
`Your license does not allow for ${feature}. To enable ${feature}, please upgrade to a license that supports this feature.`,
);
}
}

View file

@ -0,0 +1,3 @@
import { ApplicationError } from 'n8n-workflow';
export class InvalidRoleError extends ApplicationError {}

View file

@ -0,0 +1,7 @@
import { ApplicationError } from 'n8n-workflow';
export class NotStringArrayError extends ApplicationError {
constructor(env: string) {
super(`${env} is not a string array.`);
}
}

View file

@ -0,0 +1,23 @@
import { ApplicationError } from 'n8n-workflow';
/**
* Special Error which allows to return also an error code and http status code
*/
export abstract class ResponseError extends ApplicationError {
/**
* Creates an instance of ResponseError.
* Must be used inside a block with `ResponseHelper.send()`.
*/
constructor(
message: string,
// The HTTP status code of response
readonly httpStatusCode: number,
// The error code in the response
readonly errorCode: number = httpStatusCode,
// The error hint the response
readonly hint: string | undefined = undefined,
) {
super(message);
this.name = 'ResponseError';
}
}

View file

@ -0,0 +1,7 @@
import { ResponseError } from './abstract/response.error';
export class AuthError extends ResponseError {
constructor(message: string, errorCode?: number) {
super(message, 401, errorCode);
}
}

View file

@ -0,0 +1,7 @@
import { ResponseError } from './abstract/response.error';
export class BadRequestError extends ResponseError {
constructor(message: string, errorCode?: number) {
super(message, 400, errorCode);
}
}

View file

@ -0,0 +1,7 @@
import { ResponseError } from './abstract/response.error';
export class ConflictError extends ResponseError {
constructor(message: string, hint: string | undefined = undefined) {
super(message, 409, 409, hint);
}
}

View file

@ -0,0 +1,7 @@
import { ResponseError } from './abstract/response.error';
export class InternalServerError extends ResponseError {
constructor(message: string, errorCode = 500) {
super(message, 500, errorCode);
}
}

View file

@ -0,0 +1,7 @@
import { ResponseError } from './abstract/response.error';
export class NotFoundError extends ResponseError {
constructor(message: string, hint: string | undefined = undefined) {
super(message, 404, 404, hint);
}
}

View file

@ -0,0 +1,7 @@
import { ResponseError } from './abstract/response.error';
export class ServiceUnavailableError extends ResponseError {
constructor(message: string, errorCode = 503) {
super(message, 503, errorCode);
}
}

View file

@ -0,0 +1,7 @@
import { ResponseError } from './abstract/response.error';
export class UnauthorizedError extends ResponseError {
constructor(message: string, hint: string | undefined = undefined) {
super(message, 403, 403, hint);
}
}

View file

@ -0,0 +1,7 @@
import { ResponseError } from './abstract/response.error';
export class UnprocessableRequestError extends ResponseError {
constructor(message: string, hint: string | undefined = undefined) {
super(message, 422, 422, hint);
}
}

View file

@ -0,0 +1,3 @@
import { ApplicationError } from 'n8n-workflow';
export class SharedWorkflowNotFoundError extends ApplicationError {}

View file

@ -0,0 +1,9 @@
import { ApplicationError } from 'n8n-workflow';
export class UnrecognizedNodeTypeError extends ApplicationError {
severity = 'warning';
constructor(nodeType: string) {
super(`Unrecognized node type: ${nodeType}".`);
}
}

View file

@ -0,0 +1,3 @@
import { ApplicationError } from 'n8n-workflow';
export class VariableCountLimitReachedError extends ApplicationError {}

View file

@ -0,0 +1,3 @@
import { ApplicationError } from 'n8n-workflow';
export class VariableValidationError extends ApplicationError {}

View file

@ -0,0 +1,3 @@
import { ApplicationError } from 'n8n-workflow';
export class WorkflowHistoryVersionNotFoundError extends ApplicationError {}

View file

@ -9,7 +9,6 @@ import {
MessageEventBusDestinationSyslog,
} from './MessageEventBusDestination/MessageEventBusDestinationSyslog.ee';
import { MessageEventBusDestinationWebhook } from './MessageEventBusDestination/MessageEventBusDestinationWebhook.ee';
import { BadRequestError } from '@/ResponseHelper';
import type {
MessageEventBusDestinationWebhookOptions,
MessageEventBusDestinationOptions,
@ -20,6 +19,7 @@ import type { MessageEventBusDestination } from './MessageEventBusDestination/Me
import type { DeleteResult } from 'typeorm';
import { AuthenticatedRequest } from '@/requests';
import { logStreamingLicensedMiddleware } from './middleware/logStreamingEnabled.middleware.ee';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
// ----------------------------------------
// TypeGuards

View file

@ -9,13 +9,13 @@ import type { EventMessageTypes, FailedEventSummary } from './EventMessageClasse
import { eventNamesAll } from './EventMessageClasses';
import type { EventMessageAuditOptions } from './EventMessageClasses/EventMessageAudit';
import { EventMessageAudit } from './EventMessageClasses/EventMessageAudit';
import { BadRequestError } from '@/ResponseHelper';
import type { IRunExecutionData } from 'n8n-workflow';
import { EventMessageTypeNames } from 'n8n-workflow';
import type { EventMessageNodeOptions } from './EventMessageClasses/EventMessageNode';
import { EventMessageNode } from './EventMessageClasses/EventMessageNode';
import { recoverExecutionDataFromEventLogMessages } from './MessageEventBus/recoverEvents';
import { RestController, Get, Post, Authorized } from '@/decorators';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
// ----------------------------------------
// TypeGuards

View file

@ -15,7 +15,6 @@ import type {
import { NodeTypes } from '@/NodeTypes';
import { Queue } from '@/Queue';
import type { ExecutionRequest } from '@/requests';
import * as ResponseHelper from '@/ResponseHelper';
import { getSharedWorkflowIds } from '@/WorkflowHelpers';
import { WorkflowRunner } from '@/WorkflowRunner';
import * as GenericHelpers from '@/GenericHelpers';
@ -24,6 +23,8 @@ import { getStatusUsingPreviousExecutionStatusMethod } from './executionHelpers'
import { ExecutionRepository } from '@db/repositories/execution.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { Logger } from '@/Logger';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
export interface IGetExecutionsQueryFilter {
id?: FindOperator<string> | string;
@ -114,9 +115,7 @@ export class ExecutionsService {
userId: req.user.id,
filter: req.query.filter,
});
throw new ResponseHelper.InternalServerError(
'Parameter "filter" contained invalid JSON string.',
);
throw new InternalServerError('Parameter "filter" contained invalid JSON string.');
}
}
@ -231,9 +230,7 @@ export class ExecutionsService {
executionId,
},
);
throw new ResponseHelper.NotFoundError(
`The execution with the ID "${executionId}" does not exist.`,
);
throw new NotFoundError(`The execution with the ID "${executionId}" does not exist.`);
}
if (execution.finished) {
@ -351,9 +348,7 @@ export class ExecutionsService {
requestFilters = requestFiltersRaw as IGetExecutionsQueryFilter;
}
} catch (error) {
throw new ResponseHelper.InternalServerError(
'Parameter "filter" contained invalid JSON string.',
);
throw new InternalServerError('Parameter "filter" contained invalid JSON string.');
}
}

View file

@ -8,6 +8,8 @@ import { LicenseService } from './License.service';
import { License } from '@/License';
import type { AuthenticatedRequest, LicenseRequest } from '@/requests';
import { InternalHooks } from '@/InternalHooks';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
export const licenseController = express.Router();
@ -24,9 +26,7 @@ licenseController.use((req: AuthenticatedRequest, res, next) => {
});
ResponseHelper.sendErrorResponse(
res,
new ResponseHelper.UnauthorizedError(
'Only an instance owner may activate or renew a license',
),
new UnauthorizedError('Only an instance owner may activate or renew a license'),
);
return;
}
@ -85,7 +85,7 @@ licenseController.post(
Container.get(Logger).error(message, { stack: error.stack ?? 'n/a' });
}
throw new ResponseHelper.BadRequestError(message);
throw new BadRequestError(message);
}
// Return the read data, plus the management JWT
@ -113,7 +113,7 @@ licenseController.post(
// not awaiting so as not to make the endpoint hang
void Container.get(InternalHooks).onLicenseRenewAttempt({ success: false });
if (error instanceof Error) {
throw new ResponseHelper.BadRequestError(error.message);
throw new BadRequestError(error.message);
}
}

View file

@ -7,7 +7,7 @@ import { Parser as XmlParser } from 'xml2js';
import { parseIncomingMessage } from 'n8n-core';
import { jsonParse } from 'n8n-workflow';
import config from '@/config';
import { UnprocessableRequestError } from '@/ResponseHelper';
import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error';
const xmlParser = new XmlParser({
async: true,

View file

@ -3,8 +3,7 @@ import { RoleRepository } from '@db/repositories/role.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { CacheService } from './cache.service';
import type { RoleNames, RoleScopes } from '@db/entities/Role';
class InvalidRoleError extends Error {}
import { InvalidRoleError } from '@/errors/invalid-role.error';
@Service()
export class RoleService {

View file

@ -15,8 +15,8 @@ import { UserManagementMailer } from '@/UserManagement/email';
import { InternalHooks } from '@/InternalHooks';
import { RoleService } from '@/services/role.service';
import { ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
import { InternalServerError } from '@/ResponseHelper';
import type { UserRequest } from '@/requests';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
@Service()
export class UserService {

View file

@ -9,7 +9,6 @@ import {
} from '../middleware/samlEnabledMiddleware';
import { SamlService } from '../saml.service.ee';
import { SamlConfiguration } from '../types/requests';
import { AuthError, BadRequestError } from '@/ResponseHelper';
import { getInitSSOFormView } from '../views/initSsoPost';
import { issueCookie } from '@/auth/jwt';
import { validate } from 'class-validator';
@ -27,6 +26,8 @@ import { getSamlConnectionTestFailedView } from '../views/samlConnectionTestFail
import { InternalHooks } from '@/InternalHooks';
import url from 'url';
import querystring from 'querystring';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { AuthError } from '@/errors/response-errors/auth.error';
@Service()
@RestController('/sso/saml')

View file

@ -2,7 +2,6 @@ import type express from 'express';
import Container, { Service } from 'typedi';
import type { User } from '@db/entities/User';
import { jsonParse } from 'n8n-workflow';
import { AuthError, BadRequestError } from '@/ResponseHelper';
import { getServiceProviderInstance } from './serviceProvider.ee';
import type { SamlUserAttributes } from './types/samlUserAttributes';
import { isSsoJustInTimeProvisioningEnabled } from '../ssoHelpers';
@ -29,6 +28,8 @@ import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';
import { Logger } from '@/Logger';
import { UserRepository } from '@db/repositories/user.repository';
import { SettingsRepository } from '@db/repositories/settings.repository';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { AuthError } from '@/errors/response-errors/auth.error';
@Service()
export class SamlService {

View file

@ -3,7 +3,6 @@ import config from '@/config';
import { AuthIdentity } from '@db/entities/AuthIdentity';
import { User } from '@db/entities/User';
import { License } from '@/License';
import { AuthError, InternalServerError } from '@/ResponseHelper';
import { hashPassword } from '@/UserManagement/UserManagementHelper';
import type { SamlPreferences } from './types/samlPreferences';
import type { SamlUserAttributes } from './types/samlUserAttributes';
@ -21,6 +20,9 @@ import type { SamlConfiguration } from './types/requests';
import { RoleService } from '@/services/role.service';
import { UserRepository } from '@db/repositories/user.repository';
import { AuthIdentityRepository } from '@db/repositories/authIdentity.repository';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { AuthError } from '@/errors/response-errors/auth.error';
/**
* Check whether the SAML feature is licensed and enabled in the instance
*/

View file

@ -1,15 +1,14 @@
import { Authorized, RestController, Get, Middleware } from '@/decorators';
import { WorkflowHistoryRequest } from '@/requests';
import { Service } from 'typedi';
import {
HistoryVersionNotFoundError,
SharedWorkflowNotFoundError,
WorkflowHistoryService,
} from './workflowHistory.service.ee';
import { WorkflowHistoryService } from './workflowHistory.service.ee';
import { Request, Response, NextFunction } from 'express';
import { isWorkflowHistoryEnabled, isWorkflowHistoryLicensed } from './workflowHistoryHelper.ee';
import { NotFoundError } from '@/ResponseHelper';
import { paginationListQueryMiddleware } from '@/middlewares/listQuery/pagination';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { SharedWorkflowNotFoundError } from '@/errors/shared-workflow-not-found.error';
import { WorkflowHistoryVersionNotFoundError } from '@/errors/workflow-history-version-not-found.error';
const DEFAULT_TAKE = 20;
@ -67,7 +66,7 @@ export class WorkflowHistoryController {
} catch (e) {
if (e instanceof SharedWorkflowNotFoundError) {
throw new NotFoundError('Could not find workflow');
} else if (e instanceof HistoryVersionNotFoundError) {
} else if (e instanceof WorkflowHistoryVersionNotFoundError) {
throw new NotFoundError('Could not find version');
}
throw e;

View file

@ -7,9 +7,8 @@ import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repo
import { Service } from 'typedi';
import { isWorkflowHistoryEnabled } from './workflowHistoryHelper.ee';
import { Logger } from '@/Logger';
export class SharedWorkflowNotFoundError extends Error {}
export class HistoryVersionNotFoundError extends Error {}
import { SharedWorkflowNotFoundError } from '@/errors/shared-workflow-not-found.error';
import { WorkflowHistoryVersionNotFoundError } from '@/errors/workflow-history-version-not-found.error';
@Service()
export class WorkflowHistoryService {
@ -36,7 +35,7 @@ export class WorkflowHistoryService {
): Promise<Array<Omit<WorkflowHistory, 'nodes' | 'connections'>>> {
const sharedWorkflow = await this.getSharedWorkflow(user, workflowId);
if (!sharedWorkflow) {
throw new SharedWorkflowNotFoundError();
throw new SharedWorkflowNotFoundError('');
}
return this.workflowHistoryRepository.find({
where: {
@ -52,7 +51,7 @@ export class WorkflowHistoryService {
async getVersion(user: User, workflowId: string, versionId: string): Promise<WorkflowHistory> {
const sharedWorkflow = await this.getSharedWorkflow(user, workflowId);
if (!sharedWorkflow) {
throw new SharedWorkflowNotFoundError();
throw new SharedWorkflowNotFoundError('');
}
const hist = await this.workflowHistoryRepository.findOne({
where: {
@ -61,7 +60,7 @@ export class WorkflowHistoryService {
},
});
if (!hist) {
throw new HistoryVersionNotFoundError();
throw new WorkflowHistoryVersionNotFoundError('');
}
return hist;
}

View file

@ -23,6 +23,10 @@ import { listQueryMiddleware } from '@/middlewares';
import { TagService } from '@/services/tag.service';
import { Logger } from '@/Logger';
import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
export const EEWorkflowController = express.Router();
@ -52,13 +56,13 @@ EEWorkflowController.put(
!Array.isArray(shareWithIds) ||
!shareWithIds.every((userId) => typeof userId === 'string')
) {
throw new ResponseHelper.BadRequestError('Bad request');
throw new BadRequestError('Bad request');
}
const { ownsWorkflow, workflow } = await EEWorkflows.isOwned(req.user, workflowId);
if (!ownsWorkflow || !workflow) {
throw new ResponseHelper.UnauthorizedError('Forbidden');
throw new UnauthorizedError('Forbidden');
}
let newShareeIds: string[] = [];
@ -101,13 +105,13 @@ EEWorkflowController.get(
const workflow = await EEWorkflows.get({ id: workflowId }, { relations });
if (!workflow) {
throw new ResponseHelper.NotFoundError(`Workflow with ID "${workflowId}" does not exist`);
throw new NotFoundError(`Workflow with ID "${workflowId}" does not exist`);
}
const userSharing = workflow.shared?.find((shared) => shared.user.id === req.user.id);
if (!userSharing && req.user.globalRole.name !== 'owner') {
throw new ResponseHelper.UnauthorizedError(
throw new UnauthorizedError(
'You do not have permission to access this workflow. Ask the owner to share it with you',
);
}
@ -156,7 +160,7 @@ EEWorkflowController.post(
try {
EEWorkflows.validateCredentialPermissionsToUser(newWorkflow, allCredentials);
} catch (error) {
throw new ResponseHelper.BadRequestError(
throw new BadRequestError(
'The workflow you are trying to save contains credentials that are not shared with you',
);
}
@ -181,7 +185,7 @@ EEWorkflowController.post(
if (!savedWorkflow) {
Container.get(Logger).error('Failed to create workflow', { userId: req.user.id });
throw new ResponseHelper.InternalServerError(
throw new InternalServerError(
'An error occurred while saving your workflow. Please try again.',
);
}

View file

@ -27,6 +27,9 @@ import { TagService } from '@/services/tag.service';
import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee';
import { Logger } from '@/Logger';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
export const workflowsController = express.Router();
workflowsController.use('/', EEWorkflowController);
@ -84,7 +87,7 @@ workflowsController.post(
if (!savedWorkflow) {
Container.get(Logger).error('Failed to create workflow', { userId: req.user.id });
throw new ResponseHelper.InternalServerError('Failed to save workflow');
throw new InternalServerError('Failed to save workflow');
}
await Container.get(WorkflowHistoryService).saveVersion(
@ -160,10 +163,10 @@ workflowsController.get(
'/from-url',
ResponseHelper.send(async (req: express.Request): Promise<IWorkflowResponse> => {
if (req.query.url === undefined) {
throw new ResponseHelper.BadRequestError('The parameter "url" is missing!');
throw new BadRequestError('The parameter "url" is missing!');
}
if (!/^http[s]?:\/\/.*\.json$/i.exec(req.query.url as string)) {
throw new ResponseHelper.BadRequestError(
throw new BadRequestError(
'The parameter "url" is not valid! It does not seem to be a URL pointing to a n8n workflow JSON file.',
);
}
@ -172,7 +175,7 @@ workflowsController.get(
const { data } = await axios.get<IWorkflowResponse>(req.query.url as string);
workflowData = data;
} catch (error) {
throw new ResponseHelper.BadRequestError('The URL does not point to valid JSON file!');
throw new BadRequestError('The URL does not point to valid JSON file!');
}
// Do a very basic check if it is really a n8n-workflow-json
@ -183,7 +186,7 @@ workflowsController.get(
typeof workflowData.connections !== 'object' ||
Array.isArray(workflowData.connections)
) {
throw new ResponseHelper.BadRequestError(
throw new BadRequestError(
'The data in the file does not seem to be a n8n workflow JSON file!',
);
}
@ -221,7 +224,7 @@ workflowsController.get(
workflowId,
userId: req.user.id,
});
throw new ResponseHelper.NotFoundError(
throw new NotFoundError(
'Could not load the workflow - you can only access workflows owned by you',
);
}
@ -271,7 +274,7 @@ workflowsController.delete(
workflowId,
userId: req.user.id,
});
throw new ResponseHelper.BadRequestError(
throw new BadRequestError(
'Could not delete the workflow - you can only remove workflows owned by you',
);
}

View file

@ -1,6 +1,5 @@
import type { DeleteResult, EntityManager } from 'typeorm';
import { In, Not } from 'typeorm';
import * as ResponseHelper from '@/ResponseHelper';
import * as WorkflowHelpers from '@/WorkflowHelpers';
import { SharedWorkflow } from '@db/entities/SharedWorkflow';
import type { User } from '@db/entities/User';
@ -17,6 +16,8 @@ import { RoleService } from '@/services/role.service';
import Container from 'typedi';
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
export class EEWorkflowsService extends WorkflowsService {
static async isOwned(
@ -170,7 +171,7 @@ export class EEWorkflowsService extends WorkflowsService {
const previousVersion = await EEWorkflowsService.get({ id: workflowId });
if (!previousVersion) {
throw new ResponseHelper.NotFoundError('Workflow not found');
throw new NotFoundError('Workflow not found');
}
const allCredentials = await CredentialsService.getMany(user);
@ -183,9 +184,9 @@ export class EEWorkflowsService extends WorkflowsService {
);
} catch (error) {
if (error instanceof NodeOperationError) {
throw new ResponseHelper.BadRequestError(error.message);
throw new BadRequestError(error.message);
}
throw new ResponseHelper.BadRequestError(
throw new BadRequestError(
'Invalid workflow credentials - make sure you have access to all credentials and try again.',
);
}

View file

@ -6,7 +6,6 @@ import { In, Like } from 'typeorm';
import pick from 'lodash/pick';
import { v4 as uuid } from 'uuid';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import * as ResponseHelper from '@/ResponseHelper';
import * as WorkflowHelpers from '@/WorkflowHelpers';
import config from '@/config';
import type { SharedWorkflow } from '@db/entities/SharedWorkflow';
@ -34,6 +33,8 @@ import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee'
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { WorkflowTagMappingRepository } from '@db/repositories/workflowTagMapping.repository';
import { ExecutionRepository } from '@db/repositories/execution.repository';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
export class WorkflowsService {
static async getSharing(
@ -208,7 +209,7 @@ export class WorkflowsService {
workflowId,
userId: user.id,
});
throw new ResponseHelper.NotFoundError(
throw new NotFoundError(
'You do not have permission to update this workflow. Ask the owner to share it with you.',
);
}
@ -220,7 +221,7 @@ export class WorkflowsService {
workflow.versionId !== '' &&
workflow.versionId !== shared.workflow.versionId
) {
throw new ResponseHelper.BadRequestError(
throw new BadRequestError(
'Your most recent changes may be lost, because someone else just updated this workflow. Open this workflow in a new tab to see those new updates.',
100,
);
@ -330,7 +331,7 @@ export class WorkflowsService {
});
if (updatedWorkflow === null) {
throw new ResponseHelper.BadRequestError(
throw new BadRequestError(
`Workflow with ID "${workflowId}" could not be found to be updated.`,
);
}
@ -368,7 +369,7 @@ export class WorkflowsService {
message = message ?? (error as Error).message;
// Now return the original error for UI to display
throw new ResponseHelper.BadRequestError(message);
throw new BadRequestError(message);
}
}

View file

@ -6,7 +6,6 @@ import type { PublicUser } from '@/Interfaces';
import type { User } from '@db/entities/User';
import { MeController } from '@/controllers/me.controller';
import { AUTH_COOKIE_NAME } from '@/constants';
import { BadRequestError } from '@/ResponseHelper';
import type { AuthenticatedRequest, MeRequest } from '@/requests';
import { UserService } from '@/services/user.service';
import { ExternalHooks } from '@/ExternalHooks';
@ -14,6 +13,7 @@ import { InternalHooks } from '@/InternalHooks';
import { License } from '@/License';
import { badPasswords } from '../shared/testData';
import { mockInstance } from '../../shared/mocking';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
describe('MeController', () => {
const externalHooks = mockInstance(ExternalHooks);

View file

@ -7,7 +7,6 @@ import { OAuth1CredentialController } from '@/controllers/oauth/oAuth1Credential
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import type { User } from '@db/entities/User';
import type { OAuthRequest } from '@/requests';
import { BadRequestError, NotFoundError } from '@/ResponseHelper';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { ExternalHooks } from '@/ExternalHooks';
@ -17,6 +16,8 @@ import { SecretsHelper } from '@/SecretsHelpers';
import { CredentialsHelper } from '@/CredentialsHelper';
import { mockInstance } from '../../shared/mocking';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
describe('OAuth1CredentialController', () => {
mockInstance(Logger);

View file

@ -9,7 +9,6 @@ import { OAuth2CredentialController } from '@/controllers/oauth/oAuth2Credential
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import type { User } from '@db/entities/User';
import type { OAuthRequest } from '@/requests';
import { BadRequestError, NotFoundError } from '@/ResponseHelper';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { ExternalHooks } from '@/ExternalHooks';
@ -19,6 +18,8 @@ import { SecretsHelper } from '@/SecretsHelpers';
import { CredentialsHelper } from '@/CredentialsHelper';
import { mockInstance } from '../../shared/mocking';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
describe('OAuth2CredentialController', () => {
mockInstance(Logger);

View file

@ -5,7 +5,6 @@ import type { IInternalHooksClass } from '@/Interfaces';
import type { User } from '@db/entities/User';
import type { SettingsRepository } from '@db/repositories/settings.repository';
import type { Config } from '@/config';
import { BadRequestError } from '@/ResponseHelper';
import type { OwnerRequest } from '@/requests';
import { OwnerController } from '@/controllers/owner.controller';
import { AUTH_COOKIE_NAME } from '@/constants';
@ -14,6 +13,7 @@ import { License } from '@/License';
import { mockInstance } from '../../shared/mocking';
import { badPasswords } from '../shared/testData';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
describe('OwnerController', () => {
const config = mock<Config>();

View file

@ -6,7 +6,7 @@ import {
TranslationController,
CREDENTIAL_TRANSLATIONS_DIR,
} from '@/controllers/translation.controller';
import { BadRequestError } from '@/ResponseHelper';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
describe('TranslationController', () => {
const config = mock<Config>();