Serveur TypeScript (Fastify 5) permettant de déposer des fichiers médias
dans des dossiers nommés, via une page web de glisser-déposer.
- Authentification OpenID Connect gérée côté serveur (Authorization Code +
PKCE, client confidentiel, openid-client) ; session via cookie chiffré
(@fastify/secure-session).
- Configuration dans un fichier JSON (schéma fourni) ; l'environnement ne
porte que le pointeur MEDIADROP_CONFIG.
- Cibles de dépôt nommées : liste { name, label, dir }, sélection par
?target=, première cible par défaut.
- Upload multipart en streaming : nom de fichier dérivé côté serveur (pas
de path traversal), liste blanche de types MIME, plafond de taille.
- TypeScript strict, ESLint + Prettier sans warning, 46 tests Vitest.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
58 lines
1.6 KiB
TypeScript
58 lines
1.6 KiB
TypeScript
/**
|
|
* Liste blanche des types de médias acceptés et correspondance type MIME -> extension.
|
|
*
|
|
* Note : le type MIME utilisé ici est celui *annoncé par le client*. Il n'est pas
|
|
* vérifié via les octets de signature du fichier (voir README, section durcissement).
|
|
*/
|
|
|
|
/** Types MIME autorisés par défaut lorsque `ALLOWED_MIME_TYPES` n'est pas défini. */
|
|
export const DEFAULT_ALLOWED_MIME: readonly string[] = [
|
|
'image/jpeg',
|
|
'image/png',
|
|
'image/gif',
|
|
'image/webp',
|
|
'image/avif',
|
|
'video/mp4',
|
|
'video/webm',
|
|
'video/quicktime',
|
|
'audio/mpeg',
|
|
'audio/ogg',
|
|
'audio/wav',
|
|
'audio/flac',
|
|
'audio/mp4',
|
|
];
|
|
|
|
const MIME_TO_EXTENSION: Readonly<Record<string, string>> = {
|
|
'image/jpeg': '.jpg',
|
|
'image/png': '.png',
|
|
'image/gif': '.gif',
|
|
'image/webp': '.webp',
|
|
'image/avif': '.avif',
|
|
'video/mp4': '.mp4',
|
|
'video/webm': '.webm',
|
|
'video/quicktime': '.mov',
|
|
'audio/mpeg': '.mp3',
|
|
'audio/ogg': '.ogg',
|
|
'audio/wav': '.wav',
|
|
'audio/flac': '.flac',
|
|
'audio/mp4': '.m4a',
|
|
};
|
|
|
|
/** Politique de filtrage : une liste blanche explicite, ou `'all'` pour tout accepter. */
|
|
export type MimePolicy = readonly string[] | 'all';
|
|
|
|
/** Indique si un type MIME est accepté selon la politique fournie. */
|
|
export function isAllowedMedia(mimeType: string, policy: MimePolicy): boolean {
|
|
if (policy === 'all') {
|
|
return true;
|
|
}
|
|
return policy.includes(mimeType);
|
|
}
|
|
|
|
/**
|
|
* Extension de fichier sûre pour un type MIME connu.
|
|
* Renvoie `null` si le type n'est pas répertorié (l'appelant choisit un repli).
|
|
*/
|
|
export function extensionForMime(mimeType: string): string | null {
|
|
return MIME_TO_EXTENSION[mimeType] ?? null;
|
|
}
|