mirror of
https://github.com/lingble/twenty.git
synced 2025-11-03 06:07:56 +00:00
Fix images in note rich text (#6550)
## Before <img width="439" alt="Screenshot 2024-08-06 at 11 20 06" src="https://github.com/user-attachments/assets/d2aa9411-cdf4-4457-8997-7cbecb8fe7e3"> ## After <img width="501" alt="Screenshot 2024-08-06 at 11 20 09" src="https://github.com/user-attachments/assets/a5a68fff-1542-4b62-939c-50070f15b692">
This commit is contained in:
@@ -0,0 +1,57 @@
|
|||||||
|
import { QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface';
|
||||||
|
|
||||||
|
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||||
|
import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||||
|
import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.workspace-entity';
|
||||||
|
|
||||||
|
type RichTextBlock = Record<string, any>;
|
||||||
|
|
||||||
|
type RichTextBody = RichTextBlock[];
|
||||||
|
|
||||||
|
export class ActivityQueryResultGetterHandler
|
||||||
|
implements QueryResultGetterHandlerInterface
|
||||||
|
{
|
||||||
|
constructor(private readonly fileService: FileService) {}
|
||||||
|
|
||||||
|
async handle(
|
||||||
|
activity: TaskWorkspaceEntity | NoteWorkspaceEntity,
|
||||||
|
workspaceId: string,
|
||||||
|
): Promise<TaskWorkspaceEntity | NoteWorkspaceEntity> {
|
||||||
|
if (!activity.id || !activity.body) {
|
||||||
|
return activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
const body: RichTextBody = JSON.parse(activity.body);
|
||||||
|
|
||||||
|
const bodyWithSignedPayload = await Promise.all(
|
||||||
|
body.map(async (block: RichTextBlock) => {
|
||||||
|
if (block.type !== 'image' || !block.props.url) {
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageProps = block.props;
|
||||||
|
const imageUrl = new URL(imageProps.url);
|
||||||
|
|
||||||
|
imageUrl.searchParams.delete('token');
|
||||||
|
|
||||||
|
const signedPayload = await this.fileService.encodeFileToken({
|
||||||
|
note_block_id: block.id,
|
||||||
|
workspace_id: workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...block,
|
||||||
|
props: {
|
||||||
|
...imageProps,
|
||||||
|
url: `${imageUrl.toString()}?token=${signedPayload}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...activity,
|
||||||
|
body: JSON.stringify(bodyWithSignedPayload),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
import { QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface';
|
import { QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface';
|
||||||
|
|
||||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||||
|
import { AttachmentWorkspaceEntity } from 'src/modules/attachment/standard-objects/attachment.workspace-entity';
|
||||||
|
|
||||||
export class AttachmentQueryResultGetterHandler
|
export class AttachmentQueryResultGetterHandler
|
||||||
implements QueryResultGetterHandlerInterface
|
implements QueryResultGetterHandlerInterface
|
||||||
{
|
{
|
||||||
constructor(private readonly fileService: FileService) {}
|
constructor(private readonly fileService: FileService) {}
|
||||||
|
|
||||||
async handle(attachment: any, workspaceId: string): Promise<any> {
|
async handle(
|
||||||
|
attachment: AttachmentWorkspaceEntity,
|
||||||
|
workspaceId: string,
|
||||||
|
): Promise<AttachmentWorkspaceEntity> {
|
||||||
if (!attachment.id || !attachment?.fullPath) {
|
if (!attachment.id || !attachment?.fullPath) {
|
||||||
return attachment;
|
return attachment;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class PersonQueryResultGetterHandler
|
|||||||
async handle(
|
async handle(
|
||||||
person: PersonWorkspaceEntity,
|
person: PersonWorkspaceEntity,
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
): Promise<any> {
|
): Promise<PersonWorkspaceEntity> {
|
||||||
if (!person.id || !person?.avatarUrl) {
|
if (!person.id || !person?.avatarUrl) {
|
||||||
return person;
|
return person;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class WorkspaceMemberQueryResultGetterHandler
|
|||||||
async handle(
|
async handle(
|
||||||
workspaceMember: WorkspaceMemberWorkspaceEntity,
|
workspaceMember: WorkspaceMemberWorkspaceEntity,
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
): Promise<any> {
|
): Promise<WorkspaceMemberWorkspaceEntity> {
|
||||||
if (!workspaceMember.id || !workspaceMember?.avatarUrl) {
|
if (!workspaceMember.id || !workspaceMember?.avatarUrl) {
|
||||||
return workspaceMember;
|
return workspaceMember;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface';
|
import { QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface';
|
||||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||||
|
|
||||||
|
import { ActivityQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler';
|
||||||
import { AttachmentQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/attachment-query-result-getter.handler';
|
import { AttachmentQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/attachment-query-result-getter.handler';
|
||||||
import { PersonQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/person-query-result-getter.handler';
|
import { PersonQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/person-query-result-getter.handler';
|
||||||
import { WorkspaceMemberQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/workspace-member-query-result-getter.handler';
|
import { WorkspaceMemberQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/workspace-member-query-result-getter.handler';
|
||||||
@@ -24,6 +25,8 @@ export class QueryResultGettersFactory {
|
|||||||
'workspaceMember',
|
'workspaceMember',
|
||||||
new WorkspaceMemberQueryResultGetterHandler(this.fileService),
|
new WorkspaceMemberQueryResultGetterHandler(this.fileService),
|
||||||
],
|
],
|
||||||
|
['note', new ActivityQueryResultGetterHandler(this.fileService)],
|
||||||
|
['task', new ActivityQueryResultGetterHandler(this.fileService)],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
|
||||||
import { FileUploadResolver } from 'src/engine/core-modules/file/file-upload/resolvers/file-upload.resolver';
|
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 { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
||||||
|
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||||
|
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
imports: [FileModule],
|
||||||
providers: [FileUploadService, FileUploadResolver, EnvironmentService],
|
providers: [FileUploadService, FileUploadResolver, EnvironmentService],
|
||||||
exports: [FileUploadService, FileUploadResolver],
|
exports: [FileUploadService, FileUploadResolver],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
|
||||||
|
|
||||||
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
|
||||||
import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service';
|
|
||||||
|
|
||||||
import { FileUploadService } from './file-upload.service';
|
|
||||||
|
|
||||||
describe('FileUploadService', () => {
|
|
||||||
let service: FileUploadService;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
|
||||||
providers: [
|
|
||||||
FileUploadService,
|
|
||||||
{
|
|
||||||
provide: FileStorageService,
|
|
||||||
useValue: {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: EnvironmentService,
|
|
||||||
useValue: {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
service = module.get<FileUploadService>(FileUploadService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be defined', () => {
|
|
||||||
expect(service).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -8,12 +8,16 @@ import { v4 as uuidV4 } from 'uuid';
|
|||||||
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
|
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
|
||||||
|
|
||||||
import { settings } from 'src/engine/constants/settings';
|
import { settings } from 'src/engine/constants/settings';
|
||||||
|
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||||
import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service';
|
import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service';
|
||||||
import { getCropSize } from 'src/utils/image';
|
import { getCropSize } from 'src/utils/image';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FileUploadService {
|
export class FileUploadService {
|
||||||
constructor(private readonly fileStorage: FileStorageService) {}
|
constructor(
|
||||||
|
private readonly fileStorage: FileStorageService,
|
||||||
|
private readonly fileService: FileService,
|
||||||
|
) {}
|
||||||
|
|
||||||
private async _uploadFile({
|
private async _uploadFile({
|
||||||
file,
|
file,
|
||||||
@@ -78,10 +82,14 @@ export class FileUploadService {
|
|||||||
folder,
|
folder,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const signedPayload = await this.fileService.encodeFileToken({
|
||||||
|
workspace_id: workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
mimeType,
|
mimeType,
|
||||||
path: `${fileFolder}/${name}`,
|
path: `${fileFolder}/${name}?token=${signedPayload}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
|
|
||||||
import { FilePathGuard } from 'src/engine/core-modules/file/guards/file-path-guard';
|
import { FilePathGuard } from 'src/engine/core-modules/file/guards/file-path-guard';
|
||||||
import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module';
|
import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module';
|
||||||
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
||||||
@@ -9,7 +8,7 @@ import { FileController } from './controllers/file.controller';
|
|||||||
import { FileService } from './services/file.service';
|
import { FileService } from './services/file.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [FileUploadModule, JwtModule],
|
imports: [JwtModule],
|
||||||
providers: [FileService, EnvironmentService, FilePathGuard],
|
providers: [FileService, EnvironmentService, FilePathGuard],
|
||||||
exports: [FileService],
|
exports: [FileService],
|
||||||
controllers: [FileController],
|
controllers: [FileController],
|
||||||
|
|||||||
Reference in New Issue
Block a user