Interface propriétaire sous /espace-hote/carbets : - Liste, création, édition et suppression de carbets (formulaire complet : présentation, localisation, accès pirogue, commodités). - Upload photos/vidéos vers S3/MinIO (route handler multipart), réordonnancement et suppression des médias, photo de couverture. - Statut de publication (brouillon / publié / archivé) avec garde « au moins un média avant publication ». Réutilise le schéma Prisma (SYS-2) et l'authentification NextAuth (SYS-3) : gating via requireRole([OWNER, ADMIN]) et contrôle de propriété sur chaque mutation. Stockage objet configurable par variables S3_* (compatible MinIO). Co-Authored-By: Paperclip <noreply@paperclip.ing>
54 lines
1.5 KiB
TypeScript
54 lines
1.5 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
|
|
import { MediaType } from "@/generated/prisma/enums";
|
|
|
|
export const MAX_PHOTO_BYTES = 10 * 1024 * 1024; // 10 Mo
|
|
export const MAX_VIDEO_BYTES = 200 * 1024 * 1024; // 200 Mo
|
|
|
|
const PHOTO_MIME: Record<string, string> = {
|
|
"image/jpeg": "jpg",
|
|
"image/png": "png",
|
|
"image/webp": "webp",
|
|
"image/avif": "avif",
|
|
};
|
|
|
|
const VIDEO_MIME: Record<string, string> = {
|
|
"video/mp4": "mp4",
|
|
"video/webm": "webm",
|
|
"video/quicktime": "mov",
|
|
};
|
|
|
|
export const ACCEPTED_MIME_TYPES = [
|
|
...Object.keys(PHOTO_MIME),
|
|
...Object.keys(VIDEO_MIME),
|
|
];
|
|
|
|
export function mediaTypeForMime(mime: string): MediaType | null {
|
|
if (mime in PHOTO_MIME) return MediaType.PHOTO;
|
|
if (mime in VIDEO_MIME) return MediaType.VIDEO;
|
|
return null;
|
|
}
|
|
|
|
export function extensionForMime(mime: string): string {
|
|
return PHOTO_MIME[mime] ?? VIDEO_MIME[mime] ?? "bin";
|
|
}
|
|
|
|
export function maxBytesForType(type: MediaType): number {
|
|
return type === MediaType.VIDEO ? MAX_VIDEO_BYTES : MAX_PHOTO_BYTES;
|
|
}
|
|
|
|
export function buildMediaKey(carbetId: string, mime: string): string {
|
|
return `carbets/${carbetId}/${randomUUID()}.${extensionForMime(mime)}`;
|
|
}
|
|
|
|
export function humanFileSize(bytes: number): string {
|
|
if (bytes < 1024) return `${bytes} o`;
|
|
const units = ["Ko", "Mo", "Go"];
|
|
let value = bytes / 1024;
|
|
let unitIndex = 0;
|
|
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
value /= 1024;
|
|
unitIndex += 1;
|
|
}
|
|
return `${value.toFixed(1)} ${units[unitIndex]}`;
|
|
}
|