mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
✨ Use convict for configuration to make n8n easier to configure
This commit is contained in:
parent
fbaf445bf8
commit
d027545986
|
@ -17,6 +17,9 @@ services:
|
|||
n8n:
|
||||
image: n8n
|
||||
restart: always
|
||||
environment:
|
||||
- DB_TYPE=mongodb
|
||||
- DB_MONGODB_CONNECTION_URL=mongodb://n8nuser:${MONGO_NON_ROOT_PASSWORD}@mongo:27017/${MONGO_INITDB_DATABASE}
|
||||
ports:
|
||||
- 5678:5678
|
||||
links:
|
||||
|
@ -25,4 +28,4 @@ services:
|
|||
- ~/.n8n:/root/.n8n
|
||||
# Wait 5 seconds to start n8n to make sure that MongoDB is ready
|
||||
# when n8n tries to connect to it
|
||||
command: /bin/sh -c "sleep 5; n8n start --NODE_CONFIG='{\"database\":{\"type\":\"mongodb\", \"mongodbConfig\":{\"url\":\"mongodb://n8nuser:${MONGO_NON_ROOT_PASSWORD}@mongo:27017/${MONGO_INITDB_DATABASE}\"}}}'"
|
||||
command: /bin/sh -c "sleep 5; n8n start"
|
||||
|
|
|
@ -89,10 +89,11 @@ Replace the following placeholders with the actual data:
|
|||
docker run -it --rm \
|
||||
--name n8n \
|
||||
-p 5678:5678 \
|
||||
-e DB_TYPE=mongodb \
|
||||
-e DB_MONGODB_CONNECTION_URL="mongodb://MONGO_USER:MONGO_PASSWORD@MONGO_SERVER:MONGO_PORT/MONGO_DATABASE" \
|
||||
-v ~/.n8n:/root/.n8n \
|
||||
n8nio/n8n \
|
||||
n8n start \
|
||||
--NODE_CONFIG='{\"database\":{\"type\":\"mongodb\", \"mongodbConfig\":{\"url\":\"mongodb://MONGO_USER:MONGO_PASSWORD@MONGO_SERVER:MONGO_PORT/MONGO_DATABASE\"}}}'"
|
||||
n8n start
|
||||
```
|
||||
|
||||
A full working setup with docker-compose can be found [here](../../compose/withMongo/README.md)
|
||||
|
|
|
@ -71,30 +71,22 @@ n8n start --tunnel
|
|||
### Start with MongoDB as Database
|
||||
|
||||
By default n8n uses SQLite to save credentials, past executions and workflows.
|
||||
To use MongoDB instead you can either overwrite the default configuration on
|
||||
startup like this:
|
||||
To use MongoDB instead you can provide the environment varialbles `DB_TYPE` and
|
||||
`DB_MONGODB_CONNECTION_URL` like in the example bellow.
|
||||
|
||||
Replace the following placeholders with the actual data:
|
||||
- MONGO_DATABASE
|
||||
- MONGO_HOST
|
||||
- MONGO_PORT
|
||||
- MONGO_USER
|
||||
- MONGO_PASSWORD
|
||||
|
||||
```bash
|
||||
n8n start \
|
||||
--NODE_CONFIG='{\"database\":{\"type\":\"mongodb\", \"mongodbConfig\":{\"url\":\"mongodb://MONGO_USER:MONGO_PASSWORD@MONGO_SERVER:MONGO_PORT/MONGO_DATABASE\"}}}'"
|
||||
export DB_TYPE=mongodb
|
||||
export DB_MONGODB_CONNECTION_URL=mongodb://MONGO_USER:MONGO_PASSWORD@MONGO_HOST:MONGO_PORT/MONGO_DATABASE
|
||||
n8n start
|
||||
```
|
||||
|
||||
Or you can provide a custom configuration file by copying the default
|
||||
configuration file [(config/defaults.ts)](https://github.com/n8n-io/n8n/blob/master/packages/cli/config/default.ts).
|
||||
Make sure the file is also called `default.ts` and then set the path of the
|
||||
parent directory as environment variable `NODE_CONFIG_DIR`.
|
||||
|
||||
For example like this:
|
||||
```bash
|
||||
export NODE_CONFIG_DIR=/directory-containing-config-file
|
||||
```
|
||||
|
||||
Change in the config file the value under `database.type` from `sqlite`
|
||||
to `mongodb` and adjust the Mongo connection URL
|
||||
`database.mongodbConfig` accordingly.
|
||||
|
||||
n8n will then read your custom configuration and use MongoDB instead.
|
||||
|
||||
|
||||
## Execute Workflow from CLI
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Vorpal = require('vorpal');
|
||||
import { Args } from 'vorpal';
|
||||
import * as config from 'config';
|
||||
import * as config from '../config';
|
||||
|
||||
const open = require('open');
|
||||
|
||||
|
@ -105,7 +105,7 @@ flag "--init" to fix this problem!`);
|
|||
subdomain: userSettings.tunnelSubdomain,
|
||||
};
|
||||
|
||||
const port = config.get('urls.port') as number;
|
||||
const port = config.get('port') as number;
|
||||
|
||||
// @ts-ignore
|
||||
const webhookTunnel = await tunnel(port, tunnelSettings);
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
module.exports = {
|
||||
urls: {
|
||||
// Default path of the rest-endpoint
|
||||
endpointRest: 'rest',
|
||||
// Default path of the webhook-endpoint
|
||||
endpointWebhook: 'webhook',
|
||||
// Default path of the webhook-endpoint for testing
|
||||
endpointWebhookTest: 'webhook-test',
|
||||
|
||||
// How n8n can be reached (Editor & REST-API)
|
||||
host: 'localhost',
|
||||
port: 5678,
|
||||
protocol: 'http',
|
||||
},
|
||||
database: {
|
||||
type: 'sqlite', // Available types: sqlite, mongodb
|
||||
|
||||
// MongoDB specific settings
|
||||
mongodbConfig: {
|
||||
url: 'mongodb://user:password@localhost:27017/database',
|
||||
},
|
||||
},
|
||||
|
||||
executions: {
|
||||
// If a workflow executes all the data gets saved by default. This
|
||||
// could be a problem when a workflow gets executed a lot and processes
|
||||
// a lot of data. To not write the database full it is possible to
|
||||
// not save the execution at all.
|
||||
// Depending on if the execution did succeed or error a different
|
||||
// save behaviour can be set.
|
||||
saveDataErrorExecution: 'all', // Available options: all, none
|
||||
saveDataSuccessExecution: 'all', // Available options: all, none
|
||||
|
||||
// If the executions of workflows which got started via the editor
|
||||
// should be saved. By default they will not be saved as this runs
|
||||
// are normally only for testing and debugging. This setting can
|
||||
// also be overwritten on a per workflow basis in the workflow settings
|
||||
// in the editor.
|
||||
saveManualExecutions: false,
|
||||
},
|
||||
|
||||
nodes: {
|
||||
// Nodes not to load even if found
|
||||
// exclude: ['n8n-nodes-base.executeCommand'],
|
||||
errorTriggerType: 'n8n-nodes-base.errorTrigger',
|
||||
},
|
||||
|
||||
// The timezone to use. Is important for nodes like "Cron" which start the
|
||||
// workflow automatically at a specified time. This setting can also be
|
||||
// overwritten on a per worfklow basis in the workflow settings in the
|
||||
// editor.
|
||||
timezone: 'America/New_York',
|
||||
};
|
150
packages/cli/config/index.ts
Normal file
150
packages/cli/config/index.ts
Normal file
|
@ -0,0 +1,150 @@
|
|||
import * as convict from 'convict';
|
||||
import * as dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const config = convict({
|
||||
|
||||
database: {
|
||||
type: {
|
||||
doc: 'Type of database to use',
|
||||
format: ['sqlite', 'mongodb'],
|
||||
default: 'sqlite',
|
||||
env: 'DB_TYPE'
|
||||
},
|
||||
mongodb: {
|
||||
connectionUrl: {
|
||||
doc: 'MongoDB Connection URL',
|
||||
format: '*',
|
||||
default: 'mongodb://user:password@localhost:27017/database',
|
||||
env: 'DB_MONGODB_CONNECTION_URL'
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
executions: {
|
||||
// If a workflow executes all the data gets saved by default. This
|
||||
// could be a problem when a workflow gets executed a lot and processes
|
||||
// a lot of data. To not write the database full it is possible to
|
||||
// not save the execution at all.
|
||||
// Depending on if the execution did succeed or error a different
|
||||
// save behaviour can be set.
|
||||
saveDataOnError: {
|
||||
doc: 'What workflow execution data to save on error',
|
||||
format: ['all', 'none'],
|
||||
default: 'all',
|
||||
env: 'EXECUTIONS_DATA_SAVE_ON_ERROR'
|
||||
},
|
||||
saveDataOnSuccess: {
|
||||
doc: 'What workflow execution data to save on success',
|
||||
format: ['all', 'none'],
|
||||
default: 'all',
|
||||
env: 'EXECUTIONS_DATA_SAVE_ON_SUCCESS'
|
||||
},
|
||||
|
||||
// If the executions of workflows which got started via the editor
|
||||
// should be saved. By default they will not be saved as this runs
|
||||
// are normally only for testing and debugging. This setting can
|
||||
// also be overwritten on a per workflow basis in the workflow settings
|
||||
// in the editor.
|
||||
saveDataManualExecutions: {
|
||||
doc: 'Save data of executions when started manually via editor',
|
||||
default: false,
|
||||
env: 'EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS'
|
||||
},
|
||||
},
|
||||
|
||||
generic: {
|
||||
// The timezone to use. Is important for nodes like "Cron" which start the
|
||||
// workflow automatically at a specified time. This setting can also be
|
||||
// overwritten on a per worfklow basis in the workflow settings in the
|
||||
// editor.
|
||||
timezone: {
|
||||
doc: 'The timezone to use',
|
||||
format: '*',
|
||||
default: 'America/New_York',
|
||||
env: 'GENERIC_TIMEZONE'
|
||||
},
|
||||
},
|
||||
|
||||
// How n8n can be reached (Editor & REST-API)
|
||||
host: {
|
||||
format: String,
|
||||
default: 'localhost',
|
||||
arg: 'host',
|
||||
env: 'N8N_HOST',
|
||||
doc: 'Host name n8n can be reached'
|
||||
},
|
||||
port: {
|
||||
format: Number,
|
||||
default: 5678,
|
||||
arg: 'port',
|
||||
env: 'N8N_PORT',
|
||||
doc: 'HTTP port n8n can be reached'
|
||||
},
|
||||
protocol: {
|
||||
format: ['http', 'https'],
|
||||
default: 'http',
|
||||
env: 'N8N_PROTOCOL',
|
||||
doc: 'HTTP Protocol via which n8n can be reached'
|
||||
},
|
||||
|
||||
endpoints: {
|
||||
rest: {
|
||||
format: String,
|
||||
default: 'rest',
|
||||
env: 'N8N_ENDPOINT_REST',
|
||||
doc: 'Path for rest endpoint'
|
||||
},
|
||||
webhook: {
|
||||
format: String,
|
||||
default: 'webhook',
|
||||
env: 'N8N_ENDPOINT_WEBHOOK',
|
||||
doc: 'Path for webhook endpoint'
|
||||
},
|
||||
webhookTest: {
|
||||
format: String,
|
||||
default: 'webhook-test',
|
||||
env: 'N8N_ENDPOINT_WEBHOOK_TEST',
|
||||
doc: 'Path for test-webhook endpoint'
|
||||
},
|
||||
},
|
||||
|
||||
nodes: {
|
||||
exclude: {
|
||||
doc: 'Nodes not to load',
|
||||
format: function check(rawValue) {
|
||||
try {
|
||||
const values = JSON.parse(rawValue);
|
||||
if (!Array.isArray(values)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
for (const value of values) {
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
throw new TypeError(`The Nodes to exclude is not a valid Array of strings.`);
|
||||
}
|
||||
},
|
||||
default: '[]',
|
||||
env: 'NODES_EXCLUDE'
|
||||
},
|
||||
errorTriggerType: {
|
||||
doc: 'Node Type to use as Error Trigger',
|
||||
format: String,
|
||||
default: 'n8n-nodes-base.errorTrigger',
|
||||
env: 'NODES_ERROR_TRIGGER_TYPE'
|
||||
},
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
config.validate({
|
||||
allowed: 'strict',
|
||||
});
|
||||
|
||||
export = config;
|
|
@ -39,8 +39,9 @@
|
|||
"dist"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/config": "0.0.34",
|
||||
"@types/connect-history-api-fallback": "^1.3.1",
|
||||
"@types/convict": "^4.2.1",
|
||||
"@types/dotenv": "^6.1.1",
|
||||
"@types/express": "^4.16.1",
|
||||
"@types/jest": "^23.3.2",
|
||||
"@types/localtunnel": "^1.9.0",
|
||||
|
@ -58,8 +59,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"body-parser": "^1.18.3",
|
||||
"config": "^3.0.1",
|
||||
"connect-history-api-fallback": "^1.6.0",
|
||||
"convict": "^5.0.0",
|
||||
"dotenv": "^8.0.0",
|
||||
"express": "^4.16.4",
|
||||
"flatted": "^2.0.0",
|
||||
"glob-promise": "^3.4.0",
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
getRepository,
|
||||
} from "typeorm";
|
||||
|
||||
import * as config from 'config';
|
||||
import * as config from './../config';
|
||||
|
||||
|
||||
import {
|
||||
|
@ -40,7 +40,7 @@ export async function init(): Promise<IDatabaseCollections> {
|
|||
entities = MongoDb;
|
||||
connectionOptions = {
|
||||
type: 'mongodb',
|
||||
url: config.get('database.mongodbConfig.url') as string,
|
||||
url: config.get('database.mongodb.connectionUrl') as string,
|
||||
useNewUrlParser: true,
|
||||
};
|
||||
} else if (dbType === 'sqlite') {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as config from 'config';
|
||||
import * as config from '../config';
|
||||
import * as express from 'express';
|
||||
|
||||
|
||||
|
@ -25,9 +25,9 @@ export function logOutput(message: string, level = 'log'): void {
|
|||
* @returns {string}
|
||||
*/
|
||||
export function getBaseUrl(): string {
|
||||
const protocol = config.get('urls.protocol') as string;
|
||||
const host = config.get('urls.host') as string;
|
||||
const port = config.get('urls.port') as number;
|
||||
const protocol = config.get('protocol') as string;
|
||||
const host = config.get('host') as string;
|
||||
const port = config.get('port') as number;
|
||||
|
||||
if (protocol === 'http' && port === 80 || protocol === 'https' && port === 443) {
|
||||
return `${protocol}://${host}/`;
|
||||
|
|
|
@ -89,6 +89,7 @@ export interface ICredentialsDecryptedResponse extends ICredentialsDecryptedDb {
|
|||
}
|
||||
|
||||
export type DatabaseType = 'mongodb' | 'sqlite';
|
||||
export type SaveExecutionDataType = 'all' | 'none';
|
||||
|
||||
export interface IExecutionBase {
|
||||
id?: number | string | ObjectID;
|
||||
|
@ -171,18 +172,47 @@ export interface IExecutionDeleteFilter {
|
|||
|
||||
export interface IN8nConfig {
|
||||
database: IN8nConfigDatabase;
|
||||
nodes?: IN8nConfigNodes;
|
||||
endpoints: IN8nConfigEndpoints;
|
||||
executions: IN8nConfigExecutions;
|
||||
generic: IN8nConfigGeneric;
|
||||
host: string;
|
||||
nodes: IN8nConfigNodes;
|
||||
port: number;
|
||||
protocol: 'http' | 'https';
|
||||
}
|
||||
|
||||
export interface IN8nConfigDatabase {
|
||||
type: DatabaseType;
|
||||
mongodbConfig?: {
|
||||
url: string;
|
||||
mongodb: {
|
||||
connectionUrl: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IN8nConfigEndpoints {
|
||||
rest: string;
|
||||
webhook: string;
|
||||
webhookTest: string;
|
||||
}
|
||||
|
||||
export interface IN8nConfigExecutions {
|
||||
saveDataOnError: SaveExecutionDataType;
|
||||
saveDataOnSuccess: SaveExecutionDataType;
|
||||
saveDataManualExecutions: boolean;
|
||||
}
|
||||
|
||||
export interface IN8nConfigExecutions {
|
||||
saveDataOnError: SaveExecutionDataType;
|
||||
saveDataOnSuccess: SaveExecutionDataType;
|
||||
saveDataManualExecutions: boolean;
|
||||
}
|
||||
|
||||
export interface IN8nConfigGeneric {
|
||||
timezone: string;
|
||||
}
|
||||
|
||||
export interface IN8nConfigNodes {
|
||||
exclude?: string[];
|
||||
errorTriggerType: string;
|
||||
exclude: string[];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,11 +6,8 @@ import {
|
|||
ICredentialType,
|
||||
INodeType,
|
||||
} from 'n8n-workflow';
|
||||
import {
|
||||
IN8nConfigNodes,
|
||||
} from './';
|
||||
|
||||
import * as config from 'config';
|
||||
import * as config from '../config';
|
||||
import {
|
||||
access as fsAccess,
|
||||
readdir as fsReaddir,
|
||||
|
@ -66,10 +63,7 @@ class LoadNodesAndCredentialsClass {
|
|||
throw new Error('Could not find "node_modules" folder!');
|
||||
}
|
||||
|
||||
const nodeSettings = config.get('nodes') as IN8nConfigNodes | undefined;
|
||||
if (nodeSettings !== undefined && nodeSettings.exclude !== undefined) {
|
||||
this.excludeNodes = nodeSettings.exclude;
|
||||
}
|
||||
this.excludeNodes = config.get('nodes.exclude');
|
||||
|
||||
// Get all the installed packages which contain n8n nodes
|
||||
const packages = await this.getN8nNodePackages();
|
||||
|
|
|
@ -62,7 +62,7 @@ import {
|
|||
} from 'typeorm';
|
||||
|
||||
import * as parseUrl from 'parseurl';
|
||||
import * as config from 'config';
|
||||
import * as config from '../config';
|
||||
// @ts-ignore
|
||||
import * as timezones from 'google-timezones-json';
|
||||
|
||||
|
@ -83,12 +83,12 @@ class App {
|
|||
constructor() {
|
||||
this.app = express();
|
||||
|
||||
this.endpointWebhook = config.get('urls.endpointWebhook') as string;
|
||||
this.endpointWebhookTest = config.get('urls.endpointWebhookTest') as string;
|
||||
this.saveDataErrorExecution = config.get('executions.saveDataErrorExecution') as string;
|
||||
this.saveDataSuccessExecution = config.get('executions.saveDataSuccessExecution') as string;
|
||||
this.saveManualExecutions = config.get('executions.saveManualExecutions') as boolean;
|
||||
this.timezone = config.get('timezone') as string;
|
||||
this.endpointWebhook = config.get('endpoints.webhook') as string;
|
||||
this.endpointWebhookTest = config.get('endpoints.webhookTest') as string;
|
||||
this.saveDataErrorExecution = config.get('executions.saveDataOnError') as string;
|
||||
this.saveDataSuccessExecution = config.get('executions.saveDataOnSuccess') as string;
|
||||
this.saveManualExecutions = config.get('executions.saveDataManualExecutions') as boolean;
|
||||
this.timezone = config.get('generic.timezone') as string;
|
||||
|
||||
this.config();
|
||||
this.activeWorkflowRunner = ActiveWorkflowRunner.getInstance();
|
||||
|
@ -1030,7 +1030,7 @@ class App {
|
|||
}
|
||||
|
||||
export function start() {
|
||||
const PORT = config.get('urls.port');
|
||||
const PORT = config.get('port');
|
||||
|
||||
const app = new App().app;
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
Workflow,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import * as config from 'config';
|
||||
import * as config from '../config';
|
||||
|
||||
const pushInstance = Push.getInstance();
|
||||
|
||||
|
@ -120,7 +120,7 @@ const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, workflowI
|
|||
|
||||
const workflowSavePromise = WorkflowHelpers.saveStaticData(workflowInstance);
|
||||
|
||||
let saveManualExecutions = config.get('executions.saveManualExecutions') as boolean;
|
||||
let saveManualExecutions = config.get('executions.saveDataManualExecutions') as boolean;
|
||||
if (workflowInstance.settings !== undefined && workflowInstance.settings.saveManualExecutions !== undefined) {
|
||||
// Apply to workflow override
|
||||
saveManualExecutions = workflowInstance.settings.saveManualExecutions as boolean;
|
||||
|
@ -137,8 +137,8 @@ const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, workflowI
|
|||
}
|
||||
|
||||
// Check config to know if execution should be saved or not
|
||||
let saveDataErrorExecution = config.get('executions.saveDataErrorExecution') as string;
|
||||
let saveDataSuccessExecution = config.get('executions.saveDataSuccessExecution') as string;
|
||||
let saveDataErrorExecution = config.get('executions.saveDataOnError') as string;
|
||||
let saveDataSuccessExecution = config.get('executions.saveDataOnSuccess') as string;
|
||||
if (workflowInstance.settings !== undefined) {
|
||||
saveDataErrorExecution = (workflowInstance.settings.saveDataErrorExecution as string) || saveDataErrorExecution;
|
||||
saveDataSuccessExecution = (workflowInstance.settings.saveDataSuccessExecution as string) || saveDataSuccessExecution;
|
||||
|
@ -198,9 +198,9 @@ const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, workflowI
|
|||
export async function get(mode: WorkflowExecuteMode, workflowData: IWorkflowBase, workflowInstance: Workflow, sessionId?: string, retryOf?: string): Promise<IWorkflowExecuteAdditionalData> {
|
||||
const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl();
|
||||
|
||||
const timezone = config.get('timezone') as string;
|
||||
const webhookBaseUrl = urlBaseWebhook + config.get('urls.endpointWebhook') as string;
|
||||
const webhookTestBaseUrl = urlBaseWebhook + config.get('urls.endpointWebhookTest') as string;
|
||||
const timezone = config.get('generic.timezone') as string;
|
||||
const webhookBaseUrl = urlBaseWebhook + config.get('endpoints.webhook') as string;
|
||||
const webhookTestBaseUrl = urlBaseWebhook + config.get('endpoints.webhookTest') as string;
|
||||
|
||||
const encryptionKey = await UserSettings.getEncryptionKey();
|
||||
if (encryptionKey === undefined) {
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
Workflow,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import * as config from 'config';
|
||||
import * as config from '../config';
|
||||
|
||||
const ERROR_TRIGGER_TYPE = config.get('nodes.errorTriggerType') as string;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
// Have to deactivate for TypeORM
|
||||
// "strict": true,
|
||||
"preserveConstEnums": true,
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist/",
|
||||
"target": "es2017",
|
||||
|
|
Loading…
Reference in a new issue