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

@ -16,6 +16,7 @@ export {
} from './image/getLogoUrlFromDomainName';
export { capitalize } from './strings/capitalize';
export { absoluteUrlSchema } from './url/absoluteUrlSchema';
export { buildSignedPath } from './url/buildSignedPath';
export { getAbsoluteUrlOrThrow } from './url/getAbsoluteUrlOrThrow';
export { getUrlHostnameOrThrow } from './url/getUrlHostnameOrThrow';
export { isValidHostname } from './url/isValidHostname';

View File

@ -0,0 +1,69 @@
import { buildSignedPath } from '@/utils';
describe('buildSignedPath', () => {
it('should build a signed path', () => {
expect(
buildSignedPath({ path: 'folder/test.txt', token: 'tokenValue' }),
).toBe('folder/tokenValue/test.txt');
});
it('should build a signed path with original subFolder', () => {
expect(
buildSignedPath({
path: 'folder/original/test.txt',
token: 'tokenValue',
}),
).toBe('folder/original/tokenValue/test.txt');
});
it('should build a signed path with multiple dots filename', () => {
expect(
buildSignedPath({
path: 'folder/test.data.12032026.txt',
token: 'tokenValue',
}),
).toBe('folder/tokenValue/test.data.12032026.txt');
});
it('should throw when building signed path with missing filename', () => {
expect(() =>
buildSignedPath({
path: 'folder/',
token: 'tokenValue',
}),
).toThrow(
"Filename empty: cannot build signed path from folderPath 'folder/'",
);
});
it('should throw when building signed path with empty path', () => {
expect(() =>
buildSignedPath({
path: '',
token: 'tokenValue',
}),
).toThrow("Filename empty: cannot build signed path from folderPath ''");
});
it('should ignore absolute https urls', () => {
expect(
buildSignedPath({
path: 'https://twentyhq.github.io/placeholder-images/workspaces/twenty-logo.png',
token: 'tokenValue',
}),
).toBe(
'https://twentyhq.github.io/placeholder-images/workspaces/twenty-logo.png',
);
});
it('should ignore absolute http urls', () => {
expect(
buildSignedPath({
path: 'http://twentyhq.github.io/placeholder-images/workspaces/twenty-logo.png',
token: 'tokenValue',
}),
).toBe(
'http://twentyhq.github.io/placeholder-images/workspaces/twenty-logo.png',
);
});
});

View File

@ -0,0 +1,25 @@
import { isNonEmptyString } from '@sniptt/guards';
export const buildSignedPath = ({
path,
token,
}: {
path: string;
token: string;
}) => {
if (path.startsWith('https:') || path.startsWith('http:')) {
return path;
}
const directories = path.split('/');
const filename = directories.pop();
if (!isNonEmptyString(filename)) {
throw new Error(
`Filename empty: cannot build signed path from folderPath '${path}'`,
);
}
return `${directories.join('/')}/${token}/${filename}`;
};

View File

@ -3,3 +3,4 @@ export * from './getAbsoluteUrlOrThrow';
export * from './getUrlHostnameOrThrow';
export * from './isValidHostname';
export * from './isValidUrl';
export * from './buildSignedPath';