File storage refactor - Switch to applicationUniversalIdentifier (#17541)
This commit is contained in:
+9
@@ -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();
|
||||
|
||||
|
||||
@@ -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],
|
||||
};
|
||||
|
||||
+17
-7
@@ -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<FileEntity>,
|
||||
@InjectRepository(ApplicationEntity)
|
||||
private readonly applicationRepository: Repository<ApplicationEntity>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -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<FileEntity> {
|
||||
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<Readable> {
|
||||
const driver = this.fileStorageDriverFactory.getCurrentDriver();
|
||||
|
||||
const folderPath = `${workspaceId}/${applicationId}/${fileFolder}/${destinationPath}`;
|
||||
const folderPath = `${workspaceId}/${applicationUniversalIdentifier}/${fileFolder}/${destinationPath}`;
|
||||
|
||||
return driver.read({ filePath: folderPath });
|
||||
}
|
||||
|
||||
+8
-1
@@ -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],
|
||||
})
|
||||
|
||||
+13
-1
@@ -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<ApplicationEntity>,
|
||||
) {}
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user