refactor: Improve error logging/reporting for cli (#4691)

* use response error classes instead of `ResponseError` everywhere

* improve error logging in dev mode or when telemetry is disabled
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2022-11-22 14:00:36 +01:00 committed by GitHub
parent 5364e7fc92
commit 0b754a4f85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 253 additions and 393 deletions

View file

@ -161,6 +161,7 @@
"passport": "^0.6.0", "passport": "^0.6.0",
"passport-cookie": "^1.0.9", "passport-cookie": "^1.0.9",
"passport-jwt": "^4.0.0", "passport-jwt": "^4.0.0",
"picocolors": "^1.0.0",
"pg": "^8.3.0", "pg": "^8.3.0",
"posthog-node": "^1.3.0", "posthog-node": "^1.3.0",
"prom-client": "^13.1.0", "prom-client": "^13.1.0",

View file

@ -191,10 +191,8 @@ export class ActiveWorkflowRunner {
): Promise<IResponseCallbackData> { ): Promise<IResponseCallbackData> {
Logger.debug(`Received webhook "${httpMethod}" for path "${path}"`); Logger.debug(`Received webhook "${httpMethod}" for path "${path}"`);
if (this.activeWorkflows === null) { if (this.activeWorkflows === null) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
'The "activeWorkflows" instance did not get initialized yet.', 'The "activeWorkflows" instance did not get initialized yet.',
404,
404,
); );
} }
@ -224,10 +222,8 @@ export class ActiveWorkflowRunner {
}); });
if (dynamicWebhooks === undefined || dynamicWebhooks.length === 0) { if (dynamicWebhooks === undefined || dynamicWebhooks.length === 0) {
// The requested webhook is not registered // The requested webhook is not registered
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
`The requested webhook "${httpMethod} ${path}" is not registered.`, `The requested webhook "${httpMethod} ${path}" is not registered.`,
404,
404,
WEBHOOK_PROD_UNREGISTERED_HINT, WEBHOOK_PROD_UNREGISTERED_HINT,
); );
} }
@ -252,10 +248,8 @@ export class ActiveWorkflowRunner {
} }
}); });
if (webhook === undefined) { if (webhook === undefined) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
`The requested webhook "${httpMethod} ${path}" is not registered.`, `The requested webhook "${httpMethod} ${path}" is not registered.`,
404,
404,
WEBHOOK_PROD_UNREGISTERED_HINT, WEBHOOK_PROD_UNREGISTERED_HINT,
); );
} }
@ -277,10 +271,8 @@ export class ActiveWorkflowRunner {
relations: ['shared', 'shared.user', 'shared.user.globalRole'], relations: ['shared', 'shared.user', 'shared.user.globalRole'],
}); });
if (workflowData === undefined) { if (workflowData === undefined) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
`Could not find workflow with id "${webhook.workflowId}"`, `Could not find workflow with id "${webhook.workflowId}"`,
404,
404,
); );
} }
@ -313,7 +305,7 @@ export class ActiveWorkflowRunner {
const workflowStartNode = workflow.getNode(webhookData.node); const workflowStartNode = workflow.getNode(webhookData.node);
if (workflowStartNode === null) { if (workflowStartNode === null) {
throw new ResponseHelper.ResponseError('Could not find node to process webhook.', 404, 404); throw new ResponseHelper.NotFoundError('Could not find node to process webhook.');
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View file

@ -212,7 +212,7 @@ export async function validateEntity(
.join(' | '); .join(' | ');
if (errorMessages) { if (errorMessages) {
throw new ResponseHelper.ResponseError(errorMessages, undefined, 400); throw new ResponseHelper.BadRequestError(errorMessages);
} }
} }

View file

@ -5,7 +5,8 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { parse, stringify } from 'flatted'; import { parse, stringify } from 'flatted';
import { ErrorReporterProxy as ErrorReporter } from 'n8n-workflow'; import picocolors from 'picocolors';
import { ErrorReporterProxy as ErrorReporter, NodeApiError } from 'n8n-workflow';
import type { import type {
IExecutionDb, IExecutionDb,
@ -15,44 +16,69 @@ import type {
IWorkflowDb, IWorkflowDb,
} from './Interfaces'; } from './Interfaces';
const inDevelopment = !process.env.NODE_ENV || process.env.NODE_ENV === 'development';
/** /**
* Special Error which allows to return also an error code and http status code * Special Error which allows to return also an error code and http status code
*
* @class ResponseError
* @extends {Error}
*/ */
export class ResponseError extends Error { abstract class ResponseError extends Error {
// The HTTP status code of response
httpStatusCode?: number;
// The error code in the response
errorCode?: number;
// The error hint the response
hint?: string;
/** /**
* Creates an instance of ResponseError. * Creates an instance of ResponseError.
* Must be used inside a block with `ResponseHelper.send()`. * Must be used inside a block with `ResponseHelper.send()`.
*
* @param {string} message The error message
* @param {number} [errorCode] The error code which can be used by frontend to identify the actual error
* @param {number} [httpStatusCode] The HTTP status code the response should have
* @param {string} [hint] The error hint to provide a context (webhook related)
*/ */
constructor(message: string, errorCode?: number, httpStatusCode?: number, hint?: string) { 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); super(message);
this.name = 'ResponseError'; this.name = 'ResponseError';
}
}
if (errorCode) { export class BadRequestError extends ResponseError {
this.errorCode = errorCode; constructor(message: string) {
} super(message, 400);
if (httpStatusCode) { }
this.httpStatusCode = httpStatusCode; }
}
if (hint) { export class AuthError extends ResponseError {
this.hint = hint; constructor(message: string) {
} super(message, 401);
}
}
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 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);
} }
} }
@ -95,40 +121,49 @@ export function sendSuccessResponse(
} }
} }
export function sendErrorResponse(res: Response, error: ResponseError) { interface ErrorResponse {
code: number;
message: string;
hint?: string;
stacktrace?: string;
}
export function sendErrorResponse(res: Response, error: Error) {
let httpStatusCode = 500; let httpStatusCode = 500;
if (error.httpStatusCode) {
httpStatusCode = error.httpStatusCode;
}
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') { const response: ErrorResponse = {
console.error('ERROR RESPONSE');
console.error(error);
}
const response = {
code: 0, code: 0,
message: 'Unknown error', message: 'Unknown error',
hint: '',
}; };
if (error.name === 'NodeApiError') { if (error instanceof ResponseError) {
if (inDevelopment) {
console.error(picocolors.red(error.httpStatusCode), error.message);
}
response.message = error.message;
httpStatusCode = error.httpStatusCode;
if (error.errorCode) {
response.code = error.errorCode;
}
if (error.hint) {
response.hint = error.hint;
}
}
if (error instanceof NodeApiError) {
if (inDevelopment) {
console.error(picocolors.red(error.name), error.message);
}
Object.assign(response, error); Object.assign(response, error);
} }
if (error.errorCode) { if (error.stack && inDevelopment) {
response.code = error.errorCode; response.stacktrace = error.stack;
}
if (error.message) {
response.message = error.message;
}
if (error.hint) {
response.hint = error.hint;
}
if (error.stack && process.env.NODE_ENV !== 'production') {
// @ts-ignore
response.stack = error.stack;
} }
res.status(httpStatusCode).json(response); res.status(httpStatusCode).json(response);
} }
@ -154,7 +189,9 @@ export function send<T, R extends Request, S extends Response>(
sendSuccessResponse(res, data, raw); sendSuccessResponse(res, data, raw);
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
ErrorReporter.error(error); if (!(error instanceof ResponseError) || error.httpStatusCode > 404) {
ErrorReporter.error(error);
}
if (isUniqueConstraintError(error)) { if (isUniqueConstraintError(error)) {
error.message = 'There is already an entry with this name'; error.message = 'There is already an entry with this name';

View file

@ -157,7 +157,6 @@ import { WaitTracker, WaitTrackerClass } from '@/WaitTracker';
import * as WebhookHelpers from '@/WebhookHelpers'; import * as WebhookHelpers from '@/WebhookHelpers';
import * as WebhookServer from '@/WebhookServer'; import * as WebhookServer from '@/WebhookServer';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'; import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
import { ResponseError } from '@/ResponseHelper';
import { toHttpNodeParameters } from '@/CurlConverterHelper'; import { toHttpNodeParameters } from '@/CurlConverterHelper';
import { setupErrorMiddleware } from '@/ErrorReporting'; import { setupErrorMiddleware } from '@/ErrorReporting';
import { getLicense } from '@/License'; import { getLicense } from '@/License';
@ -725,7 +724,7 @@ class App {
// eslint-disable-next-line consistent-return // eslint-disable-next-line consistent-return
this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => { this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
if (!Db.isInitialized) { if (!Db.isInitialized) {
const error = new ResponseHelper.ResponseError('Database is not ready!', undefined, 503); const error = new ResponseHelper.ServiceUnavailableError('Database is not ready!');
return ResponseHelper.sendErrorResponse(res, error); return ResponseHelper.sendErrorResponse(res, error);
} }
@ -766,7 +765,7 @@ class App {
} catch (err) { } catch (err) {
ErrorReporter.error(err); ErrorReporter.error(err);
LoggerProxy.error('No Database connection!', err); LoggerProxy.error('No Database connection!', err);
const error = new ResponseHelper.ResponseError('No Database connection!', undefined, 503); const error = new ResponseHelper.ServiceUnavailableError('No Database connection!');
return ResponseHelper.sendErrorResponse(res, error); return ResponseHelper.sendErrorResponse(res, error);
} }
@ -869,7 +868,9 @@ class App {
const { path, methodName } = req.query; const { path, methodName } = req.query;
if (!req.query.currentNodeParameters) { if (!req.query.currentNodeParameters) {
throw new ResponseError('Parameter currentNodeParameters is required.', undefined, 400); throw new ResponseHelper.BadRequestError(
'Parameter currentNodeParameters is required.',
);
} }
const currentNodeParameters = jsonParse( const currentNodeParameters = jsonParse(
@ -904,7 +905,7 @@ class App {
); );
} }
throw new ResponseError('Parameter methodName is required.', undefined, 400); throw new ResponseHelper.BadRequestError('Parameter methodName is required.');
}, },
), ),
); );
@ -1076,10 +1077,8 @@ class App {
userId: req.user.id, userId: req.user.id,
}); });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
`Workflow with ID "${workflowId}" could not be found.`, `Workflow with ID "${workflowId}" could not be found.`,
undefined,
400,
); );
} }
@ -1103,7 +1102,7 @@ class App {
const parameters = toHttpNodeParameters(curlCommand); const parameters = toHttpNodeParameters(curlCommand);
return ResponseHelper.flattenObject(parameters, 'parameters'); return ResponseHelper.flattenObject(parameters, 'parameters');
} catch (e) { } catch (e) {
throw new ResponseHelper.ResponseError(`Invalid cURL command`, undefined, 400); throw new ResponseHelper.BadRequestError(`Invalid cURL command`);
} }
}, },
), ),
@ -1178,11 +1177,7 @@ class App {
if (!credentialId) { if (!credentialId) {
LoggerProxy.error('OAuth1 credential authorization failed due to missing credential ID'); LoggerProxy.error('OAuth1 credential authorization failed due to missing credential ID');
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError('Required credential ID is missing');
'Required credential ID is missing',
undefined,
400,
);
} }
const credential = await getCredentialForUser(credentialId, req.user); const credential = await getCredentialForUser(credentialId, req.user);
@ -1192,18 +1187,14 @@ class App {
'OAuth1 credential authorization failed because the current user does not have the correct permissions', 'OAuth1 credential authorization failed because the current user does not have the correct permissions',
{ userId: req.user.id }, { userId: req.user.id },
); );
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL);
RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL,
undefined,
404,
);
} }
let encryptionKey: string; let encryptionKey: string;
try { try {
encryptionKey = await UserSettings.getEncryptionKey(); encryptionKey = await UserSettings.getEncryptionKey();
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError(error.message, undefined, 500); throw new ResponseHelper.InternalServerError(error.message);
} }
const mode: WorkflowExecuteMode = 'internal'; const mode: WorkflowExecuteMode = 'internal';
@ -1304,12 +1295,10 @@ class App {
const { oauth_verifier, oauth_token, cid: credentialId } = req.query; const { oauth_verifier, oauth_token, cid: credentialId } = req.query;
if (!oauth_verifier || !oauth_token) { if (!oauth_verifier || !oauth_token) {
const errorResponse = new ResponseHelper.ResponseError( const errorResponse = new ResponseHelper.ServiceUnavailableError(
`Insufficient parameters for OAuth1 callback. Received following query parameters: ${JSON.stringify( `Insufficient parameters for OAuth1 callback. Received following query parameters: ${JSON.stringify(
req.query, req.query,
)}`, )}`,
undefined,
503,
); );
LoggerProxy.error( LoggerProxy.error(
'OAuth1 callback failed because of insufficient parameters received', 'OAuth1 callback failed because of insufficient parameters received',
@ -1328,10 +1317,8 @@ class App {
userId: req.user?.id, userId: req.user?.id,
credentialId, credentialId,
}); });
const errorResponse = new ResponseHelper.ResponseError( const errorResponse = new ResponseHelper.NotFoundError(
RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL,
undefined,
404,
); );
return ResponseHelper.sendErrorResponse(res, errorResponse); return ResponseHelper.sendErrorResponse(res, errorResponse);
} }
@ -1340,7 +1327,7 @@ class App {
try { try {
encryptionKey = await UserSettings.getEncryptionKey(); encryptionKey = await UserSettings.getEncryptionKey();
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError(error.message, undefined, 500); throw new ResponseHelper.InternalServerError(error.message);
} }
const mode: WorkflowExecuteMode = 'internal'; const mode: WorkflowExecuteMode = 'internal';
@ -1378,11 +1365,7 @@ class App {
userId: req.user?.id, userId: req.user?.id,
credentialId, credentialId,
}); });
const errorResponse = new ResponseHelper.ResponseError( const errorResponse = new ResponseHelper.NotFoundError('Unable to get access tokens!');
'Unable to get access tokens!',
undefined,
404,
);
return ResponseHelper.sendErrorResponse(res, errorResponse); return ResponseHelper.sendErrorResponse(res, errorResponse);
} }
@ -1539,7 +1522,7 @@ class App {
const sharedWorkflowIds = await getSharedWorkflowIds(req.user); const sharedWorkflowIds = await getSharedWorkflowIds(req.user);
if (!sharedWorkflowIds.length) { if (!sharedWorkflowIds.length) {
throw new ResponseHelper.ResponseError('Execution not found', undefined, 404); throw new ResponseHelper.NotFoundError('Execution not found');
} }
const execution = await Db.collections.Execution.findOne({ const execution = await Db.collections.Execution.findOne({
@ -1550,7 +1533,7 @@ class App {
}); });
if (!execution) { if (!execution) {
throw new ResponseHelper.ResponseError('Execution not found', undefined, 404); throw new ResponseHelper.NotFoundError('Execution not found');
} }
if (config.getEnv('executions.mode') === 'queue') { if (config.getEnv('executions.mode') === 'queue') {

View file

@ -67,10 +67,8 @@ export class TestWebhooks {
webhookData = this.activeWebhooks!.get(httpMethod, pathElements.join('/'), webhookId); webhookData = this.activeWebhooks!.get(httpMethod, pathElements.join('/'), webhookId);
if (webhookData === undefined) { if (webhookData === undefined) {
// The requested webhook is not registered // The requested webhook is not registered
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
`The requested webhook "${httpMethod} ${path}" is not registered.`, `The requested webhook "${httpMethod} ${path}" is not registered.`,
404,
404,
WEBHOOK_TEST_UNREGISTERED_HINT, WEBHOOK_TEST_UNREGISTERED_HINT,
); );
} }
@ -94,10 +92,8 @@ export class TestWebhooks {
// TODO: Clean that duplication up one day and improve code generally // TODO: Clean that duplication up one day and improve code generally
if (this.testWebhookData[webhookKey] === undefined) { if (this.testWebhookData[webhookKey] === undefined) {
// The requested webhook is not registered // The requested webhook is not registered
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
`The requested webhook "${httpMethod} ${path}" is not registered.`, `The requested webhook "${httpMethod} ${path}" is not registered.`,
404,
404,
WEBHOOK_TEST_UNREGISTERED_HINT, WEBHOOK_TEST_UNREGISTERED_HINT,
); );
} }
@ -108,7 +104,7 @@ export class TestWebhooks {
// get additional data // get additional data
const workflowStartNode = workflow.getNode(webhookData.node); const workflowStartNode = workflow.getNode(webhookData.node);
if (workflowStartNode === null) { if (workflowStartNode === null) {
throw new ResponseHelper.ResponseError('Could not find node to process webhook.', 404, 404); throw new ResponseHelper.NotFoundError('Could not find node to process webhook.');
} }
// eslint-disable-next-line no-async-promise-executor // eslint-disable-next-line no-async-promise-executor
@ -173,10 +169,8 @@ export class TestWebhooks {
if (webhookMethods === undefined) { if (webhookMethods === undefined) {
// The requested webhook is not registered // The requested webhook is not registered
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
`The requested webhook "${path}" is not registered.`, `The requested webhook "${path}" is not registered.`,
404,
404,
WEBHOOK_TEST_UNREGISTERED_HINT, WEBHOOK_TEST_UNREGISTERED_HINT,
); );
} }

View file

@ -90,7 +90,7 @@ export function getInstanceBaseUrl(): string {
// TODO: Enforce at model level // TODO: Enforce at model level
export function validatePassword(password?: string): string { export function validatePassword(password?: string): string {
if (!password) { if (!password) {
throw new ResponseHelper.ResponseError('Password is mandatory', undefined, 400); throw new ResponseHelper.BadRequestError('Password is mandatory');
} }
const hasInvalidLength = const hasInvalidLength =
@ -117,7 +117,7 @@ export function validatePassword(password?: string): string {
message.push('Password must contain at least 1 uppercase letter.'); message.push('Password must contain at least 1 uppercase letter.');
} }
throw new ResponseHelper.ResponseError(message.join(' '), undefined, 400); throw new ResponseHelper.BadRequestError(message.join(' '));
} }
return password; return password;

View file

@ -77,24 +77,20 @@ export function authenticationMethods(this: N8nApp): void {
} }
if (config.get('userManagement.isInstanceOwnerSetUp')) { if (config.get('userManagement.isInstanceOwnerSetUp')) {
throw new ResponseHelper.ResponseError('Not logged in', undefined, 401); throw new ResponseHelper.AuthError('Not logged in');
} }
try { try {
user = await Db.collections.User.findOneOrFail({ relations: ['globalRole'] }); user = await Db.collections.User.findOneOrFail({ relations: ['globalRole'] });
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
'No users found in database - did you wipe the users table? Create at least one user.', 'No users found in database - did you wipe the users table? Create at least one user.',
undefined,
500,
); );
} }
if (user.email || user.password) { if (user.email || user.password) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
'Invalid database state - user has password set.', 'Invalid database state - user has password set.',
undefined,
500,
); );
} }

