mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-19 11:01:15 +00:00
refactor(core): Validate all string union config fields (#14527)
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Config, Env, Nested } from '../decorators';
|
||||
|
||||
const samesiteSchema = z.enum(['strict', 'lax', 'none']);
|
||||
|
||||
type Samesite = z.infer<typeof samesiteSchema>;
|
||||
|
||||
@Config
|
||||
class CookieConfig {
|
||||
/** This sets the `Secure` flag on n8n auth cookie */
|
||||
@@ -7,8 +13,8 @@ class CookieConfig {
|
||||
secure: boolean = true;
|
||||
|
||||
/** This sets the `Samesite` flag on n8n auth cookie */
|
||||
@Env('N8N_SAMESITE_COOKIE')
|
||||
samesite: 'strict' | 'lax' | 'none' = 'lax';
|
||||
@Env('N8N_SAMESITE_COOKIE', samesiteSchema)
|
||||
samesite: Samesite = 'lax';
|
||||
}
|
||||
|
||||
@Config
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Config, Env, Nested } from '../decorators';
|
||||
|
||||
const cacheBackendSchema = z.enum(['memory', 'redis', 'auto']);
|
||||
type CacheBackend = z.infer<typeof cacheBackendSchema>;
|
||||
|
||||
@Config
|
||||
class MemoryConfig {
|
||||
/** Max size of memory cache in bytes */
|
||||
@@ -25,8 +30,8 @@ class RedisConfig {
|
||||
@Config
|
||||
export class CacheConfig {
|
||||
/** Backend to use for caching. */
|
||||
@Env('N8N_CACHE_BACKEND')
|
||||
backend: 'memory' | 'redis' | 'auto' = 'auto';
|
||||
@Env('N8N_CACHE_BACKEND', cacheBackendSchema)
|
||||
backend: CacheBackend = 'auto';
|
||||
|
||||
@Nested
|
||||
memory: MemoryConfig;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Config, Env, Nested } from '../decorators';
|
||||
|
||||
const dbLoggingOptionsSchema = z.enum(['query', 'error', 'schema', 'warn', 'info', 'log', 'all']);
|
||||
type DbLoggingOptions = z.infer<typeof dbLoggingOptionsSchema>;
|
||||
|
||||
@Config
|
||||
class LoggingConfig {
|
||||
/** Whether database logging is enabled. */
|
||||
@@ -9,8 +14,8 @@ class LoggingConfig {
|
||||
/**
|
||||
* Database logging level. Requires `DB_LOGGING_MAX_EXECUTION_TIME` to be higher than `0`.
|
||||
*/
|
||||
@Env('DB_LOGGING_OPTIONS')
|
||||
options: 'query' | 'error' | 'schema' | 'warn' | 'info' | 'log' | 'all' = 'error';
|
||||
@Env('DB_LOGGING_OPTIONS', dbLoggingOptionsSchema)
|
||||
options: DbLoggingOptions = 'error';
|
||||
|
||||
/**
|
||||
* Only queries that exceed this time (ms) will be logged. Set `0` to disable.
|
||||
@@ -131,11 +136,14 @@ export class SqliteConfig {
|
||||
executeVacuumOnStartup: boolean = false;
|
||||
}
|
||||
|
||||
const dbTypeSchema = z.enum(['sqlite', 'mariadb', 'mysqldb', 'postgresdb']);
|
||||
type DbType = z.infer<typeof dbTypeSchema>;
|
||||
|
||||
@Config
|
||||
export class DatabaseConfig {
|
||||
/** Type of database to use */
|
||||
@Env('DB_TYPE')
|
||||
type: 'sqlite' | 'mariadb' | 'mysqldb' | 'postgresdb' = 'sqlite';
|
||||
@Env('DB_TYPE', dbTypeSchema)
|
||||
type: DbType = 'sqlite';
|
||||
|
||||
/** Prefix for table names */
|
||||
@Env('DB_TABLE_PREFIX')
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Config, Env, Nested } from '../decorators';
|
||||
|
||||
@Config
|
||||
@@ -15,6 +17,9 @@ class LogWriterConfig {
|
||||
logBaseName: string = 'n8nEventLog';
|
||||
}
|
||||
|
||||
const recoveryModeSchema = z.enum(['simple', 'extensive']);
|
||||
type RecoveryMode = z.infer<typeof recoveryModeSchema>;
|
||||
|
||||
@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 */
|
||||
@@ -26,6 +31,6 @@ export class EventBusConfig {
|
||||
logWriter: LogWriterConfig;
|
||||
|
||||
/** Whether to recover execution details after a crash or only mark status executions as crashed. */
|
||||
@Env('N8N_EVENTBUS_RECOVERY_MODE')
|
||||
crashRecoveryMode: 'simple' | 'extensive' = 'extensive';
|
||||
@Env('N8N_EVENTBUS_RECOVERY_MODE', recoveryModeSchema)
|
||||
crashRecoveryMode: RecoveryMode = 'extensive';
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Config, Env, Nested } from '../decorators';
|
||||
|
||||
const protocolSchema = z.enum(['http', 'https']);
|
||||
|
||||
export type Protocol = z.infer<typeof protocolSchema>;
|
||||
|
||||
@Config
|
||||
class S3BucketConfig {
|
||||
/** Name of the n8n bucket in S3-compatible external storage */
|
||||
@@ -28,8 +34,8 @@ export class S3Config {
|
||||
@Env('N8N_EXTERNAL_STORAGE_S3_HOST')
|
||||
host: string = '';
|
||||
|
||||
@Env('N8N_EXTERNAL_STORAGE_S3_PROTOCOL')
|
||||
protocol: 'http' | 'https' = 'https';
|
||||
@Env('N8N_EXTERNAL_STORAGE_S3_PROTOCOL', protocolSchema)
|
||||
protocol: Protocol = 'https';
|
||||
|
||||
@Nested
|
||||
bucket: S3BucketConfig;
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Config, Env } from '../decorators';
|
||||
|
||||
const releaseChannelSchema = z.enum(['stable', 'beta', 'nightly', 'dev']);
|
||||
type ReleaseChannel = z.infer<typeof releaseChannelSchema>;
|
||||
|
||||
@Config
|
||||
export class GenericConfig {
|
||||
/** Default timezone for the n8n instance. Can be overridden on a per-workflow basis. */
|
||||
@Env('GENERIC_TIMEZONE')
|
||||
timezone: string = 'America/New_York';
|
||||
|
||||
@Env('N8N_RELEASE_TYPE')
|
||||
releaseChannel: 'stable' | 'beta' | 'nightly' | 'dev' = 'dev';
|
||||
@Env('N8N_RELEASE_TYPE', releaseChannelSchema)
|
||||
releaseChannel: ReleaseChannel = 'dev';
|
||||
|
||||
/** Grace period (in seconds) to wait for components to shut down before process exit. */
|
||||
@Env('N8N_GRACEFUL_SHUTDOWN_TIMEOUT')
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { CommaSeperatedStringArray } from '../custom-types';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { CommaSeparatedStringArray } from '../custom-types';
|
||||
import { Config, Env, Nested } from '../decorators';
|
||||
|
||||
/** Scopes (areas of functionality) to filter logs by. */
|
||||
@@ -41,6 +43,9 @@ class FileLoggingConfig {
|
||||
location: string = 'logs/n8n.log';
|
||||
}
|
||||
|
||||
const logLevelSchema = z.enum(['error', 'warn', 'info', 'debug', 'silent']);
|
||||
type LogLevel = z.infer<typeof logLevelSchema>;
|
||||
|
||||
@Config
|
||||
export class LoggingConfig {
|
||||
/**
|
||||
@@ -49,8 +54,8 @@ export class LoggingConfig {
|
||||
*
|
||||
* @example `N8N_LOG_LEVEL=info` will output `error`, `warn` and `info` logs, but not `debug`.
|
||||
*/
|
||||
@Env('N8N_LOG_LEVEL')
|
||||
level: 'error' | 'warn' | 'info' | 'debug' | 'silent' = 'info';
|
||||
@Env('N8N_LOG_LEVEL', logLevelSchema)
|
||||
level: LogLevel = 'info';
|
||||
|
||||
/**
|
||||
* Where to output logs to. Options are: `console` or `file` or both in a comma separated list.
|
||||
@@ -58,7 +63,7 @@ export class LoggingConfig {
|
||||
* @example `N8N_LOG_OUTPUT=console,file` will output to both console and file.
|
||||
*/
|
||||
@Env('N8N_LOG_OUTPUT')
|
||||
outputs: CommaSeperatedStringArray<'console' | 'file'> = ['console'];
|
||||
outputs: CommaSeparatedStringArray<'console' | 'file'> = ['console'];
|
||||
|
||||
@Nested
|
||||
file: FileLoggingConfig;
|
||||
@@ -85,5 +90,5 @@ export class LoggingConfig {
|
||||
* `N8N_LOG_SCOPES=license,waiting-executions`
|
||||
*/
|
||||
@Env('N8N_LOG_SCOPES')
|
||||
scopes: CommaSeperatedStringArray<LogScope> = [];
|
||||
scopes: CommaSeparatedStringArray<LogScope> = [];
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Config, Env, Nested } from '../decorators';
|
||||
|
||||
@Config
|
||||
@@ -64,11 +66,14 @@ export class TemplateConfig {
|
||||
'credentials-shared': string = '';
|
||||
}
|
||||
|
||||
const emailModeSchema = z.enum(['', 'smtp']);
|
||||
type EmailMode = z.infer<typeof emailModeSchema>;
|
||||
|
||||
@Config
|
||||
class EmailConfig {
|
||||
/** How to send emails */
|
||||
@Env('N8N_EMAIL_MODE')
|
||||
mode: '' | 'smtp' = 'smtp';
|
||||
@Env('N8N_EMAIL_MODE', emailModeSchema)
|
||||
mode: EmailMode = 'smtp';
|
||||
|
||||
@Nested
|
||||
smtp: SmtpConfig;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Config, Env } from '../decorators';
|
||||
|
||||
const callerPolicySchema = z.enum(['any', 'none', 'workflowsFromAList', 'workflowsFromSameOwner']);
|
||||
type CallerPolicy = z.infer<typeof callerPolicySchema>;
|
||||
|
||||
@Config
|
||||
export class WorkflowsConfig {
|
||||
/** Default name for workflow */
|
||||
@@ -7,9 +12,8 @@ export class WorkflowsConfig {
|
||||
defaultName: string = 'My workflow';
|
||||
|
||||
/** Default option for which workflows may call the current workflow */
|
||||
@Env('N8N_WORKFLOW_CALLER_POLICY_DEFAULT_OPTION')
|
||||
callerPolicyDefaultOption: 'any' | 'none' | 'workflowsFromAList' | 'workflowsFromSameOwner' =
|
||||
'workflowsFromSameOwner';
|
||||
@Env('N8N_WORKFLOW_CALLER_POLICY_DEFAULT_OPTION', callerPolicySchema)
|
||||
callerPolicyDefaultOption: CallerPolicy = 'workflowsFromSameOwner';
|
||||
|
||||
/** How many workflows to activate simultaneously during startup. */
|
||||
@Env('N8N_WORKFLOW_ACTIVATION_BATCH_SIZE')
|
||||
|
||||
@@ -6,7 +6,7 @@ abstract class StringArray<T extends string> extends Array<T> {
|
||||
}
|
||||
}
|
||||
|
||||
export class CommaSeperatedStringArray<T extends string> extends StringArray<T> {
|
||||
export class CommaSeparatedStringArray<T extends string> extends StringArray<T> {
|
||||
constructor(str: string) {
|
||||
super(str, ',');
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { AiAssistantConfig } from './configs/aiAssistant.config';
|
||||
import { AuthConfig } from './configs/auth.config';
|
||||
import { CacheConfig } from './configs/cache.config';
|
||||
@@ -38,6 +40,10 @@ export type { LogScope } from './configs/logging.config';
|
||||
export { WorkflowsConfig } from './configs/workflows.config';
|
||||
export * from './custom-types';
|
||||
|
||||
const protocolSchema = z.enum(['http', 'https']);
|
||||
|
||||
export type Protocol = z.infer<typeof protocolSchema>;
|
||||
|
||||
@Config
|
||||
export class GlobalConfig {
|
||||
@Nested
|
||||
@@ -99,8 +105,8 @@ export class GlobalConfig {
|
||||
listen_address: string = '0.0.0.0';
|
||||
|
||||
/** HTTP Protocol via which n8n can be reached */
|
||||
@Env('N8N_PROTOCOL')
|
||||
protocol: 'http' | 'https' = 'http';
|
||||
@Env('N8N_PROTOCOL', protocolSchema)
|
||||
protocol: Protocol = 'http';
|
||||
|
||||
@Nested
|
||||
endpoints: EndpointsConfig;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { CommaSeperatedStringArray, ColonSeparatedStringArray } from '../src/custom-types';
|
||||
import { CommaSeparatedStringArray, ColonSeparatedStringArray } from '../src/custom-types';
|
||||
|
||||
describe('CommaSeperatedStringArray', () => {
|
||||
describe('CommaSeparatedStringArray', () => {
|
||||
it('should parse comma-separated string into array', () => {
|
||||
const result = new CommaSeperatedStringArray('a,b,c');
|
||||
const result = new CommaSeparatedStringArray('a,b,c');
|
||||
expect(result).toEqual(['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
it('should handle empty strings', () => {
|
||||
const result = new CommaSeperatedStringArray('a,b,,,');
|
||||
const result = new CommaSeparatedStringArray('a,b,,,');
|
||||
expect(result).toEqual(['a', 'b']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { CommaSeperatedStringArray, Config, Env } from '@n8n/config';
|
||||
import { CommaSeparatedStringArray, Config, Env } from '@n8n/config';
|
||||
import { UnexpectedError } from 'n8n-workflow';
|
||||
|
||||
const moduleNames = ['insights'] as const;
|
||||
type ModuleName = (typeof moduleNames)[number];
|
||||
|
||||
class Modules extends CommaSeperatedStringArray<ModuleName> {
|
||||
class Modules extends CommaSeparatedStringArray<ModuleName> {
|
||||
constructor(str: string) {
|
||||
super(str);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user