diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts index bc1dcdc36f940..ab3338e58dbef 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts @@ -27,6 +27,7 @@ import { WorkflowHistoryService } from '@/workflows/workflowHistory/workflowHist import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflow.repository'; import { TagRepository } from '@/databases/repositories/tag.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; +import { ProjectRepository } from '@/databases/repositories/project.repository'; export = { createWorkflow: [ @@ -41,7 +42,10 @@ export = { addNodeIds(workflow); - const createdWorkflow = await createWorkflow(workflow, req.user, 'workflow:owner'); + const project = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail( + req.user.id, + ); + const createdWorkflow = await createWorkflow(workflow, req.user, project, 'workflow:owner'); await Container.get(WorkflowHistoryService).saveVersion( req.user, diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts index 8d53a72ea14c1..2df7f78062bea 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts @@ -6,6 +6,7 @@ import { SharedWorkflow, type WorkflowSharingRole } from '@db/entities/SharedWor import config from '@/config'; import { WorkflowRepository } from '@db/repositories/workflow.repository'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; +import type { Project } from '@/databases/entities/Project'; function insertIf(condition: boolean, elements: string[]): string[] { return condition ? elements : []; @@ -42,6 +43,7 @@ export async function getWorkflowById(id: string): Promise { return await Db.transaction(async (transactionManager) => { @@ -53,6 +55,7 @@ export async function createWorkflow( Object.assign(newSharedWorkflow, { role, user, + project: personalProject, workflow: savedWorkflow, }); await transactionManager.save(newSharedWorkflow); diff --git a/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts b/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts index a457efb5a8b7b..9425ae6cc1a56 100644 --- a/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts +++ b/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts @@ -5,10 +5,14 @@ import { SharedWorkflow, type WorkflowSharingRole } from '../entities/SharedWork import { type User } from '../entities/User'; import type { Scope } from '@n8n/permissions'; import type { WorkflowEntity } from '../entities/WorkflowEntity'; +import { ProjectRepository } from './project.repository'; @Service() export class SharedWorkflowRepository extends Repository { - constructor(dataSource: DataSource) { + constructor( + dataSource: DataSource, + private readonly projectRepository: ProjectRepository, + ) { super(SharedWorkflow, dataSource.manager); } @@ -125,18 +129,22 @@ export class SharedWorkflowRepository extends Repository { } async share(transaction: EntityManager, workflow: WorkflowEntity, users: User[]) { - const newSharedWorkflows = users.reduce((acc, user) => { + const newSharedWorkflows = []; + + for (const user of users) { if (user.isPending) { - return acc; + continue; } + + const project = await this.projectRepository.getPersonalProjectForUserOrFail(user.id); const entity: Partial = { workflowId: workflow.id, userId: user.id, + projectId: project.id, role: 'workflow:editor', }; - acc.push(this.create(entity)); - return acc; - }, []); + newSharedWorkflows.push(this.create(entity)); + } return await transaction.save(newSharedWorkflows); } diff --git a/packages/cli/src/services/import.service.ts b/packages/cli/src/services/import.service.ts index 32f6894f9b844..16200a1621610 100644 --- a/packages/cli/src/services/import.service.ts +++ b/packages/cli/src/services/import.service.ts @@ -1,4 +1,4 @@ -import { Service } from 'typedi'; +import Container, { Service } from 'typedi'; import { v4 as uuid } from 'uuid'; import { type INode, type INodeCredentialsDetails } from 'n8n-workflow'; @@ -12,6 +12,7 @@ import { WorkflowEntity } from '@db/entities/WorkflowEntity'; import { WorkflowTagMapping } from '@db/entities/WorkflowTagMapping'; import type { TagEntity } from '@db/entities/TagEntity'; import type { ICredentialsDb } from '@/Interfaces'; +import { ProjectRepository } from '@/databases/repositories/project.repository'; @Service() export class ImportService { @@ -57,10 +58,14 @@ export class ImportService { const workflowId = upsertResult.identifiers.at(0)?.id as string; - await tx.upsert(SharedWorkflow, { workflowId, userId, role: 'workflow:owner' }, [ - 'workflowId', - 'userId', - ]); + const personalProject = + await Container.get(ProjectRepository).getPersonalProjectForUserOrFail(userId); + + await tx.upsert( + SharedWorkflow, + { workflowId, userId, projectId: personalProject.id, role: 'workflow:owner' }, + ['workflowId', 'userId'], + ); if (!workflow.tags?.length) continue; diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index 807b892cc3a27..751521cc1b6c9 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -38,6 +38,7 @@ import { EnterpriseWorkflowService } from './workflow.service.ee'; import { WorkflowExecutionService } from './workflowExecution.service'; import { WorkflowSharingService } from './workflowSharing.service'; import { UserManagementMailer } from '@/UserManagement/email'; +import { ProjectRepository } from '@/databases/repositories/project.repository'; @Authorized() @RestController('/workflows') @@ -61,6 +62,7 @@ export class WorkflowsController { private readonly license: License, private readonly mailer: UserManagementMailer, private readonly credentialsService: CredentialsService, + private readonly projectRepository: ProjectRepository, ) {} @Post('/') @@ -110,11 +112,14 @@ export class WorkflowsController { await Db.transaction(async (transactionManager) => { savedWorkflow = await transactionManager.save(newWorkflow); + const project = await this.projectRepository.getPersonalProjectForUserOrFail(req.user.id); + const newSharedWorkflow = new SharedWorkflow(); Object.assign(newSharedWorkflow, { role: 'workflow:owner', user: req.user, + project, workflow: savedWorkflow, }); diff --git a/packages/cli/test/integration/PermissionChecker.test.ts b/packages/cli/test/integration/PermissionChecker.test.ts index b8035926e8df7..bc19d2423652b 100644 --- a/packages/cli/test/integration/PermissionChecker.test.ts +++ b/packages/cli/test/integration/PermissionChecker.test.ts @@ -27,6 +27,7 @@ import type { SaveCredentialFunction } from '../integration/shared/types'; import { mockNodeTypesData } from '../unit/Helpers'; import { affixRoleToSaveCredential } from '../integration/shared/db/credentials'; import { createOwner, createUser } from '../integration/shared/db/users'; +import { ProjectRepository } from '@/databases/repositories/project.repository'; export const toTargetCallErrorMsg = (subworkflowId: string) => `Target workflow ID ${subworkflowId} may not be called`; @@ -240,10 +241,14 @@ describe('check()', () => { }; const workflowEntity = await Container.get(WorkflowRepository).save(workflowDetails); + const project = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail( + member.id, + ); await Container.get(SharedWorkflowRepository).save({ workflow: workflowEntity, user: member, + project, role: 'workflow:owner', }); diff --git a/packages/cli/test/integration/shared/db/workflows.ts b/packages/cli/test/integration/shared/db/workflows.ts index 5603db7ab9625..1f6035c9a2a6c 100644 --- a/packages/cli/test/integration/shared/db/workflows.ts +++ b/packages/cli/test/integration/shared/db/workflows.ts @@ -7,6 +7,7 @@ import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; import { WorkflowRepository } from '@db/repositories/workflow.repository'; import type { SharedWorkflow } from '@db/entities/SharedWorkflow'; +import { ProjectRepository } from '@/databases/repositories/project.repository'; export async function createManyWorkflows( amount: number, @@ -48,8 +49,10 @@ export async function createWorkflow(attributes: Partial = {}, u const workflow = await Container.get(WorkflowRepository).save(workflowEntity); if (user) { + const project = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail(user.id); await Container.get(SharedWorkflowRepository).save({ user, + project, workflow, role: 'workflow:owner', }); @@ -58,11 +61,19 @@ export async function createWorkflow(attributes: Partial = {}, u } export async function shareWorkflowWithUsers(workflow: WorkflowEntity, users: User[]) { - const sharedWorkflows: Array> = users.map((user) => ({ - userId: user.id, - workflowId: workflow.id, - role: 'workflow:editor', - })); + const sharedWorkflows: Array> = await Promise.all( + users.map(async (user) => { + const project = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail( + user.id, + ); + return { + userId: user.id, + projectId: project.id, + workflowId: workflow.id, + role: 'workflow:editor', + }; + }), + ); return await Container.get(SharedWorkflowRepository).save(sharedWorkflows); }