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>
Serveur :
- POST /upload/init -> session { id, chunkSize (16 Mo), chunkCount }
- PUT /upload/:id/:index -> écrit le morceau à son offset (octet-stream brut,
bufferisé, parser dédié) ; renvoie { received, total }
- POST /upload/:id/complete -> vérifie tous les morceaux + la taille, renomme
le .mediadrop-part vers le chemin final ; 201 + métadonnées
- DELETE /upload/:id -> abandon, supprime le fichier partiel
- src/lib/upload-sessions.ts : registre en mémoire, purge après 30 min
d'inactivité, 100 sessions max
- src/lib/targets.ts : resolveTarget / resolvePlacement extraits et partagés
- writeChunkAt / createEmptyFile / finalizeFile / removeFile dans storage.ts
- limites, allowDirectories, jokers MIME et confinement dans la cible
s'appliquent aussi au chemin fragmenté
Page :
- bascule automatique en fragmenté au-delà de 32 Mo : 3 morceaux en parallèle,
3 réessais par morceau, progression et annulation agrégées
- makeReporter partagé (débit + ETA) ; job.cancel unifié (simple / fragmenté /
en file d'attente)
Tests : +tests/upload-chunked.test.ts (9), +writeChunkAt (storage) ; 73 tests.
Vérifié en live : 40 Mio -> 3 morceaux // -> octets exacts, DELETE nettoie.
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>