View file

@ -39,7 +39,7 @@ export function meNamespace(this: N8nApp): void {
userId: req.user.id, userId: req.user.id,
payload: req.body, payload: req.body,
}); });
throw new ResponseHelper.ResponseError('Email is mandatory', undefined, 400); throw new ResponseHelper.BadRequestError('Email is mandatory');
} }
if (!validator.isEmail(email)) { if (!validator.isEmail(email)) {
@ -47,7 +47,7 @@ export function meNamespace(this: N8nApp): void {
userId: req.user.id, userId: req.user.id,
invalidEmail: email, invalidEmail: email,
}); });
throw new ResponseHelper.ResponseError('Invalid email address', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid email address');
} }
const { email: currentEmail } = req.user; const { email: currentEmail } = req.user;
@ -84,20 +84,16 @@ export function meNamespace(this: N8nApp): void {
const { currentPassword, newPassword } = req.body; const { currentPassword, newPassword } = req.body;
if (typeof currentPassword !== 'string' || typeof newPassword !== 'string') { if (typeof currentPassword !== 'string' || typeof newPassword !== 'string') {
throw new ResponseHelper.ResponseError('Invalid payload.', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid payload.');
} }
if (!req.user.password) { if (!req.user.password) {
throw new ResponseHelper.ResponseError('Requesting user not set up.'); throw new ResponseHelper.BadRequestError('Requesting user not set up.');
} }
const isCurrentPwCorrect = await compareHash(currentPassword, req.user.password); const isCurrentPwCorrect = await compareHash(currentPassword, req.user.password);
if (!isCurrentPwCorrect) { if (!isCurrentPwCorrect) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError('Provided current password is incorrect.');
'Provided current password is incorrect.',
undefined,
400,
);
} }
const validPassword = validatePassword(newPassword); const validPassword = validatePassword(newPassword);
@ -135,11 +131,7 @@ export function meNamespace(this: N8nApp): void {
userId: req.user.id, userId: req.user.id,
}, },
); );
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError('Personalization answers are mandatory');
'Personalization answers are mandatory',
undefined,
400,
);
} }
await Db.collections.User.save({ await Db.collections.User.save({

View file

@ -31,7 +31,7 @@ export function ownerNamespace(this: N8nApp): void {
userId, userId,
}, },
); );
throw new ResponseHelper.ResponseError('Invalid request', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid request');
} }
if (!email || !validator.isEmail(email)) { if (!email || !validator.isEmail(email)) {
@ -39,7 +39,7 @@ export function ownerNamespace(this: N8nApp): void {
userId, userId,
invalidEmail: email, invalidEmail: email,
}); });
throw new ResponseHelper.ResponseError('Invalid email address', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid email address');
} }
const validPassword = validatePassword(password); const validPassword = validatePassword(password);
@ -49,11 +49,7 @@ export function ownerNamespace(this: N8nApp): void {
'Request to claim instance ownership failed because of missing first name or last name in payload', 'Request to claim instance ownership failed because of missing first name or last name in payload',
{ userId, payload: req.body }, { userId, payload: req.body },
); );
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError('First and last names are mandatory');
'First and last names are mandatory',
undefined,
400,
);
} }
let owner = await Db.collections.User.findOne(userId, { let owner = await Db.collections.User.findOne(userId, {
@ -67,7 +63,7 @@ export function ownerNamespace(this: N8nApp): void {
userId, userId,
}, },
); );
throw new ResponseHelper.ResponseError('Invalid request', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid request');
} }
owner = Object.assign(owner, { owner = Object.assign(owner, {

View file

@ -28,10 +28,8 @@ export function passwordResetNamespace(this: N8nApp): void {
ResponseHelper.send(async (req: PasswordResetRequest.Email) => { ResponseHelper.send(async (req: PasswordResetRequest.Email) => {
if (config.getEnv('userManagement.emails.mode') === '') { if (config.getEnv('userManagement.emails.mode') === '') {
Logger.debug('Request to send password reset email failed because emailing was not set up'); Logger.debug('Request to send password reset email failed because emailing was not set up');
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
'Email sending must be set up in order to request a password reset email', 'Email sending must be set up in order to request a password reset email',
undefined,
500,
); );
} }
@ -42,7 +40,7 @@ export function passwordResetNamespace(this: N8nApp): void {
'Request to send password reset email failed because of missing email in payload', 'Request to send password reset email failed because of missing email in payload',
{ payload: req.body }, { payload: req.body },
); );
throw new ResponseHelper.ResponseError('Email is mandatory', undefined, 400); throw new ResponseHelper.BadRequestError('Email is mandatory');
} }
if (!validator.isEmail(email)) { if (!validator.isEmail(email)) {
@ -50,7 +48,7 @@ export function passwordResetNamespace(this: N8nApp): void {
'Request to send password reset email failed because of invalid email in payload', 'Request to send password reset email failed because of invalid email in payload',
{ invalidEmail: email }, { invalidEmail: email },
); );
throw new ResponseHelper.ResponseError('Invalid email address', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid email address');
} }
// User should just be able to reset password if one is already present // User should just be able to reset password if one is already present
@ -93,10 +91,8 @@ export function passwordResetNamespace(this: N8nApp): void {
public_api: false, public_api: false,
}); });
if (error instanceof Error) { if (error instanceof Error) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
`Please contact your administrator: ${error.message}`, `Please contact your administrator: ${error.message}`,
undefined,
500,
); );
} }
} }
@ -131,7 +127,7 @@ export function passwordResetNamespace(this: N8nApp): void {
queryString: req.query, queryString: req.query,
}, },
); );
throw new ResponseHelper.ResponseError('', undefined, 400); throw new ResponseHelper.BadRequestError('');
} }
// Timestamp is saved in seconds // Timestamp is saved in seconds
@ -151,7 +147,7 @@ export function passwordResetNamespace(this: N8nApp): void {
resetPasswordToken, resetPasswordToken,
}, },
); );
throw new ResponseHelper.ResponseError('', undefined, 404); throw new ResponseHelper.NotFoundError('');
} }
Logger.info('Reset-password token resolved successfully', { userId: id }); Logger.info('Reset-password token resolved successfully', { userId: id });
@ -178,10 +174,8 @@ export function passwordResetNamespace(this: N8nApp): void {
payload: req.body, payload: req.body,
}, },
); );
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
'Missing user ID or password or reset password token', 'Missing user ID or password or reset password token',
undefined,
400,
); );
} }
@ -204,7 +198,7 @@ export function passwordResetNamespace(this: N8nApp): void {
resetPasswordToken, resetPasswordToken,
}, },
); );
throw new ResponseHelper.ResponseError('', undefined, 404); throw new ResponseHelper.NotFoundError('');
} }
await Db.collections.User.update(userId, { await Db.collections.User.update(userId, {

View file

@ -37,10 +37,8 @@ export function usersNamespace(this: N8nApp): void {
Logger.debug( Logger.debug(
'Request to send email invite(s) to user(s) failed because emailing was not set up', 'Request to send email invite(s) to user(s) failed because emailing was not set up',
); );
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
'Email sending must be set up in order to request a password reset email', 'Email sending must be set up in order to request a password reset email',
undefined,
500,
); );
} }
@ -49,10 +47,8 @@ export function usersNamespace(this: N8nApp): void {
mailer = await UserManagementMailer.getInstance(); mailer = await UserManagementMailer.getInstance();
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
`There is a problem with your SMTP setup! ${error.message}`, `There is a problem with your SMTP setup! ${error.message}`,
undefined,
500,
); );
} }
} }
@ -62,17 +58,15 @@ export function usersNamespace(this: N8nApp): void {
Logger.debug( Logger.debug(
'Request to send email invite(s) to user(s) failed because user management is disabled', 'Request to send email invite(s) to user(s) failed because user management is disabled',
); );
throw new ResponseHelper.ResponseError('User management is disabled'); throw new ResponseHelper.BadRequestError('User management is disabled');
} }
if (!config.getEnv('userManagement.isInstanceOwnerSetUp')) { if (!config.getEnv('userManagement.isInstanceOwnerSetUp')) {
Logger.debug( Logger.debug(
'Request to send email invite(s) to user(s) failed because the owner account is not set up', 'Request to send email invite(s) to user(s) failed because the owner account is not set up',
); );
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
'You must set up your own account before inviting others', 'You must set up your own account before inviting others',
undefined,
400,
); );
} }
@ -83,7 +77,7 @@ export function usersNamespace(this: N8nApp): void {
payload: req.body, payload: req.body,
}, },
); );
throw new ResponseHelper.ResponseError('Invalid payload', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid payload');
} }
if (!req.body.length) return []; if (!req.body.length) return [];
@ -92,19 +86,15 @@ export function usersNamespace(this: N8nApp): void {
// Validate payload // Validate payload
req.body.forEach((invite) => { req.body.forEach((invite) => {
if (typeof invite !== 'object' || !invite.email) { if (typeof invite !== 'object' || !invite.email) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
'Request to send email invite(s) to user(s) failed because the payload is not an array shaped Array<{ email: string }>', 'Request to send email invite(s) to user(s) failed because the payload is not an array shaped Array<{ email: string }>',
undefined,
400,
); );
} }
if (!validator.isEmail(invite.email)) { if (!validator.isEmail(invite.email)) {
Logger.debug('Invalid email in payload', { invalidEmail: invite.email }); Logger.debug('Invalid email in payload', { invalidEmail: invite.email });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
`Request to send email invite(s) to user(s) failed because of an invalid email address: ${invite.email}`, `Request to send email invite(s) to user(s) failed because of an invalid email address: ${invite.email}`,
undefined,
400,
); );
} }
createUsers[invite.email.toLowerCase()] = null; createUsers[invite.email.toLowerCase()] = null;
@ -116,10 +106,8 @@ export function usersNamespace(this: N8nApp): void {
Logger.error( Logger.error(
'Request to send email invite(s) to user(s) failed because no global member role was found in database', 'Request to send email invite(s) to user(s) failed because no global member role was found in database',
); );
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
'Members role not found in database - inconsistent state', 'Members role not found in database - inconsistent state',
undefined,
500,
); );
} }
@ -163,7 +151,7 @@ export function usersNamespace(this: N8nApp): void {
} catch (error) { } catch (error) {
ErrorReporter.error(error); ErrorReporter.error(error);
Logger.error('Failed to create user shells', { userShells: createUsers }); Logger.error('Failed to create user shells', { userShells: createUsers });
throw new ResponseHelper.ResponseError('An error occurred during user creation'); throw new ResponseHelper.InternalServerError('An error occurred during user creation');
} }
Logger.info('Created user shell(s) successfully', { userId: req.user.id }); Logger.info('Created user shell(s) successfully', { userId: req.user.id });
@ -245,7 +233,7 @@ export function usersNamespace(this: N8nApp): void {
'Request to resolve signup token failed because of missing user IDs in query string', 'Request to resolve signup token failed because of missing user IDs in query string',
{ inviterId, inviteeId }, { inviterId, inviteeId },
); );
throw new ResponseHelper.ResponseError('Invalid payload', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid payload');
} }
// Postgres validates UUID format // Postgres validates UUID format
@ -254,7 +242,7 @@ export function usersNamespace(this: N8nApp): void {
Logger.debug('Request to resolve signup token failed because of invalid user ID', { Logger.debug('Request to resolve signup token failed because of invalid user ID', {
userId, userId,
}); });
throw new ResponseHelper.ResponseError('Invalid userId', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid userId');
} }
} }
@ -265,7 +253,7 @@ export function usersNamespace(this: N8nApp): void {
'Request to resolve signup token failed because the ID of the inviter and/or the ID of the invitee were not found in database', 'Request to resolve signup token failed because the ID of the inviter and/or the ID of the invitee were not found in database',
{ inviterId, inviteeId }, { inviterId, inviteeId },
); );
throw new ResponseHelper.ResponseError('Invalid invite URL', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid invite URL');
} }
const invitee = users.find((user) => user.id === inviteeId); const invitee = users.find((user) => user.id === inviteeId);
@ -275,10 +263,8 @@ export function usersNamespace(this: N8nApp): void {
inviterId, inviterId,
inviteeId, inviteeId,
}); });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
'The invitation was likely either deleted or already claimed', 'The invitation was likely either deleted or already claimed',
undefined,
400,
); );
} }
@ -291,7 +277,7 @@ export function usersNamespace(this: N8nApp): void {
inviterId: inviter?.id, inviterId: inviter?.id,
}, },
); );
throw new ResponseHelper.ResponseError('Invalid request', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid request');
} }
void InternalHooksManager.getInstance().onUserInviteEmailClick({ void InternalHooksManager.getInstance().onUserInviteEmailClick({
@ -321,7 +307,7 @@ export function usersNamespace(this: N8nApp): void {
'Request to fill out a user shell failed because of missing properties in payload', 'Request to fill out a user shell failed because of missing properties in payload',
{ payload: req.body }, { payload: req.body },
); );
throw new ResponseHelper.ResponseError('Invalid payload', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid payload');
} }
const validPassword = validatePassword(password); const validPassword = validatePassword(password);
@ -339,7 +325,7 @@ export function usersNamespace(this: N8nApp): void {
inviteeId, inviteeId,
}, },
); );
throw new ResponseHelper.ResponseError('Invalid payload or URL', undefined, 400); throw new ResponseHelper.BadRequestError('Invalid payload or URL');
} }
const invitee = users.find((user) => user.id === inviteeId) as User; const invitee = users.find((user) => user.id === inviteeId) as User;
@ -349,11 +335,7 @@ export function usersNamespace(this: N8nApp): void {
'Request to fill out a user shell failed because the invite had already been accepted', 'Request to fill out a user shell failed because the invite had already been accepted',
{ inviteeId }, { inviteeId },
); );
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError('This invite has been accepted already');
'This invite has been accepted already',
undefined,
400,
);
} }
invitee.firstName = firstName; invitee.firstName = firstName;
@ -398,16 +380,14 @@ export function usersNamespace(this: N8nApp): void {
'Request to delete a user failed because it attempted to delete the requesting user', 'Request to delete a user failed because it attempted to delete the requesting user',
{ userId: req.user.id }, { userId: req.user.id },
); );
throw new ResponseHelper.ResponseError('Cannot delete your own user', undefined, 400); throw new ResponseHelper.BadRequestError('Cannot delete your own user');
} }
const { transferId } = req.query; const { transferId } = req.query;
if (transferId === idToDelete) { if (transferId === idToDelete) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
'Request to delete a user failed because the user to delete and the transferee are the same user', 'Request to delete a user failed because the user to delete and the transferee are the same user',
undefined,
400,
); );
} }
@ -416,10 +396,8 @@ export function usersNamespace(this: N8nApp): void {
}); });
if (!users.length || (transferId && users.length !== 2)) { if (!users.length || (transferId && users.length !== 2)) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
'Request to delete a user failed because the ID of the user to delete and/or the ID of the transferee were not found in DB', 'Request to delete a user failed because the ID of the user to delete and/or the ID of the transferee were not found in DB',
undefined,
404,
); );
} }
@ -502,10 +480,8 @@ export function usersNamespace(this: N8nApp): void {
if (!isEmailSetUp()) { if (!isEmailSetUp()) {
Logger.error('Request to reinvite a user failed because email sending was not set up'); Logger.error('Request to reinvite a user failed because email sending was not set up');
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
'Email sending must be set up in order to invite other users', 'Email sending must be set up in order to invite other users',
undefined,
500,
); );
} }
@ -515,7 +491,7 @@ export function usersNamespace(this: N8nApp): void {
Logger.debug( Logger.debug(
'Request to reinvite a user failed because the ID of the reinvitee was not found in database', 'Request to reinvite a user failed because the ID of the reinvitee was not found in database',
); );
throw new ResponseHelper.ResponseError('Could not find user', undefined, 404); throw new ResponseHelper.NotFoundError('Could not find user');
} }
if (reinvitee.password) { if (reinvitee.password) {
@ -523,11 +499,7 @@ export function usersNamespace(this: N8nApp): void {
'Request to reinvite a user failed because the invite had already been accepted', 'Request to reinvite a user failed because the invite had already been accepted',
{ userId: reinvitee.id }, { userId: reinvitee.id },
); );
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError('User has already accepted the invite');
'User has already accepted the invite',
undefined,
400,
);
} }
const baseUrl = getInstanceBaseUrl(); const baseUrl = getInstanceBaseUrl();
@ -538,7 +510,7 @@ export function usersNamespace(this: N8nApp): void {
mailer = await UserManagementMailer.getInstance(); mailer = await UserManagementMailer.getInstance();
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
throw new ResponseHelper.ResponseError(error.message, undefined, 500); throw new ResponseHelper.InternalServerError(error.message);
} }
} }
@ -559,11 +531,7 @@ export function usersNamespace(this: N8nApp): void {
inviteAcceptUrl, inviteAcceptUrl,
domain: baseUrl, domain: baseUrl,
}); });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(`Failed to send email to ${reinvitee.email}`);
`Failed to send email to ${reinvitee.email}`,
undefined,
500,
);
} }
void InternalHooksManager.getInstance().onUserReinvite({ void InternalHooksManager.getInstance().onUserReinvite({

View file

@ -44,21 +44,13 @@ export class WaitingWebhooks {
const execution = await Db.collections.Execution.findOne(executionId); const execution = await Db.collections.Execution.findOne(executionId);
if (execution === undefined) { if (execution === undefined) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(`The execution "${executionId} does not exist.`);
`The execution "${executionId} does not exist.`,
404,
404,
);
} }
const fullExecutionData = ResponseHelper.unflattenExecutionData(execution); const fullExecutionData = ResponseHelper.unflattenExecutionData(execution);
if (fullExecutionData.finished || fullExecutionData.data.resultData.error) { if (fullExecutionData.finished || fullExecutionData.data.resultData.error) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.ConflictError(`The execution "${executionId} has finished already.`);
`The execution "${executionId} has finished already.`,
409,
409,
);
} }
return this.startExecution(httpMethod, path, fullExecutionData, req, res); return this.startExecution(httpMethod, path, fullExecutionData, req, res);
@ -107,7 +99,7 @@ export class WaitingWebhooks {
try { try {
workflowOwner = await getWorkflowOwner(workflowData.id!.toString()); workflowOwner = await getWorkflowOwner(workflowData.id!.toString());
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError('Could not find workflow', undefined, 404); throw new ResponseHelper.NotFoundError('Could not find workflow');
} }
const additionalData = await WorkflowExecuteAdditionalData.getBase(workflowOwner.id); const additionalData = await WorkflowExecuteAdditionalData.getBase(workflowOwner.id);
@ -128,13 +120,13 @@ export class WaitingWebhooks {
// If no data got found it means that the execution can not be started via a webhook. // 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. // Return 404 because we do not want to give any data if the execution exists or not.
const errorMessage = `The execution "${executionId}" with webhook suffix path "${path}" is not known.`; const errorMessage = `The execution "${executionId}" with webhook suffix path "${path}" is not known.`;
throw new ResponseHelper.ResponseError(errorMessage, 404, 404); throw new ResponseHelper.NotFoundError(errorMessage);
} }
const workflowStartNode = workflow.getNode(lastNodeExecuted); const workflowStartNode = workflow.getNode(lastNodeExecuted);
if (workflowStartNode === null) { if (workflowStartNode === null) {
throw new ResponseHelper.ResponseError('Could not find node to process webhook.', 404, 404); throw new ResponseHelper.NotFoundError('Could not find node to process webhook.');
} }
const runExecutionData = fullExecutionData.data; const runExecutionData = fullExecutionData.data;

View file

@ -152,7 +152,7 @@ export async function executeWebhook(
if (nodeType === undefined) { if (nodeType === undefined) {
const errorMessage = `The type of the webhook node "${workflowStartNode.name}" is not known`; const errorMessage = `The type of the webhook node "${workflowStartNode.name}" is not known`;
responseCallback(new Error(errorMessage), {}); responseCallback(new Error(errorMessage), {});
throw new ResponseHelper.ResponseError(errorMessage, 500, 500); throw new ResponseHelper.InternalServerError(errorMessage);
} }
const additionalKeys: IWorkflowDataProxyAdditionalKeys = { const additionalKeys: IWorkflowDataProxyAdditionalKeys = {
@ -169,7 +169,7 @@ export async function executeWebhook(
try { try {
user = await getWorkflowOwner(workflowData.id.toString()); user = await getWorkflowOwner(workflowData.id.toString());
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError('Cannot find workflow', undefined, 404); throw new ResponseHelper.NotFoundError('Cannot find workflow');
} }
} }
@ -212,7 +212,7 @@ export async function executeWebhook(
// that something does not resolve properly. // that something does not resolve properly.
const errorMessage = `The response mode '${responseMode}' is not valid!`; const errorMessage = `The response mode '${responseMode}' is not valid!`;
responseCallback(new Error(errorMessage), {}); responseCallback(new Error(errorMessage), {});
throw new ResponseHelper.ResponseError(errorMessage, 500, 500); throw new ResponseHelper.InternalServerError(errorMessage);
} }
// Add the Response and Request so that this data can be accessed in the node // Add the Response and Request so that this data can be accessed in the node
@ -661,7 +661,7 @@ export async function executeWebhook(
responseCallback(new Error('There was a problem executing the workflow'), {}); responseCallback(new Error('There was a problem executing the workflow'), {});
} }
throw new ResponseHelper.ResponseError(e.message, 500, 500); throw new ResponseHelper.InternalServerError(e.message);
}); });
// eslint-disable-next-line consistent-return // eslint-disable-next-line consistent-return
@ -671,7 +671,7 @@ export async function executeWebhook(
responseCallback(new Error('There was a problem executing the workflow'), {}); responseCallback(new Error('There was a problem executing the workflow'), {});
} }
throw new ResponseHelper.ResponseError(e.message, 500, 500); throw new ResponseHelper.InternalServerError(e.message);
} }
} }

