From 72249e0de803184d019b1cba587a7c905bf68f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Mon, 30 Jan 2023 14:42:30 +0100 Subject: [PATCH] refactor(core): Load and validate all config at startup (no-changelog) (#5283) --- packages/cli/src/CredentialsOverwrites.ts | 9 +-- packages/cli/src/Db.ts | 35 ++++------ packages/cli/src/GenericHelpers.ts | 68 +------------------ packages/cli/src/Server.ts | 40 +++-------- .../email/UserManagementMailer.ts | 5 +- packages/cli/src/WaitTracker.ts | 6 +- packages/cli/src/WorkflowRunnerProcess.ts | 3 +- packages/cli/src/commands/db/revert.ts | 2 +- packages/cli/src/commands/execute.ts | 2 +- packages/cli/src/commands/executeBatch.ts | 2 +- packages/cli/src/commands/start.ts | 6 +- packages/cli/src/commands/webhook.ts | 2 +- packages/cli/src/commands/worker.ts | 2 +- packages/cli/src/config/index.ts | 45 +++++++++--- packages/cli/src/databases/config.ts | 13 ++-- .../cli/src/executions/executions.service.ts | 3 +- 16 files changed, 80 insertions(+), 163 deletions(-) diff --git a/packages/cli/src/CredentialsOverwrites.ts b/packages/cli/src/CredentialsOverwrites.ts index 414f3eb349e5d..c3a5702df113f 100644 --- a/packages/cli/src/CredentialsOverwrites.ts +++ b/packages/cli/src/CredentialsOverwrites.ts @@ -1,18 +1,15 @@ +import config from '@/config'; import type { ICredentialDataDecryptedObject, ICredentialTypes } from 'n8n-workflow'; import { deepCopy, LoggerProxy as Logger, jsonParse } from 'n8n-workflow'; import type { ICredentialsOverwrite } from '@/Interfaces'; -import * as GenericHelpers from '@/GenericHelpers'; class CredentialsOverwritesClass { private overwriteData: ICredentialsOverwrite = {}; private resolvedTypes: string[] = []; - constructor(private credentialTypes: ICredentialTypes) {} - - async init() { - const data = (await GenericHelpers.getConfigValue('credentials.overwrite.data')) as string; - + constructor(private credentialTypes: ICredentialTypes) { + const data = config.getEnv('credentials.overwrite.data'); const overwriteData = jsonParse(data, { errorMessage: 'The credentials-overwrite is not valid JSON.', }); diff --git a/packages/cli/src/Db.ts b/packages/cli/src/Db.ts index 76bdd1e905781..68ff6a0adaf38 100644 --- a/packages/cli/src/Db.ts +++ b/packages/cli/src/Db.ts @@ -14,7 +14,6 @@ import type { import { DataSource as Connection } from 'typeorm'; import type { TlsOptions } from 'tls'; import type { DatabaseType, IDatabaseCollections } from '@/Interfaces'; -import * as GenericHelpers from '@/GenericHelpers'; import config from '@/config'; @@ -44,17 +43,13 @@ export function linkRepository( return connection.getRepository(entityClass); } -export async function getConnectionOptions(dbType: DatabaseType): Promise { +export function getConnectionOptions(dbType: DatabaseType): ConnectionOptions { switch (dbType) { case 'postgresdb': - const sslCa = (await GenericHelpers.getConfigValue('database.postgresdb.ssl.ca')) as string; - const sslCert = (await GenericHelpers.getConfigValue( - 'database.postgresdb.ssl.cert', - )) as string; - const sslKey = (await GenericHelpers.getConfigValue('database.postgresdb.ssl.key')) as string; - const sslRejectUnauthorized = (await GenericHelpers.getConfigValue( - 'database.postgresdb.ssl.rejectUnauthorized', - )) as boolean; + const sslCa = config.getEnv('database.postgresdb.ssl.ca'); + const sslCert = config.getEnv('database.postgresdb.ssl.cert'); + const sslKey = config.getEnv('database.postgresdb.ssl.key'); + const sslRejectUnauthorized = config.getEnv('database.postgresdb.ssl.rejectUnauthorized'); let ssl: TlsOptions | undefined; if (sslCa !== '' || sslCert !== '' || sslKey !== '' || !sslRejectUnauthorized) { @@ -68,7 +63,7 @@ export async function getConnectionOptions(dbType: DatabaseType): Promise { if (isInitialized) return collections; - const dbType = (await GenericHelpers.getConfigValue('database.type')) as DatabaseType; - const connectionOptions = testConnectionOptions ?? (await getConnectionOptions(dbType)); + const dbType = config.getEnv('database.type'); + const connectionOptions = testConnectionOptions ?? getConnectionOptions(dbType); - let loggingOption: LoggerOptions = (await GenericHelpers.getConfigValue( - 'database.logging.enabled', - )) as boolean; + let loggingOption: LoggerOptions = config.getEnv('database.logging.enabled'); if (loggingOption) { - const optionsString = ( - (await GenericHelpers.getConfigValue('database.logging.options')) as string - ).replace(/\s+/g, ''); + const optionsString = config.getEnv('database.logging.options').replace(/\s+/g, ''); if (optionsString === 'all') { loggingOption = optionsString; @@ -112,9 +103,7 @@ export async function init( } } - const maxQueryExecutionTime = (await GenericHelpers.getConfigValue( - 'database.logging.maxQueryExecutionTime', - )) as string; + const maxQueryExecutionTime = config.getEnv('database.logging.maxQueryExecutionTime'); Object.assign(connectionOptions, { entities: Object.values(entities), diff --git a/packages/cli/src/GenericHelpers.ts b/packages/cli/src/GenericHelpers.ts index 90810af1a57ea..e3940a620b7c3 100644 --- a/packages/cli/src/GenericHelpers.ts +++ b/packages/cli/src/GenericHelpers.ts @@ -5,22 +5,19 @@ /* eslint-disable no-underscore-dangle */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import type express from 'express'; -import { readFile as fsReadFile } from 'fs/promises'; import type { ExecutionError, - IDataObject, INode, IRunExecutionData, Workflow, WorkflowExecuteMode, } from 'n8n-workflow'; import { validate } from 'class-validator'; +import { Like } from 'typeorm'; import config from '@/config'; import * as Db from '@/Db'; import type { ICredentialsDb, IExecutionDb, IExecutionFlattedDb, IWorkflowDb } from '@/Interfaces'; import * as ResponseHelper from '@/ResponseHelper'; -// eslint-disable-next-line import/order -import { Like } from 'typeorm'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; import type { TagEntity } from '@db/entities/TagEntity'; @@ -28,7 +25,6 @@ import type { User } from '@db/entities/User'; /** * Returns the base URL n8n is reachable from - * */ export function getBaseUrl(): string { const protocol = config.getEnv('protocol'); @@ -44,73 +40,11 @@ export function getBaseUrl(): string { /** * Returns the session id if one is set - * */ export function getSessionId(req: express.Request): string | undefined { return req.headers.sessionid as string | undefined; } -/** - * Extracts configuration schema for key - */ -function extractSchemaForKey(configKey: string, configSchema: IDataObject): IDataObject { - const configKeyParts = configKey.split('.'); - - // eslint-disable-next-line no-restricted-syntax - for (const key of configKeyParts) { - if (configSchema[key] === undefined) { - throw new Error(`Key "${key}" of ConfigKey "${configKey}" does not exist`); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - } else if ((configSchema[key]! as IDataObject)._cvtProperties === undefined) { - configSchema = configSchema[key] as IDataObject; - } else { - configSchema = (configSchema[key] as IDataObject)._cvtProperties as IDataObject; - } - } - return configSchema; -} - -/** - * Gets value from config with support for "_FILE" environment variables - * - * @param {string} configKey The key of the config data to get - */ -export async function getConfigValue( - configKey: string, -): Promise { - // Get the environment variable - const configSchema = config.getSchema(); - // @ts-ignore - const currentSchema = extractSchemaForKey(configKey, configSchema._cvtProperties as IDataObject); - // Check if environment variable is defined for config key - if (currentSchema.env === undefined) { - // No environment variable defined, so return value from config - // @ts-ignore - return config.getEnv(configKey); - } - - // Check if special file environment variable exists - const fileEnvironmentVariable = process.env[`${currentSchema.env}_FILE`]; - if (fileEnvironmentVariable === undefined) { - // Does not exist, so return value from config - // @ts-ignore - return config.getEnv(configKey); - } - - let data; - try { - data = await fsReadFile(fileEnvironmentVariable, 'utf8'); - } catch (error) { - if (error.code === 'ENOENT') { - throw new Error(`The file "${fileEnvironmentVariable}" could not be found.`); - } - - throw error; - } - - return data; -} - /** * Generate a unique name for a workflow or credentials entity. * diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 257d6e2045052..37875490feb03 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -409,23 +409,17 @@ class Server extends AbstractServer { // Check for basic auth credentials if activated const basicAuthActive = config.getEnv('security.basicAuth.active'); if (basicAuthActive) { - const basicAuthUser = (await GenericHelpers.getConfigValue( - 'security.basicAuth.user', - )) as string; + const basicAuthUser = config.getEnv('security.basicAuth.user'); if (basicAuthUser === '') { throw new Error('Basic auth is activated but no user got defined. Please set one!'); } - const basicAuthPassword = (await GenericHelpers.getConfigValue( - 'security.basicAuth.password', - )) as string; + const basicAuthPassword = config.getEnv('security.basicAuth.password'); if (basicAuthPassword === '') { throw new Error('Basic auth is activated but no password got defined. Please set one!'); } - const basicAuthHashEnabled = (await GenericHelpers.getConfigValue( - 'security.basicAuth.hash', - )) as boolean; + const basicAuthHashEnabled = config.getEnv('security.basicAuth.hash') as boolean; let validPassword: null | string = null; @@ -483,31 +477,19 @@ class Server extends AbstractServer { // Check for and validate JWT if configured const jwtAuthActive = config.getEnv('security.jwtAuth.active'); if (jwtAuthActive) { - const jwtAuthHeader = (await GenericHelpers.getConfigValue( - 'security.jwtAuth.jwtHeader', - )) as string; + const jwtAuthHeader = config.getEnv('security.jwtAuth.jwtHeader'); if (jwtAuthHeader === '') { throw new Error('JWT auth is activated but no request header was defined. Please set one!'); } - const jwksUri = (await GenericHelpers.getConfigValue('security.jwtAuth.jwksUri')) as string; + const jwksUri = config.getEnv('security.jwtAuth.jwksUri'); if (jwksUri === '') { throw new Error('JWT auth is activated but no JWK Set URI was defined. Please set one!'); } - const jwtHeaderValuePrefix = (await GenericHelpers.getConfigValue( - 'security.jwtAuth.jwtHeaderValuePrefix', - )) as string; - const jwtIssuer = (await GenericHelpers.getConfigValue( - 'security.jwtAuth.jwtIssuer', - )) as string; - const jwtNamespace = (await GenericHelpers.getConfigValue( - 'security.jwtAuth.jwtNamespace', - )) as string; - const jwtAllowedTenantKey = (await GenericHelpers.getConfigValue( - 'security.jwtAuth.jwtAllowedTenantKey', - )) as string; - const jwtAllowedTenant = (await GenericHelpers.getConfigValue( - 'security.jwtAuth.jwtAllowedTenant', - )) as string; + const jwtHeaderValuePrefix = config.getEnv('security.jwtAuth.jwtHeaderValuePrefix'); + const jwtIssuer = config.getEnv('security.jwtAuth.jwtIssuer'); + const jwtNamespace = config.getEnv('security.jwtAuth.jwtNamespace'); + const jwtAllowedTenantKey = config.getEnv('security.jwtAuth.jwtAllowedTenantKey'); + const jwtAllowedTenant = config.getEnv('security.jwtAuth.jwtAllowedTenant'); // eslint-disable-next-line no-inner-declarations function isTenantAllowed(decodedToken: object): boolean { @@ -1456,7 +1438,7 @@ export async function start(): Promise { const binaryDataConfig = config.getEnv('binaryDataManager'); const diagnosticInfo: IDiagnosticInfo = { basicAuthActive: config.getEnv('security.basicAuth.active'), - databaseType: (await GenericHelpers.getConfigValue('database.type')) as DatabaseType, + databaseType: config.getEnv('database.type'), disableProductionWebhooksOnMainProcess: config.getEnv( 'endpoints.disableProductionWebhooksOnMainProcess', ), diff --git a/packages/cli/src/UserManagement/email/UserManagementMailer.ts b/packages/cli/src/UserManagement/email/UserManagementMailer.ts index 67cc4ec6e28f6..2c9ef67198177 100644 --- a/packages/cli/src/UserManagement/email/UserManagementMailer.ts +++ b/packages/cli/src/UserManagement/email/UserManagementMailer.ts @@ -2,7 +2,6 @@ import { existsSync } from 'fs'; import { readFile } from 'fs/promises'; import Handlebars from 'handlebars'; import { join as pathJoin } from 'path'; -import * as GenericHelpers from '@/GenericHelpers'; import config from '@/config'; import type { InviteEmailData, @@ -23,9 +22,7 @@ async function getTemplate( ): Promise