diff --git a/packages/@n8n/config/src/configs/cache.ts b/packages/@n8n/config/src/configs/cache.ts new file mode 100644 index 0000000000..8a24bdc18b --- /dev/null +++ b/packages/@n8n/config/src/configs/cache.ts @@ -0,0 +1,36 @@ +import { Config, Env, Nested } from '../decorators'; + +@Config +class MemoryConfig { + /** Max size of memory cache in bytes */ + @Env('N8N_CACHE_MEMORY_MAX_SIZE') + maxSize = 3 * 1024 * 1024; // 3 MiB + + /** Time to live (in milliseconds) for data cached in memory. */ + @Env('N8N_CACHE_MEMORY_TTL') + ttl = 3600 * 1000; // 1 hour +} + +@Config +class RedisConfig { + /** Prefix for cache keys in Redis. */ + @Env('N8N_CACHE_REDIS_KEY_PREFIX') + prefix = 'redis'; + + /** Time to live (in milliseconds) for data cached in Redis. 0 for no TTL. */ + @Env('N8N_CACHE_REDIS_TTL') + ttl = 3600 * 1000; // 1 hour +} + +@Config +export class CacheConfig { + /** Backend to use for caching. */ + @Env('N8N_CACHE_BACKEND') + backend: 'memory' | 'redis' | 'auto' = 'auto'; + + @Nested + memory: MemoryConfig; + + @Nested + redis: RedisConfig; +} diff --git a/packages/@n8n/config/src/configs/credentials.ts b/packages/@n8n/config/src/configs/credentials.ts index 9659061c05..ee5f78a681 100644 --- a/packages/@n8n/config/src/configs/credentials.ts +++ b/packages/@n8n/config/src/configs/credentials.ts @@ -7,19 +7,19 @@ class CredentialsOverwrite { * Format: { CREDENTIAL_NAME: { PARAMETER: VALUE }} */ @Env('CREDENTIALS_OVERWRITE_DATA') - readonly data: string = '{}'; + data = '{}'; /** Internal API endpoint to fetch overwritten credential types from. */ @Env('CREDENTIALS_OVERWRITE_ENDPOINT') - readonly endpoint: string = ''; + endpoint = ''; } @Config export class CredentialsConfig { /** Default name for credentials */ @Env('CREDENTIALS_DEFAULT_NAME') - readonly defaultName: string = 'My credentials'; + defaultName = 'My credentials'; @Nested - readonly overwrite: CredentialsOverwrite; + overwrite: CredentialsOverwrite; } diff --git a/packages/@n8n/config/src/configs/database.ts b/packages/@n8n/config/src/configs/database.ts index 384ecb1fb0..06a3f85465 100644 --- a/packages/@n8n/config/src/configs/database.ts +++ b/packages/@n8n/config/src/configs/database.ts @@ -4,19 +4,19 @@ import { Config, Env, Nested } from '../decorators'; class LoggingConfig { /** Whether database logging is enabled. */ @Env('DB_LOGGING_ENABLED') - readonly enabled: boolean = false; + enabled = false; /** * Database logging level. Requires `DB_LOGGING_MAX_EXECUTION_TIME` to be higher than `0`. */ @Env('DB_LOGGING_OPTIONS') - readonly options: 'query' | 'error' | 'schema' | 'warn' | 'info' | 'log' | 'all' = 'error'; + options: 'query' | 'error' | 'schema' | 'warn' | 'info' | 'log' | 'all' = 'error'; /** * Only queries that exceed this time (ms) will be logged. Set `0` to disable. */ @Env('DB_LOGGING_MAX_EXECUTION_TIME') - readonly maxQueryExecutionTime: number = 0; + maxQueryExecutionTime = 0; } @Config @@ -26,97 +26,97 @@ class PostgresSSLConfig { * If `DB_POSTGRESDB_SSL_CA`, `DB_POSTGRESDB_SSL_CERT`, or `DB_POSTGRESDB_SSL_KEY` are defined, `DB_POSTGRESDB_SSL_ENABLED` defaults to `true`. */ @Env('DB_POSTGRESDB_SSL_ENABLED') - readonly enabled: boolean = false; + enabled = false; /** SSL certificate authority */ @Env('DB_POSTGRESDB_SSL_CA') - readonly ca: string = ''; + ca = ''; /** SSL certificate */ @Env('DB_POSTGRESDB_SSL_CERT') - readonly cert: string = ''; + cert = ''; /** SSL key */ @Env('DB_POSTGRESDB_SSL_KEY') - readonly key: string = ''; + key = ''; /** If unauthorized SSL connections should be rejected */ @Env('DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED') - readonly rejectUnauthorized: boolean = true; + rejectUnauthorized = true; } @Config class PostgresConfig { /** Postgres database name */ @Env('DB_POSTGRESDB_DATABASE') - database: string = 'n8n'; + database = 'n8n'; /** Postgres database host */ @Env('DB_POSTGRESDB_HOST') - readonly host: string = 'localhost'; + host = 'localhost'; /** Postgres database password */ @Env('DB_POSTGRESDB_PASSWORD') - readonly password: string = ''; + password = ''; /** Postgres database port */ @Env('DB_POSTGRESDB_PORT') - readonly port: number = 5432; + port: number = 5432; /** Postgres database user */ @Env('DB_POSTGRESDB_USER') - readonly user: string = 'postgres'; + user = 'postgres'; /** Postgres database schema */ @Env('DB_POSTGRESDB_SCHEMA') - readonly schema: string = 'public'; + schema = 'public'; /** Postgres database pool size */ @Env('DB_POSTGRESDB_POOL_SIZE') - readonly poolSize = 2; + poolSize = 2; @Nested - readonly ssl: PostgresSSLConfig; + ssl: PostgresSSLConfig; } @Config class MysqlConfig { /** @deprecated MySQL database name */ @Env('DB_MYSQLDB_DATABASE') - database: string = 'n8n'; + database = 'n8n'; /** MySQL database host */ @Env('DB_MYSQLDB_HOST') - readonly host: string = 'localhost'; + host = 'localhost'; /** MySQL database password */ @Env('DB_MYSQLDB_PASSWORD') - readonly password: string = ''; + password = ''; /** MySQL database port */ @Env('DB_MYSQLDB_PORT') - readonly port: number = 3306; + port: number = 3306; /** MySQL database user */ @Env('DB_MYSQLDB_USER') - readonly user: string = 'root'; + user = 'root'; } @Config class SqliteConfig { /** SQLite database file name */ @Env('DB_SQLITE_DATABASE') - readonly database: string = 'database.sqlite'; + database = 'database.sqlite'; /** SQLite database pool size. Set to `0` to disable pooling. */ @Env('DB_SQLITE_POOL_SIZE') - readonly poolSize: number = 0; + poolSize: number = 0; /** * Enable SQLite WAL mode. */ @Env('DB_SQLITE_ENABLE_WAL') - readonly enableWAL: boolean = this.poolSize > 1; + enableWAL = this.poolSize > 1; /** * Run `VACUUM` on startup to rebuild the database, reducing file size and optimizing indexes. @@ -124,7 +124,7 @@ class SqliteConfig { * @warning Long-running blocking operation that will increase startup time. */ @Env('DB_SQLITE_VACUUM_ON_STARTUP') - readonly executeVacuumOnStartup: boolean = false; + executeVacuumOnStartup = false; } @Config @@ -135,17 +135,17 @@ export class DatabaseConfig { /** Prefix for table names */ @Env('DB_TABLE_PREFIX') - readonly tablePrefix: string = ''; + tablePrefix = ''; @Nested - readonly logging: LoggingConfig; + logging: LoggingConfig; @Nested - readonly postgresdb: PostgresConfig; + postgresdb: PostgresConfig; @Nested - readonly mysqldb: MysqlConfig; + mysqldb: MysqlConfig; @Nested - readonly sqlite: SqliteConfig; + sqlite: SqliteConfig; } diff --git a/packages/@n8n/config/src/configs/email.ts b/packages/@n8n/config/src/configs/email.ts index 318c352380..f0e130c3b4 100644 --- a/packages/@n8n/config/src/configs/email.ts +++ b/packages/@n8n/config/src/configs/email.ts @@ -4,75 +4,75 @@ import { Config, Env, Nested } from '../decorators'; export class SmtpAuth { /** SMTP login username */ @Env('N8N_SMTP_USER') - readonly user: string = ''; + user = ''; /** SMTP login password */ @Env('N8N_SMTP_PASS') - readonly pass: string = ''; + pass = ''; /** SMTP OAuth Service Client */ @Env('N8N_SMTP_OAUTH_SERVICE_CLIENT') - readonly serviceClient: string = ''; + serviceClient = ''; /** SMTP OAuth Private Key */ @Env('N8N_SMTP_OAUTH_PRIVATE_KEY') - readonly privateKey: string = ''; + privateKey = ''; } @Config export class SmtpConfig { /** SMTP server host */ @Env('N8N_SMTP_HOST') - readonly host: string = ''; + host = ''; /** SMTP server port */ @Env('N8N_SMTP_PORT') - readonly port: number = 465; + port: number = 465; /** Whether to use SSL for SMTP */ @Env('N8N_SMTP_SSL') - readonly secure: boolean = true; + secure: boolean = true; /** Whether to use STARTTLS for SMTP when SSL is disabled */ @Env('N8N_SMTP_STARTTLS') - readonly startTLS: boolean = true; + startTLS: boolean = true; /** How to display sender name */ @Env('N8N_SMTP_SENDER') - readonly sender: string = ''; + sender = ''; @Nested - readonly auth: SmtpAuth; + auth: SmtpAuth; } @Config export class TemplateConfig { /** Overrides default HTML template for inviting new people (use full path) */ @Env('N8N_UM_EMAIL_TEMPLATES_INVITE') - readonly invite: string = ''; + invite = ''; /** Overrides default HTML template for resetting password (use full path) */ @Env('N8N_UM_EMAIL_TEMPLATES_PWRESET') - readonly passwordReset: string = ''; + passwordReset = ''; /** Overrides default HTML template for notifying that a workflow was shared (use full path) */ @Env('N8N_UM_EMAIL_TEMPLATES_WORKFLOW_SHARED') - readonly workflowShared: string = ''; + workflowShared = ''; /** Overrides default HTML template for notifying that credentials were shared (use full path) */ @Env('N8N_UM_EMAIL_TEMPLATES_CREDENTIALS_SHARED') - readonly credentialsShared: string = ''; + credentialsShared = ''; } @Config export class EmailConfig { /** How to send emails */ @Env('N8N_EMAIL_MODE') - readonly mode: '' | 'smtp' = 'smtp'; + mode: '' | 'smtp' = 'smtp'; @Nested - readonly smtp: SmtpConfig; + smtp: SmtpConfig; @Nested - readonly template: TemplateConfig; + template: TemplateConfig; } diff --git a/packages/@n8n/config/src/configs/endpoints.ts b/packages/@n8n/config/src/configs/endpoints.ts index 7a04a8249f..4957c5afa5 100644 --- a/packages/@n8n/config/src/configs/endpoints.ts +++ b/packages/@n8n/config/src/configs/endpoints.ts @@ -4,99 +4,99 @@ import { Config, Env, Nested } from '../decorators'; class PrometheusMetricsConfig { /** Whether to enable the `/metrics` endpoint to expose Prometheus metrics. */ @Env('N8N_METRICS') - readonly enable: boolean = false; + enable = false; /** Prefix for Prometheus metric names. */ @Env('N8N_METRICS_PREFIX') - readonly prefix: string = 'n8n_'; + prefix = 'n8n_'; /** Whether to expose system and Node.js metrics. See: https://www.npmjs.com/package/prom-client */ @Env('N8N_METRICS_INCLUDE_DEFAULT_METRICS') - readonly includeDefaultMetrics = true; + includeDefaultMetrics = true; /** Whether to include a label for workflow ID on workflow metrics. */ @Env('N8N_METRICS_INCLUDE_WORKFLOW_ID_LABEL') - readonly includeWorkflowIdLabel: boolean = false; + includeWorkflowIdLabel = false; /** Whether to include a label for node type on node metrics. */ @Env('N8N_METRICS_INCLUDE_NODE_TYPE_LABEL') - readonly includeNodeTypeLabel: boolean = false; + includeNodeTypeLabel = false; /** Whether to include a label for credential type on credential metrics. */ @Env('N8N_METRICS_INCLUDE_CREDENTIAL_TYPE_LABEL') - readonly includeCredentialTypeLabel: boolean = false; + includeCredentialTypeLabel = false; /** Whether to expose metrics for API endpoints. See: https://www.npmjs.com/package/express-prom-bundle */ @Env('N8N_METRICS_INCLUDE_API_ENDPOINTS') - readonly includeApiEndpoints: boolean = false; + includeApiEndpoints = false; /** Whether to include a label for the path of API endpoint calls. */ @Env('N8N_METRICS_INCLUDE_API_PATH_LABEL') - readonly includeApiPathLabel: boolean = false; + includeApiPathLabel = false; /** Whether to include a label for the HTTP method of API endpoint calls. */ @Env('N8N_METRICS_INCLUDE_API_METHOD_LABEL') - readonly includeApiMethodLabel: boolean = false; + includeApiMethodLabel = false; /** Whether to include a label for the status code of API endpoint calls. */ @Env('N8N_METRICS_INCLUDE_API_STATUS_CODE_LABEL') - readonly includeApiStatusCodeLabel: boolean = false; + includeApiStatusCodeLabel = false; /** Whether to include metrics for cache hits and misses. */ @Env('N8N_METRICS_INCLUDE_CACHE_METRICS') - readonly includeCacheMetrics: boolean = false; + includeCacheMetrics = false; /** Whether to include metrics derived from n8n's internal events */ @Env('N8N_METRICS_INCLUDE_MESSAGE_EVENT_BUS_METRICS') - readonly includeMessageEventBusMetrics: boolean = false; + includeMessageEventBusMetrics = false; } @Config export class EndpointsConfig { /** Max payload size in MiB */ @Env('N8N_PAYLOAD_SIZE_MAX') - readonly payloadSizeMax: number = 16; + payloadSizeMax: number = 16; @Nested - readonly metrics: PrometheusMetricsConfig; + metrics: PrometheusMetricsConfig; /** Path segment for REST API endpoints. */ @Env('N8N_ENDPOINT_REST') - readonly rest: string = 'rest'; + rest = 'rest'; /** Path segment for form endpoints. */ @Env('N8N_ENDPOINT_FORM') - readonly form: string = 'form'; + form = 'form'; /** Path segment for test form endpoints. */ @Env('N8N_ENDPOINT_FORM_TEST') - readonly formTest: string = 'form-test'; + formTest = 'form-test'; /** Path segment for waiting form endpoints. */ @Env('N8N_ENDPOINT_FORM_WAIT') - readonly formWaiting: string = 'form-waiting'; + formWaiting = 'form-waiting'; /** Path segment for webhook endpoints. */ @Env('N8N_ENDPOINT_WEBHOOK') - readonly webhook: string = 'webhook'; + webhook = 'webhook'; /** Path segment for test webhook endpoints. */ @Env('N8N_ENDPOINT_WEBHOOK_TEST') - readonly webhookTest: string = 'webhook-test'; + webhookTest = 'webhook-test'; /** Path segment for waiting webhook endpoints. */ @Env('N8N_ENDPOINT_WEBHOOK_WAIT') - readonly webhookWaiting: string = 'webhook-waiting'; + webhookWaiting = 'webhook-waiting'; /** Whether to disable n8n's UI (frontend). */ @Env('N8N_DISABLE_UI') - readonly disableUi: boolean = false; + disableUi = false; /** Whether to disable production webhooks on the main process, when using webhook-specific processes. */ @Env('N8N_DISABLE_PRODUCTION_MAIN_PROCESS') - readonly disableProductionWebhooksOnMainProcess: boolean = false; + disableProductionWebhooksOnMainProcess = false; /** Colon-delimited list of additional endpoints to not open the UI on. */ @Env('N8N_ADDITIONAL_NON_UI_ROUTES') - readonly additionalNonUIRoutes: string = ''; + additionalNonUIRoutes = ''; } diff --git a/packages/@n8n/config/src/configs/event-bus.ts b/packages/@n8n/config/src/configs/event-bus.ts index ed1226fa92..87db613e63 100644 --- a/packages/@n8n/config/src/configs/event-bus.ts +++ b/packages/@n8n/config/src/configs/event-bus.ts @@ -2,30 +2,30 @@ import { Config, Env, Nested } from '../decorators'; @Config class LogWriterConfig { - /** Number of event log files to keep */ + /* of event log files to keep */ @Env('N8N_EVENTBUS_LOGWRITER_KEEPLOGCOUNT') - readonly keepLogCount: number = 3; + keepLogCount = 3; /** Max size (in KB) of an event log file before a new one is started */ @Env('N8N_EVENTBUS_LOGWRITER_MAXFILESIZEINKB') - readonly maxFileSizeInKB: number = 10240; // 10 MB + maxFileSizeInKB = 10240; // 10 MB /** Basename of event log file */ @Env('N8N_EVENTBUS_LOGWRITER_LOGBASENAME') - readonly logBaseName: string = 'n8nEventLog'; + logBaseName = 'n8nEventLog'; } @Config export class EventBusConfig { /** How often (in ms) to check for unsent event messages. Can in rare cases cause a message to be sent twice. `0` to disable */ @Env('N8N_EVENTBUS_CHECKUNSENTINTERVAL') - readonly checkUnsentInterval: number = 0; + checkUnsentInterval = 0; /** Endpoint to retrieve n8n version information from */ @Nested - readonly logWriter: LogWriterConfig; + logWriter: LogWriterConfig; /** Whether to recover execution details after a crash or only mark status executions as crashed. */ @Env('N8N_EVENTBUS_RECOVERY_MODE') - readonly crashRecoveryMode: 'simple' | 'extensive' = 'extensive'; + crashRecoveryMode: 'simple' | 'extensive' = 'extensive'; } diff --git a/packages/@n8n/config/src/configs/external-secrets.ts b/packages/@n8n/config/src/configs/external-secrets.ts index a5310d675e..2e51be87bc 100644 --- a/packages/@n8n/config/src/configs/external-secrets.ts +++ b/packages/@n8n/config/src/configs/external-secrets.ts @@ -4,9 +4,9 @@ import { Config, Env } from '../decorators'; export class ExternalSecretsConfig { /** How often (in seconds) to check for secret updates */ @Env('N8N_EXTERNAL_SECRETS_UPDATE_INTERVAL') - readonly updateInterval: number = 300; + updateInterval = 300; /** Whether to prefer GET over LIST when fetching secrets from Hashicorp Vault */ @Env('N8N_EXTERNAL_SECRETS_PREFER_GET') - readonly preferGet: boolean = false; + preferGet = false; } diff --git a/packages/@n8n/config/src/configs/external-storage.ts b/packages/@n8n/config/src/configs/external-storage.ts index c876e0ee34..3dd1448b44 100644 --- a/packages/@n8n/config/src/configs/external-storage.ts +++ b/packages/@n8n/config/src/configs/external-storage.ts @@ -4,39 +4,39 @@ import { Config, Env, Nested } from '../decorators'; class S3BucketConfig { /** Name of the n8n bucket in S3-compatible external storage */ @Env('N8N_EXTERNAL_STORAGE_S3_BUCKET_NAME') - readonly name: string = ''; + name = ''; /** Region of the n8n bucket in S3-compatible external storage @example "us-east-1" */ @Env('N8N_EXTERNAL_STORAGE_S3_BUCKET_REGION') - readonly region: string = ''; + region = ''; } @Config class S3CredentialsConfig { /** Access key in S3-compatible external storage */ @Env('N8N_EXTERNAL_STORAGE_S3_ACCESS_KEY') - readonly accessKey: string = ''; + accessKey = ''; /** Access secret in S3-compatible external storage */ @Env('N8N_EXTERNAL_STORAGE_S3_ACCESS_SECRET') - readonly accessSecret: string = ''; + accessSecret = ''; } @Config class S3Config { /** Host of the n8n bucket in S3-compatible external storage @example "s3.us-east-1.amazonaws.com" */ @Env('N8N_EXTERNAL_STORAGE_S3_HOST') - readonly host: string = ''; + host = ''; @Nested - readonly bucket: S3BucketConfig; + bucket: S3BucketConfig; @Nested - readonly credentials: S3CredentialsConfig; + credentials: S3CredentialsConfig; } @Config export class ExternalStorageConfig { @Nested - readonly s3: S3Config; + s3: S3Config; } diff --git a/packages/@n8n/config/src/configs/nodes.ts b/packages/@n8n/config/src/configs/nodes.ts index f845607a8c..dbe3705c6f 100644 --- a/packages/@n8n/config/src/configs/nodes.ts +++ b/packages/@n8n/config/src/configs/nodes.ts @@ -31,16 +31,16 @@ class CommunityPackagesConfig { export class NodesConfig { /** Node types to load. Includes all if unspecified. @example '["n8n-nodes-base.hackerNews"]' */ @Env('NODES_INCLUDE') - readonly include: JsonStringArray = []; + include: JsonStringArray = []; /** Node types not to load. Excludes none if unspecified. @example '["n8n-nodes-base.hackerNews"]' */ @Env('NODES_EXCLUDE') - readonly exclude: JsonStringArray = []; + exclude: JsonStringArray = []; /** Node type to use as error trigger */ @Env('NODES_ERROR_TRIGGER_TYPE') - readonly errorTriggerType: string = 'n8n-nodes-base.errorTrigger'; + errorTriggerType = 'n8n-nodes-base.errorTrigger'; @Nested - readonly communityPackages: CommunityPackagesConfig; + communityPackages: CommunityPackagesConfig; } diff --git a/packages/@n8n/config/src/configs/public-api.ts b/packages/@n8n/config/src/configs/public-api.ts index 33e3bf3fc3..b62cac68c7 100644 --- a/packages/@n8n/config/src/configs/public-api.ts +++ b/packages/@n8n/config/src/configs/public-api.ts @@ -4,13 +4,13 @@ import { Config, Env } from '../decorators'; export class PublicApiConfig { /** Whether to disable the Public API */ @Env('N8N_PUBLIC_API_DISABLED') - readonly disabled: boolean = false; + disabled = false; /** Path segment for the Public API */ @Env('N8N_PUBLIC_API_ENDPOINT') - readonly path: string = 'api'; + path = 'api'; /** Whether to disable the Swagger UI for the Public API */ @Env('N8N_PUBLIC_API_SWAGGERUI_DISABLED') - readonly swaggerUiDisabled: boolean = false; + swaggerUiDisabled = false; } diff --git a/packages/@n8n/config/src/configs/templates.ts b/packages/@n8n/config/src/configs/templates.ts index 3e10c892b3..3b05048b36 100644 --- a/packages/@n8n/config/src/configs/templates.ts +++ b/packages/@n8n/config/src/configs/templates.ts @@ -4,9 +4,9 @@ import { Config, Env } from '../decorators'; export class TemplatesConfig { /** Whether to load workflow templates. */ @Env('N8N_TEMPLATES_ENABLED') - readonly enabled: boolean = true; + enabled = true; /** Host to retrieve workflow templates from endpoints. */ @Env('N8N_TEMPLATES_HOST') - readonly host: string = 'https://api.n8n.io/api/'; + host = 'https://api.n8n.io/api/'; } diff --git a/packages/@n8n/config/src/configs/version-notifications.ts b/packages/@n8n/config/src/configs/version-notifications.ts index 1aa693228d..5fe495ed6c 100644 --- a/packages/@n8n/config/src/configs/version-notifications.ts +++ b/packages/@n8n/config/src/configs/version-notifications.ts @@ -4,13 +4,13 @@ import { Config, Env } from '../decorators'; export class VersionNotificationsConfig { /** Whether to request notifications about new n8n versions */ @Env('N8N_VERSION_NOTIFICATIONS_ENABLED') - readonly enabled: boolean = true; + enabled = true; /** Endpoint to retrieve n8n version information from */ @Env('N8N_VERSION_NOTIFICATIONS_ENDPOINT') - readonly endpoint: string = 'https://api.n8n.io/api/versions/'; + endpoint = 'https://api.n8n.io/api/versions/'; /** URL for versions panel to page instructing user on how to update n8n instance */ @Env('N8N_VERSION_NOTIFICATIONS_INFO_URL') - readonly infoUrl: string = 'https://docs.n8n.io/hosting/installation/updating/'; + infoUrl = 'https://docs.n8n.io/hosting/installation/updating/'; } diff --git a/packages/@n8n/config/src/configs/workflows.ts b/packages/@n8n/config/src/configs/workflows.ts index b19f4bc95d..9ca004c886 100644 --- a/packages/@n8n/config/src/configs/workflows.ts +++ b/packages/@n8n/config/src/configs/workflows.ts @@ -4,17 +4,14 @@ import { Config, Env } from '../decorators'; export class WorkflowsConfig { /** Default name for workflow */ @Env('WORKFLOWS_DEFAULT_NAME') - readonly defaultName: string = 'My workflow'; + defaultName = 'My workflow'; /** Show onboarding flow in new workflow */ @Env('N8N_ONBOARDING_FLOW_DISABLED') - readonly onboardingFlowDisabled: boolean = false; + onboardingFlowDisabled = false; /** Default option for which workflows may call the current workflow */ @Env('N8N_WORKFLOW_CALLER_POLICY_DEFAULT_OPTION') - readonly callerPolicyDefaultOption: - | 'any' - | 'none' - | 'workflowsFromAList' - | 'workflowsFromSameOwner' = 'workflowsFromSameOwner'; + callerPolicyDefaultOption: 'any' | 'none' | 'workflowsFromAList' | 'workflowsFromSameOwner' = + 'workflowsFromSameOwner'; } diff --git a/packages/@n8n/config/src/index.ts b/packages/@n8n/config/src/index.ts index d7bb09889d..33d3e67655 100644 --- a/packages/@n8n/config/src/index.ts +++ b/packages/@n8n/config/src/index.ts @@ -11,6 +11,7 @@ import { NodesConfig } from './configs/nodes'; import { ExternalStorageConfig } from './configs/external-storage'; import { WorkflowsConfig } from './configs/workflows'; import { EndpointsConfig } from './configs/endpoints'; +import { CacheConfig } from './configs/cache'; @Config class UserManagementConfig { @@ -75,4 +76,7 @@ export class GlobalConfig { @Nested readonly endpoints: EndpointsConfig; + + @Nested + readonly cache: CacheConfig; } diff --git a/packages/@n8n/config/test/config.test.ts b/packages/@n8n/config/test/config.test.ts index a36a74d1e2..db27b280ed 100644 --- a/packages/@n8n/config/test/config.test.ts +++ b/packages/@n8n/config/test/config.test.ts @@ -172,6 +172,17 @@ describe('GlobalConfig', () => { webhookTest: 'webhook-test', webhookWaiting: 'webhook-waiting', }, + cache: { + backend: 'auto', + memory: { + maxSize: 3145728, + ttl: 3600000, + }, + redis: { + prefix: 'redis', + ttl: 3600000, + }, + }, }; it('should use all default values when no env variables are defined', () => { diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts index f99c14885d..c424066c85 100644 --- a/packages/cli/src/config/schema.ts +++ b/packages/cli/src/config/schema.ts @@ -660,43 +660,6 @@ export const schema = { }, }, - cache: { - backend: { - doc: 'Backend to use for caching', - format: ['memory', 'redis', 'auto'] as const, - default: 'auto', - env: 'N8N_CACHE_BACKEND', - }, - memory: { - maxSize: { - doc: 'Maximum size of memory cache in bytes', - format: Number, - default: 3 * 1024 * 1024, // 3 MB - env: 'N8N_CACHE_MEMORY_MAX_SIZE', - }, - ttl: { - doc: 'Time to live for cached items in memory (in ms)', - format: Number, - default: 3600 * 1000, // 1 hour - env: 'N8N_CACHE_MEMORY_TTL', - }, - }, - redis: { - prefix: { - doc: 'Prefix for all cache keys', - format: String, - default: 'cache', - env: 'N8N_CACHE_REDIS_KEY_PREFIX', - }, - ttl: { - doc: 'Time to live for cached items in redis (in ms), 0 for no TTL', - format: Number, - default: 3600 * 1000, // 1 hour - env: 'N8N_CACHE_REDIS_TTL', - }, - }, - }, - /** * @important Do not remove until after cloud hooks are updated to stop using convict config. */ diff --git a/packages/cli/src/services/cache/cache.service.ts b/packages/cli/src/services/cache/cache.service.ts index 75dad03b49..daf51911ff 100644 --- a/packages/cli/src/services/cache/cache.service.ts +++ b/packages/cli/src/services/cache/cache.service.ts @@ -13,6 +13,7 @@ import type { } from '@/services/cache/cache.types'; import { TIME } from '@/constants'; import { TypedEmitter } from '@/TypedEmitter'; +import { GlobalConfig } from '@n8n/config'; type CacheEvents = { 'metrics.cache.hit': never; @@ -22,12 +23,15 @@ type CacheEvents = { @Service() export class CacheService extends TypedEmitter { + constructor(private readonly globalConfig: GlobalConfig) { + super(); + } + private cache: TaggedRedisCache | TaggedMemoryCache; async init() { - const backend = config.getEnv('cache.backend'); + const { backend } = this.globalConfig.cache; const mode = config.getEnv('executions.mode'); - const ttl = config.getEnv('cache.redis.ttl'); const useRedis = backend === 'redis' || (backend === 'auto' && mode === 'queue'); @@ -36,8 +40,9 @@ export class CacheService extends TypedEmitter { const redisClientService = Container.get(RedisClientService); const prefixBase = config.getEnv('redis.prefix'); - const cachePrefix = config.getEnv('cache.redis.prefix'); - const prefix = redisClientService.toValidPrefix(`${prefixBase}:${cachePrefix}:`); + const prefix = redisClientService.toValidPrefix( + `${prefixBase}:${this.globalConfig.cache.redis.prefix}:`, + ); const redisClient = redisClientService.createClient({ type: 'client(cache)', @@ -45,7 +50,9 @@ export class CacheService extends TypedEmitter { }); const { redisStoreUsingClient } = await import('@/services/cache/redis.cache-manager'); - const redisStore = redisStoreUsingClient(redisClient, { ttl }); + const redisStore = redisStoreUsingClient(redisClient, { + ttl: this.globalConfig.cache.redis.ttl, + }); const redisCache = await caching(redisStore); @@ -54,7 +61,7 @@ export class CacheService extends TypedEmitter { return; } - const maxSize = config.getEnv('cache.memory.maxSize'); + const { maxSize, ttl } = this.globalConfig.cache.memory; const sizeCalculation = (item: unknown) => { const str = jsonStringify(item, { replaceCircularRefs: true }); diff --git a/packages/cli/test/integration/activation-errors.service.test.ts b/packages/cli/test/integration/activation-errors.service.test.ts index 7635660db3..5d56f0dc90 100644 --- a/packages/cli/test/integration/activation-errors.service.test.ts +++ b/packages/cli/test/integration/activation-errors.service.test.ts @@ -1,8 +1,13 @@ import { ActivationErrorsService } from '@/ActivationErrors.service'; import { CacheService } from '@/services/cache/cache.service'; +import { GlobalConfig } from '@n8n/config'; +import { mockInstance } from '@test/mocking'; describe('ActivationErrorsService', () => { - const cacheService = new CacheService(); + const globalConfig = mockInstance(GlobalConfig, { + cache: { backend: 'memory', memory: { maxSize: 3 * 1024 * 1024, ttl: 3600 * 1000 } }, + }); + const cacheService = new CacheService(globalConfig); const activationErrorsService = new ActivationErrorsService(cacheService); const firstWorkflowId = 'GSG0etbfTA2CNPDX'; diff --git a/packages/cli/test/integration/prometheus-metrics.test.ts b/packages/cli/test/integration/prometheus-metrics.test.ts index 9f6a0fad6e..1eccb9b7d0 100644 --- a/packages/cli/test/integration/prometheus-metrics.test.ts +++ b/packages/cli/test/integration/prometheus-metrics.test.ts @@ -12,8 +12,8 @@ jest.unmock('@/eventbus/MessageEventBus/MessageEventBus'); const toLines = (response: Response) => response.text.trim().split('\n'); const globalConfig = Container.get(GlobalConfig); -// @ts-expect-error `metrics` is a readonly property globalConfig.endpoints.metrics = { + enable: true, prefix: 'n8n_test_', includeDefaultMetrics: true, includeApiEndpoints: true, diff --git a/packages/cli/test/unit/services/cache.service.test.ts b/packages/cli/test/unit/services/cache.service.test.ts index a742b20698..5c024b2903 100644 --- a/packages/cli/test/unit/services/cache.service.test.ts +++ b/packages/cli/test/unit/services/cache.service.test.ts @@ -1,6 +1,8 @@ import { CacheService } from '@/services/cache/cache.service'; import config from '@/config'; import { sleep } from 'n8n-workflow'; +import { GlobalConfig } from '@n8n/config'; +import Container from 'typedi'; jest.mock('ioredis', () => { const Redis = require('ioredis-mock'); @@ -13,10 +15,12 @@ jest.mock('ioredis', () => { for (const backend of ['memory', 'redis'] as const) { describe(backend, () => { let cacheService: CacheService; + let globalConfig: GlobalConfig; beforeAll(async () => { - config.set('cache.backend', backend); - cacheService = new CacheService(); + globalConfig = Container.get(GlobalConfig); + globalConfig.cache.backend = backend; + cacheService = new CacheService(globalConfig); await cacheService.init(); }); @@ -43,7 +47,7 @@ for (const backend of ['memory', 'redis'] as const) { if (backend === 'memory') { test('should honor max size when enough', async () => { - config.set('cache.memory.maxSize', 16); // enough bytes for "withoutUnicode" + globalConfig.cache.memory.maxSize = 16; // enough bytes for "withoutUnicode" await cacheService.init(); await cacheService.set('key', 'withoutUnicode'); @@ -51,12 +55,12 @@ for (const backend of ['memory', 'redis'] as const) { await expect(cacheService.get('key')).resolves.toBe('withoutUnicode'); // restore - config.set('cache.memory.maxSize', 3 * 1024 * 1024); + globalConfig.cache.memory.maxSize = 3 * 1024 * 1024; await cacheService.init(); }); test('should honor max size when not enough', async () => { - config.set('cache.memory.maxSize', 16); // not enough bytes for "withUnicodeԱԲԳ" + globalConfig.cache.memory.maxSize = 16; // not enough bytes for "withUnicodeԱԲԳ" await cacheService.init(); await cacheService.set('key', 'withUnicodeԱԲԳ'); @@ -64,7 +68,8 @@ for (const backend of ['memory', 'redis'] as const) { await expect(cacheService.get('key')).resolves.toBeUndefined(); // restore - config.set('cache.memory.maxSize', 3 * 1024 * 1024); + globalConfig.cache.memory.maxSize = 3 * 1024 * 1024; + // restore await cacheService.init(); }); }