View file

@ -294,7 +294,7 @@ class App {
this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => { this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
if (!Db.isInitialized) { if (!Db.isInitialized) {
const error = new ResponseHelper.ResponseError('Database is not ready!', undefined, 503); const error = new ResponseHelper.ServiceUnavailableError('Database is not ready!');
return ResponseHelper.sendErrorResponse(res, error); return ResponseHelper.sendErrorResponse(res, error);
} }
@ -318,7 +318,7 @@ class App {
await connection.query('SELECT 1'); await connection.query('SELECT 1');
// eslint-disable-next-line id-denylist // eslint-disable-next-line id-denylist
} catch (err) { } catch (err) {
const error = new ResponseHelper.ResponseError('No Database connection!', undefined, 503); const error = new ResponseHelper.ServiceUnavailableError('No Database connection!');
return ResponseHelper.sendErrorResponse(res, error); return ResponseHelper.sendErrorResponse(res, error);
} }

View file

@ -172,10 +172,8 @@ executionsController.get(
userId: req.user.id, userId: req.user.id,
filter: req.query.filter, filter: req.query.filter,
}); });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
`Parameter "filter" contained invalid JSON string.`, `Parameter "filter" contained invalid JSON string.`,
500,
500,
); );
} }
} }
@ -363,10 +361,8 @@ executionsController.post(
executionId, executionId,
}, },
); );
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
`The execution with the ID "${executionId}" does not exist.`, `The execution with the ID "${executionId}" does not exist.`,
404,
404,
); );
} }
@ -485,10 +481,8 @@ executionsController.post(
requestFilters = requestFiltersRaw as IGetExecutionsQueryFilter; requestFilters = requestFiltersRaw as IGetExecutionsQueryFilter;
} }
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
`Parameter "filter" contained invalid JSON string.`, `Parameter "filter" contained invalid JSON string.`,
500,
500,
); );
} }
} }

