1
1
mirror of https://github.com/n8n-io/n8n.git synced 2024-11-10 12:35:46 +03:00

refactor(core): Move frontend settings to service (no-changelog) (#7396)

Quick follow-up to https://github.com/n8n-io/n8n/pull/7283
This commit is contained in:
Iván Ovejero 2023-10-11 17:12:19 +02:00 committed by GitHub
parent 1e0753516c
commit 54e900955a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 269 additions and 273 deletions

View File

@ -40,7 +40,6 @@ import type {
INodeParameters,
INodePropertyOptions,
INodeTypeNameVersion,
ITelemetrySettings,
WorkflowExecuteMode,
ICredentialTypes,
ExecutionStatus,
@ -64,7 +63,6 @@ import {
GENERATED_STATIC_DIR,
inDevelopment,
inE2ETests,
LICENSE_FEATURES,
N8N_VERSION,
RESPONSE_ERROR_MESSAGES,
TEMPLATES_DIR,
@ -99,12 +97,7 @@ import { BinaryDataController } from './controllers/binaryData.controller';
import { ExternalSecretsController } from '@/ExternalSecrets/ExternalSecrets.controller.ee';
import { executionsController } from '@/executions/executions.controller';
import { isApiEnabled, loadPublicApiVersions } from '@/PublicApi';
import {
getInstanceBaseUrl,
isEmailSetUp,
isSharingEnabled,
whereClause,
} from '@/UserManagement/UserManagementHelper';
import { whereClause } from '@/UserManagement/UserManagementHelper';
import { UserManagementMailer } from '@/UserManagement/email';
import * as Db from '@/Db';
import type {
@ -130,40 +123,25 @@ import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'
import { toHttpNodeParameters } from '@/CurlConverterHelper';
import { EventBusController } from '@/eventbus/eventBus.controller';
import { EventBusControllerEE } from '@/eventbus/eventBus.controller.ee';
import { isLogStreamingEnabled } from '@/eventbus/MessageEventBus/MessageEventBusHelper';
import { licenseController } from './license/license.controller';
import { Push, setupPushServer, setupPushHandler } from '@/push';
import { setupAuthMiddlewares } from './middlewares';
import {
getLdapLoginLabel,
handleLdapInit,
isLdapEnabled,
isLdapLoginEnabled,
} from './Ldap/helpers';
import { handleLdapInit, isLdapEnabled } from './Ldap/helpers';
import { AbstractServer } from './AbstractServer';
import { PostHogClient } from './posthog';
import { eventBus } from './eventbus';
import { Container } from 'typedi';
import { InternalHooks } from './InternalHooks';
import { License } from './License';
import {
getStatusUsingPreviousExecutionStatusMethod,
isAdvancedExecutionFiltersEnabled,
isDebugInEditorLicensed,
} from './executions/executionHelpers';
import { getSamlLoginLabel, isSamlLoginEnabled, isSamlLicensed } from './sso/saml/samlHelpers';
import { getStatusUsingPreviousExecutionStatusMethod } from './executions/executionHelpers';
import { SamlController } from './sso/saml/routes/saml.controller.ee';
import { SamlService } from './sso/saml/saml.service.ee';
import { variablesController } from './environments/variables/variables.controller';
import { LdapManager } from './Ldap/LdapManager.ee';
import { getVariablesLimit, isVariablesEnabled } from '@/environments/variables/enviromentHelpers';
import {
getCurrentAuthenticationMethod,
isLdapCurrentAuthenticationMethod,
isSamlCurrentAuthenticationMethod,
} from './sso/ssoHelpers';
import { isExternalSecretsEnabled } from './ExternalSecrets/externalSecretsHelper.ee';
import { isSourceControlLicensed } from '@/environments/sourceControl/sourceControlHelper.ee';
import { SourceControlService } from '@/environments/sourceControl/sourceControl.service.ee';
import { SourceControlController } from '@/environments/sourceControl/sourceControl.controller.ee';
import { ExecutionRepository, SettingsRepository } from '@db/repositories';
@ -176,11 +154,6 @@ import { JwtService } from './services/jwt.service';
import { RoleService } from './services/role.service';
import { UserService } from './services/user.service';
import { OrchestrationController } from './controllers/orchestration.controller';
import {
getWorkflowHistoryLicensePruneTime,
getWorkflowHistoryPruneTime,
isWorkflowHistoryEnabled,
} from './workflows/workflowHistory/workflowHistoryHelper.ee';
import { WorkflowHistoryController } from './workflows/workflowHistory/workflowHistory.controller.ee';
const exec = promisify(callbackExec);
@ -192,8 +165,6 @@ export class Server extends AbstractServer {
activeExecutionsInstance: ActiveExecutions;
frontendSettings: IN8nUISettings;
presetCredentialsLoaded: boolean;
loadNodesAndCredentials: LoadNodesAndCredentials;
@ -217,146 +188,6 @@ export class Server extends AbstractServer {
this.testWebhooksEnabled = true;
this.webhooksEnabled = !config.getEnv('endpoints.disableProductionWebhooksOnMainProcess');
const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl();
const telemetrySettings: ITelemetrySettings = {
enabled: config.getEnv('diagnostics.enabled'),
};
if (telemetrySettings.enabled) {
const conf = config.getEnv('diagnostics.config.frontend');
const [key, url] = conf.split(';');
if (!key || !url) {
LoggerProxy.warn('Diagnostics frontend config is invalid');
telemetrySettings.enabled = false;
}
telemetrySettings.config = { key, url };
}
// Define it here to avoid calling the function multiple times
const instanceBaseUrl = getInstanceBaseUrl();
this.frontendSettings = {
endpointWebhook: this.endpointWebhook,
endpointWebhookTest: this.endpointWebhookTest,
saveDataErrorExecution: config.getEnv('executions.saveDataOnError'),
saveDataSuccessExecution: config.getEnv('executions.saveDataOnSuccess'),
saveManualExecutions: config.getEnv('executions.saveDataManualExecutions'),
executionTimeout: config.getEnv('executions.timeout'),
maxExecutionTimeout: config.getEnv('executions.maxTimeout'),
workflowCallerPolicyDefaultOption: config.getEnv('workflows.callerPolicyDefaultOption'),
timezone: this.timezone,
urlBaseWebhook,
urlBaseEditor: instanceBaseUrl,
versionCli: '',
releaseChannel: config.getEnv('generic.releaseChannel'),
oauthCallbackUrls: {
oauth1: `${instanceBaseUrl}/${this.restEndpoint}/oauth1-credential/callback`,
oauth2: `${instanceBaseUrl}/${this.restEndpoint}/oauth2-credential/callback`,
},
versionNotifications: {
enabled: config.getEnv('versionNotifications.enabled'),
endpoint: config.getEnv('versionNotifications.endpoint'),
infoUrl: config.getEnv('versionNotifications.infoUrl'),
},
instanceId: '',
telemetry: telemetrySettings,
posthog: {
enabled: config.getEnv('diagnostics.enabled'),
apiHost: config.getEnv('diagnostics.config.posthog.apiHost'),
apiKey: config.getEnv('diagnostics.config.posthog.apiKey'),
autocapture: false,
disableSessionRecording: config.getEnv(
'diagnostics.config.posthog.disableSessionRecording',
),
debug: config.getEnv('logs.level') === 'debug',
},
personalizationSurveyEnabled:
config.getEnv('personalization.enabled') && config.getEnv('diagnostics.enabled'),
defaultLocale: config.getEnv('defaultLocale'),
userManagement: {
quota: Container.get(License).getUsersLimit(),
showSetupOnFirstLoad: config.getEnv('userManagement.isInstanceOwnerSetUp') === false,
smtpSetup: isEmailSetUp(),
authenticationMethod: getCurrentAuthenticationMethod(),
},
sso: {
saml: {
loginEnabled: false,
loginLabel: '',
},
ldap: {
loginEnabled: false,
loginLabel: '',
},
},
publicApi: {
enabled: isApiEnabled(),
latestVersion: 1,
path: config.getEnv('publicApi.path'),
swaggerUi: {
enabled: !config.getEnv('publicApi.swaggerUi.disabled'),
},
},
workflowTagsDisabled: config.getEnv('workflowTagsDisabled'),
logLevel: config.getEnv('logs.level'),
hiringBannerEnabled: config.getEnv('hiringBanner.enabled'),
templates: {
enabled: config.getEnv('templates.enabled'),
host: config.getEnv('templates.host'),
},
onboardingCallPromptEnabled: config.getEnv('onboardingCallPrompt.enabled'),
executionMode: config.getEnv('executions.mode'),
pushBackend: config.getEnv('push.backend'),
communityNodesEnabled: config.getEnv('nodes.communityPackages.enabled'),
deployment: {
type: config.getEnv('deployment.type'),
},
isNpmAvailable: false,
allowedModules: {
builtIn: process.env.NODE_FUNCTION_ALLOW_BUILTIN?.split(',') ?? undefined,
external: process.env.NODE_FUNCTION_ALLOW_EXTERNAL?.split(',') ?? undefined,
},
enterprise: {
sharing: false,
ldap: false,
saml: false,
logStreaming: false,
advancedExecutionFilters: false,
variables: false,
sourceControl: false,
auditLogs: false,
externalSecrets: false,
showNonProdBanner: false,
debugInEditor: false,
workflowHistory: false,
},
mfa: {
enabled: false,
},
hideUsagePage: config.getEnv('hideUsagePage'),
license: {
environment: config.getEnv('license.tenantId') === 1 ? 'production' : 'staging',
},
variables: {
limit: 0,
},
expressions: {
evaluator: config.getEnv('expression.evaluator'),
},
banners: {
dismissed: [],
},
ai: {
enabled: config.getEnv('ai.enabled'),
},
workflowHistory: {
pruneTime: -1,
licensePruneTime: -1,
},
};
}
async start() {
@ -443,92 +274,6 @@ export class Server extends AbstractServer {
);
}
/**
* Returns the current settings for the frontend
*/
private async getSettingsForFrontend(): Promise<IN8nUISettings> {
// Update all urls, in case `WEBHOOK_URL` was updated by `--tunnel`
const instanceBaseUrl = getInstanceBaseUrl();
this.frontendSettings.urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl();
this.frontendSettings.urlBaseEditor = instanceBaseUrl;
this.frontendSettings.oauthCallbackUrls = {
oauth1: `${instanceBaseUrl}/${this.restEndpoint}/oauth1-credential/callback`,
oauth2: `${instanceBaseUrl}/${this.restEndpoint}/oauth2-credential/callback`,
};
// refresh user management status
Object.assign(this.frontendSettings.userManagement, {
quota: Container.get(License).getUsersLimit(),
authenticationMethod: getCurrentAuthenticationMethod(),
showSetupOnFirstLoad:
config.getEnv('userManagement.isInstanceOwnerSetUp') === false &&
config.getEnv('deployment.type').startsWith('desktop_') === false,
});
let dismissedBanners: string[] = [];
try {
dismissedBanners = config.getEnv('ui.banners.dismissed') ?? [];
} catch {
// not yet in DB
}
this.frontendSettings.banners.dismissed = dismissedBanners;
// refresh enterprise status
Object.assign(this.frontendSettings.enterprise, {
sharing: isSharingEnabled(),
logStreaming: isLogStreamingEnabled(),
ldap: isLdapEnabled(),
saml: isSamlLicensed(),
advancedExecutionFilters: isAdvancedExecutionFiltersEnabled(),
variables: isVariablesEnabled(),
sourceControl: isSourceControlLicensed(),
externalSecrets: isExternalSecretsEnabled(),
showNonProdBanner: Container.get(License).isFeatureEnabled(
LICENSE_FEATURES.SHOW_NON_PROD_BANNER,
),
debugInEditor: isDebugInEditorLicensed(),
workflowHistory: isWorkflowHistoryEnabled(),
});
if (isLdapEnabled()) {
Object.assign(this.frontendSettings.sso.ldap, {
loginLabel: getLdapLoginLabel(),
loginEnabled: isLdapLoginEnabled(),
});
}
if (isSamlLicensed()) {
Object.assign(this.frontendSettings.sso.saml, {
loginLabel: getSamlLoginLabel(),
loginEnabled: isSamlLoginEnabled(),
});
}
if (isVariablesEnabled()) {
this.frontendSettings.variables.limit = getVariablesLimit();
}
if (isWorkflowHistoryEnabled()) {
Object.assign(this.frontendSettings.workflowHistory, {
pruneTime: getWorkflowHistoryPruneTime(),
licensePruneTime: getWorkflowHistoryLicensePruneTime(),
});
}
if (config.getEnv('nodes.communityPackages.enabled')) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { CommunityPackagesService } = await import('@/services/communityPackages.service');
this.frontendSettings.missingPackages =
Container.get(CommunityPackagesService).hasMissingPackages;
}
this.frontendSettings.mfa.enabled = isMfaFeatureEnabled();
return this.frontendSettings;
}
private async registerControllers(ignoredEndpoints: Readonly<string[]>) {
const { app, externalHooks, activeWorkflowRunner, nodeTypes } = this;
const repositories = Db.collections;
@ -628,17 +373,17 @@ export class Server extends AbstractServer {
this.instanceId = await UserSettings.getInstanceId();
this.frontendSettings.isNpmAvailable = await exec('npm --version')
.then(() => true)
.catch(() => false);
this.frontendService.addToSettings({
isNpmAvailable: await exec('npm --version')
.then(() => true)
.catch(() => false),
versionCli: N8N_VERSION,
instanceId: this.instanceId,
});
this.frontendSettings.versionCli = N8N_VERSION;
await this.externalHooks.run('frontend.settings', [this.frontendService.getSettings()]);
this.frontendSettings.instanceId = this.instanceId;
await this.externalHooks.run('frontend.settings', [this.frontendSettings]);
await this.postHog.init(this.frontendSettings.instanceId);
await this.postHog.init(this.instanceId);
const publicApiEndpoint = config.getEnv('publicApi.path');
const excludeEndpoints = config.getEnv('security.excludeEndpoints');
@ -665,7 +410,7 @@ export class Server extends AbstractServer {
if (isApiEnabled()) {
const { apiRouters, apiLatestVersion } = await loadPublicApiVersions(publicApiEndpoint);
this.app.use(...apiRouters);
this.frontendSettings.publicApi.latestVersion = apiLatestVersion;
this.frontendService.settings.publicApi.latestVersion = apiLatestVersion;
}
// Parse cookies for easier access
this.app.use(cookieParser());
@ -1465,7 +1210,7 @@ export class Server extends AbstractServer {
async (req: express.Request, res: express.Response): Promise<IN8nUISettings> => {
void Container.get(InternalHooks).onFrontendSettingsAPI(req.headers.sessionid as string);
return this.getSettingsForFrontend();
return this.frontendService.getSettings();
},
),
);

View File

@ -1,23 +1,187 @@
import { Service } from 'typedi';
import { Container, Service } from 'typedi';
import uniq from 'lodash/uniq';
import { createWriteStream } from 'fs';
import { mkdir } from 'fs/promises';
import path from 'path';
import type { ICredentialType, INodeTypeBaseDescription } from 'n8n-workflow';
import type {
ICredentialType,
IN8nUISettings,
INodeTypeBaseDescription,
ITelemetrySettings,
} from 'n8n-workflow';
import { GENERATED_STATIC_DIR } from '@/constants';
import { GENERATED_STATIC_DIR, LICENSE_FEATURES } from '@/constants';
import { CredentialsOverwrites } from '@/CredentialsOverwrites';
import { CredentialTypes } from '@/CredentialTypes';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { License } from '@/License';
import { getInstanceBaseUrl, isEmailSetUp } from '@/UserManagement/UserManagementHelper';
import * as WebhookHelpers from '@/WebhookHelpers';
import { LoggerProxy } from 'n8n-workflow';
import config from '@/config';
import { getCurrentAuthenticationMethod } from '@/sso/ssoHelpers';
import { getLdapLoginLabel } from '@/Ldap/helpers';
import { getSamlLoginLabel } from '@/sso/saml/samlHelpers';
import { getVariablesLimit } from '@/environments/variables/enviromentHelpers';
import {
getWorkflowHistoryLicensePruneTime,
getWorkflowHistoryPruneTime,
} from '@/workflows/workflowHistory/workflowHistoryHelper.ee';
@Service()
export class FrontendService {
settings: IN8nUISettings;
constructor(
private readonly loadNodesAndCredentials: LoadNodesAndCredentials,
private readonly credentialTypes: CredentialTypes,
private readonly credentialsOverwrites: CredentialsOverwrites,
) {}
private readonly license: License,
) {
this.initSettings();
}
private initSettings() {
const instanceBaseUrl = getInstanceBaseUrl();
const restEndpoint = config.getEnv('endpoints.rest');
const telemetrySettings: ITelemetrySettings = {
enabled: config.getEnv('diagnostics.enabled'),
};
if (telemetrySettings.enabled) {
const conf = config.getEnv('diagnostics.config.frontend');
const [key, url] = conf.split(';');
if (!key || !url) {
LoggerProxy.warn('Diagnostics frontend config is invalid');
telemetrySettings.enabled = false;
}
telemetrySettings.config = { key, url };
}
this.settings = {
endpointWebhook: config.getEnv('endpoints.webhook'),
endpointWebhookTest: config.getEnv('endpoints.webhookTest'),
saveDataErrorExecution: config.getEnv('executions.saveDataOnError'),
saveDataSuccessExecution: config.getEnv('executions.saveDataOnSuccess'),
saveManualExecutions: config.getEnv('executions.saveDataManualExecutions'),
executionTimeout: config.getEnv('executions.timeout'),
maxExecutionTimeout: config.getEnv('executions.maxTimeout'),
workflowCallerPolicyDefaultOption: config.getEnv('workflows.callerPolicyDefaultOption'),
timezone: config.getEnv('generic.timezone'),
urlBaseWebhook: WebhookHelpers.getWebhookBaseUrl(),
urlBaseEditor: instanceBaseUrl,
versionCli: '',
releaseChannel: config.getEnv('generic.releaseChannel'),
oauthCallbackUrls: {
oauth1: `${instanceBaseUrl}/${restEndpoint}/oauth1-credential/callback`,
oauth2: `${instanceBaseUrl}/${restEndpoint}/oauth2-credential/callback`,
},
versionNotifications: {
enabled: config.getEnv('versionNotifications.enabled'),
endpoint: config.getEnv('versionNotifications.endpoint'),
infoUrl: config.getEnv('versionNotifications.infoUrl'),
},
instanceId: '',
telemetry: telemetrySettings,
posthog: {
enabled: config.getEnv('diagnostics.enabled'),
apiHost: config.getEnv('diagnostics.config.posthog.apiHost'),
apiKey: config.getEnv('diagnostics.config.posthog.apiKey'),
autocapture: false,
disableSessionRecording: config.getEnv(
'diagnostics.config.posthog.disableSessionRecording',
),
debug: config.getEnv('logs.level') === 'debug',
},
personalizationSurveyEnabled:
config.getEnv('personalization.enabled') && config.getEnv('diagnostics.enabled'),
defaultLocale: config.getEnv('defaultLocale'),
userManagement: {
quota: this.license.getUsersLimit(),
showSetupOnFirstLoad: !config.getEnv('userManagement.isInstanceOwnerSetUp'),
smtpSetup: isEmailSetUp(),
authenticationMethod: getCurrentAuthenticationMethod(),
},
sso: {
saml: {
loginEnabled: false,
loginLabel: '',
},
ldap: {
loginEnabled: false,
loginLabel: '',
},
},
publicApi: {
enabled: !config.get('publicApi.disabled') && !this.license.isAPIDisabled(),
latestVersion: 1,
path: config.getEnv('publicApi.path'),
swaggerUi: {
enabled: !config.getEnv('publicApi.swaggerUi.disabled'),
},
},
workflowTagsDisabled: config.getEnv('workflowTagsDisabled'),
logLevel: config.getEnv('logs.level'),
hiringBannerEnabled: config.getEnv('hiringBanner.enabled'),
templates: {
enabled: config.getEnv('templates.enabled'),
host: config.getEnv('templates.host'),
},
onboardingCallPromptEnabled: config.getEnv('onboardingCallPrompt.enabled'),
executionMode: config.getEnv('executions.mode'),
pushBackend: config.getEnv('push.backend'),
communityNodesEnabled: config.getEnv('nodes.communityPackages.enabled'),
deployment: {
type: config.getEnv('deployment.type'),
},
isNpmAvailable: false,
allowedModules: {
builtIn: process.env.NODE_FUNCTION_ALLOW_BUILTIN?.split(',') ?? undefined,
external: process.env.NODE_FUNCTION_ALLOW_EXTERNAL?.split(',') ?? undefined,
},
enterprise: {
sharing: false,
ldap: false,
saml: false,
logStreaming: false,
advancedExecutionFilters: false,
variables: false,
sourceControl: false,
auditLogs: false,
externalSecrets: false,
showNonProdBanner: false,
debugInEditor: false,
workflowHistory: false,
},
mfa: {
enabled: false,
},
hideUsagePage: config.getEnv('hideUsagePage'),
license: {
environment: config.getEnv('license.tenantId') === 1 ? 'production' : 'staging',
},
variables: {
limit: 0,
},
expressions: {
evaluator: config.getEnv('expression.evaluator'),
},
banners: {
dismissed: [],
},
ai: {
enabled: config.getEnv('ai.enabled'),
},
workflowHistory: {
pruneTime: -1,
licensePruneTime: -1,
},
};
}
async generateTypes() {
this.overwriteCredentialsProperties();
@ -29,6 +193,93 @@ export class FrontendService {
this.writeStaticJSON('credentials', credentials);
}
async getSettings(): Promise<IN8nUISettings> {
const restEndpoint = config.getEnv('endpoints.rest');
// Update all urls, in case `WEBHOOK_URL` was updated by `--tunnel`
const instanceBaseUrl = getInstanceBaseUrl();
this.settings.urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl();
this.settings.urlBaseEditor = instanceBaseUrl;
this.settings.oauthCallbackUrls = {
oauth1: `${instanceBaseUrl}/${restEndpoint}/oauth1-credential/callback`,
oauth2: `${instanceBaseUrl}/${restEndpoint}/oauth2-credential/callback`,
};
// refresh user management status
Object.assign(this.settings.userManagement, {
quota: this.license.getUsersLimit(),
authenticationMethod: getCurrentAuthenticationMethod(),
showSetupOnFirstLoad:
!config.getEnv('userManagement.isInstanceOwnerSetUp') &&
!config.getEnv('deployment.type').startsWith('desktop_'),
});
let dismissedBanners: string[] = [];
try {
dismissedBanners = config.getEnv('ui.banners.dismissed') ?? [];
} catch {
// not yet in DB
}
this.settings.banners.dismissed = dismissedBanners;
// refresh enterprise status
Object.assign(this.settings.enterprise, {
sharing: this.license.isSharingEnabled(),
logStreaming: this.license.isLogStreamingEnabled(),
ldap: this.license.isLdapEnabled(),
saml: this.license.isSamlEnabled(),
advancedExecutionFilters: this.license.isAdvancedExecutionFiltersEnabled(),
variables: this.license.isVariablesEnabled(),
sourceControl: this.license.isSourceControlLicensed(),
externalSecrets: this.license.isExternalSecretsEnabled(),
showNonProdBanner: this.license.isFeatureEnabled(LICENSE_FEATURES.SHOW_NON_PROD_BANNER),
debugInEditor: this.license.isDebugInEditorLicensed(),
workflowHistory:
this.license.isWorkflowHistoryLicensed() && config.getEnv('workflowHistory.enabled'),
});
if (this.license.isLdapEnabled()) {
Object.assign(this.settings.sso.ldap, {
loginLabel: getLdapLoginLabel(),
loginEnabled: config.getEnv('sso.ldap.loginEnabled'),
});
}
if (this.license.isSamlEnabled()) {
Object.assign(this.settings.sso.saml, {
loginLabel: getSamlLoginLabel(),
loginEnabled: config.getEnv('sso.saml.loginEnabled'),
});
}
if (this.license.isVariablesEnabled()) {
this.settings.variables.limit = getVariablesLimit();
}
if (this.license.isWorkflowHistoryLicensed() && config.getEnv('workflowHistory.enabled')) {
Object.assign(this.settings.workflowHistory, {
pruneTime: getWorkflowHistoryPruneTime(),
licensePruneTime: getWorkflowHistoryLicensePruneTime(),
});
}
if (config.getEnv('nodes.communityPackages.enabled')) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { CommunityPackagesService } = await import('@/services/communityPackages.service');
this.settings.missingPackages = Container.get(CommunityPackagesService).hasMissingPackages;
}
this.settings.mfa.enabled = config.get('mfa.enabled');
return this.settings;
}
addToSettings(newSettings: Record<string, unknown>) {
this.settings = { ...this.settings, ...newSettings };
}
private writeStaticJSON(name: string, data: INodeTypeBaseDescription[] | ICredentialType[]) {
const filePath = path.join(GENERATED_STATIC_DIR, `types/${name}.json`);
const stream = createWriteStream(filePath, 'utf-8');