Fichiers & GED

Référence technique du domaine Fichiers & GED — upload S3, URL pré-signées, Object Lock Compliance, versioning, partages temporaires et OCR.

Fichiers & GED

Périmètre : upload/download S3 (Scaleway), URL pré-signées, classement multi-entités (FileLink), versioning, partages temporaires (FileShare), OCR optionnel et conformité légale (Object Lock Compliance 10 ans pour factures et contrats).

Les routes d'API vivent sous /api/workspace/files/* ; la bibliothèque globale est à /workspace/files. En développement le stockage utilise le driver filesystem (répertoire .uploads/) ; en production Scaleway Object Storage fr-par. Source : docs/13-domain-files.md.

Vocabulaire & entités

TermeEntitéDéfinition
FichierFileObjet stocké dans un bucket S3 ; métadonnées conservées en base.
BucketStorageBucketConteneur S3, un ou plusieurs par organisation selon le type de contenu.
LienFileLinkAssociation fichier ↔ entité cible (asset, contrat, facture, opportunité…).
PartageFileShareURL publique signée et expirable d'un fichier.
VersionFileVersionAncienne révision d'un fichier (versioning optionnel par bucket).

Le modèle complet est dans docs/04-data-model.md §10. Un FileLink est unique par (fileId, targetType, targetId, category).

Écrans

ÉcranRouteContenu
Bibliothèque fichiers/workspace/filesDataTable : nom, type MIME, taille, entité liée, uploadeur, date ; filtres, recherche FTS (si OCR).
Section Documents (inline)Fiches entitésFileLink par fiche : drag-drop, catégorisation, épinglage, aperçu inline.
Partage temporaire/share/[token]Page publique avec preview + download ; protection mot de passe optionnelle.

Architecture buckets

documents-default

Documents généraux (devis, photos…). Object Lock désactivé. Versioning activé.

invoices-archive

Factures émises (PDF + Factur-X). Object Lock Compliance 10 ans. Versioning activé.

contracts-archive

Contrats signés. Object Lock Compliance 10 ans. Versioning activé.

signatures-evidence

Preuves de signature (export Yousign). Object Lock Compliance 10 ans.

audit-archive

Export mensuel audit log. Object Lock Compliance 10 ans.

temp-uploads

Uploads transitoires avant validation finale. Pas d'Object Lock. TTL 24 h.

Garde-fou légal. Les buckets invoices-archive, contracts-archive, signatures-evidence et audit-archive sont configurés en mode Object Lock Compliance avec une rétention de 10 ans. Aucun objet ne peut être supprimé avant expiration, même par le compte root Scaleway. Cette contrainte est irréversible une fois posée sur le bucket.