View file

@ -71,7 +71,7 @@ nodesController.post(
const { name } = req.body; const { name } = req.body;
if (!name) { if (!name) {
throw new ResponseHelper.ResponseError(PACKAGE_NAME_NOT_PROVIDED, undefined, 400); throw new ResponseHelper.BadRequestError(PACKAGE_NAME_NOT_PROVIDED);
} }
let parsed: CommunityPackages.ParsedPackageName; let parsed: CommunityPackages.ParsedPackageName;
@ -79,21 +79,17 @@ nodesController.post(
try { try {
parsed = parseNpmPackageName(name); parsed = parseNpmPackageName(name);
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
error instanceof Error ? error.message : 'Failed to parse package name', error instanceof Error ? error.message : 'Failed to parse package name',
undefined,
400,
); );
} }
if (parsed.packageName === STARTER_TEMPLATE_NAME) { if (parsed.packageName === STARTER_TEMPLATE_NAME) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
[ [
`Package "${parsed.packageName}" is only a template`, `Package "${parsed.packageName}" is only a template`,
'Please enter an actual package to install', 'Please enter an actual package to install',
].join('.'), ].join('.'),
undefined,
400,
); );
} }
@ -101,23 +97,19 @@ nodesController.post(
const hasLoaded = hasPackageLoaded(name); const hasLoaded = hasPackageLoaded(name);
if (isInstalled && hasLoaded) { if (isInstalled && hasLoaded) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
[ [
`Package "${parsed.packageName}" is already installed`, `Package "${parsed.packageName}" is already installed`,
'To update it, click the corresponding button in the UI', 'To update it, click the corresponding button in the UI',
].join('.'), ].join('.'),
undefined,
400,
); );
} }
const packageStatus = await checkNpmPackageStatus(name); const packageStatus = await checkNpmPackageStatus(name);
if (packageStatus.status !== 'OK') { if (packageStatus.status !== 'OK') {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
`Package "${name}" is banned so it cannot be installed`, `Package "${name}" is banned so it cannot be installed`,
undefined,
400,
); );
} }
@ -144,7 +136,7 @@ nodesController.post(
const clientError = error instanceof Error ? isClientError(error) : false; const clientError = error instanceof Error ? isClientError(error) : false;
throw new ResponseHelper.ResponseError(message, undefined, clientError ? 400 : 500); throw new ResponseHelper[clientError ? 'BadRequestError' : 'InternalServerError'](message);
} }
if (!hasLoaded) removePackageFromMissingList(name); if (!hasLoaded) removePackageFromMissingList(name);
@ -228,7 +220,7 @@ nodesController.delete(
const { name } = req.query; const { name } = req.query;
if (!name) { if (!name) {
throw new ResponseHelper.ResponseError(PACKAGE_NAME_NOT_PROVIDED, undefined, 400); throw new ResponseHelper.BadRequestError(PACKAGE_NAME_NOT_PROVIDED);
} }
try { try {
@ -236,13 +228,13 @@ nodesController.delete(
} catch (error) { } catch (error) {
const message = error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON; const message = error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON;
throw new ResponseHelper.ResponseError(message, undefined, 400); throw new ResponseHelper.BadRequestError(message);
} }
const installedPackage = await findInstalledPackage(name); const installedPackage = await findInstalledPackage(name);
if (!installedPackage) { if (!installedPackage) {
throw new ResponseHelper.ResponseError(PACKAGE_NOT_INSTALLED, undefined, 400); throw new ResponseHelper.BadRequestError(PACKAGE_NOT_INSTALLED);
} }
try { try {
@ -253,7 +245,7 @@ nodesController.delete(
error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON, error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON,
].join(':'); ].join(':');
throw new ResponseHelper.ResponseError(message, undefined, 500); throw new ResponseHelper.InternalServerError(message);
} }
const pushInstance = Push.getInstance(); const pushInstance = Push.getInstance();
@ -288,13 +280,13 @@ nodesController.patch(
const { name } = req.body; const { name } = req.body;
if (!name) { if (!name) {
throw new ResponseHelper.ResponseError(PACKAGE_NAME_NOT_PROVIDED, undefined, 400); throw new ResponseHelper.BadRequestError(PACKAGE_NAME_NOT_PROVIDED);
} }
const previouslyInstalledPackage = await findInstalledPackage(name); const previouslyInstalledPackage = await findInstalledPackage(name);
if (!previouslyInstalledPackage) { if (!previouslyInstalledPackage) {
throw new ResponseHelper.ResponseError(PACKAGE_NOT_INSTALLED, undefined, 400); throw new ResponseHelper.BadRequestError(PACKAGE_NOT_INSTALLED);
} }
try { try {
@ -345,7 +337,7 @@ nodesController.patch(
error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON, error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON,
].join(':'); ].join(':');
throw new ResponseHelper.ResponseError(message, undefined, 500); throw new ResponseHelper.InternalServerError(message);
} }
}), }),
); );

View file

@ -21,13 +21,18 @@ export const externalHooks: IExternalHooksClass = ExternalHooks();
export const tagsController = express.Router(); export const tagsController = express.Router();
const workflowsEnabledMiddleware: express.RequestHandler = (req, res, next) => {
if (config.getEnv('workflowTagsDisabled')) {
throw new ResponseHelper.BadRequestError('Workflow tags are disabled');
}
next();
};
// Retrieves all tags, with or without usage count // Retrieves all tags, with or without usage count
tagsController.get( tagsController.get(
'/', '/',
workflowsEnabledMiddleware,
ResponseHelper.send(async (req: express.Request): Promise<TagEntity[] | ITagWithCountDb[]> => { ResponseHelper.send(async (req: express.Request): Promise<TagEntity[] | ITagWithCountDb[]> => {
if (config.getEnv('workflowTagsDisabled')) {
throw new ResponseHelper.ResponseError('Workflow tags are disabled');
}
if (req.query.withUsageCount === 'true') { if (req.query.withUsageCount === 'true') {
const tablePrefix = config.getEnv('database.tablePrefix'); const tablePrefix = config.getEnv('database.tablePrefix');
return TagHelpers.getTagsWithCountDb(tablePrefix); return TagHelpers.getTagsWithCountDb(tablePrefix);
@ -40,10 +45,8 @@ tagsController.get(
// Creates a tag // Creates a tag
tagsController.post( tagsController.post(
'/', '/',
workflowsEnabledMiddleware,
ResponseHelper.send(async (req: express.Request): Promise<TagEntity | void> => { ResponseHelper.send(async (req: express.Request): Promise<TagEntity | void> => {
if (config.getEnv('workflowTagsDisabled')) {
throw new ResponseHelper.ResponseError('Workflow tags are disabled');
}
const newTag = new TagEntity(); const newTag = new TagEntity();
newTag.name = req.body.name.trim(); newTag.name = req.body.name.trim();
@ -61,11 +64,8 @@ tagsController.post(
// Updates a tag // Updates a tag
tagsController.patch( tagsController.patch(
'/:id', '/:id',
workflowsEnabledMiddleware,
ResponseHelper.send(async (req: express.Request): Promise<TagEntity | void> => { ResponseHelper.send(async (req: express.Request): Promise<TagEntity | void> => {
if (config.getEnv('workflowTagsDisabled')) {
throw new ResponseHelper.ResponseError('Workflow tags are disabled');
}
const { name } = req.body; const { name } = req.body;
const { id } = req.params; const { id } = req.params;
@ -87,18 +87,14 @@ tagsController.patch(
tagsController.delete( tagsController.delete(
'/:id', '/:id',
workflowsEnabledMiddleware,
ResponseHelper.send(async (req: TagsRequest.Delete): Promise<boolean> => { ResponseHelper.send(async (req: TagsRequest.Delete): Promise<boolean> => {
if (config.getEnv('workflowTagsDisabled')) {
throw new ResponseHelper.ResponseError('Workflow tags are disabled');
}
if ( if (
config.getEnv('userManagement.isInstanceOwnerSetUp') === true && config.getEnv('userManagement.isInstanceOwnerSetUp') === true &&
req.user.globalRole.name !== 'owner' req.user.globalRole.name !== 'owner'
) { ) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.UnauthorizedError(
'You are not allowed to perform this action', 'You are not allowed to perform this action',
undefined,
403,
'Only owners can remove tags', 'Only owners can remove tags',
); );
} }

View file

@ -391,11 +391,7 @@ export class Worker extends Command {
await connection.query('SELECT 1'); await connection.query('SELECT 1');
} catch (e) { } catch (e) {
LoggerProxy.error('No Database connection!', e); LoggerProxy.error('No Database connection!', e);
const error = new ResponseHelper.ResponseError( const error = new ResponseHelper.ServiceUnavailableError('No Database connection!');
'No Database connection!',
undefined,
503,
);
return ResponseHelper.sendErrorResponse(res, error); return ResponseHelper.sendErrorResponse(res, error);
} }
@ -406,11 +402,7 @@ export class Worker extends Command {
await Worker.jobQueue.client.ping(); await Worker.jobQueue.client.ping();
} catch (e) { } catch (e) {
LoggerProxy.error('No Redis connection!', e); LoggerProxy.error('No Redis connection!', e);
const error = new ResponseHelper.ResponseError( const error = new ResponseHelper.ServiceUnavailableError('No Redis connection!');
'No Redis connection!',
undefined,
503,
);
return ResponseHelper.sendErrorResponse(res, error); return ResponseHelper.sendErrorResponse(res, error);
} }

View file

@ -54,7 +54,7 @@ EECredentialsController.get(
const includeDecryptedData = req.query.includeData === 'true'; const includeDecryptedData = req.query.includeData === 'true';
if (Number.isNaN(Number(credentialId))) { if (Number.isNaN(Number(credentialId))) {
throw new ResponseHelper.ResponseError(`Credential ID must be a number.`, undefined, 400); throw new ResponseHelper.BadRequestError(`Credential ID must be a number.`);
} }
let credential = (await EECredentials.get( let credential = (await EECredentials.get(
@ -63,17 +63,15 @@ EECredentialsController.get(
)) as CredentialsEntity & CredentialWithSharings; )) as CredentialsEntity & CredentialWithSharings;
if (!credential) { if (!credential) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
'Could not load the credential. If you think this is an error, ask the owner to share it with you again', 'Could not load the credential. If you think this is an error, ask the owner to share it with you again',
undefined,
404,
); );
} }
const userSharing = credential.shared?.find((shared) => shared.user.id === req.user.id); const userSharing = credential.shared?.find((shared) => shared.user.id === req.user.id);
if (!userSharing && req.user.globalRole.name !== 'owner') { if (!userSharing && req.user.globalRole.name !== 'owner') {
throw new ResponseHelper.ResponseError(`Forbidden.`, undefined, 403); throw new ResponseHelper.UnauthorizedError(`Forbidden.`);
} }
credential = EECredentials.addOwnerAndSharings(credential); credential = EECredentials.addOwnerAndSharings(credential);
@ -117,7 +115,7 @@ EECredentialsController.post(
if (!ownsCredential) { if (!ownsCredential) {
const sharing = await EECredentials.getSharing(req.user, credentials.id); const sharing = await EECredentials.getSharing(req.user, credentials.id);
if (!sharing) { if (!sharing) {
throw new ResponseHelper.ResponseError(`Forbidden`, undefined, 403); throw new ResponseHelper.UnauthorizedError(`Forbidden`);
} }
const decryptedData = await EECredentials.decrypt(encryptionKey, sharing.credentials); const decryptedData = await EECredentials.decrypt(encryptionKey, sharing.credentials);
@ -144,13 +142,13 @@ EECredentialsController.put(
!Array.isArray(shareWithIds) || !Array.isArray(shareWithIds) ||
!shareWithIds.every((userId) => typeof userId === 'string') !shareWithIds.every((userId) => typeof userId === 'string')
) { ) {
throw new ResponseHelper.ResponseError('Bad request', undefined, 400); throw new ResponseHelper.BadRequestError('Bad request');
} }
const { ownsCredential, credential } = await EECredentials.isOwned(req.user, credentialId); const { ownsCredential, credential } = await EECredentials.isOwned(req.user, credentialId);
if (!ownsCredential || !credential) { if (!ownsCredential || !credential) {
throw new ResponseHelper.ResponseError('Forbidden', undefined, 403); throw new ResponseHelper.UnauthorizedError('Forbidden');
} }
let amountRemoved: number | null = null; let amountRemoved: number | null = null;

View file

@ -75,16 +75,14 @@ credentialsController.get(
const includeDecryptedData = req.query.includeData === 'true'; const includeDecryptedData = req.query.includeData === 'true';
if (Number.isNaN(Number(credentialId))) { if (Number.isNaN(Number(credentialId))) {
throw new ResponseHelper.ResponseError(`Credential ID must be a number.`, undefined, 400); throw new ResponseHelper.BadRequestError(`Credential ID must be a number.`);
} }
const sharing = await CredentialsService.getSharing(req.user, credentialId, ['credentials']); const sharing = await CredentialsService.getSharing(req.user, credentialId, ['credentials']);
if (!sharing) { if (!sharing) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
`Credential with ID "${credentialId}" could not be found.`, `Credential with ID "${credentialId}" could not be found.`,
undefined,
404,
); );
} }
@ -159,10 +157,8 @@ credentialsController.patch(
credentialId, credentialId,
userId: req.user.id, userId: req.user.id,
}); });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
'Credential to be updated not found. You can only update credentials owned by you', 'Credential to be updated not found. You can only update credentials owned by you',
undefined,
404,
); );
} }
@ -183,10 +179,8 @@ credentialsController.patch(
const responseData = await CredentialsService.update(credentialId, newCredentialData); const responseData = await CredentialsService.update(credentialId, newCredentialData);
if (responseData === undefined) { if (responseData === undefined) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
`Credential ID "${credentialId}" could not be found to be updated.`, `Credential ID "${credentialId}" could not be found to be updated.`,
undefined,
404,
); );
} }
@ -217,10 +211,8 @@ credentialsController.delete(
credentialId, credentialId,
userId: req.user.id, userId: req.user.id,
}); });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
'Credential to be deleted not found. You can only removed credentials owned by you', 'Credential to be deleted not found. You can only removed credentials owned by you',
undefined,
404,
); );
} }

View file

@ -206,11 +206,7 @@ export class CredentialsService {
try { try {
return await UserSettings.getEncryptionKey(); return await UserSettings.getEncryptionKey();
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY);
RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY,
undefined,
500,
);
} }
} }

View file

@ -58,7 +58,7 @@ oauth2CredentialController.get(
const { id: credentialId } = req.query; const { id: credentialId } = req.query;
if (!credentialId) { if (!credentialId) {
throw new ResponseHelper.ResponseError('Required credential ID is missing', undefined, 400); throw new ResponseHelper.BadRequestError('Required credential ID is missing');
} }
const credential = await getCredentialForUser(credentialId, req.user); const credential = await getCredentialForUser(credentialId, req.user);
@ -68,14 +68,14 @@ oauth2CredentialController.get(
userId: req.user.id, userId: req.user.id,
credentialId, credentialId,
}); });
throw new ResponseHelper.ResponseError(RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, undefined, 404); throw new ResponseHelper.NotFoundError(RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL);
} }
let encryptionKey: string; let encryptionKey: string;
try { try {
encryptionKey = await UserSettings.getEncryptionKey(); encryptionKey = await UserSettings.getEncryptionKey();
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError((error as Error).message, undefined, 500); throw new ResponseHelper.InternalServerError((error as Error).message);
} }
const mode: WorkflowExecuteMode = 'internal'; const mode: WorkflowExecuteMode = 'internal';
@ -173,12 +173,10 @@ oauth2CredentialController.get(
const { code, state: stateEncoded } = req.query; const { code, state: stateEncoded } = req.query;
if (!code || !stateEncoded) { if (!code || !stateEncoded) {
const errorResponse = new ResponseHelper.ResponseError( const errorResponse = new ResponseHelper.ServiceUnavailableError(
`Insufficient parameters for OAuth2 callback. Received following query parameters: ${JSON.stringify( `Insufficient parameters for OAuth2 callback. Received following query parameters: ${JSON.stringify(
req.query, req.query,
)}`, )}`,
undefined,
503,
); );
return ResponseHelper.sendErrorResponse(res, errorResponse); return ResponseHelper.sendErrorResponse(res, errorResponse);
} }
@ -190,10 +188,8 @@ oauth2CredentialController.get(
token: string; token: string;
}; };
} catch (error) { } catch (error) {
const errorResponse = new ResponseHelper.ResponseError( const errorResponse = new ResponseHelper.ServiceUnavailableError(
'Invalid state format returned', 'Invalid state format returned',
undefined,
503,
); );
return ResponseHelper.sendErrorResponse(res, errorResponse); return ResponseHelper.sendErrorResponse(res, errorResponse);
} }
@ -205,10 +201,8 @@ oauth2CredentialController.get(
userId: req.user?.id, userId: req.user?.id,
credentialId: state.cid, credentialId: state.cid,
}); });
const errorResponse = new ResponseHelper.ResponseError( const errorResponse = new ResponseHelper.NotFoundError(
RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL,
undefined,
404,
); );
return ResponseHelper.sendErrorResponse(res, errorResponse); return ResponseHelper.sendErrorResponse(res, errorResponse);
} }
@ -217,11 +211,7 @@ oauth2CredentialController.get(
try { try {
encryptionKey = await UserSettings.getEncryptionKey(); encryptionKey = await UserSettings.getEncryptionKey();
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError((error as IDataObject).message as string);
(error as IDataObject).message as string,
undefined,
500,
);
} }
const mode: WorkflowExecuteMode = 'internal'; const mode: WorkflowExecuteMode = 'internal';
@ -250,10 +240,8 @@ oauth2CredentialController.get(
userId: req.user?.id, userId: req.user?.id,
credentialId: state.cid, credentialId: state.cid,
}); });
const errorResponse = new ResponseHelper.ResponseError( const errorResponse = new ResponseHelper.NotFoundError(
'The OAuth2 callback state is invalid!', 'The OAuth2 callback state is invalid!',
undefined,
404,
); );
return ResponseHelper.sendErrorResponse(res, errorResponse); return ResponseHelper.sendErrorResponse(res, errorResponse);
} }
@ -299,11 +287,7 @@ oauth2CredentialController.get(
userId: req.user?.id, userId: req.user?.id,
credentialId: state.cid, credentialId: state.cid,
}); });
const errorResponse = new ResponseHelper.ResponseError( const errorResponse = new ResponseHelper.NotFoundError('Unable to get access tokens!');
'Unable to get access tokens!',
undefined,
404,
);
return ResponseHelper.sendErrorResponse(res, errorResponse); return ResponseHelper.sendErrorResponse(res, errorResponse);
} }

