ci(core): Reduce memory usage in tests (part-2) (no-changelog) (#7671)

This also gets rid of `Db.collection`, which was another source of
circular dependencies.
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-11-10 15:04:26 +01:00 committed by GitHub
parent 37dd658dc5
commit 000e76e3b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
174 changed files with 892 additions and 808 deletions

View file

@ -60,6 +60,9 @@ jobs:
- name: Test Backend - name: Test Backend
run: pnpm test:backend run: pnpm test:backend
- name: Test Nodes
run: pnpm test:nodes
- name: Test Frontend - name: Test Frontend
run: pnpm test:frontend run: pnpm test:frontend

View file

@ -25,7 +25,8 @@
"start:tunnel": "./packages/cli/bin/n8n start --tunnel", "start:tunnel": "./packages/cli/bin/n8n start --tunnel",
"start:windows": "cd packages/cli/bin && n8n", "start:windows": "cd packages/cli/bin && n8n",
"test": "turbo run test", "test": "turbo run test",
"test:backend": "pnpm --filter=!n8n-design-system --filter=!n8n-editor-ui test", "test:backend": "pnpm --filter=!n8n-design-system --filter=!n8n-editor-ui --filter=!n8n-nodes-base test",
"test:nodes": "pnpm --filter=n8n-nodes-base test",
"test:frontend": "pnpm --filter=n8n-design-system --filter=n8n-editor-ui test", "test:frontend": "pnpm --filter=n8n-design-system --filter=n8n-editor-ui test",
"watch": "turbo run watch", "watch": "turbo run watch",
"webhook": "./packages/cli/bin/n8n webhook", "webhook": "./packages/cli/bin/n8n webhook",

View file

@ -19,7 +19,7 @@ import type {
IWorkflowExecutionDataProcess, IWorkflowExecutionDataProcess,
} from '@/Interfaces'; } from '@/Interfaces';
import { isWorkflowIdValid } from '@/utils'; import { isWorkflowIdValid } from '@/utils';
import { ExecutionRepository } from '@db/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
@Service() @Service()

View file