Sécurité stockage

  • Chiffrement at-rest (Scaleway SSE-S3) et in-transit (HTTPS uniquement).
  • Aucun bucket accessible en accès public direct : tout passe par URL pré-signée (TTL 15 min par défaut).
  • Validation MIME type + extension à l'upload.
  • Taille max par défaut 50 MB (paramétrable par bucket ou type d'usage).
  • Scan antivirus asynchrone (ClamAV self-hosted ou Scaleway Cloud) sur les uploads suspects.
  • Quota de stockage par organisation : Organization.storageQuotaBytes.

Modèle de données (points clés)

  • File.status : PENDING (upload en cours) → READY (confirmé côté S3) → soft-deleted.
  • Un FileLink est créé dès le statut PENDING, mais les listings par cible ne montrent que les fichiers READY.
  • File.ocrText : texte extrait par OCR, indexé en FTS PostgreSQL pour la recherche dans le contenu.
  • FileShare.downloadCount : incrémenté à chaque téléchargement, downloadLimit optionnel.
  • La déduplication est scoped au bucket (les rétentions Object Lock diffèrent d'un bucket à l'autre ; pas de déduplication inter-buckets ni inter-organisations).

API

Toutes les routes exigent une session valide et la permission indiquée (RBAC).

MéthodeRoutePermission
POST/api/workspace/files/upload-urlfile:upload
POST/api/workspace/files/[id]/completefile:upload
GET/api/workspace/filesfile:read
GET/api/workspace/files/[id]file:read
GET/api/workspace/files/[id]/download-urlfile:read
POST/api/workspace/files/[id]/replacefile:upload
DELETE/api/workspace/files/[id]file:delete
POST/api/workspace/files/[id]/sharefile:share
DELETE/api/workspace/files/shares/[id]file:share
GET/share/[token]Public (vérification token + mot de passe)
POST/api/workspace/files/[id]/linkfile:upload
DELETE/api/workspace/files/links/[id]file:delete
POST/api/workspace/files/upload-urlAuth

Initialise un upload : valide la cible, contrôle le quota et le MIME, applique la déduplication SHA-256, génère une URL pré-signée S3 PUT (TTL 30 min) et crée le File en statut PENDING.

Corps (JSON)

filename
string required
Nom original du fichier (ex. facture-2026-01.pdf).
mimeType
string required
Type MIME validé contre la whitelist du bucket.
sizeBytes
number required
Taille en octets (≤ limite du bucket).
sha256
string
Empreinte SHA-256 du contenu (déclenche la déduplication si fournie).
bucket
string required
Identifiant du bucket cible (documents-default, invoices-archive, …).
targetType
string
Type de l'entité liée (asset, contract, invoice, …). Obligatoire avec targetId.
targetId
string
Identifiant de l'entité liée. Obligatoire avec targetType.
category
string
Catégorie fonctionnelle (photo-face, contract-pdf, delivery-receipt, …).

Requête

curl -s -X POST "$API/api/workspace/files/upload-url" \
  -H "Content-Type: application/json" -H "Cookie: $SESSION" \
  -d '{"filename":"bon-livraison.pdf","mimeType":"application/pdf","sizeBytes":184320,"bucket":"documents-default","targetType":"asset","targetId":"ast_123","category":"delivery-receipt"}'

Réponse

{
  "fileId": "fil_…",
  "presignedUrl": "https://s3.fr-par.scw.cloud/…?X-Amz-Signature=…",
  "expiresAt": "2026-06-16T10:30:00Z",
  "deduplicated": false
}

Workflows

Upload signé (flux nominal)

1. Client calcule SHA-256 du fichier
2. POST /upload-url → { presignedUrl, fileId } (File PENDING créé, FileLink posé)
3. Client PUT directement vers S3 (évite le transit serveur)
4. POST /[id]/complete { etag }
   → serveur vérifie présence S3, calcule checksum, File.status = READY
5. Job async : scan antivirus + OCR si éligible

Si sha256 fourni et doublon détecté dans le même bucket/org → réponse { deduplicated: true, fileId }, le client saute le PUT et le /complete.

Download via URL pré-signée

1. GET /[id]/download-url
   → serveur vérifie RBAC + appartenance à une entité accessible par le user
2. Retourne { url, expiresAt } (TTL 15 min)
3. Client suit l'URL → S3 directement
4. Accès logué (AccessLog si activé)

Proxy filesystem en développement

En environnement development, le driver filesystem remplace S3. Le serveur expose :

  • PUT /dev-upload → écrit dans .uploads/
  • GET /dev-download → sert le fichier local

Les URL pré-signées sont relatives (même origine) pour que le proxy fonctionne sans CORS. L'aperçu PDF inline nécessite X-Frame-Options: SAMEORIGIN (le middleware global pose DENY — penser à l'override sur le proxy).

Object Lock Compliance 10 ans (factures & contrats)

Les buckets invoices-archive, contracts-archive, signatures-evidence et audit-archive sont configurés une seule fois à la création du bucket avec la politique de rétention COMPLIANCE / 3 650 jours. Une fois un objet écrit :

  • Aucune API (y compris root) ne peut le supprimer avant la date d'expiration.
  • Le versioning est actif : chaque remplacement crée une nouvelle version ; l'ancienne reste protégée.
  • La suppression logique (File.deletedAt) n'a aucun effet sur l'objet S3 sous-jacent.

Versioning

Quand l'action "Remplacer" est déclenchée sur un fichier :

  • L'ancien fichier devient FileVersion (référence S3 conservée).
  • Le nouveau fichier prend la place dans le FileLink.
  • L'historique reste consultable dans la section Documents de la fiche.

Suppression

  • Logique : File.deletedAt = now(), liens FileLink détachés dans la même transaction.
  • Physique : job file-physical-delete (quotidien) purge les fichiers soft-deleted depuis > 30 j, sauf si le bucket est en Object Lock (suppression physique alors impossible).
  • L'UI affiche le nombre de fiches concernées avant confirmation (GET /files/[id]/links).

OCR (optionnel)

  • Déclenché sur upload PDF / image (sauf flag skipOcr) → job async file-ocr.
  • Texte extrait stocké dans File.ocrText, indexé en FTS PostgreSQL.
  • Provider : Mastra self-hosted ou Veryfi/Rossum pour les factures fournisseur.

Règles métier

  • Taille max upload : 50 MB par défaut, paramétrable par bucket ou type d'usage.
  • MIME whitelist par bucket (invoices-archive n'accepte que application/pdf).
  • Isolation multi-tenant stricte : un user ne voit que les fichiers de ses organisations.
  • FileLink unique par (fileId, targetType, targetId, category).
  • Quota de stockage par organisation : Organization.storageQuotaBytes.
  • Bucket Object Lock : suppression impossible avant expiration de la rétention.
  • Job file-cleanup-pending : purge les fichiers restés PENDING (upload avorté) et leurs liens.