View file

@ -46,13 +46,13 @@ EEWorkflowController.put(
!Array.isArray(shareWithIds) || !Array.isArray(shareWithIds) ||
!shareWithIds.every((userId) => typeof userId === 'string') !shareWithIds.every((userId) => typeof userId === 'string')
) { ) {
throw new ResponseHelper.ResponseError('Bad request', undefined, 400); throw new ResponseHelper.BadRequestError('Bad request');
} }
const { ownsWorkflow, workflow } = await EEWorkflows.isOwned(req.user, workflowId); const { ownsWorkflow, workflow } = await EEWorkflows.isOwned(req.user, workflowId);
if (!ownsWorkflow || !workflow) { if (!ownsWorkflow || !workflow) {
throw new ResponseHelper.ResponseError('Forbidden', undefined, 403); throw new ResponseHelper.UnauthorizedError('Forbidden');
} }
let newShareeIds: string[] = []; let newShareeIds: string[] = [];
@ -86,20 +86,14 @@ EEWorkflowController.get(
); );
if (!workflow) { if (!workflow) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(`Workflow with ID "${workflowId}" does not exist`);
`Workflow with ID "${workflowId}" does not exist`,
undefined,
404,
);
} }
const userSharing = workflow.shared?.find((shared) => shared.user.id === req.user.id); const userSharing = workflow.shared?.find((shared) => shared.user.id === req.user.id);
if (!userSharing && req.user.globalRole.name !== 'owner') { if (!userSharing && req.user.globalRole.name !== 'owner') {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.UnauthorizedError(
'It looks like you cannot access this workflow. Ask the owner to share it with you.', 'It looks like you cannot access this workflow. Ask the owner to share it with you.',
undefined,
403,
); );
} }
@ -143,10 +137,8 @@ EEWorkflowController.post(
try { try {
EEWorkflows.validateCredentialPermissionsToUser(newWorkflow, allCredentials); EEWorkflows.validateCredentialPermissionsToUser(newWorkflow, allCredentials);
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
'The workflow you are trying to save contains credentials that are not shared with you', 'The workflow you are trying to save contains credentials that are not shared with you',
undefined,
400,
); );
} }
@ -173,7 +165,7 @@ EEWorkflowController.post(
if (!savedWorkflow) { if (!savedWorkflow) {
LoggerProxy.error('Failed to create workflow', { userId: req.user.id }); LoggerProxy.error('Failed to create workflow', { userId: req.user.id });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
'An error occurred while saving your workflow. Please try again.', 'An error occurred while saving your workflow. Please try again.',
); );
} }

