diff --git a/.gitignore b/.gitignore index 1b2f93c3f3..b3eac39207 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ google-generated-credentials.json _START_PACKAGE .env .vscode +.idea diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index ab75bb0527..ca9a5d10ca 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -8,7 +8,7 @@ const config = convict({ database: { type: { doc: 'Type of database to use', - format: ['sqlite', 'mongodb', 'postgresdb'], + format: ['sqlite', 'mongodb', 'mysqldb', 'postgresdb'], default: 'sqlite', env: 'DB_TYPE' }, @@ -52,6 +52,38 @@ const config = convict({ env: 'DB_POSTGRESDB_USER' }, }, + mysqldb: { + database: { + doc: 'MySQL Database', + format: String, + default: 'n8n', + env: 'DB_MYSQLDB_DATABASE' + }, + host: { + doc: 'MySQL Host', + format: String, + default: 'localhost', + env: 'DB_MYSQLDB_HOST' + }, + password: { + doc: 'MySQL Password', + format: String, + default: '', + env: 'DB_MYSQLDB_PASSWORD' + }, + port: { + doc: 'MySQL Port', + format: Number, + default: 5432, + env: 'DB_MYSQLDB_PORT' + }, + user: { + doc: 'MySQL User', + format: String, + default: 'root', + env: 'DB_MYSQLDB_USER' + }, + }, }, executions: { diff --git a/packages/cli/src/Db.ts b/packages/cli/src/Db.ts index 58d234ef3c..3864304748 100644 --- a/packages/cli/src/Db.ts +++ b/packages/cli/src/Db.ts @@ -18,6 +18,7 @@ import { MongoDb, PostgresDb, SQLite, + MySQLDb, } from './databases'; export let collections: IDatabaseCollections = { @@ -36,33 +37,53 @@ export async function init(synchronize?: boolean): Promise let connectionOptions: ConnectionOptions; let dbNotExistError: string | undefined; - if (dbType === 'mongodb') { - entities = MongoDb; - connectionOptions = { - type: 'mongodb', - url: await GenericHelpers.getConfigValue('database.mongodb.connectionUrl') as string, - useNewUrlParser: true, - }; - } else if (dbType === 'postgresdb') { - dbNotExistError = 'does not exist'; - entities = PostgresDb; - connectionOptions = { - type: 'postgres', - database: await GenericHelpers.getConfigValue('database.postgresdb.database') as string, - host: await GenericHelpers.getConfigValue('database.postgresdb.host') as string, - password: await GenericHelpers.getConfigValue('database.postgresdb.password') as string, - port: await GenericHelpers.getConfigValue('database.postgresdb.port') as number, - username: await GenericHelpers.getConfigValue('database.postgresdb.user') as string, - }; - } else if (dbType === 'sqlite') { - dbNotExistError = 'no such table:'; - entities = SQLite; - connectionOptions = { - type: 'sqlite', - database: path.join(n8nFolder, 'database.sqlite'), - }; - } else { - throw new Error(`The database "${dbType}" is currently not supported!`); + switch (dbType) { + case 'mongodb': + entities = MongoDb; + connectionOptions = { + type: 'mongodb', + url: await GenericHelpers.getConfigValue('database.mongodb.connectionUrl') as string, + useNewUrlParser: true, + }; + break; + + case 'postgresdb': + dbNotExistError = 'does not exist'; + entities = PostgresDb; + connectionOptions = { + type: 'postgres', + database: await GenericHelpers.getConfigValue('database.postgresdb.database') as string, + host: await GenericHelpers.getConfigValue('database.postgresdb.host') as string, + password: await GenericHelpers.getConfigValue('database.postgresdb.password') as string, + port: await GenericHelpers.getConfigValue('database.postgresdb.port') as number, + username: await GenericHelpers.getConfigValue('database.postgresdb.user') as string, + }; + break; + + case 'mysqldb': + dbNotExistError = 'does not exist'; + entities = MySQLDb; + connectionOptions = { + type: 'mysql', + database: await GenericHelpers.getConfigValue('database.mysqldb.database') as string, + host: await GenericHelpers.getConfigValue('database.mysqldb.host') as string, + password: await GenericHelpers.getConfigValue('database.mysqldb.password') as string, + port: await GenericHelpers.getConfigValue('database.mysqldb.port') as number, + username: await GenericHelpers.getConfigValue('database.mysqldb.user') as string + }; + break; + + case 'sqlite': + dbNotExistError = 'no such table:'; + entities = SQLite; + connectionOptions = { + type: 'sqlite', + database: path.join(n8nFolder, 'database.sqlite'), + }; + break; + + default: + throw new Error(`The database "${dbType}" is currently not supported!`); } Object.assign(connectionOptions, { diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index dbc39dccaf..33d7b02e7f 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -87,7 +87,7 @@ export interface ICredentialsDecryptedResponse extends ICredentialsDecryptedDb { id: string; } -export type DatabaseType = 'mongodb' | 'postgresdb' | 'sqlite'; +export type DatabaseType = 'mongodb' | 'postgresdb' | 'mysqldb' | 'sqlite'; export type SaveExecutionDataType = 'all' | 'none'; export interface IExecutionBase { diff --git a/packages/cli/src/TestWebhooks.ts b/packages/cli/src/TestWebhooks.ts index 8631f259ab..12a2ef92e2 100644 --- a/packages/cli/src/TestWebhooks.ts +++ b/packages/cli/src/TestWebhooks.ts @@ -208,26 +208,32 @@ export class TestWebhooks { } const nodeTypes = NodeTypes(); - const findQuery = { - where: { - id: findIn(this.activeWebhooks.getWorkflowIds()) - }, - } as FindManyOptions; + /* + * Here I check first if WorkflowIds is empty. This is done because when entering an empty array for TypeORM's In option, a syntax error is generated in MySQL. + * Because the SQL is: ... FROM `workflow_entity`` WorkflowEntity` WHERE `WorkflowEntity`.`id` IN () + * + * The empty IN function is not accepted in MySQL. + */ + const WorkflowIds = this.activeWebhooks.getWorkflowIds(); + if (WorkflowIds.length > 0) { + const findQuery = { + where: { + id: findIn(WorkflowIds) + }, + } as FindManyOptions; - const workflowsDb = await Db.collections.Workflow!.find(findQuery); - const workflows: Workflow[] = []; - for (const workflowData of workflowsDb) { - const workflow = new Workflow(workflowData.id.toString(), workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); - workflows.push(workflow); + const workflowsDb = await Db.collections.Workflow!.find(findQuery); + const workflows: Workflow[] = []; + for (const workflowData of workflowsDb) { + const workflow = new Workflow(workflowData.id.toString(), workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); + workflows.push(workflow); + } + + return this.activeWebhooks.removeAll(workflows); } - - return this.activeWebhooks.removeAll(workflows); } - } - - let testWebhooksInstance: TestWebhooks | undefined; export function getInstance(): TestWebhooks { diff --git a/packages/cli/src/databases/index.ts b/packages/cli/src/databases/index.ts index 9263d9230f..48ba5c86eb 100644 --- a/packages/cli/src/databases/index.ts +++ b/packages/cli/src/databases/index.ts @@ -1,9 +1,11 @@ import * as MongoDb from './mongodb'; import * as PostgresDb from './postgresdb'; import * as SQLite from './sqlite'; +import * as MySQLDb from './mysqldb'; export { MongoDb, PostgresDb, SQLite, + MySQLDb, }; diff --git a/packages/cli/src/databases/mysqldb/CredentialsEntity.ts b/packages/cli/src/databases/mysqldb/CredentialsEntity.ts new file mode 100644 index 0000000000..e3ef375254 --- /dev/null +++ b/packages/cli/src/databases/mysqldb/CredentialsEntity.ts @@ -0,0 +1,44 @@ +import { + ICredentialNodeAccess, +} from 'n8n-workflow'; + +import { + ICredentialsDb, +} from '../../'; + +import { + Column, + Entity, + Index, + PrimaryGeneratedColumn, +} from 'typeorm'; + +@Entity() +export class CredentialsEntity implements ICredentialsDb { + + @PrimaryGeneratedColumn() + id: number; + + @Column({ + length: 128 + }) + name: string; + + @Column('text') + data: string; + + @Index() + @Column({ + length: 32 + }) + type: string; + + @Column('json') + nodesAccess: ICredentialNodeAccess[]; + + @Column('timestamp') + createdAt: Date; + + @Column('timestamp') + updatedAt: Date; +} diff --git a/packages/cli/src/databases/mysqldb/ExecutionEntity.ts b/packages/cli/src/databases/mysqldb/ExecutionEntity.ts new file mode 100644 index 0000000000..8a7f691f0f --- /dev/null +++ b/packages/cli/src/databases/mysqldb/ExecutionEntity.ts @@ -0,0 +1,51 @@ +import { + WorkflowExecuteMode, +} from 'n8n-workflow'; + +import { + IExecutionFlattedDb, + IWorkflowDb, +} from '../../'; + +import { + Column, + Entity, + Index, + PrimaryGeneratedColumn, +} from 'typeorm'; + + +@Entity() +export class ExecutionEntity implements IExecutionFlattedDb { + + @PrimaryGeneratedColumn() + id: number; + + @Column('text') + data: string; + + @Column() + finished: boolean; + + @Column('varchar') + mode: WorkflowExecuteMode; + + @Column({ nullable: true }) + retryOf: string; + + @Column({ nullable: true }) + retrySuccessId: string; + + @Column('timestamp') + startedAt: Date; + + @Column('timestamp') + stoppedAt: Date; + + @Column('json') + workflowData: IWorkflowDb; + + @Index() + @Column({ nullable: true }) + workflowId: string; +} diff --git a/packages/cli/src/databases/mysqldb/WorkflowEntity.ts b/packages/cli/src/databases/mysqldb/WorkflowEntity.ts new file mode 100644 index 0000000000..3f870b929b --- /dev/null +++ b/packages/cli/src/databases/mysqldb/WorkflowEntity.ts @@ -0,0 +1,55 @@ +import { + IConnections, + IDataObject, + INode, + IWorkflowSettings, +} from 'n8n-workflow'; + +import { + IWorkflowDb, +} from '../../'; + +import { + Column, + Entity, + PrimaryGeneratedColumn, +} from 'typeorm'; + +@Entity() +export class WorkflowEntity implements IWorkflowDb { + + @PrimaryGeneratedColumn() + id: number; + + @Column({ + length: 128 + }) + name: string; + + @Column() + active: boolean; + + @Column('json') + nodes: INode[]; + + @Column('json') + connections: IConnections; + + @Column('timestamp') + createdAt: Date; + + @Column('timestamp') + updatedAt: Date; + + @Column({ + type: 'json', + nullable: true, + }) + settings?: IWorkflowSettings; + + @Column({ + type: 'json', + nullable: true, + }) + staticData?: IDataObject; +} diff --git a/packages/cli/src/databases/mysqldb/index.ts b/packages/cli/src/databases/mysqldb/index.ts new file mode 100644 index 0000000000..164d67fd0c --- /dev/null +++ b/packages/cli/src/databases/mysqldb/index.ts @@ -0,0 +1,3 @@ +export * from './CredentialsEntity'; +export * from './ExecutionEntity'; +export * from './WorkflowEntity'; diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index e2ce2b51f6..ccd63f549c 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -17,7 +17,7 @@ "build": "vue-cli-service build", "dev": "npm run serve", "lint": "vue-cli-service lint", - "serve": "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", "tslint": "tslint -p tsconfig.json -c tslint.json", "test:e2e": "vue-cli-service test:e2e",