diff --git a/packages/twenty-server/src/engine/core-modules/file-storage/__tests__/file-storage.service.spec.ts b/packages/twenty-server/src/engine/core-modules/file-storage/__tests__/file-storage.service.spec.ts index 8e168af0bc..afd9a1f2c3 100644 --- a/packages/twenty-server/src/engine/core-modules/file-storage/__tests__/file-storage.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/file-storage/__tests__/file-storage.service.spec.ts @@ -3,6 +3,7 @@ import { getRepositoryToken } from '@nestjs/typeorm'; import { Readable } from 'stream'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; import { FileStorageDriverFactory } from 'src/engine/core-modules/file-storage/file-storage-driver.factory'; import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service'; import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity'; @@ -19,6 +20,10 @@ describe('FileStorageService', () => { save: jest.fn(), }; + const mockApplicationRepository = { + findOneOrFail: jest.fn(), + }; + beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ @@ -31,6 +36,10 @@ describe('FileStorageService', () => { provide: getRepositoryToken(FileEntity), useValue: mockFileRepository, }, + { + provide: getRepositoryToken(ApplicationEntity), + useValue: mockApplicationRepository, + }, ], }).compile(); diff --git a/packages/twenty-server/src/engine/core-modules/file-storage/file-storage.module.ts b/packages/twenty-server/src/engine/core-modules/file-storage/file-storage.module.ts index bfd3317315..4bff82ca2c 100644 --- a/packages/twenty-server/src/engine/core-modules/file-storage/file-storage.module.ts +++ b/packages/twenty-server/src/engine/core-modules/file-storage/file-storage.module.ts @@ -1,6 +1,7 @@ import { type DynamicModule, Global } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; import { FileStorageDriverFactory } from 'src/engine/core-modules/file-storage/file-storage-driver.factory'; import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service'; import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity'; @@ -11,7 +12,10 @@ export class FileStorageModule { static forRoot(): DynamicModule { return { module: FileStorageModule, - imports: [TwentyConfigModule, TypeOrmModule.forFeature([FileEntity])], + imports: [ + TwentyConfigModule, + TypeOrmModule.forFeature([FileEntity, ApplicationEntity]), + ], providers: [FileStorageDriverFactory, FileStorageService], exports: [FileStorageService], }; diff --git a/packages/twenty-server/src/engine/core-modules/file-storage/file-storage.service.ts b/packages/twenty-server/src/engine/core-modules/file-storage/file-storage.service.ts index 2fa0f6fb2c..dce64ebb6d 100644 --- a/packages/twenty-server/src/engine/core-modules/file-storage/file-storage.service.ts +++ b/packages/twenty-server/src/engine/core-modules/file-storage/file-storage.service.ts @@ -6,6 +6,7 @@ import { type Readable } from 'stream'; import { FileFolder, Sources } from 'twenty-shared/types'; import { Repository } from 'typeorm'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; import { FileStorageDriverFactory } from 'src/engine/core-modules/file-storage/file-storage-driver.factory'; import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity'; @@ -17,6 +18,8 @@ export class FileStorageService { private readonly fileStorageDriverFactory: FileStorageDriverFactory, @InjectRepository(FileEntity) private readonly fileRepository: Repository, + @InjectRepository(ApplicationEntity) + private readonly applicationRepository: Repository, ) {} /** @@ -44,7 +47,7 @@ export class FileStorageService { destinationPath, mimeType, fileFolder, - applicationId, + applicationUniversalIdentifier, workspaceId, fileId, }: { @@ -52,14 +55,21 @@ export class FileStorageService { destinationPath: string; mimeType: string | undefined; fileFolder: FileFolder; - applicationId: string; + applicationUniversalIdentifier: string; workspaceId: string; fileId?: string; }): Promise { const driver = this.fileStorageDriverFactory.getCurrentDriver(); + const application = await this.applicationRepository.findOneOrFail({ + where: { + universalIdentifier: applicationUniversalIdentifier, + workspaceId, + }, + }); + const driverParams = { - filePath: `${workspaceId}/${applicationId}/${fileFolder}/${destinationPath}`, + filePath: `${workspaceId}/${applicationUniversalIdentifier}/${fileFolder}/${destinationPath}`, mimeType, sourceFile, }; @@ -69,7 +79,7 @@ export class FileStorageService { const fileEntity = await this.fileRepository.save({ path: `${fileFolder}/${destinationPath}`, workspaceId, - applicationId, + applicationId: application.id, id: fileId, size: typeof sourceFile === 'string' @@ -93,17 +103,17 @@ export class FileStorageService { read_v2({ destinationPath, fileFolder, - applicationId, + applicationUniversalIdentifier, workspaceId, }: { destinationPath: string; fileFolder: FileFolder; - applicationId: string; + applicationUniversalIdentifier: string; workspaceId: string; }): Promise { const driver = this.fileStorageDriverFactory.getCurrentDriver(); - const folderPath = `${workspaceId}/${applicationId}/${fileFolder}/${destinationPath}`; + const folderPath = `${workspaceId}/${applicationUniversalIdentifier}/${fileFolder}/${destinationPath}`; return driver.read({ filePath: folderPath }); } diff --git a/packages/twenty-server/src/engine/core-modules/file/file-upload/file-upload.module.ts b/packages/twenty-server/src/engine/core-modules/file/file-upload/file-upload.module.ts index 1da6e3c49d..dbc2f79e4a 100644 --- a/packages/twenty-server/src/engine/core-modules/file/file-upload/file-upload.module.ts +++ b/packages/twenty-server/src/engine/core-modules/file/file-upload/file-upload.module.ts @@ -1,13 +1,20 @@ import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; import { FileUploadResolver } from 'src/engine/core-modules/file/file-upload/resolvers/file-upload.resolver'; import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service'; import { FileModule } from 'src/engine/core-modules/file/file.module'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; @Module({ - imports: [FileModule, HttpModule, PermissionsModule], + imports: [ + FileModule, + HttpModule, + PermissionsModule, + TypeOrmModule.forFeature([ApplicationEntity]), + ], providers: [FileUploadService, FileUploadResolver], exports: [FileUploadService, FileUploadResolver], }) diff --git a/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts b/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts index 572221d057..27175e2152 100644 --- a/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts +++ b/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts @@ -1,13 +1,16 @@ import { HttpService } from '@nestjs/axios'; import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; import DOMPurify from 'dompurify'; import FileType from 'file-type'; import sharp from 'sharp'; import { FileFolder } from 'twenty-shared/types'; +import { Repository } from 'typeorm'; import { v4 } from 'uuid'; import { settings } from 'src/engine/constants/settings'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service'; import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; @@ -29,6 +32,8 @@ export class FileUploadService { private readonly fileStorage: FileStorageService, private readonly fileService: FileService, private readonly httpService: HttpService, + @InjectRepository(ApplicationEntity) + private readonly applicationRepository: Repository, ) {} private async _uploadFile({ @@ -236,12 +241,19 @@ export class FileUploadService { const fileId = v4(); const name = `${fileId}${ext ? `.${ext}` : ''}`; + const application = await this.applicationRepository.findOneOrFail({ + where: { + id: applicationId, + workspaceId, + }, + }); + return await this.fileStorage.write_v2({ sourceFile: sanitizedFile, destinationPath: name, mimeType, fileFolder: FileFolder.FilesField, - applicationId, + applicationUniversalIdentifier: application.universalIdentifier, workspaceId, fileId, }); diff --git a/packages/twenty-server/src/engine/core-modules/file/file.module.ts b/packages/twenty-server/src/engine/core-modules/file/file.module.ts index 31cbbd7ac2..4c6a70251d 100644 --- a/packages/twenty-server/src/engine/core-modules/file/file.module.ts +++ b/packages/twenty-server/src/engine/core-modules/file/file.module.ts @@ -2,6 +2,7 @@ import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; import { FilePathGuard } from 'src/engine/core-modules/file/guards/file-path-guard'; import { FileDeletionJob } from 'src/engine/core-modules/file/jobs/file-deletion.job'; import { FileWorkspaceFolderDeletionJob } from 'src/engine/core-modules/file/jobs/file-workspace-folder-deletion.job'; @@ -21,7 +22,7 @@ import { FileService } from './services/file.service'; @Module({ imports: [ JwtModule, - TypeOrmModule.forFeature([FileEntity, WorkspaceEntity]), + TypeOrmModule.forFeature([FileEntity, WorkspaceEntity, ApplicationEntity]), HttpModule, PermissionsModule, ],