84 lines
2.2 KiB
TypeScript
84 lines
2.2 KiB
TypeScript
export type ImageStorageProvider = 'database' | 's3';
|
|
|
|
export type S3ImageStorageConfig = {
|
|
provider: 's3';
|
|
endpoint: string;
|
|
region: string;
|
|
bucket: string;
|
|
accessKeyId: string;
|
|
secretAccessKey: string;
|
|
publicBaseUrl: string | null;
|
|
keyPrefix: string;
|
|
};
|
|
|
|
const trimOptional = (value: string | undefined) => {
|
|
const trimmed = value?.trim();
|
|
return trimmed ? trimmed : null;
|
|
};
|
|
|
|
export const getImageStorageProvider = (): ImageStorageProvider =>
|
|
process.env.IMAGE_STORAGE_PROVIDER === 's3' ? 's3' : 'database';
|
|
|
|
export const getS3ImageStorageConfig = (): S3ImageStorageConfig | null => {
|
|
if (getImageStorageProvider() !== 's3') {
|
|
return null;
|
|
}
|
|
|
|
const endpoint = trimOptional(process.env.S3_ENDPOINT);
|
|
const region = trimOptional(process.env.S3_REGION);
|
|
const bucket = trimOptional(process.env.S3_BUCKET);
|
|
const accessKeyId = trimOptional(process.env.S3_ACCESS_KEY_ID);
|
|
const secretAccessKey = trimOptional(process.env.S3_SECRET_ACCESS_KEY);
|
|
|
|
if (!endpoint || !region || !bucket || !accessKeyId || !secretAccessKey) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
provider: 's3',
|
|
endpoint,
|
|
region,
|
|
bucket,
|
|
accessKeyId,
|
|
secretAccessKey,
|
|
publicBaseUrl: trimOptional(process.env.S3_PUBLIC_BASE_URL),
|
|
keyPrefix: trimOptional(process.env.S3_KEY_PREFIX) ?? 'bird-photos',
|
|
};
|
|
};
|
|
|
|
export const isS3ImageStorageConfigured = () => getS3ImageStorageConfig() !== null;
|
|
|
|
export const buildBirdPhotoObjectKey = ({
|
|
workspaceId,
|
|
birdId,
|
|
extension,
|
|
now = new Date(),
|
|
}: {
|
|
workspaceId: number;
|
|
birdId: string;
|
|
extension: string;
|
|
now?: Date;
|
|
}) => {
|
|
const prefix = trimOptional(process.env.S3_KEY_PREFIX) ?? 'bird-photos';
|
|
const safeExtension = extension.replace(/^\./, '').toLowerCase() || 'bin';
|
|
const timestamp = now.toISOString().replace(/[:.]/g, '-');
|
|
|
|
return `${prefix}/workspace-${workspaceId}/${birdId}/${timestamp}.${safeExtension}`;
|
|
};
|
|
|
|
export const getImageExtensionFromContentType = (contentType: string) => {
|
|
switch (contentType.toLowerCase()) {
|
|
case 'image/jpeg':
|
|
case 'image/jpg':
|
|
return 'jpg';
|
|
case 'image/png':
|
|
return 'png';
|
|
case 'image/webp':
|
|
return 'webp';
|
|
case 'image/gif':
|
|
return 'gif';
|
|
default:
|
|
return 'bin';
|
|
}
|
|
};
|