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:
@ -1025,10 +1025,10 @@ export type Mutation = {
|
||||
updateWorkspace: Workspace;
|
||||
updateWorkspaceFeatureFlag: Scalars['Boolean']['output'];
|
||||
updateWorkspaceMemberRole: WorkspaceMember;
|
||||
uploadFile: Scalars['String']['output'];
|
||||
uploadImage: Scalars['String']['output'];
|
||||
uploadProfilePicture: Scalars['String']['output'];
|
||||
uploadWorkspaceLogo: Scalars['String']['output'];
|
||||
uploadFile: SignedFileDto;
|
||||
uploadImage: SignedFileDto;
|
||||
uploadProfilePicture: SignedFileDto;
|
||||
uploadWorkspaceLogo: SignedFileDto;
|
||||
upsertObjectPermissions: Array<ObjectPermission>;
|
||||
upsertSettingPermissions: Array<SettingPermission>;
|
||||
userLookupAdminPanel: UserLookup;
|
||||
@ -2175,6 +2175,12 @@ export type SignUpOutput = {
|
||||
workspace: WorkspaceUrlsAndId;
|
||||
};
|
||||
|
||||
export type SignedFileDto = {
|
||||
__typename?: 'SignedFileDTO';
|
||||
path: Scalars['String']['output'];
|
||||
token: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type StandardOverrides = {
|
||||
__typename?: 'StandardOverrides';
|
||||
description?: Maybe<Scalars['String']['output']>;
|
||||
|
||||
@ -941,10 +941,10 @@ export type Mutation = {
|
||||
updateWorkspace: Workspace;
|
||||
updateWorkspaceFeatureFlag: Scalars['Boolean'];
|
||||
updateWorkspaceMemberRole: WorkspaceMember;
|
||||
uploadFile: Scalars['String'];
|
||||
uploadImage: Scalars['String'];
|
||||
uploadProfilePicture: Scalars['String'];
|
||||
uploadWorkspaceLogo: Scalars['String'];
|
||||
uploadFile: SignedFileDto;
|
||||
uploadImage: SignedFileDto;
|
||||
uploadProfilePicture: SignedFileDto;
|
||||
uploadWorkspaceLogo: SignedFileDto;
|
||||
upsertObjectPermissions: Array<ObjectPermission>;
|
||||
upsertSettingPermissions: Array<SettingPermission>;
|
||||
userLookupAdminPanel: UserLookup;
|
||||
@ -1970,6 +1970,12 @@ export type SignUpOutput = {
|
||||
workspace: WorkspaceUrlsAndId;
|
||||
};
|
||||
|
||||
export type SignedFileDto = {
|
||||
__typename?: 'SignedFileDTO';
|
||||
path: Scalars['String'];
|
||||
token: Scalars['String'];
|
||||
};
|
||||
|
||||
export type StandardOverrides = {
|
||||
__typename?: 'StandardOverrides';
|
||||
description?: Maybe<Scalars['String']>;
|
||||
@ -2554,7 +2560,7 @@ export type UploadFileMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UploadFileMutation = { __typename?: 'Mutation', uploadFile: string };
|
||||
export type UploadFileMutation = { __typename?: 'Mutation', uploadFile: { __typename?: 'SignedFileDTO', path: string, token: string } };
|
||||
|
||||
export type UploadImageMutationVariables = Exact<{
|
||||
file: Scalars['Upload'];
|
||||
@ -2562,7 +2568,7 @@ export type UploadImageMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UploadImageMutation = { __typename?: 'Mutation', uploadImage: string };
|
||||
export type UploadImageMutation = { __typename?: 'Mutation', uploadImage: { __typename?: 'SignedFileDTO', path: string, token: string } };
|
||||
|
||||
export type AuthTokenFragmentFragment = { __typename?: 'AuthToken', token: string, expiresAt: string };
|
||||
|
||||
@ -2973,7 +2979,7 @@ export type UploadProfilePictureMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProfilePicture: string };
|
||||
export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProfilePicture: { __typename?: 'SignedFileDTO', path: string, token: string } };
|
||||
|
||||
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@ -3104,7 +3110,7 @@ export type UploadWorkspaceLogoMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UploadWorkspaceLogoMutation = { __typename?: 'Mutation', uploadWorkspaceLogo: string };
|
||||
export type UploadWorkspaceLogoMutation = { __typename?: 'Mutation', uploadWorkspaceLogo: { __typename?: 'SignedFileDTO', path: string, token: string } };
|
||||
|
||||
export type CheckCustomDomainValidRecordsMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@ -3570,7 +3576,10 @@ export type TrackAnalyticsMutationResult = Apollo.MutationResult<TrackAnalyticsM
|
||||
export type TrackAnalyticsMutationOptions = Apollo.BaseMutationOptions<TrackAnalyticsMutation, TrackAnalyticsMutationVariables>;
|
||||
export const UploadFileDocument = gql`
|
||||
mutation uploadFile($file: Upload!, $fileFolder: FileFolder) {
|
||||
uploadFile(file: $file, fileFolder: $fileFolder)
|
||||
uploadFile(file: $file, fileFolder: $fileFolder) {
|
||||
path
|
||||
token
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type UploadFileMutationFn = Apollo.MutationFunction<UploadFileMutation, UploadFileMutationVariables>;
|
||||
@ -3602,7 +3611,10 @@ export type UploadFileMutationResult = Apollo.MutationResult<UploadFileMutation>
|
||||
export type UploadFileMutationOptions = Apollo.BaseMutationOptions<UploadFileMutation, UploadFileMutationVariables>;
|
||||
export const UploadImageDocument = gql`
|
||||
mutation uploadImage($file: Upload!, $fileFolder: FileFolder) {
|
||||
uploadImage(file: $file, fileFolder: $fileFolder)
|
||||
uploadImage(file: $file, fileFolder: $fileFolder) {
|
||||
path
|
||||
token
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type UploadImageMutationFn = Apollo.MutationFunction<UploadImageMutation, UploadImageMutationVariables>;
|
||||
@ -5833,7 +5845,10 @@ export type DeleteUserAccountMutationResult = Apollo.MutationResult<DeleteUserAc
|
||||
export type DeleteUserAccountMutationOptions = Apollo.BaseMutationOptions<DeleteUserAccountMutation, DeleteUserAccountMutationVariables>;
|
||||
export const UploadProfilePictureDocument = gql`
|
||||
mutation UploadProfilePicture($file: Upload!) {
|
||||
uploadProfilePicture(file: $file)
|
||||
uploadProfilePicture(file: $file) {
|
||||
path
|
||||
token
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type UploadProfilePictureMutationFn = Apollo.MutationFunction<UploadProfilePictureMutation, UploadProfilePictureMutationVariables>;
|
||||
@ -6499,7 +6514,10 @@ export type UpdateWorkspaceMutationResult = Apollo.MutationResult<UpdateWorkspac
|
||||
export type UpdateWorkspaceMutationOptions = Apollo.BaseMutationOptions<UpdateWorkspaceMutation, UpdateWorkspaceMutationVariables>;
|
||||
export const UploadWorkspaceLogoDocument = gql`
|
||||
mutation UploadWorkspaceLogo($file: Upload!) {
|
||||
uploadWorkspaceLogo(file: $file)
|
||||
uploadWorkspaceLogo(file: $file) {
|
||||
path
|
||||
token
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type UploadWorkspaceLogoMutationFn = Apollo.MutationFunction<UploadWorkspaceLogoMutation, UploadWorkspaceLogoMutationVariables>;
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
import { computePathWithoutToken } from '../useUploadAttachmentFile';
|
||||
|
||||
describe('computePathWithoutToken', () => {
|
||||
it('should remove token from path', () => {
|
||||
const input = 'https://example.com/image.jpg?token=abc123';
|
||||
const expected = 'https://example.com/image.jpg';
|
||||
expect(computePathWithoutToken(input)).toBe(expected);
|
||||
});
|
||||
|
||||
it('should handle path without token', () => {
|
||||
const input = 'https://example.com/image.jpg?size=large';
|
||||
expect(computePathWithoutToken(input)).toBe(input);
|
||||
});
|
||||
});
|
||||
@ -7,13 +7,8 @@ import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getActivi
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { FileFolder, useUploadFileMutation } from '~/generated/graphql';
|
||||
|
||||
// Note: This is probably not the right way to do this.
|
||||
export const computePathWithoutToken = (attachmentPath: string): string => {
|
||||
return attachmentPath.replace(/\?token=[^&]*$/, '');
|
||||
};
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useUploadAttachmentFile = () => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
@ -36,12 +31,14 @@ export const useUploadAttachmentFile = () => {
|
||||
},
|
||||
});
|
||||
|
||||
const attachmentPath = result?.data?.uploadFile;
|
||||
const signedFile = result?.data?.uploadFile;
|
||||
|
||||
if (!isNonEmptyString(attachmentPath)) {
|
||||
if (!isDefined(signedFile)) {
|
||||
throw new Error("Couldn't upload the attachment.");
|
||||
}
|
||||
|
||||
const { path: attachmentPath } = signedFile;
|
||||
|
||||
const targetableObjectFieldIdName = getActivityTargetObjectFieldIdName({
|
||||
nameSingular: targetableObject.targetObjectNameSingular,
|
||||
});
|
||||
@ -49,7 +46,7 @@ export const useUploadAttachmentFile = () => {
|
||||
const attachmentToCreate = {
|
||||
authorId: currentWorkspaceMember?.id,
|
||||
name: file.name,
|
||||
fullPath: computePathWithoutToken(attachmentPath),
|
||||
fullPath: attachmentPath,
|
||||
type: getFileType(file.name),
|
||||
[targetableObjectFieldIdName]: targetableObject.id,
|
||||
createdAt: new Date().toISOString(),
|
||||
|
||||
@ -2,6 +2,9 @@ import { gql } from '@apollo/client';
|
||||
|
||||
export const UPLOAD_FILE = gql`
|
||||
mutation uploadFile($file: Upload!, $fileFolder: FileFolder) {
|
||||
uploadFile(file: $file, fileFolder: $fileFolder)
|
||||
uploadFile(file: $file, fileFolder: $fileFolder) {
|
||||
path
|
||||
token
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -2,6 +2,9 @@ import { gql } from '@apollo/client';
|
||||
|
||||
export const UPLOAD_IMAGE = gql`
|
||||
mutation uploadImage($file: Upload!, $fileFolder: FileFolder) {
|
||||
uploadImage(file: $file, fileFolder: $fileFolder)
|
||||
uploadImage(file: $file, fileFolder: $fileFolder) {
|
||||
path
|
||||
token
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -42,16 +42,16 @@ export const useRecordShowContainerActions = ({
|
||||
},
|
||||
});
|
||||
|
||||
const avatarUrl = result?.data?.uploadImage;
|
||||
const avatarSignedFile = result?.data?.uploadImage;
|
||||
|
||||
if (!avatarUrl || isUndefinedOrNull(updateOneRecord)) {
|
||||
if (!avatarSignedFile || isUndefinedOrNull(updateOneRecord)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await updateOneRecord({
|
||||
idToUpdate: objectRecordId,
|
||||
updateOneRecordInput: {
|
||||
avatarUrl,
|
||||
avatarUrl: avatarSignedFile.path,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -5,7 +5,7 @@ import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMembe
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { ImageInput } from '@/ui/input/components/ImageInput';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { buildSignedPath, isDefined } from 'twenty-shared/utils';
|
||||
import { useUploadProfilePictureMutation } from '~/generated/graphql';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
@ -51,22 +51,22 @@ export const ProfilePictureUploader = () => {
|
||||
setUploadController(null);
|
||||
setErrorMessage(null);
|
||||
|
||||
const avatarUrl = result?.data?.uploadProfilePicture.split('?')[0];
|
||||
const signedFile = result?.data?.uploadProfilePicture;
|
||||
|
||||
if (!avatarUrl) {
|
||||
if (!isDefined(signedFile)) {
|
||||
throw new Error('Avatar URL not found');
|
||||
}
|
||||
|
||||
await updateOneRecord({
|
||||
idToUpdate: currentWorkspaceMember?.id,
|
||||
updateOneRecordInput: {
|
||||
avatarUrl,
|
||||
avatarUrl: signedFile.path,
|
||||
},
|
||||
});
|
||||
|
||||
setCurrentWorkspaceMember({
|
||||
...currentWorkspaceMember,
|
||||
avatarUrl: result?.data?.uploadProfilePicture,
|
||||
avatarUrl: buildSignedPath(signedFile),
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
useUploadWorkspaceLogoMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
import { buildSignedPath } from 'twenty-shared/utils';
|
||||
|
||||
export const WorkspaceLogoUploader = () => {
|
||||
const [uploadLogo] = useUploadWorkspaceLogoMutation();
|
||||
@ -29,7 +30,7 @@ export const WorkspaceLogoUploader = () => {
|
||||
onCompleted: (data) => {
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
logo: data.uploadWorkspaceLogo,
|
||||
logo: buildSignedPath(data.uploadWorkspaceLogo),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -2,6 +2,9 @@ import { gql } from '@apollo/client';
|
||||
|
||||
export const UPLOAD_PROFILE_PICTURE = gql`
|
||||
mutation UploadProfilePicture($file: Upload!) {
|
||||
uploadProfilePicture(file: $file)
|
||||
uploadProfilePicture(file: $file) {
|
||||
path
|
||||
token
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -2,6 +2,9 @@ import { gql } from '@apollo/client';
|
||||
|
||||
export const UPLOAD_WORKSPACE_LOGO = gql`
|
||||
mutation UploadWorkspaceLogo($file: Upload!) {
|
||||
uploadWorkspaceLogo(file: $file)
|
||||
uploadWorkspaceLogo(file: $file) {
|
||||
path
|
||||
token
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user