mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-10 14:44:05 -08:00
190 lines
5.6 KiB
TypeScript
190 lines
5.6 KiB
TypeScript
import Vorpal = require('vorpal');
|
|
import { Args } from 'vorpal';
|
|
import * as config from '../config';
|
|
|
|
const open = require('open');
|
|
|
|
import * as localtunnel from 'localtunnel';
|
|
import {
|
|
ActiveWorkflowRunner,
|
|
CredentialTypes,
|
|
Db,
|
|
GenericHelpers,
|
|
LoadNodesAndCredentials,
|
|
NodeTypes,
|
|
TestWebhooks,
|
|
Server,
|
|
} from "../src";
|
|
import {
|
|
UserSettings,
|
|
} from "n8n-core";
|
|
|
|
import { promisify } from "util";
|
|
const tunnel = promisify(localtunnel);
|
|
|
|
let activeWorkflowRunner: ActiveWorkflowRunner.ActiveWorkflowRunner | undefined;
|
|
let processExistCode = 0;
|
|
|
|
/**
|
|
* Opens the UI in browser
|
|
*
|
|
*/
|
|
function openBrowser() {
|
|
const editorUrl = GenericHelpers.getBaseUrl();
|
|
|
|
open(editorUrl, { wait: true })
|
|
.catch((error: Error) => {
|
|
console.log(`\nWas not able to open URL in browser. Please open manually by visiting:\n${editorUrl}\n`);
|
|
});
|
|
}
|
|
|
|
|
|
module.exports = (vorpal: Vorpal) => {
|
|
return vorpal
|
|
.command('start')
|
|
// @ts-ignore
|
|
.description('Starts n8n. Makes Web-UI available and starts active workflows')
|
|
.option('-o --open',
|
|
'Opens the UI automatically in browser')
|
|
.option('--tunnel',
|
|
'Runs the webhooks via a hooks.n8n.cloud tunnel server')
|
|
.option('\n')
|
|
// TODO: Add validation
|
|
// .validate((args: Args) => {
|
|
// })
|
|
.action((args: Args) => {
|
|
|
|
if (process.pid === 1) {
|
|
console.error(`The n8n node process should not run as process with ID 1 because that will cause
|
|
problems with shutting everything down correctly. If started with docker use the
|
|
flag "--init" to fix this problem!`);
|
|
return;
|
|
}
|
|
|
|
// TODO: Start here the the script in a subprocess which can get restarted when new nodes get added and so new packages have to get installed
|
|
|
|
// npm install / rm (in other process)
|
|
// restart process depending on exit code (lets say 50 means restart)
|
|
|
|
// Wrap that the process does not close but we can still use async
|
|
(async () => {
|
|
try {
|
|
// Start directly with the init of the database to improve startup time
|
|
const startDbInitPromise = Db.init();
|
|
|
|
// Make sure the settings exist
|
|
const userSettings = await UserSettings.prepareUserSettings();
|
|
|
|
// Load all node and credential types
|
|
const loadNodesAndCredentials = LoadNodesAndCredentials();
|
|
await loadNodesAndCredentials.init();
|
|
|
|
// Add the found types to an instance other parts of the application can use
|
|
const nodeTypes = NodeTypes();
|
|
await nodeTypes.init(loadNodesAndCredentials.nodeTypes);
|
|
const credentialTypes = CredentialTypes();
|
|
await credentialTypes.init(loadNodesAndCredentials.credentialTypes);
|
|
|
|
// Wait till the database is ready
|
|
await startDbInitPromise;
|
|
|
|
if (args.options.tunnel !== undefined) {
|
|
console.log('\nWaiting for tunnel ...');
|
|
|
|
if (userSettings.tunnelSubdomain === undefined) {
|
|
// When no tunnel subdomain did exist yet create a new random one
|
|
const availableCharacters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
userSettings.tunnelSubdomain = Array.from({ length: 24 }).map(() => {
|
|
return availableCharacters.charAt(Math.floor(Math.random() * availableCharacters.length));
|
|
}).join('');
|
|
|
|
await UserSettings.writeUserSettings(userSettings);
|
|
}
|
|
|
|
const tunnelSettings: localtunnel.TunnelConfig = {
|
|
host: 'https://hooks.n8n.cloud',
|
|
subdomain: userSettings.tunnelSubdomain,
|
|
};
|
|
|
|
const port = config.get('port') as number;
|
|
|
|
// @ts-ignore
|
|
const webhookTunnel = await tunnel(port, tunnelSettings);
|
|
|
|
process.env.WEBHOOK_TUNNEL_URL = webhookTunnel.url + '/';
|
|
console.log(`Tunnel URL: ${process.env.WEBHOOK_TUNNEL_URL}\n`);
|
|
}
|
|
|
|
await Server.start();
|
|
|
|
// Start to get active workflows and run their triggers
|
|
activeWorkflowRunner = ActiveWorkflowRunner.getInstance();
|
|
await activeWorkflowRunner.init();
|
|
|
|
const editorUrl = GenericHelpers.getBaseUrl();
|
|
console.log(`\nEditor is now accessible via:\n${editorUrl}`);
|
|
|
|
// Allow to open n8n editor by pressing "o"
|
|
if (Boolean(process.stdout.isTTY) && process.stdin.setRawMode) {
|
|
process.stdin.setRawMode(true);
|
|
process.stdin.resume();
|
|
process.stdin.setEncoding('utf8');
|
|
let inputText = '';
|
|
|
|
if (args.options.browser !== undefined) {
|
|
openBrowser();
|
|
}
|
|
console.log(`\nPress "o" to open in Browser.`);
|
|
process.stdin.on("data", (key) => {
|
|
if (key === 'o') {
|
|
openBrowser();
|
|
inputText = '';
|
|
} else {
|
|
// When anything else got pressed, record it and send it on enter into the child process
|
|
if (key.charCodeAt(0) === 13) {
|
|
// send to child process and print in terminal
|
|
process.stdout.write('\n');
|
|
inputText = '';
|
|
} else {
|
|
// record it and write into terminal
|
|
inputText += key;
|
|
process.stdout.write(key);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error(`There was an error: ${error.message}`);
|
|
|
|
processExistCode = 1;
|
|
// @ts-ignore
|
|
process.emit('SIGINT');
|
|
}
|
|
})();
|
|
|
|
|
|
vorpal.sigint(async () => {
|
|
console.log(`\nStopping n8n...`);
|
|
|
|
setTimeout(() => {
|
|
// In case that something goes wrong with shutdown we
|
|
// kill after max. 30 seconds no matter what
|
|
process.exit(processExistCode);
|
|
}, 30000);
|
|
|
|
const removePromises = [];
|
|
if (activeWorkflowRunner !== undefined) {
|
|
removePromises.push(activeWorkflowRunner.removeAll());
|
|
}
|
|
|
|
// Remove all test webhooks
|
|
const testWebhooks = TestWebhooks.getInstance();
|
|
removePromises.push(testWebhooks.removeAll());
|
|
|
|
await Promise.all(removePromises);
|
|
|
|
process.exit(processExistCode);
|
|
});
|
|
});
|
|
};
|