View file

@ -91,7 +91,7 @@ workflowsController.post(
if (!savedWorkflow) { if (!savedWorkflow) {
LoggerProxy.error('Failed to create workflow', { userId: req.user.id }); LoggerProxy.error('Failed to create workflow', { userId: req.user.id });
throw new ResponseHelper.ResponseError('Failed to save workflow'); throw new ResponseHelper.InternalServerError('Failed to save workflow');
} }
if (tagIds && !config.getEnv('workflowTagsDisabled') && savedWorkflow.tags) { if (tagIds && !config.getEnv('workflowTagsDisabled') && savedWorkflow.tags) {
@ -152,13 +152,11 @@ workflowsController.get(
`/from-url`, `/from-url`,
ResponseHelper.send(async (req: express.Request): Promise<IWorkflowResponse> => { ResponseHelper.send(async (req: express.Request): Promise<IWorkflowResponse> => {
if (req.query.url === undefined) { if (req.query.url === undefined) {
throw new ResponseHelper.ResponseError(`The parameter "url" is missing!`, undefined, 400); throw new ResponseHelper.BadRequestError(`The parameter "url" is missing!`);
} }
if (!/^http[s]?:\/\/.*\.json$/i.exec(req.query.url as string)) { if (!/^http[s]?:\/\/.*\.json$/i.exec(req.query.url as string)) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
`The parameter "url" is not valid! It does not seem to be a URL pointing to a n8n workflow JSON file.`, `The parameter "url" is not valid! It does not seem to be a URL pointing to a n8n workflow JSON file.`,
undefined,
400,
); );
} }
let workflowData: IWorkflowResponse | undefined; let workflowData: IWorkflowResponse | undefined;
@ -166,11 +164,7 @@ workflowsController.get(
const { data } = await axios.get<IWorkflowResponse>(req.query.url as string); const { data } = await axios.get<IWorkflowResponse>(req.query.url as string);
workflowData = data; workflowData = data;
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(`The URL does not point to valid JSON file!`);
`The URL does not point to valid JSON file!`,
undefined,
400,
);
} }
// Do a very basic check if it is really a n8n-workflow-json // Do a very basic check if it is really a n8n-workflow-json
@ -182,10 +176,8 @@ workflowsController.get(
typeof workflowData.connections !== 'object' || typeof workflowData.connections !== 'object' ||
Array.isArray(workflowData.connections) Array.isArray(workflowData.connections)
) { ) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
`The data in the file does not seem to be a n8n workflow JSON file!`, `The data in the file does not seem to be a n8n workflow JSON file!`,
undefined,
400,
); );
} }
@ -222,10 +214,8 @@ workflowsController.get(
workflowId, workflowId,
userId: req.user.id, userId: req.user.id,
}); });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
'Could not load the workflow - you can only access workflows owned by you', 'Could not load the workflow - you can only access workflows owned by you',
undefined,
404,
); );
} }
@ -297,10 +287,8 @@ workflowsController.delete(
workflowId, workflowId,
userId: req.user.id, userId: req.user.id,
}); });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
'Could not delete the workflow - you can only remove workflows owned by you', 'Could not delete the workflow - you can only remove workflows owned by you',
undefined,
400,
); );
} }

