mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
Merge branch 'master' into save-changes-warning
This commit is contained in:
@@ -19,7 +19,7 @@ Condition notice.
|
|||||||
|
|
||||||
Software: n8n
|
Software: n8n
|
||||||
|
|
||||||
License: Apache 2.0
|
License: Apache 2.0 with Commons Clause
|
||||||
|
|
||||||
Licensor: n8n GmbH
|
Licensor: n8n GmbH
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
MONGO_INITDB_ROOT_USERNAME=changeUser
|
|
||||||
MONGO_INITDB_ROOT_PASSWORD=changePassword
|
|
||||||
MONGO_INITDB_DATABASE=n8n
|
|
||||||
|
|
||||||
MONGO_NON_ROOT_USERNAME=changeUser
|
|
||||||
MONGO_NON_ROOT_PASSWORD=changePassword
|
|
||||||
|
|
||||||
N8N_BASIC_AUTH_USER=changeUser
|
|
||||||
N8N_BASIC_AUTH_PASSWORD=changePassword
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# n8n with MongoDB
|
|
||||||
|
|
||||||
Starts n8n with MongoDB as database.
|
|
||||||
|
|
||||||
|
|
||||||
## Start
|
|
||||||
|
|
||||||
To start n8n with MongoDB simply start docker-compose by executing the following
|
|
||||||
command in the current folder.
|
|
||||||
|
|
||||||
|
|
||||||
**IMPORTANT:** But before you do that change the default users and passwords in the `.env` file!
|
|
||||||
|
|
||||||
```
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
To stop it execute:
|
|
||||||
|
|
||||||
```
|
|
||||||
docker-compose stop
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
The default name of the database, user and password for MongoDB can be changed in the `.env` file in the current directory.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
version: '3.1'
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
mongo:
|
|
||||||
image: mongo:4.0
|
|
||||||
restart: always
|
|
||||||
environment:
|
|
||||||
- MONGO_INITDB_ROOT_USERNAME
|
|
||||||
- MONGO_INITDB_ROOT_PASSWORD
|
|
||||||
- MONGO_INITDB_DATABASE
|
|
||||||
- MONGO_NON_ROOT_USERNAME
|
|
||||||
- MONGO_NON_ROOT_PASSWORD
|
|
||||||
volumes:
|
|
||||||
- ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh
|
|
||||||
|
|
||||||
n8n:
|
|
||||||
image: n8nio/n8n
|
|
||||||
restart: always
|
|
||||||
environment:
|
|
||||||
- DB_TYPE=mongodb
|
|
||||||
- DB_MONGODB_CONNECTION_URL=mongodb://${MONGO_NON_ROOT_USERNAME}:${MONGO_NON_ROOT_PASSWORD}@mongo:27017/${MONGO_INITDB_DATABASE}
|
|
||||||
- N8N_BASIC_AUTH_ACTIVE=true
|
|
||||||
- N8N_BASIC_AUTH_USER
|
|
||||||
- N8N_BASIC_AUTH_PASSWORD
|
|
||||||
ports:
|
|
||||||
- 5678:5678
|
|
||||||
links:
|
|
||||||
- mongo
|
|
||||||
volumes:
|
|
||||||
- ~/.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"
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e;
|
|
||||||
|
|
||||||
# Create a default non-root role
|
|
||||||
MONGO_NON_ROOT_ROLE="${MONGO_NON_ROOT_ROLE:-readWrite}"
|
|
||||||
|
|
||||||
if [ -n "${MONGO_NON_ROOT_USERNAME:-}" ] && [ -n "${MONGO_NON_ROOT_PASSWORD:-}" ]; then
|
|
||||||
"${mongo[@]}" "$MONGO_INITDB_DATABASE" <<-EOJS
|
|
||||||
db.createUser({
|
|
||||||
user: $(_js_escape "$MONGO_NON_ROOT_USERNAME"),
|
|
||||||
pwd: $(_js_escape "$MONGO_NON_ROOT_PASSWORD"),
|
|
||||||
roles: [ { role: $(_js_escape "$MONGO_NON_ROOT_ROLE"), db: $(_js_escape "$MONGO_INITDB_DATABASE") } ]
|
|
||||||
})
|
|
||||||
EOJS
|
|
||||||
else
|
|
||||||
echo "SETUP INFO: No Environment variables given!"
|
|
||||||
fi
|
|
||||||
@@ -2,6 +2,43 @@
|
|||||||
|
|
||||||
This list shows all the versions which include breaking changes and how to upgrade.
|
This list shows all the versions which include breaking changes and how to upgrade.
|
||||||
|
|
||||||
|
## 0.87.0
|
||||||
|
|
||||||
|
### What changed?
|
||||||
|
|
||||||
|
The link.fish node got removed because the service is shutting down.
|
||||||
|
|
||||||
|
### When is action necessary?
|
||||||
|
|
||||||
|
If you are are actively using the link.fish node.
|
||||||
|
|
||||||
|
### How to upgrade:
|
||||||
|
|
||||||
|
Unfortunately, that's not possible. We'd recommend you to look for an alternative service.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.83.0
|
||||||
|
|
||||||
|
### What changed?
|
||||||
|
|
||||||
|
In the Active Campaign Node, we have changed how the `getAll` operation works with various resources for the sake of consistency. To achieve this, a new parameter called 'Simple' has been added.
|
||||||
|
|
||||||
|
### When is action necessary?
|
||||||
|
|
||||||
|
When one of the following resources/operations is used:
|
||||||
|
|
||||||
|
| Resource | Operation |
|
||||||
|
|--|--|
|
||||||
|
| Deal | Get All |
|
||||||
|
| Connector | Get All |
|
||||||
|
| E-commerce Order | Get All |
|
||||||
|
| E-commerce Customer | Get All |
|
||||||
|
| E-commerce Order Products | Get All |
|
||||||
|
|
||||||
|
### How to upgrade:
|
||||||
|
|
||||||
|
Open the affected resource/operation and set the parameter `Simple` to false.
|
||||||
|
|
||||||
## 0.79.0
|
## 0.79.0
|
||||||
|
|
||||||
### What changed?
|
### What changed?
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Condition notice.
|
|||||||
|
|
||||||
Software: n8n
|
Software: n8n
|
||||||
|
|
||||||
License: Apache 2.0
|
License: Apache 2.0 with Commons Clause
|
||||||
|
|
||||||
Licensor: n8n GmbH
|
Licensor: n8n GmbH
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ process.env.NODE_CONFIG_DIR = process.env.NODE_CONFIG_DIR || path.join(__dirname
|
|||||||
var versionFlags = [ // tslint:disable-line:no-var-keyword
|
var versionFlags = [ // tslint:disable-line:no-var-keyword
|
||||||
'-v',
|
'-v',
|
||||||
'-V',
|
'-V',
|
||||||
'--version'
|
'--version',
|
||||||
];
|
];
|
||||||
if (versionFlags.includes(process.argv.slice(-1)[0])) {
|
if (versionFlags.includes(process.argv.slice(-1)[0])) {
|
||||||
console.log(require('../package').version);
|
console.log(require('../package').version);
|
||||||
@@ -22,23 +22,10 @@ if (process.argv.length === 2) {
|
|||||||
process.argv.push('start');
|
process.argv.push('start');
|
||||||
}
|
}
|
||||||
|
|
||||||
var command = process.argv[2]; // tslint:disable-line:no-var-keyword
|
var nodeVersion = process.versions.node.split('.');
|
||||||
|
|
||||||
// Check if the command the user did enter is supported else stop
|
if (parseInt(nodeVersion[0], 10) < 12 || parseInt(nodeVersion[0], 10) === 12 && parseInt(nodeVersion[1], 10) < 9) {
|
||||||
var supportedCommands = [ // tslint:disable-line:no-var-keyword
|
console.log(`\nYour Node.js version (${process.versions.node}) is too old to run n8n.\nPlease update to version 12.9 or later!\n`);
|
||||||
'execute',
|
|
||||||
'help',
|
|
||||||
'start',
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!supportedCommands.includes(command)) {
|
|
||||||
console.log('\nThe command "' + command + '" is not known!\n');
|
|
||||||
process.argv.pop();
|
|
||||||
process.argv.push('--help');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parseInt(process.versions.node.split('.')[0], 10) < 10) {
|
|
||||||
console.log('\nThe Node.js version is too old to run n8n. Please use version 10 or later!\n');
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
70
packages/cli/commands/config/workflow/deactivate.ts
Normal file
70
packages/cli/commands/config/workflow/deactivate.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import {
|
||||||
|
Command, flags,
|
||||||
|
} from '@oclif/command';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Db,
|
||||||
|
GenericHelpers,
|
||||||
|
} from "../../../src";
|
||||||
|
|
||||||
|
|
||||||
|
export class DeactivateCommand extends Command {
|
||||||
|
static description = '\nDeactivates workflows';
|
||||||
|
|
||||||
|
static examples = [
|
||||||
|
`$ n8n config:workflow:deactivate --all`,
|
||||||
|
`$ n8n config:workflow:deactivate --id=5`,
|
||||||
|
];
|
||||||
|
|
||||||
|
static flags = {
|
||||||
|
help: flags.help({ char: 'h' }),
|
||||||
|
all: flags.boolean({
|
||||||
|
description: 'Deactivates all workflows',
|
||||||
|
}),
|
||||||
|
id: flags.string({
|
||||||
|
description: 'Deactivats the workflow with the given ID',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
const { flags } = this.parse(DeactivateCommand);
|
||||||
|
|
||||||
|
if (!flags.all && !flags.id) {
|
||||||
|
GenericHelpers.logOutput(`Either option "--all" or "--id" have to be set!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags.all && flags.id) {
|
||||||
|
GenericHelpers.logOutput(`Either "--all" or "--id" can be set never both!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Db.init();
|
||||||
|
|
||||||
|
const findQuery: IDataObject = {};
|
||||||
|
if (flags.id) {
|
||||||
|
console.log(`Deactivating workflow with ID: ${flags.id}`);
|
||||||
|
findQuery.id = flags.id;
|
||||||
|
} else {
|
||||||
|
console.log('Deactivating all workflows');
|
||||||
|
findQuery.active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Db.collections.Workflow!.update(findQuery, { active: false });
|
||||||
|
console.log('Done');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('\nGOT ERROR');
|
||||||
|
console.log('====================================');
|
||||||
|
console.error(e.message);
|
||||||
|
console.error(e.stack);
|
||||||
|
this.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ActiveExecutions,
|
ActiveExecutions,
|
||||||
CredentialsOverwrites,
|
CredentialsOverwrites,
|
||||||
|
CredentialTypes,
|
||||||
Db,
|
Db,
|
||||||
ExternalHooks,
|
ExternalHooks,
|
||||||
GenericHelpers,
|
GenericHelpers,
|
||||||
@@ -116,6 +117,8 @@ export class Execute extends Command {
|
|||||||
// Add the found types to an instance other parts of the application can use
|
// Add the found types to an instance other parts of the application can use
|
||||||
const nodeTypes = NodeTypes();
|
const nodeTypes = NodeTypes();
|
||||||
await nodeTypes.init(loadNodesAndCredentials.nodeTypes);
|
await nodeTypes.init(loadNodesAndCredentials.nodeTypes);
|
||||||
|
const credentialTypes = CredentialTypes();
|
||||||
|
await credentialTypes.init(loadNodesAndCredentials.credentialTypes);
|
||||||
|
|
||||||
if (!WorkflowHelpers.isWorkflowIdValid(workflowId)) {
|
if (!WorkflowHelpers.isWorkflowIdValid(workflowId)) {
|
||||||
workflowId = undefined;
|
workflowId = undefined;
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ const open = require('open');
|
|||||||
|
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
import {
|
import {
|
||||||
|
ActiveExecutions,
|
||||||
ActiveWorkflowRunner,
|
ActiveWorkflowRunner,
|
||||||
CredentialTypes,
|
|
||||||
CredentialsOverwrites,
|
CredentialsOverwrites,
|
||||||
|
CredentialTypes,
|
||||||
Db,
|
Db,
|
||||||
ExternalHooks,
|
ExternalHooks,
|
||||||
GenericHelpers,
|
GenericHelpers,
|
||||||
|
IExecutionsCurrentSummary,
|
||||||
LoadNodesAndCredentials,
|
LoadNodesAndCredentials,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
Server,
|
Server,
|
||||||
@@ -68,23 +70,46 @@ export class Start extends Command {
|
|||||||
static async stopProcess() {
|
static async stopProcess() {
|
||||||
console.log(`\nStopping n8n...`);
|
console.log(`\nStopping n8n...`);
|
||||||
|
|
||||||
setTimeout(() => {
|
try {
|
||||||
// In case that something goes wrong with shutdown we
|
const externalHooks = ExternalHooks();
|
||||||
// kill after max. 30 seconds no matter what
|
await externalHooks.run('n8n.stop', []);
|
||||||
process.exit(processExistCode);
|
|
||||||
}, 30000);
|
|
||||||
|
|
||||||
const removePromises = [];
|
setTimeout(() => {
|
||||||
if (activeWorkflowRunner !== undefined) {
|
// In case that something goes wrong with shutdown we
|
||||||
removePromises.push(activeWorkflowRunner.removeAll());
|
// 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);
|
||||||
|
|
||||||
|
// Wait for active workflow executions to finish
|
||||||
|
const activeExecutionsInstance = ActiveExecutions.getInstance();
|
||||||
|
let executingWorkflows = activeExecutionsInstance.getActiveExecutions();
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
while (executingWorkflows.length !== 0) {
|
||||||
|
if (count++ % 4 === 0) {
|
||||||
|
console.log(`Waiting for ${executingWorkflows.length} active executions to finish...`);
|
||||||
|
}
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 500);
|
||||||
|
});
|
||||||
|
executingWorkflows = activeExecutionsInstance.getActiveExecutions();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('There was an error shutting down n8n.', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all test webhooks
|
|
||||||
const testWebhooks = TestWebhooks.getInstance();
|
|
||||||
removePromises.push(testWebhooks.removeAll());
|
|
||||||
|
|
||||||
await Promise.all(removePromises);
|
|
||||||
|
|
||||||
process.exit(processExistCode);
|
process.exit(processExistCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,58 +10,58 @@ const config = convict({
|
|||||||
doc: 'Type of database to use',
|
doc: 'Type of database to use',
|
||||||
format: ['sqlite', 'mariadb', 'mongodb', 'mysqldb', 'postgresdb'],
|
format: ['sqlite', 'mariadb', 'mongodb', 'mysqldb', 'postgresdb'],
|
||||||
default: 'sqlite',
|
default: 'sqlite',
|
||||||
env: 'DB_TYPE'
|
env: 'DB_TYPE',
|
||||||
},
|
},
|
||||||
mongodb: {
|
mongodb: {
|
||||||
connectionUrl: {
|
connectionUrl: {
|
||||||
doc: 'MongoDB Connection URL',
|
doc: 'MongoDB Connection URL',
|
||||||
format: '*',
|
format: '*',
|
||||||
default: 'mongodb://user:password@localhost:27017/database',
|
default: 'mongodb://user:password@localhost:27017/database',
|
||||||
env: 'DB_MONGODB_CONNECTION_URL'
|
env: 'DB_MONGODB_CONNECTION_URL',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
tablePrefix: {
|
tablePrefix: {
|
||||||
doc: 'Prefix for table names',
|
doc: 'Prefix for table names',
|
||||||
format: '*',
|
format: '*',
|
||||||
default: '',
|
default: '',
|
||||||
env: 'DB_TABLE_PREFIX'
|
env: 'DB_TABLE_PREFIX',
|
||||||
},
|
},
|
||||||
postgresdb: {
|
postgresdb: {
|
||||||
database: {
|
database: {
|
||||||
doc: 'PostgresDB Database',
|
doc: 'PostgresDB Database',
|
||||||
format: String,
|
format: String,
|
||||||
default: 'n8n',
|
default: 'n8n',
|
||||||
env: 'DB_POSTGRESDB_DATABASE'
|
env: 'DB_POSTGRESDB_DATABASE',
|
||||||
},
|
},
|
||||||
host: {
|
host: {
|
||||||
doc: 'PostgresDB Host',
|
doc: 'PostgresDB Host',
|
||||||
format: String,
|
format: String,
|
||||||
default: 'localhost',
|
default: 'localhost',
|
||||||
env: 'DB_POSTGRESDB_HOST'
|
env: 'DB_POSTGRESDB_HOST',
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
doc: 'PostgresDB Password',
|
doc: 'PostgresDB Password',
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'DB_POSTGRESDB_PASSWORD'
|
env: 'DB_POSTGRESDB_PASSWORD',
|
||||||
},
|
},
|
||||||
port: {
|
port: {
|
||||||
doc: 'PostgresDB Port',
|
doc: 'PostgresDB Port',
|
||||||
format: Number,
|
format: Number,
|
||||||
default: 5432,
|
default: 5432,
|
||||||
env: 'DB_POSTGRESDB_PORT'
|
env: 'DB_POSTGRESDB_PORT',
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
doc: 'PostgresDB User',
|
doc: 'PostgresDB User',
|
||||||
format: String,
|
format: String,
|
||||||
default: 'root',
|
default: 'root',
|
||||||
env: 'DB_POSTGRESDB_USER'
|
env: 'DB_POSTGRESDB_USER',
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
doc: 'PostgresDB Schema',
|
doc: 'PostgresDB Schema',
|
||||||
format: String,
|
format: String,
|
||||||
default: 'public',
|
default: 'public',
|
||||||
env: 'DB_POSTGRESDB_SCHEMA'
|
env: 'DB_POSTGRESDB_SCHEMA',
|
||||||
},
|
},
|
||||||
|
|
||||||
ssl: {
|
ssl: {
|
||||||
@@ -89,7 +89,7 @@ const config = convict({
|
|||||||
default: true,
|
default: true,
|
||||||
env: 'DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED',
|
env: 'DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED',
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
mysqldb: {
|
mysqldb: {
|
||||||
@@ -97,31 +97,31 @@ const config = convict({
|
|||||||
doc: 'MySQL Database',
|
doc: 'MySQL Database',
|
||||||
format: String,
|
format: String,
|
||||||
default: 'n8n',
|
default: 'n8n',
|
||||||
env: 'DB_MYSQLDB_DATABASE'
|
env: 'DB_MYSQLDB_DATABASE',
|
||||||
},
|
},
|
||||||
host: {
|
host: {
|
||||||
doc: 'MySQL Host',
|
doc: 'MySQL Host',
|
||||||
format: String,
|
format: String,
|
||||||
default: 'localhost',
|
default: 'localhost',
|
||||||
env: 'DB_MYSQLDB_HOST'
|
env: 'DB_MYSQLDB_HOST',
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
doc: 'MySQL Password',
|
doc: 'MySQL Password',
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'DB_MYSQLDB_PASSWORD'
|
env: 'DB_MYSQLDB_PASSWORD',
|
||||||
},
|
},
|
||||||
port: {
|
port: {
|
||||||
doc: 'MySQL Port',
|
doc: 'MySQL Port',
|
||||||
format: Number,
|
format: Number,
|
||||||
default: 3306,
|
default: 3306,
|
||||||
env: 'DB_MYSQLDB_PORT'
|
env: 'DB_MYSQLDB_PORT',
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
doc: 'MySQL User',
|
doc: 'MySQL User',
|
||||||
format: String,
|
format: String,
|
||||||
default: 'root',
|
default: 'root',
|
||||||
env: 'DB_MYSQLDB_USER'
|
env: 'DB_MYSQLDB_USER',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -136,7 +136,7 @@ const config = convict({
|
|||||||
doc: 'Overwrites for credentials',
|
doc: 'Overwrites for credentials',
|
||||||
format: '*',
|
format: '*',
|
||||||
default: '{}',
|
default: '{}',
|
||||||
env: 'CREDENTIALS_OVERWRITE_DATA'
|
env: 'CREDENTIALS_OVERWRITE_DATA',
|
||||||
},
|
},
|
||||||
endpoint: {
|
endpoint: {
|
||||||
doc: 'Fetch credentials from API',
|
doc: 'Fetch credentials from API',
|
||||||
@@ -156,7 +156,7 @@ const config = convict({
|
|||||||
doc: 'In what process workflows should be executed',
|
doc: 'In what process workflows should be executed',
|
||||||
format: ['main', 'own'],
|
format: ['main', 'own'],
|
||||||
default: 'own',
|
default: 'own',
|
||||||
env: 'EXECUTIONS_PROCESS'
|
env: 'EXECUTIONS_PROCESS',
|
||||||
},
|
},
|
||||||
|
|
||||||
// A Workflow times out and gets canceled after this time (seconds).
|
// A Workflow times out and gets canceled after this time (seconds).
|
||||||
@@ -174,13 +174,13 @@ const config = convict({
|
|||||||
doc: 'Max run time (seconds) before stopping the workflow execution',
|
doc: 'Max run time (seconds) before stopping the workflow execution',
|
||||||
format: Number,
|
format: Number,
|
||||||
default: -1,
|
default: -1,
|
||||||
env: 'EXECUTIONS_TIMEOUT'
|
env: 'EXECUTIONS_TIMEOUT',
|
||||||
},
|
},
|
||||||
maxTimeout: {
|
maxTimeout: {
|
||||||
doc: 'Max execution time (seconds) that can be set for a workflow individually',
|
doc: 'Max execution time (seconds) that can be set for a workflow individually',
|
||||||
format: Number,
|
format: Number,
|
||||||
default: 3600,
|
default: 3600,
|
||||||
env: 'EXECUTIONS_TIMEOUT_MAX'
|
env: 'EXECUTIONS_TIMEOUT_MAX',
|
||||||
},
|
},
|
||||||
|
|
||||||
// If a workflow executes all the data gets saved by default. This
|
// If a workflow executes all the data gets saved by default. This
|
||||||
@@ -193,13 +193,13 @@ const config = convict({
|
|||||||
doc: 'What workflow execution data to save on error',
|
doc: 'What workflow execution data to save on error',
|
||||||
format: ['all', 'none'],
|
format: ['all', 'none'],
|
||||||
default: 'all',
|
default: 'all',
|
||||||
env: 'EXECUTIONS_DATA_SAVE_ON_ERROR'
|
env: 'EXECUTIONS_DATA_SAVE_ON_ERROR',
|
||||||
},
|
},
|
||||||
saveDataOnSuccess: {
|
saveDataOnSuccess: {
|
||||||
doc: 'What workflow execution data to save on success',
|
doc: 'What workflow execution data to save on success',
|
||||||
format: ['all', 'none'],
|
format: ['all', 'none'],
|
||||||
default: 'all',
|
default: 'all',
|
||||||
env: 'EXECUTIONS_DATA_SAVE_ON_SUCCESS'
|
env: 'EXECUTIONS_DATA_SAVE_ON_SUCCESS',
|
||||||
},
|
},
|
||||||
|
|
||||||
// If the executions of workflows which got started via the editor
|
// If the executions of workflows which got started via the editor
|
||||||
@@ -211,7 +211,7 @@ const config = convict({
|
|||||||
doc: 'Save data of executions when started manually via editor',
|
doc: 'Save data of executions when started manually via editor',
|
||||||
format: 'Boolean',
|
format: 'Boolean',
|
||||||
default: false,
|
default: false,
|
||||||
env: 'EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS'
|
env: 'EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS',
|
||||||
},
|
},
|
||||||
|
|
||||||
// To not exceed the database's capacity and keep its size moderate
|
// To not exceed the database's capacity and keep its size moderate
|
||||||
@@ -223,19 +223,19 @@ const config = convict({
|
|||||||
doc: 'Delete data of past executions on a rolling basis',
|
doc: 'Delete data of past executions on a rolling basis',
|
||||||
format: 'Boolean',
|
format: 'Boolean',
|
||||||
default: false,
|
default: false,
|
||||||
env: 'EXECUTIONS_DATA_PRUNE'
|
env: 'EXECUTIONS_DATA_PRUNE',
|
||||||
},
|
},
|
||||||
pruneDataMaxAge: {
|
pruneDataMaxAge: {
|
||||||
doc: 'How old (hours) the execution data has to be to get deleted',
|
doc: 'How old (hours) the execution data has to be to get deleted',
|
||||||
format: Number,
|
format: Number,
|
||||||
default: 336,
|
default: 336,
|
||||||
env: 'EXECUTIONS_DATA_MAX_AGE'
|
env: 'EXECUTIONS_DATA_MAX_AGE',
|
||||||
},
|
},
|
||||||
pruneDataTimeout: {
|
pruneDataTimeout: {
|
||||||
doc: 'Timeout (seconds) after execution data has been pruned',
|
doc: 'Timeout (seconds) after execution data has been pruned',
|
||||||
format: Number,
|
format: Number,
|
||||||
default: 3600,
|
default: 3600,
|
||||||
env: 'EXECUTIONS_DATA_PRUNE_TIMEOUT'
|
env: 'EXECUTIONS_DATA_PRUNE_TIMEOUT',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ const config = convict({
|
|||||||
doc: 'The timezone to use',
|
doc: 'The timezone to use',
|
||||||
format: '*',
|
format: '*',
|
||||||
default: 'America/New_York',
|
default: 'America/New_York',
|
||||||
env: 'GENERIC_TIMEZONE'
|
env: 'GENERIC_TIMEZONE',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -258,66 +258,78 @@ const config = convict({
|
|||||||
default: '/',
|
default: '/',
|
||||||
arg: 'path',
|
arg: 'path',
|
||||||
env: 'N8N_PATH',
|
env: 'N8N_PATH',
|
||||||
doc: 'Path n8n is deployed to'
|
doc: 'Path n8n is deployed to',
|
||||||
},
|
},
|
||||||
host: {
|
host: {
|
||||||
format: String,
|
format: String,
|
||||||
default: 'localhost',
|
default: 'localhost',
|
||||||
arg: 'host',
|
arg: 'host',
|
||||||
env: 'N8N_HOST',
|
env: 'N8N_HOST',
|
||||||
doc: 'Host name n8n can be reached'
|
doc: 'Host name n8n can be reached',
|
||||||
},
|
},
|
||||||
port: {
|
port: {
|
||||||
format: Number,
|
format: Number,
|
||||||
default: 5678,
|
default: 5678,
|
||||||
arg: 'port',
|
arg: 'port',
|
||||||
env: 'N8N_PORT',
|
env: 'N8N_PORT',
|
||||||
doc: 'HTTP port n8n can be reached'
|
doc: 'HTTP port n8n can be reached',
|
||||||
},
|
},
|
||||||
listen_address: {
|
listen_address: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '0.0.0.0',
|
default: '0.0.0.0',
|
||||||
env: 'N8N_LISTEN_ADDRESS',
|
env: 'N8N_LISTEN_ADDRESS',
|
||||||
doc: 'IP address n8n should listen on'
|
doc: 'IP address n8n should listen on',
|
||||||
},
|
},
|
||||||
protocol: {
|
protocol: {
|
||||||
format: ['http', 'https'],
|
format: ['http', 'https'],
|
||||||
default: 'http',
|
default: 'http',
|
||||||
env: 'N8N_PROTOCOL',
|
env: 'N8N_PROTOCOL',
|
||||||
doc: 'HTTP Protocol via which n8n can be reached'
|
doc: 'HTTP Protocol via which n8n can be reached',
|
||||||
},
|
},
|
||||||
ssl_key: {
|
ssl_key: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'N8N_SSL_KEY',
|
env: 'N8N_SSL_KEY',
|
||||||
doc: 'SSL Key for HTTPS Protocol'
|
doc: 'SSL Key for HTTPS Protocol',
|
||||||
},
|
},
|
||||||
ssl_cert: {
|
ssl_cert: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'N8N_SSL_CERT',
|
env: 'N8N_SSL_CERT',
|
||||||
doc: 'SSL Cert for HTTPS Protocol'
|
doc: 'SSL Cert for HTTPS Protocol',
|
||||||
},
|
},
|
||||||
|
|
||||||
security: {
|
security: {
|
||||||
|
excludeEndpoints: {
|
||||||
|
doc: 'Additional endpoints to exclude auth checks. Multiple endpoints can be separated by colon (":")',
|
||||||
|
format: String,
|
||||||
|
default: '',
|
||||||
|
env: 'N8N_AUTH_EXCLUDE_ENDPOINTS',
|
||||||
|
},
|
||||||
basicAuth: {
|
basicAuth: {
|
||||||
active: {
|
active: {
|
||||||
format: 'Boolean',
|
format: 'Boolean',
|
||||||
default: false,
|
default: false,
|
||||||
env: 'N8N_BASIC_AUTH_ACTIVE',
|
env: 'N8N_BASIC_AUTH_ACTIVE',
|
||||||
doc: 'If basic auth should be activated for editor and REST-API'
|
doc: 'If basic auth should be activated for editor and REST-API',
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'N8N_BASIC_AUTH_USER',
|
env: 'N8N_BASIC_AUTH_USER',
|
||||||
doc: 'The name of the basic auth user'
|
doc: 'The name of the basic auth user',
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'N8N_BASIC_AUTH_PASSWORD',
|
env: 'N8N_BASIC_AUTH_PASSWORD',
|
||||||
doc: 'The password of the basic auth user'
|
doc: 'The password of the basic auth user',
|
||||||
|
},
|
||||||
|
hash: {
|
||||||
|
format: 'Boolean',
|
||||||
|
default: false,
|
||||||
|
env: 'N8N_BASIC_AUTH_HASH',
|
||||||
|
doc: 'If password for basic auth is hashed',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
jwtAuth: {
|
jwtAuth: {
|
||||||
@@ -325,49 +337,49 @@ const config = convict({
|
|||||||
format: 'Boolean',
|
format: 'Boolean',
|
||||||
default: false,
|
default: false,
|
||||||
env: 'N8N_JWT_AUTH_ACTIVE',
|
env: 'N8N_JWT_AUTH_ACTIVE',
|
||||||
doc: 'If JWT auth should be activated for editor and REST-API'
|
doc: 'If JWT auth should be activated for editor and REST-API',
|
||||||
},
|
},
|
||||||
jwtHeader: {
|
jwtHeader: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'N8N_JWT_AUTH_HEADER',
|
env: 'N8N_JWT_AUTH_HEADER',
|
||||||
doc: 'The request header containing a signed JWT'
|
doc: 'The request header containing a signed JWT',
|
||||||
},
|
},
|
||||||
jwtHeaderValuePrefix: {
|
jwtHeaderValuePrefix: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'N8N_JWT_AUTH_HEADER_VALUE_PREFIX',
|
env: 'N8N_JWT_AUTH_HEADER_VALUE_PREFIX',
|
||||||
doc: 'The request header value prefix to strip (optional)'
|
doc: 'The request header value prefix to strip (optional)',
|
||||||
},
|
},
|
||||||
jwksUri: {
|
jwksUri: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'N8N_JWKS_URI',
|
env: 'N8N_JWKS_URI',
|
||||||
doc: 'The URI to fetch JWK Set for JWT authentication'
|
doc: 'The URI to fetch JWK Set for JWT authentication',
|
||||||
},
|
},
|
||||||
jwtIssuer: {
|
jwtIssuer: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'N8N_JWT_ISSUER',
|
env: 'N8N_JWT_ISSUER',
|
||||||
doc: 'JWT issuer to expect (optional)'
|
doc: 'JWT issuer to expect (optional)',
|
||||||
},
|
},
|
||||||
jwtNamespace: {
|
jwtNamespace: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'N8N_JWT_NAMESPACE',
|
env: 'N8N_JWT_NAMESPACE',
|
||||||
doc: 'JWT namespace to expect (optional)'
|
doc: 'JWT namespace to expect (optional)',
|
||||||
},
|
},
|
||||||
jwtAllowedTenantKey: {
|
jwtAllowedTenantKey: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'N8N_JWT_ALLOWED_TENANT_KEY',
|
env: 'N8N_JWT_ALLOWED_TENANT_KEY',
|
||||||
doc: 'JWT tenant key name to inspect within JWT namespace (optional)'
|
doc: 'JWT tenant key name to inspect within JWT namespace (optional)',
|
||||||
},
|
},
|
||||||
jwtAllowedTenant: {
|
jwtAllowedTenant: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'N8N_JWT_ALLOWED_TENANT',
|
env: 'N8N_JWT_ALLOWED_TENANT',
|
||||||
doc: 'JWT tenant to allow (optional)'
|
doc: 'JWT tenant to allow (optional)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -377,19 +389,19 @@ const config = convict({
|
|||||||
format: String,
|
format: String,
|
||||||
default: 'rest',
|
default: 'rest',
|
||||||
env: 'N8N_ENDPOINT_REST',
|
env: 'N8N_ENDPOINT_REST',
|
||||||
doc: 'Path for rest endpoint'
|
doc: 'Path for rest endpoint',
|
||||||
},
|
},
|
||||||
webhook: {
|
webhook: {
|
||||||
format: String,
|
format: String,
|
||||||
default: 'webhook',
|
default: 'webhook',
|
||||||
env: 'N8N_ENDPOINT_WEBHOOK',
|
env: 'N8N_ENDPOINT_WEBHOOK',
|
||||||
doc: 'Path for webhook endpoint'
|
doc: 'Path for webhook endpoint',
|
||||||
},
|
},
|
||||||
webhookTest: {
|
webhookTest: {
|
||||||
format: String,
|
format: String,
|
||||||
default: 'webhook-test',
|
default: 'webhook-test',
|
||||||
env: 'N8N_ENDPOINT_WEBHOOK_TEST',
|
env: 'N8N_ENDPOINT_WEBHOOK_TEST',
|
||||||
doc: 'Path for test-webhook endpoint'
|
doc: 'Path for test-webhook endpoint',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -397,7 +409,7 @@ const config = convict({
|
|||||||
doc: 'Files containing external hooks. Multiple files can be separated by colon (":")',
|
doc: 'Files containing external hooks. Multiple files can be separated by colon (":")',
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'EXTERNAL_HOOK_FILES'
|
env: 'EXTERNAL_HOOK_FILES',
|
||||||
},
|
},
|
||||||
|
|
||||||
nodes: {
|
nodes: {
|
||||||
@@ -421,13 +433,13 @@ const config = convict({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
default: '[]',
|
default: '[]',
|
||||||
env: 'NODES_EXCLUDE'
|
env: 'NODES_EXCLUDE',
|
||||||
},
|
},
|
||||||
errorTriggerType: {
|
errorTriggerType: {
|
||||||
doc: 'Node Type to use as Error Trigger',
|
doc: 'Node Type to use as Error Trigger',
|
||||||
format: String,
|
format: String,
|
||||||
default: 'n8n-nodes-base.errorTrigger',
|
default: 'n8n-nodes-base.errorTrigger',
|
||||||
env: 'NODES_ERROR_TRIGGER_TYPE'
|
env: 'NODES_ERROR_TRIGGER_TYPE',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n",
|
"name": "n8n",
|
||||||
"version": "0.80.0",
|
"version": "0.89.2",
|
||||||
"description": "n8n Workflow Automation Tool",
|
"description": "n8n Workflow Automation Tool",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
"start:windows": "cd bin && n8n",
|
"start:windows": "cd bin && n8n",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"tslint": "tslint -p tsconfig.json -c tslint.json",
|
"tslint": "tslint -p tsconfig.json -c tslint.json",
|
||||||
|
"tslintfix": "tslint --fix -p tsconfig.json -c tslint.json",
|
||||||
"watch": "tsc --watch",
|
"watch": "tsc --watch",
|
||||||
"typeorm": "ts-node ./node_modules/typeorm/cli.js"
|
"typeorm": "ts-node ./node_modules/typeorm/cli.js"
|
||||||
},
|
},
|
||||||
@@ -54,33 +55,35 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@oclif/dev-cli": "^1.22.2",
|
"@oclif/dev-cli": "^1.22.2",
|
||||||
"@types/basic-auth": "^1.1.2",
|
"@types/basic-auth": "^1.1.2",
|
||||||
|
"@types/bcryptjs": "^2.4.1",
|
||||||
"@types/compression": "1.0.1",
|
"@types/compression": "1.0.1",
|
||||||
"@types/connect-history-api-fallback": "^1.3.1",
|
"@types/connect-history-api-fallback": "^1.3.1",
|
||||||
"@types/convict": "^4.2.1",
|
"@types/convict": "^4.2.1",
|
||||||
"@types/dotenv": "^8.2.0",
|
"@types/dotenv": "^8.2.0",
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.17.6",
|
||||||
"@types/jest": "^25.2.1",
|
"@types/jest": "^26.0.13",
|
||||||
"@types/localtunnel": "^1.9.0",
|
"@types/localtunnel": "^1.9.0",
|
||||||
"@types/lodash.get": "^4.4.6",
|
"@types/lodash.get": "^4.4.6",
|
||||||
"@types/node": "^14.0.27",
|
"@types/node": "14.0.27",
|
||||||
"@types/open": "^6.1.0",
|
"@types/open": "^6.1.0",
|
||||||
"@types/parseurl": "^1.3.1",
|
"@types/parseurl": "^1.3.1",
|
||||||
"@types/request-promise-native": "~1.0.15",
|
"@types/request-promise-native": "~1.0.15",
|
||||||
"concurrently": "^5.1.0",
|
"concurrently": "^5.1.0",
|
||||||
"jest": "^24.9.0",
|
"jest": "^26.4.2",
|
||||||
"nodemon": "^2.0.2",
|
"nodemon": "^2.0.2",
|
||||||
"p-cancelable": "^2.0.0",
|
"p-cancelable": "^2.0.0",
|
||||||
"run-script-os": "^1.0.7",
|
"run-script-os": "^1.0.7",
|
||||||
"ts-jest": "^25.4.0",
|
"ts-jest": "^26.3.0",
|
||||||
|
"ts-node": "^8.9.1",
|
||||||
"tslint": "^6.1.2",
|
"tslint": "^6.1.2",
|
||||||
"typescript": "~3.7.4",
|
"typescript": "~3.9.7"
|
||||||
"ts-node": "^8.9.1"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oclif/command": "^1.5.18",
|
"@oclif/command": "^1.5.18",
|
||||||
"@oclif/errors": "^1.2.2",
|
"@oclif/errors": "^1.2.2",
|
||||||
"@types/jsonwebtoken": "^8.3.4",
|
"@types/jsonwebtoken": "^8.3.4",
|
||||||
"basic-auth": "^2.0.1",
|
"basic-auth": "^2.0.1",
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.18.3",
|
"body-parser": "^1.18.3",
|
||||||
"body-parser-xml": "^1.1.0",
|
"body-parser-xml": "^1.1.0",
|
||||||
"client-oauth2": "^4.2.5",
|
"client-oauth2": "^4.2.5",
|
||||||
@@ -95,15 +98,15 @@
|
|||||||
"google-timezones-json": "^1.0.2",
|
"google-timezones-json": "^1.0.2",
|
||||||
"inquirer": "^7.0.1",
|
"inquirer": "^7.0.1",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"jwks-rsa": "^1.6.0",
|
"jwks-rsa": "~1.9.0",
|
||||||
"localtunnel": "^2.0.0",
|
"localtunnel": "^2.0.0",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"mongodb": "^3.5.5",
|
"mongodb": "^3.5.5",
|
||||||
"mysql2": "^2.0.1",
|
"mysql2": "~2.1.0",
|
||||||
"n8n-core": "~0.44.0",
|
"n8n-core": "~0.48.0",
|
||||||
"n8n-editor-ui": "~0.55.0",
|
"n8n-editor-ui": "~0.60.0",
|
||||||
"n8n-nodes-base": "~0.75.0",
|
"n8n-nodes-base": "~0.85.0",
|
||||||
"n8n-workflow": "~0.39.0",
|
"n8n-workflow": "~0.42.0",
|
||||||
"oauth-1.0a": "^2.2.6",
|
"oauth-1.0a": "^2.2.6",
|
||||||
"open": "^7.0.0",
|
"open": "^7.0.0",
|
||||||
"pg": "^8.3.0",
|
"pg": "^8.3.0",
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IExecutionsCurrentSummary,
|
|
||||||
IExecutingWorkflowData,
|
IExecutingWorkflowData,
|
||||||
|
IExecutionsCurrentSummary,
|
||||||
IWorkflowExecutionDataProcess,
|
IWorkflowExecutionDataProcess,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import {
|
import {
|
||||||
IActivationError,
|
|
||||||
Db,
|
Db,
|
||||||
NodeTypes,
|
IActivationError,
|
||||||
IResponseCallbackData,
|
IResponseCallbackData,
|
||||||
|
IWebhookDb,
|
||||||
IWorkflowDb,
|
IWorkflowDb,
|
||||||
IWorkflowExecutionDataProcess,
|
IWorkflowExecutionDataProcess,
|
||||||
|
NodeTypes,
|
||||||
ResponseHelper,
|
ResponseHelper,
|
||||||
WebhookHelpers,
|
WebhookHelpers,
|
||||||
WorkflowCredentials,
|
WorkflowCredentials,
|
||||||
|
WorkflowExecuteAdditionalData,
|
||||||
WorkflowHelpers,
|
WorkflowHelpers,
|
||||||
WorkflowRunner,
|
WorkflowRunner,
|
||||||
WorkflowExecuteAdditionalData,
|
|
||||||
IWebhookDb,
|
|
||||||
} from './';
|
} from './';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -26,8 +26,8 @@ import {
|
|||||||
INode,
|
INode,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
IRunExecutionData,
|
IRunExecutionData,
|
||||||
NodeHelpers,
|
|
||||||
IWorkflowExecuteAdditionalData as IWorkflowExecuteAdditionalDataWorkflow,
|
IWorkflowExecuteAdditionalData as IWorkflowExecuteAdditionalDataWorkflow,
|
||||||
|
NodeHelpers,
|
||||||
WebhookHttpMethod,
|
WebhookHttpMethod,
|
||||||
Workflow,
|
Workflow,
|
||||||
WorkflowExecuteMode,
|
WorkflowExecuteMode,
|
||||||
@@ -181,8 +181,9 @@ export class ActiveWorkflowRunner {
|
|||||||
* @returns {string[]}
|
* @returns {string[]}
|
||||||
* @memberof ActiveWorkflowRunner
|
* @memberof ActiveWorkflowRunner
|
||||||
*/
|
*/
|
||||||
getActiveWorkflows(): Promise<IWorkflowDb[]> {
|
async getActiveWorkflows(): Promise<IWorkflowDb[]> {
|
||||||
return Db.collections.Workflow?.find({ where: { active: true }, select: ['id'] }) as Promise<IWorkflowDb[]>;
|
const activeWorkflows = await Db.collections.Workflow?.find({ where: { active: true }, select: ['id'] }) as IWorkflowDb[];
|
||||||
|
return activeWorkflows.filter(workflow => this.activationErrors[workflow.id.toString()] === undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -234,7 +235,7 @@ export class ActiveWorkflowRunner {
|
|||||||
path = node.parameters.path as string;
|
path = node.parameters.path as string;
|
||||||
|
|
||||||
if (node.parameters.path === undefined) {
|
if (node.parameters.path === undefined) {
|
||||||
path = workflow.getSimpleParameterValue(node, webhookData.webhookDescription['path']) as string | undefined;
|
path = workflow.expression.getSimpleParameterValue(node, webhookData.webhookDescription['path']) as string | undefined;
|
||||||
|
|
||||||
if (path === undefined) {
|
if (path === undefined) {
|
||||||
// TODO: Use a proper logger
|
// TODO: Use a proper logger
|
||||||
@@ -243,7 +244,7 @@ export class ActiveWorkflowRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFullPath: boolean = workflow.getSimpleParameterValue(node, webhookData.webhookDescription['isFullPath'], false) as boolean;
|
const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookData.webhookDescription['isFullPath'], false) as boolean;
|
||||||
|
|
||||||
const webhook = {
|
const webhook = {
|
||||||
workflowId: webhookData.workflowId,
|
workflowId: webhookData.workflowId,
|
||||||
@@ -257,17 +258,20 @@ export class ActiveWorkflowRunner {
|
|||||||
await Db.collections.Webhook?.insert(webhook);
|
await Db.collections.Webhook?.insert(webhook);
|
||||||
|
|
||||||
const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, false);
|
const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, false);
|
||||||
if (webhookExists === false) {
|
if (webhookExists !== true) {
|
||||||
// If webhook does not exist yet create it
|
// If webhook does not exist yet create it
|
||||||
await workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, false);
|
await workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
try {
|
||||||
|
await this.removeWorkflowWebhooks(workflow.id as string);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Could not remove webhooks of workflow "${workflow.id}" because of error: "${error.message}"`);
|
||||||
|
}
|
||||||
|
|
||||||
let errorMessage = '';
|
let errorMessage = '';
|
||||||
|
|
||||||
await Db.collections.Webhook?.delete({ workflowId: workflow.id });
|
|
||||||
|
|
||||||
// if it's a workflow from the the insert
|
// if it's a workflow from the the insert
|
||||||
// TODO check if there is standard error code for deplicate key violation that works
|
// TODO check if there is standard error code for deplicate key violation that works
|
||||||
// with all databases
|
// with all databases
|
||||||
@@ -317,6 +321,8 @@ export class ActiveWorkflowRunner {
|
|||||||
await workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, false);
|
await workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await WorkflowHelpers.saveStaticData(workflow);
|
||||||
|
|
||||||
// if it's a mongo objectId convert it to string
|
// if it's a mongo objectId convert it to string
|
||||||
if (typeof workflowData.id === 'object') {
|
if (typeof workflowData.id === 'object') {
|
||||||
workflowData.id = workflowData.id.toString();
|
workflowData.id = workflowData.id.toString();
|
||||||
@@ -346,8 +352,8 @@ export class ActiveWorkflowRunner {
|
|||||||
node,
|
node,
|
||||||
data: {
|
data: {
|
||||||
main: data,
|
main: data,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const executionData: IRunExecutionData = {
|
const executionData: IRunExecutionData = {
|
||||||
@@ -411,7 +417,7 @@ export class ActiveWorkflowRunner {
|
|||||||
const returnFunctions = NodeExecuteFunctions.getExecuteTriggerFunctions(workflow, node, additionalData, mode);
|
const returnFunctions = NodeExecuteFunctions.getExecuteTriggerFunctions(workflow, node, additionalData, mode);
|
||||||
returnFunctions.emit = (data: INodeExecutionData[][]): void => {
|
returnFunctions.emit = (data: INodeExecutionData[][]): void => {
|
||||||
WorkflowHelpers.saveStaticData(workflow);
|
WorkflowHelpers.saveStaticData(workflow);
|
||||||
this.runWorkflow(workflowData, node, data, additionalData, mode);
|
this.runWorkflow(workflowData, node, data, additionalData, mode).catch((err) => console.error(err));
|
||||||
};
|
};
|
||||||
return returnFunctions;
|
return returnFunctions;
|
||||||
});
|
});
|
||||||
@@ -495,7 +501,11 @@ export class ActiveWorkflowRunner {
|
|||||||
|
|
||||||
if (this.activeWorkflows !== null) {
|
if (this.activeWorkflows !== null) {
|
||||||
// Remove all the webhooks of the workflow
|
// Remove all the webhooks of the workflow
|
||||||
await this.removeWorkflowWebhooks(workflowId);
|
try {
|
||||||
|
await this.removeWorkflowWebhooks(workflowId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Could not remove webhooks of workflow "${workflowId}" because of error: "${error.message}"`);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.activationErrors[workflowId] !== undefined) {
|
if (this.activationErrors[workflowId] !== undefined) {
|
||||||
// If there were any activation errors delete them
|
// If there were any activation errors delete them
|
||||||
|
|||||||
@@ -5,9 +5,14 @@ import {
|
|||||||
import {
|
import {
|
||||||
ICredentialDataDecryptedObject,
|
ICredentialDataDecryptedObject,
|
||||||
ICredentialsHelper,
|
ICredentialsHelper,
|
||||||
|
INode,
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeData,
|
||||||
|
INodeTypes,
|
||||||
NodeHelpers,
|
NodeHelpers,
|
||||||
|
Workflow,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -18,6 +23,19 @@ import {
|
|||||||
} from './';
|
} from './';
|
||||||
|
|
||||||
|
|
||||||
|
const mockNodeTypes: INodeTypes = {
|
||||||
|
nodeTypes: {},
|
||||||
|
init: async (nodeTypes?: INodeTypeData): Promise<void> => { },
|
||||||
|
getAll: (): INodeType[] => {
|
||||||
|
// Does not get used in Workflow so no need to return it
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
getByName: (nodeType: string): INodeType | undefined => {
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export class CredentialsHelper extends ICredentialsHelper {
|
export class CredentialsHelper extends ICredentialsHelper {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,7 +125,7 @@ export class CredentialsHelper extends ICredentialsHelper {
|
|||||||
const credentialsProperties = this.getCredentialsProperties(type);
|
const credentialsProperties = this.getCredentialsProperties(type);
|
||||||
|
|
||||||
// Add the default credential values
|
// Add the default credential values
|
||||||
const decryptedData = NodeHelpers.getNodeParameters(credentialsProperties, decryptedDataOriginal as INodeParameters, true, false) as ICredentialDataDecryptedObject;
|
let decryptedData = NodeHelpers.getNodeParameters(credentialsProperties, decryptedDataOriginal as INodeParameters, true, false) as ICredentialDataDecryptedObject;
|
||||||
|
|
||||||
if (decryptedDataOriginal.oauthTokenData !== undefined) {
|
if (decryptedDataOriginal.oauthTokenData !== undefined) {
|
||||||
// The OAuth data gets removed as it is not defined specifically as a parameter
|
// The OAuth data gets removed as it is not defined specifically as a parameter
|
||||||
@@ -115,6 +133,18 @@ export class CredentialsHelper extends ICredentialsHelper {
|
|||||||
decryptedData.oauthTokenData = decryptedDataOriginal.oauthTokenData;
|
decryptedData.oauthTokenData = decryptedDataOriginal.oauthTokenData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mockNode: INode = {
|
||||||
|
name: '',
|
||||||
|
typeVersion: 1,
|
||||||
|
type: 'mock',
|
||||||
|
position: [0, 0],
|
||||||
|
parameters: decryptedData as INodeParameters,
|
||||||
|
};
|
||||||
|
|
||||||
|
const workflow = new Workflow({ nodes: [mockNode], connections: {}, active: false, nodeTypes: mockNodeTypes});
|
||||||
|
// Resolve expressions if any are set
|
||||||
|
decryptedData = workflow.expression.getComplexParameterValue(mockNode, decryptedData as INodeParameters, undefined) as ICredentialDataDecryptedObject;
|
||||||
|
|
||||||
// Load and apply the credentials overwrites if any exist
|
// Load and apply the credentials overwrites if any exist
|
||||||
const credentialsOverwrites = CredentialsOverwrites();
|
const credentialsOverwrites = CredentialsOverwrites();
|
||||||
return credentialsOverwrites.applyOverwrite(type, decryptedData);
|
return credentialsOverwrites.applyOverwrite(type, decryptedData);
|
||||||
|
|||||||
@@ -3,32 +3,53 @@ import {
|
|||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ICredentialsOverwrite,
|
CredentialTypes,
|
||||||
GenericHelpers,
|
GenericHelpers,
|
||||||
|
ICredentialsOverwrite,
|
||||||
} from './';
|
} from './';
|
||||||
|
|
||||||
|
|
||||||
class CredentialsOverwritesClass {
|
class CredentialsOverwritesClass {
|
||||||
|
|
||||||
|
private credentialTypes = CredentialTypes();
|
||||||
private overwriteData: ICredentialsOverwrite = {};
|
private overwriteData: ICredentialsOverwrite = {};
|
||||||
|
private resolvedTypes: string[] = [];
|
||||||
|
|
||||||
|
|
||||||
async init(overwriteData?: ICredentialsOverwrite) {
|
async init(overwriteData?: ICredentialsOverwrite) {
|
||||||
if (overwriteData !== undefined) {
|
if (overwriteData !== undefined) {
|
||||||
// If data is already given it can directly be set instead of
|
// If data is already given it can directly be set instead of
|
||||||
// loaded from environment
|
// loaded from environment
|
||||||
this.overwriteData = overwriteData;
|
this.__setData(JSON.parse(JSON.stringify(overwriteData)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await GenericHelpers.getConfigValue('credentials.overwrite.data') as string;
|
const data = await GenericHelpers.getConfigValue('credentials.overwrite.data') as string;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.overwriteData = JSON.parse(data);
|
const overwriteData = JSON.parse(data);
|
||||||
|
this.__setData(overwriteData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`The credentials-overwrite is not valid JSON.`);
|
throw new Error(`The credentials-overwrite is not valid JSON.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__setData(overwriteData: ICredentialsOverwrite) {
|
||||||
|
this.overwriteData = overwriteData;
|
||||||
|
|
||||||
|
for (const credentialTypeData of this.credentialTypes.getAll()) {
|
||||||
|
const type = credentialTypeData.name;
|
||||||
|
|
||||||
|
const overwrites = this.__getExtended(type);
|
||||||
|
|
||||||
|
if (overwrites && Object.keys(overwrites).length) {
|
||||||
|
this.overwriteData[type] = overwrites;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
applyOverwrite(type: string, data: ICredentialDataDecryptedObject) {
|
applyOverwrite(type: string, data: ICredentialDataDecryptedObject) {
|
||||||
|
|
||||||
const overwrites = this.get(type);
|
const overwrites = this.get(type);
|
||||||
@@ -48,10 +69,45 @@ class CredentialsOverwritesClass {
|
|||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__getExtended(type: string): ICredentialDataDecryptedObject | undefined {
|
||||||
|
|
||||||
|
if (this.resolvedTypes.includes(type)) {
|
||||||
|
// Type got already resolved and can so returned directly
|
||||||
|
return this.overwriteData[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
const credentialTypeData = this.credentialTypes.getByName(type);
|
||||||
|
|
||||||
|
if (credentialTypeData === undefined) {
|
||||||
|
throw new Error(`The credentials of type "${type}" are not known.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (credentialTypeData.extends === undefined) {
|
||||||
|
this.resolvedTypes.push(type);
|
||||||
|
return this.overwriteData[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
const overwrites: ICredentialDataDecryptedObject = {};
|
||||||
|
for (const credentialsTypeName of credentialTypeData.extends) {
|
||||||
|
Object.assign(overwrites, this.__getExtended(credentialsTypeName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.overwriteData[type] !== undefined) {
|
||||||
|
Object.assign(overwrites, this.overwriteData[type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resolvedTypes.push(type);
|
||||||
|
|
||||||
|
return overwrites;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
get(type: string): ICredentialDataDecryptedObject | undefined {
|
get(type: string): ICredentialDataDecryptedObject | undefined {
|
||||||
return this.overwriteData[type];
|
return this.overwriteData[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getAll(): ICredentialsOverwrite {
|
getAll(): ICredentialsOverwrite {
|
||||||
return this.overwriteData;
|
return this.overwriteData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,27 +33,27 @@ export let collections: IDatabaseCollections = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
CreateIndexStoppedAt1594828256133,
|
||||||
InitialMigration1587669153312,
|
InitialMigration1587669153312,
|
||||||
WebhookModel1589476000887,
|
WebhookModel1589476000887,
|
||||||
CreateIndexStoppedAt1594828256133,
|
|
||||||
} from './databases/postgresdb/migrations';
|
} from './databases/postgresdb/migrations';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
CreateIndexStoppedAt1594910478695,
|
||||||
InitialMigration1587563438936,
|
InitialMigration1587563438936,
|
||||||
WebhookModel1592679094242,
|
WebhookModel1592679094242,
|
||||||
CreateIndexStoppedAt1594910478695,
|
|
||||||
} from './databases/mongodb/migrations';
|
} from './databases/mongodb/migrations';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
CreateIndexStoppedAt1594902918301,
|
||||||
InitialMigration1588157391238,
|
InitialMigration1588157391238,
|
||||||
WebhookModel1592447867632,
|
WebhookModel1592447867632,
|
||||||
CreateIndexStoppedAt1594902918301,
|
|
||||||
} from './databases/mysqldb/migrations';
|
} from './databases/mysqldb/migrations';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
CreateIndexStoppedAt1594825041918,
|
||||||
InitialMigration1588102412422,
|
InitialMigration1588102412422,
|
||||||
WebhookModel1592445003908,
|
WebhookModel1592445003908,
|
||||||
CreateIndexStoppedAt1594825041918,
|
|
||||||
} from './databases/sqlite/migrations';
|
} from './databases/sqlite/migrations';
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
@@ -154,7 +154,7 @@ export async function init(): Promise<IDatabaseCollections> {
|
|||||||
migrations: [
|
migrations: [
|
||||||
InitialMigration1588102412422,
|
InitialMigration1588102412422,
|
||||||
WebhookModel1592445003908,
|
WebhookModel1592445003908,
|
||||||
CreateIndexStoppedAt1594825041918
|
CreateIndexStoppedAt1594825041918,
|
||||||
],
|
],
|
||||||
migrationsRun: true,
|
migrationsRun: true,
|
||||||
migrationsTableName: `${entityPrefix}migrations`,
|
migrationsTableName: `${entityPrefix}migrations`,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Db,
|
Db,
|
||||||
IExternalHooksFunctions,
|
|
||||||
IExternalHooksClass,
|
IExternalHooksClass,
|
||||||
|
IExternalHooksFunctions,
|
||||||
} from './';
|
} from './';
|
||||||
|
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
@@ -64,6 +64,10 @@ class ExternalHooksClass implements IExternalHooksClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exists(hookName: string): boolean {
|
||||||
|
return !!this.externalHooks[hookName];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -288,6 +288,10 @@ export interface IN8nUISettings {
|
|||||||
saveManualExecutions: boolean;
|
saveManualExecutions: boolean;
|
||||||
executionTimeout: number;
|
executionTimeout: number;
|
||||||
maxExecutionTimeout: number;
|
maxExecutionTimeout: number;
|
||||||
|
oauthCallbackUrls: {
|
||||||
|
oauth1: string;
|
||||||
|
oauth2: string;
|
||||||
|
};
|
||||||
timezone: string;
|
timezone: string;
|
||||||
urlBaseWebhook: string;
|
urlBaseWebhook: string;
|
||||||
versionCli: string;
|
versionCli: string;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypes,
|
|
||||||
INodeTypeData,
|
INodeTypeData,
|
||||||
|
INodeTypes,
|
||||||
NodeHelpers,
|
NodeHelpers,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export function sendSuccessResponse(res: Response, data: any, raw?: boolean, res
|
|||||||
res.json(data);
|
res.json(data);
|
||||||
} else {
|
} else {
|
||||||
res.json({
|
res.json({
|
||||||
data
|
data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ export function unflattenExecutionData(fullExecutionData: IExecutionFlattedDb):
|
|||||||
mode: fullExecutionData.mode,
|
mode: fullExecutionData.mode,
|
||||||
startedAt: fullExecutionData.startedAt,
|
startedAt: fullExecutionData.startedAt,
|
||||||
stoppedAt: fullExecutionData.stoppedAt,
|
stoppedAt: fullExecutionData.stoppedAt,
|
||||||
finished: fullExecutionData.finished ? fullExecutionData.finished : false
|
finished: fullExecutionData.finished ? fullExecutionData.finished : false,
|
||||||
});
|
});
|
||||||
|
|
||||||
return returnData;
|
return returnData;
|
||||||
|
|||||||
@@ -20,20 +20,24 @@ import { RequestOptions } from 'oauth-1.0a';
|
|||||||
import * as csrf from 'csrf';
|
import * as csrf from 'csrf';
|
||||||
import * as requestPromise from 'request-promise-native';
|
import * as requestPromise from 'request-promise-native';
|
||||||
import { createHmac } from 'crypto';
|
import { createHmac } from 'crypto';
|
||||||
|
import { compare } from 'bcryptjs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ActiveExecutions,
|
ActiveExecutions,
|
||||||
ActiveWorkflowRunner,
|
ActiveWorkflowRunner,
|
||||||
CredentialsHelper,
|
CredentialsHelper,
|
||||||
|
CredentialsOverwrites,
|
||||||
CredentialTypes,
|
CredentialTypes,
|
||||||
Db,
|
Db,
|
||||||
ExternalHooks,
|
ExternalHooks,
|
||||||
|
GenericHelpers,
|
||||||
IActivationError,
|
IActivationError,
|
||||||
ICustomRequest,
|
|
||||||
ICredentialsDb,
|
ICredentialsDb,
|
||||||
ICredentialsDecryptedDb,
|
ICredentialsDecryptedDb,
|
||||||
ICredentialsDecryptedResponse,
|
ICredentialsDecryptedResponse,
|
||||||
|
ICredentialsOverwrite,
|
||||||
ICredentialsResponse,
|
ICredentialsResponse,
|
||||||
|
ICustomRequest,
|
||||||
IExecutionDeleteFilter,
|
IExecutionDeleteFilter,
|
||||||
IExecutionFlatted,
|
IExecutionFlatted,
|
||||||
IExecutionFlattedDb,
|
IExecutionFlattedDb,
|
||||||
@@ -46,21 +50,18 @@ import {
|
|||||||
IN8nUISettings,
|
IN8nUISettings,
|
||||||
IPackageVersions,
|
IPackageVersions,
|
||||||
IWorkflowBase,
|
IWorkflowBase,
|
||||||
IWorkflowShortResponse,
|
|
||||||
IWorkflowResponse,
|
|
||||||
IWorkflowExecutionDataProcess,
|
IWorkflowExecutionDataProcess,
|
||||||
|
IWorkflowResponse,
|
||||||
|
IWorkflowShortResponse,
|
||||||
|
LoadNodesAndCredentials,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
Push,
|
Push,
|
||||||
ResponseHelper,
|
ResponseHelper,
|
||||||
TestWebhooks,
|
TestWebhooks,
|
||||||
WorkflowCredentials,
|
|
||||||
WebhookHelpers,
|
WebhookHelpers,
|
||||||
|
WorkflowCredentials,
|
||||||
WorkflowExecuteAdditionalData,
|
WorkflowExecuteAdditionalData,
|
||||||
WorkflowRunner,
|
WorkflowRunner,
|
||||||
GenericHelpers,
|
|
||||||
CredentialsOverwrites,
|
|
||||||
ICredentialsOverwrite,
|
|
||||||
LoadNodesAndCredentials,
|
|
||||||
} from './';
|
} from './';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -74,9 +75,9 @@ import {
|
|||||||
ICredentialType,
|
ICredentialType,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
INodeCredentials,
|
INodeCredentials,
|
||||||
INodeTypeDescription,
|
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
INodePropertyOptions,
|
INodePropertyOptions,
|
||||||
|
INodeTypeDescription,
|
||||||
IRunData,
|
IRunData,
|
||||||
IWorkflowCredentials,
|
IWorkflowCredentials,
|
||||||
Workflow,
|
Workflow,
|
||||||
@@ -120,7 +121,7 @@ class App {
|
|||||||
push: Push.Push;
|
push: Push.Push;
|
||||||
versions: IPackageVersions | undefined;
|
versions: IPackageVersions | undefined;
|
||||||
restEndpoint: string;
|
restEndpoint: string;
|
||||||
|
frontendSettings: IN8nUISettings;
|
||||||
protocol: string;
|
protocol: string;
|
||||||
sslKey: string;
|
sslKey: string;
|
||||||
sslCert: string;
|
sslCert: string;
|
||||||
@@ -154,6 +155,25 @@ class App {
|
|||||||
|
|
||||||
this.presetCredentialsLoaded = false;
|
this.presetCredentialsLoaded = false;
|
||||||
this.endpointPresetCredentials = config.get('credentials.overwrite.endpoint') as string;
|
this.endpointPresetCredentials = config.get('credentials.overwrite.endpoint') as string;
|
||||||
|
|
||||||
|
const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl();
|
||||||
|
|
||||||
|
this.frontendSettings = {
|
||||||
|
endpointWebhook: this.endpointWebhook,
|
||||||
|
endpointWebhookTest: this.endpointWebhookTest,
|
||||||
|
saveDataErrorExecution: this.saveDataErrorExecution,
|
||||||
|
saveDataSuccessExecution: this.saveDataSuccessExecution,
|
||||||
|
saveManualExecutions: this.saveManualExecutions,
|
||||||
|
executionTimeout: this.executionTimeout,
|
||||||
|
maxExecutionTimeout: this.maxExecutionTimeout,
|
||||||
|
timezone: this.timezone,
|
||||||
|
urlBaseWebhook,
|
||||||
|
versionCli: '',
|
||||||
|
oauthCallbackUrls: {
|
||||||
|
'oauth1': urlBaseWebhook + `${this.restEndpoint}/oauth1-credential/callback`,
|
||||||
|
'oauth2': urlBaseWebhook + `${this.restEndpoint}/oauth2-credential/callback`,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -171,7 +191,16 @@ class App {
|
|||||||
async config(): Promise<void> {
|
async config(): Promise<void> {
|
||||||
|
|
||||||
this.versions = await GenericHelpers.getVersions();
|
this.versions = await GenericHelpers.getVersions();
|
||||||
const authIgnoreRegex = new RegExp(`^\/(healthz|${this.endpointWebhook}|${this.endpointWebhookTest})\/?.*$`);
|
this.frontendSettings.versionCli = this.versions.cli;
|
||||||
|
|
||||||
|
await this.externalHooks.run('frontend.settings', [this.frontendSettings]);
|
||||||
|
|
||||||
|
const excludeEndpoints = config.get('security.excludeEndpoints') as string;
|
||||||
|
|
||||||
|
const ignoredEndpoints = ['healthz', this.endpointWebhook, this.endpointWebhookTest, this.endpointPresetCredentials];
|
||||||
|
ignoredEndpoints.push.apply(ignoredEndpoints, excludeEndpoints.split(':'));
|
||||||
|
|
||||||
|
const authIgnoreRegex = new RegExp(`^\/(${_(ignoredEndpoints).compact().join('|')})\/?.*$`);
|
||||||
|
|
||||||
// Check for basic auth credentials if activated
|
// Check for basic auth credentials if activated
|
||||||
const basicAuthActive = config.get('security.basicAuth.active') as boolean;
|
const basicAuthActive = config.get('security.basicAuth.active') as boolean;
|
||||||
@@ -186,7 +215,11 @@ class App {
|
|||||||
throw new Error('Basic auth is activated but no password got defined. Please set one!');
|
throw new Error('Basic auth is activated but no password got defined. Please set one!');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
|
const basicAuthHashEnabled = await GenericHelpers.getConfigValue('security.basicAuth.hash') as boolean;
|
||||||
|
|
||||||
|
let validPassword: null | string = null;
|
||||||
|
|
||||||
|
this.app.use(async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
if (req.url.match(authIgnoreRegex)) {
|
if (req.url.match(authIgnoreRegex)) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
@@ -198,12 +231,27 @@ class App {
|
|||||||
return ResponseHelper.basicAuthAuthorizationError(res, realm, 'Authorization is required!');
|
return ResponseHelper.basicAuthAuthorizationError(res, realm, 'Authorization is required!');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (basicAuthData.name !== basicAuthUser || basicAuthData.pass !== basicAuthPassword) {
|
if (basicAuthData.name === basicAuthUser) {
|
||||||
// Provided authentication data is wrong
|
if (basicAuthHashEnabled === true) {
|
||||||
return ResponseHelper.basicAuthAuthorizationError(res, realm, 'Authorization data is wrong!');
|
if (validPassword === null && await compare(basicAuthData.pass, basicAuthPassword)) {
|
||||||
|
// Password is valid so save for future requests
|
||||||
|
validPassword = basicAuthData.pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validPassword === basicAuthData.pass && validPassword !== null) {
|
||||||
|
// Provided hash is correct
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (basicAuthData.pass === basicAuthPassword) {
|
||||||
|
// Provided password is correct
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
// Provided authentication data is wrong
|
||||||
|
return ResponseHelper.basicAuthAuthorizationError(res, realm, 'Authorization data is wrong!');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +313,7 @@ class App {
|
|||||||
|
|
||||||
const jwtVerifyOptions: jwt.VerifyOptions = {
|
const jwtVerifyOptions: jwt.VerifyOptions = {
|
||||||
issuer: jwtIssuer !== '' ? jwtIssuer : undefined,
|
issuer: jwtIssuer !== '' ? jwtIssuer : undefined,
|
||||||
ignoreExpiration: false
|
ignoreExpiration: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
jwt.verify(token, getKey, jwtVerifyOptions, (err: jwt.VerifyErrors, decoded: object) => {
|
jwt.verify(token, getKey, jwtVerifyOptions, (err: jwt.VerifyErrors, decoded: object) => {
|
||||||
@@ -307,7 +355,7 @@ class App {
|
|||||||
limit: '16mb', verify: (req, res, buf) => {
|
limit: '16mb', verify: (req, res, buf) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
req.rawBody = buf;
|
req.rawBody = buf;
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Support application/xml type post data
|
// Support application/xml type post data
|
||||||
@@ -317,14 +365,14 @@ class App {
|
|||||||
normalize: true, // Trim whitespace inside text nodes
|
normalize: true, // Trim whitespace inside text nodes
|
||||||
normalizeTags: true, // Transform tags to lowercase
|
normalizeTags: true, // Transform tags to lowercase
|
||||||
explicitArray: false, // Only put properties in array if length > 1
|
explicitArray: false, // Only put properties in array if length > 1
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.app.use(bodyParser.text({
|
this.app.use(bodyParser.text({
|
||||||
limit: '16mb', verify: (req, res, buf) => {
|
limit: '16mb', verify: (req, res, buf) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
req.rawBody = buf;
|
req.rawBody = buf;
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Make sure that Vue history mode works properly
|
// Make sure that Vue history mode works properly
|
||||||
@@ -334,9 +382,9 @@ class App {
|
|||||||
from: new RegExp(`^\/(${this.restEndpoint}|healthz|css|js|${this.endpointWebhook}|${this.endpointWebhookTest})\/?.*$`),
|
from: new RegExp(`^\/(${this.restEndpoint}|healthz|css|js|${this.endpointWebhook}|${this.endpointWebhookTest})\/?.*$`),
|
||||||
to: (context) => {
|
to: (context) => {
|
||||||
return context.parsedUrl!.pathname!.toString();
|
return context.parsedUrl!.pathname!.toString();
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//support application/x-www-form-urlencoded post data
|
//support application/x-www-form-urlencoded post data
|
||||||
@@ -344,7 +392,7 @@ class App {
|
|||||||
verify: (req, res, buf) => {
|
verify: (req, res, buf) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
req.rawBody = buf;
|
req.rawBody = buf;
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (process.env['NODE_ENV'] !== 'production') {
|
if (process.env['NODE_ENV'] !== 'production') {
|
||||||
@@ -532,6 +580,7 @@ class App {
|
|||||||
newWorkflowData.updatedAt = this.getCurrentDate();
|
newWorkflowData.updatedAt = this.getCurrentDate();
|
||||||
|
|
||||||
await Db.collections.Workflow!.update(id, newWorkflowData);
|
await Db.collections.Workflow!.update(id, newWorkflowData);
|
||||||
|
await this.externalHooks.run('workflow.afterUpdate', [newWorkflowData]);
|
||||||
|
|
||||||
// We sadly get nothing back from "update". Neither if it updated a record
|
// We sadly get nothing back from "update". Neither if it updated a record
|
||||||
// nor the new value. So query now the hopefully updated entry.
|
// nor the new value. So query now the hopefully updated entry.
|
||||||
@@ -580,6 +629,7 @@ class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await Db.collections.Workflow!.delete(id);
|
await Db.collections.Workflow!.delete(id);
|
||||||
|
await this.externalHooks.run('workflow.afterDelete', [id]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
@@ -665,13 +715,36 @@ class App {
|
|||||||
const allNodes = nodeTypes.getAll();
|
const allNodes = nodeTypes.getAll();
|
||||||
|
|
||||||
allNodes.forEach((nodeData) => {
|
allNodes.forEach((nodeData) => {
|
||||||
returnData.push(nodeData.description);
|
// Make a copy of the object. If we don't do this, then when
|
||||||
|
// The method below is called the properties are removed for good
|
||||||
|
// This happens because nodes are returned as reference.
|
||||||
|
const nodeInfo: INodeTypeDescription = {...nodeData.description};
|
||||||
|
if (req.query.includeProperties !== 'true') {
|
||||||
|
// @ts-ignore
|
||||||
|
delete nodeInfo.properties;
|
||||||
|
}
|
||||||
|
returnData.push(nodeInfo);
|
||||||
});
|
});
|
||||||
|
|
||||||
return returnData;
|
return returnData;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
// Returns node information baesd on namese
|
||||||
|
this.app.post(`/${this.restEndpoint}/node-types`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<INodeTypeDescription[]> => {
|
||||||
|
const nodeNames = _.get(req, 'body.nodeNames', []) as string[];
|
||||||
|
const nodeTypes = NodeTypes();
|
||||||
|
|
||||||
|
return nodeNames.map(name => {
|
||||||
|
try {
|
||||||
|
return nodeTypes.getByName(name);
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}).filter(nodeData => !!nodeData).map(nodeData => nodeData!.description);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Node-Types
|
// Node-Types
|
||||||
@@ -1009,7 +1082,7 @@ class App {
|
|||||||
|
|
||||||
const signatureMethod = _.get(oauthCredentials, 'signatureMethod') as string;
|
const signatureMethod = _.get(oauthCredentials, 'signatureMethod') as string;
|
||||||
|
|
||||||
const oauth = new clientOAuth1({
|
const oAuthOptions: clientOAuth1.Options = {
|
||||||
consumer: {
|
consumer: {
|
||||||
key: _.get(oauthCredentials, 'consumerKey') as string,
|
key: _.get(oauthCredentials, 'consumerKey') as string,
|
||||||
secret: _.get(oauthCredentials, 'consumerSecret') as string,
|
secret: _.get(oauthCredentials, 'consumerSecret') as string,
|
||||||
@@ -1021,16 +1094,20 @@ class App {
|
|||||||
.update(base)
|
.update(base)
|
||||||
.digest('base64');
|
.digest('base64');
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
const callback = `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth1-credential/callback?cid=${req.query.id}`;
|
const oauthRequestData = {
|
||||||
|
oauth_callback: `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth1-credential/callback?cid=${req.query.id}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.externalHooks.run('oauth1.authenticate', [oAuthOptions, oauthRequestData]);
|
||||||
|
|
||||||
|
const oauth = new clientOAuth1(oAuthOptions);
|
||||||
|
|
||||||
const options: RequestOptions = {
|
const options: RequestOptions = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: (_.get(oauthCredentials, 'requestTokenUrl') as string),
|
url: (_.get(oauthCredentials, 'requestTokenUrl') as string),
|
||||||
data: {
|
data: oauthRequestData,
|
||||||
oauth_callback: callback,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const data = oauth.toHeader(oauth.authorize(options as RequestOptions));
|
const data = oauth.toHeader(oauth.authorize(options as RequestOptions));
|
||||||
@@ -1099,7 +1176,7 @@ class App {
|
|||||||
qs: {
|
qs: {
|
||||||
oauth_token,
|
oauth_token,
|
||||||
oauth_verifier,
|
oauth_verifier,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let oauthToken;
|
let oauthToken;
|
||||||
@@ -1169,11 +1246,11 @@ class App {
|
|||||||
const csrfSecret = token.secretSync();
|
const csrfSecret = token.secretSync();
|
||||||
const state = {
|
const state = {
|
||||||
token: token.create(csrfSecret),
|
token: token.create(csrfSecret),
|
||||||
cid: req.query.id
|
cid: req.query.id,
|
||||||
};
|
};
|
||||||
const stateEncodedStr = Buffer.from(JSON.stringify(state)).toString('base64') as string;
|
const stateEncodedStr = Buffer.from(JSON.stringify(state)).toString('base64') as string;
|
||||||
|
|
||||||
const oAuthObj = new clientOAuth2({
|
const oAuthOptions: clientOAuth2.Options = {
|
||||||
clientId: _.get(oauthCredentials, 'clientId') as string,
|
clientId: _.get(oauthCredentials, 'clientId') as string,
|
||||||
clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string,
|
clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string,
|
||||||
accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string,
|
accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string,
|
||||||
@@ -1181,7 +1258,11 @@ class App {
|
|||||||
redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth2-credential/callback`,
|
redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth2-credential/callback`,
|
||||||
scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','),
|
scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','),
|
||||||
state: stateEncodedStr,
|
state: stateEncodedStr,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
await this.externalHooks.run('oauth2.authenticate', [oAuthOptions]);
|
||||||
|
|
||||||
|
const oAuthObj = new clientOAuth2(oAuthOptions);
|
||||||
|
|
||||||
// Encrypt the data
|
// Encrypt the data
|
||||||
const credentials = new Credentials(result.name, result.type, result.nodesAccess);
|
const credentials = new Credentials(result.name, result.type, result.nodesAccess);
|
||||||
@@ -1267,11 +1348,11 @@ class App {
|
|||||||
|
|
||||||
const oAuth2Parameters = {
|
const oAuth2Parameters = {
|
||||||
clientId: _.get(oauthCredentials, 'clientId') as string,
|
clientId: _.get(oauthCredentials, 'clientId') as string,
|
||||||
clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string,
|
clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string | undefined,
|
||||||
accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string,
|
accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string,
|
||||||
authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string,
|
authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string,
|
||||||
redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth2-credential/callback`,
|
redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth2-credential/callback`,
|
||||||
scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ',')
|
scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_.get(oauthCredentials, 'authentication', 'header') as string === 'body') {
|
if (_.get(oauthCredentials, 'authentication', 'header') as string === 'body') {
|
||||||
@@ -1283,13 +1364,14 @@ class App {
|
|||||||
};
|
};
|
||||||
delete oAuth2Parameters.clientSecret;
|
delete oAuth2Parameters.clientSecret;
|
||||||
}
|
}
|
||||||
const redirectUri = `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth2-credential/callback`;
|
|
||||||
|
await this.externalHooks.run('oauth2.callback', [oAuth2Parameters]);
|
||||||
|
|
||||||
const oAuthObj = new clientOAuth2(oAuth2Parameters);
|
const oAuthObj = new clientOAuth2(oAuth2Parameters);
|
||||||
|
|
||||||
const queryParameters = req.originalUrl.split('?').splice(1, 1).join('');
|
const queryParameters = req.originalUrl.split('?').splice(1, 1).join('');
|
||||||
|
|
||||||
const oauthToken = await oAuthObj.code.getToken(`${redirectUri}?${queryParameters}`, options);
|
const oauthToken = await oAuthObj.code.getToken(`${oAuth2Parameters.redirectUri}?${queryParameters}`, options);
|
||||||
|
|
||||||
if (oauthToken === undefined) {
|
if (oauthToken === undefined) {
|
||||||
const errorResponse = new ResponseHelper.ResponseError('Unable to get access tokens!', undefined, 404);
|
const errorResponse = new ResponseHelper.ResponseError('Unable to get access tokens!', undefined, 404);
|
||||||
@@ -1582,18 +1664,7 @@ class App {
|
|||||||
|
|
||||||
// Returns the settings which are needed in the UI
|
// Returns the settings which are needed in the UI
|
||||||
this.app.get(`/${this.restEndpoint}/settings`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<IN8nUISettings> => {
|
this.app.get(`/${this.restEndpoint}/settings`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<IN8nUISettings> => {
|
||||||
return {
|
return this.frontendSettings;
|
||||||
endpointWebhook: this.endpointWebhook,
|
|
||||||
endpointWebhookTest: this.endpointWebhookTest,
|
|
||||||
saveDataErrorExecution: this.saveDataErrorExecution,
|
|
||||||
saveDataSuccessExecution: this.saveDataSuccessExecution,
|
|
||||||
saveManualExecutions: this.saveManualExecutions,
|
|
||||||
executionTimeout: this.executionTimeout,
|
|
||||||
maxExecutionTimeout: this.maxExecutionTimeout,
|
|
||||||
timezone: this.timezone,
|
|
||||||
urlBaseWebhook: WebhookHelpers.getWebhookBaseUrl(),
|
|
||||||
versionCli: this.versions!.cli,
|
|
||||||
};
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
@@ -1829,7 +1900,7 @@ class App {
|
|||||||
// got used
|
// got used
|
||||||
res.setHeader('Last-Modified', startTime);
|
res.setHeader('Last-Modified', startTime);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1860,5 +1931,7 @@ export async function start(): Promise<void> {
|
|||||||
const versions = await GenericHelpers.getVersions();
|
const versions = await GenericHelpers.getVersions();
|
||||||
console.log(`n8n ready on ${ADDRESS}, port ${PORT}`);
|
console.log(`n8n ready on ${ADDRESS}, port ${PORT}`);
|
||||||
console.log(`Version: ${versions.cli}`);
|
console.log(`Version: ${versions.cli}`);
|
||||||
|
|
||||||
|
await app.externalHooks.run('n8n.ready', [app]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ import * as express from 'express';
|
|||||||
import {
|
import {
|
||||||
IResponseCallbackData,
|
IResponseCallbackData,
|
||||||
IWorkflowDb,
|
IWorkflowDb,
|
||||||
NodeTypes,
|
|
||||||
Push,
|
Push,
|
||||||
ResponseHelper,
|
ResponseHelper,
|
||||||
WebhookHelpers,
|
WebhookHelpers,
|
||||||
WorkflowHelpers,
|
|
||||||
} from './';
|
} from './';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -31,6 +29,7 @@ export class TestWebhooks {
|
|||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
timeout: NodeJS.Timeout,
|
timeout: NodeJS.Timeout,
|
||||||
workflowData: IWorkflowDb;
|
workflowData: IWorkflowDb;
|
||||||
|
workflow: Workflow;
|
||||||
};
|
};
|
||||||
} = {};
|
} = {};
|
||||||
private activeWebhooks: ActiveWebhooks | null = null;
|
private activeWebhooks: ActiveWebhooks | null = null;
|
||||||
@@ -64,10 +63,13 @@ export class TestWebhooks {
|
|||||||
|
|
||||||
const webhookKey = this.activeWebhooks!.getWebhookKey(webhookData.httpMethod, webhookData.path);
|
const webhookKey = this.activeWebhooks!.getWebhookKey(webhookData.httpMethod, webhookData.path);
|
||||||
|
|
||||||
const workflowData = this.testWebhookData[webhookKey].workflowData;
|
// TODO: Clean that duplication up one day and improve code generally
|
||||||
|
if (this.testWebhookData[webhookKey] === undefined) {
|
||||||
|
// The requested webhook is not registered
|
||||||
|
throw new ResponseHelper.ResponseError(`The requested webhook "${httpMethod} ${path}" is not registered.`, 404, 404);
|
||||||
|
}
|
||||||
|
|
||||||
const nodeTypes = NodeTypes();
|
const workflow = this.testWebhookData[webhookKey].workflow;
|
||||||
const workflow = new Workflow({ id: webhookData.workflowId, name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings});
|
|
||||||
|
|
||||||
// Get the node which has the webhook defined to know where to start from and to
|
// Get the node which has the webhook defined to know where to start from and to
|
||||||
// get additional data
|
// get additional data
|
||||||
@@ -154,19 +156,26 @@ export class TestWebhooks {
|
|||||||
}, 120000);
|
}, 120000);
|
||||||
|
|
||||||
let key: string;
|
let key: string;
|
||||||
|
const activatedKey: string[] = [];
|
||||||
for (const webhookData of webhooks) {
|
for (const webhookData of webhooks) {
|
||||||
key = this.activeWebhooks!.getWebhookKey(webhookData.httpMethod, webhookData.path);
|
key = this.activeWebhooks!.getWebhookKey(webhookData.httpMethod, webhookData.path);
|
||||||
|
|
||||||
await this.activeWebhooks!.add(workflow, webhookData, mode);
|
activatedKey.push(key);
|
||||||
|
|
||||||
this.testWebhookData[key] = {
|
this.testWebhookData[key] = {
|
||||||
sessionId,
|
sessionId,
|
||||||
timeout,
|
timeout,
|
||||||
|
workflow,
|
||||||
workflowData,
|
workflowData,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Save static data!
|
try {
|
||||||
this.testWebhookData[key].workflowData.staticData = workflow.staticData;
|
await this.activeWebhooks!.add(workflow, webhookData, mode);
|
||||||
|
} catch (error) {
|
||||||
|
activatedKey.forEach(deleteKey => delete this.testWebhookData[deleteKey] );
|
||||||
|
await this.activeWebhooks!.removeWorkflow(workflow);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -181,8 +190,6 @@ export class TestWebhooks {
|
|||||||
* @memberof TestWebhooks
|
* @memberof TestWebhooks
|
||||||
*/
|
*/
|
||||||
cancelTestWebhook(workflowId: string): boolean {
|
cancelTestWebhook(workflowId: string): boolean {
|
||||||
const nodeTypes = NodeTypes();
|
|
||||||
|
|
||||||
let foundWebhook = false;
|
let foundWebhook = false;
|
||||||
for (const webhookKey of Object.keys(this.testWebhookData)) {
|
for (const webhookKey of Object.keys(this.testWebhookData)) {
|
||||||
const webhookData = this.testWebhookData[webhookKey];
|
const webhookData = this.testWebhookData[webhookKey];
|
||||||
@@ -191,8 +198,6 @@ export class TestWebhooks {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foundWebhook = true;
|
|
||||||
|
|
||||||
clearTimeout(this.testWebhookData[webhookKey].timeout);
|
clearTimeout(this.testWebhookData[webhookKey].timeout);
|
||||||
|
|
||||||
// Inform editor-ui that webhook got received
|
// Inform editor-ui that webhook got received
|
||||||
@@ -205,12 +210,17 @@ export class TestWebhooks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowData = webhookData.workflowData;
|
const workflow = this.testWebhookData[webhookKey].workflow;
|
||||||
const workflow = new Workflow({ id: workflowData.id.toString(), name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings });
|
|
||||||
|
|
||||||
// Remove the webhook
|
// Remove the webhook
|
||||||
delete this.testWebhookData[webhookKey];
|
delete this.testWebhookData[webhookKey];
|
||||||
this.activeWebhooks!.removeWorkflow(workflow);
|
|
||||||
|
if (foundWebhook === false) {
|
||||||
|
// As it removes all webhooks of the workflow execute only once
|
||||||
|
this.activeWebhooks!.removeWorkflow(workflow);
|
||||||
|
}
|
||||||
|
|
||||||
|
foundWebhook = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundWebhook;
|
return foundWebhook;
|
||||||
@@ -225,14 +235,10 @@ export class TestWebhooks {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeTypes = NodeTypes();
|
|
||||||
|
|
||||||
let workflowData: IWorkflowDb;
|
|
||||||
let workflow: Workflow;
|
let workflow: Workflow;
|
||||||
const workflows: Workflow[] = [];
|
const workflows: Workflow[] = [];
|
||||||
for (const webhookKey of Object.keys(this.testWebhookData)) {
|
for (const webhookKey of Object.keys(this.testWebhookData)) {
|
||||||
workflowData = this.testWebhookData[webhookKey].workflowData;
|
workflow = this.testWebhookData[webhookKey].workflow;
|
||||||
workflow = new Workflow({ id: workflowData.id.toString(), name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings });
|
|
||||||
workflows.push(workflow);
|
workflows.push(workflow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ import { get } from 'lodash';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
ActiveExecutions,
|
ActiveExecutions,
|
||||||
|
ExternalHooks,
|
||||||
GenericHelpers,
|
GenericHelpers,
|
||||||
IExecutionDb,
|
IExecutionDb,
|
||||||
IResponseCallbackData,
|
IResponseCallbackData,
|
||||||
IWorkflowDb,
|
IWorkflowDb,
|
||||||
IWorkflowExecutionDataProcess,
|
IWorkflowExecutionDataProcess,
|
||||||
ResponseHelper,
|
ResponseHelper,
|
||||||
WorkflowHelpers,
|
|
||||||
WorkflowRunner,
|
|
||||||
WorkflowCredentials,
|
WorkflowCredentials,
|
||||||
WorkflowExecuteAdditionalData,
|
WorkflowExecuteAdditionalData,
|
||||||
|
WorkflowHelpers,
|
||||||
|
WorkflowRunner,
|
||||||
} from './';
|
} from './';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -114,8 +115,8 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the responseMode
|
// Get the responseMode
|
||||||
const responseMode = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseMode'], 'onReceived');
|
const responseMode = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseMode'], 'onReceived');
|
||||||
const responseCode = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseCode'], 200) as number;
|
const responseCode = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseCode'], 200) as number;
|
||||||
|
|
||||||
if (!['onReceived', 'lastNode'].includes(responseMode as string)) {
|
if (!['onReceived', 'lastNode'].includes(responseMode as string)) {
|
||||||
// If the mode is not known we error. Is probably best like that instead of using
|
// If the mode is not known we error. Is probably best like that instead of using
|
||||||
@@ -173,7 +174,7 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
|||||||
await WorkflowHelpers.saveStaticData(workflow);
|
await WorkflowHelpers.saveStaticData(workflow);
|
||||||
|
|
||||||
if (webhookData.webhookDescription['responseHeaders'] !== undefined) {
|
if (webhookData.webhookDescription['responseHeaders'] !== undefined) {
|
||||||
const responseHeaders = workflow.getComplexParameterValue(workflowStartNode, webhookData.webhookDescription['responseHeaders'], undefined) as {
|
const responseHeaders = workflow.expression.getComplexParameterValue(workflowStartNode, webhookData.webhookDescription['responseHeaders'], undefined) as {
|
||||||
entries?: Array<{
|
entries?: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
@@ -251,7 +252,7 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
|||||||
data: {
|
data: {
|
||||||
main: webhookResultData.workflowData,
|
main: webhookResultData.workflowData,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const runExecutionData: IRunExecutionData = {
|
const runExecutionData: IRunExecutionData = {
|
||||||
@@ -325,7 +326,7 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseData = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseData'], 'firstEntryJson');
|
const responseData = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseData'], 'firstEntryJson');
|
||||||
|
|
||||||
if (didSendResponse === false) {
|
if (didSendResponse === false) {
|
||||||
let data: IDataObject | IDataObject[];
|
let data: IDataObject | IDataObject[];
|
||||||
@@ -340,13 +341,13 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
|||||||
|
|
||||||
data = returnData.data!.main[0]![0].json;
|
data = returnData.data!.main[0]![0].json;
|
||||||
|
|
||||||
const responsePropertyName = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responsePropertyName'], undefined);
|
const responsePropertyName = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responsePropertyName'], undefined);
|
||||||
|
|
||||||
if (responsePropertyName !== undefined) {
|
if (responsePropertyName !== undefined) {
|
||||||
data = get(data, responsePropertyName as string) as IDataObject;
|
data = get(data, responsePropertyName as string) as IDataObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseContentType = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseContentType'], undefined);
|
const responseContentType = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseContentType'], undefined);
|
||||||
|
|
||||||
if (responseContentType !== undefined) {
|
if (responseContentType !== undefined) {
|
||||||
// Send the webhook response manually to be able to set the content-type
|
// Send the webhook response manually to be able to set the content-type
|
||||||
@@ -379,7 +380,7 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
|||||||
didSendResponse = true;
|
didSendResponse = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseBinaryPropertyName = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseBinaryPropertyName'], 'data');
|
const responseBinaryPropertyName = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseBinaryPropertyName'], 'data');
|
||||||
|
|
||||||
if (responseBinaryPropertyName === undefined && didSendResponse === false) {
|
if (responseBinaryPropertyName === undefined && didSendResponse === false) {
|
||||||
responseCallback(new Error('No "responseBinaryPropertyName" is set.'), {});
|
responseCallback(new Error('No "responseBinaryPropertyName" is set.'), {});
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import {
|
|||||||
IExecuteData,
|
IExecuteData,
|
||||||
IExecuteWorkflowInfo,
|
IExecuteWorkflowInfo,
|
||||||
INode,
|
INode,
|
||||||
INodeParameters,
|
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
|
INodeParameters,
|
||||||
IRun,
|
IRun,
|
||||||
IRunExecutionData,
|
IRunExecutionData,
|
||||||
ITaskData,
|
ITaskData,
|
||||||
@@ -74,7 +74,7 @@ function executeErrorWorkflow(workflowData: IWorkflowBase, fullRunData: IRun, mo
|
|||||||
workflow: {
|
workflow: {
|
||||||
id: workflowData.id !== undefined ? workflowData.id.toString() as string : undefined,
|
id: workflowData.id !== undefined ? workflowData.id.toString() as string : undefined,
|
||||||
name: workflowData.name,
|
name: workflowData.name,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
// Run the error workflow
|
// Run the error workflow
|
||||||
WorkflowHelpers.executeErrorWorkflow(workflowData.settings.errorWorkflow as string, workflowErrorData);
|
WorkflowHelpers.executeErrorWorkflow(workflowData.settings.errorWorkflow as string, workflowErrorData);
|
||||||
@@ -191,13 +191,13 @@ function hookFunctionsPush(): IWorkflowExecuteHooks {
|
|||||||
workflowId: this.workflowData.id as string,
|
workflowId: this.workflowData.id as string,
|
||||||
workflowName: this.workflowData.name,
|
workflowName: this.workflowData.name,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
workflowExecuteAfter: [
|
workflowExecuteAfter: [
|
||||||
async function (this: WorkflowHooks, fullRunData: IRun, newStaticData: IDataObject): Promise<void> {
|
async function (this: WorkflowHooks, fullRunData: IRun, newStaticData: IDataObject): Promise<void> {
|
||||||
pushExecutionFinished(this.mode, fullRunData, this.executionId, undefined, this.retryOf);
|
pushExecutionFinished(this.mode, fullRunData, this.executionId, undefined, this.retryOf);
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +298,7 @@ function hookFunctionsSave(parentProcessMode?: string): IWorkflowExecuteHooks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,8 +374,8 @@ export async function executeWorkflow(workflowInfo: IExecuteWorkflowInfo, additi
|
|||||||
// Always start with empty data if no inputData got supplied
|
// Always start with empty data if no inputData got supplied
|
||||||
inputData = inputData || [
|
inputData = inputData || [
|
||||||
{
|
{
|
||||||
json: {}
|
json: {},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Initialize the incoming data
|
// Initialize the incoming data
|
||||||
@@ -386,7 +386,7 @@ export async function executeWorkflow(workflowInfo: IExecuteWorkflowInfo, additi
|
|||||||
data: {
|
data: {
|
||||||
main: [inputData],
|
main: [inputData],
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const runExecutionData: IRunExecutionData = {
|
const runExecutionData: IRunExecutionData = {
|
||||||
@@ -406,6 +406,8 @@ export async function executeWorkflow(workflowInfo: IExecuteWorkflowInfo, additi
|
|||||||
const workflowExecute = new WorkflowExecute(additionalDataIntegrated, mode, runExecutionData);
|
const workflowExecute = new WorkflowExecute(additionalDataIntegrated, mode, runExecutionData);
|
||||||
const data = await workflowExecute.processRunExecutionData(workflow);
|
const data = await workflowExecute.processRunExecutionData(workflow);
|
||||||
|
|
||||||
|
await externalHooks.run('workflow.postExecute', [data, workflowData]);
|
||||||
|
|
||||||
if (data.finished === true) {
|
if (data.finished === true) {
|
||||||
// Workflow did finish successfully
|
// Workflow did finish successfully
|
||||||
const returnData = WorkflowHelpers.getDataLastExecutedNodeData(data);
|
const returnData = WorkflowHelpers.getDataLastExecutedNodeData(data);
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
Db,
|
Db,
|
||||||
ICredentialsTypeData,
|
ICredentialsTypeData,
|
||||||
ITransferNodeTypes,
|
ITransferNodeTypes,
|
||||||
IWorkflowExecutionDataProcess,
|
|
||||||
IWorkflowErrorData,
|
IWorkflowErrorData,
|
||||||
|
IWorkflowExecutionDataProcess,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
WorkflowCredentials,
|
WorkflowCredentials,
|
||||||
WorkflowRunner,
|
WorkflowRunner,
|
||||||
@@ -120,12 +120,12 @@ export async function executeErrorWorkflow(workflowId: string, workflowErrorData
|
|||||||
main: [
|
main: [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
json: workflowErrorData
|
json: workflowErrorData,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const runExecutionData: IRunExecutionData = {
|
const runExecutionData: IRunExecutionData = {
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import {
|
|||||||
IExecutionError,
|
IExecutionError,
|
||||||
IRun,
|
IRun,
|
||||||
Workflow,
|
Workflow,
|
||||||
WorkflowHooks,
|
|
||||||
WorkflowExecuteMode,
|
WorkflowExecuteMode,
|
||||||
|
WorkflowHooks,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
@@ -104,11 +104,25 @@ export class WorkflowRunner {
|
|||||||
await externalHooks.run('workflow.execute', [data.workflowData, data.executionMode]);
|
await externalHooks.run('workflow.execute', [data.workflowData, data.executionMode]);
|
||||||
|
|
||||||
const executionsProcess = config.get('executions.process') as string;
|
const executionsProcess = config.get('executions.process') as string;
|
||||||
|
|
||||||
|
let executionId: string;
|
||||||
if (executionsProcess === 'main') {
|
if (executionsProcess === 'main') {
|
||||||
return this.runMainProcess(data, loadStaticData);
|
executionId = await this.runMainProcess(data, loadStaticData);
|
||||||
|
} else {
|
||||||
|
executionId = await this.runSubprocess(data, loadStaticData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.runSubprocess(data, loadStaticData);
|
if (externalHooks.exists('workflow.postExecute')) {
|
||||||
|
this.activeExecutions.getPostExecutePromise(executionId)
|
||||||
|
.then(async (executionData) => {
|
||||||
|
await externalHooks.run('workflow.postExecute', [executionData, data.workflowData]);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('There was a problem running hook "workflow.postExecute"', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return executionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -212,6 +226,7 @@ export class WorkflowRunner {
|
|||||||
|
|
||||||
let nodeTypeData: ITransferNodeTypes;
|
let nodeTypeData: ITransferNodeTypes;
|
||||||
let credentialTypeData: ICredentialsTypeData;
|
let credentialTypeData: ICredentialsTypeData;
|
||||||
|
let credentialsOverwrites = this.credentialsOverwrites;
|
||||||
|
|
||||||
if (loadAllNodeTypes === true) {
|
if (loadAllNodeTypes === true) {
|
||||||
// Supply all nodeTypes and credentialTypes
|
// Supply all nodeTypes and credentialTypes
|
||||||
@@ -219,15 +234,22 @@ export class WorkflowRunner {
|
|||||||
const credentialTypes = CredentialTypes();
|
const credentialTypes = CredentialTypes();
|
||||||
credentialTypeData = credentialTypes.credentialTypes;
|
credentialTypeData = credentialTypes.credentialTypes;
|
||||||
} else {
|
} else {
|
||||||
// Supply only nodeTypes and credentialTypes which the workflow needs
|
// Supply only nodeTypes, credentialTypes and overwrites that the workflow needs
|
||||||
nodeTypeData = WorkflowHelpers.getNodeTypeData(data.workflowData.nodes);
|
nodeTypeData = WorkflowHelpers.getNodeTypeData(data.workflowData.nodes);
|
||||||
credentialTypeData = WorkflowHelpers.getCredentialsData(data.credentials);
|
credentialTypeData = WorkflowHelpers.getCredentialsData(data.credentials);
|
||||||
|
|
||||||
|
credentialsOverwrites = {};
|
||||||
|
for (const credentialName of Object.keys(credentialTypeData)) {
|
||||||
|
if (this.credentialsOverwrites[credentialName] !== undefined) {
|
||||||
|
credentialsOverwrites[credentialName] = this.credentialsOverwrites[credentialName];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
(data as unknown as IWorkflowExecutionDataProcessWithExecution).executionId = executionId;
|
(data as unknown as IWorkflowExecutionDataProcessWithExecution).executionId = executionId;
|
||||||
(data as unknown as IWorkflowExecutionDataProcessWithExecution).nodeTypeData = nodeTypeData;
|
(data as unknown as IWorkflowExecutionDataProcessWithExecution).nodeTypeData = nodeTypeData;
|
||||||
(data as unknown as IWorkflowExecutionDataProcessWithExecution).credentialsOverwrite = this.credentialsOverwrites;
|
(data as unknown as IWorkflowExecutionDataProcessWithExecution).credentialsOverwrite = credentialsOverwrites;
|
||||||
(data as unknown as IWorkflowExecutionDataProcessWithExecution).credentialsTypeData = credentialTypeData; // TODO: Still needs correct value
|
(data as unknown as IWorkflowExecutionDataProcessWithExecution).credentialsTypeData = credentialTypeData; // TODO: Still needs correct value
|
||||||
|
|
||||||
const workflowHooks = WorkflowExecuteAdditionalData.getWorkflowHooksMain(data, executionId);
|
const workflowHooks = WorkflowExecuteAdditionalData.getWorkflowHooksMain(data, executionId);
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export class WorkflowRunnerProcess {
|
|||||||
|
|
||||||
// Load the credentials overwrites if any exist
|
// Load the credentials overwrites if any exist
|
||||||
const credentialsOverwrites = CredentialsOverwrites();
|
const credentialsOverwrites = CredentialsOverwrites();
|
||||||
await credentialsOverwrites.init();
|
await credentialsOverwrites.init(inputData.credentialsOverwrite);
|
||||||
|
|
||||||
this.workflow = new Workflow({ id: this.data.workflowData.id as string | undefined, name: this.data.workflowData.name, nodes: this.data.workflowData!.nodes, connections: this.data.workflowData!.connections, active: this.data.workflowData!.active, nodeTypes, staticData: this.data.workflowData!.staticData, settings: this.data.workflowData!.settings});
|
this.workflow = new Workflow({ id: this.data.workflowData.id as string | undefined, name: this.data.workflowData.name, nodes: this.data.workflowData!.nodes, connections: this.data.workflowData!.connections, active: this.data.workflowData!.active, nodeTypes, staticData: this.data.workflowData!.staticData, settings: this.data.workflowData!.settings});
|
||||||
const additionalData = await WorkflowExecuteAdditionalData.getBase(this.data.credentials);
|
const additionalData = await WorkflowExecuteAdditionalData.getBase(this.data.credentials);
|
||||||
@@ -135,13 +135,13 @@ export class WorkflowRunnerProcess {
|
|||||||
workflowExecuteBefore: [
|
workflowExecuteBefore: [
|
||||||
async (): Promise<void> => {
|
async (): Promise<void> => {
|
||||||
this.sendHookToParentProcess('workflowExecuteBefore', []);
|
this.sendHookToParentProcess('workflowExecuteBefore', []);
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
workflowExecuteAfter: [
|
workflowExecuteAfter: [
|
||||||
async (fullRunData: IRun, newStaticData?: IDataObject): Promise<void> => {
|
async (fullRunData: IRun, newStaticData?: IDataObject): Promise<void> => {
|
||||||
this.sendHookToParentProcess('workflowExecuteAfter', [fullRunData, newStaticData]);
|
this.sendHookToParentProcess('workflowExecuteAfter', [fullRunData, newStaticData]);
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
return new WorkflowHooks(hookFunctions, this.data!.executionMode, this.data!.executionId, this.data!.workflowData, { sessionId: this.data!.sessionId, retryOf: this.data!.retryOf as string });
|
return new WorkflowHooks(hookFunctions, this.data!.executionMode, this.data!.executionId, this.data!.workflowData, { sessionId: this.data!.sessionId, retryOf: this.data!.retryOf as string });
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export class CredentialsEntity implements ICredentialsDb {
|
|||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
length: 128
|
length: 128,
|
||||||
})
|
})
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export class CredentialsEntity implements ICredentialsDb {
|
|||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column({
|
@Column({
|
||||||
length: 32
|
length: 32,
|
||||||
})
|
})
|
||||||
type: string;
|
type: string;
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export class WorkflowEntity implements IWorkflowDb {
|
|||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
length: 128
|
length: 128,
|
||||||
})
|
})
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export class CredentialsEntity implements ICredentialsDb {
|
|||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
length: 128
|
length: 128,
|
||||||
})
|
})
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export class CredentialsEntity implements ICredentialsDb {
|
|||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column({
|
@Column({
|
||||||
length: 32
|
length: 32,
|
||||||
})
|
})
|
||||||
type: string;
|
type: string;
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export class WorkflowEntity implements IWorkflowDb {
|
|||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
length: 128
|
length: 128,
|
||||||
})
|
})
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export class CredentialsEntity implements ICredentialsDb {
|
|||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
length: 128
|
length: 128,
|
||||||
})
|
})
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export class CredentialsEntity implements ICredentialsDb {
|
|||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column({
|
@Column({
|
||||||
length: 32
|
length: 32,
|
||||||
})
|
})
|
||||||
type: string;
|
type: string;
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export class WorkflowEntity implements IWorkflowDb {
|
|||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
length: 128
|
length: 128,
|
||||||
})
|
})
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<html>
|
<html>
|
||||||
<script>
|
<script>
|
||||||
(function messageParent() {
|
(function messageParent() {
|
||||||
window.opener.postMessage('success', '*');
|
window.opener.postMessage('success', '*');
|
||||||
}());
|
}());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
Got connected. The window can be closed now.
|
Got connected. The window can be closed now.
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -46,6 +46,11 @@
|
|||||||
"forin": true,
|
"forin": true,
|
||||||
"jsdoc-format": true,
|
"jsdoc-format": true,
|
||||||
"label-position": true,
|
"label-position": true,
|
||||||
|
"indent": [
|
||||||
|
true,
|
||||||
|
"tabs",
|
||||||
|
2
|
||||||
|
],
|
||||||
"member-access": [
|
"member-access": [
|
||||||
true,
|
true,
|
||||||
"no-public"
|
"no-public"
|
||||||
@@ -60,6 +65,13 @@
|
|||||||
"no-default-export": true,
|
"no-default-export": true,
|
||||||
"no-duplicate-variable": true,
|
"no-duplicate-variable": true,
|
||||||
"no-inferrable-types": true,
|
"no-inferrable-types": true,
|
||||||
|
"ordered-imports": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"import-sources-order": "any",
|
||||||
|
"named-imports-order": "case-insensitive"
|
||||||
|
}
|
||||||
|
],
|
||||||
"no-namespace": [
|
"no-namespace": [
|
||||||
true,
|
true,
|
||||||
"allow-declarations"
|
"allow-declarations"
|
||||||
@@ -82,6 +94,18 @@
|
|||||||
"ignore-bound-class-methods"
|
"ignore-bound-class-methods"
|
||||||
],
|
],
|
||||||
"switch-default": true,
|
"switch-default": true,
|
||||||
|
"trailing-comma": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"multiline": {
|
||||||
|
"objects": "always",
|
||||||
|
"arrays": "always",
|
||||||
|
"functions": "never",
|
||||||
|
"typeLiterals": "ignore"
|
||||||
|
},
|
||||||
|
"esSpecCompliant": true
|
||||||
|
}
|
||||||
|
],
|
||||||
"triple-equals": [
|
"triple-equals": [
|
||||||
true,
|
true,
|
||||||
"allow-null-check"
|
"allow-null-check"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Condition notice.
|
|||||||
|
|
||||||
Software: n8n
|
Software: n8n
|
||||||
|
|
||||||
License: Apache 2.0
|
License: Apache 2.0 with Commons Clause
|
||||||
|
|
||||||
Licensor: n8n GmbH
|
Licensor: n8n GmbH
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n-core",
|
"name": "n8n-core",
|
||||||
"version": "0.44.0",
|
"version": "0.48.1",
|
||||||
"description": "Core functionality of n8n",
|
"description": "Core functionality of n8n",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"dev": "npm run watch",
|
"dev": "npm run watch",
|
||||||
"tslint": "tslint -p tsconfig.json -c tslint.json",
|
"tslint": "tslint -p tsconfig.json -c tslint.json",
|
||||||
|
"tslintfix": "tslint --fix -p tsconfig.json -c tslint.json",
|
||||||
"watch": "tsc --watch",
|
"watch": "tsc --watch",
|
||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
@@ -26,27 +27,28 @@
|
|||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cron": "^1.7.1",
|
"@types/cron": "^1.7.1",
|
||||||
"@types/crypto-js": "^3.1.43",
|
"@types/crypto-js": "^4.0.1",
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.17.6",
|
||||||
"@types/jest": "^25.2.1",
|
"@types/jest": "^26.0.13",
|
||||||
"@types/lodash.get": "^4.4.6",
|
"@types/lodash.get": "^4.4.6",
|
||||||
"@types/mime-types": "^2.1.0",
|
"@types/mime-types": "^2.1.0",
|
||||||
"@types/node": "^14.0.27",
|
"@types/node": "14.0.27",
|
||||||
"@types/request-promise-native": "~1.0.15",
|
"@types/request-promise-native": "~1.0.15",
|
||||||
"jest": "^24.9.0",
|
"jest": "^26.4.2",
|
||||||
"source-map-support": "^0.5.9",
|
"source-map-support": "^0.5.9",
|
||||||
"ts-jest": "^25.4.0",
|
"ts-jest": "^26.3.0",
|
||||||
"tslint": "^6.1.2",
|
"tslint": "^6.1.2",
|
||||||
"typescript": "~3.7.4"
|
"typescript": "~3.9.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"client-oauth2": "^4.2.5",
|
"client-oauth2": "^4.2.5",
|
||||||
"cron": "^1.7.2",
|
"cron": "^1.7.2",
|
||||||
"crypto-js": "3.1.9-1",
|
"crypto-js": "4.0.0",
|
||||||
"file-type": "^14.6.2",
|
"file-type": "^14.6.2",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"mime-types": "^2.1.27",
|
"mime-types": "^2.1.27",
|
||||||
"n8n-workflow": "~0.39.0",
|
"n8n-workflow": "~0.42.0",
|
||||||
|
"oauth-1.0a": "^2.2.6",
|
||||||
"p-cancelable": "^2.0.0",
|
"p-cancelable": "^2.0.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"request-promise-native": "^1.0.7"
|
"request-promise-native": "^1.0.7"
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export class ActiveWebhooks {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, this.testWebhooks);
|
const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, this.testWebhooks);
|
||||||
if (webhookExists === false) {
|
if (webhookExists !== true) {
|
||||||
// If webhook does not exist yet create it
|
// If webhook does not exist yet create it
|
||||||
await workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, this.testWebhooks);
|
await workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, this.testWebhooks);
|
||||||
|
|
||||||
@@ -60,7 +60,6 @@ export class ActiveWebhooks {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If there was a problem unregister the webhook again
|
// If there was a problem unregister the webhook again
|
||||||
delete this.webhookUrls[webhookKey];
|
delete this.webhookUrls[webhookKey];
|
||||||
delete this.workflowWebhooks[webhookData.workflowId];
|
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@@ -159,7 +158,7 @@ export class ActiveWebhooks {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes all the webhooks of the given workflow
|
* Removes all the webhooks of the given workflows
|
||||||
*/
|
*/
|
||||||
async removeAll(workflows: Workflow[]): Promise<void> {
|
async removeAll(workflows: Workflow[]): Promise<void> {
|
||||||
const removePromises = [];
|
const removePromises = [];
|
||||||
|
|||||||
@@ -67,8 +67,6 @@ export class ActiveWorkflows {
|
|||||||
* @memberof ActiveWorkflows
|
* @memberof ActiveWorkflows
|
||||||
*/
|
*/
|
||||||
async add(id: string, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, getTriggerFunctions: IGetExecuteTriggerFunctions, getPollFunctions: IGetExecutePollFunctions): Promise<void> {
|
async add(id: string, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, getTriggerFunctions: IGetExecuteTriggerFunctions, getPollFunctions: IGetExecutePollFunctions): Promise<void> {
|
||||||
console.log('ADD ID (active): ' + id);
|
|
||||||
|
|
||||||
this.workflowData[id] = {};
|
this.workflowData[id] = {};
|
||||||
const triggerNodes = workflow.getTriggerNodes();
|
const triggerNodes = workflow.getTriggerNodes();
|
||||||
|
|
||||||
@@ -204,8 +202,6 @@ export class ActiveWorkflows {
|
|||||||
* @memberof ActiveWorkflows
|
* @memberof ActiveWorkflows
|
||||||
*/
|
*/
|
||||||
async remove(id: string): Promise<void> {
|
async remove(id: string): Promise<void> {
|
||||||
console.log('REMOVE ID (active): ' + id);
|
|
||||||
|
|
||||||
if (!this.isActive(id)) {
|
if (!this.isActive(id)) {
|
||||||
// Workflow is currently not registered
|
// Workflow is currently not registered
|
||||||
throw new Error(`The workflow with the id "${id}" is currently not active and can so not be removed`);
|
throw new Error(`The workflow with the id "${id}" is currently not active and can so not be removed`);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
ICredentialsEncrypted,
|
ICredentialsEncrypted,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { enc, AES } from 'crypto-js';
|
import { AES, enc } from 'crypto-js';
|
||||||
|
|
||||||
|
|
||||||
export class Credentials extends ICredentials {
|
export class Credentials extends ICredentials {
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import {
|
|||||||
ILoadOptionsFunctions as ILoadOptionsFunctionsBase,
|
ILoadOptionsFunctions as ILoadOptionsFunctionsBase,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
INodeType,
|
INodeType,
|
||||||
|
IOAuth2Options,
|
||||||
IPollFunctions as IPollFunctionsBase,
|
IPollFunctions as IPollFunctionsBase,
|
||||||
IPollResponse,
|
IPollResponse,
|
||||||
ITriggerFunctions as ITriggerFunctionsBase,
|
ITriggerFunctions as ITriggerFunctionsBase,
|
||||||
ITriggerResponse,
|
ITriggerResponse,
|
||||||
IWebhookFunctions as IWebhookFunctionsBase,
|
IWebhookFunctions as IWebhookFunctionsBase,
|
||||||
IWorkflowSettings as IWorkflowSettingsWorkflow,
|
IWorkflowSettings as IWorkflowSettingsWorkflow,
|
||||||
IOAuth2Options,
|
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class LoadNodeParameterOptions {
|
|||||||
position: [
|
position: [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (credentials) {
|
if (credentials) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
|
BINARY_ENCODING,
|
||||||
IHookFunctions,
|
IHookFunctions,
|
||||||
ILoadOptionsFunctions,
|
ILoadOptionsFunctions,
|
||||||
IResponseError,
|
IResponseError,
|
||||||
IWorkflowSettings,
|
IWorkflowSettings,
|
||||||
BINARY_ENCODING,
|
|
||||||
} from './';
|
} from './';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
INodeType,
|
INodeType,
|
||||||
|
IOAuth2Options,
|
||||||
IPollFunctions,
|
IPollFunctions,
|
||||||
IRunExecutionData,
|
IRunExecutionData,
|
||||||
ITaskDataConnections,
|
ITaskDataConnections,
|
||||||
@@ -34,7 +35,6 @@ import {
|
|||||||
Workflow,
|
Workflow,
|
||||||
WorkflowDataProxy,
|
WorkflowDataProxy,
|
||||||
WorkflowExecuteMode,
|
WorkflowExecuteMode,
|
||||||
IOAuth2Options,
|
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import * as clientOAuth1 from 'oauth-1.0a';
|
import * as clientOAuth1 from 'oauth-1.0a';
|
||||||
@@ -43,7 +43,7 @@ import * as clientOAuth2 from 'client-oauth2';
|
|||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { OptionsWithUrl, OptionsWithUri } from 'request';
|
import { OptionsWithUri, OptionsWithUrl } from 'request';
|
||||||
import * as requestPromise from 'request-promise-native';
|
import * as requestPromise from 'request-promise-native';
|
||||||
import { createHmac } from 'crypto';
|
import { createHmac } from 'crypto';
|
||||||
import { fromBuffer } from 'file-type';
|
import { fromBuffer } from 'file-type';
|
||||||
@@ -91,7 +91,7 @@ export async function prepareBinaryData(binaryData: Buffer, filePath?: string, m
|
|||||||
// TODO: Should program it in a way that it does not have to converted to base64
|
// TODO: Should program it in a way that it does not have to converted to base64
|
||||||
// It should only convert to and from base64 when saved in database because
|
// It should only convert to and from base64 when saved in database because
|
||||||
// of for example an error or when there is a wait node.
|
// of for example an error or when there is a wait node.
|
||||||
data: binaryData.toString(BINARY_ENCODING)
|
data: binaryData.toString(BINARY_ENCODING),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
@@ -152,11 +152,16 @@ export function requestOAuth2(this: IAllExecuteFunctions, credentialsType: strin
|
|||||||
// on the token-type used.
|
// on the token-type used.
|
||||||
const newRequestOptions = token.sign(requestOptions as clientOAuth2.RequestObject);
|
const newRequestOptions = token.sign(requestOptions as clientOAuth2.RequestObject);
|
||||||
|
|
||||||
|
// If keep bearer is false remove the it from the authorization header
|
||||||
|
if (oAuth2Options?.keepBearer === false) {
|
||||||
|
//@ts-ignore
|
||||||
|
newRequestOptions?.headers?.Authorization = newRequestOptions?.headers?.Authorization.split(' ')[1];
|
||||||
|
}
|
||||||
|
|
||||||
return this.helpers.request!(newRequestOptions)
|
return this.helpers.request!(newRequestOptions)
|
||||||
.catch(async (error: IResponseError) => {
|
.catch(async (error: IResponseError) => {
|
||||||
// TODO: Check if also other codes are possible
|
// TODO: Check if also other codes are possible
|
||||||
if (error.statusCode === 401) {
|
if (error.statusCode === 401) {
|
||||||
// TODO: Whole refresh process is not tested yet
|
|
||||||
// Token is probably not valid anymore. So try refresh it.
|
// Token is probably not valid anymore. So try refresh it.
|
||||||
|
|
||||||
const tokenRefreshOptions: IDataObject = {};
|
const tokenRefreshOptions: IDataObject = {};
|
||||||
@@ -388,7 +393,7 @@ export function getNodeParameter(workflow: Workflow, runExecutionData: IRunExecu
|
|||||||
|
|
||||||
let returnData;
|
let returnData;
|
||||||
try {
|
try {
|
||||||
returnData = workflow.getParameterValue(value, runExecutionData, runIndex, itemIndex, node.name, connectionInputData);
|
returnData = workflow.expression.getParameterValue(value, runExecutionData, runIndex, itemIndex, node.name, connectionInputData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
e.message += ` [Error in parameter: "${parameterName}"]`;
|
e.message += ` [Error in parameter: "${parameterName}"]`;
|
||||||
throw e;
|
throw e;
|
||||||
@@ -434,12 +439,12 @@ export function getNodeWebhookUrl(name: string, workflow: Workflow, node: INode,
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = workflow.getSimpleParameterValue(node, webhookDescription['path']);
|
const path = workflow.expression.getSimpleParameterValue(node, webhookDescription['path']);
|
||||||
if (path === undefined) {
|
if (path === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFullPath: boolean = workflow.getSimpleParameterValue(node, webhookDescription['isFullPath'], false) as boolean;
|
const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookDescription['isFullPath'], false) as boolean;
|
||||||
return NodeHelpers.getNodeWebhookUrl(baseUrl, workflow.id!, node, path.toString(), isFullPath);
|
return NodeHelpers.getNodeWebhookUrl(baseUrl, workflow.id!, node, path.toString(), isFullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -654,7 +659,7 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx
|
|||||||
return continueOnFail(node);
|
return continueOnFail(node);
|
||||||
},
|
},
|
||||||
evaluateExpression: (expression: string, itemIndex: number) => {
|
evaluateExpression: (expression: string, itemIndex: number) => {
|
||||||
return workflow.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, itemIndex, node.name, connectionInputData);
|
return workflow.expression.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, itemIndex, node.name, connectionInputData);
|
||||||
},
|
},
|
||||||
async executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any> { // tslint:disable-line:no-any
|
async executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any> { // tslint:disable-line:no-any
|
||||||
return additionalData.executeWorkflow(workflowInfo, additionalData, inputData);
|
return additionalData.executeWorkflow(workflowInfo, additionalData, inputData);
|
||||||
@@ -752,7 +757,7 @@ export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData:
|
|||||||
},
|
},
|
||||||
evaluateExpression: (expression: string, evaluateItemIndex: number | undefined) => {
|
evaluateExpression: (expression: string, evaluateItemIndex: number | undefined) => {
|
||||||
evaluateItemIndex = evaluateItemIndex === undefined ? itemIndex : evaluateItemIndex;
|
evaluateItemIndex = evaluateItemIndex === undefined ? itemIndex : evaluateItemIndex;
|
||||||
return workflow.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, evaluateItemIndex, node.name, connectionInputData);
|
return workflow.expression.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, evaluateItemIndex, node.name, connectionInputData);
|
||||||
},
|
},
|
||||||
getContext(type: string): IContextObject {
|
getContext(type: string): IContextObject {
|
||||||
return NodeHelpers.getContext(runExecutionData, type, node);
|
return NodeHelpers.getContext(runExecutionData, type, node);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
ENCRYPTION_KEY_ENV_OVERWRITE,
|
ENCRYPTION_KEY_ENV_OVERWRITE,
|
||||||
EXTENSIONS_SUBDIRECTORY,
|
EXTENSIONS_SUBDIRECTORY,
|
||||||
|
IUserSettings,
|
||||||
USER_FOLDER_ENV_OVERWRITE,
|
USER_FOLDER_ENV_OVERWRITE,
|
||||||
USER_SETTINGS_FILE_NAME,
|
USER_SETTINGS_FILE_NAME,
|
||||||
USER_SETTINGS_SUBFOLDER,
|
USER_SETTINGS_SUBFOLDER,
|
||||||
IUserSettings,
|
|
||||||
} from '.';
|
} from '.';
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import {
|
|||||||
ITaskDataConnections,
|
ITaskDataConnections,
|
||||||
IWaitingForExecution,
|
IWaitingForExecution,
|
||||||
IWorkflowExecuteAdditionalData,
|
IWorkflowExecuteAdditionalData,
|
||||||
WorkflowExecuteMode,
|
|
||||||
Workflow,
|
Workflow,
|
||||||
|
WorkflowExecuteMode,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import {
|
import {
|
||||||
NodeExecuteFunctions,
|
NodeExecuteFunctions,
|
||||||
@@ -84,7 +84,7 @@ export class WorkflowExecute {
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
this.runExecutionData = {
|
this.runExecutionData = {
|
||||||
@@ -137,8 +137,8 @@ export class WorkflowExecute {
|
|||||||
// If it has no incoming data add the default empty data
|
// If it has no incoming data add the default empty data
|
||||||
incomingData.push([
|
incomingData.push([
|
||||||
{
|
{
|
||||||
json: {}
|
json: {},
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
// Get the data of the incoming connections
|
// Get the data of the incoming connections
|
||||||
@@ -156,7 +156,7 @@ export class WorkflowExecute {
|
|||||||
node: workflow.getNode(startNode) as INode,
|
node: workflow.getNode(startNode) as INode,
|
||||||
data: {
|
data: {
|
||||||
main: incomingData,
|
main: incomingData,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
nodeExecutionStack.push(executeData);
|
nodeExecutionStack.push(executeData);
|
||||||
@@ -252,7 +252,7 @@ export class WorkflowExecute {
|
|||||||
if (this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] === undefined) {
|
if (this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] === undefined) {
|
||||||
// Node does not have data for runIndex yet so create also empty one and init it
|
// Node does not have data for runIndex yet so create also empty one and init it
|
||||||
this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] = {
|
this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] = {
|
||||||
main: []
|
main: [],
|
||||||
};
|
};
|
||||||
for (let i = 0; i < workflow.connectionsByDestinationNode[connectionData.node]['main'].length; i++) {
|
for (let i = 0; i < workflow.connectionsByDestinationNode[connectionData.node]['main'].length; i++) {
|
||||||
this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main.push(null);
|
this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main.push(null);
|
||||||
@@ -282,7 +282,7 @@ export class WorkflowExecute {
|
|||||||
// So add it to the execution stack
|
// So add it to the execution stack
|
||||||
this.runExecutionData.executionData!.nodeExecutionStack.push({
|
this.runExecutionData.executionData!.nodeExecutionStack.push({
|
||||||
node: workflow.nodes[connectionData.node],
|
node: workflow.nodes[connectionData.node],
|
||||||
data: this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex]
|
data: this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove the data from waiting
|
// Remove the data from waiting
|
||||||
@@ -426,15 +426,15 @@ export class WorkflowExecute {
|
|||||||
this.runExecutionData.executionData!.waitingExecution[connectionData.node] = {};
|
this.runExecutionData.executionData!.waitingExecution[connectionData.node] = {};
|
||||||
}
|
}
|
||||||
this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] = {
|
this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] = {
|
||||||
main: connectionDataArray
|
main: connectionDataArray,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// All data is there so add it directly to stack
|
// All data is there so add it directly to stack
|
||||||
this.runExecutionData.executionData!.nodeExecutionStack.push({
|
this.runExecutionData.executionData!.nodeExecutionStack.push({
|
||||||
node: workflow.nodes[connectionData.node],
|
node: workflow.nodes[connectionData.node],
|
||||||
data: {
|
data: {
|
||||||
main: connectionDataArray
|
main: connectionDataArray,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -608,7 +608,7 @@ export class WorkflowExecute {
|
|||||||
nodeSuccessData[0] = [
|
nodeSuccessData[0] = [
|
||||||
{
|
{
|
||||||
json: {},
|
json: {},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -622,6 +622,8 @@ export class WorkflowExecute {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
this.runExecutionData.resultData.lastNodeExecuted = executionData.node.name;
|
||||||
|
|
||||||
executionError = {
|
executionError = {
|
||||||
message: error.message,
|
message: error.message,
|
||||||
stack: error.stack,
|
stack: error.stack,
|
||||||
@@ -637,7 +639,7 @@ export class WorkflowExecute {
|
|||||||
}
|
}
|
||||||
taskData = {
|
taskData = {
|
||||||
startTime,
|
startTime,
|
||||||
executionTime: (new Date().getTime()) - startTime
|
executionTime: (new Date().getTime()) - startTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (executionError !== undefined) {
|
if (executionError !== undefined) {
|
||||||
@@ -667,7 +669,7 @@ export class WorkflowExecute {
|
|||||||
|
|
||||||
// Node executed successfully. So add data and go on.
|
// Node executed successfully. So add data and go on.
|
||||||
taskData.data = ({
|
taskData.data = ({
|
||||||
'main': nodeSuccessData
|
'main': nodeSuccessData,
|
||||||
} as ITaskDataConnections);
|
} as ITaskDataConnections);
|
||||||
|
|
||||||
this.executeHook('nodeExecuteAfter', [executionNode.name, taskData]);
|
this.executeHook('nodeExecuteAfter', [executionNode.name, taskData]);
|
||||||
@@ -698,7 +700,10 @@ export class WorkflowExecute {
|
|||||||
return Promise.reject(new Error(`The node "${executionNode.name}" connects to not found node "${connectionData.node}"`));
|
return Promise.reject(new Error(`The node "${executionNode.name}" connects to not found node "${connectionData.node}"`));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addNodeToBeExecuted(workflow, connectionData, parseInt(outputIndex, 10), executionNode.name, nodeSuccessData!, runIndex);
|
if (nodeSuccessData![outputIndex] && nodeSuccessData![outputIndex].length !== 0) {
|
||||||
|
// Add the node only if there is data for it to process
|
||||||
|
this.addNodeToBeExecuted(workflow, connectionData, parseInt(outputIndex, 10), executionNode.name, nodeSuccessData!, runIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,86 +3,86 @@ import { Credentials } from '../src';
|
|||||||
|
|
||||||
describe('Credentials', () => {
|
describe('Credentials', () => {
|
||||||
|
|
||||||
describe('without nodeType set', () => {
|
describe('without nodeType set', () => {
|
||||||
|
|
||||||
test('should be able to set and read key data without initial data set', () => {
|
test('should be able to set and read key data without initial data set', () => {
|
||||||
|
|
||||||
const credentials = new Credentials('testName', 'testType', []);
|
const credentials = new Credentials('testName', 'testType', []);
|
||||||
|
|
||||||
const key = 'key1';
|
const key = 'key1';
|
||||||
const password = 'password';
|
const password = 'password';
|
||||||
// const nodeType = 'base.noOp';
|
// const nodeType = 'base.noOp';
|
||||||
const newData = 1234;
|
const newData = 1234;
|
||||||
|
|
||||||
credentials.setDataKey(key, newData, password);
|
credentials.setDataKey(key, newData, password);
|
||||||
|
|
||||||
expect(credentials.getDataKey(key, password)).toEqual(newData);
|
expect(credentials.getDataKey(key, password)).toEqual(newData);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should be able to set and read key data with initial data set', () => {
|
test('should be able to set and read key data with initial data set', () => {
|
||||||
|
|
||||||
const key = 'key2';
|
const key = 'key2';
|
||||||
const password = 'password';
|
const password = 'password';
|
||||||
|
|
||||||
// Saved under "key1"
|
// Saved under "key1"
|
||||||
const initialData = 4321;
|
const initialData = 4321;
|
||||||
const initialDataEncoded = 'U2FsdGVkX1+0baznXt+Ag/ub8A2kHLyoLxn/rR9h4XQ=';
|
const initialDataEncoded = 'U2FsdGVkX1+0baznXt+Ag/ub8A2kHLyoLxn/rR9h4XQ=';
|
||||||
|
|
||||||
const credentials = new Credentials('testName', 'testType', [], initialDataEncoded);
|
const credentials = new Credentials('testName', 'testType', [], initialDataEncoded);
|
||||||
|
|
||||||
const newData = 1234;
|
const newData = 1234;
|
||||||
|
|
||||||
// Set and read new data
|
// Set and read new data
|
||||||
credentials.setDataKey(key, newData, password);
|
credentials.setDataKey(key, newData, password);
|
||||||
expect(credentials.getDataKey(key, password)).toEqual(newData);
|
expect(credentials.getDataKey(key, password)).toEqual(newData);
|
||||||
|
|
||||||
// Read the data which got provided encrypted on init
|
// Read the data which got provided encrypted on init
|
||||||
expect(credentials.getDataKey('key1', password)).toEqual(initialData);
|
expect(credentials.getDataKey('key1', password)).toEqual(initialData);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with nodeType set', () => {
|
describe('with nodeType set', () => {
|
||||||
|
|
||||||
test('should be able to set and read key data without initial data set', () => {
|
test('should be able to set and read key data without initial data set', () => {
|
||||||
|
|
||||||
const nodeAccess = [
|
const nodeAccess = [
|
||||||
{
|
{
|
||||||
nodeType: 'base.noOp',
|
nodeType: 'base.noOp',
|
||||||
user: 'userName',
|
user: 'userName',
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const credentials = new Credentials('testName', 'testType', nodeAccess);
|
const credentials = new Credentials('testName', 'testType', nodeAccess);
|
||||||
|
|
||||||
const key = 'key1';
|
const key = 'key1';
|
||||||
const password = 'password';
|
const password = 'password';
|
||||||
const nodeType = 'base.noOp';
|
const nodeType = 'base.noOp';
|
||||||
const newData = 1234;
|
const newData = 1234;
|
||||||
|
|
||||||
credentials.setDataKey(key, newData, password);
|
credentials.setDataKey(key, newData, password);
|
||||||
|
|
||||||
// Should be able to read with nodeType which has access
|
// Should be able to read with nodeType which has access
|
||||||
expect(credentials.getDataKey(key, password, nodeType)).toEqual(newData);
|
expect(credentials.getDataKey(key, password, nodeType)).toEqual(newData);
|
||||||
|
|
||||||
// Should not be able to read with nodeType which does NOT have access
|
// Should not be able to read with nodeType which does NOT have access
|
||||||
// expect(credentials.getDataKey(key, password, 'base.otherNode')).toThrowError(Error);
|
// expect(credentials.getDataKey(key, password, 'base.otherNode')).toThrowError(Error);
|
||||||
try {
|
try {
|
||||||
credentials.getDataKey(key, password, 'base.otherNode');
|
credentials.getDataKey(key, password, 'base.otherNode');
|
||||||
expect(true).toBe(false);
|
expect(true).toBe(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e.message).toBe('The node of type "base.otherNode" does not have access to credentials "testName" of type "testType".');
|
expect(e.message).toBe('The node of type "base.otherNode" does not have access to credentials "testName" of type "testType".');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the data which will be saved in database
|
// Get the data which will be saved in database
|
||||||
const dbData = credentials.getDataToSave();
|
const dbData = credentials.getDataToSave();
|
||||||
expect(dbData.name).toEqual('testName');
|
expect(dbData.name).toEqual('testName');
|
||||||
expect(dbData.type).toEqual('testType');
|
expect(dbData.type).toEqual('testType');
|
||||||
expect(dbData.nodesAccess).toEqual(nodeAccess);
|
expect(dbData.nodesAccess).toEqual(nodeAccess);
|
||||||
// Compare only the first 6 characters as the rest seems to change with each execution
|
// Compare only the first 6 characters as the rest seems to change with each execution
|
||||||
expect(dbData.data!.slice(0, 6)).toEqual('U2FsdGVkX1+wpQWkj+YTzaPSNTFATjnlmFKIsUTZdhk='.slice(0, 6));
|
expect(dbData.data!.slice(0, 6)).toEqual('U2FsdGVkX1+wpQWkj+YTzaPSNTFATjnlmFKIsUTZdhk='.slice(0, 6));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypes,
|
|
||||||
INodeTypeData,
|
INodeTypeData,
|
||||||
|
INodeTypes,
|
||||||
IRun,
|
IRun,
|
||||||
ITaskData,
|
ITaskData,
|
||||||
IWorkflowBase,
|
IWorkflowBase,
|
||||||
@@ -87,7 +87,7 @@ class NodeTypesClass implements INodeTypes {
|
|||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
mode: [
|
mode: [
|
||||||
'passThrough'
|
'passThrough',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -104,7 +104,7 @@ class NodeTypesClass implements INodeTypes {
|
|||||||
default: 'input1',
|
default: 'input1',
|
||||||
description: 'Defines of which input the data should be used as output of node.',
|
description: 'Defines of which input the data should be used as output of node.',
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
// const itemsInput2 = this.getInputData(1);
|
// const itemsInput2 = this.getInputData(1);
|
||||||
@@ -131,7 +131,7 @@ class NodeTypesClass implements INodeTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [returnData];
|
return [returnData];
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'n8n-nodes-base.set': {
|
'n8n-nodes-base.set': {
|
||||||
@@ -186,11 +186,11 @@ class NodeTypesClass implements INodeTypes {
|
|||||||
default: 0,
|
default: 0,
|
||||||
description: 'The number value to write in the property.',
|
description: 'The number value to write in the property.',
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
@@ -213,7 +213,7 @@ class NodeTypesClass implements INodeTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this.prepareOutputData(returnData);
|
return this.prepareOutputData(returnData);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'n8n-nodes-base.start': {
|
'n8n-nodes-base.start': {
|
||||||
@@ -231,7 +231,7 @@ class NodeTypesClass implements INodeTypes {
|
|||||||
},
|
},
|
||||||
inputs: [],
|
inputs: [],
|
||||||
outputs: ['main'],
|
outputs: ['main'],
|
||||||
properties: []
|
properties: [],
|
||||||
},
|
},
|
||||||
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ describe('WorkflowExecute', () => {
|
|||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
100,
|
100,
|
||||||
300
|
300,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
@@ -56,19 +56,19 @@ describe('WorkflowExecute', () => {
|
|||||||
"number": [
|
"number": [
|
||||||
{
|
{
|
||||||
"name": "value1",
|
"name": "value1",
|
||||||
"value": 1
|
"value": 1,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"name": "Set",
|
"name": "Set",
|
||||||
"type": "n8n-nodes-base.set",
|
"type": "n8n-nodes-base.set",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
280,
|
280,
|
||||||
300
|
300,
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"connections": {
|
"connections": {
|
||||||
"Start": {
|
"Start": {
|
||||||
@@ -77,12 +77,12 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
"node": "Set",
|
"node": "Set",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
@@ -115,8 +115,8 @@ describe('WorkflowExecute', () => {
|
|||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
100,
|
100,
|
||||||
300
|
300,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
@@ -124,18 +124,18 @@ describe('WorkflowExecute', () => {
|
|||||||
"number": [
|
"number": [
|
||||||
{
|
{
|
||||||
"name": "value1",
|
"name": "value1",
|
||||||
"value": 1
|
"value": 1,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"name": "Set1",
|
"name": "Set1",
|
||||||
"type": "n8n-nodes-base.set",
|
"type": "n8n-nodes-base.set",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
300,
|
300,
|
||||||
250
|
250,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
@@ -143,19 +143,19 @@ describe('WorkflowExecute', () => {
|
|||||||
"number": [
|
"number": [
|
||||||
{
|
{
|
||||||
"name": "value2",
|
"name": "value2",
|
||||||
"value": 2
|
"value": 2,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"name": "Set2",
|
"name": "Set2",
|
||||||
"type": "n8n-nodes-base.set",
|
"type": "n8n-nodes-base.set",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
500,
|
500,
|
||||||
400
|
400,
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"connections": {
|
"connections": {
|
||||||
"Start": {
|
"Start": {
|
||||||
@@ -164,15 +164,15 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
"node": "Set1",
|
"node": "Set1",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"node": "Set2",
|
"node": "Set2",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
"Set1": {
|
"Set1": {
|
||||||
"main": [
|
"main": [
|
||||||
@@ -180,12 +180,12 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
"node": "Set2",
|
"node": "Set2",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
@@ -201,7 +201,7 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
value1: 1,
|
value1: 1,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
Set2: [
|
Set2: [
|
||||||
[
|
[
|
||||||
@@ -228,15 +228,15 @@ describe('WorkflowExecute', () => {
|
|||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"mode": "passThrough"
|
"mode": "passThrough",
|
||||||
},
|
},
|
||||||
"name": "Merge4",
|
"name": "Merge4",
|
||||||
"type": "n8n-nodes-base.merge",
|
"type": "n8n-nodes-base.merge",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
1150,
|
1150,
|
||||||
500
|
500,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
@@ -244,18 +244,18 @@ describe('WorkflowExecute', () => {
|
|||||||
"number": [
|
"number": [
|
||||||
{
|
{
|
||||||
"name": "value2",
|
"name": "value2",
|
||||||
"value": 2
|
"value": 2,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"name": "Set2",
|
"name": "Set2",
|
||||||
"type": "n8n-nodes-base.set",
|
"type": "n8n-nodes-base.set",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
290,
|
290,
|
||||||
400
|
400,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
@@ -263,18 +263,18 @@ describe('WorkflowExecute', () => {
|
|||||||
"number": [
|
"number": [
|
||||||
{
|
{
|
||||||
"name": "value4",
|
"name": "value4",
|
||||||
"value": 4
|
"value": 4,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"name": "Set4",
|
"name": "Set4",
|
||||||
"type": "n8n-nodes-base.set",
|
"type": "n8n-nodes-base.set",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
850,
|
850,
|
||||||
200
|
200,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
@@ -282,30 +282,30 @@ describe('WorkflowExecute', () => {
|
|||||||
"number": [
|
"number": [
|
||||||
{
|
{
|
||||||
"name": "value3",
|
"name": "value3",
|
||||||
"value": 3
|
"value": 3,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"name": "Set3",
|
"name": "Set3",
|
||||||
"type": "n8n-nodes-base.set",
|
"type": "n8n-nodes-base.set",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
650,
|
650,
|
||||||
200
|
200,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"mode": "passThrough"
|
"mode": "passThrough",
|
||||||
},
|
},
|
||||||
"name": "Merge4",
|
"name": "Merge4",
|
||||||
"type": "n8n-nodes-base.merge",
|
"type": "n8n-nodes-base.merge",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
1150,
|
1150,
|
||||||
500
|
500,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {},
|
"parameters": {},
|
||||||
@@ -314,21 +314,21 @@ describe('WorkflowExecute', () => {
|
|||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
1000,
|
1000,
|
||||||
400
|
400,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"mode": "passThrough",
|
"mode": "passThrough",
|
||||||
"output": "input2"
|
"output": "input2",
|
||||||
},
|
},
|
||||||
"name": "Merge2",
|
"name": "Merge2",
|
||||||
"type": "n8n-nodes-base.merge",
|
"type": "n8n-nodes-base.merge",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
700,
|
700,
|
||||||
400
|
400,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {},
|
"parameters": {},
|
||||||
@@ -337,8 +337,8 @@ describe('WorkflowExecute', () => {
|
|||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
500,
|
500,
|
||||||
300
|
300,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
@@ -346,18 +346,18 @@ describe('WorkflowExecute', () => {
|
|||||||
"number": [
|
"number": [
|
||||||
{
|
{
|
||||||
"name": "value1",
|
"name": "value1",
|
||||||
"value": 1
|
"value": 1,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"name": "Set1",
|
"name": "Set1",
|
||||||
"type": "n8n-nodes-base.set",
|
"type": "n8n-nodes-base.set",
|
||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
300,
|
300,
|
||||||
200
|
200,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {},
|
"parameters": {},
|
||||||
@@ -366,9 +366,9 @@ describe('WorkflowExecute', () => {
|
|||||||
"typeVersion": 1,
|
"typeVersion": 1,
|
||||||
"position": [
|
"position": [
|
||||||
100,
|
100,
|
||||||
300
|
300,
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"connections": {
|
"connections": {
|
||||||
"Set2": {
|
"Set2": {
|
||||||
@@ -377,15 +377,15 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
"node": "Merge1",
|
"node": "Merge1",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 1
|
"index": 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"node": "Merge2",
|
"node": "Merge2",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 1
|
"index": 1,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
"Set4": {
|
"Set4": {
|
||||||
"main": [
|
"main": [
|
||||||
@@ -393,10 +393,10 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
"node": "Merge3",
|
"node": "Merge3",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
"Set3": {
|
"Set3": {
|
||||||
"main": [
|
"main": [
|
||||||
@@ -404,10 +404,10 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
"node": "Set4",
|
"node": "Set4",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
"Merge3": {
|
"Merge3": {
|
||||||
"main": [
|
"main": [
|
||||||
@@ -415,10 +415,10 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
"node": "Merge4",
|
"node": "Merge4",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
"Merge2": {
|
"Merge2": {
|
||||||
"main": [
|
"main": [
|
||||||
@@ -426,10 +426,10 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
"node": "Merge3",
|
"node": "Merge3",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 1
|
"index": 1,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
"Merge1": {
|
"Merge1": {
|
||||||
"main": [
|
"main": [
|
||||||
@@ -437,10 +437,10 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
"node": "Merge2",
|
"node": "Merge2",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
"Set1": {
|
"Set1": {
|
||||||
"main": [
|
"main": [
|
||||||
@@ -448,15 +448,15 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
"node": "Merge1",
|
"node": "Merge1",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"node": "Set3",
|
"node": "Set3",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
"Start": {
|
"Start": {
|
||||||
"main": [
|
"main": [
|
||||||
@@ -464,22 +464,22 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
"node": "Set1",
|
"node": "Set1",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"node": "Set2",
|
"node": "Set2",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"node": "Merge4",
|
"node": "Merge4",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 1
|
"index": 1,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
@@ -534,14 +534,14 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
value2: 2,
|
value2: 2,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
Merge2: [
|
Merge2: [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
value2: 2,
|
value2: 2,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
Merge3: [
|
Merge3: [
|
||||||
[
|
[
|
||||||
@@ -553,7 +553,7 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
value2: 2,
|
value2: 2,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
Merge4: [
|
Merge4: [
|
||||||
[
|
[
|
||||||
@@ -565,7 +565,7 @@ describe('WorkflowExecute', () => {
|
|||||||
{
|
{
|
||||||
value2: 2,
|
value2: 2,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -46,6 +46,11 @@
|
|||||||
"forin": true,
|
"forin": true,
|
||||||
"jsdoc-format": true,
|
"jsdoc-format": true,
|
||||||
"label-position": true,
|
"label-position": true,
|
||||||
|
"indent": [
|
||||||
|
true,
|
||||||
|
"tabs",
|
||||||
|
2
|
||||||
|
],
|
||||||
"member-access": [
|
"member-access": [
|
||||||
true,
|
true,
|
||||||
"no-public"
|
"no-public"
|
||||||
@@ -60,6 +65,13 @@
|
|||||||
"no-default-export": true,
|
"no-default-export": true,
|
||||||
"no-duplicate-variable": true,
|
"no-duplicate-variable": true,
|
||||||
"no-inferrable-types": true,
|
"no-inferrable-types": true,
|
||||||
|
"ordered-imports": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"import-sources-order": "any",
|
||||||
|
"named-imports-order": "case-insensitive"
|
||||||
|
}
|
||||||
|
],
|
||||||
"no-namespace": [
|
"no-namespace": [
|
||||||
true,
|
true,
|
||||||
"allow-declarations"
|
"allow-declarations"
|
||||||
@@ -82,6 +94,18 @@
|
|||||||
"ignore-bound-class-methods"
|
"ignore-bound-class-methods"
|
||||||
],
|
],
|
||||||
"switch-default": true,
|
"switch-default": true,
|
||||||
|
"trailing-comma": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"multiline": {
|
||||||
|
"objects": "always",
|
||||||
|
"arrays": "always",
|
||||||
|
"functions": "always",
|
||||||
|
"typeLiterals": "ignore"
|
||||||
|
},
|
||||||
|
"esSpecCompliant": true
|
||||||
|
}
|
||||||
|
],
|
||||||
"triple-equals": [
|
"triple-equals": [
|
||||||
true,
|
true,
|
||||||
"allow-null-check"
|
"allow-null-check"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Condition notice.
|
|||||||
|
|
||||||
Software: n8n
|
Software: n8n
|
||||||
|
|
||||||
License: Apache 2.0
|
License: Apache 2.0 with Commons Clause
|
||||||
|
|
||||||
Licensor: n8n GmbH
|
Licensor: n8n GmbH
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n-editor-ui",
|
"name": "n8n-editor-ui",
|
||||||
"version": "0.55.0",
|
"version": "0.60.0",
|
||||||
"description": "Workflow Editor UI for n8n",
|
"description": "Workflow Editor UI for n8n",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
@@ -20,12 +20,11 @@
|
|||||||
"serve": "cross-env VUE_APP_URL_BASE_API=http://localhost:5678/ vue-cli-service serve",
|
"serve": "cross-env VUE_APP_URL_BASE_API=http://localhost:5678/ vue-cli-service serve",
|
||||||
"test": "npm run test:unit",
|
"test": "npm run test:unit",
|
||||||
"tslint": "tslint -p tsconfig.json -c tslint.json",
|
"tslint": "tslint -p tsconfig.json -c tslint.json",
|
||||||
|
"tslintfix": "tslint --fix -p tsconfig.json -c tslint.json",
|
||||||
"test:e2e": "vue-cli-service test:e2e",
|
"test:e2e": "vue-cli-service test:e2e",
|
||||||
"test:unit": "vue-cli-service test:unit"
|
"test:unit": "vue-cli-service test:unit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {},
|
||||||
"uuid": "^8.1.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@beyonk/google-fonts-webpack-plugin": "^1.2.3",
|
"@beyonk/google-fonts-webpack-plugin": "^1.2.3",
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.19",
|
"@fortawesome/fontawesome-svg-core": "^1.2.19",
|
||||||
@@ -34,16 +33,16 @@
|
|||||||
"@types/dateformat": "^3.0.0",
|
"@types/dateformat": "^3.0.0",
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.17.6",
|
||||||
"@types/file-saver": "^2.0.1",
|
"@types/file-saver": "^2.0.1",
|
||||||
"@types/jest": "^25.2.1",
|
"@types/jest": "^26.0.13",
|
||||||
"@types/lodash.get": "^4.4.6",
|
"@types/lodash.get": "^4.4.6",
|
||||||
"@types/lodash.set": "^4.3.6",
|
"@types/lodash.set": "^4.3.6",
|
||||||
"@types/node": "^14.0.27",
|
"@types/node": "14.0.27",
|
||||||
"@types/quill": "^2.0.1",
|
"@types/quill": "^2.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.13.0",
|
"@typescript-eslint/eslint-plugin": "^2.13.0",
|
||||||
"@typescript-eslint/parser": "^2.13.0",
|
"@typescript-eslint/parser": "^2.13.0",
|
||||||
"@vue/cli-plugin-babel": "^4.1.2",
|
"@vue/cli-plugin-babel": "^4.1.2",
|
||||||
"@vue/cli-plugin-eslint": "^4.1.2",
|
"@vue/cli-plugin-eslint": "^4.1.2",
|
||||||
"@vue/cli-plugin-typescript": "~4.1.2",
|
"@vue/cli-plugin-typescript": "~4.5.6",
|
||||||
"@vue/cli-plugin-unit-jest": "^4.1.2",
|
"@vue/cli-plugin-unit-jest": "^4.1.2",
|
||||||
"@vue/cli-service": "^3.11.0",
|
"@vue/cli-service": "^3.11.0",
|
||||||
"@vue/eslint-config-standard": "^5.0.1",
|
"@vue/eslint-config-standard": "^5.0.1",
|
||||||
@@ -66,16 +65,18 @@
|
|||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.set": "^4.3.2",
|
"lodash.set": "^4.3.2",
|
||||||
"n8n-workflow": "~0.39.0",
|
"n8n-workflow": "~0.42.0",
|
||||||
"node-sass": "^4.12.0",
|
"node-sass": "^4.12.0",
|
||||||
|
"normalize-wheel": "^1.0.1",
|
||||||
"prismjs": "^1.17.1",
|
"prismjs": "^1.17.1",
|
||||||
"quill": "^2.0.0-dev.3",
|
"quill": "^2.0.0-dev.3",
|
||||||
"quill-autoformat": "^0.1.1",
|
"quill-autoformat": "^0.1.1",
|
||||||
"sass-loader": "^8.0.0",
|
"sass-loader": "^8.0.0",
|
||||||
"string-template-parser": "^1.2.6",
|
"string-template-parser": "^1.2.6",
|
||||||
"ts-jest": "^25.4.0",
|
"ts-jest": "^26.3.0",
|
||||||
"tslint": "^6.1.2",
|
"tslint": "^6.1.2",
|
||||||
"typescript": "~3.7.4",
|
"typescript": "~3.9.7",
|
||||||
|
"uuid": "^8.1.0",
|
||||||
"vue": "^2.6.9",
|
"vue": "^2.6.9",
|
||||||
"vue-cli-plugin-webpack-bundle-analyzer": "^2.0.0",
|
"vue-cli-plugin-webpack-bundle-analyzer": "^2.0.0",
|
||||||
"vue-json-tree": "^0.4.1",
|
"vue-json-tree": "^0.4.1",
|
||||||
@@ -83,6 +84,7 @@
|
|||||||
"vue-router": "^3.0.6",
|
"vue-router": "^3.0.6",
|
||||||
"vue-template-compiler": "^2.5.17",
|
"vue-template-compiler": "^2.5.17",
|
||||||
"vue-typed-mixins": "^0.2.0",
|
"vue-typed-mixins": "^0.2.0",
|
||||||
|
"vue2-touch-events": "^2.3.2",
|
||||||
"vuex": "^3.1.1"
|
"vuex": "^3.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ export interface IRestApi {
|
|||||||
makeRestApiRequest(method: string, endpoint: string, data?: any): Promise<any>; // tslint:disable-line:no-any
|
makeRestApiRequest(method: string, endpoint: string, data?: any): Promise<any>; // tslint:disable-line:no-any
|
||||||
getSettings(): Promise<IN8nUISettings>;
|
getSettings(): Promise<IN8nUISettings>;
|
||||||
getNodeTypes(): Promise<INodeTypeDescription[]>;
|
getNodeTypes(): Promise<INodeTypeDescription[]>;
|
||||||
|
getNodesInformation(nodeList: string[]): Promise<INodeTypeDescription[]>;
|
||||||
getNodeParameterOptions(nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]>;
|
getNodeParameterOptions(nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]>;
|
||||||
removeTestWebhook(workflowId: string): Promise<boolean>;
|
removeTestWebhook(workflowId: string): Promise<boolean>;
|
||||||
runWorkflow(runData: IStartRunData): Promise<IExecutionPushResponse>;
|
runWorkflow(runData: IStartRunData): Promise<IExecutionPushResponse>;
|
||||||
@@ -399,6 +400,10 @@ export interface IN8nUISettings {
|
|||||||
timezone: string;
|
timezone: string;
|
||||||
executionTimeout: number;
|
executionTimeout: number;
|
||||||
maxExecutionTimeout: number;
|
maxExecutionTimeout: number;
|
||||||
|
oauthCallbackUrls: {
|
||||||
|
oauth1: string;
|
||||||
|
oauth2: string;
|
||||||
|
};
|
||||||
urlBaseWebhook: string;
|
urlBaseWebhook: string;
|
||||||
versionCli: string;
|
versionCli: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<div name="title" class="title-container" slot="title">
|
<div name="title" class="title-container" slot="title">
|
||||||
<div class="title-left">{{title}}</div>
|
<div class="title-left">{{title}}</div>
|
||||||
<div class="title-right">
|
<div class="title-right">
|
||||||
<div v-if="credentialType" class="docs-container">
|
<div v-if="credentialType && documentationUrl" class="docs-container">
|
||||||
<svg class="help-logo" target="_blank" width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<svg class="help-logo" target="_blank" width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<title>Node Documentation</title>
|
<title>Node Documentation</title>
|
||||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
<span v-if="credentialType" class="doc-link-text">Need help? <a class="doc-hyperlink" :href="'https://docs.n8n.io/credentials/' + documentationUrl + '/?utm_source=n8n_app&utm_medium=left_nav_menu&utm_campaign=create_new_credentials_modal'" target="_blank">Open credential docs</a></span>
|
<span class="doc-link-text">Need help? <a class="doc-hyperlink" :href="'https://docs.n8n.io/credentials/' + documentationUrl + '/?utm_source=n8n_app&utm_medium=left_nav_menu&utm_campaign=create_new_credentials_modal'" target="_blank">Open credential docs</a></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -109,27 +109,19 @@ export default mixins(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
documentationUrl (): string {
|
documentationUrl (): string | undefined {
|
||||||
|
let credentialTypeName = '';
|
||||||
if (this.editCredentials) {
|
if (this.editCredentials) {
|
||||||
const credentialType = this.$store.getters.credentialType(this.editCredentials.type);
|
credentialTypeName = this.editCredentials.type as string;
|
||||||
if (credentialType.documentationUrl === undefined) {
|
|
||||||
return credentialType.name;
|
|
||||||
} else {
|
|
||||||
return `${credentialType.documentationUrl}`;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (this.credentialType) {
|
credentialTypeName = this.credentialType as string;
|
||||||
const credentialType = this.$store.getters.credentialType(this.credentialType);
|
|
||||||
|
|
||||||
if (credentialType.documentationUrl === undefined) {
|
|
||||||
return credentialType.name;
|
|
||||||
} else {
|
|
||||||
return `${credentialType.documentationUrl}`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const credentialType = this.$store.getters.credentialType(credentialTypeName);
|
||||||
|
if (credentialType.documentationUrl !== undefined) {
|
||||||
|
return `${credentialType.documentationUrl}`;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
},
|
},
|
||||||
node (): INodeUi {
|
node (): INodeUi {
|
||||||
return this.$store.getters.activeNode;
|
return this.$store.getters.activeNode;
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ export default mixins(
|
|||||||
oAuthCallbackUrl (): string {
|
oAuthCallbackUrl (): string {
|
||||||
const types = this.parentTypes(this.credentialTypeData.name);
|
const types = this.parentTypes(this.credentialTypeData.name);
|
||||||
const oauthType = (this.credentialTypeData.name === 'oAuth2Api' || types.includes('oAuth2Api')) ? 'oauth2' : 'oauth1';
|
const oauthType = (this.credentialTypeData.name === 'oAuth2Api' || types.includes('oAuth2Api')) ? 'oauth2' : 'oauth1';
|
||||||
return this.$store.getters.getWebhookBaseUrl + `rest/${oauthType}-credential/callback`;
|
return this.$store.getters.oauthCallbackUrls[oauthType];
|
||||||
},
|
},
|
||||||
requiredPropertiesFilled (): boolean {
|
requiredPropertiesFilled (): boolean {
|
||||||
for (const property of this.credentialProperties) {
|
for (const property of this.credentialProperties) {
|
||||||
@@ -404,10 +404,11 @@ export default mixins(
|
|||||||
message: 'Connected successfully!',
|
message: 'Connected successfully!',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Make sure that the event gets removed again
|
||||||
|
window.removeEventListener('message', receiveMessage, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the event gets removed again
|
|
||||||
window.removeEventListener('message', receiveMessage, false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('message', receiveMessage, false);
|
window.addEventListener('message', receiveMessage, false);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="node-wrapper" :style="nodePosition">
|
<div class="node-wrapper" :style="nodePosition">
|
||||||
<div class="node-default" :ref="data.name" :style="nodeStyle" :class="nodeClass" @dblclick="setNodeActive" @click.left="mouseLeftClick">
|
<div class="node-default" :ref="data.name" :style="nodeStyle" :class="nodeClass" @dblclick="setNodeActive" @click.left="mouseLeftClick" v-touch:start="touchStart" v-touch:end="touchEnd">
|
||||||
<div v-if="hasIssues" class="node-info-icon node-issues">
|
<div v-if="hasIssues" class="node-info-icon node-issues">
|
||||||
<el-tooltip placement="top" effect="light">
|
<el-tooltip placement="top" effect="light">
|
||||||
<div slot="content" v-html="nodeIssues"></div>
|
<div slot="content" v-html="nodeIssues"></div>
|
||||||
@@ -13,19 +13,19 @@
|
|||||||
<font-awesome-icon icon="sync-alt" spin />
|
<font-awesome-icon icon="sync-alt" spin />
|
||||||
</div>
|
</div>
|
||||||
<div class="node-options" v-if="!isReadOnly">
|
<div class="node-options" v-if="!isReadOnly">
|
||||||
<div @click.stop.left="deleteNode" class="option" title="Delete Node" >
|
<div v-touch:tap="deleteNode" class="option" title="Delete Node" >
|
||||||
<font-awesome-icon icon="trash" />
|
<font-awesome-icon icon="trash" />
|
||||||
</div>
|
</div>
|
||||||
<div @click.stop.left="disableNode" class="option" title="Activate/Deactivate Node" >
|
<div v-touch:tap="disableNode" class="option" title="Activate/Deactivate Node" >
|
||||||
<font-awesome-icon :icon="nodeDisabledIcon" />
|
<font-awesome-icon :icon="nodeDisabledIcon" />
|
||||||
</div>
|
</div>
|
||||||
<div @click.stop.left="duplicateNode" class="option" title="Duplicate Node" >
|
<div v-touch:tap="duplicateNode" class="option" title="Duplicate Node" >
|
||||||
<font-awesome-icon icon="clone" />
|
<font-awesome-icon icon="clone" />
|
||||||
</div>
|
</div>
|
||||||
<div @click.stop.left="setNodeActive" class="option touch" title="Edit Node" v-if="!isReadOnly">
|
<div v-touch:tap="setNodeActive" class="option touch" title="Edit Node" v-if="!isReadOnly">
|
||||||
<font-awesome-icon class="execute-icon" icon="cog" />
|
<font-awesome-icon class="execute-icon" icon="cog" />
|
||||||
</div>
|
</div>
|
||||||
<div @click.stop.left="executeNode" class="option" title="Execute Node" v-if="!isReadOnly && !workflowRunning">
|
<div v-touch:tap="executeNode" class="option" title="Execute Node" v-if="!isReadOnly && !workflowRunning">
|
||||||
<font-awesome-icon class="execute-icon" icon="play-circle" />
|
<font-awesome-icon class="execute-icon" icon="play-circle" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -110,6 +110,10 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
|||||||
classes.push('is-touch-device');
|
classes.push('is-touch-device');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isTouchActive) {
|
||||||
|
classes.push('touch-active');
|
||||||
|
}
|
||||||
|
|
||||||
return classes;
|
return classes;
|
||||||
},
|
},
|
||||||
nodeIssues (): string {
|
nodeIssues (): string {
|
||||||
@@ -134,7 +138,7 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.nodeType !== null && this.nodeType.subtitle !== undefined) {
|
if (this.nodeType !== null && this.nodeType.subtitle !== undefined) {
|
||||||
return this.workflow.getSimpleParameterValue(this.data as INode, this.nodeType.subtitle) as string | undefined;
|
return this.workflow.expression.getSimpleParameterValue(this.data as INode, this.nodeType.subtitle) as string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.data.parameters.operation !== undefined) {
|
if (this.data.parameters.operation !== undefined) {
|
||||||
@@ -174,7 +178,7 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
|||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isTouchDevice: 'ontouchstart' in window || navigator.msMaxTouchPoints,
|
isTouchActive: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -199,6 +203,14 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
|||||||
setNodeActive () {
|
setNodeActive () {
|
||||||
this.$store.commit('setActiveNode', this.data.name);
|
this.$store.commit('setActiveNode', this.data.name);
|
||||||
},
|
},
|
||||||
|
touchStart () {
|
||||||
|
if (this.isTouchDevice === true && this.isMacOs === false && this.isTouchActive === false) {
|
||||||
|
this.isTouchActive = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.isTouchActive = false;
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -268,6 +280,7 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.touch-active,
|
||||||
&:hover {
|
&:hover {
|
||||||
.node-execute {
|
.node-execute {
|
||||||
display: initial;
|
display: initial;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="node-icon-wrapper" :style="iconStyleData">
|
<div class="node-icon-wrapper" :style="iconStyleData" :class="{full: isSvgIcon}">
|
||||||
<div v-if="nodeIconData !== null" class="icon">
|
<div v-if="nodeIconData !== null" class="icon">
|
||||||
<img :src="nodeIconData.path" style="width: 100%; height: 100%;" v-if="nodeIconData.type === 'file'"/>
|
<img :src="nodeIconData.path" style="width: 100%; height: 100%;" v-if="nodeIconData.type === 'file'"/>
|
||||||
<font-awesome-icon :icon="nodeIconData.path" v-else-if="nodeIconData.type === 'fa'" />
|
<font-awesome-icon :icon="nodeIconData.path" v-else-if="nodeIconData.type === 'fa'" />
|
||||||
@@ -17,6 +17,7 @@ import Vue from 'vue';
|
|||||||
interface NodeIconData {
|
interface NodeIconData {
|
||||||
type: string;
|
type: string;
|
||||||
path: string;
|
path: string;
|
||||||
|
fileExtension?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
@@ -41,6 +42,12 @@ export default Vue.extend({
|
|||||||
'border-radius': Math.ceil(size / 2) + 'px',
|
'border-radius': Math.ceil(size / 2) + 'px',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
isSvgIcon (): boolean {
|
||||||
|
if (this.nodeIconData && this.nodeIconData.type === 'file' && this.nodeIconData.fileExtension === 'svg') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
nodeIconData (): null | NodeIconData {
|
nodeIconData (): null | NodeIconData {
|
||||||
if (this.nodeType === null) {
|
if (this.nodeType === null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -51,13 +58,14 @@ export default Vue.extend({
|
|||||||
if (this.nodeType.icon) {
|
if (this.nodeType.icon) {
|
||||||
let type, path;
|
let type, path;
|
||||||
[type, path] = this.nodeType.icon.split(':');
|
[type, path] = this.nodeType.icon.split(':');
|
||||||
const returnData = {
|
const returnData: NodeIconData = {
|
||||||
type,
|
type,
|
||||||
path,
|
path,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (type === 'file') {
|
if (type === 'file') {
|
||||||
returnData.path = restUrl + '/node-icon/' + this.nodeType.name;
|
returnData.path = restUrl + '/node-icon/' + this.nodeType.name;
|
||||||
|
returnData.fileExtension = path.split('.').slice(-1).join();
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnData;
|
return returnData;
|
||||||
@@ -83,6 +91,10 @@ export default Vue.extend({
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
||||||
|
&.full .icon {
|
||||||
|
margin: 0.24em;
|
||||||
|
}
|
||||||
|
|
||||||
.node-icon-placeholder {
|
.node-icon-placeholder {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ export default mixins(
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
description: 'If active, the workflow continues even if this node\'s <br /execution fails. When this occurs, the node passes along input data from<br />previous nodes - so your workflow should account for unexpected output data.',
|
description: 'If active, the workflow continues even if this node\'s <br />execution fails. When this occurs, the node passes along input data from<br />previous nodes - so your workflow should account for unexpected output data.',
|
||||||
},
|
},
|
||||||
] as INodeProperties[],
|
] as INodeProperties[],
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import Vue from 'vue';
|
|||||||
export default Vue.extend(
|
export default Vue.extend(
|
||||||
{
|
{
|
||||||
name: 'PageContentWrapper',
|
name: 'PageContentWrapper',
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ export default mixins(
|
|||||||
* @returns
|
* @returns
|
||||||
* @memberof Workflow
|
* @memberof Workflow
|
||||||
*/
|
*/
|
||||||
getNodeOutputData (runData: IRunData, nodeName: string, filterText: string, itemIndex = 0, runIndex = 0, inputName = 'main', outputIndex = 0): IVariableSelectorOption[] | null {
|
getNodeOutputData (runData: IRunData, nodeName: string, filterText: string, itemIndex = 0, runIndex = 0, inputName = 'main', outputIndex = 0, useShort = false): IVariableSelectorOption[] | null {
|
||||||
if (!runData.hasOwnProperty(nodeName)) {
|
if (!runData.hasOwnProperty(nodeName)) {
|
||||||
// No data found for node
|
// No data found for node
|
||||||
return null;
|
return null;
|
||||||
@@ -291,9 +291,12 @@ export default mixins(
|
|||||||
|
|
||||||
// Get json data
|
// Get json data
|
||||||
if (outputData.hasOwnProperty('json')) {
|
if (outputData.hasOwnProperty('json')) {
|
||||||
|
|
||||||
|
const jsonPropertyPrefix = useShort === true ? '$json' : `$node["${nodeName}"].json`;
|
||||||
|
|
||||||
const jsonDataOptions: IVariableSelectorOption[] = [];
|
const jsonDataOptions: IVariableSelectorOption[] = [];
|
||||||
for (const propertyName of Object.keys(outputData.json)) {
|
for (const propertyName of Object.keys(outputData.json)) {
|
||||||
jsonDataOptions.push.apply(jsonDataOptions, this.jsonDataToFilterOption(outputData.json[propertyName], `$node["${nodeName}"].json`, propertyName, filterText));
|
jsonDataOptions.push.apply(jsonDataOptions, this.jsonDataToFilterOption(outputData.json[propertyName], jsonPropertyPrefix, propertyName, filterText));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jsonDataOptions.length) {
|
if (jsonDataOptions.length) {
|
||||||
@@ -308,6 +311,9 @@ export default mixins(
|
|||||||
|
|
||||||
// Get binary data
|
// Get binary data
|
||||||
if (outputData.hasOwnProperty('binary')) {
|
if (outputData.hasOwnProperty('binary')) {
|
||||||
|
|
||||||
|
const binaryPropertyPrefix = useShort === true ? '$binary' : `$node["${nodeName}"].binary`;
|
||||||
|
|
||||||
const binaryData = [];
|
const binaryData = [];
|
||||||
let binaryPropertyData = [];
|
let binaryPropertyData = [];
|
||||||
|
|
||||||
@@ -326,7 +332,7 @@ export default mixins(
|
|||||||
binaryPropertyData.push(
|
binaryPropertyData.push(
|
||||||
{
|
{
|
||||||
name: propertyName,
|
name: propertyName,
|
||||||
key: `$node["${nodeName}"].binary.${dataPropertyName}.${propertyName}`,
|
key: `${binaryPropertyPrefix}.${dataPropertyName}.${propertyName}`,
|
||||||
value: outputData.binary![dataPropertyName][propertyName],
|
value: outputData.binary![dataPropertyName][propertyName],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -336,7 +342,7 @@ export default mixins(
|
|||||||
binaryData.push(
|
binaryData.push(
|
||||||
{
|
{
|
||||||
name: dataPropertyName,
|
name: dataPropertyName,
|
||||||
key: `$node["${nodeName}"].binary.${dataPropertyName}`,
|
key: `${binaryPropertyPrefix}.${dataPropertyName}`,
|
||||||
options: this.sortOptions(binaryPropertyData),
|
options: this.sortOptions(binaryPropertyData),
|
||||||
allowParentSelect: true,
|
allowParentSelect: true,
|
||||||
},
|
},
|
||||||
@@ -347,7 +353,7 @@ export default mixins(
|
|||||||
returnData.push(
|
returnData.push(
|
||||||
{
|
{
|
||||||
name: 'Binary',
|
name: 'Binary',
|
||||||
key: `$node["${nodeName}"].binary`,
|
key: binaryPropertyPrefix,
|
||||||
options: this.sortOptions(binaryData),
|
options: this.sortOptions(binaryData),
|
||||||
allowParentSelect: true,
|
allowParentSelect: true,
|
||||||
},
|
},
|
||||||
@@ -474,7 +480,7 @@ export default mixins(
|
|||||||
// (example "IF" node. If node is connected to "true" or to "false" output)
|
// (example "IF" node. If node is connected to "true" or to "false" output)
|
||||||
const outputIndex = this.workflow.getNodeConnectionOutputIndex(activeNode.name, parentNode[0], 'main');
|
const outputIndex = this.workflow.getNodeConnectionOutputIndex(activeNode.name, parentNode[0], 'main');
|
||||||
|
|
||||||
tempOutputData = this.getNodeOutputData(runData, parentNode[0], filterText, itemIndex, 0, 'main', outputIndex) as IVariableSelectorOption[];
|
tempOutputData = this.getNodeOutputData(runData, parentNode[0], filterText, itemIndex, 0, 'main', outputIndex, true) as IVariableSelectorOption[];
|
||||||
|
|
||||||
if (tempOutputData) {
|
if (tempOutputData) {
|
||||||
if (JSON.stringify(tempOutputData).length < 102400) {
|
if (JSON.stringify(tempOutputData).length < 102400) {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export const deviceSupportHelpers = Vue.extend({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isTouchDevice: 'ontouchstart' in window || navigator.msMaxTouchPoints,
|
||||||
|
isMacOs: /(ipad|iphone|ipod|mac)/i.test(navigator.platform),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// TODO: Check if used anywhere
|
||||||
|
controlKeyCode(): string {
|
||||||
|
if (this.isMacOs) {
|
||||||
|
return 'Meta';
|
||||||
|
}
|
||||||
|
return 'Control';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isCtrlKeyPressed(e: MouseEvent | KeyboardEvent): boolean {
|
||||||
|
if (this.isTouchDevice === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.isMacOs) {
|
||||||
|
return e.metaKey;
|
||||||
|
}
|
||||||
|
return e.ctrlKey;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -2,20 +2,19 @@ import { INodeUi } from '@/Interface';
|
|||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
|
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
|
||||||
import { nodeIndex } from '@/components/mixins/nodeIndex';
|
import { nodeIndex } from '@/components/mixins/nodeIndex';
|
||||||
|
|
||||||
export const mouseSelect = mixins(nodeIndex).extend({
|
export const mouseSelect = mixins(
|
||||||
|
deviceSupportHelpers,
|
||||||
|
nodeIndex,
|
||||||
|
).extend({
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
selectActive: false,
|
selectActive: false,
|
||||||
selectBox: document.createElement('span'),
|
selectBox: document.createElement('span'),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
isMacOs (): boolean {
|
|
||||||
return /(ipad|iphone|ipod|mac)/i.test(navigator.platform);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted () {
|
mounted () {
|
||||||
this.createSelectBox();
|
this.createSelectBox();
|
||||||
},
|
},
|
||||||
@@ -34,6 +33,9 @@ export const mouseSelect = mixins(nodeIndex).extend({
|
|||||||
this.$el.appendChild(this.selectBox);
|
this.$el.appendChild(this.selectBox);
|
||||||
},
|
},
|
||||||
isCtrlKeyPressed (e: MouseEvent | KeyboardEvent): boolean {
|
isCtrlKeyPressed (e: MouseEvent | KeyboardEvent): boolean {
|
||||||
|
if (this.isTouchDevice === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (this.isMacOs) {
|
if (this.isMacOs) {
|
||||||
return e.metaKey;
|
return e.metaKey;
|
||||||
}
|
}
|
||||||
@@ -125,6 +127,13 @@ export const mouseSelect = mixins(nodeIndex).extend({
|
|||||||
},
|
},
|
||||||
mouseUpMouseSelect (e: MouseEvent) {
|
mouseUpMouseSelect (e: MouseEvent) {
|
||||||
if (this.selectActive === false) {
|
if (this.selectActive === false) {
|
||||||
|
if (this.isTouchDevice === true) {
|
||||||
|
// @ts-ignore
|
||||||
|
if (e.target && e.target.id.includes('node-view')) {
|
||||||
|
// Deselect all nodes
|
||||||
|
this.deselectAllNodes();
|
||||||
|
}
|
||||||
|
}
|
||||||
// If it is not active return direcly.
|
// If it is not active return direcly.
|
||||||
// Else normal node dragging will not work.
|
// Else normal node dragging will not work.
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,41 +1,43 @@
|
|||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
// @ts-ignore
|
||||||
|
import normalizeWheel from 'normalize-wheel';
|
||||||
|
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
|
||||||
import { nodeIndex } from '@/components/mixins/nodeIndex';
|
import { nodeIndex } from '@/components/mixins/nodeIndex';
|
||||||
|
|
||||||
export const moveNodeWorkflow = mixins(nodeIndex).extend({
|
export const moveNodeWorkflow = mixins(
|
||||||
|
deviceSupportHelpers,
|
||||||
|
nodeIndex,
|
||||||
|
).extend({
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
moveLastPosition: [0, 0],
|
moveLastPosition: [0, 0],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
controlKeyCode (): string {
|
|
||||||
if (this.isMacOs) {
|
|
||||||
return 'Meta';
|
|
||||||
}
|
|
||||||
return 'Control';
|
|
||||||
},
|
|
||||||
isMacOs (): boolean {
|
|
||||||
return /(ipad|iphone|ipod|mac)/i.test(navigator.platform);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
isCtrlKeyPressed (e: MouseEvent | KeyboardEvent): boolean {
|
getMousePosition(e: MouseEvent | TouchEvent) {
|
||||||
if (this.isMacOs) {
|
// @ts-ignore
|
||||||
return e.metaKey;
|
const x = e.pageX !== undefined ? e.pageX : (e.touches && e.touches[0] && e.touches[0].pageX ? e.touches[0].pageX : 0);
|
||||||
}
|
// @ts-ignore
|
||||||
return e.ctrlKey;
|
const y = e.pageY !== undefined ? e.pageY : (e.touches && e.touches[0] && e.touches[0].pageY ? e.touches[0].pageY : 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
moveWorkflow (e: MouseEvent) {
|
moveWorkflow (e: MouseEvent) {
|
||||||
const offsetPosition = this.$store.getters.getNodeViewOffsetPosition;
|
const offsetPosition = this.$store.getters.getNodeViewOffsetPosition;
|
||||||
|
|
||||||
const nodeViewOffsetPositionX = offsetPosition[0] + (e.pageX - this.moveLastPosition[0]);
|
const position = this.getMousePosition(e);
|
||||||
const nodeViewOffsetPositionY = offsetPosition[1] + (e.pageY - this.moveLastPosition[1]);
|
|
||||||
|
const nodeViewOffsetPositionX = offsetPosition[0] + (position.x - this.moveLastPosition[0]);
|
||||||
|
const nodeViewOffsetPositionY = offsetPosition[1] + (position.y - this.moveLastPosition[1]);
|
||||||
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [nodeViewOffsetPositionX, nodeViewOffsetPositionY], setStateDirty: true});
|
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [nodeViewOffsetPositionX, nodeViewOffsetPositionY], setStateDirty: true});
|
||||||
|
|
||||||
// Update the last position
|
// Update the last position
|
||||||
this.moveLastPosition[0] = e.pageX;
|
this.moveLastPosition[0] = position.x;
|
||||||
this.moveLastPosition[1] = e.pageY;
|
this.moveLastPosition[1] = position.y;
|
||||||
},
|
},
|
||||||
mouseDownMoveWorkflow (e: MouseEvent) {
|
mouseDownMoveWorkflow (e: MouseEvent) {
|
||||||
if (this.isCtrlKeyPressed(e) === false) {
|
if (this.isCtrlKeyPressed(e) === false) {
|
||||||
@@ -51,8 +53,10 @@ export const moveNodeWorkflow = mixins(nodeIndex).extend({
|
|||||||
|
|
||||||
this.$store.commit('setNodeViewMoveInProgress', true);
|
this.$store.commit('setNodeViewMoveInProgress', true);
|
||||||
|
|
||||||
this.moveLastPosition[0] = e.pageX;
|
const position = this.getMousePosition(e);
|
||||||
this.moveLastPosition[1] = e.pageY;
|
|
||||||
|
this.moveLastPosition[0] = position.x;
|
||||||
|
this.moveLastPosition[1] = position.y;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.$el.addEventListener('mousemove', this.mouseMoveNodeWorkflow);
|
this.$el.addEventListener('mousemove', this.mouseMoveNodeWorkflow);
|
||||||
@@ -72,6 +76,15 @@ export const moveNodeWorkflow = mixins(nodeIndex).extend({
|
|||||||
// Nothing else to do. Simply leave the node view at the current offset
|
// Nothing else to do. Simply leave the node view at the current offset
|
||||||
},
|
},
|
||||||
mouseMoveNodeWorkflow (e: MouseEvent) {
|
mouseMoveNodeWorkflow (e: MouseEvent) {
|
||||||
|
// @ts-ignore
|
||||||
|
if (e.target && !e.target.id.includes('node-view')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$store.getters.isActionActive('dragActive')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (e.buttons === 0) {
|
if (e.buttons === 0) {
|
||||||
// Mouse button is not pressed anymore so stop selection mode
|
// Mouse button is not pressed anymore so stop selection mode
|
||||||
// Happens normally when mouse leave the view pressed and then
|
// Happens normally when mouse leave the view pressed and then
|
||||||
@@ -84,9 +97,10 @@ export const moveNodeWorkflow = mixins(nodeIndex).extend({
|
|||||||
this.moveWorkflow(e);
|
this.moveWorkflow(e);
|
||||||
},
|
},
|
||||||
wheelMoveWorkflow (e: WheelEvent) {
|
wheelMoveWorkflow (e: WheelEvent) {
|
||||||
|
const normalized = normalizeWheel(e);
|
||||||
const offsetPosition = this.$store.getters.getNodeViewOffsetPosition;
|
const offsetPosition = this.$store.getters.getNodeViewOffsetPosition;
|
||||||
const nodeViewOffsetPositionX = offsetPosition[0] - e.deltaX;
|
const nodeViewOffsetPositionX = offsetPosition[0] - normalized.pixelX;
|
||||||
const nodeViewOffsetPositionY = offsetPosition[1] - e.deltaY;
|
const nodeViewOffsetPositionY = offsetPosition[1] - normalized.pixelY;
|
||||||
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [nodeViewOffsetPositionX, nodeViewOffsetPositionY], setStateDirty: true});
|
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [nodeViewOffsetPositionX, nodeViewOffsetPositionY], setStateDirty: true});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,20 +2,20 @@ import { IConnectionsUi, IEndpointOptions, INodeUi, XYPositon } from '@/Interfac
|
|||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
|
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
|
||||||
import { nodeIndex } from '@/components/mixins/nodeIndex';
|
import { nodeIndex } from '@/components/mixins/nodeIndex';
|
||||||
import { NODE_NAME_PREFIX } from '@/constants';
|
import { NODE_NAME_PREFIX } from '@/constants';
|
||||||
|
|
||||||
export const nodeBase = mixins(nodeIndex).extend({
|
export const nodeBase = mixins(
|
||||||
|
deviceSupportHelpers,
|
||||||
|
nodeIndex,
|
||||||
|
).extend({
|
||||||
mounted () {
|
mounted () {
|
||||||
// Initialize the node
|
// Initialize the node
|
||||||
if (this.data !== null) {
|
if (this.data !== null) {
|
||||||
this.__addNode(this.data);
|
this.__addNode(this.data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
data (): INodeUi {
|
data (): INodeUi {
|
||||||
return this.$store.getters.nodeByName(this.name);
|
return this.$store.getters.nodeByName(this.name);
|
||||||
@@ -26,9 +26,6 @@ export const nodeBase = mixins(nodeIndex).extend({
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
isMacOs (): boolean {
|
|
||||||
return /(ipad|iphone|ipod|mac)/i.test(navigator.platform);
|
|
||||||
},
|
|
||||||
nodeName (): string {
|
nodeName (): string {
|
||||||
return NODE_NAME_PREFIX + this.nodeIndex;
|
return NODE_NAME_PREFIX + this.nodeIndex;
|
||||||
},
|
},
|
||||||
@@ -336,26 +333,27 @@ export const nodeBase = mixins(nodeIndex).extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
|
touchEnd(e: MouseEvent) {
|
||||||
isCtrlKeyPressed (e: MouseEvent | KeyboardEvent): boolean {
|
if (this.isTouchDevice) {
|
||||||
if (this.isMacOs) {
|
if (this.$store.getters.isActionActive('dragActive')) {
|
||||||
return e.metaKey;
|
this.$store.commit('removeActiveAction', 'dragActive');
|
||||||
}
|
|
||||||
return e.ctrlKey;
|
|
||||||
},
|
|
||||||
|
|
||||||
mouseLeftClick (e: MouseEvent) {
|
|
||||||
if (this.$store.getters.isActionActive('dragActive')) {
|
|
||||||
this.$store.commit('removeActiveAction', 'dragActive');
|
|
||||||
} else {
|
|
||||||
if (this.isCtrlKeyPressed(e) === false) {
|
|
||||||
this.$emit('deselectAllNodes');
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (this.$store.getters.isNodeSelected(this.data.name)) {
|
},
|
||||||
this.$emit('deselectNode', this.name);
|
mouseLeftClick (e: MouseEvent) {
|
||||||
|
if (!this.isTouchDevice) {
|
||||||
|
if (this.$store.getters.isActionActive('dragActive')) {
|
||||||
|
this.$store.commit('removeActiveAction', 'dragActive');
|
||||||
} else {
|
} else {
|
||||||
this.$emit('nodeSelected', this.name);
|
if (this.isCtrlKeyPressed(e) === false) {
|
||||||
|
this.$emit('deselectAllNodes');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$store.getters.isNodeSelected(this.data.name)) {
|
||||||
|
this.$emit('deselectNode', this.name);
|
||||||
|
} else {
|
||||||
|
this.$emit('nodeSelected', this.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -152,6 +152,10 @@ export const restApi = Vue.extend({
|
|||||||
return self.restApi().makeRestApiRequest('GET', `/node-types`);
|
return self.restApi().makeRestApiRequest('GET', `/node-types`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getNodesInformation: (nodeList: string[]): Promise<INodeTypeDescription[]> => {
|
||||||
|
return self.restApi().makeRestApiRequest('POST', `/node-types`, {nodeNames: nodeList});
|
||||||
|
},
|
||||||
|
|
||||||
// Returns all the parameter options from the server
|
// Returns all the parameter options from the server
|
||||||
getNodeParameterOptions: (nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]> => {
|
getNodeParameterOptions: (nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]> => {
|
||||||
const sendData = {
|
const sendData = {
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ export const workflowHelpers = mixins(
|
|||||||
connectionInputData = [];
|
connectionInputData = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return workflow.getParameterValue(expression, runExecutionData, runIndex, itemIndex, activeNode.name, connectionInputData, true);
|
return workflow.expression.getParameterValue(expression, runExecutionData, runIndex, itemIndex, activeNode.name, connectionInputData, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Saves the currently loaded workflow to the database.
|
// Saves the currently loaded workflow to the database.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Vue from 'vue';
|
|||||||
import 'prismjs';
|
import 'prismjs';
|
||||||
import 'prismjs/themes/prism.css';
|
import 'prismjs/themes/prism.css';
|
||||||
import 'vue-prism-editor/dist/VuePrismEditor.css';
|
import 'vue-prism-editor/dist/VuePrismEditor.css';
|
||||||
|
import Vue2TouchEvents from 'vue2-touch-events';
|
||||||
|
|
||||||
import * as ElementUI from 'element-ui';
|
import * as ElementUI from 'element-ui';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -91,6 +92,9 @@ import {
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
|
|
||||||
import { store } from './store';
|
import { store } from './store';
|
||||||
|
|
||||||
|
Vue.use(Vue2TouchEvents);
|
||||||
|
|
||||||
Vue.use(ElementUI, { locale });
|
Vue.use(ElementUI, { locale });
|
||||||
|
|
||||||
library.add(faAngleDoubleLeft);
|
library.add(faAngleDoubleLeft);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
IConnection,
|
IConnection,
|
||||||
IConnections,
|
IConnections,
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
|
IDataObject,
|
||||||
INodeConnections,
|
INodeConnections,
|
||||||
INodeIssueData,
|
INodeIssueData,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
@@ -56,6 +57,7 @@ export const store = new Vuex.Store({
|
|||||||
executionTimeout: -1,
|
executionTimeout: -1,
|
||||||
maxExecutionTimeout: Number.MAX_SAFE_INTEGER,
|
maxExecutionTimeout: Number.MAX_SAFE_INTEGER,
|
||||||
versionCli: '0.0.0',
|
versionCli: '0.0.0',
|
||||||
|
oauthCallbackUrls: {},
|
||||||
workflowExecutionData: null as IExecutionResponse | null,
|
workflowExecutionData: null as IExecutionResponse | null,
|
||||||
lastSelectedNode: null as string | null,
|
lastSelectedNode: null as string | null,
|
||||||
lastSelectedNodeOutputIndex: null as number | null,
|
lastSelectedNodeOutputIndex: null as number | null,
|
||||||
@@ -535,6 +537,9 @@ export const store = new Vuex.Store({
|
|||||||
setVersionCli (state, version: string) {
|
setVersionCli (state, version: string) {
|
||||||
Vue.set(state, 'versionCli', version);
|
Vue.set(state, 'versionCli', version);
|
||||||
},
|
},
|
||||||
|
setOauthCallbackUrls(state, urls: IDataObject) {
|
||||||
|
Vue.set(state, 'oauthCallbackUrls', urls);
|
||||||
|
},
|
||||||
|
|
||||||
addNodeType (state, typeData: INodeTypeDescription) {
|
addNodeType (state, typeData: INodeTypeDescription) {
|
||||||
if (!typeData.hasOwnProperty('name')) {
|
if (!typeData.hasOwnProperty('name')) {
|
||||||
@@ -602,6 +607,14 @@ export const store = new Vuex.Store({
|
|||||||
Vue.set(state.workflow, 'settings', {});
|
Vue.set(state.workflow, 'settings', {});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateNodeTypes (state, nodeTypes: INodeTypeDescription[]) {
|
||||||
|
const updatedNodeNames = nodeTypes.map(node => node.name) as string[];
|
||||||
|
const oldNodesNotChanged = state.nodeTypes.filter(node => !updatedNodeNames.includes(node.name));
|
||||||
|
const updatedNodes = [...oldNodesNotChanged, ...nodeTypes];
|
||||||
|
Vue.set(state, 'nodeTypes', updatedNodes);
|
||||||
|
state.nodeTypes = updatedNodes;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
|
||||||
@@ -658,6 +671,9 @@ export const store = new Vuex.Store({
|
|||||||
versionCli: (state): string => {
|
versionCli: (state): string => {
|
||||||
return state.versionCli;
|
return state.versionCli;
|
||||||
},
|
},
|
||||||
|
oauthCallbackUrls: (state): object => {
|
||||||
|
return state.oauthCallbackUrls;
|
||||||
|
},
|
||||||
|
|
||||||
// Push Connection
|
// Push Connection
|
||||||
pushConnectionActive: (state): boolean => {
|
pushConnectionActive: (state): boolean => {
|
||||||
|
|||||||
@@ -3,11 +3,15 @@
|
|||||||
<div
|
<div
|
||||||
class="node-view-wrapper"
|
class="node-view-wrapper"
|
||||||
:class="workflowClasses"
|
:class="workflowClasses"
|
||||||
|
@touchstart="mouseDown"
|
||||||
|
@touchend="mouseUp"
|
||||||
|
@touchmove="mouseMoveNodeWorkflow"
|
||||||
@mousedown="mouseDown"
|
@mousedown="mouseDown"
|
||||||
|
v-touch:tap="touchTap"
|
||||||
@mouseup="mouseUp"
|
@mouseup="mouseUp"
|
||||||
@wheel="wheelScroll"
|
@wheel="wheelScroll"
|
||||||
>
|
>
|
||||||
<div class="node-view-background" :style="backgroundStyle"></div>
|
<div id="node-view-background" class="node-view-background" :style="backgroundStyle"></div>
|
||||||
<div id="node-view" class="node-view" :style="workflowStyle">
|
<div id="node-view" class="node-view" :style="workflowStyle">
|
||||||
<node
|
<node
|
||||||
v-for="nodeData in nodes"
|
v-for="nodeData in nodes"
|
||||||
@@ -356,14 +360,20 @@ export default mixins(
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
mouseDown (e: MouseEvent) {
|
touchTap (e: MouseEvent | TouchEvent) {
|
||||||
|
if (this.isTouchDevice) {
|
||||||
|
this.mouseDown(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mouseDown (e: MouseEvent | TouchEvent) {
|
||||||
// Save the location of the mouse click
|
// Save the location of the mouse click
|
||||||
|
const position = this.getMousePosition(e);
|
||||||
const offsetPosition = this.$store.getters.getNodeViewOffsetPosition;
|
const offsetPosition = this.$store.getters.getNodeViewOffsetPosition;
|
||||||
this.lastClickPosition[0] = e.pageX - offsetPosition[0];
|
this.lastClickPosition[0] = position.x - offsetPosition[0];
|
||||||
this.lastClickPosition[1] = e.pageY - offsetPosition[1];
|
this.lastClickPosition[1] = position.y - offsetPosition[1];
|
||||||
|
|
||||||
this.mouseDownMouseSelect(e);
|
this.mouseDownMouseSelect(e as MouseEvent);
|
||||||
this.mouseDownMoveWorkflow(e);
|
this.mouseDownMoveWorkflow(e as MouseEvent);
|
||||||
|
|
||||||
// Hide the node-creator
|
// Hide the node-creator
|
||||||
this.createNodeActive = false;
|
this.createNodeActive = false;
|
||||||
@@ -962,7 +972,7 @@ export default mixins(
|
|||||||
// If a node is active then add the new node directly after the current one
|
// If a node is active then add the new node directly after the current one
|
||||||
// newNodeData.position = [activeNode.position[0], activeNode.position[1] + 60];
|
// newNodeData.position = [activeNode.position[0], activeNode.position[1] + 60];
|
||||||
newNodeData.position = this.getNewNodePosition(
|
newNodeData.position = this.getNewNodePosition(
|
||||||
[lastSelectedNode.position[0] + 150, lastSelectedNode.position[1]],
|
[lastSelectedNode.position[0] + 200, lastSelectedNode.position[1]],
|
||||||
[100, 0],
|
[100, 0],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -1456,6 +1466,11 @@ export default mixins(
|
|||||||
[0, 150],
|
[0, 150],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (newNodeData.webhookId) {
|
||||||
|
// Make sure that the node gets a new unique webhook-ID
|
||||||
|
newNodeData.webhookId = uuidv4();
|
||||||
|
}
|
||||||
|
|
||||||
await this.addNodes([newNodeData]);
|
await this.addNodes([newNodeData]);
|
||||||
|
|
||||||
// Automatically deselect all nodes and select the current one and also active
|
// Automatically deselect all nodes and select the current one and also active
|
||||||
@@ -1593,6 +1608,11 @@ export default mixins(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Before proceeding we must check if all nodes contain the `properties` attribute.
|
||||||
|
// Nodes are loaded without this information so we must make sure that all nodes
|
||||||
|
// being added have this information.
|
||||||
|
await this.loadNodesProperties(nodes.map(node => node.type));
|
||||||
|
|
||||||
// Add the node to the node-list
|
// Add the node to the node-list
|
||||||
let nodeType: INodeTypeDescription | null;
|
let nodeType: INodeTypeDescription | null;
|
||||||
let foundNodeIssues: INodeIssues | null;
|
let foundNodeIssues: INodeIssues | null;
|
||||||
@@ -1703,6 +1723,9 @@ export default mixins(
|
|||||||
let oldName: string;
|
let oldName: string;
|
||||||
let newName: string;
|
let newName: string;
|
||||||
const createNodes: INode[] = [];
|
const createNodes: INode[] = [];
|
||||||
|
|
||||||
|
await this.loadNodesProperties(data.nodes.map(node => node.type));
|
||||||
|
|
||||||
data.nodes.forEach(node => {
|
data.nodes.forEach(node => {
|
||||||
if (nodeTypesCount[node.type] !== undefined) {
|
if (nodeTypesCount[node.type] !== undefined) {
|
||||||
if (nodeTypesCount[node.type].exist >= nodeTypesCount[node.type].max) {
|
if (nodeTypesCount[node.type].exist >= nodeTypesCount[node.type].max) {
|
||||||
@@ -1745,6 +1768,10 @@ export default mixins(
|
|||||||
for (type of Object.keys(currentConnections[sourceNode])) {
|
for (type of Object.keys(currentConnections[sourceNode])) {
|
||||||
connection[type] = [];
|
connection[type] = [];
|
||||||
for (sourceIndex = 0; sourceIndex < currentConnections[sourceNode][type].length; sourceIndex++) {
|
for (sourceIndex = 0; sourceIndex < currentConnections[sourceNode][type].length; sourceIndex++) {
|
||||||
|
if (!currentConnections[sourceNode][type][sourceIndex]) {
|
||||||
|
// There is so something wrong with the data so ignore
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const nodeSourceConnections = [];
|
const nodeSourceConnections = [];
|
||||||
for (connectionIndex = 0; connectionIndex < currentConnections[sourceNode][type][sourceIndex].length; connectionIndex++) {
|
for (connectionIndex = 0; connectionIndex < currentConnections[sourceNode][type][sourceIndex].length; connectionIndex++) {
|
||||||
const nodeConnection: NodeInputConnections = [];
|
const nodeConnection: NodeInputConnections = [];
|
||||||
@@ -1908,6 +1935,7 @@ export default mixins(
|
|||||||
this.$store.commit('setExecutionTimeout', settings.executionTimeout);
|
this.$store.commit('setExecutionTimeout', settings.executionTimeout);
|
||||||
this.$store.commit('setMaxExecutionTimeout', settings.maxExecutionTimeout);
|
this.$store.commit('setMaxExecutionTimeout', settings.maxExecutionTimeout);
|
||||||
this.$store.commit('setVersionCli', settings.versionCli);
|
this.$store.commit('setVersionCli', settings.versionCli);
|
||||||
|
this.$store.commit('setOauthCallbackUrls', settings.oauthCallbackUrls);
|
||||||
},
|
},
|
||||||
async loadNodeTypes (): Promise<void> {
|
async loadNodeTypes (): Promise<void> {
|
||||||
const nodeTypes = await this.restApi().getNodeTypes();
|
const nodeTypes = await this.restApi().getNodeTypes();
|
||||||
@@ -1921,6 +1949,17 @@ export default mixins(
|
|||||||
const credentials = await this.restApi().getAllCredentials();
|
const credentials = await this.restApi().getAllCredentials();
|
||||||
this.$store.commit('setCredentials', credentials);
|
this.$store.commit('setCredentials', credentials);
|
||||||
},
|
},
|
||||||
|
async loadNodesProperties(nodeNames: string[]): Promise<void> {
|
||||||
|
const allNodes = this.$store.getters.allNodeTypes;
|
||||||
|
const nodesToBeFetched = allNodes.filter((node: INodeTypeDescription) => nodeNames.includes(node.name) && !node.hasOwnProperty('properties')).map((node: INodeTypeDescription) => node.name) as string[];
|
||||||
|
if (nodesToBeFetched.length > 0) {
|
||||||
|
// Only call API if node information is actually missing
|
||||||
|
this.startLoading();
|
||||||
|
const nodeInfo = await this.restApi().getNodesInformation(nodesToBeFetched);
|
||||||
|
this.$store.commit('updateNodeTypes', nodeInfo);
|
||||||
|
this.stopLoading();
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
async mounted () {
|
async mounted () {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
"forin": true,
|
"forin": true,
|
||||||
"jsdoc-format": true,
|
"jsdoc-format": true,
|
||||||
"label-position": true,
|
"label-position": true,
|
||||||
|
"indent": [true, "tabs", 2],
|
||||||
"member-access": [
|
"member-access": [
|
||||||
true,
|
true,
|
||||||
"no-public"
|
"no-public"
|
||||||
@@ -83,6 +84,18 @@
|
|||||||
"ignore-bound-class-methods"
|
"ignore-bound-class-methods"
|
||||||
],
|
],
|
||||||
"switch-default": true,
|
"switch-default": true,
|
||||||
|
"trailing-comma": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"multiline": {
|
||||||
|
"objects": "always",
|
||||||
|
"arrays": "always",
|
||||||
|
"functions": "always",
|
||||||
|
"typeLiterals": "ignore"
|
||||||
|
},
|
||||||
|
"esSpecCompliant": true
|
||||||
|
}
|
||||||
|
],
|
||||||
"triple-equals": [
|
"triple-equals": [
|
||||||
true,
|
true,
|
||||||
"allow-null-check"
|
"allow-null-check"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Condition notice.
|
|||||||
|
|
||||||
Software: n8n
|
Software: n8n
|
||||||
|
|
||||||
License: Apache 2.0
|
License: Apache 2.0 with Commons Clause
|
||||||
|
|
||||||
Licensor: n8n GmbH
|
Licensor: n8n GmbH
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n-node-dev",
|
"name": "n8n-node-dev",
|
||||||
"version": "0.10.0",
|
"version": "0.11.0",
|
||||||
"description": "CLI to simplify n8n credentials/node development",
|
"description": "CLI to simplify n8n credentials/node development",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
"postpack": "rm -f oclif.manifest.json",
|
"postpack": "rm -f oclif.manifest.json",
|
||||||
"prepack": "echo \"Building project...\" && rm -rf dist && tsc -b && oclif-dev manifest",
|
"prepack": "echo \"Building project...\" && rm -rf dist && tsc -b && oclif-dev manifest",
|
||||||
"tslint": "tslint -p tsconfig.json -c tslint.json",
|
"tslint": "tslint -p tsconfig.json -c tslint.json",
|
||||||
|
"tslintfix": "tslint --fix -p tsconfig.json -c tslint.json",
|
||||||
"watch": "tsc --watch"
|
"watch": "tsc --watch"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -54,15 +55,16 @@
|
|||||||
"@oclif/command": "^1.5.18",
|
"@oclif/command": "^1.5.18",
|
||||||
"@oclif/errors": "^1.2.2",
|
"@oclif/errors": "^1.2.2",
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.17.6",
|
||||||
"@types/node": "^14.0.27",
|
"@types/node": "14.0.27",
|
||||||
"change-case": "^4.1.1",
|
"change-case": "^4.1.1",
|
||||||
"copyfiles": "^2.1.1",
|
"copyfiles": "^2.1.1",
|
||||||
"inquirer": "^7.0.1",
|
"inquirer": "^7.0.1",
|
||||||
"n8n-core": "^0.44.0",
|
"n8n-core": "^0.48.0",
|
||||||
"n8n-workflow": "^0.39.0",
|
"n8n-workflow": "^0.42.0",
|
||||||
|
"oauth-1.0a": "^2.2.6",
|
||||||
"replace-in-file": "^6.0.0",
|
"replace-in-file": "^6.0.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"tmp-promise": "^2.0.2",
|
"tmp-promise": "^2.0.2",
|
||||||
"typescript": "~3.7.4"
|
"typescript": "~3.9.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export async function createTemplate(sourceFilePath: string, destinationFilePath
|
|||||||
// Replace the variables in the template file
|
// Replace the variables in the template file
|
||||||
const options: ReplaceInFileConfig = {
|
const options: ReplaceInFileConfig = {
|
||||||
files: [
|
files: [
|
||||||
destinationFilePath
|
destinationFilePath,
|
||||||
],
|
],
|
||||||
from: [],
|
from: [],
|
||||||
to: [],
|
to: [],
|
||||||
|
|||||||
@@ -46,6 +46,11 @@
|
|||||||
"forin": true,
|
"forin": true,
|
||||||
"jsdoc-format": true,
|
"jsdoc-format": true,
|
||||||
"label-position": true,
|
"label-position": true,
|
||||||
|
"indent": [
|
||||||
|
true,
|
||||||
|
"tabs",
|
||||||
|
2
|
||||||
|
],
|
||||||
"member-access": [
|
"member-access": [
|
||||||
true,
|
true,
|
||||||
"no-public"
|
"no-public"
|
||||||
@@ -60,6 +65,13 @@
|
|||||||
"no-default-export": true,
|
"no-default-export": true,
|
||||||
"no-duplicate-variable": true,
|
"no-duplicate-variable": true,
|
||||||
"no-inferrable-types": true,
|
"no-inferrable-types": true,
|
||||||
|
"ordered-imports": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"import-sources-order": "any",
|
||||||
|
"named-imports-order": "case-insensitive"
|
||||||
|
}
|
||||||
|
],
|
||||||
"no-namespace": [
|
"no-namespace": [
|
||||||
true,
|
true,
|
||||||
"allow-declarations"
|
"allow-declarations"
|
||||||
@@ -82,6 +94,18 @@
|
|||||||
"ignore-bound-class-methods"
|
"ignore-bound-class-methods"
|
||||||
],
|
],
|
||||||
"switch-default": true,
|
"switch-default": true,
|
||||||
|
"trailing-comma": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"multiline": {
|
||||||
|
"objects": "always",
|
||||||
|
"arrays": "always",
|
||||||
|
"functions": "always",
|
||||||
|
"typeLiterals": "ignore"
|
||||||
|
},
|
||||||
|
"esSpecCompliant": true
|
||||||
|
}
|
||||||
|
],
|
||||||
"triple-equals": [
|
"triple-equals": [
|
||||||
true,
|
true,
|
||||||
"allow-null-check"
|
"allow-null-check"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Condition notice.
|
|||||||
|
|
||||||
Software: n8n
|
Software: n8n
|
||||||
|
|
||||||
License: Apache 2.0
|
License: Apache 2.0 with Commons Clause
|
||||||
|
|
||||||
Licensor: n8n GmbH
|
Licensor: n8n GmbH
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export class AcuitySchedulingOAuth2Api implements ICredentialType {
|
|||||||
name: 'scope',
|
name: 'scope',
|
||||||
type: 'hidden' as NodePropertyTypes,
|
type: 'hidden' as NodePropertyTypes,
|
||||||
default: 'api-v1',
|
default: 'api-v1',
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Auth URI Query Parameters',
|
displayName: 'Auth URI Query Parameters',
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
NodePropertyTypes,
|
NodePropertyTypes,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
|
||||||
export class AsanaApi implements ICredentialType {
|
export class AsanaApi implements ICredentialType {
|
||||||
name = 'asanaApi';
|
name = 'asanaApi';
|
||||||
displayName = 'Asana API';
|
displayName = 'Asana API';
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class AsanaOAuth2Api implements ICredentialType {
|
||||||
|
name = 'asanaOAuth2Api';
|
||||||
|
extends = [
|
||||||
|
'oAuth2Api',
|
||||||
|
];
|
||||||
|
displayName = 'Asana OAuth2 API';
|
||||||
|
documentationUrl = 'asana';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'Authorization URL',
|
||||||
|
name: 'authUrl',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'https://app.asana.com/-/oauth_authorize',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Access Token URL',
|
||||||
|
name: 'accessTokenUrl',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'https://app.asana.com/-/oauth_token',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Scope',
|
||||||
|
name: 'scope',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Auth URI Query Parameters',
|
||||||
|
name: 'authQueryParameters',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Authentication',
|
||||||
|
name: 'authentication',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'body',
|
||||||
|
description: 'Resource to consume.',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
18
packages/nodes-base/credentials/AutomizyApi.credentials.ts
Normal file
18
packages/nodes-base/credentials/AutomizyApi.credentials.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class AutomizyApi implements ICredentialType {
|
||||||
|
name = 'automizyApi';
|
||||||
|
displayName = 'Automizy API';
|
||||||
|
documentationUrl = 'automizy';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'API Token',
|
||||||
|
name: 'apiToken',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -6,9 +6,9 @@ import {
|
|||||||
|
|
||||||
export class BitlyOAuth2Api implements ICredentialType {
|
export class BitlyOAuth2Api implements ICredentialType {
|
||||||
name = 'bitlyOAuth2Api';
|
name = 'bitlyOAuth2Api';
|
||||||
displayName = 'Bitly OAuth2 API';
|
displayName = 'Bitly OAuth2 API';
|
||||||
documentationUrl = 'bitly';
|
documentationUrl = 'bitly';
|
||||||
extends = [
|
extends = [
|
||||||
'oAuth2Api',
|
'oAuth2Api',
|
||||||
];
|
];
|
||||||
properties = [
|
properties = [
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class ClickUpOAuth2Api implements ICredentialType {
|
||||||
|
name = 'clickUpOAuth2Api';
|
||||||
|
extends = [
|
||||||
|
'oAuth2Api',
|
||||||
|
];
|
||||||
|
displayName = 'ClickUp OAuth2 API';
|
||||||
|
documentationUrl = 'clickUp';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'Authorization URL',
|
||||||
|
name: 'authUrl',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'https://app.clickup.com/api',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Access Token URL',
|
||||||
|
name: 'accessTokenUrl',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'https://api.clickup.com/api/v2/oauth/token',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Scope',
|
||||||
|
name: 'scope',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Auth URI Query Parameters',
|
||||||
|
name: 'authQueryParameters',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Authentication',
|
||||||
|
name: 'authentication',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'body',
|
||||||
|
description: 'Resource to consume.',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
export class ContentfulApi implements ICredentialType {
|
export class ContentfulApi implements ICredentialType {
|
||||||
name = 'contentfulApi';
|
name = 'contentfulApi';
|
||||||
displayName = 'Contenful API';
|
displayName = 'Contenful API';
|
||||||
|
documentationUrl = 'contentful';
|
||||||
properties = [
|
properties = [
|
||||||
{
|
{
|
||||||
displayName: 'Space ID',
|
displayName: 'Space ID',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
export class ConvertKitApi implements ICredentialType {
|
export class ConvertKitApi implements ICredentialType {
|
||||||
name = 'convertKitApi';
|
name = 'convertKitApi';
|
||||||
displayName = 'ConvertKit API';
|
displayName = 'ConvertKit API';
|
||||||
|
documentationUrl = 'convertKit';
|
||||||
properties = [
|
properties = [
|
||||||
{
|
{
|
||||||
displayName: 'API Secret',
|
displayName: 'API Secret',
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export class DisqusApi implements ICredentialType {
|
|||||||
name: 'accessToken',
|
name: 'accessToken',
|
||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Visit your account details page, and grab the Access Token. See <a href="https://disqus.com/api/docs/auth/">Disqus auth</a>.'
|
description: 'Visit your account details page, and grab the Access Token. See <a href="https://disqus.com/api/docs/auth/">Disqus auth</a>.',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export class DriftApi implements ICredentialType {
|
|||||||
name: 'accessToken',
|
name: 'accessToken',
|
||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Visit your account details page, and grab the Access Token. See <a href="https://devdocs.drift.com/docs/quick-start">Drift auth</a>.'
|
description: 'Visit your account details page, and grab the Access Token. See <a href="https://devdocs.drift.com/docs/quick-start">Drift auth</a>.',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ export class DropboxOAuth2Api implements ICredentialType {
|
|||||||
type: 'hidden' as NodePropertyTypes,
|
type: 'hidden' as NodePropertyTypes,
|
||||||
default: 'https://api.dropboxapi.com/oauth2/token',
|
default: 'https://api.dropboxapi.com/oauth2/token',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Scope',
|
displayName: 'Scope',
|
||||||
name: 'scope',
|
name: 'scope',
|
||||||
type: 'hidden' as NodePropertyTypes,
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export class EventbriteOAuth2Api implements ICredentialType {
|
|||||||
displayName: 'Authentication',
|
displayName: 'Authentication',
|
||||||
name: 'authentication',
|
name: 'authentication',
|
||||||
type: 'hidden' as NodePropertyTypes,
|
type: 'hidden' as NodePropertyTypes,
|
||||||
default: 'body'
|
default: 'body',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class FreshdeskApi implements ICredentialType {
|
|||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
placeholder: 'company',
|
placeholder: 'company',
|
||||||
description: 'If the URL you get displayed on Freshdesk is "https://company.freshdesk.com" enter "company"',
|
description: 'If the URL you get displayed on Freshdesk is "https://company.freshdesk.com" enter "company"',
|
||||||
default: ''
|
default: '',
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export class Ftp implements ICredentialType {
|
|||||||
required: true,
|
required: true,
|
||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
default: '',
|
default: '',
|
||||||
placeholder: 'localhost'
|
placeholder: 'localhost',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Port',
|
displayName: 'Port',
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
const scopes = [
|
||||||
|
'https://www.googleapis.com/auth/admin.directory.group',
|
||||||
|
'https://www.googleapis.com/auth/admin.directory.user',
|
||||||
|
'https://www.googleapis.com/auth/admin.directory.domain.readonly',
|
||||||
|
'https://www.googleapis.com/auth/admin.directory.userschema.readonly',
|
||||||
|
];
|
||||||
|
|
||||||
|
export class GSuiteAdminOAuth2Api implements ICredentialType {
|
||||||
|
name = 'gSuiteAdminOAuth2Api';
|
||||||
|
extends = [
|
||||||
|
'googleOAuth2Api',
|
||||||
|
];
|
||||||
|
displayName = 'G Suite Admin OAuth2 API';
|
||||||
|
documentationUrl = 'google';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'Scope',
|
||||||
|
name: 'scope',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: scopes.join(' '),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ export class GitlabApi implements ICredentialType {
|
|||||||
displayName: 'Gitlab Server',
|
displayName: 'Gitlab Server',
|
||||||
name: 'server',
|
name: 'server',
|
||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
default: 'https://gitlab.com'
|
default: 'https://gitlab.com',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Access Token',
|
displayName: 'Access Token',
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export class GitlabOAuth2Api implements ICredentialType {
|
|||||||
displayName: 'Gitlab Server',
|
displayName: 'Gitlab Server',
|
||||||
name: 'server',
|
name: 'server',
|
||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
default: 'https://gitlab.com'
|
default: 'https://gitlab.com',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Authorization URL',
|
displayName: 'Authorization URL',
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export class GoogleOAuth2Api implements ICredentialType {
|
|||||||
displayName: 'Auth URI Query Parameters',
|
displayName: 'Auth URI Query Parameters',
|
||||||
name: 'authQueryParameters',
|
name: 'authQueryParameters',
|
||||||
type: 'hidden' as NodePropertyTypes,
|
type: 'hidden' as NodePropertyTypes,
|
||||||
default: 'access_type=offline',
|
default: 'access_type=offline&prompt=consent',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Authentication',
|
displayName: 'Authentication',
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
const scopes = [
|
const scopes = [
|
||||||
'https://www.googleapis.com/auth/drive',
|
|
||||||
'https://www.googleapis.com/auth/drive.file',
|
'https://www.googleapis.com/auth/drive.file',
|
||||||
'https://www.googleapis.com/auth/spreadsheets',
|
'https://www.googleapis.com/auth/spreadsheets',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class GoogleTasksOAuth2Api implements ICredentialType {
|
|||||||
displayName: 'Scope',
|
displayName: 'Scope',
|
||||||
name: 'scope',
|
name: 'scope',
|
||||||
type: 'hidden' as NodePropertyTypes,
|
type: 'hidden' as NodePropertyTypes,
|
||||||
default: scopes.join(' ')
|
default: scopes.join(' '),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
const scopes = [
|
||||||
|
'https://www.googleapis.com/auth/cloud-translation',
|
||||||
|
];
|
||||||
|
|
||||||
|
export class GoogleTranslateOAuth2Api implements ICredentialType {
|
||||||
|
name = 'googleTranslateOAuth2Api';
|
||||||
|
extends = [
|
||||||
|
'googleOAuth2Api',
|
||||||
|
];
|
||||||
|
displayName = 'Google Translate OAuth2 API';
|
||||||
|
documentationUrl = 'google';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'Scope',
|
||||||
|
name: 'scope',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: scopes.join(' '),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -13,14 +13,14 @@ export class HarvestApi implements ICredentialType {
|
|||||||
name: 'accountId',
|
name: 'accountId',
|
||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Visit your account details page, and grab the Account ID. See <a href="https://help.getharvest.com/api-v2/authentication-api/authentication/authentication/">Harvest Personal Access Tokens</a>.'
|
description: 'Visit your account details page, and grab the Account ID. See <a href="https://help.getharvest.com/api-v2/authentication-api/authentication/authentication/">Harvest Personal Access Tokens</a>.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Access Token',
|
displayName: 'Access Token',
|
||||||
name: 'accessToken',
|
name: 'accessToken',
|
||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Visit your account details page, and grab the Access Token. See <a href="https://help.getharvest.com/api-v2/authentication-api/authentication/authentication/">Harvest Personal Access Tokens</a>.'
|
description: 'Visit your account details page, and grab the Access Token. See <a href="https://help.getharvest.com/api-v2/authentication-api/authentication/authentication/">Harvest Personal Access Tokens</a>.',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
|
|
||||||
export class HubspotDeveloperApi implements ICredentialType {
|
export class HubspotDeveloperApi implements ICredentialType {
|
||||||
name = 'hubspotDeveloperApi';
|
name = 'hubspotDeveloperApi';
|
||||||
displayName = 'Hubspot API';
|
displayName = 'Hubspot Developer API';
|
||||||
documentationUrl = 'hubspot';
|
documentationUrl = 'hubspot';
|
||||||
properties = [
|
properties = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,11 +44,11 @@ export class HubspotOAuth2Api implements ICredentialType {
|
|||||||
default: 'grant_type=authorization_code',
|
default: 'grant_type=authorization_code',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Authentication',
|
displayName: 'Authentication',
|
||||||
name: 'authentication',
|
name: 'authentication',
|
||||||
type: 'hidden' as NodePropertyTypes,
|
type: 'hidden' as NodePropertyTypes,
|
||||||
default: 'body',
|
default: 'body',
|
||||||
description: 'Resource to consume.',
|
description: 'Resource to consume.',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user