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/docker/images/n8n/README.md b/docker/images/n8n/README.md index 16d12e226d..984a024848 100644 --- a/docker/images/n8n/README.md +++ b/docker/images/n8n/README.md @@ -175,6 +175,31 @@ docker run -it --rm \ A full working setup with docker-compose can be found [here](https://github.com/n8n-io/n8n/blob/master/docker/compose/withPostgres/README.md) +#### Use with MySQL + +Replace the following placeholders with the actual data: + - MYSQLDB_DATABASE + - MYSQLDB_HOST + - MYSQLDB_PASSWORD + - MYSQLDB_PORT + - MYSQLDB_USER + +``` +docker run -it --rm \ + --name n8n \ + -p 5678:5678 \ + -e DB_TYPE=mysqldb \ + -e DB_MYSQLDB_DATABASE= \ + -e DB_MYSQLDB_HOST= \ + -e DB_MYSQLDB_PORT= \ + -e DB_MYSQLDB_USER= \ + -e DB_MYSQLDB_PASSWORD= \ + -v ~/.n8n:/root/.n8n \ + n8nio/n8n \ + n8n start +``` + + ## Passing Sensitive Data via File To avoid passing sensitive information via environment variables "_FILE" may be diff --git a/docs/database.md b/docs/database.md index e55669c294..1410374a26 100644 --- a/docs/database.md +++ b/docs/database.md @@ -51,6 +51,30 @@ export DB_POSTGRESDB_PASSWORD=n8n n8n start ``` +## MySQL + +The compatibility with MySQL was tested, even so, it is advisable to observe the operation of the application with this DB, as it is a new option, recently added. If you spot any problems, feel free to submit a PR. + +To use MySQL as database you can provide the following environment variables: + - `DB_TYPE=mysqldb` + - `DB_MYSQLDB_DATABASE` (default: 'n8n') + - `DB_MYSQLDB_HOST` (default: 'localhost') + - `DB_MYSQLDB_PORT` (default: 3306) + - `DB_MYSQLDB_USER` (default: 'root') + - `DB_MYSQLDB_PASSWORD` (default: empty) + + +```bash +export DB_TYPE=mysqldb +export DB_MYSQLDB_DATABASE=n8n +export DB_MYSQLDB_HOST=mysqldb +export DB_MYSQLDB_PORT=3306 +export DB_MYSQLDB_USER=n8n +export DB_MYSQLDB_PASSWORD=n8n + +n8n start +``` + ## SQLite The default database which gets used if no other one is defined. diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index ab75bb0527..b54fc33f65 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: 3306, + env: 'DB_MYSQLDB_PORT' + }, + user: { + doc: 'MySQL User', + format: String, + default: 'root', + env: 'DB_MYSQLDB_USER' + }, + }, }, executions: { diff --git a/packages/cli/package.json b/packages/cli/package.json index f14916a275..06a0d7fbbf 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -93,6 +93,7 @@ "localtunnel": "^2.0.0", "lodash.get": "^4.4.2", "mongodb": "^3.2.3", + "mysql2": "^2.0.1", "n8n-core": "~0.24.0", "n8n-editor-ui": "~0.35.0", "n8n-nodes-base": "~0.47.0", 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 8c24f228b6..0623979892 100644 --- a/packages/cli/src/TestWebhooks.ts +++ b/packages/cli/src/TestWebhooks.ts @@ -205,8 +205,6 @@ export class TestWebhooks { let workflow: Workflow; const workflows: Workflow[] = []; for (const webhookKey of Object.keys(this.testWebhookData)) { - console.log('webhookKey: ' + webhookKey); - workflowData = this.testWebhookData[webhookKey].workflowData; workflow = new Workflow(workflowData.id.toString(), workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); workflows.push(); @@ -214,11 +212,8 @@ export class TestWebhooks { 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..5654581ff0 --- /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('datetime') + createdAt: Date; + + @Column('datetime') + 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..e0c084fcfc --- /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('datetime') + startedAt: Date; + + @Column('datetime') + 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..4cca4e62a6 --- /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('datetime') + createdAt: Date; + + @Column('datetime') + 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",