mediaDrop/src/errors.ts
Nicolas 246b078fec fix(server): destination des uploads fragmentés et gestion des erreurs
- init fragmenté : lit target/path dans la query (corps en repli) (#1)
- erreurs client interrompu / disque plein / fichier partiel absent mappées
  en 400/507/404 sans webhook d'échec pour les interruptions (#2)
- nettoyage des sessions expirées et des .part orphelins sécurisé,
  log des unhandledRejection (#2)
- plafond de sessions porté à 256 pour l'usage multi-fenêtres (#3)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 20:32:17 +02:00

79 lines
2.6 KiB
TypeScript

/**
* Hiérarchie d'erreurs de l'application.
*
* Toutes dérivent de `AppError`, qui porte un `statusCode` HTTP. Le gestionnaire
* d'erreurs Fastify (`src/error-handler.ts`) s'appuie dessus pour formater la réponse.
* Les messages ne doivent jamais contenir de donnée sensible (jeton, chemin absolu...).
*/
export abstract class AppError extends Error {
public readonly statusCode: number;
protected constructor(message: string, statusCode: number, options?: { cause?: unknown }) {
super(message, options);
this.name = this.constructor.name;
this.statusCode = statusCode;
}
}
/** Configuration invalide au démarrage (fichier JSON absent, illisible ou incohérent). */
export class ConfigError extends AppError {
public constructor(message: string, options?: { cause?: unknown }) {
super(message, 500, options);
}
}
/** Découverte OIDC impossible au démarrage (issuer injoignable ou réponse invalide). */
export class OidcDiscoveryError extends AppError {
public constructor(message: string, options?: { cause?: unknown }) {
super(message, 500, options);
}
}
/** Jeton d'accès absent, mal formé, expiré ou invalide. */
export class UnauthorizedError extends AppError {
public constructor(message = "Jeton d'accès absent ou invalide") {
super(message, 401);
}
}
/** Requête mal formée (fichier manquant, type non autorisé...). */
export class ValidationError extends AppError {
public constructor(message: string) {
super(message, 400);
}
}
/** Fichier au-dessus de la taille maximale autorisée. */
export class PayloadTooLargeError extends AppError {
public constructor(message = 'Le fichier dépasse la taille maximale autorisée') {
super(message, 413);
}
}
/** Session d'upload introuvable ou expirée. */
export class UploadSessionNotFoundError extends AppError {
public constructor(message = "Session d'upload inconnue ou expirée") {
super(message, 404);
}
}
/** Trop de sessions d'upload simultanées. */
export class TooManyUploadsError extends AppError {
public constructor(message = "Trop d'uploads en cours, réessayez plus tard") {
super(message, 429);
}
}
/** Le client a interrompu la requête (annulation, pause, coupure réseau) avant la fin du corps. */
export class ClientAbortedError extends AppError {
public constructor(message = 'Requête interrompue par le client') {
super(message, 400);
}
}
/** Plus d'espace disque disponible sur la cible de dépôt. */
export class InsufficientStorageError extends AppError {
public constructor(message = 'Espace de stockage insuffisant sur le serveur') {
super(message, 507);
}
}