Fixes an edge case when a user signs up with Google and the profile avatar network request times out, we crash instead of creating the user without an avatar. Added `axios-retry` to retry max 2 times and if it still fails we gracefully skip avatar image instead of crashing Fixes Sentry TWENTY-SERVER-FDQ Sonarly https://sonarly.com/issue/6564 --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
68 lines
1.7 KiB
TypeScript
68 lines
1.7 KiB
TypeScript
import { type AxiosInstance } from 'axios';
|
|
|
|
const cropRegex = /([w|h])([0-9]+)/;
|
|
|
|
export type ShortCropSize = `${'w' | 'h'}${number}` | 'original';
|
|
|
|
export interface CropSize {
|
|
type: 'width' | 'height';
|
|
value: number;
|
|
}
|
|
|
|
export const getCropSize = (value: ShortCropSize): CropSize | null => {
|
|
const match = value.match(cropRegex);
|
|
|
|
if (value === 'original' || match === null) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
type: match[1] === 'w' ? 'width' : 'height',
|
|
value: +match[2],
|
|
};
|
|
};
|
|
|
|
export const getImageBufferFromUrl = async (
|
|
url: string,
|
|
axiosInstance: AxiosInstance,
|
|
): Promise<Buffer> => {
|
|
if (!url || typeof url !== 'string' || url.trim().length === 0) {
|
|
throw new Error('Invalid URL provided: URL must be a non-empty string');
|
|
}
|
|
|
|
try {
|
|
const response = await axiosInstance.get(url, {
|
|
responseType: 'arraybuffer',
|
|
validateStatus: (status) => status >= 200 && status < 300,
|
|
maxRedirects: 5,
|
|
timeout: 10000,
|
|
});
|
|
|
|
if (!response.data) {
|
|
throw new Error('Received empty response from image URL');
|
|
}
|
|
|
|
const bufferLength = Buffer.isBuffer(response.data)
|
|
? response.data.length
|
|
: response.data.byteLength;
|
|
|
|
if (bufferLength === 0) {
|
|
throw new Error('Received empty response from image URL');
|
|
}
|
|
|
|
const contentType = response.headers['content-type'];
|
|
|
|
if (contentType && !contentType.startsWith('image/')) {
|
|
throw new Error(
|
|
`Invalid content type: expected image/*, got ${contentType}`,
|
|
);
|
|
}
|
|
|
|
return Buffer.from(response.data, 'binary');
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
|
|
throw new Error(`Failed to fetch image from ${url}: ${message}`);
|
|
}
|
|
};
|