mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 13:39:44 +02:00
94 lines
3.2 KiB
TypeScript
94 lines
3.2 KiB
TypeScript
import { Config, Env, ExecutionsConfig } from '@n8n/config';
|
|
import { createHash } from 'node:crypto';
|
|
import { z } from 'zod';
|
|
|
|
import { InstanceSettings, type DeploymentStateRepo } from '@/instance-settings';
|
|
import { StorageConfig } from '@/storage.config';
|
|
|
|
export const BINARY_DATA_MODES = ['default', 'filesystem', 's3', 'azure', 'database'] as const;
|
|
|
|
const binaryDataModesSchema = z.enum(BINARY_DATA_MODES);
|
|
|
|
const availableModesSchema = z
|
|
.string()
|
|
.transform((value) => value.split(','))
|
|
.pipe(binaryDataModesSchema.array());
|
|
|
|
const dbMaxFileSizeSchema = z.coerce
|
|
.number()
|
|
.max(1024, 'Binary data max file size in `database` mode cannot exceed 1024 MiB'); // because of Postgres BYTEA hard limit
|
|
|
|
@Config
|
|
export class BinaryDataConfig {
|
|
/** Available modes of binary data storage, as comma separated strings. */
|
|
availableModes: z.infer<typeof availableModesSchema> = ['filesystem', 's3', 'database'];
|
|
|
|
/** Storage mode for binary data. Defaults to 'filesystem' in regular mode, 'database' in scaling mode. */
|
|
@Env('N8N_DEFAULT_BINARY_DATA_MODE', binaryDataModesSchema)
|
|
mode!: z.infer<typeof binaryDataModesSchema>;
|
|
|
|
/** Path for binary data storage in "filesystem" mode. */
|
|
@Env('N8N_BINARY_DATA_STORAGE_PATH')
|
|
localStoragePath: string;
|
|
|
|
/**
|
|
* Secret for creating publicly-accesible signed URLs for binary data.
|
|
* When not passed in, this will be derived from the instances's encryption-key
|
|
**/
|
|
@Env('N8N_BINARY_DATA_SIGNING_SECRET')
|
|
signingSecret: string;
|
|
|
|
/** Maximum file size (in MiB) for binary data in `database` mode. **/
|
|
@Env('N8N_BINARY_DATA_DATABASE_MAX_FILE_SIZE', dbMaxFileSizeSchema)
|
|
dbMaxFileSize: number = 512;
|
|
|
|
constructor(
|
|
{ encryptionKey }: InstanceSettings,
|
|
executionsConfig: ExecutionsConfig,
|
|
storageConfig: StorageConfig,
|
|
) {
|
|
/**
|
|
* Set the binary data storage path:
|
|
*
|
|
* - N8N_BINARY_DATA_STORAGE_PATH, else
|
|
* - N8N_STORAGE_PATH, else
|
|
* - ~/.n8n/storage
|
|
*
|
|
* `~/.n8n/binaryData` is no longer the default if the env var is unset.
|
|
*/
|
|
this.localStoragePath ??= storageConfig.storagePath;
|
|
this.signingSecret = createHash('sha256')
|
|
.update(`url-signing:${encryptionKey}`)
|
|
.digest('base64');
|
|
|
|
this.mode ??= executionsConfig.mode === 'queue' ? 'database' : 'filesystem';
|
|
}
|
|
|
|
/**
|
|
* Two-phase init: reads or creates the signing.binary_data deployment-key row.
|
|
* Must be called after DB migrations complete, before any signed-URL generation.
|
|
* Precedence: N8N_BINARY_DATA_SIGNING_SECRET env → DB active row → derive-from-key (and persist)
|
|
*/
|
|
async initialize(
|
|
repo: Pick<DeploymentStateRepo, 'findActiveSigningSecret' | 'seedSigningSecret'>,
|
|
): Promise<void> {
|
|
if (process.env.N8N_BINARY_DATA_SIGNING_SECRET) {
|
|
return;
|
|
}
|
|
const existing = await repo.findActiveSigningSecret('signing.binary_data', {
|
|
rewrapLegacy: true,
|
|
});
|
|
if (existing !== null) {
|
|
this.signingSecret = existing;
|
|
return;
|
|
}
|
|
await repo.seedSigningSecret('signing.binary_data', this.signingSecret);
|
|
// The winner may be a pre-wrap row inserted concurrently by an older
|
|
// process — rewrap on this read too, so startup always leaves it wrapped.
|
|
const winner = await repo.findActiveSigningSecret('signing.binary_data', {
|
|
rewrapLegacy: true,
|
|
});
|
|
if (winner !== null) this.signingSecret = winner;
|
|
}
|
|
}
|