@ -33,7 +33,6 @@ import {
import type express from 'express'; import type express from 'express';
import * as Db from '@/Db';
import type { import type {
IResponseCallbackData, IResponseCallbackData,
IWebhookManager, IWebhookManager,
@ -63,7 +62,8 @@ import { webhookNotFoundErrorMessage } from './utils';
import { In } from 'typeorm'; import { In } from 'typeorm';
import { WebhookService } from './services/webhook.service'; import { WebhookService } from './services/webhook.service';
import { Logger } from './Logger'; import { Logger } from './Logger';
import { WorkflowRepository } from '@/databases/repositories'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import config from '@/config'; import config from '@/config';
import type { MultiMainInstancePublisher } from './services/orchestration/main/MultiMainInstance.publisher.ee'; import type { MultiMainInstancePublisher } from './services/orchestration/main/MultiMainInstance.publisher.ee';
@ -104,6 +104,7 @@ export class ActiveWorkflowRunner implements IWebhookManager {
private readonly nodeTypes: NodeTypes, private readonly nodeTypes: NodeTypes,
private readonly webhookService: WebhookService, private readonly webhookService: WebhookService,
private readonly workflowRepository: WorkflowRepository, private readonly workflowRepository: WorkflowRepository,
private readonly sharedWorkflowRepository: SharedWorkflowRepository,
) {} ) {}
async init() { async init() {
@ -188,7 +189,7 @@ export class ActiveWorkflowRunner implements IWebhookManager {
}); });
} }
const workflowData = await Db.collections.Workflow.findOne({ const workflowData = await this.workflowRepository.findOne({
where: { id: webhook.workflowId }, where: { id: webhook.workflowId },
relations: ['shared', 'shared.user', 'shared.user.globalRole'], relations: ['shared', 'shared.user', 'shared.user.globalRole'],
}); });
@ -296,7 +297,7 @@ export class ActiveWorkflowRunner implements IWebhookManager {
Object.assign(where, { workflowId: In(activeIds) }); Object.assign(where, { workflowId: In(activeIds) });
const sharings = await Db.collections.SharedWorkflow.find({ const sharings = await this.sharedWorkflowRepository.find({
select: ['workflowId'], select: ['workflowId'],
where, where,
}); });
@ -416,7 +417,7 @@ export class ActiveWorkflowRunner implements IWebhookManager {
* Clear workflow-defined webhooks from the `webhook_entity` table. * Clear workflow-defined webhooks from the `webhook_entity` table.
*/ */
async clearWebhooks(workflowId: string) { async clearWebhooks(workflowId: string) {
const workflowData = await Db.collections.Workflow.findOne({ const workflowData = await this.workflowRepository.findOne({
where: { id: workflowId }, where: { id: workflowId },
relations: ['shared', 'shared.user', 'shared.user.globalRole'], relations: ['shared', 'shared.user', 'shared.user.globalRole'],
}); });

View file

@ -43,7 +43,6 @@ import {
ErrorReporterProxy as ErrorReporter, ErrorReporterProxy as ErrorReporter,
} from 'n8n-workflow'; } from 'n8n-workflow';
import * as Db from '@/Db';
import type { ICredentialsDb } from '@/Interfaces'; import type { ICredentialsDb } from '@/Interfaces';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'; import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
@ -54,6 +53,8 @@ import { CredentialsOverwrites } from '@/CredentialsOverwrites';
import { RESPONSE_ERROR_MESSAGES } from './constants'; import { RESPONSE_ERROR_MESSAGES } from './constants';
import { isObjectLiteral } from './utils'; import { isObjectLiteral } from './utils';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
const { OAUTH2_CREDENTIAL_TEST_SUCCEEDED, OAUTH2_CREDENTIAL_TEST_FAILED } = RESPONSE_ERROR_MESSAGES; const { OAUTH2_CREDENTIAL_TEST_SUCCEEDED, OAUTH2_CREDENTIAL_TEST_FAILED } = RESPONSE_ERROR_MESSAGES;
@ -102,6 +103,8 @@ export class CredentialsHelper extends ICredentialsHelper {
private readonly credentialTypes: CredentialTypes, private readonly credentialTypes: CredentialTypes,
private readonly nodeTypes: NodeTypes, private readonly nodeTypes: NodeTypes,
private readonly credentialsOverwrites: CredentialsOverwrites, private readonly credentialsOverwrites: CredentialsOverwrites,
private readonly credentialsRepository: CredentialsRepository,
private readonly sharedCredentialsRepository: SharedCredentialsRepository,
) { ) {
super(); super();
} }
@ -271,11 +274,13 @@ export class CredentialsHelper extends ICredentialsHelper {
try { try {
credential = userId credential = userId
? await Db.collections.SharedCredentials.findOneOrFail({ ? await this.sharedCredentialsRepository
relations: ['credentials'], .findOneOrFail({
where: { credentials: { id: nodeCredential.id, type }, userId }, relations: ['credentials'],
}).then((shared) => shared.credentials) where: { credentials: { id: nodeCredential.id, type }, userId },
: await Db.collections.Credentials.findOneByOrFail({ id: nodeCredential.id, type }); })
.then((shared) => shared.credentials)
: await this.credentialsRepository.findOneByOrFail({ id: nodeCredential.id, type });
} catch (error) { } catch (error) {
throw new CredentialNotFoundError(nodeCredential.id, type); throw new CredentialNotFoundError(nodeCredential.id, type);
} }
@ -463,7 +468,7 @@ export class CredentialsHelper extends ICredentialsHelper {
type, type,
}; };
await Db.collections.Credentials.update(findQuery, newCredentialsData); await this.credentialsRepository.update(findQuery, newCredentialsData);
} }
private static hasAccessToken(credentialsDecrypted: ICredentialsDecrypted) { private static hasAccessToken(credentialsDecrypted: ICredentialsDecrypted) {
@ -774,7 +779,7 @@ export class CredentialsHelper extends ICredentialsHelper {
return false; return false;
} }
const credential = await Db.collections.SharedCredentials.findOne({ const credential = await this.sharedCredentialsRepository.findOne({
where: { where: {
role: { role: {
scope: 'credential', scope: 'credential',

View file

@ -1,13 +1,10 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/restrict-template-expressions */
import { Container } from 'typedi'; import { Container } from 'typedi';
import type { DataSourceOptions as ConnectionOptions, EntityManager, LoggerOptions } from 'typeorm'; import type { DataSourceOptions as ConnectionOptions, EntityManager, LoggerOptions } from 'typeorm';
import { DataSource as Connection } from 'typeorm'; import { DataSource as Connection } from 'typeorm';
import type { TlsOptions } from 'tls'; import type { TlsOptions } from 'tls';
import { ErrorReporterProxy as ErrorReporter } from 'n8n-workflow'; import { ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
import type { IDatabaseCollections } from '@/Interfaces';
import config from '@/config'; import config from '@/config';
import { entities } from '@db/entities'; import { entities } from '@db/entities';
@ -21,28 +18,6 @@ import {
import { inTest } from '@/constants'; import { inTest } from '@/constants';
import { wrapMigration } from '@db/utils/migrationHelpers'; import { wrapMigration } from '@db/utils/migrationHelpers';
import type { DatabaseType, Migration } from '@db/types'; import type { DatabaseType, Migration } from '@db/types';
import {
AuthIdentityRepository,
AuthProviderSyncHistoryRepository,
CredentialsRepository,
EventDestinationsRepository,
ExecutionDataRepository,
ExecutionMetadataRepository,
ExecutionRepository,
InstalledNodesRepository,
InstalledPackagesRepository,
RoleRepository,
SettingsRepository,
SharedCredentialsRepository,
SharedWorkflowRepository,
UserRepository,
VariablesRepository,
WorkflowRepository,
WorkflowStatisticsRepository,
WorkflowTagMappingRepository,
} from '@db/repositories';
export const collections = {} as IDatabaseCollections;
let connection: Connection; let connection: Connection;
@ -165,32 +140,6 @@ export async function init(testConnectionOptions?: ConnectionOptions): Promise<v
} }
connectionState.connected = true; connectionState.connected = true;
/**
* @important Do not add to these collections. Inject the repository as a dependency instead.
*/
collections.AuthIdentity = Container.get(AuthIdentityRepository);
collections.AuthProviderSyncHistory = Container.get(AuthProviderSyncHistoryRepository);
collections.EventDestinations = Container.get(EventDestinationsRepository);
collections.Execution = Container.get(ExecutionRepository);
collections.ExecutionData = Container.get(ExecutionDataRepository);
collections.ExecutionMetadata = Container.get(ExecutionMetadataRepository);
collections.InstalledNodes = Container.get(InstalledNodesRepository);
collections.InstalledPackages = Container.get(InstalledPackagesRepository);
collections.SharedCredentials = Container.get(SharedCredentialsRepository);
collections.SharedWorkflow = Container.get(SharedWorkflowRepository);
collections.Variables = Container.get(VariablesRepository);
collections.WorkflowStatistics = Container.get(WorkflowStatisticsRepository);
collections.WorkflowTagMapping = Container.get(WorkflowTagMappingRepository);
/**
* @important Do not remove these collections until cloud hooks are backwards compatible.
*/
collections.Role = Container.get(RoleRepository);
collections.User = Container.get(UserRepository);
collections.Settings = Container.get(SettingsRepository);
collections.Credentials = Container.get(CredentialsRepository);
collections.Workflow = Container.get(WorkflowRepository);
} }
export async function migrate() { export async function migrate() {

View file

@ -1,14 +1,15 @@
/* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable @typescript-eslint/no-var-requires */
import { Service } from 'typedi'; import { Service } from 'typedi';
import * as Db from '@/Db';
import type { import type {
IExternalHooksClass, IExternalHooksClass,
IExternalHooksFileData, IExternalHooksFileData,
IExternalHooksFunctions, IExternalHooksFunctions,
} from '@/Interfaces'; } from '@/Interfaces';
import config from '@/config'; import config from '@/config';
import { UserRepository } from '@db/repositories/user.repository';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SettingsRepository } from '@db/repositories/settings.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
@Service() @Service()
export class ExternalHooks implements IExternalHooksClass { export class ExternalHooks implements IExternalHooksClass {
@ -16,7 +17,25 @@ export class ExternalHooks implements IExternalHooksClass {
[key: string]: Array<() => {}>; [key: string]: Array<() => {}>;
} = {}; } = {};
initDidRun = false; private initDidRun = false;
private dbCollections: IExternalHooksFunctions['dbCollections'];
constructor(
userRepository: UserRepository,
settingsRepository: SettingsRepository,
credentialsRepository: CredentialsRepository,
workflowRepository: WorkflowRepository,
) {
/* eslint-disable @typescript-eslint/naming-convention */
this.dbCollections = {
User: userRepository,
Settings: settingsRepository,
Credentials: credentialsRepository,
Workflow: workflowRepository,
};
/* eslint-enable @typescript-eslint/naming-convention */
}
async init(): Promise<void> { async init(): Promise<void> {
if (this.initDidRun) { if (this.initDidRun) {
@ -83,14 +102,14 @@ export class ExternalHooks implements IExternalHooksClass {
} }
async run(hookName: string, hookParameters?: any[]): Promise<void> { async run(hookName: string, hookParameters?: any[]): Promise<void> {
const externalHookFunctions: IExternalHooksFunctions = {
dbCollections: Db.collections,
};
if (this.externalHooks[hookName] === undefined) { if (this.externalHooks[hookName] === undefined) {
return; return;
} }
const externalHookFunctions: IExternalHooksFunctions = {
dbCollections: this.dbCollections,
};
for (const externalHookFunction of this.externalHooks[hookName]) { for (const externalHookFunction of this.externalHooks[hookName]) {
await externalHookFunction.apply(externalHookFunctions, hookParameters); await externalHookFunction.apply(externalHookFunctions, hookParameters);
} }

View file

@ -1,4 +1,4 @@
import { SettingsRepository } from '@/databases/repositories'; import { SettingsRepository } from '@db/repositories/settings.repository';
import type { import type {
ExternalSecretsSettings, ExternalSecretsSettings,
SecretsProvider, SecretsProvider,

View file

@ -10,7 +10,6 @@ import { validate } from 'class-validator';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { Like } from 'typeorm'; import { Like } from 'typeorm';
import config from '@/config'; import config from '@/config';
import * as Db from '@/Db';
import type { ExecutionPayload, ICredentialsDb, IWorkflowDb } from '@/Interfaces'; import type { ExecutionPayload, ICredentialsDb, IWorkflowDb } from '@/Interfaces';
import * as ResponseHelper from '@/ResponseHelper'; import * as ResponseHelper from '@/ResponseHelper';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
@ -18,7 +17,9 @@ import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import type { TagEntity } from '@db/entities/TagEntity'; import type { TagEntity } from '@db/entities/TagEntity';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
import type { UserUpdatePayload } from '@/requests'; import type { UserUpdatePayload } from '@/requests';
import { ExecutionRepository } from '@db/repositories'; import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { ExecutionRepository } from '@db/repositories/execution.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
/** /**
* Returns the base URL n8n is reachable from * Returns the base URL n8n is reachable from
@ -64,8 +65,8 @@ export async function generateUniqueName(
const found: Array<WorkflowEntity | ICredentialsDb> = const found: Array<WorkflowEntity | ICredentialsDb> =
entityType === 'workflow' entityType === 'workflow'
? await Db.collections.Workflow.find(findConditions) ? await Container.get(WorkflowRepository).find(findConditions)
: await Db.collections.Credentials.find(findConditions); : await Container.get(CredentialsRepository).find(findConditions);
// name is unique // name is unique
if (found.length === 0) { if (found.length === 0) {

View file

@ -30,7 +30,7 @@ import type { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import type { WorkflowExecute } from 'n8n-core'; import type { WorkflowExecute } from 'n8n-core';
import type PCancelable from 'p-cancelable'; import type PCancelable from 'p-cancelable';
import type { FindOperator, Repository } from 'typeorm'; import type { FindOperator } from 'typeorm';
import type { ChildProcess } from 'child_process'; import type { ChildProcess } from 'child_process';
@ -40,26 +40,10 @@ import type { Role } from '@db/entities/Role';
import type { SharedCredentials } from '@db/entities/SharedCredentials'; import type { SharedCredentials } from '@db/entities/SharedCredentials';
import type { TagEntity } from '@db/entities/TagEntity'; import type { TagEntity } from '@db/entities/TagEntity';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
import type { import type { CredentialsRepository } from '@db/repositories/credentials.repository';
AuthIdentityRepository, import type { SettingsRepository } from '@db/repositories/settings.repository';
AuthProviderSyncHistoryRepository, import type { UserRepository } from '@db/repositories/user.repository';
CredentialsRepository, import type { WorkflowRepository } from '@db/repositories/workflow.repository';
EventDestinationsRepository,
ExecutionDataRepository,
ExecutionMetadataRepository,
ExecutionRepository,
InstalledNodesRepository,
InstalledPackagesRepository,
RoleRepository,
SettingsRepository,
SharedCredentialsRepository,
SharedWorkflowRepository,
UserRepository,
VariablesRepository,
WorkflowRepository,
WorkflowStatisticsRepository,
WorkflowTagMappingRepository,
} from '@db/repositories';
import type { LICENSE_FEATURES, LICENSE_QUOTAS } from './constants'; import type { LICENSE_FEATURES, LICENSE_QUOTAS } from './constants';
import type { WorkflowWithSharingsAndCredentials } from './workflows/workflows.types'; import type { WorkflowWithSharingsAndCredentials } from './workflows/workflows.types';
@ -71,33 +55,6 @@ export interface ICredentialsOverwrite {
[key: string]: ICredentialDataDecryptedObject; [key: string]: ICredentialDataDecryptedObject;
} }
/**
* @important Do not add to these collections. Inject the repository as a dependency instead.
*/
/* eslint-disable @typescript-eslint/naming-convention */
export interface IDatabaseCollections extends Record<string, Repository<any>> {
AuthIdentity: AuthIdentityRepository;
AuthProviderSyncHistory: AuthProviderSyncHistoryRepository;
Credentials: CredentialsRepository;
EventDestinations: EventDestinationsRepository;
Execution: ExecutionRepository;
ExecutionData: ExecutionDataRepository;
ExecutionMetadata: ExecutionMetadataRepository;
InstalledNodes: InstalledNodesRepository;
InstalledPackages: InstalledPackagesRepository;
Role: RoleRepository;
Settings: SettingsRepository;
SharedCredentials: SharedCredentialsRepository;
SharedWorkflow: SharedWorkflowRepository;
User: UserRepository;
Variables: VariablesRepository;
Workflow: WorkflowRepository;
WorkflowStatistics: WorkflowStatisticsRepository;
WorkflowTagMapping: WorkflowTagMappingRepository;
}
/* eslint-enable @typescript-eslint/naming-convention */
// ---------------------------------- // ----------------------------------
// tags // tags
// ---------------------------------- // ----------------------------------
@ -285,7 +242,14 @@ export interface IExternalHooksFileData {
} }
export interface IExternalHooksFunctions { export interface IExternalHooksFunctions {
dbCollections: IDatabaseCollections; dbCollections: {
/* eslint-disable @typescript-eslint/naming-convention */
User: UserRepository;
Settings: SettingsRepository;
Credentials: CredentialsRepository;
Workflow: WorkflowRepository;
/* eslint-enable @typescript-eslint/naming-convention */
};
} }
export interface IExternalHooksClass { export interface IExternalHooksClass {

View file

@ -32,6 +32,10 @@ import {
import { BadRequestError, InternalServerError } from '../ResponseHelper'; import { BadRequestError, InternalServerError } from '../ResponseHelper';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { UserRepository } from '@db/repositories/user.repository';
import { SettingsRepository } from '@db/repositories/settings.repository';
import { AuthProviderSyncHistoryRepository } from '@db/repositories/authProviderSyncHistory.repository';
import { AuthIdentityRepository } from '@db/repositories/authIdentity.repository';
/** /**
* Check whether the LDAP feature is disabled in the instance * Check whether the LDAP feature is disabled in the instance
@ -114,7 +118,7 @@ export const validateLdapConfigurationSchema = (
* Retrieve the LDAP configuration (decrypted) form the database * Retrieve the LDAP configuration (decrypted) form the database
*/ */
export const getLdapConfig = async (): Promise<LdapConfig> => { export const getLdapConfig = async (): Promise<LdapConfig> => {
const configuration = await Db.collections.Settings.findOneByOrFail({ const configuration = await Container.get(SettingsRepository).findOneByOrFail({
key: LDAP_FEATURE_NAME, key: LDAP_FEATURE_NAME,
}); });
const configurationData = jsonParse<LdapConfig>(configuration.value); const configurationData = jsonParse<LdapConfig>(configuration.value);
@ -171,7 +175,7 @@ export const updateLdapConfig = async (ldapConfig: LdapConfig): Promise<void> =>
} }
} }
await Db.collections.Settings.update( await Container.get(SettingsRepository).update(
{ key: LDAP_FEATURE_NAME }, { key: LDAP_FEATURE_NAME },
{ value: JSON.stringify(ldapConfig), loadOnStartup: true }, { value: JSON.stringify(ldapConfig), loadOnStartup: true },
); );
@ -284,7 +288,7 @@ export const findAndAuthenticateLdapUser = async (
export const getAuthIdentityByLdapId = async ( export const getAuthIdentityByLdapId = async (
idAttributeValue: string, idAttributeValue: string,
): Promise<AuthIdentity | null> => { ): Promise<AuthIdentity | null> => {
return Db.collections.AuthIdentity.findOne({ return Container.get(AuthIdentityRepository).findOne({
relations: ['user', 'user.globalRole'], relations: ['user', 'user.globalRole'],
where: { where: {
providerId: idAttributeValue, providerId: idAttributeValue,
@ -294,7 +298,7 @@ export const getAuthIdentityByLdapId = async (
}; };
export const getUserByEmail = async (email: string): Promise<User | null> => { export const getUserByEmail = async (email: string): Promise<User | null> => {
return Db.collections.User.findOne({ return Container.get(UserRepository).findOne({
where: { email }, where: { email },
relations: ['globalRole'], relations: ['globalRole'],
}); });
@ -322,7 +326,7 @@ export const mapLdapAttributesToUser = (
* Retrieve LDAP ID of all LDAP users in the database * Retrieve LDAP ID of all LDAP users in the database
*/ */
export const getLdapIds = async (): Promise<string[]> => { export const getLdapIds = async (): Promise<string[]> => {
const identities = await Db.collections.AuthIdentity.find({ const identities = await Container.get(AuthIdentityRepository).find({
select: ['providerId'], select: ['providerId'],
where: { where: {
providerType: 'ldap', providerType: 'ldap',
@ -332,7 +336,7 @@ export const getLdapIds = async (): Promise<string[]> => {
}; };
export const getLdapUsers = async (): Promise<User[]> => { export const getLdapUsers = async (): Promise<User[]> => {
const identities = await Db.collections.AuthIdentity.find({ const identities = await Container.get(AuthIdentityRepository).find({
relations: ['user'], relations: ['user'],
where: { where: {
providerType: 'ldap', providerType: 'ldap',
@ -409,7 +413,7 @@ export const processUsers = async (
export const saveLdapSynchronization = async ( export const saveLdapSynchronization = async (
data: Omit<AuthProviderSyncHistory, 'id' | 'providerType'>, data: Omit<AuthProviderSyncHistory, 'id' | 'providerType'>,
): Promise<void> => { ): Promise<void> => {
await Db.collections.AuthProviderSyncHistory.save({ await Container.get(AuthProviderSyncHistoryRepository).save({
...data, ...data,
providerType: 'ldap', providerType: 'ldap',
}); });
@ -423,7 +427,7 @@ export const getLdapSynchronizations = async (
perPage: number, perPage: number,
): Promise<AuthProviderSyncHistory[]> => { ): Promise<AuthProviderSyncHistory[]> => {
const _page = Math.abs(page); const _page = Math.abs(page);
return Db.collections.AuthProviderSyncHistory.find({ return Container.get(AuthProviderSyncHistoryRepository).find({
where: { providerType: 'ldap' }, where: { providerType: 'ldap' },
order: { id: 'DESC' }, order: { id: 'DESC' },
take: perPage, take: perPage,
@ -450,11 +454,11 @@ export const getMappingAttributes = (ldapConfig: LdapConfig): string[] => {
}; };
export const createLdapAuthIdentity = async (user: User, ldapId: string) => { export const createLdapAuthIdentity = async (user: User, ldapId: string) => {
return Db.collections.AuthIdentity.save(AuthIdentity.create(user, ldapId)); return Container.get(AuthIdentityRepository).save(AuthIdentity.create(user, ldapId));
}; };
export const createLdapUserOnLocalDb = async (role: Role, data: Partial<User>, ldapId: string) => { export const createLdapUserOnLocalDb = async (role: Role, data: Partial<User>, ldapId: string) => {
const user = await Db.collections.User.save({ const user = await Container.get(UserRepository).save({
password: randomPassword(), password: randomPassword(),
globalRole: role, globalRole: role,
...data, ...data,
@ -466,10 +470,10 @@ export const createLdapUserOnLocalDb = async (role: Role, data: Partial<User>, l
export const updateLdapUserOnLocalDb = async (identity: AuthIdentity, data: Partial<User>) => { export const updateLdapUserOnLocalDb = async (identity: AuthIdentity, data: Partial<User>) => {
const userId = identity?.user?.id; const userId = identity?.user?.id;
if (userId) { if (userId) {
await Db.collections.User.update({ id: userId }, data); await Container.get(UserRepository).update({ id: userId }, data);
} }
}; };
const deleteAllLdapIdentities = async () => { const deleteAllLdapIdentities = async () => {
return Db.collections.AuthIdentity.delete({ providerType: 'ldap' }); return Container.get(AuthIdentityRepository).delete({ providerType: 'ldap' });
}; };

View file

@ -4,7 +4,6 @@ import { InstanceSettings, ObjectStoreService } from 'n8n-core';
import Container, { Service } from 'typedi'; import Container, { Service } from 'typedi';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import config from '@/config'; import config from '@/config';
import * as Db from '@/Db';
import { import {
LICENSE_FEATURES, LICENSE_FEATURES,
LICENSE_QUOTAS, LICENSE_QUOTAS,
@ -12,7 +11,8 @@ import {
SETTINGS_LICENSE_CERT_KEY, SETTINGS_LICENSE_CERT_KEY,
UNLIMITED_LICENSE_QUOTA, UNLIMITED_LICENSE_QUOTA,
} from './constants'; } from './constants';
import { WorkflowRepository } from '@/databases/repositories'; import { SettingsRepository } from '@db/repositories/settings.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import type { BooleanLicenseFeature, N8nInstanceType, NumericLicenseFeature } from './Interfaces'; import type { BooleanLicenseFeature, N8nInstanceType, NumericLicenseFeature } from './Interfaces';
import type { RedisServicePubSubPublisher } from './services/redis/RedisServicePubSubPublisher'; import type { RedisServicePubSubPublisher } from './services/redis/RedisServicePubSubPublisher';
import { RedisService } from './services/redis.service'; import { RedisService } from './services/redis.service';
@ -40,6 +40,8 @@ export class License {
constructor( constructor(
private readonly logger: Logger, private readonly logger: Logger,
private readonly instanceSettings: InstanceSettings, private readonly instanceSettings: InstanceSettings,
private readonly settingsRepository: SettingsRepository,
private readonly workflowRepository: WorkflowRepository,
) {} ) {}
async init(instanceType: N8nInstanceType = 'main') { async init(instanceType: N8nInstanceType = 'main') {
@ -91,7 +93,7 @@ export class License {
return [ return [
{ {
name: 'activeWorkflows', name: 'activeWorkflows',
value: await Container.get(WorkflowRepository).count({ where: { active: true } }), value: await this.workflowRepository.count({ where: { active: true } }),
}, },
]; ];
} }
@ -102,7 +104,7 @@ export class License {
if (ephemeralLicense) { if (ephemeralLicense) {
return ephemeralLicense; return ephemeralLicense;
} }
const databaseSettings = await Db.collections.Settings.findOne({ const databaseSettings = await this.settingsRepository.findOne({
where: { where: {
key: SETTINGS_LICENSE_CERT_KEY, key: SETTINGS_LICENSE_CERT_KEY,
}, },
@ -153,7 +155,7 @@ export class License {
async saveCertStr(value: TLicenseBlock): Promise<void> { async saveCertStr(value: TLicenseBlock): Promise<void> {
// if we have an ephemeral license, we don't want to save it to the database // if we have an ephemeral license, we don't want to save it to the database
if (config.get('license.cert')) return; if (config.get('license.cert')) return;
await Db.collections.Settings.upsert( await this.settingsRepository.upsert(
{ {
key: SETTINGS_LICENSE_CERT_KEY, key: SETTINGS_LICENSE_CERT_KEY,
value, value,

View file

@ -1,13 +1,14 @@
import Container from 'typedi';
import config from '@/config'; import config from '@/config';
import * as Db from '@/Db';
import { MFA_FEATURE_ENABLED } from './constants'; import { MFA_FEATURE_ENABLED } from './constants';
import { UserRepository } from '@db/repositories/user.repository';
export const isMfaFeatureEnabled = () => config.get(MFA_FEATURE_ENABLED); export const isMfaFeatureEnabled = () => config.get(MFA_FEATURE_ENABLED);
const isMfaFeatureDisabled = () => !isMfaFeatureEnabled(); const isMfaFeatureDisabled = () => !isMfaFeatureEnabled();
const getUsersWithMfaEnabled = async () => const getUsersWithMfaEnabled = async () =>
Db.collections.User.count({ where: { mfaEnabled: true } }); Container.get(UserRepository).count({ where: { mfaEnabled: true } });
export const handleMfaDisable = async () => { export const handleMfaDisable = async () => {
if (isMfaFeatureDisabled()) { if (isMfaFeatureDisabled()) {

View file

@ -1,7 +1,7 @@
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { Service } from 'typedi'; import { Service } from 'typedi';
import { Cipher } from 'n8n-core'; import { Cipher } from 'n8n-core';
import { UserRepository } from '@db/repositories'; import { UserRepository } from '@db/repositories/user.repository';
import { TOTPService } from './totp.service'; import { TOTPService } from './totp.service';
@Service() @Service()

View file

@ -11,11 +11,11 @@ import type { OpenAPIV3 } from 'openapi-types';
import type { JsonObject } from 'swagger-ui-express'; import type { JsonObject } from 'swagger-ui-express';
import config from '@/config'; import config from '@/config';
import * as Db from '@/Db';
import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { License } from '@/License'; import { License } from '@/License';
import { UserRepository } from '@db/repositories/user.repository';
async function createApiRouter( async function createApiRouter(
version: string, version: string,
@ -95,7 +95,7 @@ async function createApiRouter(
schema: OpenAPIV3.ApiKeySecurityScheme, schema: OpenAPIV3.ApiKeySecurityScheme,
): Promise<boolean> => { ): Promise<boolean> => {
const apiKey = req.headers[schema.name.toLowerCase()] as string; const apiKey = req.headers[schema.name.toLowerCase()] as string;
const user = await Db.collections.User.findOne({ const user = await Container.get(UserRepository).findOne({
where: { apiKey }, where: { apiKey },
relations: ['globalRole'], relations: ['globalRole'],
}); });

View file

@ -10,9 +10,11 @@ import type { IDependency, IJsonSchema } from '../../../types';
import type { CredentialRequest } from '@/requests'; import type { CredentialRequest } from '@/requests';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
export async function getCredentials(credentialId: string): Promise<ICredentialsDb | null> { export async function getCredentials(credentialId: string): Promise<ICredentialsDb | null> {
return Db.collections.Credentials.findOneBy({ id: credentialId }); return Container.get(CredentialsRepository).findOneBy({ id: credentialId });
} }
export async function getSharedCredentials( export async function getSharedCredentials(
@ -20,7 +22,7 @@ export async function getSharedCredentials(
credentialId: string, credentialId: string,
relations?: string[], relations?: string[],
): Promise<SharedCredentials | null> { ): Promise<SharedCredentials | null> {
return Db.collections.SharedCredentials.findOne({ return Container.get(SharedCredentialsRepository).findOne({
where: { where: {
userId, userId,
credentialsId: credentialId, credentialsId: credentialId,
@ -83,7 +85,7 @@ export async function saveCredential(
export async function removeCredential(credentials: CredentialsEntity): Promise<ICredentialsDb> { export async function removeCredential(credentials: CredentialsEntity): Promise<ICredentialsDb> {
await Container.get(ExternalHooks).run('credentials.delete', [credentials.id]); await Container.get(ExternalHooks).run('credentials.delete', [credentials.id]);
return Db.collections.Credentials.remove(credentials); return Container.get(CredentialsRepository).remove(credentials);
} }
export async function encryptCredential(credential: CredentialsEntity): Promise<ICredentialsDb> { export async function encryptCredential(credential: CredentialsEntity): Promise<ICredentialsDb> {

View file

@ -8,7 +8,7 @@ import { getSharedWorkflowIds } from '../workflows/workflows.service';
import { encodeNextCursor } from '../../shared/services/pagination.service'; import { encodeNextCursor } from '../../shared/services/pagination.service';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { ExecutionRepository } from '@/databases/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
export = { export = {
deleteExecution: [ deleteExecution: [

View file

@ -3,9 +3,8 @@ import { In, Not, Raw, LessThan } from 'typeorm';
import { Container } from 'typedi'; import { Container } from 'typedi';
import type { ExecutionStatus } from 'n8n-workflow'; import type { ExecutionStatus } from 'n8n-workflow';
import * as Db from '@/Db';
import type { IExecutionBase, IExecutionFlattedDb } from '@/Interfaces'; import type { IExecutionBase, IExecutionFlattedDb } from '@/Interfaces';
import { ExecutionRepository } from '@db/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
function getStatusCondition(status: ExecutionStatus) { function getStatusCondition(status: ExecutionStatus) {
const condition: Pick<FindOptionsWhere<IExecutionFlattedDb>, 'status'> = {}; const condition: Pick<FindOptionsWhere<IExecutionFlattedDb>, 'status'> = {};
@ -83,7 +82,7 @@ export async function getExecutionsCount(data: {
excludedWorkflowIds?: string[]; excludedWorkflowIds?: string[];
}): Promise<number> { }): Promise<number> {
// TODO: Consider moving this to the repository as well // TODO: Consider moving this to the repository as well
const executions = await Db.collections.Execution.count({ const executions = await Container.get(ExecutionRepository).count({
where: { where: {
...(data.lastId && { id: LessThan(data.lastId) }), ...(data.lastId && { id: LessThan(data.lastId) }),
...(data.status && { ...getStatusCondition(data.status) }), ...(data.status && { ...getStatusCondition(data.status) }),

View file

@ -1,5 +1,5 @@
import { Container } from 'typedi'; import { Container } from 'typedi';
import { UserRepository } from '@db/repositories'; import { UserRepository } from '@db/repositories/user.repository';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
import pick from 'lodash/pick'; import pick from 'lodash/pick';
import { validate as uuidValidate } from 'uuid'; import { validate as uuidValidate } from 'uuid';

View file

@ -10,6 +10,8 @@ import type { Role } from '@db/entities/Role';
import config from '@/config'; import config from '@/config';
import { TagService } from '@/services/tag.service'; import { TagService } from '@/services/tag.service';
import Container from 'typedi'; import Container from 'typedi';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
function insertIf(condition: boolean, elements: string[]): string[] { function insertIf(condition: boolean, elements: string[]): string[] {
return condition ? elements : []; return condition ? elements : [];
@ -17,7 +19,7 @@ function insertIf(condition: boolean, elements: string[]): string[] {
export async function getSharedWorkflowIds(user: User): Promise<string[]> { export async function getSharedWorkflowIds(user: User): Promise<string[]> {
const where = user.globalRole.name === 'owner' ? {} : { userId: user.id }; const where = user.globalRole.name === 'owner' ? {} : { userId: user.id };
const sharedWorkflows = await Db.collections.SharedWorkflow.find({ const sharedWorkflows = await Container.get(SharedWorkflowRepository).find({
where, where,
select: ['workflowId'], select: ['workflowId'],
}); });
@ -28,7 +30,7 @@ export async function getSharedWorkflow(
user: User, user: User,
workflowId?: string | undefined, workflowId?: string | undefined,
): Promise<SharedWorkflow | null> { ): Promise<SharedWorkflow | null> {
return Db.collections.SharedWorkflow.findOne({ return Container.get(SharedWorkflowRepository).findOne({
where: { where: {
...(!user.isOwner && { userId: user.id }), ...(!user.isOwner && { userId: user.id }),
...(workflowId && { workflowId }), ...(workflowId && { workflowId }),
@ -44,7 +46,7 @@ export async function getSharedWorkflows(
workflowIds?: string[]; workflowIds?: string[];
}, },
): Promise<SharedWorkflow[]> { ): Promise<SharedWorkflow[]> {
return Db.collections.SharedWorkflow.find({ return Container.get(SharedWorkflowRepository).find({
where: { where: {
...(!user.isOwner && { userId: user.id }), ...(!user.isOwner && { userId: user.id }),
...(options.workflowIds && { workflowId: In(options.workflowIds) }), ...(options.workflowIds && { workflowId: In(options.workflowIds) }),
@ -54,7 +56,7 @@ export async function getSharedWorkflows(
} }
export async function getWorkflowById(id: string): Promise<WorkflowEntity | null> { export async function getWorkflowById(id: string): Promise<WorkflowEntity | null> {
return Db.collections.Workflow.findOne({ return Container.get(WorkflowRepository).findOne({
where: { id }, where: { id },
}); });
} }
@ -97,28 +99,34 @@ export async function createWorkflow(
} }
export async function setWorkflowAsActive(workflow: WorkflowEntity): Promise<UpdateResult> { export async function setWorkflowAsActive(workflow: WorkflowEntity): Promise<UpdateResult> {
return Db.collections.Workflow.update(workflow.id, { active: true, updatedAt: new Date() }); return Container.get(WorkflowRepository).update(workflow.id, {
active: true,
updatedAt: new Date(),
});
} }
export async function setWorkflowAsInactive(workflow: WorkflowEntity): Promise<UpdateResult> { export async function setWorkflowAsInactive(workflow: WorkflowEntity): Promise<UpdateResult> {
return Db.collections.Workflow.update(workflow.id, { active: false, updatedAt: new Date() }); return Container.get(WorkflowRepository).update(workflow.id, {
active: false,
updatedAt: new Date(),
});
} }
export async function deleteWorkflow(workflow: WorkflowEntity): Promise<WorkflowEntity> { export async function deleteWorkflow(workflow: WorkflowEntity): Promise<WorkflowEntity> {
return Db.collections.Workflow.remove(workflow); return Container.get(WorkflowRepository).remove(workflow);
} }
export async function getWorkflowsAndCount( export async function getWorkflowsAndCount(
options: FindManyOptions<WorkflowEntity>, options: FindManyOptions<WorkflowEntity>,
): Promise<[WorkflowEntity[], number]> { ): Promise<[WorkflowEntity[], number]> {
return Db.collections.Workflow.findAndCount(options); return Container.get(WorkflowRepository).findAndCount(options);
} }
export async function updateWorkflow( export async function updateWorkflow(
workflowId: string, workflowId: string,
updateData: WorkflowEntity, updateData: WorkflowEntity,
): Promise<UpdateResult> { ): Promise<UpdateResult> {
return Db.collections.Workflow.update(workflowId, updateData); return Container.get(WorkflowRepository).update(workflowId, updateData);
} }
export function parseTagNames(tags: string): string[] { export function parseTagNames(tags: string): string[] {

View file

@ -85,7 +85,6 @@ import { executionsController } from '@/executions/executions.controller';
import { isApiEnabled, loadPublicApiVersions } from '@/PublicApi'; import { isApiEnabled, loadPublicApiVersions } from '@/PublicApi';
import { whereClause } from '@/UserManagement/UserManagementHelper'; import { whereClause } from '@/UserManagement/UserManagementHelper';
import { UserManagementMailer } from '@/UserManagement/email'; import { UserManagementMailer } from '@/UserManagement/email';
import * as Db from '@/Db';
import type { ICredentialsOverwrite, IDiagnosticInfo, IExecutionsStopData } from '@/Interfaces'; import type { ICredentialsOverwrite, IDiagnosticInfo, IExecutionsStopData } from '@/Interfaces';
import { ActiveExecutions } from '@/ActiveExecutions'; import { ActiveExecutions } from '@/ActiveExecutions';
import { CredentialsOverwrites } from '@/CredentialsOverwrites'; import { CredentialsOverwrites } from '@/CredentialsOverwrites';
@ -119,8 +118,14 @@ import {
} from './sso/ssoHelpers'; } from './sso/ssoHelpers';
import { SourceControlService } from '@/environments/sourceControl/sourceControl.service.ee'; import { SourceControlService } from '@/environments/sourceControl/sourceControl.service.ee';
import { SourceControlController } from '@/environments/sourceControl/sourceControl.controller.ee'; import { SourceControlController } from '@/environments/sourceControl/sourceControl.controller.ee';
import { ExecutionRepository, SettingsRepository } from '@db/repositories';
import type { ExecutionEntity } from '@db/entities/ExecutionEntity'; import type { ExecutionEntity } from '@db/entities/ExecutionEntity';
import { ExecutionRepository } from '@db/repositories/execution.repository';
import { SettingsRepository } from '@db/repositories/settings.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { MfaService } from './Mfa/mfa.service'; import { MfaService } from './Mfa/mfa.service';
import { handleMfaDisable, isMfaFeatureEnabled } from './Mfa/helpers'; import { handleMfaDisable, isMfaFeatureEnabled } from './Mfa/helpers';
import type { FrontendService } from './services/frontend.service'; import type { FrontendService } from './services/frontend.service';
@ -236,18 +241,19 @@ export class Server extends AbstractServer {
void this.loadNodesAndCredentials.setupHotReload(); void this.loadNodesAndCredentials.setupHotReload();
} }
void Db.collections.Workflow.findOne({ void Container.get(WorkflowRepository)
select: ['createdAt'], .findOne({
order: { createdAt: 'ASC' }, select: ['createdAt'],
where: {}, order: { createdAt: 'ASC' },
}).then(async (workflow) => where: {},
Container.get(InternalHooks).onServerStarted(diagnosticInfo, workflow?.createdAt), })
); .then(async (workflow) =>
Container.get(InternalHooks).onServerStarted(diagnosticInfo, workflow?.createdAt),
);
} }
private async registerControllers(ignoredEndpoints: Readonly<string[]>) { private async registerControllers(ignoredEndpoints: Readonly<string[]>) {
const { app, externalHooks, activeWorkflowRunner, nodeTypes, logger } = this; const { app, externalHooks, activeWorkflowRunner, nodeTypes, logger } = this;
const repositories = Db.collections;
setupAuthMiddlewares(app, ignoredEndpoints, this.restEndpoint); setupAuthMiddlewares(app, ignoredEndpoints, this.restEndpoint);
const internalHooks = Container.get(InternalHooks); const internalHooks = Container.get(InternalHooks);
@ -281,8 +287,8 @@ export class Server extends AbstractServer {
logger, logger,
externalHooks, externalHooks,
internalHooks, internalHooks,
repositories.SharedCredentials, Container.get(SharedCredentialsRepository),
repositories.SharedWorkflow, Container.get(SharedWorkflowRepository),
activeWorkflowRunner, activeWorkflowRunner,
mailer, mailer,
jwtService, jwtService,
@ -623,7 +629,7 @@ export class Server extends AbstractServer {
ResponseHelper.send(async (req: WorkflowRequest.GetAllActivationErrors) => { ResponseHelper.send(async (req: WorkflowRequest.GetAllActivationErrors) => {
const { id: workflowId } = req.params; const { id: workflowId } = req.params;
const shared = await Db.collections.SharedWorkflow.findOne({ const shared = await Container.get(SharedWorkflowRepository).findOne({
relations: ['workflow'], relations: ['workflow'],
where: whereClause({ where: whereClause({
user: req.user, user: req.user,

View file

@ -2,13 +2,15 @@ import type { INode, Workflow } from 'n8n-workflow';
import { NodeOperationError, SubworkflowOperationError } from 'n8n-workflow'; import { NodeOperationError, SubworkflowOperationError } from 'n8n-workflow';
import type { FindOptionsWhere } from 'typeorm'; import type { FindOptionsWhere } from 'typeorm';
import { In } from 'typeorm'; import { In } from 'typeorm';
import * as Db from '@/Db';
import config from '@/config'; import config from '@/config';
import type { SharedCredentials } from '@db/entities/SharedCredentials'; import type { SharedCredentials } from '@db/entities/SharedCredentials';
import { isSharingEnabled } from './UserManagementHelper'; import { isSharingEnabled } from './UserManagementHelper';
import { OwnershipService } from '@/services/ownership.service'; import { OwnershipService } from '@/services/ownership.service';
import Container from 'typedi'; import Container from 'typedi';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { UserRepository } from '@db/repositories/user.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
export class PermissionChecker { export class PermissionChecker {
/** /**
@ -25,7 +27,7 @@ export class PermissionChecker {
// allow if requesting user is instance owner // allow if requesting user is instance owner
const user = await Db.collections.User.findOneOrFail({ const user = await Container.get(UserRepository).findOneOrFail({
where: { id: userId }, where: { id: userId },
relations: ['globalRole'], relations: ['globalRole'],
}); });
@ -38,7 +40,7 @@ export class PermissionChecker {
let workflowUserIds = [userId]; let workflowUserIds = [userId];
if (workflow.id && isSharingEnabled()) { if (workflow.id && isSharingEnabled()) {
const workflowSharings = await Db.collections.SharedWorkflow.find({ const workflowSharings = await Container.get(SharedWorkflowRepository).find({
relations: ['workflow'], relations: ['workflow'],
where: { workflowId: workflow.id }, where: { workflowId: workflow.id },
select: ['userId'], select: ['userId'],
@ -54,7 +56,7 @@ export class PermissionChecker {
credentialsWhere.roleId = role.id; credentialsWhere.roleId = role.id;
} }
const credentialSharings = await Db.collections.SharedCredentials.find({ const credentialSharings = await Container.get(SharedCredentialsRepository).find({
where: credentialsWhere, where: credentialsWhere,
}); });

View file

@ -2,7 +2,6 @@ import { In } from 'typeorm';
import { compare, genSaltSync, hash } from 'bcryptjs'; import { compare, genSaltSync, hash } from 'bcryptjs';
import { Container } from 'typedi'; import { Container } from 'typedi';
import * as Db from '@/Db';
import * as ResponseHelper from '@/ResponseHelper'; import * as ResponseHelper from '@/ResponseHelper';
import type { WhereClause } from '@/Interfaces'; import type { WhereClause } from '@/Interfaces';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
@ -11,6 +10,7 @@ import config from '@/config';
import { License } from '@/License'; import { License } from '@/License';
import { getWebhookBaseUrl } from '@/WebhookHelpers'; import { getWebhookBaseUrl } from '@/WebhookHelpers';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { UserRepository } from '@db/repositories/user.repository';
export function isSharingEnabled(): boolean { export function isSharingEnabled(): boolean {
return Container.get(License).isSharingEnabled(); return Container.get(License).isSharingEnabled();
@ -19,7 +19,7 @@ export function isSharingEnabled(): boolean {
export async function getInstanceOwner() { export async function getInstanceOwner() {
const globalOwnerRole = await Container.get(RoleService).findGlobalOwnerRole(); const globalOwnerRole = await Container.get(RoleService).findGlobalOwnerRole();
return Db.collections.User.findOneOrFail({ return Container.get(UserRepository).findOneOrFail({
relations: ['globalRole'], relations: ['globalRole'],
where: { where: {
globalRoleId: globalOwnerRole.id, globalRoleId: globalOwnerRole.id,
@ -77,7 +77,7 @@ export function validatePassword(password?: string): string {
} }
export async function getUserById(userId: string): Promise<User> { export async function getUserById(userId: string): Promise<User> {
const user = await Db.collections.User.findOneOrFail({ const user = await Container.get(UserRepository).findOneOrFail({
where: { id: userId }, where: { id: userId },
relations: ['globalRole'], relations: ['globalRole'],
}); });

View file

@ -13,7 +13,7 @@ import type {
} from '@/Interfaces'; } from '@/Interfaces';
import { WorkflowRunner } from '@/WorkflowRunner'; import { WorkflowRunner } from '@/WorkflowRunner';
import { recoverExecutionDataFromEventLogMessages } from './eventbus/MessageEventBus/recoverEvents'; import { recoverExecutionDataFromEventLogMessages } from './eventbus/MessageEventBus/recoverEvents';
import { ExecutionRepository } from '@db/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
import type { ExecutionEntity } from '@db/entities/ExecutionEntity'; import type { ExecutionEntity } from '@db/entities/ExecutionEntity';
import { OwnershipService } from './services/ownership.service'; import { OwnershipService } from './services/ownership.service';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';

View file

@ -12,7 +12,7 @@ import type {
WaitingWebhookRequest, WaitingWebhookRequest,
} from '@/Interfaces'; } from '@/Interfaces';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'; import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
import { ExecutionRepository } from '@db/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
import { OwnershipService } from './services/ownership.service'; import { OwnershipService } from './services/ownership.service';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';

View file

@ -1,5 +1,6 @@
import Container from 'typedi';
import type { INode, IWorkflowCredentials } from 'n8n-workflow'; import type { INode, IWorkflowCredentials } from 'n8n-workflow';
import * as Db from '@/Db'; import { CredentialsRepository } from '@db/repositories/credentials.repository';
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
export async function WorkflowCredentials(nodes: INode[]): Promise<IWorkflowCredentials> { export async function WorkflowCredentials(nodes: INode[]): Promise<IWorkflowCredentials> {
@ -29,7 +30,7 @@ export async function WorkflowCredentials(nodes: INode[]): Promise<IWorkflowCred
} }
if (!returnCredentials[type][nodeCredentials.id]) { if (!returnCredentials[type][nodeCredentials.id]) {
foundCredentials = await Db.collections.Credentials.findOneBy({ foundCredentials = await Container.get(CredentialsRepository).findOneBy({
id: nodeCredentials.id, id: nodeCredentials.id,
type, type,
}); });

View file

@ -52,7 +52,7 @@ import { findSubworkflowStart, isWorkflowIdValid } from '@/utils';
import { PermissionChecker } from './UserManagement/PermissionChecker'; import { PermissionChecker } from './UserManagement/PermissionChecker';
import { WorkflowsService } from './workflows/workflows.services'; import { WorkflowsService } from './workflows/workflows.services';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { ExecutionRepository } from '@db/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
import { EventsService } from '@/services/events.service'; import { EventsService } from '@/services/events.service';
import { SecretsHelper } from './SecretsHelpers'; import { SecretsHelper } from './SecretsHelpers';
import { OwnershipService } from './services/ownership.service'; import { OwnershipService } from './services/ownership.service';

View file

@ -21,7 +21,7 @@ import {
Workflow, Workflow,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import * as Db from '@/Db'; import omit from 'lodash/omit';
import type { import type {
ExecutionPayload, ExecutionPayload,
IWorkflowErrorData, IWorkflowErrorData,
@ -32,15 +32,18 @@ import { WorkflowRunner } from '@/WorkflowRunner';
import config from '@/config'; import config from '@/config';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
import omit from 'lodash/omit';
import { PermissionChecker } from './UserManagement/PermissionChecker'; import { PermissionChecker } from './UserManagement/PermissionChecker';
import { UserService } from './services/user.service'; import { UserService } from './services/user.service';
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import type { SharedWorkflow } from '@db/entities/SharedWorkflow'; import type { SharedWorkflow } from '@db/entities/SharedWorkflow';
import type { RoleNames } from '@db/entities/Role'; import type { RoleNames } from '@db/entities/Role';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { ExecutionRepository } from '@db/repositories/execution.repository';
import { RoleRepository } from '@db/repositories/role.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { RoleService } from './services/role.service'; import { RoleService } from './services/role.service';
import { ExecutionRepository, RoleRepository } from './databases/repositories';
import { VariablesService } from './environments/variables/variables.service'; import { VariablesService } from './environments/variables/variables.service';
import type { CredentialsEntity } from './databases/entities/CredentialsEntity';
import { Logger } from './Logger'; import { Logger } from './Logger';
const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType'); const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType');
@ -144,7 +147,7 @@ export async function executeErrorWorkflow(
const logger = Container.get(Logger); const logger = Container.get(Logger);
// Wrap everything in try/catch to make sure that no errors bubble up and all get caught here // Wrap everything in try/catch to make sure that no errors bubble up and all get caught here
try { try {
const workflowData = await Db.collections.Workflow.findOneBy({ id: workflowId }); const workflowData = await Container.get(WorkflowRepository).findOneBy({ id: workflowId });
if (workflowData === null) { if (workflowData === null) {
// The error workflow could not be found // The error workflow could not be found
@ -282,7 +285,7 @@ export async function executeErrorWorkflow(
* Returns the static data of workflow * Returns the static data of workflow
*/ */
export async function getStaticDataById(workflowId: string) { export async function getStaticDataById(workflowId: string) {
const workflowData = await Db.collections.Workflow.findOne({ const workflowData = await Container.get(WorkflowRepository).findOne({
select: ['staticData'], select: ['staticData'],
where: { id: workflowId }, where: { id: workflowId },
}); });
@ -330,7 +333,7 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi
credentialsByName[nodeCredentialType] = {}; credentialsByName[nodeCredentialType] = {};
} }
if (credentialsByName[nodeCredentialType][name] === undefined) { if (credentialsByName[nodeCredentialType][name] === undefined) {
const credentials = await Db.collections.Credentials.findBy({ const credentials = await Container.get(CredentialsRepository).findBy({
name, name,
type: nodeCredentialType, type: nodeCredentialType,
}); });
@ -366,7 +369,7 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi
// check if credentials for ID-type are not yet cached // check if credentials for ID-type are not yet cached
if (credentialsById[nodeCredentialType][nodeCredentials.id] === undefined) { if (credentialsById[nodeCredentialType][nodeCredentials.id] === undefined) {
// check first if ID-type combination exists // check first if ID-type combination exists
const credentials = await Db.collections.Credentials.findOneBy({ const credentials = await Container.get(CredentialsRepository).findOneBy({
id: nodeCredentials.id, id: nodeCredentials.id,
type: nodeCredentialType, type: nodeCredentialType,
}); });
@ -380,7 +383,7 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi
continue; continue;
} }
// no credentials found for ID, check if some exist for name // no credentials found for ID, check if some exist for name
const credsByName = await Db.collections.Credentials.findBy({ const credsByName = await Container.get(CredentialsRepository).findBy({
name: nodeCredentials.name, name: nodeCredentials.name,
type: nodeCredentialType, type: nodeCredentialType,
}); });
@ -429,7 +432,7 @@ export async function getSharedWorkflowIds(user: User, roles?: RoleNames[]): Pro
where.roleId = In(roleIds); where.roleId = In(roleIds);
} }
const sharedWorkflows = await Db.collections.SharedWorkflow.find({ const sharedWorkflows = await Container.get(SharedWorkflowRepository).find({
where, where,
select: ['workflowId'], select: ['workflowId'],
}); });
@ -444,19 +447,21 @@ export async function isBelowOnboardingThreshold(user: User): Promise<boolean> {
const skippedTypes = ['n8n-nodes-base.start', 'n8n-nodes-base.stickyNote']; const skippedTypes = ['n8n-nodes-base.start', 'n8n-nodes-base.stickyNote'];
const workflowOwnerRole = await Container.get(RoleService).findWorkflowOwnerRole(); const workflowOwnerRole = await Container.get(RoleService).findWorkflowOwnerRole();
const ownedWorkflowsIds = await Db.collections.SharedWorkflow.find({ const ownedWorkflowsIds = await Container.get(SharedWorkflowRepository)
where: { .find({
userId: user.id, where: {
roleId: workflowOwnerRole?.id, userId: user.id,
}, roleId: workflowOwnerRole?.id,
select: ['workflowId'], },
}).then((ownedWorkflows) => ownedWorkflows.map(({ workflowId }) => workflowId)); select: ['workflowId'],
})
.then((ownedWorkflows) => ownedWorkflows.map(({ workflowId }) => workflowId));
if (ownedWorkflowsIds.length > 15) { if (ownedWorkflowsIds.length > 15) {
belowThreshold = false; belowThreshold = false;
} else { } else {
// just fetch workflows' nodes to keep memory footprint low // just fetch workflows' nodes to keep memory footprint low
const workflows = await Db.collections.Workflow.find({ const workflows = await Container.get(WorkflowRepository).find({
where: { id: In(ownedWorkflowsIds) }, where: { id: In(ownedWorkflowsIds) },
select: ['nodes'], select: ['nodes'],
}); });

View file

@ -48,7 +48,7 @@ import { PermissionChecker } from '@/UserManagement/PermissionChecker';
import { Push } from '@/push'; import { Push } from '@/push';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { InternalHooks } from './InternalHooks'; import { InternalHooks } from './InternalHooks';
import { ExecutionRepository } from '@db/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
import { Logger } from './Logger'; import { Logger } from './Logger';
export class WorkflowRunner { export class WorkflowRunner {

View file

@ -1,4 +1,3 @@
import * as Db from '@/Db';
import { separate } from '@/utils'; import { separate } from '@/utils';
import config from '@/config'; import config from '@/config';
import { RISK_CATEGORIES } from '@/audit/constants'; import { RISK_CATEGORIES } from '@/audit/constants';
@ -9,6 +8,8 @@ import { reportNodesRisk } from '@/audit/risks/nodes.risk';
import { reportFilesystemRisk } from '@/audit/risks/filesystem.risk'; import { reportFilesystemRisk } from '@/audit/risks/filesystem.risk';
import { reportInstanceRisk } from '@/audit/risks/instance.risk'; import { reportInstanceRisk } from '@/audit/risks/instance.risk';
import type { Risk } from '@/audit/types'; import type { Risk } from '@/audit/types';
import Container from 'typedi';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
export const SYNC_MAP: Record<string, Risk.SyncReportFn> = { export const SYNC_MAP: Record<string, Risk.SyncReportFn> = {
database: reportDatabaseRisk, database: reportDatabaseRisk,
@ -35,7 +36,7 @@ export async function audit(
config.set('security.audit.daysAbandonedWorkflow', daysAbandonedWorkflow); config.set('security.audit.daysAbandonedWorkflow', daysAbandonedWorkflow);
} }
const workflows = await Db.collections.Workflow.find({ const workflows = await Container.get(WorkflowRepository).find({
select: ['id', 'name', 'active', 'nodes', 'connections'], select: ['id', 'name', 'active', 'nodes', 'connections'],
}); });

View file

@ -6,11 +6,9 @@ import config from '@/config';
import { CREDENTIALS_REPORT } from '@/audit/constants'; import { CREDENTIALS_REPORT } from '@/audit/constants';
import type { Risk } from '@/audit/types'; import type { Risk } from '@/audit/types';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { import { CredentialsRepository } from '@db/repositories/credentials.repository';
CredentialsRepository, import { ExecutionRepository } from '@db/repositories/execution.repository';
ExecutionDataRepository, import { ExecutionDataRepository } from '@db/repositories/executionData.repository';
ExecutionRepository,
} from '@db/repositories';
async function getAllCredsInUse(workflows: WorkflowEntity[]) { async function getAllCredsInUse(workflows: WorkflowEntity[]) {
const credsInAnyUse = new Set<string>(); const credsInAnyUse = new Set<string>();

View file

@ -1,7 +1,6 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import type { Response } from 'express'; import type { Response } from 'express';
import { createHash } from 'crypto'; import { createHash } from 'crypto';
import * as Db from '@/Db';
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants'; import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants';
import type { JwtPayload, JwtToken } from '@/Interfaces'; import type { JwtPayload, JwtToken } from '@/Interfaces';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
@ -9,6 +8,7 @@ import config from '@/config';
import * as ResponseHelper from '@/ResponseHelper'; import * as ResponseHelper from '@/ResponseHelper';
import { License } from '@/License'; import { License } from '@/License';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { UserRepository } from '@db/repositories/user.repository';
export function issueJWT(user: User): JwtToken { export function issueJWT(user: User): JwtToken {
const { id, email, password } = user; const { id, email, password } = user;
@ -50,7 +50,7 @@ export const createPasswordSha = (user: User) =>
.digest('hex'); .digest('hex');
export async function resolveJwtContent(jwtPayload: JwtPayload): Promise<User> { export async function resolveJwtContent(jwtPayload: JwtPayload): Promise<User> {
const user = await Db.collections.User.findOne({ const user = await Container.get(UserRepository).findOne({
where: { id: jwtPayload.id }, where: { id: jwtPayload.id },
relations: ['globalRole'], relations: ['globalRole'],
}); });

View file

@ -1,16 +1,16 @@
import * as Db from '@/Db';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
import { compareHash } from '@/UserManagement/UserManagementHelper'; import { compareHash } from '@/UserManagement/UserManagementHelper';
import * as ResponseHelper from '@/ResponseHelper'; import * as ResponseHelper from '@/ResponseHelper';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { isLdapLoginEnabled } from '@/Ldap/helpers'; import { isLdapLoginEnabled } from '@/Ldap/helpers';
import { UserRepository } from '@db/repositories/user.repository';
export const handleEmailLogin = async ( export const handleEmailLogin = async (
email: string, email: string,
password: string, password: string,
): Promise<User | undefined> => { ): Promise<User | undefined> => {
const user = await Db.collections.User.findOne({ const user = await Container.get(UserRepository).findOne({
where: { email }, where: { email },
relations: ['globalRole', 'authIdentities'], relations: ['globalRole', 'authIdentities'],
}); });

View file

@ -1,4 +1,4 @@
import type { User } from '@/databases/entities/User'; import type { User } from '@db/entities/User';
import type { Workflow } from 'n8n-workflow'; import type { Workflow } from 'n8n-workflow';
import { Service } from 'typedi'; import { Service } from 'typedi';

View file

@ -20,7 +20,7 @@ import { PostHogClient } from '@/posthog';
import { License } from '@/License'; import { License } from '@/License';
import { ExternalSecretsManager } from '@/ExternalSecrets/ExternalSecretsManager.ee'; import { ExternalSecretsManager } from '@/ExternalSecrets/ExternalSecretsManager.ee';
import { initExpressionEvaluator } from '@/ExpressionEvalator'; import { initExpressionEvaluator } from '@/ExpressionEvalator';
import { generateHostInstanceId } from '../databases/utils/generators'; import { generateHostInstanceId } from '@db/utils/generators';
import { WorkflowHistoryManager } from '@/workflows/workflowHistory/workflowHistoryManager.ee'; import { WorkflowHistoryManager } from '@/workflows/workflowHistory/workflowHistoryManager.ee';
export abstract class BaseCommand extends Command { export abstract class BaseCommand extends Command {

View file

@ -5,13 +5,13 @@ import type { IWorkflowBase } from 'n8n-workflow';
import { ExecutionBaseError } from 'n8n-workflow'; import { ExecutionBaseError } from 'n8n-workflow';
import { ActiveExecutions } from '@/ActiveExecutions'; import { ActiveExecutions } from '@/ActiveExecutions';
import * as Db from '@/Db';
import { WorkflowRunner } from '@/WorkflowRunner'; import { WorkflowRunner } from '@/WorkflowRunner';
import type { IWorkflowExecutionDataProcess } from '@/Interfaces'; import type { IWorkflowExecutionDataProcess } from '@/Interfaces';
import { getInstanceOwner } from '@/UserManagement/UserManagementHelper'; import { getInstanceOwner } from '@/UserManagement/UserManagementHelper';
import { findCliWorkflowStart, isWorkflowIdValid } from '@/utils'; import { findCliWorkflowStart, isWorkflowIdValid } from '@/utils';
import { BaseCommand } from './BaseCommand'; import { BaseCommand } from './BaseCommand';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
export class Execute extends BaseCommand { export class Execute extends BaseCommand {
static description = '\nExecutes a given workflow'; static description = '\nExecutes a given workflow';
@ -81,7 +81,7 @@ export class Execute extends BaseCommand {
if (flags.id) { if (flags.id) {
// Id of workflow is given // Id of workflow is given
workflowId = flags.id; workflowId = flags.id;
workflowData = await Db.collections.Workflow.findOneBy({ id: workflowId }); workflowData = await Container.get(WorkflowRepository).findOneBy({ id: workflowId });
if (workflowData === null) { if (workflowData === null) {
this.logger.info(`The workflow with the id "${workflowId}" does not exist.`); this.logger.info(`The workflow with the id "${workflowId}" does not exist.`);
process.exit(1); process.exit(1);

View file

@ -9,7 +9,6 @@ import { diff } from 'json-diff';
import pick from 'lodash/pick'; import pick from 'lodash/pick';
import { ActiveExecutions } from '@/ActiveExecutions'; import { ActiveExecutions } from '@/ActiveExecutions';
import * as Db from '@/Db';
import { WorkflowRunner } from '@/WorkflowRunner'; import { WorkflowRunner } from '@/WorkflowRunner';
import type { IWorkflowDb, IWorkflowExecutionDataProcess } from '@/Interfaces'; import type { IWorkflowDb, IWorkflowExecutionDataProcess } from '@/Interfaces';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
@ -24,6 +23,7 @@ import type {
IResult, IResult,
IWorkflowExecutionProgress, IWorkflowExecutionProgress,
} from '../types/commands.types'; } from '../types/commands.types';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
const re = /\d+/; const re = /\d+/;
@ -278,7 +278,7 @@ export class ExecuteBatch extends BaseCommand {
ExecuteBatch.instanceOwner = await getInstanceOwner(); ExecuteBatch.instanceOwner = await getInstanceOwner();
const query = Db.collections.Workflow.createQueryBuilder('workflows'); const query = Container.get(WorkflowRepository).createQueryBuilder('workflows');
if (ids.length > 0) { if (ids.length > 0) {
query.andWhere('workflows.id in (:...ids)', { ids }); query.andWhere('workflows.id in (:...ids)', { ids });

View file

@ -3,9 +3,10 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import type { FindOptionsWhere } from 'typeorm'; import type { FindOptionsWhere } from 'typeorm';
import { Credentials } from 'n8n-core'; import { Credentials } from 'n8n-core';
import * as Db from '@/Db';
import type { ICredentialsDb, ICredentialsDecryptedDb } from '@/Interfaces'; import type { ICredentialsDb, ICredentialsDecryptedDb } from '@/Interfaces';
import { BaseCommand } from '../BaseCommand'; import { BaseCommand } from '../BaseCommand';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import Container from 'typedi';
export class ExportCredentialsCommand extends BaseCommand { export class ExportCredentialsCommand extends BaseCommand {
static description = 'Export credentials'; static description = 'Export credentials';
@ -110,7 +111,8 @@ export class ExportCredentialsCommand extends BaseCommand {
findQuery.id = flags.id; findQuery.id = flags.id;
} }
const credentials: ICredentialsDb[] = await Db.collections.Credentials.findBy(findQuery); const credentials: ICredentialsDb[] =
await Container.get(CredentialsRepository).findBy(findQuery);
if (flags.decrypted) { if (flags.decrypted) {
for (let i = 0; i < credentials.length; i++) { for (let i = 0; i < credentials.length; i++) {

View file

@ -2,9 +2,10 @@ import { flags } from '@oclif/command';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import type { FindOptionsWhere } from 'typeorm'; import type { FindOptionsWhere } from 'typeorm';
import * as Db from '@/Db';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { BaseCommand } from '../BaseCommand'; import { BaseCommand } from '../BaseCommand';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import Container from 'typedi';
export class ExportWorkflowsCommand extends BaseCommand { export class ExportWorkflowsCommand extends BaseCommand {
static description = 'Export workflows'; static description = 'Export workflows';
@ -104,7 +105,7 @@ export class ExportWorkflowsCommand extends BaseCommand {
findQuery.id = flags.id; findQuery.id = flags.id;
} }
const workflows = await Db.collections.Workflow.find({ const workflows = await Container.get(WorkflowRepository).find({
where: findQuery, where: findQuery,
relations: ['tags'], relations: ['tags'],
}); });

View file

@ -15,6 +15,7 @@ import type { ICredentialsEncrypted } from 'n8n-workflow';
import { jsonParse } from 'n8n-workflow'; import { jsonParse } from 'n8n-workflow';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { UM_FIX_INSTRUCTION } from '@/constants'; import { UM_FIX_INSTRUCTION } from '@/constants';
import { UserRepository } from '@db/repositories/user.repository';
export class ImportCredentialsCommand extends BaseCommand { export class ImportCredentialsCommand extends BaseCommand {
static description = 'Import credentials'; static description = 'Import credentials';
@ -175,7 +176,7 @@ export class ImportCredentialsCommand extends BaseCommand {
const owner = const owner =
ownerGlobalRole && ownerGlobalRole &&
(await Db.collections.User.findOneBy({ globalRoleId: ownerGlobalRole.id })); (await Container.get(UserRepository).findOneBy({ globalRoleId: ownerGlobalRole.id }));
if (!owner) { if (!owner) {
throw new Error(`Failed to find owner. ${UM_FIX_INSTRUCTION}`); throw new Error(`Failed to find owner. ${UM_FIX_INSTRUCTION}`);
@ -185,7 +186,7 @@ export class ImportCredentialsCommand extends BaseCommand {
} }
private async getAssignee(userId: string) { private async getAssignee(userId: string) {
const user = await Db.collections.User.findOneBy({ id: userId }); const user = await Container.get(UserRepository).findOneBy({ id: userId });
if (!user) { if (!user) {
throw new Error(`Failed to find user with ID ${userId}`); throw new Error(`Failed to find user with ID ${userId}`);

View file

@ -19,6 +19,8 @@ import { generateNanoId } from '@db/utils/generators';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { TagService } from '@/services/tag.service'; import { TagService } from '@/services/tag.service';
import { UM_FIX_INSTRUCTION } from '@/constants'; import { UM_FIX_INSTRUCTION } from '@/constants';
import { UserRepository } from '@db/repositories/user.repository';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
function assertHasWorkflowsToImport(workflows: unknown): asserts workflows is IWorkflowToImport[] { function assertHasWorkflowsToImport(workflows: unknown): asserts workflows is IWorkflowToImport[] {
if (!Array.isArray(workflows)) { if (!Array.isArray(workflows)) {
@ -95,7 +97,7 @@ export class ImportWorkflowsCommand extends BaseCommand {
await this.initOwnerWorkflowRole(); await this.initOwnerWorkflowRole();
const user = flags.userId ? await this.getAssignee(flags.userId) : await this.getOwner(); const user = flags.userId ? await this.getAssignee(flags.userId) : await this.getOwner();
const credentials = await Db.collections.Credentials.find(); const credentials = await Container.get(CredentialsRepository).find();
const tags = await this.tagService.getAll(); const tags = await this.tagService.getAll();
let totalImported = 0; let totalImported = 0;
@ -239,7 +241,7 @@ export class ImportWorkflowsCommand extends BaseCommand {
const owner = const owner =
ownerGlobalRole && ownerGlobalRole &&
(await Db.collections.User.findOneBy({ globalRoleId: ownerGlobalRole?.id })); (await Container.get(UserRepository).findOneBy({ globalRoleId: ownerGlobalRole?.id }));
if (!owner) { if (!owner) {
throw new Error(`Failed to find owner. ${UM_FIX_INSTRUCTION}`); throw new Error(`Failed to find owner. ${UM_FIX_INSTRUCTION}`);
@ -249,7 +251,7 @@ export class ImportWorkflowsCommand extends BaseCommand {
} }
private async getAssignee(userId: string) { private async getAssignee(userId: string) {
const user = await Db.collections.User.findOneBy({ id: userId }); const user = await Container.get(UserRepository).findOneBy({ id: userId });
if (!user) { if (!user) {
throw new Error(`Failed to find user with ID ${userId}`); throw new Error(`Failed to find user with ID ${userId}`);

View file

@ -1,22 +1,25 @@
import * as Db from '@/Db'; import Container from 'typedi';
import { LDAP_DEFAULT_CONFIGURATION, LDAP_FEATURE_NAME } from '@/Ldap/constants'; import { LDAP_DEFAULT_CONFIGURATION, LDAP_FEATURE_NAME } from '@/Ldap/constants';
import { In } from 'typeorm'; import { In } from 'typeorm';
import { AuthIdentityRepository } from '@db/repositories/authIdentity.repository';
import { AuthProviderSyncHistoryRepository } from '@db/repositories/authProviderSyncHistory.repository';
import { SettingsRepository } from '@db/repositories/settings.repository';
import { UserRepository } from '@db/repositories/user.repository';
import { BaseCommand } from '../BaseCommand'; import { BaseCommand } from '../BaseCommand';
export class Reset extends BaseCommand { export class Reset extends BaseCommand {
static description = '\nResets the database to the default ldap state'; static description = '\nResets the database to the default ldap state';
async run(): Promise<void> { async run(): Promise<void> {
const { AuthIdentity, AuthProviderSyncHistory, Settings, User } = Db.collections; const ldapIdentities = await Container.get(AuthIdentityRepository).find({
const ldapIdentities = await AuthIdentity.find({
where: { providerType: 'ldap' }, where: { providerType: 'ldap' },
select: ['userId'], select: ['userId'],
}); });
await AuthProviderSyncHistory.delete({ providerType: 'ldap' }); await Container.get(AuthProviderSyncHistoryRepository).delete({ providerType: 'ldap' });
await AuthIdentity.delete({ providerType: 'ldap' }); await Container.get(AuthIdentityRepository).delete({ providerType: 'ldap' });
await User.delete({ id: In(ldapIdentities.map((i) => i.userId)) }); await Container.get(UserRepository).delete({ id: In(ldapIdentities.map((i) => i.userId)) });
await Settings.delete({ key: LDAP_FEATURE_NAME }); await Container.get(SettingsRepository).delete({ key: LDAP_FEATURE_NAME });
await Settings.insert({ await Container.get(SettingsRepository).insert({
key: LDAP_FEATURE_NAME, key: LDAP_FEATURE_NAME,
value: JSON.stringify(LDAP_DEFAULT_CONFIGURATION), value: JSON.stringify(LDAP_DEFAULT_CONFIGURATION),
loadOnStartup: true, loadOnStartup: true,

View file

@ -1,6 +1,7 @@
import * as Db from '@/Db';
import { SETTINGS_LICENSE_CERT_KEY } from '@/constants'; import { SETTINGS_LICENSE_CERT_KEY } from '@/constants';
import { BaseCommand } from '../BaseCommand'; import { BaseCommand } from '../BaseCommand';
import { SettingsRepository } from '@db/repositories/settings.repository';
import Container from 'typedi';
export class ClearLicenseCommand extends BaseCommand { export class ClearLicenseCommand extends BaseCommand {
static description = 'Clear license'; static description = 'Clear license';
@ -9,7 +10,7 @@ export class ClearLicenseCommand extends BaseCommand {
async run() { async run() {
this.logger.info('Clearing license from database.'); this.logger.info('Clearing license from database.');
await Db.collections.Settings.delete({ await Container.get(SettingsRepository).delete({
key: SETTINGS_LICENSE_CERT_KEY, key: SETTINGS_LICENSE_CERT_KEY,
}); });
this.logger.info('Done. Restart n8n to take effect.'); this.logger.info('Done. Restart n8n to take effect.');

View file

@ -1,8 +1,9 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import type { FindOptionsWhere } from 'typeorm'; import type { FindOptionsWhere } from 'typeorm';
import * as Db from '@/Db';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { BaseCommand } from '../BaseCommand'; import { BaseCommand } from '../BaseCommand';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import Container from 'typedi';
export class ListWorkflowCommand extends BaseCommand { export class ListWorkflowCommand extends BaseCommand {
static description = '\nList workflows'; static description = '\nList workflows';
@ -36,7 +37,7 @@ export class ListWorkflowCommand extends BaseCommand {
findQuery.active = flags.active === 'true'; findQuery.active = flags.active === 'true';
} }
const workflows = await Db.collections.Workflow.findBy(findQuery); const workflows = await Container.get(WorkflowRepository).findBy(findQuery);
if (flags.onlyId) { if (flags.onlyId) {
workflows.forEach((workflow) => this.logger.info(workflow.id)); workflows.forEach((workflow) => this.logger.info(workflow.id));
} else { } else {

View file

@ -1,6 +1,7 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import * as Db from '@/Db';
import { BaseCommand } from '../BaseCommand'; import { BaseCommand } from '../BaseCommand';
import Container from 'typedi';
import { UserRepository } from '@db/repositories/user.repository';
export class DisableMFACommand extends BaseCommand { export class DisableMFACommand extends BaseCommand {
static description = 'Disable MFA authentication for a user'; static description = 'Disable MFA authentication for a user';
@ -27,7 +28,7 @@ export class DisableMFACommand extends BaseCommand {
return; return;
} }
const updateOperationResult = await Db.collections.User.update( const updateOperationResult = await Container.get(UserRepository).update(
{ email: flags.email }, { email: flags.email },
{ mfaSecret: null, mfaRecoveryCodes: [], mfaEnabled: false }, { mfaSecret: null, mfaRecoveryCodes: [], mfaEnabled: false },
); );

View file

@ -18,7 +18,6 @@ import config from '@/config';
import { ActiveExecutions } from '@/ActiveExecutions'; import { ActiveExecutions } from '@/ActiveExecutions';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import * as Db from '@/Db';
import * as GenericHelpers from '@/GenericHelpers'; import * as GenericHelpers from '@/GenericHelpers';
import { Server } from '@/Server'; import { Server } from '@/Server';
import { EDITOR_UI_DIST_DIR, LICENSE_FEATURES } from '@/constants'; import { EDITOR_UI_DIST_DIR, LICENSE_FEATURES } from '@/constants';
@ -30,6 +29,8 @@ import { IConfig } from '@oclif/config';
import { SingleMainInstancePublisher } from '@/services/orchestration/main/SingleMainInstance.publisher'; import { SingleMainInstancePublisher } from '@/services/orchestration/main/SingleMainInstance.publisher';
import { OrchestrationHandlerMainService } from '@/services/orchestration/main/orchestration.handler.main.service'; import { OrchestrationHandlerMainService } from '@/services/orchestration/main/orchestration.handler.main.service';
import { PruningService } from '@/services/pruning.service'; import { PruningService } from '@/services/pruning.service';
import { SettingsRepository } from '@db/repositories/settings.repository';
import { ExecutionRepository } from '@db/repositories/execution.repository';
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
const open = require('open'); const open = require('open');
@ -286,7 +287,9 @@ export class Start extends BaseCommand {
} }
// Load settings from database and set them to config. // Load settings from database and set them to config.
const databaseSettings = await Db.collections.Settings.findBy({ loadOnStartup: true }); const databaseSettings = await Container.get(SettingsRepository).findBy({
loadOnStartup: true,
});
databaseSettings.forEach((setting) => { databaseSettings.forEach((setting) => {
config.set(setting.key, jsonParse(setting.value, { fallbackValue: setting.value })); config.set(setting.key, jsonParse(setting.value, { fallbackValue: setting.value }));
}); });
@ -304,7 +307,7 @@ export class Start extends BaseCommand {
if (dbType === 'sqlite') { if (dbType === 'sqlite') {
const shouldRunVacuum = config.getEnv('database.sqlite.executeVacuumOnStartup'); const shouldRunVacuum = config.getEnv('database.sqlite.executeVacuumOnStartup');
if (shouldRunVacuum) { if (shouldRunVacuum) {
await Db.collections.Execution.query('VACUUM;'); await Container.get(ExecutionRepository).query('VACUUM;');
} }
} }

View file

@ -1,9 +1,10 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import type { FindOptionsWhere } from 'typeorm'; import type { FindOptionsWhere } from 'typeorm';
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'; import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
import * as Db from '@/Db';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { BaseCommand } from '../BaseCommand'; import { BaseCommand } from '../BaseCommand';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import Container from 'typedi';
export class UpdateWorkflowCommand extends BaseCommand { export class UpdateWorkflowCommand extends BaseCommand {
static description = 'Update workflows'; static description = 'Update workflows';
@ -64,7 +65,7 @@ export class UpdateWorkflowCommand extends BaseCommand {
findQuery.active = true; findQuery.active = true;
} }
await Db.collections.Workflow.update(findQuery, updateQuery); await Container.get(WorkflowRepository).update(findQuery, updateQuery);
this.logger.info('Done'); this.logger.info('Done');
} }

View file

@ -1,10 +1,14 @@
import { Container } from 'typedi'; import { Container } from 'typedi';
import { Not } from 'typeorm'; import { Not } from 'typeorm';
import * as Db from '@/Db';
import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { User } from '@db/entities/User'; import { User } from '@db/entities/User';
import { BaseCommand } from '../BaseCommand'; import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SettingsRepository } from '@db/repositories/settings.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { UserRepository } from '@db/repositories/user.repository';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { BaseCommand } from '../BaseCommand';
const defaultUserProps = { const defaultUserProps = {
firstName: null, firstName: null,
@ -24,34 +28,34 @@ export class Reset extends BaseCommand {
const ownerWorkflowRole = await Container.get(RoleService).findWorkflowOwnerRole(); const ownerWorkflowRole = await Container.get(RoleService).findWorkflowOwnerRole();
const ownerCredentialRole = await Container.get(RoleService).findCredentialOwnerRole(); const ownerCredentialRole = await Container.get(RoleService).findCredentialOwnerRole();
await Db.collections.SharedWorkflow.update( await Container.get(SharedWorkflowRepository).update(
{ userId: Not(owner.id), roleId: ownerWorkflowRole.id }, { userId: Not(owner.id), roleId: ownerWorkflowRole.id },
{ user: owner }, { user: owner },
); );
await Db.collections.SharedCredentials.update( await Container.get(SharedCredentialsRepository).update(
{ userId: Not(owner.id), roleId: ownerCredentialRole.id }, { userId: Not(owner.id), roleId: ownerCredentialRole.id },
{ user: owner }, { user: owner },
); );
await Db.collections.User.delete({ id: Not(owner.id) }); await Container.get(UserRepository).delete({ id: Not(owner.id) });
await Db.collections.User.save(Object.assign(owner, defaultUserProps)); await Container.get(UserRepository).save(Object.assign(owner, defaultUserProps));
const danglingCredentials: CredentialsEntity[] = const danglingCredentials: CredentialsEntity[] = await Container.get(CredentialsRepository)
await Db.collections.Credentials.createQueryBuilder('credentials') .createQueryBuilder('credentials')
.leftJoinAndSelect('credentials.shared', 'shared') .leftJoinAndSelect('credentials.shared', 'shared')
.where('shared.credentialsId is null') .where('shared.credentialsId is null')
.getMany(); .getMany();
const newSharedCredentials = danglingCredentials.map((credentials) => const newSharedCredentials = danglingCredentials.map((credentials) =>
Db.collections.SharedCredentials.create({ Container.get(SharedCredentialsRepository).create({
credentials, credentials,
user: owner, user: owner,
role: ownerCredentialRole, role: ownerCredentialRole,
}), }),
); );
await Db.collections.SharedCredentials.save(newSharedCredentials); await Container.get(SharedCredentialsRepository).save(newSharedCredentials);
await Db.collections.Settings.update( await Container.get(SettingsRepository).update(
{ key: 'userManagement.isInstanceOwnerSetUp' }, { key: 'userManagement.isInstanceOwnerSetUp' },
{ value: 'false' }, { value: 'false' },
); );
@ -62,7 +66,7 @@ export class Reset extends BaseCommand {
async getInstanceOwner(): Promise<User> { async getInstanceOwner(): Promise<User> {
const globalRole = await Container.get(RoleService).findGlobalOwnerRole(); const globalRole = await Container.get(RoleService).findGlobalOwnerRole();
const owner = await Db.collections.User.findOneBy({ globalRoleId: globalRole.id }); const owner = await Container.get(UserRepository).findOneBy({ globalRoleId: globalRole.id });
if (owner) return owner; if (owner) return owner;
@ -70,9 +74,9 @@ export class Reset extends BaseCommand {
Object.assign(user, { ...defaultUserProps, globalRole }); Object.assign(user, { ...defaultUserProps, globalRole });
await Db.collections.User.save(user); await Container.get(UserRepository).save(user);
return Db.collections.User.findOneByOrFail({ globalRoleId: globalRole.id }); return Container.get(UserRepository).findOneByOrFail({ globalRoleId: globalRole.id });
} }
async catch(error: Error): Promise<void> { async catch(error: Error): Promise<void> {

View file

@ -27,7 +27,8 @@ import { Queue } from '@/Queue';
import { generateFailedExecutionFromError } from '@/WorkflowHelpers'; import { generateFailedExecutionFromError } from '@/WorkflowHelpers';
import { N8N_VERSION } from '@/constants'; import { N8N_VERSION } from '@/constants';
import { BaseCommand } from './BaseCommand'; import { BaseCommand } from './BaseCommand';
import { ExecutionRepository } from '@db/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { OwnershipService } from '@/services/ownership.service'; import { OwnershipService } from '@/services/ownership.service';
import type { ICredentialsOverwrite } from '@/Interfaces'; import type { ICredentialsOverwrite } from '@/Interfaces';
import { CredentialsOverwrites } from '@/CredentialsOverwrites'; import { CredentialsOverwrites } from '@/CredentialsOverwrites';
@ -137,7 +138,7 @@ export class Worker extends BaseCommand {
let { staticData } = fullExecutionData.workflowData; let { staticData } = fullExecutionData.workflowData;
if (loadStaticData) { if (loadStaticData) {
const workflowData = await Db.collections.Workflow.findOne({ const workflowData = await Container.get(WorkflowRepository).findOne({
select: ['id', 'staticData'], select: ['id', 'staticData'],
where: { where: {
id: workflowId, id: workflowId,

View file

@ -3,7 +3,9 @@ import { Service } from 'typedi';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import config from '@/config'; import config from '@/config';
import type { Role } from '@db/entities/Role'; import type { Role } from '@db/entities/Role';
import { RoleRepository, SettingsRepository, UserRepository } from '@db/repositories'; import { RoleRepository } from '@db/repositories/role.repository';
import { SettingsRepository } from '@db/repositories/settings.repository';
import { UserRepository } from '@db/repositories/user.repository';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import { hashPassword } from '@/UserManagement/UserManagementHelper'; import { hashPassword } from '@/UserManagement/UserManagementHelper';
import { eventBus } from '@/eventbus/MessageEventBus/MessageEventBus'; import { eventBus } from '@/eventbus/MessageEventBus/MessageEventBus';

View file

@ -4,7 +4,8 @@ import type { ICredentialDataDecryptedObject, IWorkflowExecuteAdditionalData } f
import config from '@/config'; import config from '@/config';
import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
import { CredentialsRepository, SharedCredentialsRepository } from '@db/repositories'; import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import type { ICredentialsDb } from '@/Interfaces'; import type { ICredentialsDb } from '@/Interfaces';
import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';
import type { OAuthRequest } from '@/requests'; import type { OAuthRequest } from '@/requests';

View file

@ -8,7 +8,7 @@ import { Response } from 'express';
import { Config } from '@/config'; import { Config } from '@/config';
import { OwnerRequest } from '@/requests'; import { OwnerRequest } from '@/requests';
import { IInternalHooksClass } from '@/Interfaces'; import { IInternalHooksClass } from '@/Interfaces';
import { SettingsRepository } from '@db/repositories'; import { SettingsRepository } from '@db/repositories/settings.repository';
import { PostHogClient } from '@/posthog'; import { PostHogClient } from '@/posthog';
import { UserService } from '@/services/user.service'; import { UserService } from '@/services/user.service';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';

View file

@ -29,7 +29,8 @@ import type { PublicUser, ITelemetryUserDeletionData } from '@/Interfaces';
import { AuthIdentity } from '@db/entities/AuthIdentity'; import { AuthIdentity } from '@db/entities/AuthIdentity';
import { PostHogClient } from '@/posthog'; import { PostHogClient } from '@/posthog';
import { isSamlLicensedAndEnabled } from '../sso/saml/samlHelpers'; import { isSamlLicensedAndEnabled } from '../sso/saml/samlHelpers';
import { SharedCredentialsRepository, SharedWorkflowRepository } from '@db/repositories'; import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { plainToInstance } from 'class-transformer'; import { plainToInstance } from 'class-transformer';
import { License } from '@/License'; import { License } from '@/License';
import { Container } from 'typedi'; import { Container } from 'typedi';

View file

@ -3,7 +3,8 @@ import { Response, NextFunction } from 'express';
import { Get, Middleware, RestController } from '@/decorators'; import { Get, Middleware, RestController } from '@/decorators';
import type { WorkflowStatistics } from '@db/entities/WorkflowStatistics'; import type { WorkflowStatistics } from '@db/entities/WorkflowStatistics';
import { StatisticsNames } from '@db/entities/WorkflowStatistics'; import { StatisticsNames } from '@db/entities/WorkflowStatistics';
import { SharedWorkflowRepository, WorkflowStatisticsRepository } from '@db/repositories'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { WorkflowStatisticsRepository } from '@db/repositories/workflowStatistics.repository';
import { ExecutionRequest } from '@/requests'; import { ExecutionRequest } from '@/requests';
import { whereClause } from '@/UserManagement/UserManagementHelper'; import { whereClause } from '@/UserManagement/UserManagementHelper';
import { NotFoundError } from '@/ResponseHelper'; import { NotFoundError } from '@/ResponseHelper';

View file

@ -10,7 +10,7 @@ import { EECredentialsService as EECredentials } from './credentials.service.ee'
import { OwnershipService } from '@/services/ownership.service'; import { OwnershipService } from '@/services/ownership.service';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import type { CredentialsEntity } from '@/databases/entities/CredentialsEntity'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
export const EECredentialsController = express.Router(); export const EECredentialsController = express.Router();

View file

@ -1,6 +1,5 @@
import type { DeleteResult, EntityManager, FindOptionsWhere } from 'typeorm'; import type { DeleteResult, EntityManager, FindOptionsWhere } from 'typeorm';
import { In, Not } from 'typeorm'; import { In, Not } from 'typeorm';
import * as Db from '@/Db';
import { CredentialsEntity } from '@db/entities/CredentialsEntity'; import { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { SharedCredentials } from '@db/entities/SharedCredentials'; import { SharedCredentials } from '@db/entities/SharedCredentials';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
@ -8,6 +7,7 @@ import { UserService } from '@/services/user.service';
import { CredentialsService } from './credentials.service'; import { CredentialsService } from './credentials.service';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import Container from 'typedi'; import Container from 'typedi';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
export class EECredentialsService extends CredentialsService { export class EECredentialsService extends CredentialsService {
static async isOwned( static async isOwned(
@ -43,7 +43,7 @@ export class EECredentialsService extends CredentialsService {
where.userId = user.id; where.userId = user.id;
} }
return Db.collections.SharedCredentials.findOne({ return Container.get(SharedCredentialsRepository).findOne({
where, where,
relations, relations,
}); });
@ -83,7 +83,7 @@ export class EECredentialsService extends CredentialsService {
const newSharedCredentials = users const newSharedCredentials = users
.filter((user) => !user.isPending) .filter((user) => !user.isPending)
.map((user) => .map((user) =>
Db.collections.SharedCredentials.create({ Container.get(SharedCredentialsRepository).create({
credentialsId: credential.id, credentialsId: credential.id,
userId: user.id, userId: user.id,
roleId: role?.id, roleId: role?.id,

View file

@ -25,13 +25,15 @@ import { CredentialTypes } from '@/CredentialTypes';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { OwnershipService } from '@/services/ownership.service'; import { OwnershipService } from '@/services/ownership.service';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
export class CredentialsService { export class CredentialsService {
static async get( static async get(
where: FindOptionsWhere<ICredentialsDb>, where: FindOptionsWhere<ICredentialsDb>,
options?: { relations: string[] }, options?: { relations: string[] },
): Promise<ICredentialsDb | null> { ): Promise<ICredentialsDb | null> {
return Db.collections.Credentials.findOne({ return Container.get(CredentialsRepository).findOne({
relations: options?.relations, relations: options?.relations,
where, where,
}); });
@ -88,14 +90,14 @@ export class CredentialsService {
const isDefaultSelect = !options.listQueryOptions?.select; const isDefaultSelect = !options.listQueryOptions?.select;
if (returnAll) { if (returnAll) {
const credentials = await Db.collections.Credentials.find(findManyOptions); const credentials = await Container.get(CredentialsRepository).find(findManyOptions);
return isDefaultSelect ? this.addOwnedByAndSharedWith(credentials) : credentials; return isDefaultSelect ? this.addOwnedByAndSharedWith(credentials) : credentials;
} }
const ids = await this.getAccessibleCredentials(user.id); const ids = await this.getAccessibleCredentials(user.id);
const credentials = await Db.collections.Credentials.find({ const credentials = await Container.get(CredentialsRepository).find({
...findManyOptions, ...findManyOptions,
where: { ...findManyOptions.where, id: In(ids) }, // only accessible credentials where: { ...findManyOptions.where, id: In(ids) }, // only accessible credentials
}); });
@ -107,7 +109,7 @@ export class CredentialsService {
* Get the IDs of all credentials owned by or shared with a user. * Get the IDs of all credentials owned by or shared with a user.
*/ */
private static async getAccessibleCredentials(userId: string) { private static async getAccessibleCredentials(userId: string) {
const sharings = await Db.collections.SharedCredentials.find({ const sharings = await Container.get(SharedCredentialsRepository).find({
relations: ['role'], relations: ['role'],
where: { where: {
userId, userId,
@ -125,7 +127,7 @@ export class CredentialsService {
options.relations = ['shared', 'shared.user', 'shared.role']; options.relations = ['shared', 'shared.user', 'shared.role'];
} }
return Db.collections.Credentials.find(options); return Container.get(CredentialsRepository).find(options);
} }
/** /**
@ -152,7 +154,7 @@ export class CredentialsService {
} }
} }
return Db.collections.SharedCredentials.findOne({ where, relations }); return Container.get(SharedCredentialsRepository).findOne({ where, relations });
} }
static async prepareCreateData( static async prepareCreateData(
@ -162,7 +164,7 @@ export class CredentialsService {
// This saves us a merge but requires some type casting. These // This saves us a merge but requires some type casting. These
// types are compatible for this case. // types are compatible for this case.
const newCredentials = Db.collections.Credentials.create(rest as ICredentialsDb); const newCredentials = Container.get(CredentialsRepository).create(rest as ICredentialsDb);
await validateEntity(newCredentials); await validateEntity(newCredentials);
@ -185,7 +187,7 @@ export class CredentialsService {
// This saves us a merge but requires some type casting. These // This saves us a merge but requires some type casting. These
// types are compatible for this case. // types are compatible for this case.
const updateData = Db.collections.Credentials.create(mergedData as ICredentialsDb); const updateData = Container.get(CredentialsRepository).create(mergedData as ICredentialsDb);
await validateEntity(updateData); await validateEntity(updateData);
@ -234,11 +236,11 @@ export class CredentialsService {
await Container.get(ExternalHooks).run('credentials.update', [newCredentialData]); await Container.get(ExternalHooks).run('credentials.update', [newCredentialData]);
// Update the credentials in DB // Update the credentials in DB
await Db.collections.Credentials.update(credentialId, newCredentialData); await Container.get(CredentialsRepository).update(credentialId, newCredentialData);
// 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 updated entry. // nor the new value. So query now the updated entry.
return Db.collections.Credentials.findOneBy({ id: credentialId }); return Container.get(CredentialsRepository).findOneBy({ id: credentialId });
} }
static async save( static async save(
@ -281,7 +283,7 @@ export class CredentialsService {
static async delete(credentials: CredentialsEntity): Promise<void> { static async delete(credentials: CredentialsEntity): Promise<void> {
await Container.get(ExternalHooks).run('credentials.delete', [credentials.id]); await Container.get(ExternalHooks).run('credentials.delete', [credentials.id]);
await Db.collections.Credentials.remove(credentials); await Container.get(CredentialsRepository).remove(credentials);
} }
static async test( static async test(

View file

@ -1,21 +0,0 @@
export { AuthIdentityRepository } from './authIdentity.repository';
export { AuthProviderSyncHistoryRepository } from './authProviderSyncHistory.repository';
export { CredentialsRepository } from './credentials.repository';
export { EventDestinationsRepository } from './eventDestinations.repository';
export { ExecutionDataRepository } from './executionData.repository';
export { ExecutionMetadataRepository } from './executionMetadata.repository';
export { ExecutionRepository } from './execution.repository';
export { InstalledNodesRepository } from './installedNodes.repository';
export { InstalledPackagesRepository } from './installedPackages.repository';
export { RoleRepository } from './role.repository';
export { SettingsRepository } from './settings.repository';
export { SharedCredentialsRepository } from './sharedCredentials.repository';
export { SharedWorkflowRepository } from './sharedWorkflow.repository';
export { TagRepository } from './tag.repository';
export { UserRepository } from './user.repository';
export { VariablesRepository } from './variables.repository';
export { WebhookRepository } from './webhook.repository';
export { WorkflowHistoryRepository } from './workflowHistory.repository';
export { WorkflowRepository } from './workflow.repository';
export { WorkflowStatisticsRepository } from './workflowStatistics.repository';
export { WorkflowTagMappingRepository } from './workflowTagMapping.repository';

View file

@ -33,7 +33,7 @@ import type { Variables } from '@db/entities/Variables';
import type { SourceControlWorkflowVersionId } from './types/sourceControlWorkflowVersionId'; import type { SourceControlWorkflowVersionId } from './types/sourceControlWorkflowVersionId';
import type { ExportableCredential } from './types/exportableCredential'; import type { ExportableCredential } from './types/exportableCredential';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { TagRepository } from '@/databases/repositories'; import { TagRepository } from '@db/repositories/tag.repository';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
@Service() @Service()

View file

@ -1,4 +1,4 @@
import { Service } from 'typedi'; import Container, { Service } from 'typedi';
import path from 'path'; import path from 'path';
import { import {
SOURCE_CONTROL_CREDENTIAL_EXPORT_FOLDER, SOURCE_CONTROL_CREDENTIAL_EXPORT_FOLDER,
@ -6,7 +6,6 @@ import {
SOURCE_CONTROL_TAGS_EXPORT_FILE, SOURCE_CONTROL_TAGS_EXPORT_FILE,
SOURCE_CONTROL_WORKFLOW_EXPORT_FOLDER, SOURCE_CONTROL_WORKFLOW_EXPORT_FOLDER,
} from './constants'; } from './constants';
import * as Db from '@/Db';
import type { ICredentialDataDecryptedObject } from 'n8n-workflow'; import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
import { writeFile as fsWriteFile, rm as fsRm } from 'fs/promises'; import { writeFile as fsWriteFile, rm as fsRm } from 'fs/promises';
import { rmSync } from 'fs'; import { rmSync } from 'fs';
@ -25,8 +24,12 @@ import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { In } from 'typeorm'; import { In } from 'typeorm';
import type { SourceControlledFile } from './types/sourceControlledFile'; import type { SourceControlledFile } from './types/sourceControlledFile';
import { VariablesService } from '../variables/variables.service'; import { VariablesService } from '../variables/variables.service';
import { TagRepository } from '@/databases/repositories'; import { TagRepository } from '@db/repositories/tag.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { WorkflowTagMappingRepository } from '@db/repositories/workflowTagMapping.repository';
@Service() @Service()
export class SourceControlExportService { export class SourceControlExportService {
@ -102,7 +105,7 @@ export class SourceControlExportService {
try { try {
sourceControlFoldersExistCheck([this.workflowExportFolder]); sourceControlFoldersExistCheck([this.workflowExportFolder]);
const workflowIds = candidates.map((e) => e.id); const workflowIds = candidates.map((e) => e.id);
const sharedWorkflows = await Db.collections.SharedWorkflow.find({ const sharedWorkflows = await Container.get(SharedWorkflowRepository).find({
relations: ['role', 'user'], relations: ['role', 'user'],
where: { where: {
role: { role: {
@ -112,7 +115,7 @@ export class SourceControlExportService {
workflowId: In(workflowIds), workflowId: In(workflowIds),
}, },
}); });
const workflows = await Db.collections.Workflow.find({ const workflows = await Container.get(WorkflowRepository).find({
where: { where: {
id: In(workflowIds), id: In(workflowIds),
}, },
@ -181,7 +184,7 @@ export class SourceControlExportService {
files: [], files: [],
}; };
} }
const mappings = await Db.collections.WorkflowTagMapping.find(); const mappings = await Container.get(WorkflowTagMappingRepository).find();
const fileName = path.join(this.gitFolder, SOURCE_CONTROL_TAGS_EXPORT_FILE); const fileName = path.join(this.gitFolder, SOURCE_CONTROL_TAGS_EXPORT_FILE);
await fsWriteFile( await fsWriteFile(
fileName, fileName,
@ -236,7 +239,7 @@ export class SourceControlExportService {
try { try {
sourceControlFoldersExistCheck([this.credentialExportFolder]); sourceControlFoldersExistCheck([this.credentialExportFolder]);
const credentialIds = candidates.map((e) => e.id); const credentialIds = candidates.map((e) => e.id);
const credentialsToBeExported = await Db.collections.SharedCredentials.find({ const credentialsToBeExported = await Container.get(SharedCredentialsRepository).find({
relations: ['credentials', 'role', 'user'], relations: ['credentials', 'role', 'user'],
where: { where: {
credentialsId: In(credentialIds), credentialsId: In(credentialIds),

View file

@ -7,7 +7,6 @@ import {
SOURCE_CONTROL_VARIABLES_EXPORT_FILE, SOURCE_CONTROL_VARIABLES_EXPORT_FILE,
SOURCE_CONTROL_WORKFLOW_EXPORT_FOLDER, SOURCE_CONTROL_WORKFLOW_EXPORT_FOLDER,
} from './constants'; } from './constants';
import * as Db from '@/Db';
import glob from 'fast-glob'; import glob from 'fast-glob';
import { jsonParse } from 'n8n-workflow'; import { jsonParse } from 'n8n-workflow';
import { readFile as fsReadFile } from 'fs/promises'; import { readFile as fsReadFile } from 'fs/promises';
@ -26,9 +25,16 @@ import { getCredentialExportPath, getWorkflowExportPath } from './sourceControlH
import type { SourceControlledFile } from './types/sourceControlledFile'; import type { SourceControlledFile } from './types/sourceControlledFile';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { VariablesService } from '../variables/variables.service'; import { VariablesService } from '../variables/variables.service';
import { TagRepository } from '@/databases/repositories'; import { TagRepository } from '@db/repositories/tag.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { UserRepository } from '@db/repositories/user.repository';
import { UM_FIX_INSTRUCTION } from '@/constants'; import { UM_FIX_INSTRUCTION } from '@/constants';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { WorkflowTagMappingRepository } from '@db/repositories/workflowTagMapping.repository';
import { VariablesRepository } from '@db/repositories/variables.repository';
@Service() @Service()
export class SourceControlImportService { export class SourceControlImportService {
@ -110,7 +116,7 @@ export class SourceControlImportService {
} }
public async getLocalVersionIdsFromDb(): Promise<SourceControlWorkflowVersionId[]> { public async getLocalVersionIdsFromDb(): Promise<SourceControlWorkflowVersionId[]> {
const localWorkflows = await Db.collections.Workflow.find({ const localWorkflows = await Container.get(WorkflowRepository).find({
select: ['id', 'name', 'versionId', 'updatedAt'], select: ['id', 'name', 'versionId', 'updatedAt'],
}); });
return localWorkflows.map((local) => ({ return localWorkflows.map((local) => ({
@ -153,7 +159,7 @@ export class SourceControlImportService {
public async getLocalCredentialsFromDb(): Promise< public async getLocalCredentialsFromDb(): Promise<
Array<ExportableCredential & { filename: string }> Array<ExportableCredential & { filename: string }>
> { > {
const localCredentials = await Db.collections.Credentials.find({ const localCredentials = await Container.get(CredentialsRepository).find({
select: ['id', 'name', 'type', 'nodesAccess'], select: ['id', 'name', 'type', 'nodesAccess'],
}); });
return localCredentials.map((local) => ({ return localCredentials.map((local) => ({
@ -209,7 +215,7 @@ export class SourceControlImportService {
const localTags = await this.tagRepository.find({ const localTags = await this.tagRepository.find({
select: ['id', 'name'], select: ['id', 'name'],
}); });
const localMappings = await Db.collections.WorkflowTagMapping.find({ const localMappings = await Container.get(WorkflowTagMappingRepository).find({
select: ['workflowId', 'tagId'], select: ['workflowId', 'tagId'],
}); });
return { tags: localTags, mappings: localMappings }; return { tags: localTags, mappings: localMappings };
@ -219,13 +225,13 @@ export class SourceControlImportService {
const ownerWorkflowRole = await this.getWorkflowOwnerRole(); const ownerWorkflowRole = await this.getWorkflowOwnerRole();
const workflowRunner = this.activeWorkflowRunner; const workflowRunner = this.activeWorkflowRunner;
const candidateIds = candidates.map((c) => c.id); const candidateIds = candidates.map((c) => c.id);
const existingWorkflows = await Db.collections.Workflow.find({ const existingWorkflows = await Container.get(WorkflowRepository).find({
where: { where: {
id: In(candidateIds), id: In(candidateIds),
}, },
select: ['id', 'name', 'versionId', 'active'], select: ['id', 'name', 'versionId', 'active'],
}); });
const allSharedWorkflows = await Db.collections.SharedWorkflow.find({ const allSharedWorkflows = await Container.get(SharedWorkflowRepository).find({
where: { where: {
workflowId: In(candidateIds), workflowId: In(candidateIds),
}, },
@ -244,7 +250,10 @@ export class SourceControlImportService {
const existingWorkflow = existingWorkflows.find((e) => e.id === importedWorkflow.id); const existingWorkflow = existingWorkflows.find((e) => e.id === importedWorkflow.id);
importedWorkflow.active = existingWorkflow?.active ?? false; importedWorkflow.active = existingWorkflow?.active ?? false;
this.logger.debug(`Updating workflow id ${importedWorkflow.id ?? 'new'}`); this.logger.debug(`Updating workflow id ${importedWorkflow.id ?? 'new'}`);
const upsertResult = await Db.collections.Workflow.upsert({ ...importedWorkflow }, ['id']); const upsertResult = await Container.get(WorkflowRepository).upsert(
{ ...importedWorkflow },
['id'],
);
if (upsertResult?.identifiers?.length !== 1) { if (upsertResult?.identifiers?.length !== 1) {
throw new Error(`Failed to upsert workflow ${importedWorkflow.id ?? 'new'}`); throw new Error(`Failed to upsert workflow ${importedWorkflow.id ?? 'new'}`);
} }
@ -254,7 +263,7 @@ export class SourceControlImportService {
if (cachedOwnerIds.has(importedWorkflow.owner)) { if (cachedOwnerIds.has(importedWorkflow.owner)) {
workflowOwnerId = cachedOwnerIds.get(importedWorkflow.owner) ?? userId; workflowOwnerId = cachedOwnerIds.get(importedWorkflow.owner) ?? userId;
} else { } else {
const foundUser = await Db.collections.User.findOne({ const foundUser = await Container.get(UserRepository).findOne({
where: { where: {
email: importedWorkflow.owner, email: importedWorkflow.owner,
}, },
@ -278,7 +287,7 @@ export class SourceControlImportService {
); );
if (!existingSharedWorkflowOwnerByUserId && !existingSharedWorkflowOwnerByRoleId) { if (!existingSharedWorkflowOwnerByUserId && !existingSharedWorkflowOwnerByRoleId) {
// no owner exists yet, so create one // no owner exists yet, so create one
await Db.collections.SharedWorkflow.insert({ await Container.get(SharedWorkflowRepository).insert({
workflowId: importedWorkflow.id, workflowId: importedWorkflow.id,
userId: workflowOwnerId, userId: workflowOwnerId,
roleId: ownerWorkflowRole.id, roleId: ownerWorkflowRole.id,
@ -288,7 +297,7 @@ export class SourceControlImportService {
} else if (existingSharedWorkflowOwnerByUserId && !existingSharedWorkflowOwnerByRoleId) { } else if (existingSharedWorkflowOwnerByUserId && !existingSharedWorkflowOwnerByRoleId) {
// if the worklflow has a non-global owner that is referenced by the owner file, // if the worklflow has a non-global owner that is referenced by the owner file,
// and no existing global owner, update the owner to the user referenced in the owner file // and no existing global owner, update the owner to the user referenced in the owner file
await Db.collections.SharedWorkflow.update( await Container.get(SharedWorkflowRepository).update(
{ {
workflowId: importedWorkflow.id, workflowId: importedWorkflow.id,
userId: workflowOwnerId, userId: workflowOwnerId,
@ -310,7 +319,7 @@ export class SourceControlImportService {
} catch (error) { } catch (error) {
this.logger.error(`Failed to activate workflow ${existingWorkflow.id}`, error as Error); this.logger.error(`Failed to activate workflow ${existingWorkflow.id}`, error as Error);
} finally { } finally {
await Db.collections.Workflow.update( await Container.get(WorkflowRepository).update(
{ id: existingWorkflow.id }, { id: existingWorkflow.id },
{ versionId: importedWorkflow.versionId }, { versionId: importedWorkflow.versionId },
); );
@ -331,7 +340,7 @@ export class SourceControlImportService {
public async importCredentialsFromWorkFolder(candidates: SourceControlledFile[], userId: string) { public async importCredentialsFromWorkFolder(candidates: SourceControlledFile[], userId: string) {
const candidateIds = candidates.map((c) => c.id); const candidateIds = candidates.map((c) => c.id);
const existingCredentials = await Db.collections.Credentials.find({ const existingCredentials = await Container.get(CredentialsRepository).find({
where: { where: {
id: In(candidateIds), id: In(candidateIds),
}, },
@ -339,7 +348,7 @@ export class SourceControlImportService {
}); });
const ownerCredentialRole = await this.getCredentialOwnerRole(); const ownerCredentialRole = await this.getCredentialOwnerRole();
const ownerGlobalRole = await this.getOwnerGlobalRole(); const ownerGlobalRole = await this.getOwnerGlobalRole();
const existingSharedCredentials = await Db.collections.SharedCredentials.find({ const existingSharedCredentials = await Container.get(SharedCredentialsRepository).find({
select: ['userId', 'credentialsId', 'roleId'], select: ['userId', 'credentialsId', 'roleId'],
where: { where: {
credentialsId: In(candidateIds), credentialsId: In(candidateIds),
@ -370,7 +379,7 @@ export class SourceControlImportService {
newCredentialObject.nodesAccess = nodesAccess || existingCredential?.nodesAccess || []; newCredentialObject.nodesAccess = nodesAccess || existingCredential?.nodesAccess || [];
this.logger.debug(`Updating credential id ${newCredentialObject.id as string}`); this.logger.debug(`Updating credential id ${newCredentialObject.id as string}`);
await Db.collections.Credentials.upsert(newCredentialObject, ['id']); await Container.get(CredentialsRepository).upsert(newCredentialObject, ['id']);
if (!sharedOwner) { if (!sharedOwner) {
const newSharedCredential = new SharedCredentials(); const newSharedCredential = new SharedCredentials();
@ -378,7 +387,7 @@ export class SourceControlImportService {
newSharedCredential.userId = userId; newSharedCredential.userId = userId;
newSharedCredential.roleId = ownerCredentialRole.id; newSharedCredential.roleId = ownerCredentialRole.id;
await Db.collections.SharedCredentials.upsert({ ...newSharedCredential }, [ await Container.get(SharedCredentialsRepository).upsert({ ...newSharedCredential }, [
'credentialsId', 'credentialsId',
'userId', 'userId',
]); ]);
@ -413,7 +422,7 @@ export class SourceControlImportService {
const existingWorkflowIds = new Set( const existingWorkflowIds = new Set(
( (
await Db.collections.Workflow.find({ await Container.get(WorkflowRepository).find({
select: ['id'], select: ['id'],
}) })
).map((e) => e.id), ).map((e) => e.id),
@ -442,7 +451,7 @@ export class SourceControlImportService {
await Promise.all( await Promise.all(
mappedTags.mappings.map(async (mapping) => { mappedTags.mappings.map(async (mapping) => {
if (!existingWorkflowIds.has(String(mapping.workflowId))) return; if (!existingWorkflowIds.has(String(mapping.workflowId))) return;
await Db.collections.WorkflowTagMapping.upsert( await Container.get(WorkflowTagMappingRepository).upsert(
{ tagId: String(mapping.tagId), workflowId: String(mapping.workflowId) }, { tagId: String(mapping.tagId), workflowId: String(mapping.workflowId) },
{ {
skipUpdateIfNoValuesChanged: true, skipUpdateIfNoValuesChanged: true,
@ -489,12 +498,12 @@ export class SourceControlImportService {
overriddenKeys.splice(overriddenKeys.indexOf(variable.key), 1); overriddenKeys.splice(overriddenKeys.indexOf(variable.key), 1);
} }
try { try {
await Db.collections.Variables.upsert({ ...variable }, ['id']); await Container.get(VariablesRepository).upsert({ ...variable }, ['id']);
} catch (errorUpsert) { } catch (errorUpsert) {
if (isUniqueConstraintError(errorUpsert as Error)) { if (isUniqueConstraintError(errorUpsert as Error)) {
this.logger.debug(`Variable ${variable.key} already exists, updating instead`); this.logger.debug(`Variable ${variable.key} already exists, updating instead`);
try { try {
await Db.collections.Variables.update({ key: variable.key }, { ...variable }); await Container.get(VariablesRepository).update({ key: variable.key }, { ...variable });
} catch (errorUpdate) { } catch (errorUpdate) {
this.logger.debug(`Failed to update variable ${variable.key}, skipping`); this.logger.debug(`Failed to update variable ${variable.key}, skipping`);
this.logger.debug((errorUpdate as Error).message); this.logger.debug((errorUpdate as Error).message);
@ -509,8 +518,11 @@ export class SourceControlImportService {
if (overriddenKeys.length > 0 && valueOverrides) { if (overriddenKeys.length > 0 && valueOverrides) {
for (const key of overriddenKeys) { for (const key of overriddenKeys) {
result.imported.push(key); result.imported.push(key);
const newVariable = Db.collections.Variables.create({ key, value: valueOverrides[key] }); const newVariable = Container.get(VariablesRepository).create({
await Db.collections.Variables.save(newVariable); key,
value: valueOverrides[key],
});
await Container.get(VariablesRepository).save(newVariable);
} }
} }

View file

@ -1,4 +1,4 @@
import { Service } from 'typedi'; import Container, { Service } from 'typedi';
import { SourceControlPreferences } from './types/sourceControlPreferences'; import { SourceControlPreferences } from './types/sourceControlPreferences';
import type { ValidationError } from 'class-validator'; import type { ValidationError } from 'class-validator';
import { validate } from 'class-validator'; import { validate } from 'class-validator';
@ -11,7 +11,6 @@ import {
} from './sourceControlHelper.ee'; } from './sourceControlHelper.ee';
import { InstanceSettings } from 'n8n-core'; import { InstanceSettings } from 'n8n-core';
import { jsonParse } from 'n8n-workflow'; import { jsonParse } from 'n8n-workflow';
import * as Db from '@/Db';
import { import {
SOURCE_CONTROL_SSH_FOLDER, SOURCE_CONTROL_SSH_FOLDER,
SOURCE_CONTROL_GIT_FOLDER, SOURCE_CONTROL_GIT_FOLDER,
@ -22,6 +21,7 @@ import path from 'path';
import type { KeyPairType } from './types/keyPairType'; import type { KeyPairType } from './types/keyPairType';
import config from '@/config'; import config from '@/config';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { SettingsRepository } from '@db/repositories/settings.repository';
@Service() @Service()
export class SourceControlPreferencesService { export class SourceControlPreferencesService {
@ -171,7 +171,7 @@ export class SourceControlPreferencesService {
if (saveToDb) { if (saveToDb) {
const settingsValue = JSON.stringify(this._sourceControlPreferences); const settingsValue = JSON.stringify(this._sourceControlPreferences);
try { try {
await Db.collections.Settings.save({ await Container.get(SettingsRepository).save({
key: SOURCE_CONTROL_PREFERENCES_DB_KEY, key: SOURCE_CONTROL_PREFERENCES_DB_KEY,
value: settingsValue, value: settingsValue,
loadOnStartup: true, loadOnStartup: true,
@ -186,7 +186,7 @@ export class SourceControlPreferencesService {
async loadFromDbAndApplySourceControlPreferences(): Promise< async loadFromDbAndApplySourceControlPreferences(): Promise<
SourceControlPreferences | undefined SourceControlPreferences | undefined
> { > {
const loadedPreferences = await Db.collections.Settings.findOne({ const loadedPreferences = await Container.get(SettingsRepository).findOne({
where: { key: SOURCE_CONTROL_PREFERENCES_DB_KEY }, where: { key: SOURCE_CONTROL_PREFERENCES_DB_KEY },
}); });
if (loadedPreferences) { if (loadedPreferences) {

View file

@ -1,7 +1,7 @@
import type { Variables } from '@db/entities/Variables'; import type { Variables } from '@db/entities/Variables';
import { CacheService } from '@/services/cache.service'; import { CacheService } from '@/services/cache.service';
import Container, { Service } from 'typedi'; import Container, { Service } from 'typedi';
import { VariablesRepository } from '@/databases/repositories'; import { VariablesRepository } from '@db/repositories/variables.repository';
import type { DeepPartial } from 'typeorm'; import type { DeepPartial } from 'typeorm';
@Service() @Service()

View file

@ -11,7 +11,6 @@ import type { MessageEventBusDestination } from '../MessageEventBusDestination/M
import { MessageEventBusLogWriter } from '../MessageEventBusWriter/MessageEventBusLogWriter'; import { MessageEventBusLogWriter } from '../MessageEventBusWriter/MessageEventBusLogWriter';
import EventEmitter from 'events'; import EventEmitter from 'events';
import config from '@/config'; import config from '@/config';
import * as Db from '@/Db';
import { messageEventBusDestinationFromDb } from '../MessageEventBusDestination/MessageEventBusDestinationFromDb'; import { messageEventBusDestinationFromDb } from '../MessageEventBusDestination/MessageEventBusDestinationFromDb';
import uniqby from 'lodash/uniqBy'; import uniqby from 'lodash/uniqBy';
import type { EventMessageConfirmSource } from '../EventMessageClasses/EventMessageConfirm'; import type { EventMessageConfirmSource } from '../EventMessageClasses/EventMessageConfirm';
@ -29,11 +28,13 @@ import {
import { recoverExecutionDataFromEventLogMessages } from './recoverEvents'; import { recoverExecutionDataFromEventLogMessages } from './recoverEvents';
import { METRICS_EVENT_NAME } from '../MessageEventBusDestination/Helpers.ee'; import { METRICS_EVENT_NAME } from '../MessageEventBusDestination/Helpers.ee';
import { Container, Service } from 'typedi'; import { Container, Service } from 'typedi';
import { ExecutionRepository, WorkflowRepository } from '@/databases/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import type { AbstractEventMessageOptions } from '../EventMessageClasses/AbstractEventMessageOptions'; import type { AbstractEventMessageOptions } from '../EventMessageClasses/AbstractEventMessageOptions';
import { getEventMessageObjectByType } from '../EventMessageClasses/Helpers'; import { getEventMessageObjectByType } from '../EventMessageClasses/Helpers';
import { SingleMainInstancePublisher } from '@/services/orchestration/main/SingleMainInstance.publisher'; import { SingleMainInstancePublisher } from '@/services/orchestration/main/SingleMainInstance.publisher';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { EventDestinationsRepository } from '@db/repositories/eventDestinations.repository';
export type EventMessageReturnMode = 'sent' | 'unsent' | 'all' | 'unfinished'; export type EventMessageReturnMode = 'sent' | 'unsent' | 'all' | 'unfinished';
@ -79,7 +80,7 @@ export class MessageEventBus extends EventEmitter {
this.logger.debug('Initializing event bus...'); this.logger.debug('Initializing event bus...');
const savedEventDestinations = await Db.collections.EventDestinations.find({}); const savedEventDestinations = await Container.get(EventDestinationsRepository).find({});
if (savedEventDestinations.length > 0) { if (savedEventDestinations.length > 0) {
for (const destinationData of savedEventDestinations) { for (const destinationData of savedEventDestinations) {
try { try {

View file

@ -6,7 +6,7 @@ import { Push } from '@/push';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { getWorkflowHooksMain } from '@/WorkflowExecuteAdditionalData'; import { getWorkflowHooksMain } from '@/WorkflowExecuteAdditionalData';
import { ExecutionRepository } from '@db/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
export async function recoverExecutionDataFromEventLogMessages( export async function recoverExecutionDataFromEventLogMessages(
executionId: string, executionId: string,

View file

@ -6,13 +6,13 @@ import {
MessageEventBusDestinationTypeNames, MessageEventBusDestinationTypeNames,
MessageEventBusDestinationOptions, MessageEventBusDestinationOptions,
} from 'n8n-workflow'; } from 'n8n-workflow';
import * as Db from '@/Db';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import type { AbstractEventMessage } from '../EventMessageClasses/AbstractEventMessage'; import type { AbstractEventMessage } from '../EventMessageClasses/AbstractEventMessage';
import type { EventMessageTypes } from '../EventMessageClasses'; import type { EventMessageTypes } from '../EventMessageClasses';
import type { EventMessageConfirmSource } from '../EventMessageClasses/EventMessageConfirm'; import type { EventMessageConfirmSource } from '../EventMessageClasses/EventMessageConfirm';
import { MessageEventBus } from '../MessageEventBus/MessageEventBus'; import { MessageEventBus } from '../MessageEventBus/MessageEventBus';
import type { MessageWithCallback } from '../MessageEventBus/MessageEventBus'; import type { MessageWithCallback } from '../MessageEventBus/MessageEventBus';
import { EventDestinationsRepository } from '@db/repositories/eventDestinations.repository';
export abstract class MessageEventBusDestination implements MessageEventBusDestinationOptions { export abstract class MessageEventBusDestination implements MessageEventBusDestinationOptions {
// Since you can't have static abstract functions - this just serves as a reminder that you need to implement these. Please. // Since you can't have static abstract functions - this just serves as a reminder that you need to implement these. Please.
@ -96,7 +96,7 @@ export abstract class MessageEventBusDestination implements MessageEventBusDesti
id: this.getId(), id: this.getId(),
destination: this.serialize(), destination: this.serialize(),
}; };
const dbResult: InsertResult = await Db.collections.EventDestinations.upsert(data, { const dbResult: InsertResult = await Container.get(EventDestinationsRepository).upsert(data, {
skipUpdateIfNoValuesChanged: true, skipUpdateIfNoValuesChanged: true,
conflictPaths: ['id'], conflictPaths: ['id'],
}); });
@ -108,7 +108,7 @@ export abstract class MessageEventBusDestination implements MessageEventBusDesti
} }
static async deleteFromDb(id: string): Promise<DeleteResult> { static async deleteFromDb(id: string): Promise<DeleteResult> {
const dbResult = await Db.collections.EventDestinations.delete({ id }); const dbResult = await Container.get(EventDestinationsRepository).delete({ id });
return dbResult; return dbResult;
} }

View file

@ -1,5 +1,5 @@
import { MessageEventBusDestinationTypeNames } from 'n8n-workflow'; import { MessageEventBusDestinationTypeNames } from 'n8n-workflow';
import type { EventDestinations } from '@/databases/entities/EventDestinations'; import type { EventDestinations } from '@db/entities/EventDestinations';
import type { MessageEventBus } from '../MessageEventBus/MessageEventBus'; import type { MessageEventBus } from '../MessageEventBus/MessageEventBus';
import type { MessageEventBusDestination } from './MessageEventBusDestination.ee'; import type { MessageEventBusDestination } from './MessageEventBusDestination.ee';
import { MessageEventBusDestinationSentry } from './MessageEventBusDestinationSentry.ee'; import { MessageEventBusDestinationSentry } from './MessageEventBusDestinationSentry.ee';

View file

@ -3,7 +3,7 @@ import type { ExecutionStatus, IRun, IWorkflowBase } from 'n8n-workflow';
import type { ExecutionPayload, IExecutionDb } from '@/Interfaces'; import type { ExecutionPayload, IExecutionDb } from '@/Interfaces';
import pick from 'lodash/pick'; import pick from 'lodash/pick';
import { isWorkflowIdValid } from '@/utils'; import { isWorkflowIdValid } from '@/utils';
import { ExecutionRepository } from '@db/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
import { ExecutionMetadataService } from '@/services/executionMetadata.service'; import { ExecutionMetadataService } from '@/services/executionMetadata.service';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';

View file

@ -18,11 +18,11 @@ import type { ExecutionRequest } from '@/requests';
import * as ResponseHelper from '@/ResponseHelper'; import * as ResponseHelper from '@/ResponseHelper';
import { getSharedWorkflowIds } from '@/WorkflowHelpers'; import { getSharedWorkflowIds } from '@/WorkflowHelpers';
import { WorkflowRunner } from '@/WorkflowRunner'; import { WorkflowRunner } from '@/WorkflowRunner';
import * as Db from '@/Db';
import * as GenericHelpers from '@/GenericHelpers'; import * as GenericHelpers from '@/GenericHelpers';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { getStatusUsingPreviousExecutionStatusMethod } from './executionHelpers'; import { getStatusUsingPreviousExecutionStatusMethod } from './executionHelpers';
import { ExecutionRepository } from '@db/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
export interface IGetExecutionsQueryFilter { export interface IGetExecutionsQueryFilter {
@ -274,7 +274,7 @@ export class ExecutionsService {
// Loads the currently saved workflow to execute instead of the // Loads the currently saved workflow to execute instead of the
// one saved at the time of the execution. // one saved at the time of the execution.
const workflowId = execution.workflowData.id as string; const workflowId = execution.workflowData.id as string;
const workflowData = (await Db.collections.Workflow.findOneBy({ const workflowData = (await Container.get(WorkflowRepository).findOneBy({
id: workflowId, id: workflowId,
})) as IWorkflowBase; })) as IWorkflowBase;

View file

@ -1,11 +1,13 @@
import { Container } from 'typedi'; import { Container } from 'typedi';
import { License } from '@/License'; import { License } from '@/License';
import type { ILicenseReadResponse } from '@/Interfaces'; import type { ILicenseReadResponse } from '@/Interfaces';
import * as Db from '@/Db'; import { WorkflowRepository } from '@db/repositories/workflow.repository';
export class LicenseService { export class LicenseService {
static async getActiveTriggerCount(): Promise<number> { static async getActiveTriggerCount(): Promise<number> {
const totalTriggerCount = await Db.collections.Workflow.sum('triggerCount', { active: true }); const totalTriggerCount = await Container.get(WorkflowRepository).sum('triggerCount', {
active: true,
});
return totalTriggerCount ?? 0; return totalTriggerCount ?? 0;
} }

View file

@ -2,7 +2,7 @@ import { EventEmitter } from 'events';
import { assert, jsonStringify } from 'n8n-workflow'; import { assert, jsonStringify } from 'n8n-workflow';
import type { IPushDataType } from '@/Interfaces'; import type { IPushDataType } from '@/Interfaces';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import type { User } from '@/databases/entities/User'; import type { User } from '@db/entities/User';
/** /**
* Abstract class for two-way push communication. * Abstract class for two-way push communication.

View file

@ -13,7 +13,7 @@ import { SSEPush } from './sse.push';
import { WebSocketPush } from './websocket.push'; import { WebSocketPush } from './websocket.push';
import type { PushResponse, SSEPushRequest, WebSocketPushRequest } from './types'; import type { PushResponse, SSEPushRequest, WebSocketPushRequest } from './types';
import type { IPushDataType } from '@/Interfaces'; import type { IPushDataType } from '@/Interfaces';
import type { User } from '@/databases/entities/User'; import type { User } from '@db/entities/User';
const useWebSockets = config.getEnv('push.backend') === 'websocket'; const useWebSockets = config.getEnv('push.backend') === 'websocket';

View file

@ -3,7 +3,7 @@ import { Service } from 'typedi';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { AbstractPush } from './abstract.push'; import { AbstractPush } from './abstract.push';
import type { PushRequest, PushResponse } from './types'; import type { PushRequest, PushResponse } from './types';
import type { User } from '@/databases/entities/User'; import type { User } from '@db/entities/User';
type Connection = { req: PushRequest; res: PushResponse }; type Connection = { req: PushRequest; res: PushResponse };

View file

@ -1,4 +1,4 @@
import type { User } from '@/databases/entities/User'; import type { User } from '@db/entities/User';
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
import type { WebSocket } from 'ws'; import type { WebSocket } from 'ws';

View file

@ -2,7 +2,7 @@ import type WebSocket from 'ws';
import { Service } from 'typedi'; import { Service } from 'typedi';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { AbstractPush } from './abstract.push'; import { AbstractPush } from './abstract.push';
import type { User } from '@/databases/entities/User'; import type { User } from '@db/entities/User';
function heartbeat(this: WebSocket) { function heartbeat(this: WebSocket) {
this.isAlive = true; this.isAlive = true;

View file

@ -26,9 +26,9 @@ import type { Role } from '@db/entities/Role';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
import type { UserManagementMailer } from '@/UserManagement/email'; import type { UserManagementMailer } from '@/UserManagement/email';
import type { Variables } from '@db/entities/Variables'; import type { Variables } from '@db/entities/Variables';
import type { WorkflowEntity } from './databases/entities/WorkflowEntity'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import type { CredentialsEntity } from './databases/entities/CredentialsEntity'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import type { WorkflowHistory } from './databases/entities/WorkflowHistory'; import type { WorkflowHistory } from '@db/entities/WorkflowHistory';
export class UserUpdatePayload implements Pick<User, 'email' | 'firstName' | 'lastName'> { export class UserUpdatePayload implements Pick<User, 'email' | 'firstName' | 'lastName'> {
@IsEmail() @IsEmail()

View file

@ -10,8 +10,8 @@ import { InstanceSettings } from 'n8n-core';
import type { PackageDirectoryLoader } from 'n8n-core'; import type { PackageDirectoryLoader } from 'n8n-core';
import { toError } from '@/utils'; import { toError } from '@/utils';
import { InstalledPackagesRepository } from '@/databases/repositories/installedPackages.repository'; import { InstalledPackagesRepository } from '@db/repositories/installedPackages.repository';
import type { InstalledPackages } from '@/databases/entities/InstalledPackages'; import type { InstalledPackages } from '@db/entities/InstalledPackages';
import { import {
NODE_PACKAGE_PREFIX, NODE_PACKAGE_PREFIX,
NPM_COMMAND_TOKENS, NPM_COMMAND_TOKENS,

View file

@ -2,7 +2,7 @@ import { EventEmitter } from 'events';
import { Container, Service } from 'typedi'; import { Container, Service } from 'typedi';
import type { INode, IRun, IWorkflowBase } from 'n8n-workflow'; import type { INode, IRun, IWorkflowBase } from 'n8n-workflow';
import { StatisticsNames } from '@db/entities/WorkflowStatistics'; import { StatisticsNames } from '@db/entities/WorkflowStatistics';
import { WorkflowStatisticsRepository } from '@db/repositories'; import { WorkflowStatisticsRepository } from '@db/repositories/workflowStatistics.repository';
import { UserService } from '@/services/user.service'; import { UserService } from '@/services/user.service';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { OwnershipService } from './ownership.service'; import { OwnershipService } from './ownership.service';

View file

@ -1,6 +1,6 @@
import { ExecutionMetadataRepository } from '@/databases/repositories';
import { Service } from 'typedi'; import { Service } from 'typedi';
import type { ExecutionMetadata } from '@/databases/entities/ExecutionMetadata'; import { ExecutionMetadataRepository } from '@db/repositories/executionMetadata.repository';
import type { ExecutionMetadata } from '@db/entities/ExecutionMetadata';
@Service() @Service()
export class ExecutionMetadataService { export class ExecutionMetadataService {

View file

@ -1,12 +1,12 @@
import { Service } from 'typedi'; import { Service } from 'typedi';
import { CacheService } from './cache.service'; import { CacheService } from './cache.service';
import { SharedWorkflowRepository } from '@/databases/repositories'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import type { User } from '@/databases/entities/User'; import type { User } from '@db/entities/User';
import { RoleService } from './role.service'; import { RoleService } from './role.service';
import { UserService } from './user.service'; import { UserService } from './user.service';
import type { Credentials, ListQuery } from '@/requests'; import type { Credentials, ListQuery } from '@/requests';
import type { Role } from '@/databases/entities/Role'; import type { Role } from '@db/entities/Role';
import type { CredentialsEntity } from '@/databases/entities/CredentialsEntity'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
@Service() @Service()
export class OwnershipService { export class OwnershipService {

View file

@ -6,9 +6,9 @@ import type { FindOptionsWhere } from 'typeorm';
import { TIME, inTest } from '@/constants'; import { TIME, inTest } from '@/constants';
import config from '@/config'; import config from '@/config';
import { ExecutionRepository } from '@/databases/repositories'; import { ExecutionRepository } from '@db/repositories/execution.repository';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { ExecutionEntity } from '@/databases/entities/ExecutionEntity'; import { ExecutionEntity } from '@db/entities/ExecutionEntity';
@Service() @Service()
export class PruningService { export class PruningService {

View file

@ -1,7 +1,8 @@
import { RoleRepository, SharedWorkflowRepository } from '@/databases/repositories';
import { Service } from 'typedi'; import { Service } from 'typedi';
import { RoleRepository } from '@db/repositories/role.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { CacheService } from './cache.service'; import { CacheService } from './cache.service';
import type { RoleNames, RoleScopes } from '@/databases/entities/Role'; import type { RoleNames, RoleScopes } from '@db/entities/Role';
class InvalidRoleError extends Error {} class InvalidRoleError extends Error {}

View file

@ -1,8 +1,8 @@
import { TagRepository } from '@/databases/repositories'; import { TagRepository } from '@db/repositories/tag.repository';
import { Service } from 'typedi'; import { Service } from 'typedi';
import { validateEntity } from '@/GenericHelpers'; import { validateEntity } from '@/GenericHelpers';
import type { ITagToImport, ITagWithCountDb, IWorkflowToImport } from '@/Interfaces'; import type { ITagToImport, ITagWithCountDb, IWorkflowToImport } from '@/Interfaces';
import type { TagEntity } from '@/databases/entities/TagEntity'; import type { TagEntity } from '@db/entities/TagEntity';
import type { EntityManager, FindManyOptions, FindOneOptions } from 'typeorm'; import type { EntityManager, FindManyOptions, FindOneOptions } from 'typeorm';
import type { UpsertOptions } from 'typeorm/repository/UpsertOptions'; import type { UpsertOptions } from 'typeorm/repository/UpsertOptions';
import { ExternalHooks } from '@/ExternalHooks'; import { ExternalHooks } from '@/ExternalHooks';

View file

@ -3,7 +3,7 @@ import type { EntityManager, FindManyOptions, FindOneOptions, FindOptionsWhere }
import { In } from 'typeorm'; import { In } from 'typeorm';
import { User } from '@db/entities/User'; import { User } from '@db/entities/User';
import type { IUserSettings } from 'n8n-workflow'; import type { IUserSettings } from 'n8n-workflow';
import { UserRepository } from '@/databases/repositories'; import { UserRepository } from '@db/repositories/user.repository';
import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';
import type { PublicUser } from '@/Interfaces'; import type { PublicUser } from '@/Interfaces';
import type { PostHogClient } from '@/posthog'; import type { PostHogClient } from '@/posthog';

View file

@ -1,7 +1,7 @@
import { WebhookRepository } from '@/databases/repositories'; import { WebhookRepository } from '@db/repositories/webhook.repository';
import { Service } from 'typedi'; import { Service } from 'typedi';
import { CacheService } from './cache.service'; import { CacheService } from './cache.service';
import type { WebhookEntity } from '@/databases/entities/WebhookEntity'; import type { WebhookEntity } from '@db/entities/WebhookEntity';
import type { IHttpRequestMethods } from 'n8n-workflow'; import type { IHttpRequestMethods } from 'n8n-workflow';
import type { DeepPartial } from 'typeorm'; import type { DeepPartial } from 'typeorm';

View file

@ -1,6 +1,5 @@
import type express from 'express'; import type express from 'express';
import { Service } from 'typedi'; import Container, { Service } from 'typedi';
import * as Db from '@/Db';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
import { jsonParse } from 'n8n-workflow'; import { jsonParse } from 'n8n-workflow';
import { AuthError, BadRequestError } from '@/ResponseHelper'; import { AuthError, BadRequestError } from '@/ResponseHelper';
@ -28,6 +27,8 @@ import type { SamlLoginBinding } from './types';
import { validateMetadata, validateResponse } from './samlValidator'; import { validateMetadata, validateResponse } from './samlValidator';
import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper'; import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { UserRepository } from '@db/repositories/user.repository';
import { SettingsRepository } from '@db/repositories/settings.repository';
@Service() @Service()
export class SamlService { export class SamlService {
@ -167,7 +168,7 @@ export class SamlService {
const attributes = await this.getAttributesFromLoginResponse(req, binding); const attributes = await this.getAttributesFromLoginResponse(req, binding);
if (attributes.email) { if (attributes.email) {
const lowerCasedEmail = attributes.email.toLowerCase(); const lowerCasedEmail = attributes.email.toLowerCase();
const user = await Db.collections.User.findOne({ const user = await Container.get(UserRepository).findOne({
where: { email: lowerCasedEmail }, where: { email: lowerCasedEmail },
relations: ['globalRole', 'authIdentities'], relations: ['globalRole', 'authIdentities'],
}); });
@ -257,7 +258,7 @@ export class SamlService {
} }
async loadFromDbAndApplySamlPreferences(apply = true): Promise<SamlPreferences | undefined> { async loadFromDbAndApplySamlPreferences(apply = true): Promise<SamlPreferences | undefined> {
const samlPreferences = await Db.collections.Settings.findOne({ const samlPreferences = await Container.get(SettingsRepository).findOne({
where: { key: SAML_PREFERENCES_DB_KEY }, where: { key: SAML_PREFERENCES_DB_KEY },
}); });
if (samlPreferences) { if (samlPreferences) {
@ -275,16 +276,16 @@ export class SamlService {
} }
async saveSamlPreferencesToDb(): Promise<SamlPreferences | undefined> { async saveSamlPreferencesToDb(): Promise<SamlPreferences | undefined> {
const samlPreferences = await Db.collections.Settings.findOne({ const samlPreferences = await Container.get(SettingsRepository).findOne({
where: { key: SAML_PREFERENCES_DB_KEY }, where: { key: SAML_PREFERENCES_DB_KEY },
}); });
const settingsValue = JSON.stringify(this.samlPreferences); const settingsValue = JSON.stringify(this.samlPreferences);
let result: Settings; let result: Settings;
if (samlPreferences) { if (samlPreferences) {
samlPreferences.value = settingsValue; samlPreferences.value = settingsValue;
result = await Db.collections.Settings.save(samlPreferences); result = await Container.get(SettingsRepository).save(samlPreferences);
} else { } else {
result = await Db.collections.Settings.save({ result = await Container.get(SettingsRepository).save({
key: SAML_PREFERENCES_DB_KEY, key: SAML_PREFERENCES_DB_KEY,
value: settingsValue, value: settingsValue,
loadOnStartup: true, loadOnStartup: true,

View file

@ -1,6 +1,5 @@
import { Container } from 'typedi'; import { Container } from 'typedi';
import config from '@/config'; import config from '@/config';
import * as Db from '@/Db';
import { AuthIdentity } from '@db/entities/AuthIdentity'; import { AuthIdentity } from '@db/entities/AuthIdentity';
import { User } from '@db/entities/User'; import { User } from '@db/entities/User';
import { License } from '@/License'; import { License } from '@/License';
@ -20,6 +19,8 @@ import {
import { getServiceProviderConfigTestReturnUrl } from './serviceProvider.ee'; import { getServiceProviderConfigTestReturnUrl } from './serviceProvider.ee';
import type { SamlConfiguration } from './types/requests'; import type { SamlConfiguration } from './types/requests';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { UserRepository } from '@db/repositories/user.repository';
import { AuthIdentityRepository } from '@db/repositories/authIdentity.repository';
/** /**
* Check whether the SAML feature is licensed and enabled in the instance * Check whether the SAML feature is licensed and enabled in the instance
*/ */
@ -107,10 +108,10 @@ export async function createUserFromSamlAttributes(attributes: SamlUserAttribute
authIdentity.providerId = attributes.userPrincipalName; authIdentity.providerId = attributes.userPrincipalName;
authIdentity.providerType = 'saml'; authIdentity.providerType = 'saml';
authIdentity.user = user; authIdentity.user = user;
const resultAuthIdentity = await Db.collections.AuthIdentity.save(authIdentity); const resultAuthIdentity = await Container.get(AuthIdentityRepository).save(authIdentity);
if (!resultAuthIdentity) throw new AuthError('Could not create AuthIdentity'); if (!resultAuthIdentity) throw new AuthError('Could not create AuthIdentity');
user.authIdentities = [authIdentity]; user.authIdentities = [authIdentity];
const resultUser = await Db.collections.User.save(user); const resultUser = await Container.get(UserRepository).save(user);
if (!resultUser) throw new AuthError('Could not create User'); if (!resultUser) throw new AuthError('Could not create User');
return resultUser; return resultUser;
} }
@ -131,10 +132,10 @@ export async function updateUserFromSamlAttributes(
} else { } else {
samlAuthIdentity.providerId = attributes.userPrincipalName; samlAuthIdentity.providerId = attributes.userPrincipalName;
} }
await Db.collections.AuthIdentity.save(samlAuthIdentity); await Container.get(AuthIdentityRepository).save(samlAuthIdentity);
user.firstName = attributes.firstName; user.firstName = attributes.firstName;
user.lastName = attributes.lastName; user.lastName = attributes.lastName;
const resultUser = await Db.collections.User.save(user); const resultUser = await Container.get(UserRepository).save(user);
if (!resultUser) throw new AuthError('Could not create User'); if (!resultUser) throw new AuthError('Could not create User');
return resultUser; return resultUser;
} }

View file

@ -1,6 +1,7 @@
import config from '@/config'; import config from '@/config';
import * as Db from '@/Db'; import { SettingsRepository } from '@db/repositories/settings.repository';
import type { AuthProviderType } from '@db/entities/AuthIdentity'; import type { AuthProviderType } from '@db/entities/AuthIdentity';
import Container from 'typedi';
/** /**
* Only one authentication method can be active at a time. This function sets the current authentication method * Only one authentication method can be active at a time. This function sets the current authentication method
@ -12,7 +13,7 @@ export async function setCurrentAuthenticationMethod(
authenticationMethod: AuthProviderType, authenticationMethod: AuthProviderType,
): Promise<void> { ): Promise<void> {
config.set('userManagement.authenticationMethod', authenticationMethod); config.set('userManagement.authenticationMethod', authenticationMethod);
await Db.collections.Settings.save({ await Container.get(SettingsRepository).save({
key: 'userManagement.authenticationMethod', key: 'userManagement.authenticationMethod',
value: authenticationMethod, value: authenticationMethod,
loadOnStartup: true, loadOnStartup: true,

View file

@ -1,8 +1,8 @@
import type { SharedWorkflow } from '@/databases/entities/SharedWorkflow'; import type { SharedWorkflow } from '@db/entities/SharedWorkflow';
import type { User } from '@/databases/entities/User'; import type { User } from '@db/entities/User';
import type { WorkflowEntity } from '@/databases/entities/WorkflowEntity'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import type { WorkflowHistory } from '@/databases/entities/WorkflowHistory'; import type { WorkflowHistory } from '@db/entities/WorkflowHistory';
import { SharedWorkflowRepository } from '@/databases/repositories'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repository'; import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repository';
import { Service } from 'typedi'; import { Service } from 'typedi';
import { isWorkflowHistoryEnabled } from './workflowHistoryHelper.ee'; import { isWorkflowHistoryEnabled } from './workflowHistoryHelper.ee';

View file

@ -1,7 +1,7 @@
import { Service } from 'typedi'; import { Service } from 'typedi';
import { LessThan } from 'typeorm'; import { LessThan } from 'typeorm';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { WorkflowHistoryRepository } from '@/databases/repositories'; import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repository';
import { WORKFLOW_HISTORY_PRUNE_INTERVAL } from './constants'; import { WORKFLOW_HISTORY_PRUNE_INTERVAL } from './constants';
import { getWorkflowHistoryPruneTime, isWorkflowHistoryEnabled } from './workflowHistoryHelper.ee'; import { getWorkflowHistoryPruneTime, isWorkflowHistoryEnabled } from './workflowHistoryHelper.ee';

View file

@ -26,6 +26,7 @@ import { listQueryMiddleware } from '@/middlewares';
import { TagService } from '@/services/tag.service'; import { TagService } from '@/services/tag.service';
import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee'; import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
export const workflowsController = express.Router(); export const workflowsController = express.Router();
workflowsController.use('/', EEWorkflowController); workflowsController.use('/', EEWorkflowController);
@ -205,7 +206,7 @@ workflowsController.get(
relations = relations.filter((relation) => relation !== 'workflow.tags'); relations = relations.filter((relation) => relation !== 'workflow.tags');
} }
const shared = await Db.collections.SharedWorkflow.findOne({ const shared = await Container.get(SharedWorkflowRepository).findOne({
relations, relations,
where: whereClause({ where: whereClause({
user: req.user, user: req.user,

View file

@ -1,6 +1,5 @@
import type { DeleteResult, EntityManager } from 'typeorm'; import type { DeleteResult, EntityManager } from 'typeorm';
import { In, Not } from 'typeorm'; import { In, Not } from 'typeorm';
import * as Db from '@/Db';
import * as ResponseHelper from '@/ResponseHelper'; import * as ResponseHelper from '@/ResponseHelper';
import * as WorkflowHelpers from '@/WorkflowHelpers'; import * as WorkflowHelpers from '@/WorkflowHelpers';
import { SharedWorkflow } from '@db/entities/SharedWorkflow'; import { SharedWorkflow } from '@db/entities/SharedWorkflow';
@ -16,7 +15,8 @@ import { CredentialsService } from '@/credentials/credentials.service';
import { NodeOperationError } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import Container from 'typedi'; import Container from 'typedi';
import type { CredentialsEntity } from '@/databases/entities/CredentialsEntity'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
export class EEWorkflowsService extends WorkflowsService { export class EEWorkflowsService extends WorkflowsService {
static async isOwned( static async isOwned(
@ -73,7 +73,7 @@ export class EEWorkflowsService extends WorkflowsService {
userId: user.id, userId: user.id,
roleId: role?.id, roleId: role?.id,
}; };
acc.push(Db.collections.SharedWorkflow.create(entity)); acc.push(Container.get(SharedWorkflowRepository).create(entity));
return acc; return acc;
}, []); }, []);

View file

@ -6,7 +6,6 @@ import { In, Like } from 'typeorm';
import pick from 'lodash/pick'; import pick from 'lodash/pick';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import * as Db from '@/Db';
import * as ResponseHelper from '@/ResponseHelper'; import * as ResponseHelper from '@/ResponseHelper';
import * as WorkflowHelpers from '@/WorkflowHelpers'; import * as WorkflowHelpers from '@/WorkflowHelpers';
import config from '@/config'; import config from '@/config';
@ -24,13 +23,16 @@ import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'
import { TestWebhooks } from '@/TestWebhooks'; import { TestWebhooks } from '@/TestWebhooks';
import { whereClause } from '@/UserManagement/UserManagementHelper'; import { whereClause } from '@/UserManagement/UserManagementHelper';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { WorkflowRepository } from '@/databases/repositories'; import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { OwnershipService } from '@/services/ownership.service'; import { OwnershipService } from '@/services/ownership.service';
import { isStringArray, isWorkflowIdValid } from '@/utils'; import { isStringArray, isWorkflowIdValid } from '@/utils';
import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee'; import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee';
import { BinaryDataService } from 'n8n-core'; import { BinaryDataService } from 'n8n-core';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { WorkflowTagMappingRepository } from '@db/repositories/workflowTagMapping.repository';
import { ExecutionRepository } from '@db/repositories/execution.repository';
export class WorkflowsService { export class WorkflowsService {
static async getSharing( static async getSharing(
@ -48,7 +50,7 @@ export class WorkflowsService {
where.userId = user.id; where.userId = user.id;
} }
return Db.collections.SharedWorkflow.findOne({ where, relations }); return Container.get(SharedWorkflowRepository).findOne({ where, relations });
} }
/** /**
@ -94,7 +96,10 @@ export class WorkflowsService {
} }
static async get(workflow: FindOptionsWhere<WorkflowEntity>, options?: { relations: string[] }) { static async get(workflow: FindOptionsWhere<WorkflowEntity>, options?: { relations: string[] }) {
return Db.collections.Workflow.findOne({ where: workflow, relations: options?.relations }); return Container.get(WorkflowRepository).findOne({
where: workflow,
relations: options?.relations,
});
} }
static async getMany(sharedWorkflowIds: string[], options?: ListQuery.Options) { static async getMany(sharedWorkflowIds: string[], options?: ListQuery.Options) {
@ -186,7 +191,7 @@ export class WorkflowsService {
forceSave?: boolean, forceSave?: boolean,
roles?: string[], roles?: string[],
): Promise<WorkflowEntity> { ): Promise<WorkflowEntity> {
const shared = await Db.collections.SharedWorkflow.findOne({ const shared = await Container.get(SharedWorkflowRepository).findOne({
relations: ['workflow', 'role'], relations: ['workflow', 'role'],
where: whereClause({ where: whereClause({
user, user,
@ -282,7 +287,7 @@ export class WorkflowsService {
await validateEntity(workflow); await validateEntity(workflow);
} }
await Db.collections.Workflow.update( await Container.get(WorkflowRepository).update(
workflowId, workflowId,
pick(workflow, [ pick(workflow, [
'name', 'name',
@ -297,8 +302,8 @@ export class WorkflowsService {
); );
if (tagIds && !config.getEnv('workflowTagsDisabled')) { if (tagIds && !config.getEnv('workflowTagsDisabled')) {
await Db.collections.WorkflowTagMapping.delete({ workflowId }); await Container.get(WorkflowTagMappingRepository).delete({ workflowId });
await Db.collections.WorkflowTagMapping.insert( await Container.get(WorkflowTagMappingRepository).insert(
tagIds.map((tagId) => ({ tagId, workflowId })), tagIds.map((tagId) => ({ tagId, workflowId })),
); );
} }
@ -311,7 +316,7 @@ export class WorkflowsService {
// 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.
const updatedWorkflow = await Db.collections.Workflow.findOne({ const updatedWorkflow = await Container.get(WorkflowRepository).findOne({
where: { id: workflowId }, where: { id: workflowId },
relations, relations,
}); });
@ -342,7 +347,7 @@ export class WorkflowsService {
} catch (error) { } catch (error) {
// If workflow could not be activated set it again to inactive // If workflow could not be activated set it again to inactive
// and revert the versionId change so UI remains consistent // and revert the versionId change so UI remains consistent
await Db.collections.Workflow.update(workflowId, { await Container.get(WorkflowRepository).update(workflowId, {
active: false, active: false,
versionId: shared.workflow.versionId, versionId: shared.workflow.versionId,
}); });
@ -447,7 +452,7 @@ export class WorkflowsService {
static async delete(user: User, workflowId: string): Promise<WorkflowEntity | undefined> { static async delete(user: User, workflowId: string): Promise<WorkflowEntity | undefined> {
await Container.get(ExternalHooks).run('workflow.delete', [workflowId]); await Container.get(ExternalHooks).run('workflow.delete', [workflowId]);
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({ const sharedWorkflow = await Container.get(SharedWorkflowRepository).findOne({
relations: ['workflow', 'role'], relations: ['workflow', 'role'],
where: whereClause({ where: whereClause({
user, user,
@ -466,12 +471,14 @@ export class WorkflowsService {
await Container.get(ActiveWorkflowRunner).remove(workflowId); await Container.get(ActiveWorkflowRunner).remove(workflowId);
} }
const idsForDeletion = await Db.collections.Execution.find({ const idsForDeletion = await Container.get(ExecutionRepository)
select: ['id'], .find({
where: { workflowId }, select: ['id'],
}).then((rows) => rows.map(({ id: executionId }) => ({ workflowId, executionId }))); where: { workflowId },
})
.then((rows) => rows.map(({ id: executionId }) => ({ workflowId, executionId })));
await Db.collections.Workflow.delete(workflowId); await Container.get(WorkflowRepository).delete(workflowId);
await Container.get(BinaryDataService).deleteMany(idsForDeletion); await Container.get(BinaryDataService).deleteMany(idsForDeletion);
void Container.get(InternalHooks).onWorkflowDeleted(user, workflowId, false); void Container.get(InternalHooks).onWorkflowDeleted(user, workflowId, false);
@ -481,7 +488,7 @@ export class WorkflowsService {
} }
static async updateWorkflowTriggerCount(id: string, triggerCount: number): Promise<UpdateResult> { static async updateWorkflowTriggerCount(id: string, triggerCount: number): Promise<UpdateResult> {
const qb = Db.collections.Workflow.createQueryBuilder('workflow'); const qb = Container.get(WorkflowRepository).createQueryBuilder('workflow');
return qb return qb
.update() .update()
.set({ .set({
@ -527,7 +534,7 @@ export class WorkflowsService {
* @param {IDataObject} newStaticData The static data to save * @param {IDataObject} newStaticData The static data to save
*/ */
static async saveStaticDataById(workflowId: string, newStaticData: IDataObject): Promise<void> { static async saveStaticDataById(workflowId: string, newStaticData: IDataObject): Promise<void> {
await Db.collections.Workflow.update(workflowId, { await Container.get(WorkflowRepository).update(workflowId, {
staticData: newStaticData, staticData: newStaticData,
}); });
} }

View file

@ -14,14 +14,15 @@ import { WebhookService } from '@/services/webhook.service';
import * as WebhookHelpers from '@/WebhookHelpers'; import * as WebhookHelpers from '@/WebhookHelpers';
import * as AdditionalData from '@/WorkflowExecuteAdditionalData'; import * as AdditionalData from '@/WorkflowExecuteAdditionalData';
import { WorkflowRunner } from '@/WorkflowRunner'; import { WorkflowRunner } from '@/WorkflowRunner';
import type { User } from '@db/entities/User';
import { mockInstance, setSchedulerAsLoadedNode } from './shared/utils'; import type { WebhookEntity } from '@db/entities/WebhookEntity';
import * as testDb from './shared/testDb';
import type { User } from '@/databases/entities/User';
import type { WebhookEntity } from '@/databases/entities/WebhookEntity';
import { NodeTypes } from '@/NodeTypes'; import { NodeTypes } from '@/NodeTypes';
import { chooseRandomly } from './shared/random';
import { MultiMainInstancePublisher } from '@/services/orchestration/main/MultiMainInstance.publisher.ee'; import { MultiMainInstancePublisher } from '@/services/orchestration/main/MultiMainInstance.publisher.ee';
import { mockInstance } from '../shared/mocking';
import { chooseRandomly } from './shared/random';
import { setSchedulerAsLoadedNode } from './shared/utils';
import * as testDb from './shared/testDb';
import { createOwner } from './shared/db/users'; import { createOwner } from './shared/db/users';
import { createWorkflow } from './shared/db/workflows'; import { createWorkflow } from './shared/db/workflows';

View file

@ -1,36 +1,38 @@
import type { SuperAgentTest } from 'supertest'; import type { SuperAgentTest } from 'supertest';
import { License } from '@/License'; import { License } from '@/License';
import * as utils from '../shared/utils/';
import type { ExternalSecretsSettings, SecretsProviderState } from '@/Interfaces'; import type { ExternalSecretsSettings, SecretsProviderState } from '@/Interfaces';
import { Cipher } from 'n8n-core'; import { Cipher } from 'n8n-core';
import { SettingsRepository } from '@/databases/repositories/settings.repository'; import { SettingsRepository } from '@db/repositories/settings.repository';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { ExternalSecretsProviders } from '@/ExternalSecrets/ExternalSecretsProviders.ee'; import { ExternalSecretsProviders } from '@/ExternalSecrets/ExternalSecretsProviders.ee';
import config from '@/config';
import { ExternalSecretsManager } from '@/ExternalSecrets/ExternalSecretsManager.ee';
import { CREDENTIAL_BLANKING_VALUE } from '@/constants';
import { jsonParse, type IDataObject } from 'n8n-workflow';
import { mock } from 'jest-mock-extended';
import { mockInstance } from '../../shared/mocking';
import { setupTestServer } from '../shared/utils';
import { createOwner, createUser } from '../shared/db/users';
import { import {
DummyProvider, DummyProvider,
FailedProvider, FailedProvider,
MockProviders, MockProviders,
TestFailProvider, TestFailProvider,
} from '../../shared/ExternalSecrets/utils'; } from '../../shared/ExternalSecrets/utils';
import config from '@/config';
import { ExternalSecretsManager } from '@/ExternalSecrets/ExternalSecretsManager.ee';
import { CREDENTIAL_BLANKING_VALUE } from '@/constants';
import { jsonParse, type IDataObject } from 'n8n-workflow';
import { mock } from 'jest-mock-extended';
import { createOwner, createUser } from '../shared/db/users';
let authOwnerAgent: SuperAgentTest; let authOwnerAgent: SuperAgentTest;
let authMemberAgent: SuperAgentTest; let authMemberAgent: SuperAgentTest;
const licenseLike = utils.mockInstance(License, { const licenseLike = mockInstance(License, {
isExternalSecretsEnabled: jest.fn().mockReturnValue(true), isExternalSecretsEnabled: jest.fn().mockReturnValue(true),
isWithinUsersLimit: jest.fn().mockReturnValue(true), isWithinUsersLimit: jest.fn().mockReturnValue(true),
}); });
const mockProvidersInstance = new MockProviders(); const mockProvidersInstance = new MockProviders();
utils.mockInstance(ExternalSecretsProviders, mockProvidersInstance); mockInstance(ExternalSecretsProviders, mockProvidersInstance);
const testServer = utils.setupTestServer({ endpointGroups: ['externalSecrets'] }); const testServer = setupTestServer({ endpointGroups: ['externalSecrets'] });
const connectedDate = '2023-08-01T12:32:29.000Z'; const connectedDate = '2023-08-01T12:32:29.000Z';

View file

@ -1,11 +1,15 @@
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import * as Db from '@/Db';
import config from '@/config'; import config from '@/config';
import { audit } from '@/audit'; import { audit } from '@/audit';
import { CREDENTIALS_REPORT } from '@/audit/constants'; import { CREDENTIALS_REPORT } from '@/audit/constants';
import { getRiskSection } from './utils'; import { getRiskSection } from './utils';
import * as testDb from '../shared/testDb'; import * as testDb from '../shared/testDb';
import { generateNanoId } from '@db/utils/generators'; import { generateNanoId } from '@db/utils/generators';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import Container from 'typedi';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { ExecutionRepository } from '@db/repositories/execution.repository';
import { ExecutionDataRepository } from '@db/repositories/executionData.repository';
beforeAll(async () => { beforeAll(async () => {
await testDb.init(); await testDb.init();
@ -46,8 +50,8 @@ test('should report credentials not in any use', async () => {
}; };
await Promise.all([ await Promise.all([
Db.collections.Credentials.save(credentialDetails), Container.get(CredentialsRepository).save(credentialDetails),
Db.collections.Workflow.save(workflowDetails), Container.get(WorkflowRepository).save(workflowDetails),
]); ]);
const testAudit = await audit(['credentials']); const testAudit = await audit(['credentials']);
@ -74,7 +78,7 @@ test('should report credentials not in active use', async () => {
nodesAccess: [{ nodeType: 'n8n-nodes-base.slack', date: '2022-12-21T11:23:00.561Z' }], nodesAccess: [{ nodeType: 'n8n-nodes-base.slack', date: '2022-12-21T11:23:00.561Z' }],
}; };
const credential = await Db.collections.Credentials.save(credentialDetails); const credential = await Container.get(CredentialsRepository).save(credentialDetails);
const workflowDetails = { const workflowDetails = {
id: generateNanoId(), id: generateNanoId(),
@ -93,7 +97,7 @@ test('should report credentials not in active use', async () => {
], ],
}; };
await Db.collections.Workflow.save(workflowDetails); await Container.get(WorkflowRepository).save(workflowDetails);
const testAudit = await audit(['credentials']); const testAudit = await audit(['credentials']);
@ -119,7 +123,7 @@ test('should report credential in not recently executed workflow', async () => {
nodesAccess: [{ nodeType: 'n8n-nodes-base.slack', date: '2022-12-21T11:23:00.561Z' }], nodesAccess: [{ nodeType: 'n8n-nodes-base.slack', date: '2022-12-21T11:23:00.561Z' }],
}; };
const credential = await Db.collections.Credentials.save(credentialDetails); const credential = await Container.get(CredentialsRepository).save(credentialDetails);
const workflowDetails = { const workflowDetails = {
id: generateNanoId(), id: generateNanoId(),
@ -144,12 +148,12 @@ test('should report credential in not recently executed workflow', async () => {
], ],
}; };
const workflow = await Db.collections.Workflow.save(workflowDetails); const workflow = await Container.get(WorkflowRepository).save(workflowDetails);
const date = new Date(); const date = new Date();
date.setDate(date.getDate() - config.getEnv('security.audit.daysAbandonedWorkflow') - 1); date.setDate(date.getDate() - config.getEnv('security.audit.daysAbandonedWorkflow') - 1);
const savedExecution = await Db.collections.Execution.save({ const savedExecution = await Container.get(ExecutionRepository).save({
finished: true, finished: true,
mode: 'manual', mode: 'manual',
startedAt: date, startedAt: date,
@ -157,7 +161,7 @@ test('should report credential in not recently executed workflow', async () => {
workflowId: workflow.id, workflowId: workflow.id,
waitTill: null, waitTill: null,
}); });
await Db.collections.ExecutionData.save({ await Container.get(ExecutionDataRepository).save({
execution: savedExecution, execution: savedExecution,
data: '[]', data: '[]',
workflowData: workflow, workflowData: workflow,
@ -187,7 +191,7 @@ test('should not report credentials in recently executed workflow', async () =>
nodesAccess: [{ nodeType: 'n8n-nodes-base.slack', date: '2022-12-21T11:23:00.561Z' }], nodesAccess: [{ nodeType: 'n8n-nodes-base.slack', date: '2022-12-21T11:23:00.561Z' }],
}; };
const credential = await Db.collections.Credentials.save(credentialDetails); const credential = await Container.get(CredentialsRepository).save(credentialDetails);
const workflowDetails = { const workflowDetails = {
id: generateNanoId(), id: generateNanoId(),
@ -212,12 +216,12 @@ test('should not report credentials in recently executed workflow', async () =>
], ],
}; };
const workflow = await Db.collections.Workflow.save(workflowDetails); const workflow = await Container.get(WorkflowRepository).save(workflowDetails);
const date = new Date(); const date = new Date();
date.setDate(date.getDate() - config.getEnv('security.audit.daysAbandonedWorkflow') + 1); date.setDate(date.getDate() - config.getEnv('security.audit.daysAbandonedWorkflow') + 1);
const savedExecution = await Db.collections.Execution.save({ const savedExecution = await Container.get(ExecutionRepository).save({
finished: true, finished: true,
mode: 'manual', mode: 'manual',
startedAt: date, startedAt: date,
@ -226,7 +230,7 @@ test('should not report credentials in recently executed workflow', async () =>
waitTill: null, waitTill: null,
}); });
await Db.collections.ExecutionData.save({ await Container.get(ExecutionDataRepository).save({
execution: savedExecution, execution: savedExecution,
data: '[]', data: '[]',
workflowData: workflow, workflowData: workflow,

View file

@ -1,5 +1,4 @@
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import * as Db from '@/Db';
import { audit } from '@/audit'; import { audit } from '@/audit';
import { import {
DATABASE_REPORT, DATABASE_REPORT,
@ -9,6 +8,8 @@ import {
import { getRiskSection, saveManualTriggerWorkflow } from './utils'; import { getRiskSection, saveManualTriggerWorkflow } from './utils';
import * as testDb from '../shared/testDb'; import * as testDb from '../shared/testDb';
import { generateNanoId } from '@db/utils/generators'; import { generateNanoId } from '@db/utils/generators';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import Container from 'typedi';
beforeAll(async () => { beforeAll(async () => {
await testDb.init(); await testDb.init();
@ -50,7 +51,7 @@ test('should report expressions in queries', async () => {
], ],
}; };
return Db.collections.Workflow.save(details); return Container.get(WorkflowRepository).save(details);
}); });
await Promise.all(promises); await Promise.all(promises);
@ -105,7 +106,7 @@ test('should report expressions in query params', async () => {
], ],
}; };
return Db.collections.Workflow.save(details); return Container.get(WorkflowRepository).save(details);
}); });
await Promise.all(promises); await Promise.all(promises);
@ -157,7 +158,7 @@ test('should report unused query params', async () => {
], ],
}; };
return Db.collections.Workflow.save(details); return Container.get(WorkflowRepository).save(details);
}); });
await Promise.all(promises); await Promise.all(promises);

View file

@ -1,9 +1,10 @@
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import * as Db from '@/Db';
import { audit } from '@/audit'; import { audit } from '@/audit';
import { FILESYSTEM_INTERACTION_NODE_TYPES, FILESYSTEM_REPORT } from '@/audit/constants'; import { FILESYSTEM_INTERACTION_NODE_TYPES, FILESYSTEM_REPORT } from '@/audit/constants';
import { getRiskSection, saveManualTriggerWorkflow } from './utils'; import { getRiskSection, saveManualTriggerWorkflow } from './utils';
import * as testDb from '../shared/testDb'; import * as testDb from '../shared/testDb';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import Container from 'typedi';
beforeAll(async () => { beforeAll(async () => {
await testDb.init(); await testDb.init();
@ -26,7 +27,7 @@ test('should report filesystem interaction nodes', async () => {
); );
const promises = Object.entries(map).map(async ([nodeType, nodeId]) => { const promises = Object.entries(map).map(async ([nodeType, nodeId]) => {
const details = Db.collections.Workflow.create({ const details = Container.get(WorkflowRepository).create({
name: 'My Test Workflow', name: 'My Test Workflow',
active: false, active: false,
connections: {}, connections: {},
@ -42,7 +43,7 @@ test('should report filesystem interaction nodes', async () => {
], ],
}); });
return Db.collections.Workflow.save(details); return Container.get(WorkflowRepository).save(details);
}); });
await Promise.all(promises); await Promise.all(promises);

View file

@ -1,5 +1,4 @@
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import * as Db from '@/Db';
import { audit } from '@/audit'; import { audit } from '@/audit';
import { INSTANCE_REPORT, WEBHOOK_VALIDATOR_NODE_TYPES } from '@/audit/constants'; import { INSTANCE_REPORT, WEBHOOK_VALIDATOR_NODE_TYPES } from '@/audit/constants';
import { import {
@ -13,6 +12,8 @@ import * as testDb from '../shared/testDb';
import { toReportTitle } from '@/audit/utils'; import { toReportTitle } from '@/audit/utils';
import config from '@/config'; import config from '@/config';
import { generateNanoId } from '@db/utils/generators'; import { generateNanoId } from '@db/utils/generators';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import Container from 'typedi';
beforeAll(async () => { beforeAll(async () => {
await testDb.init(); await testDb.init();
@ -53,7 +54,7 @@ test('should report webhook lacking authentication', async () => {
], ],
}; };
await Db.collections.Workflow.save(details); await Container.get(WorkflowRepository).save(details);
const testAudit = await audit(['instance']); const testAudit = await audit(['instance']);
@ -97,7 +98,7 @@ test('should not report webhooks having basic or header auth', async () => {
], ],
}; };
return Db.collections.Workflow.save(details); return Container.get(WorkflowRepository).save(details);
}); });
await Promise.all(promises); await Promise.all(promises);
@ -158,7 +159,7 @@ test('should not report webhooks validated by direct children', async () => {
}, },
}; };
return Db.collections.Workflow.save(details); return Container.get(WorkflowRepository).save(details);
}); });
await Promise.all(promises); await Promise.all(promises);

View file

@ -1,15 +1,16 @@
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { Container } from 'typedi'; import { Container } from 'typedi';
import * as Db from '@/Db';
import { audit } from '@/audit'; import { audit } from '@/audit';
import { OFFICIAL_RISKY_NODE_TYPES, NODES_REPORT } from '@/audit/constants'; import { OFFICIAL_RISKY_NODE_TYPES, NODES_REPORT } from '@/audit/constants';
import { getRiskSection, MOCK_PACKAGE, saveManualTriggerWorkflow } from './utils';
import * as testDb from '../shared/testDb';
import { toReportTitle } from '@/audit/utils'; import { toReportTitle } from '@/audit/utils';
import { mockInstance } from '../shared/utils/';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { NodeTypes } from '@/NodeTypes'; import { NodeTypes } from '@/NodeTypes';
import { CommunityPackagesService } from '@/services/communityPackages.service'; import { CommunityPackagesService } from '@/services/communityPackages.service';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { mockInstance } from '../../shared/mocking';
import { getRiskSection, MOCK_PACKAGE, saveManualTriggerWorkflow } from './utils';
import * as testDb from '../shared/testDb';
const nodesAndCredentials = mockInstance(LoadNodesAndCredentials); const nodesAndCredentials = mockInstance(LoadNodesAndCredentials);
nodesAndCredentials.getCustomDirectories.mockReturnValue([]); nodesAndCredentials.getCustomDirectories.mockReturnValue([]);
@ -37,7 +38,7 @@ test('should report risky official nodes', async () => {
}, {}); }, {});
const promises = Object.entries(map).map(async ([nodeType, nodeId]) => { const promises = Object.entries(map).map(async ([nodeType, nodeId]) => {
const details = Db.collections.Workflow.create({ const details = Container.get(WorkflowRepository).create({
name: 'My Test Workflow', name: 'My Test Workflow',
active: false, active: false,
connections: {}, connections: {},
@ -53,7 +54,7 @@ test('should report risky official nodes', async () => {
], ],
}); });
return Db.collections.Workflow.save(details); return Container.get(WorkflowRepository).save(details);
}); });
await Promise.all(promises); await Promise.all(promises);

Some files were not shown because too many files have changed in this diff Show more