105 lines
2.6 KiB
TypeScript
105 lines
2.6 KiB
TypeScript
'use server';
|
|
|
|
import { getAuthUser } from '@/lib/auth-utils';
|
|
|
|
const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024; // 5 MB
|
|
|
|
function detectSafeImageMimeType(buffer: Buffer): string | null {
|
|
if (buffer.length < 12) return null;
|
|
|
|
// JPEG: FF D8 FF
|
|
if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {
|
|
return 'image/jpeg';
|
|
}
|
|
|
|
// PNG: 89 50 4E 47 0D 0A 1A 0A
|
|
if (
|
|
buffer[0] === 0x89 &&
|
|
buffer[1] === 0x50 &&
|
|
buffer[2] === 0x4e &&
|
|
buffer[3] === 0x47 &&
|
|
buffer[4] === 0x0d &&
|
|
buffer[5] === 0x0a &&
|
|
buffer[6] === 0x1a &&
|
|
buffer[7] === 0x0a
|
|
) {
|
|
return 'image/png';
|
|
}
|
|
|
|
// GIF: GIF87a or GIF89a
|
|
if (
|
|
buffer[0] === 0x47 &&
|
|
buffer[1] === 0x49 &&
|
|
buffer[2] === 0x46 &&
|
|
buffer[3] === 0x38 &&
|
|
(buffer[4] === 0x37 || buffer[4] === 0x39) &&
|
|
buffer[5] === 0x61
|
|
) {
|
|
return 'image/gif';
|
|
}
|
|
|
|
// WebP: RIFF....WEBP
|
|
if (
|
|
buffer[0] === 0x52 &&
|
|
buffer[1] === 0x49 &&
|
|
buffer[2] === 0x46 &&
|
|
buffer[3] === 0x46 &&
|
|
buffer[8] === 0x57 &&
|
|
buffer[9] === 0x45 &&
|
|
buffer[10] === 0x42 &&
|
|
buffer[11] === 0x50
|
|
) {
|
|
return 'image/webp';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export async function uploadImage(formData: FormData) {
|
|
try {
|
|
const authUser = await getAuthUser();
|
|
if (!authUser) {
|
|
return { success: false, error: 'Authentification requise' };
|
|
}
|
|
|
|
const file = formData.get('file') as File;
|
|
if (!file) {
|
|
return { success: false, error: 'Aucun fichier fourni' };
|
|
}
|
|
|
|
if (file.size > MAX_UPLOAD_SIZE_BYTES) {
|
|
return { success: false, error: 'Le fichier dépasse la taille maximale autorisée (5 Mo)' };
|
|
}
|
|
|
|
// Convert file to buffer
|
|
const bytes = await file.arrayBuffer();
|
|
const buffer = Buffer.from(bytes);
|
|
|
|
if (buffer.length > MAX_UPLOAD_SIZE_BYTES) {
|
|
return { success: false, error: 'Le fichier dépasse la taille maximale autorisée (5 Mo)' };
|
|
}
|
|
|
|
// Derive MIME type strictly from binary magic bytes (rejects SVG/HTML/scripts)
|
|
const mimeType = detectSafeImageMimeType(buffer);
|
|
if (!mimeType) {
|
|
return {
|
|
success: false,
|
|
error: 'Format de fichier non autorisé (seuls JPG, PNG, WebP et GIF sont acceptés)',
|
|
};
|
|
}
|
|
|
|
// Convert to Base64
|
|
const base64 = buffer.toString('base64');
|
|
const dataUrl = `data:${mimeType};base64,${base64}`;
|
|
|
|
// Return the data URL directly (it will be saved in the database)
|
|
return {
|
|
success: true,
|
|
url: dataUrl,
|
|
};
|
|
} catch (error) {
|
|
console.error('Error during image conversion:', error);
|
|
return { success: false, error: "Erreur lors de la conversion de l'image" };
|
|
}
|
|
}
|