mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-15 00:54:06 -08:00
🚧 Unify database entities
This commit is contained in:
parent
1231c4d670
commit
558ec49cf3
|
@ -18,11 +18,7 @@ import { TlsOptions } from 'tls';
|
||||||
|
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
|
|
||||||
import {
|
import { entities } from './databases/entities';
|
||||||
MySQLDb,
|
|
||||||
PostgresDb,
|
|
||||||
SQLite,
|
|
||||||
} from './databases';
|
|
||||||
|
|
||||||
export let collections: IDatabaseCollections = {
|
export let collections: IDatabaseCollections = {
|
||||||
Credentials: null,
|
Credentials: null,
|
||||||
|
@ -41,15 +37,12 @@ export async function init(): Promise<IDatabaseCollections> {
|
||||||
const dbType = await GenericHelpers.getConfigValue('database.type') as DatabaseType;
|
const dbType = await GenericHelpers.getConfigValue('database.type') as DatabaseType;
|
||||||
const n8nFolder = UserSettings.getUserN8nFolderPath();
|
const n8nFolder = UserSettings.getUserN8nFolderPath();
|
||||||
|
|
||||||
let entities;
|
|
||||||
let connectionOptions: ConnectionOptions;
|
let connectionOptions: ConnectionOptions;
|
||||||
|
|
||||||
const entityPrefix = config.get('database.tablePrefix');
|
const entityPrefix = config.get('database.tablePrefix');
|
||||||
|
|
||||||
switch (dbType) {
|
switch (dbType) {
|
||||||
case 'postgresdb':
|
case 'postgresdb':
|
||||||
entities = PostgresDb;
|
|
||||||
|
|
||||||
const sslCa = await GenericHelpers.getConfigValue('database.postgresdb.ssl.ca') as string;
|
const sslCa = await GenericHelpers.getConfigValue('database.postgresdb.ssl.ca') as string;
|
||||||
const sslCert = await GenericHelpers.getConfigValue('database.postgresdb.ssl.cert') as string;
|
const sslCert = await GenericHelpers.getConfigValue('database.postgresdb.ssl.cert') as string;
|
||||||
const sslKey = await GenericHelpers.getConfigValue('database.postgresdb.ssl.key') as string;
|
const sslKey = await GenericHelpers.getConfigValue('database.postgresdb.ssl.key') as string;
|
||||||
|
@ -84,7 +77,6 @@ export async function init(): Promise<IDatabaseCollections> {
|
||||||
|
|
||||||
case 'mariadb':
|
case 'mariadb':
|
||||||
case 'mysqldb':
|
case 'mysqldb':
|
||||||
entities = MySQLDb;
|
|
||||||
connectionOptions = {
|
connectionOptions = {
|
||||||
type: dbType === 'mysqldb' ? 'mysql' : 'mariadb',
|
type: dbType === 'mysqldb' ? 'mysql' : 'mariadb',
|
||||||
database: await GenericHelpers.getConfigValue('database.mysqldb.database') as string,
|
database: await GenericHelpers.getConfigValue('database.mysqldb.database') as string,
|
||||||
|
@ -100,7 +92,6 @@ export async function init(): Promise<IDatabaseCollections> {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'sqlite':
|
case 'sqlite':
|
||||||
entities = SQLite;
|
|
||||||
connectionOptions = {
|
connectionOptions = {
|
||||||
type: 'sqlite',
|
type: 'sqlite',
|
||||||
database: path.join(n8nFolder, 'database.sqlite'),
|
database: path.join(n8nFolder, 'database.sqlite'),
|
||||||
|
|
|
@ -3,6 +3,7 @@ import * as express from 'express';
|
||||||
import { join as pathJoin } from 'path';
|
import { join as pathJoin } from 'path';
|
||||||
import {
|
import {
|
||||||
readFile as fsReadFile,
|
readFile as fsReadFile,
|
||||||
|
readFileSync as fsReadFileSync,
|
||||||
} from 'fs';
|
} from 'fs';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { IDataObject } from 'n8n-workflow';
|
import { IDataObject } from 'n8n-workflow';
|
||||||
|
@ -13,7 +14,6 @@ const fsReadFileAsync = promisify(fsReadFile);
|
||||||
|
|
||||||
let versionCache: IPackageVersions | undefined;
|
let versionCache: IPackageVersions | undefined;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a message to the user
|
* Displays a message to the user
|
||||||
*
|
*
|
||||||
|
@ -82,20 +82,16 @@ export async function getVersions(): Promise<IPackageVersions> {
|
||||||
return versionCache;
|
return versionCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets value from config with support for "_FILE" environment variables
|
* Retrieves configuration values from env
|
||||||
*
|
*
|
||||||
* @export
|
* @param {string} configKey
|
||||||
* @param {string} configKey The key of the config data to get
|
* @param {IDataObject} currentSchema
|
||||||
* @returns {(Promise<string | boolean | number | undefined>)}
|
* @returns {string | boolean | number | undefined | void}
|
||||||
*/
|
*/
|
||||||
export async function getConfigValue(configKey: string): Promise<string | boolean | number | undefined> {
|
function getConfigFromEnv(configKey: string, currentSchema: IDataObject): string | boolean | number | undefined | void {
|
||||||
const configKeyParts = configKey.split('.');
|
const configKeyParts = configKey.split('.');
|
||||||
|
|
||||||
// Get the environment variable
|
|
||||||
const configSchema = config.getSchema();
|
|
||||||
let currentSchema = configSchema.properties as IDataObject;
|
|
||||||
for (const key of configKeyParts) {
|
for (const key of configKeyParts) {
|
||||||
if (currentSchema[key] === undefined) {
|
if (currentSchema[key] === undefined) {
|
||||||
throw new Error(`Key "${key}" of ConfigKey "${configKey}" does not exist`);
|
throw new Error(`Key "${key}" of ConfigKey "${configKey}" does not exist`);
|
||||||
|
@ -113,18 +109,64 @@ export async function getConfigValue(configKey: string): Promise<string | boolea
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if special file enviroment variable exists
|
// Check if special file enviroment variable exists
|
||||||
const fileEnvironmentVariable = process.env[currentSchema.env + '_FILE'];
|
if (process.env[currentSchema.env + '_FILE'] === undefined) {
|
||||||
if (fileEnvironmentVariable === undefined) {
|
|
||||||
// Does not exist, so return value from config
|
// Does not exist, so return value from config
|
||||||
return config.get(configKey);
|
return config.get(configKey);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets value from config with support for "_FILE" environment variables
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} configKey The key of the config data to get
|
||||||
|
* @returns {(Promise<string | boolean | number | undefined>)}
|
||||||
|
*/
|
||||||
|
export async function getConfigValue(configKey: string): Promise<string | boolean | number | undefined> {
|
||||||
|
// Get the environment variable
|
||||||
|
const configSchema = config.getSchema();
|
||||||
|
const currentSchema = configSchema.properties as IDataObject;
|
||||||
|
const envConfig = getConfigFromEnv(configKey, currentSchema);
|
||||||
|
if (envConfig) {
|
||||||
|
return envConfig;
|
||||||
|
}
|
||||||
|
|
||||||
let data;
|
let data;
|
||||||
try {
|
try {
|
||||||
data = await fsReadFileAsync(fileEnvironmentVariable, 'utf8') as string;
|
data = await fsReadFileAsync(process.env[currentSchema.env + '_FILE']!, 'utf8') as string;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'ENOENT') {
|
if (error.code === 'ENOENT') {
|
||||||
throw new Error(`The file "${fileEnvironmentVariable}" could not be found.`);
|
throw new Error(`The file "${process.env[currentSchema.env + '_FILE']}" could not be found.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets value from config with support for "_FILE" environment variables synchronously
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} configKey The key of the config data to get
|
||||||
|
* @returns {(string | boolean | number | undefined)}
|
||||||
|
*/
|
||||||
|
export function getConfigValueSync(configKey: string): string | boolean | number | undefined {
|
||||||
|
// Get the environment variable
|
||||||
|
const configSchema = config.getSchema();
|
||||||
|
const currentSchema = configSchema.properties as IDataObject;
|
||||||
|
const envConfig = getConfigFromEnv(configKey, currentSchema);
|
||||||
|
if (envConfig) {
|
||||||
|
return envConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
data = fsReadFileSync(process.env[currentSchema.env + '_FILE']!, 'utf8') as string;
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'ENOENT') {
|
||||||
|
throw new Error(`The file "${process.env[currentSchema.env + '_FILE']}" could not be found.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
|
|
48
packages/cli/src/databases/entities/CredentialsEntity.ts
Normal file
48
packages/cli/src/databases/entities/CredentialsEntity.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import {
|
||||||
|
ICredentialNodeAccess,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
resolveDataType
|
||||||
|
} from '../utils';
|
||||||
|
|
||||||
|
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(resolveDataType('json'))
|
||||||
|
nodesAccess: ICredentialNodeAccess[];
|
||||||
|
|
||||||
|
@Column(resolveDataType('datetime'))
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@Column(resolveDataType('datetime'))
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
56
packages/cli/src/databases/entities/ExecutionEntity.ts
Normal file
56
packages/cli/src/databases/entities/ExecutionEntity.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import {
|
||||||
|
WorkflowExecuteMode,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IExecutionFlattedDb,
|
||||||
|
IWorkflowDb,
|
||||||
|
} from '../../';
|
||||||
|
|
||||||
|
import {
|
||||||
|
resolveDataType
|
||||||
|
} from '../utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Column,
|
||||||
|
ColumnOptions,
|
||||||
|
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(resolveDataType('datetime'))
|
||||||
|
startedAt: Date;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column({ type: resolveDataType('datetime') as ColumnOptions['type'], nullable: true })
|
||||||
|
stoppedAt: Date;
|
||||||
|
|
||||||
|
@Column(resolveDataType('json'))
|
||||||
|
workflowData: IWorkflowDb;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column({ nullable: true })
|
||||||
|
workflowId: string;
|
||||||
|
}
|
23
packages/cli/src/databases/entities/TagEntity.ts
Normal file
23
packages/cli/src/databases/entities/TagEntity.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
|
||||||
|
import { ITagDb } from '../../Interfaces';
|
||||||
|
import { WorkflowEntity } from './WorkflowEntity';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class TagEntity implements ITagDb {
|
||||||
|
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column({ unique: true, length: 24 })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
updatedAt: Date;
|
||||||
|
|
||||||
|
@ManyToMany(() => WorkflowEntity, workflowEntity => workflowEntity.tags)
|
||||||
|
workflows: WorkflowEntity[];
|
||||||
|
}
|
33
packages/cli/src/databases/entities/WebhookEntity.ts
Normal file
33
packages/cli/src/databases/entities/WebhookEntity.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import {
|
||||||
|
Column,
|
||||||
|
Entity,
|
||||||
|
Index,
|
||||||
|
PrimaryColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IWebhookDb,
|
||||||
|
} from '../../Interfaces';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
@Index(['webhookId', 'method', 'pathLength'])
|
||||||
|
export class WebhookEntity implements IWebhookDb {
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
workflowId: number;
|
||||||
|
|
||||||
|
@PrimaryColumn()
|
||||||
|
webhookPath: string;
|
||||||
|
|
||||||
|
@PrimaryColumn()
|
||||||
|
method: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
node: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
webhookId: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
pathLength: number;
|
||||||
|
}
|
70
packages/cli/src/databases/entities/WorkflowEntity.ts
Normal file
70
packages/cli/src/databases/entities/WorkflowEntity.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import {
|
||||||
|
IConnections,
|
||||||
|
IDataObject,
|
||||||
|
INode,
|
||||||
|
IWorkflowSettings,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Column,
|
||||||
|
ColumnOptions,
|
||||||
|
Entity,
|
||||||
|
JoinTable,
|
||||||
|
ManyToMany,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IWorkflowDb,
|
||||||
|
} from '../../';
|
||||||
|
|
||||||
|
import {
|
||||||
|
resolveDataType
|
||||||
|
} from '../utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TagEntity,
|
||||||
|
} from './TagEntity';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class WorkflowEntity implements IWorkflowDb {
|
||||||
|
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
length: 128,
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
active: boolean;
|
||||||
|
|
||||||
|
@Column(resolveDataType('json'))
|
||||||
|
nodes: INode[];
|
||||||
|
|
||||||
|
@Column(resolveDataType('json'))
|
||||||
|
connections: IConnections;
|
||||||
|
|
||||||
|
@Column(resolveDataType('datetime'))
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@Column(resolveDataType('datetime'))
|
||||||
|
updatedAt: Date;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: resolveDataType('json') as ColumnOptions['type'],
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
settings?: IWorkflowSettings;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: resolveDataType('json') as ColumnOptions['type'],
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
staticData?: IDataObject;
|
||||||
|
|
||||||
|
@ManyToMany(() => TagEntity, tagEntity => tagEntity.workflows)
|
||||||
|
@JoinTable({ name: "workflows_tags" })
|
||||||
|
tags: TagEntity[];
|
||||||
|
}
|
11
packages/cli/src/databases/entities/index.ts
Normal file
11
packages/cli/src/databases/entities/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { CredentialsEntity } from './CredentialsEntity';
|
||||||
|
import { ExecutionEntity } from './ExecutionEntity';
|
||||||
|
import { WorkflowEntity } from './WorkflowEntity';
|
||||||
|
import { WebhookEntity } from './WebhookEntity';
|
||||||
|
|
||||||
|
export const entities = {
|
||||||
|
CredentialsEntity,
|
||||||
|
ExecutionEntity,
|
||||||
|
WorkflowEntity,
|
||||||
|
WebhookEntity,
|
||||||
|
};
|
29
packages/cli/src/databases/utils.ts
Normal file
29
packages/cli/src/databases/utils.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import {
|
||||||
|
DatabaseType,
|
||||||
|
GenericHelpers,
|
||||||
|
} from '../';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the data type for the used database type
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} dataType
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function resolveDataType(dataType: string) {
|
||||||
|
const dbType = GenericHelpers.getConfigValueSync('database.type') as DatabaseType;
|
||||||
|
|
||||||
|
const typeMap: { [key in DatabaseType]: { [key: string]: string } } = {
|
||||||
|
sqlite: {
|
||||||
|
json: 'simple-json',
|
||||||
|
},
|
||||||
|
postgresdb: {
|
||||||
|
datetime: 'timestamp',
|
||||||
|
},
|
||||||
|
mysqldb: {},
|
||||||
|
mariadb: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
return typeMap[dbType][dataType] ?? dataType;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue