refactor(core): Convert more routes to use the decorator pattern (no-changelog) (#5611)

* move nodeTypes api to a controller class
* move tags api to a controller class
* move LDAP routes to a controller class
* move nodes routes to a controller class
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-03-09 14:42:13 +01:00 committed by GitHub
parent 493f7a1c92
commit 356e916194
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 356 additions and 389 deletions

View file

@ -1,77 +0,0 @@
import express from 'express';
import { LdapManager } from '../LdapManager.ee';
import { getLdapConfig, getLdapSynchronizations, updateLdapConfig } from '../helpers';
import type { LdapConfiguration } from '../types';
import pick from 'lodash.pick';
import { NON_SENSIBLE_LDAP_CONFIG_PROPERTIES } from '../constants';
import { InternalHooks } from '@/InternalHooks';
import { Container } from 'typedi';
export const ldapController = express.Router();
/**
* GET /ldap/config
*/
ldapController.get('/config', async (req: express.Request, res: express.Response) => {
const data = await getLdapConfig();
return res.status(200).json({ data });
});
/**
* POST /ldap/test-connection
*/
ldapController.post('/test-connection', async (req: express.Request, res: express.Response) => {
try {
await LdapManager.getInstance().service.testConnection();
} catch (error) {
const errorObject = error as { message: string };
return res.status(400).json({ message: errorObject.message });
}
return res.status(200).json();
});
/**
* PUT /ldap/config
*/
ldapController.put('/config', async (req: LdapConfiguration.Update, res: express.Response) => {
try {
await updateLdapConfig(req.body);
} catch (e) {
if (e instanceof Error) {
return res.status(400).json({ message: e.message });
}
}
const data = await getLdapConfig();
void Container.get(InternalHooks).onUserUpdatedLdapSettings({
user_id: req.user.id,
...pick(data, NON_SENSIBLE_LDAP_CONFIG_PROPERTIES),
});
return res.status(200).json({ data });
});
/**
* POST /ldap/sync
*/
ldapController.post('/sync', async (req: LdapConfiguration.Sync, res: express.Response) => {
const runType = req.body.type;
try {
await LdapManager.getInstance().sync.run(runType);
} catch (e) {
if (e instanceof Error) {
return res.status(400).json({ message: e.message });
}
}
return res.status(200).json({});
});
/**
* GET /ldap/sync
*/
ldapController.get('/sync', async (req: LdapConfiguration.GetSync, res: express.Response) => {
const { page = '0', perPage = '20' } = req.query;
const data = await getLdapSynchronizations(parseInt(page, 10), parseInt(perPage, 10));
return res.status(200).json({ data });
});

View file

@ -59,7 +59,6 @@ import config from '@/config';
import * as Queue from '@/Queue'; import * as Queue from '@/Queue';
import { getSharedWorkflowIds } from '@/WorkflowHelpers'; import { getSharedWorkflowIds } from '@/WorkflowHelpers';
import { nodesController } from '@/api/nodes.api';
import { workflowsController } from '@/workflows/workflows.controller'; import { workflowsController } from '@/workflows/workflows.controller';
import { import {
EDITOR_UI_DIST_DIR, EDITOR_UI_DIST_DIR,
@ -83,16 +82,18 @@ import type {
import { registerController } from '@/decorators'; import { registerController } from '@/decorators';
import { import {
AuthController, AuthController,
LdapController,
MeController, MeController,
NodesController,
NodeTypesController,
OwnerController, OwnerController,
PasswordResetController, PasswordResetController,
TagsController,
TranslationController, TranslationController,
UsersController, UsersController,
} from '@/controllers'; } from '@/controllers';
import { executionsController } from '@/executions/executions.controller'; import { executionsController } from '@/executions/executions.controller';
import { nodeTypesController } from '@/api/nodeTypes.api';
import { tagsController } from '@/api/tags.api';
import { workflowStatsController } from '@/api/workflowStats.api'; import { workflowStatsController } from '@/api/workflowStats.api';
import { loadPublicApiVersions } from '@/PublicApi'; import { loadPublicApiVersions } from '@/PublicApi';
import { import {
@ -134,7 +135,6 @@ import { licenseController } from './license/license.controller';
import { Push, setupPushServer, setupPushHandler } from '@/push'; import { Push, setupPushServer, setupPushHandler } from '@/push';
import { setupAuthMiddlewares } from './middlewares'; import { setupAuthMiddlewares } from './middlewares';
import { initEvents } from './events'; import { initEvents } from './events';
import { ldapController } from './Ldap/routes/ldap.controller.ee';
import { getLdapLoginLabel, isLdapEnabled, isLdapLoginEnabled } from './Ldap/helpers'; import { getLdapLoginLabel, isLdapEnabled, isLdapLoginEnabled } from './Ldap/helpers';
import { AbstractServer } from './AbstractServer'; import { AbstractServer } from './AbstractServer';
import { configureMetrics } from './metrics'; import { configureMetrics } from './metrics';
@ -152,6 +152,7 @@ import { getSamlLoginLabel, isSamlLoginEnabled, isSamlLicensed } from './sso/sam
import { samlControllerPublic } from './sso/saml/routes/saml.controller.public.ee'; import { samlControllerPublic } from './sso/saml/routes/saml.controller.public.ee';
import { SamlService } from './sso/saml/saml.service.ee'; import { SamlService } from './sso/saml/saml.service.ee';
import { samlControllerProtected } from './sso/saml/routes/saml.controller.protected.ee'; import { samlControllerProtected } from './sso/saml/routes/saml.controller.protected.ee';
import { LdapManager } from './Ldap/LdapManager.ee';
const exec = promisify(callbackExec); const exec = promisify(callbackExec);
@ -371,7 +372,7 @@ class Server extends AbstractServer {
} }
private registerControllers(ignoredEndpoints: Readonly<string[]>) { private registerControllers(ignoredEndpoints: Readonly<string[]>) {
const { app, externalHooks, activeWorkflowRunner } = this; const { app, externalHooks, activeWorkflowRunner, nodeTypes } = this;
const repositories = Db.collections; const repositories = Db.collections;
setupAuthMiddlewares(app, ignoredEndpoints, this.restEndpoint, repositories.User); setupAuthMiddlewares(app, ignoredEndpoints, this.restEndpoint, repositories.User);
@ -380,11 +381,13 @@ class Server extends AbstractServer {
const mailer = getMailerInstance(); const mailer = getMailerInstance();
const postHog = this.postHog; const postHog = this.postHog;
const controllers = [ const controllers: object[] = [
new AuthController({ config, internalHooks, repositories, logger, postHog }), new AuthController({ config, internalHooks, repositories, logger, postHog }),
new OwnerController({ config, internalHooks, repositories, logger }), new OwnerController({ config, internalHooks, repositories, logger }),
new MeController({ externalHooks, internalHooks, repositories, logger }), new MeController({ externalHooks, internalHooks, repositories, logger }),
new NodeTypesController({ config, nodeTypes }),
new PasswordResetController({ config, externalHooks, internalHooks, repositories, logger }), new PasswordResetController({ config, externalHooks, internalHooks, repositories, logger }),
new TagsController({ config, repositories, externalHooks }),
new TranslationController(config, this.credentialTypes), new TranslationController(config, this.credentialTypes),
new UsersController({ new UsersController({
config, config,
@ -397,6 +400,18 @@ class Server extends AbstractServer {
postHog, postHog,
}), }),
]; ];
if (isLdapEnabled()) {
const { service, sync } = LdapManager.getInstance();
controllers.push(new LdapController(service, sync, internalHooks));
}
if (config.getEnv('nodes.communityPackages.enabled')) {
controllers.push(
new NodesController(config, this.loadNodesAndCredentials, this.push, internalHooks),
);
}
controllers.forEach((controller) => registerController(app, config, controller)); controllers.forEach((controller) => registerController(app, config, controller));
} }
@ -482,13 +497,6 @@ class Server extends AbstractServer {
this.app.use(`/${this.restEndpoint}/credentials`, credentialsController); this.app.use(`/${this.restEndpoint}/credentials`, credentialsController);
// ----------------------------------------
// Packages and nodes management
// ----------------------------------------
if (config.getEnv('nodes.communityPackages.enabled')) {
this.app.use(`/${this.restEndpoint}/nodes`, nodesController);
}
// ---------------------------------------- // ----------------------------------------
// Workflow // Workflow
// ---------------------------------------- // ----------------------------------------
@ -504,18 +512,6 @@ class Server extends AbstractServer {
// ---------------------------------------- // ----------------------------------------
this.app.use(`/${this.restEndpoint}/workflow-stats`, workflowStatsController); this.app.use(`/${this.restEndpoint}/workflow-stats`, workflowStatsController);
// ----------------------------------------
// Tags
// ----------------------------------------
this.app.use(`/${this.restEndpoint}/tags`, tagsController);
// ----------------------------------------
// LDAP
// ----------------------------------------
if (isLdapEnabled()) {
this.app.use(`/${this.restEndpoint}/ldap`, ldapController);
}
// ---------------------------------------- // ----------------------------------------
// SAML // SAML
// ---------------------------------------- // ----------------------------------------
@ -534,7 +530,6 @@ class Server extends AbstractServer {
this.app.use(`/${this.restEndpoint}/sso/saml`, samlControllerProtected); this.app.use(`/${this.restEndpoint}/sso/saml`, samlControllerProtected);
// ---------------------------------------- // ----------------------------------------
// Returns parameter values which normally get loaded from an external API or // Returns parameter values which normally get loaded from an external API or
// get generated dynamically // get generated dynamically
this.app.get( this.app.get(
@ -645,12 +640,6 @@ class Server extends AbstractServer {
), ),
); );
// ----------------------------------------
// Node-Types
// ----------------------------------------
this.app.use(`/${this.restEndpoint}/node-types`, nodeTypesController);
// ---------------------------------------- // ----------------------------------------
// Active Workflows // Active Workflows
// ---------------------------------------- // ----------------------------------------

View file

@ -1,110 +0,0 @@
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
/* eslint-disable @typescript-eslint/no-invalid-void-type */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable no-param-reassign */
import express from 'express';
import * as Db from '@/Db';
import { ExternalHooks } from '@/ExternalHooks';
import type { ITagWithCountDb } from '@/Interfaces';
import * as ResponseHelper from '@/ResponseHelper';
import config from '@/config';
import * as TagHelpers from '@/TagHelpers';
import { validateEntity } from '@/GenericHelpers';
import { TagEntity } from '@db/entities/TagEntity';
import type { TagsRequest } from '@/requests';
import { Container } from 'typedi';
export const tagsController = express.Router();
const workflowsEnabledMiddleware: express.RequestHandler = (req, res, next) => {
if (config.getEnv('workflowTagsDisabled')) {
throw new ResponseHelper.BadRequestError('Workflow tags are disabled');
}
next();
};
// Retrieves all tags, with or without usage count
tagsController.get(
'/',
workflowsEnabledMiddleware,
ResponseHelper.send(async (req: express.Request): Promise<TagEntity[] | ITagWithCountDb[]> => {
if (req.query.withUsageCount === 'true') {
const tablePrefix = config.getEnv('database.tablePrefix');
return TagHelpers.getTagsWithCountDb(tablePrefix);
}
return Db.collections.Tag.find({ select: ['id', 'name', 'createdAt', 'updatedAt'] });
}),
);
// Creates a tag
tagsController.post(
'/',
workflowsEnabledMiddleware,
ResponseHelper.send(async (req: express.Request): Promise<TagEntity | void> => {
const newTag = new TagEntity();
newTag.name = req.body.name.trim();
await Container.get(ExternalHooks).run('tag.beforeCreate', [newTag]);
await validateEntity(newTag);
const tag = await Db.collections.Tag.save(newTag);
await Container.get(ExternalHooks).run('tag.afterCreate', [tag]);
return tag;
}),
);
// Updates a tag
tagsController.patch(
'/:id(\\d+)',
workflowsEnabledMiddleware,
ResponseHelper.send(async (req: express.Request): Promise<TagEntity | void> => {
const { name } = req.body;
const { id } = req.params;
const newTag = new TagEntity();
// @ts-ignore
newTag.id = id;
newTag.name = name.trim();
await Container.get(ExternalHooks).run('tag.beforeUpdate', [newTag]);
await validateEntity(newTag);
const tag = await Db.collections.Tag.save(newTag);
await Container.get(ExternalHooks).run('tag.afterUpdate', [tag]);
return tag;
}),
);
tagsController.delete(
'/:id(\\d+)',
workflowsEnabledMiddleware,
ResponseHelper.send(async (req: TagsRequest.Delete): Promise<boolean> => {
if (
config.getEnv('userManagement.isInstanceOwnerSetUp') === true &&
req.user.globalRole.name !== 'owner'
) {
throw new ResponseHelper.UnauthorizedError(
'You are not allowed to perform this action',
'Only owners can remove tags',
);
}
const id = req.params.id;
await Container.get(ExternalHooks).run('tag.beforeDelete', [id]);
await Db.collections.Tag.delete({ id });
await Container.get(ExternalHooks).run('tag.afterDelete', [id]);
return true;
}),
);

View file

@ -1,6 +1,10 @@
export { AuthController } from './auth.controller'; export { AuthController } from './auth.controller';
export { LdapController } from './ldap.controller';
export { MeController } from './me.controller'; export { MeController } from './me.controller';
export { NodesController } from './nodes.controller';
export { NodeTypesController } from './nodeTypes.controller';
export { OwnerController } from './owner.controller'; export { OwnerController } from './owner.controller';
export { PasswordResetController } from './passwordReset.controller'; export { PasswordResetController } from './passwordReset.controller';
export { TagsController } from './tags.controller';
export { TranslationController } from './translation.controller'; export { TranslationController } from './translation.controller';
export { UsersController } from './users.controller'; export { UsersController } from './users.controller';

View file

@ -0,0 +1,65 @@
import pick from 'lodash.pick';
import { Get, Post, Put, RestController } from '@/decorators';
import { getLdapConfig, getLdapSynchronizations, updateLdapConfig } from '@/Ldap/helpers';
import { LdapService } from '@/Ldap/LdapService.ee';
import { LdapSync } from '@/Ldap/LdapSync.ee';
import { LdapConfiguration } from '@/Ldap/types';
import { BadRequestError } from '@/ResponseHelper';
import { NON_SENSIBLE_LDAP_CONFIG_PROPERTIES } from '@/Ldap/constants';
import { InternalHooks } from '@/InternalHooks';
@RestController('/ldap')
export class LdapController {
constructor(
private ldapService: LdapService,
private ldapSync: LdapSync,
private internalHooks: InternalHooks,
) {}
@Get('/config')
async getConfig() {
return getLdapConfig();
}
@Post('/test-connection')
async testConnection() {
try {
await this.ldapService.testConnection();
} catch (error) {
throw new BadRequestError((error as { message: string }).message);
}
}
@Put('/config')
async updateConfig(req: LdapConfiguration.Update) {
try {
await updateLdapConfig(req.body);
} catch (error) {
throw new BadRequestError((error as { message: string }).message);
}
const data = await getLdapConfig();
void this.internalHooks.onUserUpdatedLdapSettings({
user_id: req.user.id,
...pick(data, NON_SENSIBLE_LDAP_CONFIG_PROPERTIES),
});
return data;
}
@Get('/sync')
async getLdapSync(req: LdapConfiguration.GetSync) {
const { page = '0', perPage = '20' } = req.query;
return getLdapSynchronizations(parseInt(page, 10), parseInt(perPage, 10));
}
@Post('/sync')
async syncLdap(req: LdapConfiguration.Sync) {
try {
await this.ldapSync.run(req.body.type);
} catch (error) {
throw new BadRequestError((error as { message: string }).message);
}
}
}

View file

@ -1,39 +1,43 @@
import express from 'express';
import { readFile } from 'fs/promises'; import { readFile } from 'fs/promises';
import get from 'lodash.get'; import get from 'lodash.get';
import { Request } from 'express';
import type { INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow'; import type { INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow';
import { Post, RestController } from '@/decorators';
import config from '@/config';
import { NodeTypes } from '@/NodeTypes';
import * as ResponseHelper from '@/ResponseHelper';
import { getNodeTranslationPath } from '@/TranslationHelpers'; import { getNodeTranslationPath } from '@/TranslationHelpers';
import { Container } from 'typedi'; import type { Config } from '@/config';
import type { NodeTypes } from '@/NodeTypes';
export const nodeTypesController = express.Router(); @RestController('/node-types')
export class NodeTypesController {
private readonly config: Config;
// Returns node information based on node names and versions private readonly nodeTypes: NodeTypes;
nodeTypesController.post(
'/', constructor({ config, nodeTypes }: { config: Config; nodeTypes: NodeTypes }) {
ResponseHelper.send(async (req: express.Request): Promise<INodeTypeDescription[]> => { this.config = config;
this.nodeTypes = nodeTypes;
}
@Post('/')
async getNodeInfo(req: Request) {
const nodeInfos = get(req, 'body.nodeInfos', []) as INodeTypeNameVersion[]; const nodeInfos = get(req, 'body.nodeInfos', []) as INodeTypeNameVersion[];
const defaultLocale = config.getEnv('defaultLocale'); const defaultLocale = this.config.getEnv('defaultLocale');
if (defaultLocale === 'en') { if (defaultLocale === 'en') {
return nodeInfos.reduce<INodeTypeDescription[]>((acc, { name, version }) => { return nodeInfos.reduce<INodeTypeDescription[]>((acc, { name, version }) => {
const { description } = Container.get(NodeTypes).getByNameAndVersion(name, version); const { description } = this.nodeTypes.getByNameAndVersion(name, version);
acc.push(description); acc.push(description);
return acc; return acc;
}, []); }, []);
} }
async function populateTranslation( const populateTranslation = async (
name: string, name: string,
version: number, version: number,
nodeTypes: INodeTypeDescription[], nodeTypes: INodeTypeDescription[],
) { ) => {
const { description, sourcePath } = Container.get(NodeTypes).getWithSourcePath(name, version); const { description, sourcePath } = this.nodeTypes.getWithSourcePath(name, version);
const translationPath = await getNodeTranslationPath({ const translationPath = await getNodeTranslationPath({
nodeSourcePath: sourcePath, nodeSourcePath: sourcePath,
longNodeType: description.name, longNodeType: description.name,
@ -44,12 +48,12 @@ nodeTypesController.post(
const translation = await readFile(translationPath, 'utf8'); const translation = await readFile(translationPath, 'utf8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
description.translation = JSON.parse(translation); description.translation = JSON.parse(translation);
} catch (error) { } catch {
// ignore - no translation exists at path // ignore - no translation exists at path
} }
nodeTypes.push(description); nodeTypes.push(description);
} };
const nodeTypes: INodeTypeDescription[] = []; const nodeTypes: INodeTypeDescription[] = [];
@ -60,5 +64,5 @@ nodeTypesController.post(
await Promise.all(promises); await Promise.all(promises);
return nodeTypes; return nodeTypes;
}), }
); }

View file

@ -1,10 +1,12 @@
import express from 'express'; import { Request, Response, NextFunction } from 'express';
import type { PublicInstalledPackage } from 'n8n-workflow'; import {
RESPONSE_ERROR_MESSAGES,
import config from '@/config'; STARTER_TEMPLATE_NAME,
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; UNKNOWN_FAILURE_REASON,
import * as ResponseHelper from '@/ResponseHelper'; } from '@/constants';
import { Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
import { NodeRequest } from '@/requests';
import { BadRequestError, InternalServerError } from '@/ResponseHelper';
import { import {
checkNpmPackageStatus, checkNpmPackageStatus,
executeCommand, executeCommand,
@ -22,57 +24,50 @@ import {
getAllInstalledPackages, getAllInstalledPackages,
isPackageInstalled, isPackageInstalled,
} from '@/CommunityNodes/packageModel'; } from '@/CommunityNodes/packageModel';
import {
RESPONSE_ERROR_MESSAGES,
STARTER_TEMPLATE_NAME,
UNKNOWN_FAILURE_REASON,
} from '@/constants';
import { isAuthenticatedRequest } from '@/UserManagement/UserManagementHelper';
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 type { NodeRequest } from '@/requests'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { Push } from '@/push';
import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { Push } from '@/push';
import { Config } from '@/config';
import { isAuthenticatedRequest } from '@/UserManagement/UserManagementHelper';
const { PACKAGE_NOT_INSTALLED, PACKAGE_NAME_NOT_PROVIDED } = RESPONSE_ERROR_MESSAGES; const { PACKAGE_NOT_INSTALLED, PACKAGE_NAME_NOT_PROVIDED } = RESPONSE_ERROR_MESSAGES;
export const nodesController = express.Router(); @RestController('/nodes')
export class NodesController {
constructor(
private config: Config,
private loadNodesAndCredentials: LoadNodesAndCredentials,
private push: Push,
private internalHooks: InternalHooks,
) {}
nodesController.use((req, res, next) => { // TODO: move this into a new decorator `@Authorized`
if (!isAuthenticatedRequest(req) || req.user.globalRole.name !== 'owner') { @Middleware()
res.status(403).json({ status: 'error', message: 'Unauthorized' }); checkIfOwner(req: Request, res: Response, next: NextFunction) {
return; if (!isAuthenticatedRequest(req) || req.user.globalRole.name !== 'owner')
res.status(403).json({ status: 'error', message: 'Unauthorized' });
else next();
} }
next(); // TODO: move this into a new decorator `@IfConfig('executions.mode', 'queue')`
}); @Middleware()
checkIfCommunityNodesEnabled(req: Request, res: Response, next: NextFunction) {
nodesController.use((req, res, next) => { if (this.config.getEnv('executions.mode') === 'queue' && req.method !== 'GET')
if (config.getEnv('executions.mode') === 'queue' && req.method !== 'GET') { res.status(400).json({
res.status(400).json({ status: 'error',
status: 'error', message: 'Package management is disabled when running in "queue" mode',
message: 'Package management is disabled when running in "queue" mode', });
}); else next();
return;
} }
next(); @Post('/')
}); async installPackage(req: NodeRequest.Post) {
/**
* POST /nodes
*
* Install an n8n community package
*/
nodesController.post(
'/',
ResponseHelper.send(async (req: NodeRequest.Post) => {
const { name } = req.body; const { name } = req.body;
if (!name) { if (!name) {
throw new ResponseHelper.BadRequestError(PACKAGE_NAME_NOT_PROVIDED); throw new BadRequestError(PACKAGE_NAME_NOT_PROVIDED);
} }
let parsed: CommunityPackages.ParsedPackageName; let parsed: CommunityPackages.ParsedPackageName;
@ -80,13 +75,13 @@ nodesController.post(
try { try {
parsed = parseNpmPackageName(name); parsed = parseNpmPackageName(name);
} catch (error) { } catch (error) {
throw new ResponseHelper.BadRequestError( throw new BadRequestError(
error instanceof Error ? error.message : 'Failed to parse package name', error instanceof Error ? error.message : 'Failed to parse package name',
); );
} }
if (parsed.packageName === STARTER_TEMPLATE_NAME) { if (parsed.packageName === STARTER_TEMPLATE_NAME) {
throw new ResponseHelper.BadRequestError( throw new BadRequestError(
[ [
`Package "${parsed.packageName}" is only a template`, `Package "${parsed.packageName}" is only a template`,
'Please enter an actual package to install', 'Please enter an actual package to install',
@ -98,7 +93,7 @@ nodesController.post(
const hasLoaded = hasPackageLoaded(name); const hasLoaded = hasPackageLoaded(name);
if (isInstalled && hasLoaded) { if (isInstalled && hasLoaded) {
throw new ResponseHelper.BadRequestError( throw new BadRequestError(
[ [
`Package "${parsed.packageName}" is already installed`, `Package "${parsed.packageName}" is already installed`,
'To update it, click the corresponding button in the UI', 'To update it, click the corresponding button in the UI',
@ -109,22 +104,19 @@ nodesController.post(
const packageStatus = await checkNpmPackageStatus(name); const packageStatus = await checkNpmPackageStatus(name);
if (packageStatus.status !== 'OK') { if (packageStatus.status !== 'OK') {
throw new ResponseHelper.BadRequestError( throw new BadRequestError(`Package "${name}" is banned so it cannot be installed`);
`Package "${name}" is banned so it cannot be installed`,
);
} }
let installedPackage: InstalledPackages; let installedPackage: InstalledPackages;
try { try {
installedPackage = await Container.get(LoadNodesAndCredentials).loadNpmModule( installedPackage = await this.loadNodesAndCredentials.loadNpmModule(
parsed.packageName, parsed.packageName,
parsed.version, parsed.version,
); );
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON; const errorMessage = error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON;
void Container.get(InternalHooks).onCommunityPackageInstallFinished({ void this.internalHooks.onCommunityPackageInstallFinished({
user: req.user, user: req.user,
input_string: name, input_string: name,
package_name: parsed.packageName, package_name: parsed.packageName,
@ -136,23 +128,20 @@ nodesController.post(
const message = [`Error loading package "${name}"`, errorMessage].join(':'); const message = [`Error loading package "${name}"`, errorMessage].join(':');
const clientError = error instanceof Error ? isClientError(error) : false; const clientError = error instanceof Error ? isClientError(error) : false;
throw new (clientError ? BadRequestError : InternalServerError)(message);
throw new ResponseHelper[clientError ? 'BadRequestError' : 'InternalServerError'](message);
} }
if (!hasLoaded) removePackageFromMissingList(name); if (!hasLoaded) removePackageFromMissingList(name);
const pushInstance = Container.get(Push);
// broadcast to connected frontends that node list has been updated // broadcast to connected frontends that node list has been updated
installedPackage.installedNodes.forEach((node) => { installedPackage.installedNodes.forEach((node) => {
pushInstance.send('reloadNodeType', { this.push.send('reloadNodeType', {
name: node.type, name: node.type,
version: node.latestVersion, version: node.latestVersion,
}); });
}); });
void Container.get(InternalHooks).onCommunityPackageInstallFinished({ void this.internalHooks.onCommunityPackageInstallFinished({
user: req.user, user: req.user,
input_string: name, input_string: name,
package_name: parsed.packageName, package_name: parsed.packageName,
@ -164,17 +153,10 @@ nodesController.post(
}); });
return installedPackage; return installedPackage;
}), }
);
/** @Get('/')
* GET /nodes async getInstalledPackages() {
*
* Retrieve list of installed n8n community packages
*/
nodesController.get(
'/',
ResponseHelper.send(async (): Promise<PublicInstalledPackage[]> => {
const installedPackages = await getAllInstalledPackages(); const installedPackages = await getAllInstalledPackages();
if (installedPackages.length === 0) return []; if (installedPackages.length === 0) return [];
@ -188,7 +170,6 @@ nodesController.get(
// when there are updates, npm exits with code 1 // when there are updates, npm exits with code 1
// when there are no updates, command succeeds // when there are no updates, command succeeds
// https://github.com/npm/rfcs/issues/473 // https://github.com/npm/rfcs/issues/473
if (isNpmError(error) && error.code === 1) { if (isNpmError(error) && error.code === 1) {
pendingUpdates = JSON.parse(error.stdout) as CommunityPackages.AvailableUpdates; pendingUpdates = JSON.parse(error.stdout) as CommunityPackages.AvailableUpdates;
} }
@ -197,31 +178,21 @@ nodesController.get(
let hydratedPackages = matchPackagesWithUpdates(installedPackages, pendingUpdates); let hydratedPackages = matchPackagesWithUpdates(installedPackages, pendingUpdates);
try { try {
const missingPackages = config.get('nodes.packagesMissing') as string | undefined; const missingPackages = this.config.get('nodes.packagesMissing') as string | undefined;
if (missingPackages) { if (missingPackages) {
hydratedPackages = matchMissingPackages(hydratedPackages, missingPackages); hydratedPackages = matchMissingPackages(hydratedPackages, missingPackages);
} }
} catch { } catch {}
// Do nothing if setting is missing
}
return hydratedPackages; return hydratedPackages;
}), }
);
/** @Delete('/')
* DELETE /nodes async uninstallPackage(req: NodeRequest.Delete) {
*
* Uninstall an installed n8n community package
*/
nodesController.delete(
'/',
ResponseHelper.send(async (req: NodeRequest.Delete) => {
const { name } = req.query; const { name } = req.query;
if (!name) { if (!name) {
throw new ResponseHelper.BadRequestError(PACKAGE_NAME_NOT_PROVIDED); throw new BadRequestError(PACKAGE_NAME_NOT_PROVIDED);
} }
try { try {
@ -229,37 +200,35 @@ nodesController.delete(
} catch (error) { } catch (error) {
const message = error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON; const message = error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON;
throw new ResponseHelper.BadRequestError(message); throw new BadRequestError(message);
} }
const installedPackage = await findInstalledPackage(name); const installedPackage = await findInstalledPackage(name);
if (!installedPackage) { if (!installedPackage) {
throw new ResponseHelper.BadRequestError(PACKAGE_NOT_INSTALLED); throw new BadRequestError(PACKAGE_NOT_INSTALLED);
} }
try { try {
await Container.get(LoadNodesAndCredentials).removeNpmModule(name, installedPackage); await this.loadNodesAndCredentials.removeNpmModule(name, installedPackage);
} catch (error) { } catch (error) {
const message = [ const message = [
`Error removing package "${name}"`, `Error removing package "${name}"`,
error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON, error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON,
].join(':'); ].join(':');
throw new ResponseHelper.InternalServerError(message); throw new InternalServerError(message);
} }
const pushInstance = Container.get(Push);
// broadcast to connected frontends that node list has been updated // broadcast to connected frontends that node list has been updated
installedPackage.installedNodes.forEach((node) => { installedPackage.installedNodes.forEach((node) => {
pushInstance.send('removeNodeType', { this.push.send('removeNodeType', {
name: node.type, name: node.type,
version: node.latestVersion, version: node.latestVersion,
}); });
}); });
void Container.get(InternalHooks).onCommunityPackageDeleteFinished({ void this.internalHooks.onCommunityPackageDeleteFinished({
user: req.user, user: req.user,
package_name: name, package_name: name,
package_version: installedPackage.installedVersion, package_version: installedPackage.installedVersion,
@ -267,53 +236,44 @@ nodesController.delete(
package_author: installedPackage.authorName, package_author: installedPackage.authorName,
package_author_email: installedPackage.authorEmail, package_author_email: installedPackage.authorEmail,
}); });
}), }
);
/** @Patch('/')
* PATCH /nodes async updatePackage(req: NodeRequest.Update) {
*
* Update an installed n8n community package
*/
nodesController.patch(
'/',
ResponseHelper.send(async (req: NodeRequest.Update) => {
const { name } = req.body; const { name } = req.body;
if (!name) { if (!name) {
throw new ResponseHelper.BadRequestError(PACKAGE_NAME_NOT_PROVIDED); throw new BadRequestError(PACKAGE_NAME_NOT_PROVIDED);
} }
const previouslyInstalledPackage = await findInstalledPackage(name); const previouslyInstalledPackage = await findInstalledPackage(name);
if (!previouslyInstalledPackage) { if (!previouslyInstalledPackage) {
throw new ResponseHelper.BadRequestError(PACKAGE_NOT_INSTALLED); throw new BadRequestError(PACKAGE_NOT_INSTALLED);
} }
try { try {
const newInstalledPackage = await Container.get(LoadNodesAndCredentials).updateNpmModule( const newInstalledPackage = await this.loadNodesAndCredentials.updateNpmModule(
parseNpmPackageName(name).packageName, parseNpmPackageName(name).packageName,
previouslyInstalledPackage, previouslyInstalledPackage,
); );
const pushInstance = Container.get(Push);
// broadcast to connected frontends that node list has been updated // broadcast to connected frontends that node list has been updated
previouslyInstalledPackage.installedNodes.forEach((node) => { previouslyInstalledPackage.installedNodes.forEach((node) => {
pushInstance.send('removeNodeType', { this.push.send('removeNodeType', {
name: node.type, name: node.type,
version: node.latestVersion, version: node.latestVersion,
}); });
}); });
newInstalledPackage.installedNodes.forEach((node) => { newInstalledPackage.installedNodes.forEach((node) => {
pushInstance.send('reloadNodeType', { this.push.send('reloadNodeType', {
name: node.name, name: node.name,
version: node.latestVersion, version: node.latestVersion,
}); });
}); });
void Container.get(InternalHooks).onCommunityPackageUpdateFinished({ void this.internalHooks.onCommunityPackageUpdateFinished({
user: req.user, user: req.user,
package_name: name, package_name: name,
package_version_current: previouslyInstalledPackage.installedVersion, package_version_current: previouslyInstalledPackage.installedVersion,
@ -326,8 +286,7 @@ nodesController.patch(
return newInstalledPackage; return newInstalledPackage;
} catch (error) { } catch (error) {
previouslyInstalledPackage.installedNodes.forEach((node) => { previouslyInstalledPackage.installedNodes.forEach((node) => {
const pushInstance = Container.get(Push); this.push.send('removeNodeType', {
pushInstance.send('removeNodeType', {
name: node.type, name: node.type,
version: node.latestVersion, version: node.latestVersion,
}); });
@ -338,7 +297,7 @@ nodesController.patch(
error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON, error instanceof Error ? error.message : UNKNOWN_FAILURE_REASON,
].join(':'); ].join(':');
throw new ResponseHelper.InternalServerError(message); throw new InternalServerError(message);
} }
}), }
); }

View file

@ -0,0 +1,102 @@
import { Request, Response, NextFunction } from 'express';
import type { Repository } from 'typeorm';
import type { Config } from '@/config';
import { Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
import type { IDatabaseCollections, IExternalHooksClass, ITagWithCountDb } from '@/Interfaces';
import { TagEntity } from '@db/entities/TagEntity';
import { getTagsWithCountDb } from '@/TagHelpers';
import { validateEntity } from '@/GenericHelpers';
import { BadRequestError, UnauthorizedError } from '@/ResponseHelper';
import { TagsRequest } from '@/requests';
@RestController('/tags')
export class TagsController {
private config: Config;
private externalHooks: IExternalHooksClass;
private tagsRepository: Repository<TagEntity>;
constructor({
config,
externalHooks,
repositories,
}: {
config: Config;
externalHooks: IExternalHooksClass;
repositories: Pick<IDatabaseCollections, 'Tag'>;
}) {
this.config = config;
this.externalHooks = externalHooks;
this.tagsRepository = repositories.Tag;
}
// TODO: move this into a new decorator `@IfEnabled('workflowTagsDisabled')`
@Middleware()
workflowsEnabledMiddleware(req: Request, res: Response, next: NextFunction) {
if (this.config.getEnv('workflowTagsDisabled'))
throw new BadRequestError('Workflow tags are disabled');
next();
}
// Retrieves all tags, with or without usage count
@Get('/')
async getAll(req: TagsRequest.GetAll): Promise<TagEntity[] | ITagWithCountDb[]> {
const { withUsageCount } = req.query;
if (withUsageCount === 'true') {
const tablePrefix = this.config.getEnv('database.tablePrefix');
return getTagsWithCountDb(tablePrefix);
}
return this.tagsRepository.find({ select: ['id', 'name', 'createdAt', 'updatedAt'] });
}
// Creates a tag
@Post('/')
async createTag(req: TagsRequest.Create): Promise<TagEntity> {
const newTag = new TagEntity();
newTag.name = req.body.name.trim();
await this.externalHooks.run('tag.beforeCreate', [newTag]);
await validateEntity(newTag);
const tag = await this.tagsRepository.save(newTag);
await this.externalHooks.run('tag.afterCreate', [tag]);
return tag;
}
// Updates a tag
@Patch('/:id(\\d+)')
async updateTag(req: TagsRequest.Update): Promise<TagEntity> {
const { name } = req.body;
const { id } = req.params;
const newTag = new TagEntity();
newTag.id = id;
newTag.name = name.trim();
await this.externalHooks.run('tag.beforeUpdate', [newTag]);
await validateEntity(newTag);
const tag = await this.tagsRepository.save(newTag);
await this.externalHooks.run('tag.afterUpdate', [tag]);
return tag;
}
@Delete('/:id(\\d+)')
async deleteTag(req: TagsRequest.Delete) {
const isInstanceOwnerSetUp = this.config.getEnv('userManagement.isInstanceOwnerSetUp');
if (isInstanceOwnerSetUp && req.user.globalRole.name !== 'owner') {
throw new UnauthorizedError(
'You are not allowed to perform this action',
'Only owners can remove tags',
);
}
const { id } = req.params;
await this.externalHooks.run('tag.beforeDelete', [id]);
await this.tagsRepository.delete({ id });
await this.externalHooks.run('tag.afterDelete', [id]);
return true;
}
}

View file

@ -0,0 +1,11 @@
import { CONTROLLER_MIDDLEWARES } from './constants';
import type { MiddlewareMetadata } from './types';
// eslint-disable-next-line @typescript-eslint/naming-convention
export const Middleware = (): MethodDecorator => (target, handlerName) => {
const controllerClass = target.constructor;
const middlewares = (Reflect.getMetadata(CONTROLLER_MIDDLEWARES, controllerClass) ??
[]) as MiddlewareMetadata[];
middlewares.push({ handlerName: String(handlerName) });
Reflect.defineMetadata(CONTROLLER_MIDDLEWARES, middlewares, controllerClass);
};

View file

@ -15,5 +15,6 @@ const RouteFactory =
export const Get = RouteFactory('get'); export const Get = RouteFactory('get');
export const Post = RouteFactory('post'); export const Post = RouteFactory('post');
export const Put = RouteFactory('put');
export const Patch = RouteFactory('patch'); export const Patch = RouteFactory('patch');
export const Delete = RouteFactory('delete'); export const Delete = RouteFactory('delete');

View file

@ -1,2 +1,3 @@
export const CONTROLLER_ROUTES = 'CONTROLLER_ROUTES'; export const CONTROLLER_ROUTES = 'CONTROLLER_ROUTES';
export const CONTROLLER_BASE_PATH = 'CONTROLLER_BASE_PATH'; export const CONTROLLER_BASE_PATH = 'CONTROLLER_BASE_PATH';
export const CONTROLLER_MIDDLEWARES = 'CONTROLLER_MIDDLEWARES';

View file

@ -1,3 +1,4 @@
export { RestController } from './RestController'; export { RestController } from './RestController';
export { Get, Post, Patch, Delete } from './Route'; export { Get, Post, Put, Patch, Delete } from './Route';
export { Middleware } from './Middleware';
export { registerController } from './registerController'; export { registerController } from './registerController';

View file

@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { Router } from 'express'; import { Router } from 'express';
import type { Config } from '@/config'; import type { Config } from '@/config';
import { CONTROLLER_BASE_PATH, CONTROLLER_ROUTES } from './constants'; import { CONTROLLER_BASE_PATH, CONTROLLER_MIDDLEWARES, CONTROLLER_ROUTES } from './constants';
import { send } from '@/ResponseHelper'; // TODO: move `ResponseHelper.send` to this file import { send } from '@/ResponseHelper'; // TODO: move `ResponseHelper.send` to this file
import type { Application, Request, Response } from 'express'; import type { Application, Request, Response, RequestHandler } from 'express';
import type { Controller, RouteMetadata } from './types'; import type { Controller, MiddlewareMetadata, RouteMetadata } from './types';
export const registerController = (app: Application, config: Config, controller: object) => { export const registerController = (app: Application, config: Config, controller: object) => {
const controllerClass = controller.constructor; const controllerClass = controller.constructor;
@ -20,9 +20,17 @@ export const registerController = (app: Application, config: Config, controller:
const restBasePath = config.getEnv('endpoints.rest'); const restBasePath = config.getEnv('endpoints.rest');
const prefix = `/${[restBasePath, controllerBasePath].join('/')}`.replace(/\/+/g, '/'); const prefix = `/${[restBasePath, controllerBasePath].join('/')}`.replace(/\/+/g, '/');
const middlewares = (
(Reflect.getMetadata(CONTROLLER_MIDDLEWARES, controllerClass) ?? []) as MiddlewareMetadata[]
).map(
({ handlerName }) =>
(controller as Controller)[handlerName].bind(controller) as RequestHandler,
);
routes.forEach(({ method, path, handlerName }) => { routes.forEach(({ method, path, handlerName }) => {
router[method]( router[method](
path, path,
...middlewares,
send(async (req: Request, res: Response) => send(async (req: Request, res: Response) =>
(controller as Controller)[handlerName](req, res), (controller as Controller)[handlerName](req, res),
), ),

View file

@ -1,6 +1,10 @@
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
export type Method = 'get' | 'post' | 'patch' | 'delete'; export type Method = 'get' | 'post' | 'put' | 'patch' | 'delete';
export interface MiddlewareMetadata {
handlerName: string;
}
export interface RouteMetadata { export interface RouteMetadata {
method: Method; method: Method;

View file

@ -3,7 +3,6 @@ export * from './CredentialsHelper';
export * from './CredentialTypes'; export * from './CredentialTypes';
export * from './CredentialsOverwrites'; export * from './CredentialsOverwrites';
export * from './Interfaces'; export * from './Interfaces';
export * from './NodeTypes';
export * from './WaitingWebhooks'; export * from './WaitingWebhooks';
export * from './WorkflowCredentials'; export * from './WorkflowCredentials';
export * from './WorkflowRunner'; export * from './WorkflowRunner';

View file

@ -18,7 +18,7 @@ import {
} from '@/UserManagement/UserManagementHelper'; } from '@/UserManagement/UserManagementHelper';
import type { Repository } from 'typeorm'; import type { Repository } from 'typeorm';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
import { SamlUrls } from '../sso/saml/constants'; import { SamlUrls } from '@/sso/saml/constants';
const jwtFromRequest = (req: Request) => { const jwtFromRequest = (req: Request) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access

View file

@ -330,6 +330,9 @@ export type NodeListSearchRequest = AuthenticatedRequest<
// ---------------------------------- // ----------------------------------
export declare namespace TagsRequest { export declare namespace TagsRequest {
type GetAll = AuthenticatedRequest<{}, {}, {}, { withUsageCount: string }>;
type Create = AuthenticatedRequest<{}, {}, { name: string }>;
type Update = AuthenticatedRequest<{ id: string }, {}, { name: string }>;
type Delete = AuthenticatedRequest<{ id: string }>; type Delete = AuthenticatedRequest<{ id: string }>;
} }

View file

@ -49,13 +49,11 @@ beforeAll(async () => {
authAgent = utils.createAuthAgent(app); authAgent = utils.createAuthAgent(app);
config.set(LDAP_ENABLED, true);
defaultLdapConfig.bindingAdminPassword = await encryptPassword( defaultLdapConfig.bindingAdminPassword = await encryptPassword(
defaultLdapConfig.bindingAdminPassword, defaultLdapConfig.bindingAdminPassword,
); );
utils.initConfigFile(); utils.initConfigFile();
await utils.initLdapManager();
}); });
beforeEach(async () => { beforeEach(async () => {

View file

@ -31,11 +31,8 @@ import { DeepPartial } from 'ts-essentials';
import config from '@/config'; import config from '@/config';
import * as Db from '@/Db'; import * as Db from '@/Db';
import { WorkflowEntity } from '@db/entities/WorkflowEntity'; import { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { CredentialTypes } from '@/CredentialTypes';
import { ExternalHooks } from '@/ExternalHooks'; import { ExternalHooks } from '@/ExternalHooks';
import { NodeTypes } from '@/NodeTypes';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import { nodesController } from '@/api/nodes.api';
import { workflowsController } from '@/workflows/workflows.controller'; import { workflowsController } from '@/workflows/workflows.controller';
import { AUTH_COOKIE_NAME, NODE_PACKAGE_PREFIX } from '@/constants'; import { AUTH_COOKIE_NAME, NODE_PACKAGE_PREFIX } from '@/constants';
import { credentialsController } from '@/credentials/credentials.controller'; import { credentialsController } from '@/credentials/credentials.controller';
@ -65,7 +62,9 @@ import { eventBusRouter } from '@/eventbus/eventBusRoutes';
import { registerController } from '@/decorators'; import { registerController } from '@/decorators';
import { import {
AuthController, AuthController,
LdapController,
MeController, MeController,
NodesController,
OwnerController, OwnerController,
PasswordResetController, PasswordResetController,
UsersController, UsersController,
@ -74,11 +73,13 @@ import { setupAuthMiddlewares } from '@/middlewares';
import * as testDb from '../shared/testDb'; import * as testDb from '../shared/testDb';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { handleLdapInit } from '@/Ldap/helpers';
import { ldapController } from '@/Ldap/routes/ldap.controller.ee';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { PostHogClient } from '@/posthog'; import { PostHogClient } from '@/posthog';
import { LdapManager } from '@/Ldap/LdapManager.ee';
import { LDAP_ENABLED } from '@/Ldap/constants';
import { handleLdapInit } from '@/Ldap/helpers';
import { Push } from '@/push';
export const mockInstance = <T>( export const mockInstance = <T>(
ctor: new (...args: any[]) => T, ctor: new (...args: any[]) => T,
@ -155,10 +156,8 @@ export async function initTestServer({
const map: Record<string, express.Router | express.Router[] | any> = { const map: Record<string, express.Router | express.Router[] | any> = {
credentials: { controller: credentialsController, path: 'credentials' }, credentials: { controller: credentialsController, path: 'credentials' },
workflows: { controller: workflowsController, path: 'workflows' }, workflows: { controller: workflowsController, path: 'workflows' },
nodes: { controller: nodesController, path: 'nodes' },
license: { controller: licenseController, path: 'license' }, license: { controller: licenseController, path: 'license' },
eventBus: { controller: eventBusRouter, path: 'eventbus' }, eventBus: { controller: eventBusRouter, path: 'eventbus' },
ldap: { controller: ldapController, path: 'ldap' },
}; };
if (enablePublicAPI) { if (enablePublicAPI) {
@ -190,6 +189,27 @@ export async function initTestServer({
new AuthController({ config, logger, internalHooks, repositories }), new AuthController({ config, logger, internalHooks, repositories }),
); );
break; break;
case 'ldap':
config.set(LDAP_ENABLED, true);
await handleLdapInit();
const { service, sync } = LdapManager.getInstance();
registerController(
testServer.app,
config,
new LdapController(service, sync, internalHooks),
);
break;
case 'nodes':
registerController(
testServer.app,
config,
new NodesController(
config,
Container.get(LoadNodesAndCredentials),
Container.get(Push),
internalHooks,
),
);
case 'me': case 'me':
registerController( registerController(
testServer.app, testServer.app,
@ -246,15 +266,7 @@ const classifyEndpointGroups = (endpointGroups: EndpointGroup[]) => {
const routerEndpoints: EndpointGroup[] = []; const routerEndpoints: EndpointGroup[] = [];
const functionEndpoints: EndpointGroup[] = []; const functionEndpoints: EndpointGroup[] = [];
const ROUTER_GROUP = [ const ROUTER_GROUP = ['credentials', 'workflows', 'publicApi', 'eventBus', 'license'];
'credentials',
'nodes',
'workflows',
'publicApi',
'ldap',
'eventBus',
'license',
];
endpointGroups.forEach((group) => endpointGroups.forEach((group) =>
(ROUTER_GROUP.includes(group) ? routerEndpoints : functionEndpoints).push(group), (ROUTER_GROUP.includes(group) ? routerEndpoints : functionEndpoints).push(group),
@ -320,13 +332,6 @@ export async function initCredentialsTypes(): Promise<void> {
}; };
} }
/**
* Initialize LDAP manager.
*/
export async function initLdapManager(): Promise<void> {
await handleLdapInit();
}
/** /**
* Initialize node types. * Initialize node types.
*/ */