karbe/src/lib/media.ts
Karbé Architect b9bfc5ee32 feat(espace-hote): CRUD carbet propriétaire + upload médias S3/MinIO
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>
2026-05-29 04:58:12 +00:00

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]}`;
}