View file

@ -168,7 +168,7 @@ export class EEWorkflowsService extends WorkflowsService {
const previousVersion = await EEWorkflowsService.get({ id: parseInt(workflowId, 10) }); const previousVersion = await EEWorkflowsService.get({ id: parseInt(workflowId, 10) });
if (!previousVersion) { if (!previousVersion) {
throw new ResponseHelper.ResponseError('Workflow not found', undefined, 404); throw new ResponseHelper.NotFoundError('Workflow not found');
} }
const allCredentials = await EECredentials.getAll(user); const allCredentials = await EECredentials.getAll(user);
@ -180,10 +180,8 @@ export class EEWorkflowsService extends WorkflowsService {
allCredentials, allCredentials,
); );
} catch (error) { } catch (error) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
'Invalid workflow credentials - make sure you have access to all credentials and try again.', 'Invalid workflow credentials - make sure you have access to all credentials and try again.',
undefined,
400,
); );
} }
} }

View file

@ -124,10 +124,8 @@ export class WorkflowsService {
userId: user.id, userId: user.id,
filter, filter,
}); });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.InternalServerError(
`Parameter "filter" contained invalid JSON string.`, `Parameter "filter" contained invalid JSON string.`,
500,
500,
); );
} }
} }
@ -196,18 +194,14 @@ export class WorkflowsService {
workflowId, workflowId,
userId: user.id, userId: user.id,
}); });
throw new ResponseHelper.ResponseError( throw new ResponseHelper.NotFoundError(
'You do not have permission to update this workflow. Ask the owner to share it with you.', 'You do not have permission to update this workflow. Ask the owner to share it with you.',
undefined,
404,
); );
} }
if (!forceSave && workflow.hash !== '' && workflow.hash !== shared.workflow.hash) { if (!forceSave && workflow.hash !== '' && workflow.hash !== shared.workflow.hash) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
'We are sorry, but the workflow has been changed in the meantime. Please reload the workflow and try again.', 'We are sorry, but the workflow has been changed in the meantime. Please reload the workflow and try again.',
undefined,
400,
); );
} }
@ -290,10 +284,8 @@ export class WorkflowsService {
const updatedWorkflow = await Db.collections.Workflow.findOne(workflowId, options); const updatedWorkflow = await Db.collections.Workflow.findOne(workflowId, options);
if (updatedWorkflow === undefined) { if (updatedWorkflow === undefined) {
throw new ResponseHelper.ResponseError( throw new ResponseHelper.BadRequestError(
`Workflow with ID "${workflowId}" could not be found to be updated.`, `Workflow with ID "${workflowId}" could not be found to be updated.`,
undefined,
400,
); );
} }

View file

@ -11,10 +11,9 @@ interface ErrorReporter {
report: (error: Error | string, options?: ReportingOptions) => void; report: (error: Error | string, options?: ReportingOptions) => void;
} }
const isProduction = process.env.NODE_ENV === 'production';
const instance: ErrorReporter = { const instance: ErrorReporter = {
report: (error, options) => isProduction && Logger.error('ERROR', { error, options }), report: (error) =>
error instanceof Error && Logger.error(`${error.constructor.name}: ${error.message}`),
}; };
export function init(errorReporter: ErrorReporter) { export function init(errorReporter: ErrorReporter) {

View file

@ -183,6 +183,7 @@ importers:
passport-cookie: ^1.0.9 passport-cookie: ^1.0.9
passport-jwt: ^4.0.0 passport-jwt: ^4.0.0
pg: ^8.3.0 pg: ^8.3.0
picocolors: ^1.0.0
posthog-node: ^1.3.0 posthog-node: ^1.3.0
prom-client: ^13.1.0 prom-client: ^13.1.0
psl: ^1.8.0 psl: ^1.8.0
@ -263,6 +264,7 @@ importers:
passport-cookie: 1.0.9 passport-cookie: 1.0.9
passport-jwt: 4.0.0 passport-jwt: 4.0.0
pg: 8.8.0 pg: 8.8.0
picocolors: 1.0.0
posthog-node: 1.3.0 posthog-node: 1.3.0
prom-client: 13.2.0 prom-client: 13.2.0
psl: 1.9.0 psl: 1.9.0