mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
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:
parent
38f24a6184
commit
1c6178759c
|
@ -11,13 +11,14 @@ import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
import type { N8nInstanceType, IExternalHooksClass } from '@/Interfaces';
|
import type { N8nInstanceType, IExternalHooksClass } from '@/Interfaces';
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
import { send, sendErrorResponse, ServiceUnavailableError } from '@/ResponseHelper';
|
import { send, sendErrorResponse } from '@/ResponseHelper';
|
||||||
import { rawBodyReader, bodyParser, corsMiddleware } from '@/middlewares';
|
import { rawBodyReader, bodyParser, corsMiddleware } from '@/middlewares';
|
||||||
import { TestWebhooks } from '@/TestWebhooks';
|
import { TestWebhooks } from '@/TestWebhooks';
|
||||||
import { WaitingWebhooks } from '@/WaitingWebhooks';
|
import { WaitingWebhooks } from '@/WaitingWebhooks';
|
||||||
import { webhookRequestHandler } from '@/WebhookHelpers';
|
import { webhookRequestHandler } from '@/WebhookHelpers';
|
||||||
import { generateHostInstanceId } from './databases/utils/generators';
|
import { generateHostInstanceId } from './databases/utils/generators';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
|
import { ServiceUnavailableError } from './errors/response-errors/service-unavailable.error';
|
||||||
|
|
||||||
export abstract class AbstractServer {
|
export abstract class AbstractServer {
|
||||||
protected logger: Logger;
|
protected logger: Logger;
|
||||||
|
|
|
@ -42,7 +42,6 @@ import type {
|
||||||
WebhookAccessControlOptions,
|
WebhookAccessControlOptions,
|
||||||
WebhookRequest,
|
WebhookRequest,
|
||||||
} from '@/Interfaces';
|
} from '@/Interfaces';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
|
||||||
import * as WebhookHelpers from '@/WebhookHelpers';
|
import * as WebhookHelpers from '@/WebhookHelpers';
|
||||||
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
|
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
|
||||||
|
|
||||||
|
@ -68,6 +67,7 @@ import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.reposi
|
||||||
import { WorkflowRepository } from '@db/repositories/workflow.repository';
|
import { WorkflowRepository } from '@db/repositories/workflow.repository';
|
||||||
import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee';
|
import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee';
|
||||||
import { ActivationErrorsService } from '@/ActivationErrors.service';
|
import { ActivationErrorsService } from '@/ActivationErrors.service';
|
||||||
|
import { NotFoundError } from './errors/response-errors/not-found.error';
|
||||||
|
|
||||||
const WEBHOOK_PROD_UNREGISTERED_HINT =
|
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)";
|
"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) {
|
if (workflowData === null) {
|
||||||
throw new ResponseHelper.NotFoundError(
|
throw new NotFoundError(`Could not find workflow with id "${webhook.workflowId}"`);
|
||||||
`Could not find workflow with id "${webhook.workflowId}"`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflow = new Workflow({
|
const workflow = new Workflow({
|
||||||
|
@ -196,7 +194,7 @@ export class ActiveWorkflowRunner implements IWebhookManager {
|
||||||
const workflowStartNode = workflow.getNode(webhookData.node);
|
const workflowStartNode = workflow.getNode(webhookData.node);
|
||||||
|
|
||||||
if (workflowStartNode === null) {
|
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) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
@ -252,7 +250,7 @@ export class ActiveWorkflowRunner implements IWebhookManager {
|
||||||
|
|
||||||
const webhook = await this.webhookService.findWebhook(httpMethod, path);
|
const webhook = await this.webhookService.findWebhook(httpMethod, path);
|
||||||
if (webhook === null) {
|
if (webhook === null) {
|
||||||
throw new ResponseHelper.NotFoundError(
|
throw new NotFoundError(
|
||||||
webhookNotFoundErrorMessage(path, httpMethod),
|
webhookNotFoundErrorMessage(path, httpMethod),
|
||||||
WEBHOOK_PROD_UNREGISTERED_HINT,
|
WEBHOOK_PROD_UNREGISTERED_HINT,
|
||||||
);
|
);
|
||||||
|
|
|
@ -32,7 +32,6 @@ import type {
|
||||||
INodeTypes,
|
INodeTypes,
|
||||||
IWorkflowExecuteAdditionalData,
|
IWorkflowExecuteAdditionalData,
|
||||||
ICredentialTestFunctions,
|
ICredentialTestFunctions,
|
||||||
Severity,
|
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import {
|
import {
|
||||||
ICredentialsHelper,
|
ICredentialsHelper,
|
||||||
|
@ -55,6 +54,7 @@ import { isObjectLiteral } from './utils';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
||||||
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.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;
|
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()
|
@Service()
|
||||||
export class CredentialsHelper extends ICredentialsHelper {
|
export class CredentialsHelper extends ICredentialsHelper {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Authorized, Get, Post, RestController } from '@/decorators';
|
import { Authorized, Get, Post, RestController } from '@/decorators';
|
||||||
import { ExternalSecretsRequest } from '@/requests';
|
import { ExternalSecretsRequest } from '@/requests';
|
||||||
import { NotFoundError } from '@/ResponseHelper';
|
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { Service } from 'typedi';
|
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()
|
@Service()
|
||||||
@Authorized(['global', 'owner'])
|
@Authorized(['global', 'owner'])
|
||||||
|
@ -22,7 +23,7 @@ export class ExternalSecretsController {
|
||||||
try {
|
try {
|
||||||
return this.secretsService.getProvider(providerName);
|
return this.secretsService.getProvider(providerName);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ProviderNotFoundError) {
|
if (e instanceof ExternalSecretsProviderNotFoundError) {
|
||||||
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
|
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -41,7 +42,7 @@ export class ExternalSecretsController {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ProviderNotFoundError) {
|
if (e instanceof ExternalSecretsProviderNotFoundError) {
|
||||||
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
|
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -54,7 +55,7 @@ export class ExternalSecretsController {
|
||||||
try {
|
try {
|
||||||
await this.secretsService.saveProviderSettings(providerName, req.body, req.user.id);
|
await this.secretsService.saveProviderSettings(providerName, req.body, req.user.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ProviderNotFoundError) {
|
if (e instanceof ExternalSecretsProviderNotFoundError) {
|
||||||
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
|
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -68,7 +69,7 @@ export class ExternalSecretsController {
|
||||||
try {
|
try {
|
||||||
await this.secretsService.saveProviderConnected(providerName, req.body.connected);
|
await this.secretsService.saveProviderConnected(providerName, req.body.connected);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ProviderNotFoundError) {
|
if (e instanceof ExternalSecretsProviderNotFoundError) {
|
||||||
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
|
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -88,7 +89,7 @@ export class ExternalSecretsController {
|
||||||
}
|
}
|
||||||
return { updated: resp };
|
return { updated: resp };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ProviderNotFoundError) {
|
if (e instanceof ExternalSecretsProviderNotFoundError) {
|
||||||
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
|
throw new NotFoundError(`Could not find provider "${e.providerName}"`);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
|
|
|
@ -5,12 +5,7 @@ import type { IDataObject } from 'n8n-workflow';
|
||||||
import { deepCopy } from 'n8n-workflow';
|
import { deepCopy } from 'n8n-workflow';
|
||||||
import Container, { Service } from 'typedi';
|
import Container, { Service } from 'typedi';
|
||||||
import { ExternalSecretsManager } from './ExternalSecretsManager.ee';
|
import { ExternalSecretsManager } from './ExternalSecretsManager.ee';
|
||||||
|
import { ExternalSecretsProviderNotFoundError } from '@/errors/external-secrets-provider-not-found.error';
|
||||||
export class ProviderNotFoundError extends Error {
|
|
||||||
constructor(public providerName: string) {
|
|
||||||
super(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class ExternalSecretsService {
|
export class ExternalSecretsService {
|
||||||
|
@ -18,7 +13,7 @@ export class ExternalSecretsService {
|
||||||
const providerAndSettings =
|
const providerAndSettings =
|
||||||
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
|
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
|
||||||
if (!providerAndSettings) {
|
if (!providerAndSettings) {
|
||||||
throw new ProviderNotFoundError(providerName);
|
throw new ExternalSecretsProviderNotFoundError(providerName);
|
||||||
}
|
}
|
||||||
const { provider, settings } = providerAndSettings;
|
const { provider, settings } = providerAndSettings;
|
||||||
return {
|
return {
|
||||||
|
@ -110,7 +105,7 @@ export class ExternalSecretsService {
|
||||||
const providerAndSettings =
|
const providerAndSettings =
|
||||||
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
|
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
|
||||||
if (!providerAndSettings) {
|
if (!providerAndSettings) {
|
||||||
throw new ProviderNotFoundError(providerName);
|
throw new ExternalSecretsProviderNotFoundError(providerName);
|
||||||
}
|
}
|
||||||
const { settings } = providerAndSettings;
|
const { settings } = providerAndSettings;
|
||||||
const newData = this.unredact(data, settings.settings);
|
const newData = this.unredact(data, settings.settings);
|
||||||
|
@ -121,7 +116,7 @@ export class ExternalSecretsService {
|
||||||
const providerAndSettings =
|
const providerAndSettings =
|
||||||
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
|
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
|
||||||
if (!providerAndSettings) {
|
if (!providerAndSettings) {
|
||||||
throw new ProviderNotFoundError(providerName);
|
throw new ExternalSecretsProviderNotFoundError(providerName);
|
||||||
}
|
}
|
||||||
await Container.get(ExternalSecretsManager).setProviderConnected(providerName, connected);
|
await Container.get(ExternalSecretsManager).setProviderConnected(providerName, connected);
|
||||||
return this.getProvider(providerName);
|
return this.getProvider(providerName);
|
||||||
|
@ -135,7 +130,7 @@ export class ExternalSecretsService {
|
||||||
const providerAndSettings =
|
const providerAndSettings =
|
||||||
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
|
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
|
||||||
if (!providerAndSettings) {
|
if (!providerAndSettings) {
|
||||||
throw new ProviderNotFoundError(providerName);
|
throw new ExternalSecretsProviderNotFoundError(providerName);
|
||||||
}
|
}
|
||||||
const { settings } = providerAndSettings;
|
const { settings } = providerAndSettings;
|
||||||
const newData = this.unredact(data, settings.settings);
|
const newData = this.unredact(data, settings.settings);
|
||||||
|
@ -146,7 +141,7 @@ export class ExternalSecretsService {
|
||||||
const providerAndSettings =
|
const providerAndSettings =
|
||||||
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
|
Container.get(ExternalSecretsManager).getProviderWithSettings(providerName);
|
||||||
if (!providerAndSettings) {
|
if (!providerAndSettings) {
|
||||||
throw new ProviderNotFoundError(providerName);
|
throw new ExternalSecretsProviderNotFoundError(providerName);
|
||||||
}
|
}
|
||||||
return Container.get(ExternalSecretsManager).updateProvider(providerName);
|
return Container.get(ExternalSecretsManager).updateProvider(providerName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import { Container } from 'typedi';
|
||||||
import { Like } from 'typeorm';
|
import { Like } from 'typeorm';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import type { ExecutionPayload, ICredentialsDb, IWorkflowDb } from '@/Interfaces';
|
import type { ExecutionPayload, ICredentialsDb, IWorkflowDb } from '@/Interfaces';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
|
||||||
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
|
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
|
||||||
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
|
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
|
||||||
import type { TagEntity } from '@db/entities/TagEntity';
|
import type { TagEntity } from '@db/entities/TagEntity';
|
||||||
|
@ -20,6 +19,7 @@ import type { UserUpdatePayload } from '@/requests';
|
||||||
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
||||||
import { ExecutionRepository } from '@db/repositories/execution.repository';
|
import { ExecutionRepository } from '@db/repositories/execution.repository';
|
||||||
import { WorkflowRepository } from '@db/repositories/workflow.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
|
* Returns the base URL n8n is reachable from
|
||||||
|
@ -109,7 +109,7 @@ export async function validateEntity(
|
||||||
.join(' | ');
|
.join(' | ');
|
||||||
|
|
||||||
if (errorMessages) {
|
if (errorMessages) {
|
||||||
throw new ResponseHelper.BadRequestError(errorMessages);
|
throw new BadRequestError(errorMessages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,13 +29,14 @@ import {
|
||||||
isLdapCurrentAuthenticationMethod,
|
isLdapCurrentAuthenticationMethod,
|
||||||
setCurrentAuthenticationMethod,
|
setCurrentAuthenticationMethod,
|
||||||
} from '@/sso/ssoHelpers';
|
} from '@/sso/ssoHelpers';
|
||||||
import { BadRequestError, InternalServerError } from '../ResponseHelper';
|
|
||||||
import { RoleService } from '@/services/role.service';
|
import { RoleService } from '@/services/role.service';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
import { UserRepository } from '@db/repositories/user.repository';
|
import { UserRepository } from '@db/repositories/user.repository';
|
||||||
import { SettingsRepository } from '@db/repositories/settings.repository';
|
import { SettingsRepository } from '@db/repositories/settings.repository';
|
||||||
import { AuthProviderSyncHistoryRepository } from '@db/repositories/authProviderSyncHistory.repository';
|
import { AuthProviderSyncHistoryRepository } from '@db/repositories/authProviderSyncHistory.repository';
|
||||||
import { AuthIdentityRepository } from '@db/repositories/authIdentity.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
|
* Check whether the LDAP feature is disabled in the instance
|
||||||
|
|
|
@ -24,14 +24,6 @@ type FeatureReturnType = Partial<
|
||||||
} & { [K in NumericLicenseFeature]: number } & { [K in BooleanLicenseFeature]: boolean }
|
} & { [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()
|
@Service()
|
||||||
export class License {
|
export class License {
|
||||||
private manager: LicenseManager | undefined;
|
private manager: LicenseManager | undefined;
|
||||||
|
|
|
@ -12,14 +12,7 @@ import { LoadNodesAndCredentials } from './LoadNodesAndCredentials';
|
||||||
import { join, dirname } from 'path';
|
import { join, dirname } from 'path';
|
||||||
import { readdir } from 'fs/promises';
|
import { readdir } from 'fs/promises';
|
||||||
import type { Dirent } from 'fs';
|
import type { Dirent } from 'fs';
|
||||||
|
import { UnrecognizedNodeTypeError } from './errors/unrecognized-node-type.error';
|
||||||
class UnrecognizedNodeError extends Error {
|
|
||||||
severity = 'warning';
|
|
||||||
|
|
||||||
constructor(nodeType: string) {
|
|
||||||
super(`Unrecognized node type: ${nodeType}".`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class NodeTypes implements INodeTypes {
|
export class NodeTypes implements INodeTypes {
|
||||||
|
@ -75,7 +68,7 @@ export class NodeTypes implements INodeTypes {
|
||||||
return loadedNodes[type];
|
return loadedNodes[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnrecognizedNodeError(type);
|
throw new UnrecognizedNodeTypeError(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNodeTranslationPath({
|
async getNodeTranslationPath({
|
||||||
|
|
|
@ -12,76 +12,7 @@ import type {
|
||||||
IWorkflowDb,
|
IWorkflowDb,
|
||||||
} from '@/Interfaces';
|
} from '@/Interfaces';
|
||||||
import { inDevelopment } from '@/constants';
|
import { inDevelopment } from '@/constants';
|
||||||
|
import { ResponseError } from './errors/response-errors/abstract/response.error';
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sendSuccessResponse(
|
export function sendSuccessResponse(
|
||||||
res: Response,
|
res: Response,
|
||||||
|
|
|
@ -117,6 +117,8 @@ import { OrchestrationController } from './controllers/orchestration.controller'
|
||||||
import { WorkflowHistoryController } from './workflows/workflowHistory/workflowHistory.controller.ee';
|
import { WorkflowHistoryController } from './workflows/workflowHistory/workflowHistory.controller.ee';
|
||||||
import { InvitationController } from './controllers/invitation.controller';
|
import { InvitationController } from './controllers/invitation.controller';
|
||||||
import { CollaborationService } from './collaboration/collaboration.service';
|
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);
|
const exec = promisify(callbackExec);
|
||||||
|
|
||||||
|
@ -466,9 +468,7 @@ export class Server extends AbstractServer {
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
throw new ResponseHelper.BadRequestError(
|
throw new BadRequestError(`Workflow with ID "${workflowId}" could not be found.`);
|
||||||
`Workflow with ID "${workflowId}" could not be found.`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.activeWorkflowRunner.getActivationError(workflowId);
|
return this.activeWorkflowRunner.getActivationError(workflowId);
|
||||||
|
@ -491,7 +491,7 @@ export class Server extends AbstractServer {
|
||||||
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.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);
|
const sharedWorkflowIds = await getSharedWorkflowIds(req.user);
|
||||||
|
|
||||||
if (!sharedWorkflowIds.length) {
|
if (!sharedWorkflowIds.length) {
|
||||||
throw new ResponseHelper.NotFoundError('Execution not found');
|
throw new NotFoundError('Execution not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullExecutionData = await Container.get(ExecutionRepository).findSingleExecution(
|
const fullExecutionData = await Container.get(ExecutionRepository).findSingleExecution(
|
||||||
|
@ -637,7 +637,7 @@ export class Server extends AbstractServer {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!fullExecutionData) {
|
if (!fullExecutionData) {
|
||||||
throw new ResponseHelper.NotFoundError('Execution not found');
|
throw new NotFoundError('Execution not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.getEnv('executions.mode') === 'queue') {
|
if (config.getEnv('executions.mode') === 'queue') {
|
||||||
|
|
|
@ -20,9 +20,9 @@ import type {
|
||||||
} from '@/Interfaces';
|
} from '@/Interfaces';
|
||||||
import { Push } from '@/push';
|
import { Push } from '@/push';
|
||||||
import { NodeTypes } from '@/NodeTypes';
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
|
||||||
import * as WebhookHelpers from '@/WebhookHelpers';
|
import * as WebhookHelpers from '@/WebhookHelpers';
|
||||||
import { webhookNotFoundErrorMessage } from './utils';
|
import { webhookNotFoundErrorMessage } from './utils';
|
||||||
|
import { NotFoundError } from './errors/response-errors/not-found.error';
|
||||||
|
|
||||||
const WEBHOOK_TEST_UNREGISTERED_HINT =
|
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)";
|
"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) {
|
if (webhookData === undefined) {
|
||||||
// The requested webhook is not registered
|
// The requested webhook is not registered
|
||||||
const methods = await this.getWebhookMethods(path);
|
const methods = await this.getWebhookMethods(path);
|
||||||
throw new ResponseHelper.NotFoundError(
|
throw new NotFoundError(
|
||||||
webhookNotFoundErrorMessage(path, httpMethod, methods),
|
webhookNotFoundErrorMessage(path, httpMethod, methods),
|
||||||
WEBHOOK_TEST_UNREGISTERED_HINT,
|
WEBHOOK_TEST_UNREGISTERED_HINT,
|
||||||
);
|
);
|
||||||
|
@ -108,7 +108,7 @@ export class TestWebhooks implements IWebhookManager {
|
||||||
if (testWebhookData[webhookKey] === undefined) {
|
if (testWebhookData[webhookKey] === undefined) {
|
||||||
// The requested webhook is not registered
|
// The requested webhook is not registered
|
||||||
const methods = await this.getWebhookMethods(path);
|
const methods = await this.getWebhookMethods(path);
|
||||||
throw new ResponseHelper.NotFoundError(
|
throw new NotFoundError(
|
||||||
webhookNotFoundErrorMessage(path, httpMethod, methods),
|
webhookNotFoundErrorMessage(path, httpMethod, methods),
|
||||||
WEBHOOK_TEST_UNREGISTERED_HINT,
|
WEBHOOK_TEST_UNREGISTERED_HINT,
|
||||||
);
|
);
|
||||||
|
@ -121,7 +121,7 @@ export class TestWebhooks implements IWebhookManager {
|
||||||
// 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.NotFoundError('Could not find node to process webhook.');
|
throw new NotFoundError('Could not find node to process webhook.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
|
@ -168,10 +168,7 @@ export class TestWebhooks implements IWebhookManager {
|
||||||
const webhookMethods = this.activeWebhooks.getWebhookMethods(path);
|
const webhookMethods = this.activeWebhooks.getWebhookMethods(path);
|
||||||
if (!webhookMethods.length) {
|
if (!webhookMethods.length) {
|
||||||
// The requested webhook is not registered
|
// The requested webhook is not registered
|
||||||
throw new ResponseHelper.NotFoundError(
|
throw new NotFoundError(webhookNotFoundErrorMessage(path), WEBHOOK_TEST_UNREGISTERED_HINT);
|
||||||
webhookNotFoundErrorMessage(path),
|
|
||||||
WEBHOOK_TEST_UNREGISTERED_HINT,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return webhookMethods;
|
return webhookMethods;
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { In } from 'typeorm';
|
||||||
import { compare, genSaltSync, hash } from 'bcryptjs';
|
import { compare, genSaltSync, hash } from 'bcryptjs';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
|
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
|
||||||
import type { WhereClause } from '@/Interfaces';
|
import type { WhereClause } from '@/Interfaces';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import { MAX_PASSWORD_LENGTH, MIN_PASSWORD_LENGTH } 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 { getWebhookBaseUrl } from '@/WebhookHelpers';
|
||||||
import { RoleService } from '@/services/role.service';
|
import { RoleService } from '@/services/role.service';
|
||||||
import { UserRepository } from '@db/repositories/user.repository';
|
import { UserRepository } from '@db/repositories/user.repository';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
export function isSharingEnabled(): boolean {
|
export function isSharingEnabled(): boolean {
|
||||||
return Container.get(License).isSharingEnabled();
|
return Container.get(License).isSharingEnabled();
|
||||||
|
@ -43,7 +43,7 @@ export function generateUserInviteUrl(inviterId: string, inviteeId: string): str
|
||||||
// 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.BadRequestError('Password is mandatory');
|
throw new BadRequestError('Password is mandatory');
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasInvalidLength =
|
const hasInvalidLength =
|
||||||
|
@ -70,7 +70,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.BadRequestError(message.join(' '));
|
throw new BadRequestError(message.join(' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
return password;
|
return password;
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { NodeHelpers, Workflow } from 'n8n-workflow';
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import type express from 'express';
|
import type express from 'express';
|
||||||
|
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
|
||||||
import * as WebhookHelpers from '@/WebhookHelpers';
|
import * as WebhookHelpers from '@/WebhookHelpers';
|
||||||
import { NodeTypes } from '@/NodeTypes';
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
import type {
|
import type {
|
||||||
|
@ -15,6 +14,8 @@ import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'
|
||||||
import { ExecutionRepository } from '@db/repositories/execution.repository';
|
import { ExecutionRepository } from '@db/repositories/execution.repository';
|
||||||
import { OwnershipService } from './services/ownership.service';
|
import { OwnershipService } from './services/ownership.service';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
|
import { ConflictError } from './errors/response-errors/conflict.error';
|
||||||
|
import { NotFoundError } from './errors/response-errors/not-found.error';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class WaitingWebhooks implements IWebhookManager {
|
export class WaitingWebhooks implements IWebhookManager {
|
||||||
|
@ -43,11 +44,11 @@ export class WaitingWebhooks implements IWebhookManager {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!execution) {
|
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) {
|
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;
|
const lastNodeExecuted = execution.data.resultData.lastNodeExecuted as string;
|
||||||
|
@ -79,12 +80,12 @@ export class WaitingWebhooks implements IWebhookManager {
|
||||||
try {
|
try {
|
||||||
workflowOwner = await this.ownershipService.getWorkflowOwnerCached(workflowData.id!);
|
workflowOwner = await this.ownershipService.getWorkflowOwnerCached(workflowData.id!);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new ResponseHelper.NotFoundError('Could not find workflow');
|
throw new NotFoundError('Could not find workflow');
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowStartNode = workflow.getNode(lastNodeExecuted);
|
const workflowStartNode = workflow.getNode(lastNodeExecuted);
|
||||||
if (workflowStartNode === null) {
|
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);
|
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.
|
// 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 workflow for execution "${executionId}" does not contain a waiting webhook with a matching path/method.`;
|
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;
|
const runExecutionData = execution.data;
|
||||||
|
|
|
@ -63,6 +63,9 @@ import { OwnershipService } from './services/ownership.service';
|
||||||
import { parseBody } from './middlewares';
|
import { parseBody } from './middlewares';
|
||||||
import { WorkflowsService } from './workflows/workflows.services';
|
import { WorkflowsService } from './workflows/workflows.services';
|
||||||
import { Logger } from './Logger';
|
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);
|
const pipeline = promisify(stream.pipeline);
|
||||||
|
|
||||||
|
@ -237,7 +240,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.InternalServerError(errorMessage);
|
throw new InternalServerError(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
const additionalKeys: IWorkflowDataProxyAdditionalKeys = {
|
const additionalKeys: IWorkflowDataProxyAdditionalKeys = {
|
||||||
|
@ -254,7 +257,7 @@ export async function executeWebhook(
|
||||||
try {
|
try {
|
||||||
user = await Container.get(OwnershipService).getWorkflowOwnerCached(workflowData.id);
|
user = await Container.get(OwnershipService).getWorkflowOwnerCached(workflowData.id);
|
||||||
} catch (error) {
|
} 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.
|
// 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.InternalServerError(errorMessage);
|
throw new 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
|
||||||
|
@ -781,13 +784,13 @@ 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.InternalServerError(e.message);
|
throw new InternalServerError(e.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return executionId;
|
return executionId;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const error =
|
const error =
|
||||||
e instanceof ResponseHelper.UnprocessableRequestError
|
e instanceof UnprocessableRequestError
|
||||||
? e
|
? e
|
||||||
: new Error('There was a problem executing the workflow', { cause: e });
|
: new Error('There was a problem executing the workflow', { cause: e });
|
||||||
if (didSendResponse) throw error;
|
if (didSendResponse) throw error;
|
||||||
|
|
|
@ -4,11 +4,12 @@ import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants';
|
||||||
import type { JwtPayload, JwtToken } from '@/Interfaces';
|
import type { JwtPayload, JwtToken } from '@/Interfaces';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
|
||||||
import { License } from '@/License';
|
import { License } from '@/License';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
import { UserRepository } from '@db/repositories/user.repository';
|
import { UserRepository } from '@db/repositories/user.repository';
|
||||||
import { JwtService } from '@/services/jwt.service';
|
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 {
|
export function issueJWT(user: User): JwtToken {
|
||||||
const { id, email, password } = user;
|
const { id, email, password } = user;
|
||||||
|
@ -26,7 +27,7 @@ export function issueJWT(user: User): JwtToken {
|
||||||
!user.isOwner &&
|
!user.isOwner &&
|
||||||
!isWithinUsersLimit
|
!isWithinUsersLimit
|
||||||
) {
|
) {
|
||||||
throw new ResponseHelper.UnauthorizedError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);
|
throw new UnauthorizedError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);
|
||||||
}
|
}
|
||||||
if (password) {
|
if (password) {
|
||||||
payload.password = createHash('sha256')
|
payload.password = createHash('sha256')
|
||||||
|
@ -63,7 +64,7 @@ export async function resolveJwtContent(jwtPayload: JwtPayload): Promise<User> {
|
||||||
// currently only LDAP users during synchronization
|
// currently only LDAP users during synchronization
|
||||||
// can be set to disabled
|
// can be set to disabled
|
||||||
if (user?.disabled) {
|
if (user?.disabled) {
|
||||||
throw new ResponseHelper.AuthError('Unauthorized');
|
throw new AuthError('Unauthorized');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user || jwtPayload.password !== passwordHash || user.email !== jwtPayload.email) {
|
if (!user || jwtPayload.password !== passwordHash || user.email !== jwtPayload.email) {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import { compareHash } from '@/UserManagement/UserManagementHelper';
|
import { compareHash } from '@/UserManagement/UserManagementHelper';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import { isLdapLoginEnabled } from '@/Ldap/helpers';
|
import { isLdapLoginEnabled } from '@/Ldap/helpers';
|
||||||
import { UserRepository } from '@db/repositories/user.repository';
|
import { UserRepository } from '@db/repositories/user.repository';
|
||||||
|
import { AuthError } from '@/errors/response-errors/auth.error';
|
||||||
|
|
||||||
export const handleEmailLogin = async (
|
export const handleEmailLogin = async (
|
||||||
email: string,
|
email: string,
|
||||||
|
@ -27,7 +27,7 @@ export const handleEmailLogin = async (
|
||||||
user_id: user.id,
|
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;
|
return undefined;
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { EDITOR_UI_DIST_DIR, LICENSE_FEATURES } from '@/constants';
|
||||||
import { eventBus } from '@/eventbus';
|
import { eventBus } from '@/eventbus';
|
||||||
import { BaseCommand } from './BaseCommand';
|
import { BaseCommand } from './BaseCommand';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import { License, FeatureNotLicensedError } from '@/License';
|
import { License } from '@/License';
|
||||||
import type { IConfig } from '@oclif/config';
|
import type { IConfig } from '@oclif/config';
|
||||||
import { SingleMainSetup } from '@/services/orchestration/main/SingleMainSetup';
|
import { SingleMainSetup } from '@/services/orchestration/main/SingleMainSetup';
|
||||||
import { OrchestrationHandlerMainService } from '@/services/orchestration/main/orchestration.handler.main.service';
|
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 { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee';
|
||||||
import { SettingsRepository } from '@db/repositories/settings.repository';
|
import { SettingsRepository } from '@db/repositories/settings.repository';
|
||||||
import { ExecutionRepository } from '@db/repositories/execution.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
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
|
||||||
const open = require('open');
|
const open = require('open');
|
||||||
|
|
|
@ -40,6 +40,7 @@ import type { IConfig } from '@oclif/config';
|
||||||
import { OrchestrationHandlerWorkerService } from '@/services/orchestration/worker/orchestration.handler.worker.service';
|
import { OrchestrationHandlerWorkerService } from '@/services/orchestration/worker/orchestration.handler.worker.service';
|
||||||
import { OrchestrationWorkerService } from '@/services/orchestration/worker/orchestration.worker.service';
|
import { OrchestrationWorkerService } from '@/services/orchestration/worker/orchestration.worker.service';
|
||||||
import type { WorkerJobStatusSummary } from '../services/orchestration/worker/types';
|
import type { WorkerJobStatusSummary } from '../services/orchestration/worker/types';
|
||||||
|
import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error';
|
||||||
|
|
||||||
export class Worker extends BaseCommand {
|
export class Worker extends BaseCommand {
|
||||||
static description = '\nStarts a n8n worker';
|
static description = '\nStarts a n8n worker';
|
||||||
|
@ -413,7 +414,7 @@ export class Worker extends BaseCommand {
|
||||||
await connection.query('SELECT 1');
|
await connection.query('SELECT 1');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error('No Database connection!', e as Error);
|
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);
|
return ResponseHelper.sendErrorResponse(res, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,7 +425,7 @@ export class Worker extends BaseCommand {
|
||||||
await Worker.jobQueue.ping();
|
await Worker.jobQueue.ping();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error('No Redis connection!', e as Error);
|
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);
|
return ResponseHelper.sendErrorResponse(res, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
|
import { NotStringArrayError } from '@/errors/not-string-array.error';
|
||||||
import type { SchemaObj } from 'convict';
|
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>) => {
|
export const ensureStringArray = (values: string[], { env }: SchemaObj<string>) => {
|
||||||
if (!env) throw new Error(`Missing env: ${env}`);
|
if (!env) throw new Error(`Missing env: ${env}`);
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,6 @@ import validator from 'validator';
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import { Authorized, Get, Post, RestController } from '@/decorators';
|
import { Authorized, Get, Post, RestController } from '@/decorators';
|
||||||
import {
|
|
||||||
AuthError,
|
|
||||||
BadRequestError,
|
|
||||||
InternalServerError,
|
|
||||||
UnauthorizedError,
|
|
||||||
} from '@/ResponseHelper';
|
|
||||||
import { issueCookie, resolveJwt } from '@/auth/jwt';
|
import { issueCookie, resolveJwt } from '@/auth/jwt';
|
||||||
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants';
|
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants';
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
|
@ -27,6 +21,10 @@ import { License } from '@/License';
|
||||||
import { UserService } from '@/services/user.service';
|
import { UserService } from '@/services/user.service';
|
||||||
import { MfaService } from '@/Mfa/mfa.service';
|
import { MfaService } from '@/Mfa/mfa.service';
|
||||||
import { Logger } from '@/Logger';
|
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()
|
@Service()
|
||||||
@RestController()
|
@RestController()
|
||||||
|
|
|
@ -8,12 +8,13 @@ import {
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
import { Authorized, Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
|
import { Authorized, Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
|
||||||
import { NodeRequest } from '@/requests';
|
import { NodeRequest } from '@/requests';
|
||||||
import { BadRequestError, InternalServerError } from '@/ResponseHelper';
|
|
||||||
import type { InstalledPackages } from '@db/entities/InstalledPackages';
|
import type { InstalledPackages } from '@db/entities/InstalledPackages';
|
||||||
import type { CommunityPackages } from '@/Interfaces';
|
import type { CommunityPackages } from '@/Interfaces';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import { Push } from '@/push';
|
import { Push } from '@/push';
|
||||||
import { CommunityPackagesService } from '@/services/communityPackages.service';
|
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 {
|
const {
|
||||||
PACKAGE_NOT_INSTALLED,
|
PACKAGE_NOT_INSTALLED,
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { Authorized, Get, Middleware, RestController } from '@/decorators';
|
||||||
import { getBase } from '@/WorkflowExecuteAdditionalData';
|
import { getBase } from '@/WorkflowExecuteAdditionalData';
|
||||||
import { DynamicNodeParametersService } from '@/services/dynamicNodeParameters.service';
|
import { DynamicNodeParametersService } from '@/services/dynamicNodeParameters.service';
|
||||||
import { DynamicNodeParametersRequest } from '@/requests';
|
import { DynamicNodeParametersRequest } from '@/requests';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
const assertMethodName: RequestHandler = (req, res, next) => {
|
const assertMethodName: RequestHandler = (req, res, next) => {
|
||||||
const { methodName } = req.query as DynamicNodeParametersRequest.BaseRequest['query'];
|
const { methodName } = req.query as DynamicNodeParametersRequest.BaseRequest['query'];
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import Container, { Service } from 'typedi';
|
import Container, { Service } from 'typedi';
|
||||||
import { Authorized, NoAuthRequired, Post, RestController } from '@/decorators';
|
import { Authorized, NoAuthRequired, Post, RestController } from '@/decorators';
|
||||||
import { BadRequestError, UnauthorizedError } from '@/ResponseHelper';
|
|
||||||
import { issueCookie } from '@/auth/jwt';
|
import { issueCookie } from '@/auth/jwt';
|
||||||
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
|
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
|
@ -16,6 +15,8 @@ import { hashPassword, validatePassword } from '@/UserManagement/UserManagementH
|
||||||
import { PostHogClient } from '@/posthog';
|
import { PostHogClient } from '@/posthog';
|
||||||
import type { User } from '@/databases/entities/User';
|
import type { User } from '@/databases/entities/User';
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@RestController('/invitations')
|
@RestController('/invitations')
|
||||||
|
|
|
@ -4,9 +4,9 @@ import { getLdapConfig, getLdapSynchronizations, updateLdapConfig } from '@/Ldap
|
||||||
import { LdapService } from '@/Ldap/LdapService.ee';
|
import { LdapService } from '@/Ldap/LdapService.ee';
|
||||||
import { LdapSync } from '@/Ldap/LdapSync.ee';
|
import { LdapSync } from '@/Ldap/LdapSync.ee';
|
||||||
import { LdapConfiguration } from '@/Ldap/types';
|
import { LdapConfiguration } from '@/Ldap/types';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
|
||||||
import { NON_SENSIBLE_LDAP_CONFIG_PROPERTIES } from '@/Ldap/constants';
|
import { NON_SENSIBLE_LDAP_CONFIG_PROPERTIES } from '@/Ldap/constants';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
@Authorized(['global', 'owner'])
|
@Authorized(['global', 'owner'])
|
||||||
@RestController('/ldap')
|
@RestController('/ldap')
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { Service } from 'typedi';
|
||||||
import { randomBytes } from 'crypto';
|
import { randomBytes } from 'crypto';
|
||||||
import { Authorized, Delete, Get, Patch, Post, RestController } from '@/decorators';
|
import { Authorized, Delete, Get, Patch, Post, RestController } from '@/decorators';
|
||||||
import { compareHash, hashPassword, validatePassword } from '@/UserManagement/UserManagementHelper';
|
import { compareHash, hashPassword, validatePassword } from '@/UserManagement/UserManagementHelper';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
|
||||||
import { validateEntity } from '@/GenericHelpers';
|
import { validateEntity } from '@/GenericHelpers';
|
||||||
import { issueCookie } from '@/auth/jwt';
|
import { issueCookie } from '@/auth/jwt';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
|
@ -21,6 +20,7 @@ import { UserService } from '@/services/user.service';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@Authorized()
|
@Authorized()
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import { Authorized, Delete, Get, Post, RestController } from '@/decorators';
|
import { Authorized, Delete, Get, Post, RestController } from '@/decorators';
|
||||||
import { AuthenticatedRequest, MFA } from '@/requests';
|
import { AuthenticatedRequest, MFA } from '@/requests';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
|
||||||
import { MfaService } from '@/Mfa/mfa.service';
|
import { MfaService } from '@/Mfa/mfa.service';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@Authorized()
|
@Authorized()
|
||||||
|
|
|
@ -9,12 +9,13 @@ import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.
|
||||||
import type { ICredentialsDb } from '@/Interfaces';
|
import type { ICredentialsDb } from '@/Interfaces';
|
||||||
import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';
|
import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';
|
||||||
import type { OAuthRequest } from '@/requests';
|
import type { OAuthRequest } from '@/requests';
|
||||||
import { BadRequestError, NotFoundError } from '@/ResponseHelper';
|
|
||||||
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
|
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
|
||||||
import { CredentialsHelper } from '@/CredentialsHelper';
|
import { CredentialsHelper } from '@/CredentialsHelper';
|
||||||
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
|
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export abstract class AbstractOAuthController {
|
export abstract class AbstractOAuthController {
|
||||||
|
|
|
@ -8,8 +8,10 @@ import { createHmac } from 'crypto';
|
||||||
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
|
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
|
||||||
import { Authorized, Get, RestController } from '@/decorators';
|
import { Authorized, Get, RestController } from '@/decorators';
|
||||||
import { OAuthRequest } from '@/requests';
|
import { OAuthRequest } from '@/requests';
|
||||||
import { NotFoundError, sendErrorResponse, ServiceUnavailableError } from '@/ResponseHelper';
|
import { sendErrorResponse } from '@/ResponseHelper';
|
||||||
import { AbstractOAuthController } from './abstractOAuth.controller';
|
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 {
|
interface OAuth1CredentialData {
|
||||||
signatureMethod: 'HMAC-SHA256' | 'HMAC-SHA512' | 'HMAC-SHA1';
|
signatureMethod: 'HMAC-SHA256' | 'HMAC-SHA512' | 'HMAC-SHA1';
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import { validateEntity } from '@/GenericHelpers';
|
import { validateEntity } from '@/GenericHelpers';
|
||||||
import { Authorized, Post, RestController } from '@/decorators';
|
import { Authorized, Post, RestController } from '@/decorators';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
|
||||||
import { hashPassword, validatePassword } from '@/UserManagement/UserManagementHelper';
|
import { hashPassword, validatePassword } from '@/UserManagement/UserManagementHelper';
|
||||||
import { issueCookie } from '@/auth/jwt';
|
import { issueCookie } from '@/auth/jwt';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
|
@ -12,6 +11,7 @@ import { SettingsRepository } from '@db/repositories/settings.repository';
|
||||||
import { PostHogClient } from '@/posthog';
|
import { PostHogClient } from '@/posthog';
|
||||||
import { UserService } from '@/services/user.service';
|
import { UserService } from '@/services/user.service';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
@Authorized(['global', 'owner'])
|
@Authorized(['global', 'owner'])
|
||||||
@RestController('/owner')
|
@RestController('/owner')
|
||||||
|
|
|
@ -5,13 +5,6 @@ import { IsNull, Not } from 'typeorm';
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
|
|
||||||
import { Get, Post, RestController } from '@/decorators';
|
import { Get, Post, RestController } from '@/decorators';
|
||||||
import {
|
|
||||||
BadRequestError,
|
|
||||||
InternalServerError,
|
|
||||||
NotFoundError,
|
|
||||||
UnauthorizedError,
|
|
||||||
UnprocessableRequestError,
|
|
||||||
} from '@/ResponseHelper';
|
|
||||||
import {
|
import {
|
||||||
getInstanceBaseUrl,
|
getInstanceBaseUrl,
|
||||||
hashPassword,
|
hashPassword,
|
||||||
|
@ -29,6 +22,11 @@ import { MfaService } from '@/Mfa/mfa.service';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
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({
|
const throttle = rateLimit({
|
||||||
windowMs: 5 * 60 * 1000, // 5 minutes
|
windowMs: 5 * 60 * 1000, // 5 minutes
|
||||||
|
|
|
@ -2,9 +2,9 @@ import { Request, Response, NextFunction } from 'express';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { Authorized, Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
|
import { Authorized, Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
|
||||||
import { TagService } from '@/services/tag.service';
|
import { TagService } from '@/services/tag.service';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
|
||||||
import { TagsRequest } from '@/requests';
|
import { TagsRequest } from '@/requests';
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/tags')
|
@RestController('/tags')
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { ICredentialTypes } from 'n8n-workflow';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { access } from 'fs/promises';
|
import { access } from 'fs/promises';
|
||||||
import { Authorized, Get, RestController } from '@/decorators';
|
import { Authorized, Get, RestController } from '@/decorators';
|
||||||
import { BadRequestError, InternalServerError } from '@/ResponseHelper';
|
|
||||||
import { Config } from '@/config';
|
import { Config } from '@/config';
|
||||||
import { NODES_BASE_DIR } from '@/constants';
|
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 CREDENTIAL_TRANSLATIONS_DIR = 'n8n-nodes-base/dist/credentials/translations';
|
||||||
export const NODE_HEADERS_PATH = join(NODES_BASE_DIR, 'dist/nodes/headers');
|
export const NODE_HEADERS_PATH = join(NODES_BASE_DIR, 'dist/nodes/headers');
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { User } from '@db/entities/User';
|
||||||
import { SharedCredentials } from '@db/entities/SharedCredentials';
|
import { SharedCredentials } from '@db/entities/SharedCredentials';
|
||||||
import { SharedWorkflow } from '@db/entities/SharedWorkflow';
|
import { SharedWorkflow } from '@db/entities/SharedWorkflow';
|
||||||
import { Authorized, Delete, Get, RestController, Patch } from '@/decorators';
|
import { Authorized, Delete, Get, RestController, Patch } from '@/decorators';
|
||||||
import { BadRequestError, NotFoundError, UnauthorizedError } from '@/ResponseHelper';
|
|
||||||
import { ListQuery, UserRequest, UserSettingsUpdatePayload } from '@/requests';
|
import { ListQuery, UserRequest, UserSettingsUpdatePayload } from '@/requests';
|
||||||
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
||||||
import { IExternalHooksClass, IInternalHooksClass } from '@/Interfaces';
|
import { IExternalHooksClass, IInternalHooksClass } from '@/Interfaces';
|
||||||
|
@ -17,6 +16,9 @@ import { RoleService } from '@/services/role.service';
|
||||||
import { UserService } from '@/services/user.service';
|
import { UserService } from '@/services/user.service';
|
||||||
import { listQueryMiddleware } from '@/middlewares';
|
import { listQueryMiddleware } from '@/middlewares';
|
||||||
import { Logger } from '@/Logger';
|
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()
|
@Authorized()
|
||||||
@RestController('/users')
|
@RestController('/users')
|
||||||
|
|
|
@ -7,9 +7,9 @@ import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.reposi
|
||||||
import { WorkflowStatisticsRepository } from '@db/repositories/workflowStatistics.repository';
|
import { WorkflowStatisticsRepository } from '@db/repositories/workflowStatistics.repository';
|
||||||
import { ExecutionRequest } from '@/requests';
|
import { ExecutionRequest } from '@/requests';
|
||||||
import { whereClause } from '@/UserManagement/UserManagementHelper';
|
import { whereClause } from '@/UserManagement/UserManagementHelper';
|
||||||
import { NotFoundError } from '@/ResponseHelper';
|
|
||||||
import type { IWorkflowStatisticsDataLoaded } from '@/Interfaces';
|
import type { IWorkflowStatisticsDataLoaded } from '@/Interfaces';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
|
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||||
|
|
||||||
interface WorkflowStatisticsData<T> {
|
interface WorkflowStatisticsData<T> {
|
||||||
productionSuccess: T;
|
productionSuccess: T;
|
||||||
|
|
|
@ -11,6 +11,9 @@ import { OwnershipService } from '@/services/ownership.service';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
|
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();
|
export const EECredentialsController = express.Router();
|
||||||
|
|
||||||
|
@ -40,7 +43,7 @@ EECredentialsController.get(
|
||||||
)) as CredentialsEntity;
|
)) as CredentialsEntity;
|
||||||
|
|
||||||
if (!credential) {
|
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',
|
'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);
|
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.UnauthorizedError('Forbidden.');
|
throw new UnauthorizedError('Forbidden.');
|
||||||
}
|
}
|
||||||
|
|
||||||
credential = Container.get(OwnershipService).addOwnedByAndSharedWith(credential);
|
credential = Container.get(OwnershipService).addOwnedByAndSharedWith(credential);
|
||||||
|
@ -82,7 +85,7 @@ EECredentialsController.post(
|
||||||
const sharing = await EECredentials.getSharing(req.user, credentialId);
|
const sharing = await EECredentials.getSharing(req.user, credentialId);
|
||||||
if (!ownsCredential) {
|
if (!ownsCredential) {
|
||||||
if (!sharing) {
|
if (!sharing) {
|
||||||
throw new ResponseHelper.UnauthorizedError('Forbidden');
|
throw new UnauthorizedError('Forbidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
const decryptedData = EECredentials.decrypt(sharing.credentials);
|
const decryptedData = EECredentials.decrypt(sharing.credentials);
|
||||||
|
@ -115,12 +118,12 @@ EECredentialsController.put(
|
||||||
!Array.isArray(shareWithIds) ||
|
!Array.isArray(shareWithIds) ||
|
||||||
!shareWithIds.every((userId) => typeof userId === 'string')
|
!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);
|
const { ownsCredential, credential } = await EECredentials.isOwned(req.user, credentialId);
|
||||||
if (!ownsCredential || !credential) {
|
if (!ownsCredential || !credential) {
|
||||||
throw new ResponseHelper.UnauthorizedError('Forbidden');
|
throw new UnauthorizedError('Forbidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
let amountRemoved: number | null = null;
|
let amountRemoved: number | null = null;
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { Container } from 'typedi';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import { listQueryMiddleware } from '@/middlewares';
|
import { listQueryMiddleware } from '@/middlewares';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
|
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||||
|
|
||||||
export const credentialsController = express.Router();
|
export const credentialsController = express.Router();
|
||||||
credentialsController.use('/', EECredentialsController);
|
credentialsController.use('/', EECredentialsController);
|
||||||
|
@ -60,9 +61,7 @@ credentialsController.get(
|
||||||
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.NotFoundError(
|
throw new NotFoundError(`Credential with ID "${credentialId}" could not be found.`);
|
||||||
`Credential with ID "${credentialId}" could not be found.`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { credentials: credential } = sharing;
|
const { credentials: credential } = sharing;
|
||||||
|
@ -145,7 +144,7 @@ credentialsController.patch(
|
||||||
userId: req.user.id,
|
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',
|
'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);
|
const responseData = await CredentialsService.update(credentialId, newCredentialData);
|
||||||
|
|
||||||
if (responseData === null) {
|
if (responseData === null) {
|
||||||
throw new ResponseHelper.NotFoundError(
|
throw new NotFoundError(`Credential ID "${credentialId}" could not be found to be updated.`);
|
||||||
`Credential ID "${credentialId}" could not be found to be updated.`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the encrypted data as it is not needed in the frontend
|
// Remove the encrypted data as it is not needed in the frontend
|
||||||
|
@ -197,7 +194,7 @@ credentialsController.delete(
|
||||||
userId: req.user.id,
|
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',
|
'Credential to be deleted not found. You can only removed credentials owned by you',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,11 @@ import { SourceControlPreferencesService } from './sourceControlPreferences.serv
|
||||||
import type { SourceControlPreferences } from './types/sourceControlPreferences';
|
import type { SourceControlPreferences } from './types/sourceControlPreferences';
|
||||||
import type { SourceControlledFile } from './types/sourceControlledFile';
|
import type { SourceControlledFile } from './types/sourceControlledFile';
|
||||||
import { SOURCE_CONTROL_API_ROOT, SOURCE_CONTROL_DEFAULT_BRANCH } from './constants';
|
import { SOURCE_CONTROL_API_ROOT, SOURCE_CONTROL_DEFAULT_BRANCH } from './constants';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
|
||||||
import type { ImportResult } from './types/importResult';
|
import type { ImportResult } from './types/importResult';
|
||||||
import { InternalHooks } from '../../InternalHooks';
|
import { InternalHooks } from '../../InternalHooks';
|
||||||
import { getRepoType } from './sourceControlHelper.ee';
|
import { getRepoType } from './sourceControlHelper.ee';
|
||||||
import { SourceControlGetStatus } from './types/sourceControlGetStatus';
|
import { SourceControlGetStatus } from './types/sourceControlGetStatus';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@RestController(`/${SOURCE_CONTROL_API_ROOT}`)
|
@RestController(`/${SOURCE_CONTROL_API_ROOT}`)
|
||||||
|
|
|
@ -17,7 +17,6 @@ import {
|
||||||
import { SourceControlGitService } from './sourceControlGit.service.ee';
|
import { SourceControlGitService } from './sourceControlGit.service.ee';
|
||||||
import type { PushResult } from 'simple-git';
|
import type { PushResult } from 'simple-git';
|
||||||
import { SourceControlExportService } from './sourceControlExport.service.ee';
|
import { SourceControlExportService } from './sourceControlExport.service.ee';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
|
||||||
import type { ImportResult } from './types/importResult';
|
import type { ImportResult } from './types/importResult';
|
||||||
import type { SourceControlPushWorkFolder } from './types/sourceControlPushWorkFolder';
|
import type { SourceControlPushWorkFolder } from './types/sourceControlPushWorkFolder';
|
||||||
import type { SourceControllPullOptions } from './types/sourceControlPullWorkFolder';
|
import type { SourceControllPullOptions } from './types/sourceControlPullWorkFolder';
|
||||||
|
@ -35,6 +34,7 @@ import type { ExportableCredential } from './types/exportableCredential';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import { TagRepository } from '@db/repositories/tag.repository';
|
import { TagRepository } from '@db/repositories/tag.repository';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SourceControlService {
|
export class SourceControlService {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { Container, Service } from 'typedi';
|
import { Container, Service } from 'typedi';
|
||||||
|
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
|
||||||
import { VariablesRequest } from '@/requests';
|
import { VariablesRequest } from '@/requests';
|
||||||
import { Authorized, Delete, Get, Licensed, Patch, Post, RestController } from '@/decorators';
|
import { Authorized, Delete, Get, Licensed, Patch, Post, RestController } from '@/decorators';
|
||||||
import {
|
import { VariablesService } from './variables.service.ee';
|
||||||
VariablesService,
|
|
||||||
VariablesLicenseError,
|
|
||||||
VariablesValidationError,
|
|
||||||
} from './variables.service.ee';
|
|
||||||
import { Logger } from '@/Logger';
|
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()
|
@Service()
|
||||||
@Authorized()
|
@Authorized()
|
||||||
|
@ -31,17 +31,17 @@ export class VariablesController {
|
||||||
this.logger.info('Attempt to update a variable blocked due to lack of permissions', {
|
this.logger.info('Attempt to update a variable blocked due to lack of permissions', {
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
});
|
});
|
||||||
throw new ResponseHelper.UnauthorizedError('Unauthorized');
|
throw new UnauthorizedError('Unauthorized');
|
||||||
}
|
}
|
||||||
const variable = req.body;
|
const variable = req.body;
|
||||||
delete variable.id;
|
delete variable.id;
|
||||||
try {
|
try {
|
||||||
return await Container.get(VariablesService).create(variable);
|
return await Container.get(VariablesService).create(variable);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof VariablesLicenseError) {
|
if (error instanceof VariableCountLimitReachedError) {
|
||||||
throw new ResponseHelper.BadRequestError(error.message);
|
throw new BadRequestError(error.message);
|
||||||
} else if (error instanceof VariablesValidationError) {
|
} else if (error instanceof VariableValidationError) {
|
||||||
throw new ResponseHelper.BadRequestError(error.message);
|
throw new BadRequestError(error.message);
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ export class VariablesController {
|
||||||
const id = req.params.id;
|
const id = req.params.id;
|
||||||
const variable = await Container.get(VariablesService).getCached(id);
|
const variable = await Container.get(VariablesService).getCached(id);
|
||||||
if (variable === null) {
|
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;
|
return variable;
|
||||||
}
|
}
|
||||||
|
@ -66,17 +66,17 @@ export class VariablesController {
|
||||||
id,
|
id,
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
});
|
});
|
||||||
throw new ResponseHelper.UnauthorizedError('Unauthorized');
|
throw new UnauthorizedError('Unauthorized');
|
||||||
}
|
}
|
||||||
const variable = req.body;
|
const variable = req.body;
|
||||||
delete variable.id;
|
delete variable.id;
|
||||||
try {
|
try {
|
||||||
return await Container.get(VariablesService).update(id, variable);
|
return await Container.get(VariablesService).update(id, variable);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof VariablesLicenseError) {
|
if (error instanceof VariableCountLimitReachedError) {
|
||||||
throw new ResponseHelper.BadRequestError(error.message);
|
throw new BadRequestError(error.message);
|
||||||
} else if (error instanceof VariablesValidationError) {
|
} else if (error instanceof VariableValidationError) {
|
||||||
throw new ResponseHelper.BadRequestError(error.message);
|
throw new BadRequestError(error.message);
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ export class VariablesController {
|
||||||
id,
|
id,
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
});
|
});
|
||||||
throw new ResponseHelper.UnauthorizedError('Unauthorized');
|
throw new UnauthorizedError('Unauthorized');
|
||||||
}
|
}
|
||||||
await this.variablesService.delete(id);
|
await this.variablesService.delete(id);
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,8 @@ import { canCreateNewVariable } from './enviromentHelpers';
|
||||||
import { CacheService } from '@/services/cache.service';
|
import { CacheService } from '@/services/cache.service';
|
||||||
import { VariablesRepository } from '@db/repositories/variables.repository';
|
import { VariablesRepository } from '@db/repositories/variables.repository';
|
||||||
import type { DeepPartial } from 'typeorm';
|
import type { DeepPartial } from 'typeorm';
|
||||||
|
import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error';
|
||||||
export class VariablesLicenseError extends Error {}
|
import { VariableValidationError } from '@/errors/variable-validation.error';
|
||||||
export class VariablesValidationError extends Error {}
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class VariablesService {
|
export class VariablesService {
|
||||||
|
@ -59,19 +58,19 @@ export class VariablesService {
|
||||||
|
|
||||||
validateVariable(variable: Omit<Variables, 'id'>): void {
|
validateVariable(variable: Omit<Variables, 'id'>): void {
|
||||||
if (variable.key.length > 50) {
|
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) {
|
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) {
|
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> {
|
async create(variable: Omit<Variables, 'id'>): Promise<Variables> {
|
||||||
if (!canCreateNewVariable(await this.getCount())) {
|
if (!canCreateNewVariable(await this.getCount())) {
|
||||||
throw new VariablesLicenseError('Variables limit reached');
|
throw new VariableCountLimitReachedError('Variables limit reached');
|
||||||
}
|
}
|
||||||
this.validateVariable(variable);
|
this.validateVariable(variable);
|
||||||
|
|
||||||
|
|
10
packages/cli/src/errors/credential-not-found.error.ts
Normal file
10
packages/cli/src/errors/credential-not-found.error.ts
Normal 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';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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}`);
|
||||||
|
}
|
||||||
|
}
|
10
packages/cli/src/errors/feature-not-licensed.error.ts
Normal file
10
packages/cli/src/errors/feature-not-licensed.error.ts
Normal 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.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
3
packages/cli/src/errors/invalid-role.error.ts
Normal file
3
packages/cli/src/errors/invalid-role.error.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class InvalidRoleError extends ApplicationError {}
|
7
packages/cli/src/errors/not-string-array.error.ts
Normal file
7
packages/cli/src/errors/not-string-array.error.ts
Normal 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.`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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';
|
||||||
|
}
|
||||||
|
}
|
7
packages/cli/src/errors/response-errors/auth.error.ts
Normal file
7
packages/cli/src/errors/response-errors/auth.error.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { ResponseError } from './abstract/response.error';
|
||||||
|
|
||||||
|
export class BadRequestError extends ResponseError {
|
||||||
|
constructor(message: string, errorCode?: number) {
|
||||||
|
super(message, 400, errorCode);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { ResponseError } from './abstract/response.error';
|
||||||
|
|
||||||
|
export class InternalServerError extends ResponseError {
|
||||||
|
constructor(message: string, errorCode = 500) {
|
||||||
|
super(message, 500, errorCode);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { ResponseError } from './abstract/response.error';
|
||||||
|
|
||||||
|
export class ServiceUnavailableError extends ResponseError {
|
||||||
|
constructor(message: string, errorCode = 503) {
|
||||||
|
super(message, 503, errorCode);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class SharedWorkflowNotFoundError extends ApplicationError {}
|
9
packages/cli/src/errors/unrecognized-node-type.error.ts
Normal file
9
packages/cli/src/errors/unrecognized-node-type.error.ts
Normal 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}".`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class VariableCountLimitReachedError extends ApplicationError {}
|
3
packages/cli/src/errors/variable-validation.error.ts
Normal file
3
packages/cli/src/errors/variable-validation.error.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class VariableValidationError extends ApplicationError {}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class WorkflowHistoryVersionNotFoundError extends ApplicationError {}
|
|
@ -9,7 +9,6 @@ import {
|
||||||
MessageEventBusDestinationSyslog,
|
MessageEventBusDestinationSyslog,
|
||||||
} from './MessageEventBusDestination/MessageEventBusDestinationSyslog.ee';
|
} from './MessageEventBusDestination/MessageEventBusDestinationSyslog.ee';
|
||||||
import { MessageEventBusDestinationWebhook } from './MessageEventBusDestination/MessageEventBusDestinationWebhook.ee';
|
import { MessageEventBusDestinationWebhook } from './MessageEventBusDestination/MessageEventBusDestinationWebhook.ee';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
|
||||||
import type {
|
import type {
|
||||||
MessageEventBusDestinationWebhookOptions,
|
MessageEventBusDestinationWebhookOptions,
|
||||||
MessageEventBusDestinationOptions,
|
MessageEventBusDestinationOptions,
|
||||||
|
@ -20,6 +19,7 @@ import type { MessageEventBusDestination } from './MessageEventBusDestination/Me
|
||||||
import type { DeleteResult } from 'typeorm';
|
import type { DeleteResult } from 'typeorm';
|
||||||
import { AuthenticatedRequest } from '@/requests';
|
import { AuthenticatedRequest } from '@/requests';
|
||||||
import { logStreamingLicensedMiddleware } from './middleware/logStreamingEnabled.middleware.ee';
|
import { logStreamingLicensedMiddleware } from './middleware/logStreamingEnabled.middleware.ee';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// TypeGuards
|
// TypeGuards
|
||||||
|
|
|
@ -9,13 +9,13 @@ import type { EventMessageTypes, FailedEventSummary } from './EventMessageClasse
|
||||||
import { eventNamesAll } from './EventMessageClasses';
|
import { eventNamesAll } from './EventMessageClasses';
|
||||||
import type { EventMessageAuditOptions } from './EventMessageClasses/EventMessageAudit';
|
import type { EventMessageAuditOptions } from './EventMessageClasses/EventMessageAudit';
|
||||||
import { EventMessageAudit } from './EventMessageClasses/EventMessageAudit';
|
import { EventMessageAudit } from './EventMessageClasses/EventMessageAudit';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
|
||||||
import type { IRunExecutionData } from 'n8n-workflow';
|
import type { IRunExecutionData } from 'n8n-workflow';
|
||||||
import { EventMessageTypeNames } from 'n8n-workflow';
|
import { EventMessageTypeNames } from 'n8n-workflow';
|
||||||
import type { EventMessageNodeOptions } from './EventMessageClasses/EventMessageNode';
|
import type { EventMessageNodeOptions } from './EventMessageClasses/EventMessageNode';
|
||||||
import { EventMessageNode } from './EventMessageClasses/EventMessageNode';
|
import { EventMessageNode } from './EventMessageClasses/EventMessageNode';
|
||||||
import { recoverExecutionDataFromEventLogMessages } from './MessageEventBus/recoverEvents';
|
import { recoverExecutionDataFromEventLogMessages } from './MessageEventBus/recoverEvents';
|
||||||
import { RestController, Get, Post, Authorized } from '@/decorators';
|
import { RestController, Get, Post, Authorized } from '@/decorators';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// TypeGuards
|
// TypeGuards
|
||||||
|
|
|
@ -15,7 +15,6 @@ import type {
|
||||||
import { NodeTypes } from '@/NodeTypes';
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
import { Queue } from '@/Queue';
|
import { Queue } from '@/Queue';
|
||||||
import type { ExecutionRequest } from '@/requests';
|
import type { ExecutionRequest } from '@/requests';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
|
||||||
import { getSharedWorkflowIds } from '@/WorkflowHelpers';
|
import { getSharedWorkflowIds } from '@/WorkflowHelpers';
|
||||||
import { WorkflowRunner } from '@/WorkflowRunner';
|
import { WorkflowRunner } from '@/WorkflowRunner';
|
||||||
import * as GenericHelpers from '@/GenericHelpers';
|
import * as GenericHelpers from '@/GenericHelpers';
|
||||||
|
@ -24,6 +23,8 @@ import { getStatusUsingPreviousExecutionStatusMethod } from './executionHelpers'
|
||||||
import { ExecutionRepository } from '@db/repositories/execution.repository';
|
import { ExecutionRepository } from '@db/repositories/execution.repository';
|
||||||
import { WorkflowRepository } from '@db/repositories/workflow.repository';
|
import { WorkflowRepository } from '@db/repositories/workflow.repository';
|
||||||
import { Logger } from '@/Logger';
|
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 {
|
export interface IGetExecutionsQueryFilter {
|
||||||
id?: FindOperator<string> | string;
|
id?: FindOperator<string> | string;
|
||||||
|
@ -114,9 +115,7 @@ export class ExecutionsService {
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
filter: req.query.filter,
|
filter: req.query.filter,
|
||||||
});
|
});
|
||||||
throw new ResponseHelper.InternalServerError(
|
throw new InternalServerError('Parameter "filter" contained invalid JSON string.');
|
||||||
'Parameter "filter" contained invalid JSON string.',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,9 +230,7 @@ export class ExecutionsService {
|
||||||
executionId,
|
executionId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
throw new ResponseHelper.NotFoundError(
|
throw new NotFoundError(`The execution with the ID "${executionId}" does not exist.`);
|
||||||
`The execution with the ID "${executionId}" does not exist.`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (execution.finished) {
|
if (execution.finished) {
|
||||||
|
@ -351,9 +348,7 @@ export class ExecutionsService {
|
||||||
requestFilters = requestFiltersRaw as IGetExecutionsQueryFilter;
|
requestFilters = requestFiltersRaw as IGetExecutionsQueryFilter;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new ResponseHelper.InternalServerError(
|
throw new InternalServerError('Parameter "filter" contained invalid JSON string.');
|
||||||
'Parameter "filter" contained invalid JSON string.',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ import { LicenseService } from './License.service';
|
||||||
import { License } from '@/License';
|
import { License } from '@/License';
|
||||||
import type { AuthenticatedRequest, LicenseRequest } from '@/requests';
|
import type { AuthenticatedRequest, LicenseRequest } from '@/requests';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
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();
|
export const licenseController = express.Router();
|
||||||
|
|
||||||
|
@ -24,9 +26,7 @@ licenseController.use((req: AuthenticatedRequest, res, next) => {
|
||||||
});
|
});
|
||||||
ResponseHelper.sendErrorResponse(
|
ResponseHelper.sendErrorResponse(
|
||||||
res,
|
res,
|
||||||
new ResponseHelper.UnauthorizedError(
|
new UnauthorizedError('Only an instance owner may activate or renew a license'),
|
||||||
'Only an instance owner may activate or renew a license',
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ licenseController.post(
|
||||||
Container.get(Logger).error(message, { stack: error.stack ?? 'n/a' });
|
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
|
// Return the read data, plus the management JWT
|
||||||
|
@ -113,7 +113,7 @@ licenseController.post(
|
||||||
// not awaiting so as not to make the endpoint hang
|
// not awaiting so as not to make the endpoint hang
|
||||||
void Container.get(InternalHooks).onLicenseRenewAttempt({ success: false });
|
void Container.get(InternalHooks).onLicenseRenewAttempt({ success: false });
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
throw new ResponseHelper.BadRequestError(error.message);
|
throw new BadRequestError(error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Parser as XmlParser } from 'xml2js';
|
||||||
import { parseIncomingMessage } from 'n8n-core';
|
import { parseIncomingMessage } from 'n8n-core';
|
||||||
import { jsonParse } from 'n8n-workflow';
|
import { jsonParse } from 'n8n-workflow';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { UnprocessableRequestError } from '@/ResponseHelper';
|
import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error';
|
||||||
|
|
||||||
const xmlParser = new XmlParser({
|
const xmlParser = new XmlParser({
|
||||||
async: true,
|
async: true,
|
||||||
|
|
|
@ -3,8 +3,7 @@ import { RoleRepository } from '@db/repositories/role.repository';
|
||||||
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
||||||
import { CacheService } from './cache.service';
|
import { CacheService } from './cache.service';
|
||||||
import type { RoleNames, RoleScopes } from '@db/entities/Role';
|
import type { RoleNames, RoleScopes } from '@db/entities/Role';
|
||||||
|
import { InvalidRoleError } from '@/errors/invalid-role.error';
|
||||||
class InvalidRoleError extends Error {}
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class RoleService {
|
export class RoleService {
|
||||||
|
|
|
@ -15,8 +15,8 @@ import { UserManagementMailer } from '@/UserManagement/email';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import { RoleService } from '@/services/role.service';
|
import { RoleService } from '@/services/role.service';
|
||||||
import { ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
|
import { ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
|
||||||
import { InternalServerError } from '@/ResponseHelper';
|
|
||||||
import type { UserRequest } from '@/requests';
|
import type { UserRequest } from '@/requests';
|
||||||
|
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import {
|
||||||
} from '../middleware/samlEnabledMiddleware';
|
} from '../middleware/samlEnabledMiddleware';
|
||||||
import { SamlService } from '../saml.service.ee';
|
import { SamlService } from '../saml.service.ee';
|
||||||
import { SamlConfiguration } from '../types/requests';
|
import { SamlConfiguration } from '../types/requests';
|
||||||
import { AuthError, BadRequestError } from '@/ResponseHelper';
|
|
||||||
import { getInitSSOFormView } from '../views/initSsoPost';
|
import { getInitSSOFormView } from '../views/initSsoPost';
|
||||||
import { issueCookie } from '@/auth/jwt';
|
import { issueCookie } from '@/auth/jwt';
|
||||||
import { validate } from 'class-validator';
|
import { validate } from 'class-validator';
|
||||||
|
@ -27,6 +26,8 @@ import { getSamlConnectionTestFailedView } from '../views/samlConnectionTestFail
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import querystring from 'querystring';
|
import querystring from 'querystring';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
import { AuthError } from '@/errors/response-errors/auth.error';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@RestController('/sso/saml')
|
@RestController('/sso/saml')
|
||||||
|
|
|
@ -2,7 +2,6 @@ import type express from 'express';
|
||||||
import Container, { Service } from 'typedi';
|
import Container, { Service } from 'typedi';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import { jsonParse } from 'n8n-workflow';
|
import { jsonParse } from 'n8n-workflow';
|
||||||
import { AuthError, BadRequestError } from '@/ResponseHelper';
|
|
||||||
import { getServiceProviderInstance } from './serviceProvider.ee';
|
import { getServiceProviderInstance } from './serviceProvider.ee';
|
||||||
import type { SamlUserAttributes } from './types/samlUserAttributes';
|
import type { SamlUserAttributes } from './types/samlUserAttributes';
|
||||||
import { isSsoJustInTimeProvisioningEnabled } from '../ssoHelpers';
|
import { isSsoJustInTimeProvisioningEnabled } from '../ssoHelpers';
|
||||||
|
@ -29,6 +28,8 @@ import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
import { UserRepository } from '@db/repositories/user.repository';
|
import { UserRepository } from '@db/repositories/user.repository';
|
||||||
import { SettingsRepository } from '@db/repositories/settings.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()
|
@Service()
|
||||||
export class SamlService {
|
export class SamlService {
|
||||||
|
|
|
@ -3,7 +3,6 @@ import config from '@/config';
|
||||||
import { AuthIdentity } from '@db/entities/AuthIdentity';
|
import { AuthIdentity } from '@db/entities/AuthIdentity';
|
||||||
import { User } from '@db/entities/User';
|
import { User } from '@db/entities/User';
|
||||||
import { License } from '@/License';
|
import { License } from '@/License';
|
||||||
import { AuthError, InternalServerError } from '@/ResponseHelper';
|
|
||||||
import { hashPassword } from '@/UserManagement/UserManagementHelper';
|
import { hashPassword } from '@/UserManagement/UserManagementHelper';
|
||||||
import type { SamlPreferences } from './types/samlPreferences';
|
import type { SamlPreferences } from './types/samlPreferences';
|
||||||
import type { SamlUserAttributes } from './types/samlUserAttributes';
|
import type { SamlUserAttributes } from './types/samlUserAttributes';
|
||||||
|
@ -21,6 +20,9 @@ import type { SamlConfiguration } from './types/requests';
|
||||||
import { RoleService } from '@/services/role.service';
|
import { RoleService } from '@/services/role.service';
|
||||||
import { UserRepository } from '@db/repositories/user.repository';
|
import { UserRepository } from '@db/repositories/user.repository';
|
||||||
import { AuthIdentityRepository } from '@db/repositories/authIdentity.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
|
* Check whether the SAML feature is licensed and enabled in the instance
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import { Authorized, RestController, Get, Middleware } from '@/decorators';
|
import { Authorized, RestController, Get, Middleware } from '@/decorators';
|
||||||
import { WorkflowHistoryRequest } from '@/requests';
|
import { WorkflowHistoryRequest } from '@/requests';
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import {
|
import { WorkflowHistoryService } from './workflowHistory.service.ee';
|
||||||
HistoryVersionNotFoundError,
|
|
||||||
SharedWorkflowNotFoundError,
|
|
||||||
WorkflowHistoryService,
|
|
||||||
} from './workflowHistory.service.ee';
|
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
import { isWorkflowHistoryEnabled, isWorkflowHistoryLicensed } from './workflowHistoryHelper.ee';
|
import { isWorkflowHistoryEnabled, isWorkflowHistoryLicensed } from './workflowHistoryHelper.ee';
|
||||||
import { NotFoundError } from '@/ResponseHelper';
|
|
||||||
import { paginationListQueryMiddleware } from '@/middlewares/listQuery/pagination';
|
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;
|
const DEFAULT_TAKE = 20;
|
||||||
|
|
||||||
|
@ -67,7 +66,7 @@ export class WorkflowHistoryController {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof SharedWorkflowNotFoundError) {
|
if (e instanceof SharedWorkflowNotFoundError) {
|
||||||
throw new NotFoundError('Could not find workflow');
|
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 new NotFoundError('Could not find version');
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
|
|
|
@ -7,9 +7,8 @@ import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repo
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import { isWorkflowHistoryEnabled } from './workflowHistoryHelper.ee';
|
import { isWorkflowHistoryEnabled } from './workflowHistoryHelper.ee';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
|
import { SharedWorkflowNotFoundError } from '@/errors/shared-workflow-not-found.error';
|
||||||
export class SharedWorkflowNotFoundError extends Error {}
|
import { WorkflowHistoryVersionNotFoundError } from '@/errors/workflow-history-version-not-found.error';
|
||||||
export class HistoryVersionNotFoundError extends Error {}
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class WorkflowHistoryService {
|
export class WorkflowHistoryService {
|
||||||
|
@ -36,7 +35,7 @@ export class WorkflowHistoryService {
|
||||||
): Promise<Array<Omit<WorkflowHistory, 'nodes' | 'connections'>>> {
|
): Promise<Array<Omit<WorkflowHistory, 'nodes' | 'connections'>>> {
|
||||||
const sharedWorkflow = await this.getSharedWorkflow(user, workflowId);
|
const sharedWorkflow = await this.getSharedWorkflow(user, workflowId);
|
||||||
if (!sharedWorkflow) {
|
if (!sharedWorkflow) {
|
||||||
throw new SharedWorkflowNotFoundError();
|
throw new SharedWorkflowNotFoundError('');
|
||||||
}
|
}
|
||||||
return this.workflowHistoryRepository.find({
|
return this.workflowHistoryRepository.find({
|
||||||
where: {
|
where: {
|
||||||
|
@ -52,7 +51,7 @@ export class WorkflowHistoryService {
|
||||||
async getVersion(user: User, workflowId: string, versionId: string): Promise<WorkflowHistory> {
|
async getVersion(user: User, workflowId: string, versionId: string): Promise<WorkflowHistory> {
|
||||||
const sharedWorkflow = await this.getSharedWorkflow(user, workflowId);
|
const sharedWorkflow = await this.getSharedWorkflow(user, workflowId);
|
||||||
if (!sharedWorkflow) {
|
if (!sharedWorkflow) {
|
||||||
throw new SharedWorkflowNotFoundError();
|
throw new SharedWorkflowNotFoundError('');
|
||||||
}
|
}
|
||||||
const hist = await this.workflowHistoryRepository.findOne({
|
const hist = await this.workflowHistoryRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
|
@ -61,7 +60,7 @@ export class WorkflowHistoryService {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!hist) {
|
if (!hist) {
|
||||||
throw new HistoryVersionNotFoundError();
|
throw new WorkflowHistoryVersionNotFoundError('');
|
||||||
}
|
}
|
||||||
return hist;
|
return hist;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,10 @@ import { listQueryMiddleware } from '@/middlewares';
|
||||||
import { TagService } from '@/services/tag.service';
|
import { TagService } from '@/services/tag.service';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee';
|
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();
|
export const EEWorkflowController = express.Router();
|
||||||
|
|
||||||
|
@ -52,13 +56,13 @@ EEWorkflowController.put(
|
||||||
!Array.isArray(shareWithIds) ||
|
!Array.isArray(shareWithIds) ||
|
||||||
!shareWithIds.every((userId) => typeof userId === 'string')
|
!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);
|
const { ownsWorkflow, workflow } = await EEWorkflows.isOwned(req.user, workflowId);
|
||||||
|
|
||||||
if (!ownsWorkflow || !workflow) {
|
if (!ownsWorkflow || !workflow) {
|
||||||
throw new ResponseHelper.UnauthorizedError('Forbidden');
|
throw new UnauthorizedError('Forbidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
let newShareeIds: string[] = [];
|
let newShareeIds: string[] = [];
|
||||||
|
@ -101,13 +105,13 @@ EEWorkflowController.get(
|
||||||
const workflow = await EEWorkflows.get({ id: workflowId }, { relations });
|
const workflow = await EEWorkflows.get({ id: workflowId }, { relations });
|
||||||
|
|
||||||
if (!workflow) {
|
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);
|
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.UnauthorizedError(
|
throw new UnauthorizedError(
|
||||||
'You do not have permission to access this workflow. Ask the owner to share it with you',
|
'You do not have permission to access this workflow. Ask the owner to share it with you',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -156,7 +160,7 @@ EEWorkflowController.post(
|
||||||
try {
|
try {
|
||||||
EEWorkflows.validateCredentialPermissionsToUser(newWorkflow, allCredentials);
|
EEWorkflows.validateCredentialPermissionsToUser(newWorkflow, allCredentials);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new ResponseHelper.BadRequestError(
|
throw new 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',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -181,7 +185,7 @@ EEWorkflowController.post(
|
||||||
|
|
||||||
if (!savedWorkflow) {
|
if (!savedWorkflow) {
|
||||||
Container.get(Logger).error('Failed to create workflow', { userId: req.user.id });
|
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.',
|
'An error occurred while saving your workflow. Please try again.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,9 @@ import { TagService } from '@/services/tag.service';
|
||||||
import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee';
|
import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
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();
|
export const workflowsController = express.Router();
|
||||||
workflowsController.use('/', EEWorkflowController);
|
workflowsController.use('/', EEWorkflowController);
|
||||||
|
@ -84,7 +87,7 @@ workflowsController.post(
|
||||||
|
|
||||||
if (!savedWorkflow) {
|
if (!savedWorkflow) {
|
||||||
Container.get(Logger).error('Failed to create workflow', { userId: req.user.id });
|
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(
|
await Container.get(WorkflowHistoryService).saveVersion(
|
||||||
|
@ -160,10 +163,10 @@ 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.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)) {
|
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.',
|
'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);
|
const { data } = await axios.get<IWorkflowResponse>(req.query.url as string);
|
||||||
workflowData = data;
|
workflowData = data;
|
||||||
} catch (error) {
|
} 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
|
// Do a very basic check if it is really a n8n-workflow-json
|
||||||
|
@ -183,7 +186,7 @@ workflowsController.get(
|
||||||
typeof workflowData.connections !== 'object' ||
|
typeof workflowData.connections !== 'object' ||
|
||||||
Array.isArray(workflowData.connections)
|
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!',
|
'The data in the file does not seem to be a n8n workflow JSON file!',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -221,7 +224,7 @@ workflowsController.get(
|
||||||
workflowId,
|
workflowId,
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
});
|
});
|
||||||
throw new ResponseHelper.NotFoundError(
|
throw new 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',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -271,7 +274,7 @@ workflowsController.delete(
|
||||||
workflowId,
|
workflowId,
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
});
|
});
|
||||||
throw new ResponseHelper.BadRequestError(
|
throw new 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',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import type { DeleteResult, EntityManager } from 'typeorm';
|
import type { DeleteResult, EntityManager } from 'typeorm';
|
||||||
import { In, Not } from 'typeorm';
|
import { In, Not } from 'typeorm';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
|
||||||
import * as WorkflowHelpers from '@/WorkflowHelpers';
|
import * as WorkflowHelpers from '@/WorkflowHelpers';
|
||||||
import { SharedWorkflow } from '@db/entities/SharedWorkflow';
|
import { SharedWorkflow } from '@db/entities/SharedWorkflow';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
|
@ -17,6 +16,8 @@ import { RoleService } from '@/services/role.service';
|
||||||
import Container from 'typedi';
|
import Container from 'typedi';
|
||||||
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
|
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
|
||||||
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
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 {
|
export class EEWorkflowsService extends WorkflowsService {
|
||||||
static async isOwned(
|
static async isOwned(
|
||||||
|
@ -170,7 +171,7 @@ export class EEWorkflowsService extends WorkflowsService {
|
||||||
const previousVersion = await EEWorkflowsService.get({ id: workflowId });
|
const previousVersion = await EEWorkflowsService.get({ id: workflowId });
|
||||||
|
|
||||||
if (!previousVersion) {
|
if (!previousVersion) {
|
||||||
throw new ResponseHelper.NotFoundError('Workflow not found');
|
throw new NotFoundError('Workflow not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const allCredentials = await CredentialsService.getMany(user);
|
const allCredentials = await CredentialsService.getMany(user);
|
||||||
|
@ -183,9 +184,9 @@ export class EEWorkflowsService extends WorkflowsService {
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof NodeOperationError) {
|
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.',
|
'Invalid workflow credentials - make sure you have access to all credentials and try again.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { In, Like } from 'typeorm';
|
||||||
import pick from 'lodash/pick';
|
import pick from 'lodash/pick';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
|
||||||
import * as WorkflowHelpers from '@/WorkflowHelpers';
|
import * as WorkflowHelpers from '@/WorkflowHelpers';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import type { SharedWorkflow } from '@db/entities/SharedWorkflow';
|
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 { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
||||||
import { WorkflowTagMappingRepository } from '@db/repositories/workflowTagMapping.repository';
|
import { WorkflowTagMappingRepository } from '@db/repositories/workflowTagMapping.repository';
|
||||||
import { ExecutionRepository } from '@db/repositories/execution.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 {
|
export class WorkflowsService {
|
||||||
static async getSharing(
|
static async getSharing(
|
||||||
|
@ -208,7 +209,7 @@ export class WorkflowsService {
|
||||||
workflowId,
|
workflowId,
|
||||||
userId: user.id,
|
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.',
|
'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 !== '' &&
|
||||||
workflow.versionId !== shared.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.',
|
'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,
|
100,
|
||||||
);
|
);
|
||||||
|
@ -330,7 +331,7 @@ export class WorkflowsService {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (updatedWorkflow === null) {
|
if (updatedWorkflow === null) {
|
||||||
throw new ResponseHelper.BadRequestError(
|
throw new BadRequestError(
|
||||||
`Workflow with ID "${workflowId}" could not be found to be updated.`,
|
`Workflow with ID "${workflowId}" could not be found to be updated.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -368,7 +369,7 @@ export class WorkflowsService {
|
||||||
message = message ?? (error as Error).message;
|
message = message ?? (error as Error).message;
|
||||||
|
|
||||||
// Now return the original error for UI to display
|
// Now return the original error for UI to display
|
||||||
throw new ResponseHelper.BadRequestError(message);
|
throw new BadRequestError(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import type { PublicUser } from '@/Interfaces';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import { MeController } from '@/controllers/me.controller';
|
import { MeController } from '@/controllers/me.controller';
|
||||||
import { AUTH_COOKIE_NAME } from '@/constants';
|
import { AUTH_COOKIE_NAME } from '@/constants';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
|
||||||
import type { AuthenticatedRequest, MeRequest } from '@/requests';
|
import type { AuthenticatedRequest, MeRequest } from '@/requests';
|
||||||
import { UserService } from '@/services/user.service';
|
import { UserService } from '@/services/user.service';
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
|
@ -14,6 +13,7 @@ import { InternalHooks } from '@/InternalHooks';
|
||||||
import { License } from '@/License';
|
import { License } from '@/License';
|
||||||
import { badPasswords } from '../shared/testData';
|
import { badPasswords } from '../shared/testData';
|
||||||
import { mockInstance } from '../../shared/mocking';
|
import { mockInstance } from '../../shared/mocking';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
describe('MeController', () => {
|
describe('MeController', () => {
|
||||||
const externalHooks = mockInstance(ExternalHooks);
|
const externalHooks = mockInstance(ExternalHooks);
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { OAuth1CredentialController } from '@/controllers/oauth/oAuth1Credential
|
||||||
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
|
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import type { OAuthRequest } from '@/requests';
|
import type { OAuthRequest } from '@/requests';
|
||||||
import { BadRequestError, NotFoundError } from '@/ResponseHelper';
|
|
||||||
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
||||||
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
|
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
|
@ -17,6 +16,8 @@ import { SecretsHelper } from '@/SecretsHelpers';
|
||||||
import { CredentialsHelper } from '@/CredentialsHelper';
|
import { CredentialsHelper } from '@/CredentialsHelper';
|
||||||
|
|
||||||
import { mockInstance } from '../../shared/mocking';
|
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', () => {
|
describe('OAuth1CredentialController', () => {
|
||||||
mockInstance(Logger);
|
mockInstance(Logger);
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { OAuth2CredentialController } from '@/controllers/oauth/oAuth2Credential
|
||||||
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
|
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import type { OAuthRequest } from '@/requests';
|
import type { OAuthRequest } from '@/requests';
|
||||||
import { BadRequestError, NotFoundError } from '@/ResponseHelper';
|
|
||||||
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
||||||
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
|
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
|
@ -19,6 +18,8 @@ import { SecretsHelper } from '@/SecretsHelpers';
|
||||||
import { CredentialsHelper } from '@/CredentialsHelper';
|
import { CredentialsHelper } from '@/CredentialsHelper';
|
||||||
|
|
||||||
import { mockInstance } from '../../shared/mocking';
|
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', () => {
|
describe('OAuth2CredentialController', () => {
|
||||||
mockInstance(Logger);
|
mockInstance(Logger);
|
||||||
|
|
|
@ -5,7 +5,6 @@ import type { IInternalHooksClass } from '@/Interfaces';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import type { SettingsRepository } from '@db/repositories/settings.repository';
|
import type { SettingsRepository } from '@db/repositories/settings.repository';
|
||||||
import type { Config } from '@/config';
|
import type { Config } from '@/config';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
|
||||||
import type { OwnerRequest } from '@/requests';
|
import type { OwnerRequest } from '@/requests';
|
||||||
import { OwnerController } from '@/controllers/owner.controller';
|
import { OwnerController } from '@/controllers/owner.controller';
|
||||||
import { AUTH_COOKIE_NAME } from '@/constants';
|
import { AUTH_COOKIE_NAME } from '@/constants';
|
||||||
|
@ -14,6 +13,7 @@ import { License } from '@/License';
|
||||||
|
|
||||||
import { mockInstance } from '../../shared/mocking';
|
import { mockInstance } from '../../shared/mocking';
|
||||||
import { badPasswords } from '../shared/testData';
|
import { badPasswords } from '../shared/testData';
|
||||||
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
describe('OwnerController', () => {
|
describe('OwnerController', () => {
|
||||||
const config = mock<Config>();
|
const config = mock<Config>();
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
TranslationController,
|
TranslationController,
|
||||||
CREDENTIAL_TRANSLATIONS_DIR,
|
CREDENTIAL_TRANSLATIONS_DIR,
|
||||||
} from '@/controllers/translation.controller';
|
} from '@/controllers/translation.controller';
|
||||||
import { BadRequestError } from '@/ResponseHelper';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
describe('TranslationController', () => {
|
describe('TranslationController', () => {
|
||||||
const config = mock<Config>();
|
const config = mock<Config>();
|
||||||
|
|
Loading…
Reference in a new issue