11744 emails broken image in emails (#12265)

- refactor file tokens
- update file token management
  - generate one token per file per workspaceId
  - move token from query params to url path
This commit is contained in:
martmull
2025-05-26 22:05:21 +02:00
committed by GitHub
parent 69badf2a66
commit aa58259019
53 changed files with 775 additions and 386 deletions

View File

@ -1,9 +1,12 @@
import { buildSignedPath } from 'twenty-shared/utils';
import { QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
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';
import { extractFilenameFromPath } from 'src/engine/core-modules/file/utils/extract-file-id-from-path.utils';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type RichTextBlock = Record<string, any>;
@ -55,7 +58,7 @@ export class ActivityQueryResultGetterHandler
imageUrl.searchParams.delete('token');
const signedPayload = this.fileService.encodeFileToken({
noteBlockId: block.id,
filename: extractFilenameFromPath(imageProps.url),
workspaceId: workspaceId,
});
@ -63,7 +66,10 @@ export class ActivityQueryResultGetterHandler
...block,
props: {
...imageProps,
url: `${imageUrl.toString()}?token=${signedPayload}`,
url: buildSignedPath({
path: imageUrl.toString(),
token: signedPayload,
}),
},
};
}),

View File

@ -1,7 +1,10 @@
import { buildSignedPath } from 'twenty-shared/utils';
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 { AttachmentWorkspaceEntity } from 'src/modules/attachment/standard-objects/attachment.workspace-entity';
import { extractFilenameFromPath } from 'src/engine/core-modules/file/utils/extract-file-id-from-path.utils';
export class AttachmentQueryResultGetterHandler
implements QueryResultGetterHandlerInterface
@ -17,13 +20,20 @@ export class AttachmentQueryResultGetterHandler
}
const signedPayload = this.fileService.encodeFileToken({
attachmentId: attachment.id,
filename: extractFilenameFromPath(attachment.fullPath),
workspaceId: workspaceId,
});
const signedPath = buildSignedPath({
path: attachment.fullPath,
token: signedPayload,
});
const fullPath = `${process.env.SERVER_URL}/files/${signedPath}`;
return {
...attachment,
fullPath: `${process.env.SERVER_URL}/files/${attachment.fullPath}?token=${signedPayload}`,
fullPath,
};
}
}

View File

@ -1,7 +1,10 @@
import { buildSignedPath } from 'twenty-shared/utils';
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 { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
import { extractFilenameFromPath } from 'src/engine/core-modules/file/utils/extract-file-id-from-path.utils';
export class PersonQueryResultGetterHandler
implements QueryResultGetterHandlerInterface
@ -17,13 +20,16 @@ export class PersonQueryResultGetterHandler
}
const signedPayload = this.fileService.encodeFileToken({
personId: person.id,
workspaceId: workspaceId,
filename: extractFilenameFromPath(person.avatarUrl),
workspaceId,
});
return {
...person,
avatarUrl: `${person.avatarUrl}?token=${signedPayload}`,
avatarUrl: buildSignedPath({
path: person.avatarUrl,
token: signedPayload,
}),
};
}
}

View File

@ -1,7 +1,10 @@
import { buildSignedPath } from 'twenty-shared/utils';
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 { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { extractFilenameFromPath } from 'src/engine/core-modules/file/utils/extract-file-id-from-path.utils';
export class WorkspaceMemberQueryResultGetterHandler
implements QueryResultGetterHandlerInterface
@ -16,14 +19,17 @@ export class WorkspaceMemberQueryResultGetterHandler
return workspaceMember;
}
const signedPayload = await this.fileService.encodeFileToken({
workspaceMemberId: workspaceMember.id,
workspaceId: workspaceId,
const signedPayload = this.fileService.encodeFileToken({
filename: extractFilenameFromPath(workspaceMember.avatarUrl),
workspaceId,
});
return {
...workspaceMember,
avatarUrl: `${workspaceMember.avatarUrl}?token=${signedPayload}`,
avatarUrl: buildSignedPath({
path: workspaceMember.avatarUrl,
token: signedPayload,
}),
};
}
}