`targets[].allowedMimeTypes` : chaque cible peut définir sa propre
politique (mêmes formes : "all", types exacts, jokers "image/*"…).
Absente => la cible hérite du `allowedMimeTypes` global, qui devient
la valeur par défaut. Résolu au parsing : chaque `DropTarget` porte
toujours une politique.
- `POST /upload` et `POST /upload/init` valident contre
`target.allowedMimeTypes` (au lieu de la valeur globale).
- `GET /targets` renvoie `allowedMimeTypes` ; la page en déduit
l'attribut `accept` du sélecteur de fichiers (le serveur reste
l'autorité — le glisser-déposer n'est pas filtré).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Nouvelle clé `webhooks` : liste de points de terminaison POST notifiés à
chaque upload finalisé (`upload`, direct comme fragmenté) et à chaque
requête `/upload*` terminée en 4xx/5xx (`error`).
Chaque entrée : `url` (http/https), `events` (défaut : les deux),
`headers` (auth du récepteur), `secret` (signature HMAC-SHA256 dans
`X-MediaDrop-Signature`), `timeoutMs` (défaut 5 s, max 30 s).
Corps JSON structuré + champs `text`/`content` portant un résumé lisible,
pour fonctionner tel quel avec Slack / Discord / ntfy ou un récepteur
custom. Livraison en parallèle, sans bloquer la réponse d'upload ; une
nouvelle tentative sur erreur réseau / 429 / 5xx ; échecs journalisés en
`warn` ; l'arrêt du serveur attend les envois en cours. Sans webhook
configuré, le notifieur est une non-opération.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Remplace les 3 workers par fichier par un pool global de morceaux en vol,
réparti équitablement entre les fichiers fragmentés actifs : 1 fichier
utilise tout le pool, N fichiers en reçoivent une part chacun
(ceil(taille / N)), la capacité libérée par un fichier terminé est
redistribuée. Un fichier en cours d'init compte dans la part pour ne pas
être affamé ; les morceaux déjà en vol ne sont jamais interrompus.
La taille du pool est réglable via `chunkPoolSize` (config.json, défaut
30, borné à 256) et transmise au navigateur par un nouveau `GET /config`.
Corps des morceaux repassé en streaming (writeChunkAt sur un flux + garde
`maxBytes`) : mémoire ~constante même à pool élevé, plus de bufferisation
de 16 Mo par requête.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
isAllowedMedia gère désormais "video/*", "*/*" et "*" en plus des types
exacts (comparaison insensible à la casse). parseConfig valide la forme des
entrées et lève ConfigError sur un joker malformé ("video/**", "vi*deo/mp4",
"video/") au lieu de laisser passer une liste qui ne matche rien.
Schéma + doc mis à jour. 62 tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Dépôt de dossiers :
- flag `allowDirectories` (défaut false) par cible dans config.json + schéma.
- POST /upload accepte `?path=<chemin relatif>` : l'arborescence est recréée
sous `dir`, nom d'origine conservé, fichier écrasé si présent.
- src/lib/paths.ts : assainissement segment par segment (pas de `..`, pas de
séparateur) + vérification de confinement dans storage.ts -> aucune sortie
possible de la cible. Refus 400 si la cible n'autorise pas les dossiers.
- GET /targets expose `allowDirectories`.
- Page : glisser-déposer d'un dossier (parcours récursif webkitGetAsEntry) +
bouton « parcourir un dossier », visibles selon la cible.
Très gros fichiers (~10 Go) :
- `requestTimeout: 0` / `connectionTimeout: 0` : aucun timeout serveur imposé.
- écriture disque avec un tampon de 1 Mio.
- doc : `maxFileSizeMb` à l'échelle Go, réglages reverse proxy.
Tests : +paths.test.ts, +placement (storage), +dossier & gros fichier (upload) ;
60 tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>