Skip to content

Depot et consultation de fichiers

Vue d'ensemble

Le depot de fichiers permet a un client externe d'envoyer tout type de fichier via l'interface web. Le fichier transite par le backend NestJS qui le valide, le transfere vers Scaleway Object Storage (S3), puis enregistre les metadonnees en base.

La page AdminFolderDetailPage expose la liste des fichiers deja deposes par l'utilisateur connecte et trié dans des dossiers. Cette liste est chargee via GET /documents, triee par date de depot decroissante, et affiche le nom, la date et la taille de chaque fichier.

Chaque fichier de la liste peut etre telecharge via GET /documents/:uuid/download. Le backend verifie l'appartenance du document a l'utilisateur connecte, puis genere une URL S3 pre-signee temporaire en attachment.

Le menu d'actions permet aussi de copier un lien applicatif securise (/files/:documentUuid). A l'ouverture de ce lien, le front appelle GET /documents/:uuid/access-url; le backend revalide les droits puis genere une URL S3 pre-signee temporaire en inline.

Ce choix est documente dans l'ADR-03.

Flux complet

mermaid
flowchart TD
    U["👤 Client externe"]
    F["🖥️ AdminFolderDetailPage"]
    API["🧠 DocumentController"]
    AUTH["🔐 AuthGuard"]
    SERVICE["⚙️ DocumentService"]
    S3["☁️ Scaleway Object Storage"]
    DB[("🗄️ prisma.tool<br/>documents / users_documents")]

    U --> F
    F -->|"POST /documents<br/>multipart/form-data"| API
    F -->|"GET /documents<br/>liste des depots"| API
    API --> AUTH --> SERVICE
    SERVICE -->|"upload binaire"| S3
    SERVICE -->|"metadonnees + liaison user"| DB
    DB -->|"liste triee created_at desc"| SERVICE
    SERVICE -->|"URL pre-signee attachment"| S3
    SERVICE --> API --> F

Etape par etape

1. Frontend — envoi du fichier

Fichiers : front/src/pages/admin/folders/AdminFolderDetailPage.vue, front/src/stores/files-store.js

  • L'utilisateur depose un fichier (drag & drop ou selecteur)
  • Validation client : taille uniquement (max 50 Mo par defaut)
  • Le store envoie le fichier en multipart/form-data via axios :
    js
    api.post('/documents', formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
      onUploadProgress: (e) => { /* progression */ }
    })
  • Le jeton applicatif est injecte automatiquement par l'intercepteur axios (X-App-Access-Token: ...)

1 bis. Frontend — consultation des fichiers deposes

Fichiers : front/src/pages/admin/folders/AdminFolderDetailPage.vue, front/src/components/documents/DocumentListItem.vue, front/src/stores/files-store.js

  • Au montage de /dossiers, la page appelle fileStore.fetchDocuments()
  • Le store effectue un GET /documents
  • La section Mes fichiers deposes affiche :
    • le nom du fichier
    • l'extension
    • la date de depot formatee
    • la taille formatee (o, Ko, Mo)
  • La vue admin d'un dossier affiche aussi le deposant du fichier quand l'information est disponible
  • La section est repliable/depliable et affiche un etat vide si aucun document n'est disponible
  • Apres un upload reussi, fetchDocuments() est relance pour rafraichir la liste
  • Chaque ligne expose une action Telecharger, qui appelle GET /documents/:uuid/download
  • Chaque ligne expose une action Copier le lien, qui copie une URL applicative /files/:documentUuid

2. Backend — authentification

Fichier : back/src/guards/authGuard.ts

L'AuthGuard verifie :

  1. X-App-Access-Token est present et valide
  2. Le jeton cible bien bdd sur l'environnement courant
  3. L'utilisateur est provisionne dans la base tool (prisma.tool.users)
  4. L'utilisateur a un role assigne

Si une de ces conditions echoue → 401 Unauthorized.

3. Backend — validation du fichier

Fichier : back/src/api/documents/document.service.ts

ValidationRegleErreur
Fichier presentLe champ files doit contenir un fichier400 Missing file
Type de fichierTout type accepte, y compris sans extension-
TailleMax FILE_SIZE_LIMIT Mo (defaut 50)413 File is too large

4. Backend — upload vers Scaleway S3

Fichier : back/src/domains/s3Helper/uploadFile.ts

Le fichier est envoye vers Scaleway Object Storage via le SDK AWS S3 (protocole compatible) :

  • Client S3 : configure avec les credentials Scaleway (UPLOAD_S3_ACCESS_KEY, UPLOAD_S3_SECRET_ACCESS_KEY)
  • Endpoint : Scaleway (UPLOAD_S3_ENDPOINT), pas AWS — doit etre le base endpoint (https://s3.fr-par.scw.cloud) sans nom de bucket
  • Bucket : defini par UPLOAD_S3_BUCKET
  • Chemin : enterprises/{enterpriseUuid}/{documentUuid}
  • ACL : private (pas d'acces public)

Exemple de chemin : enterprises/550e8400-e29b-41d4-a716-446655440000/f47ac10b-58cc-4372-a567-0e02b2c3d479

5. Backend — enregistrement en base

Fichier : back/src/api/documents/document.service.ts

Apres l'upload S3, les metadonnees sont enregistrees dans prisma.tool :

documents
├── id         ← ID technique interne, jamais expose au front
├── uuid       ← UUID v4 genere par le service
├── bucket     ← nom du bucket S3
├── path       ← chemin complet dans le bucket
├── name       ← nom original du fichier
├── size       ← taille du fichier en octets
├── created_at
└── updated_at

users_documents (table de liaison interne)
├── user_id    ← ID de l'utilisateur qui a depose
└── document_id ← ID du document cree

Le contenu binaire du fichier n'est jamais stocke en base — uniquement sur S3. Le front et les liens publics manipulent uniquement documents.uuid, obligatoire et unique ; l'ID numerique reste reserve aux relations internes. Le deposant expose par les API admin est derive de la liaison users_documents -> users et contient uniquement des informations simples (firstname, lastname, email).

5 bis. Backend — consultation des fichiers

Fichier : back/src/api/documents/document.service.ts

GET /documents retourne les documents lisibles par l'utilisateur connecte. Pour un utilisateur standard, la reponse ne contient pas le deposant afin de limiter l'exposition de donnees personnelles au strict necessaire.

ts
this.prisma.tool.documents.findMany({
  where: {
    users_documents: { some: { user_id: userId } },
  },
  select: {
    uuid: true,
    name: true,
    size: true,
    created_at: true,
    folder: {
      select: { uuid: true, name: true },
    },
  },
  orderBy: { created_at: 'desc' },
})

Le champ size est stocke en BigInt cote Prisma et converti en number avant la reponse HTTP. Pour les roles administrateur et super-admin, le backend peut enrichir la liste avec depositor. Pour les endpoints admin de dossiers, GET /folders/:uuid/documents retourne le deposant de chaque fichier.

5 ter. Backend — telechargement d'un fichier depose

Fichiers : back/src/api/documents/document.controller.ts, back/src/api/documents/document.service.ts, back/src/domains/s3Helper/getFileUrl.ts

GET /documents/:uuid/download retourne une URL pre-signee temporaire :

json
{
  "url": "https://signed-url-temporaire",
  "filename": "rapport-client.csv"
}

La verification d'appartenance est centralisee dans DocumentAccessPolicy :

ts
this.prisma.tool.documents.findFirst({
  where: {
    uuid: documentUuid,
    users_documents: { some: { user_id: userId } },
  },
})

Si le document n'existe pas ou n'appartient pas a l'utilisateur, le service renvoie une erreur File not found. pour ne pas exposer l'existence d'un fichier tiers. L'URL S3 est generee avec Content-Disposition: attachment afin de declencher le telechargement dans le format d'origine.

5 quater. Backend — acces via lien applicatif

Fichiers : front/src/pages/documents/DocumentAccessPage.vue, back/src/api/documents/document.service.ts

Le lien partageable pointe vers le front :

text
/files/:documentUuid

Cette route front est protegee par le guard de navigation existant. Une fois l'utilisateur authentifie, la page appelle :

http
GET /documents/:uuid/access-url

Le backend applique la meme verification d'appartenance que pour le telechargement. La difference est le mode de signature :

EndpointUsageContent-Disposition
GET /documents/:uuid/downloadTelechargement immediatattachment
GET /documents/:uuid/access-urlOuverture depuis un lien applicatifinline

Le lien copie ne contient jamais de token, de bucket S3, de chemin interne, ni d'ID numerique auto-incremental.

6. Lecture — URL pre-signee

Fichier : back/src/domains/s3Helper/getFileUrl.ts

Pour telecharger ou afficher un fichier, le backend genere une URL pre-signee (valable 1 heure) :

ts
const signedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 })

L'URL permet un acces temporaire sans exposer les credentials S3.

Variables d'environnement

VariableDescriptionExemple
UPLOAD_S3_REGIONRegion Scalewayfr-par
UPLOAD_S3_ENDPOINTEndpoint S3 Scaleway (base, sans bucket)https://s3.fr-par.scw.cloud
UPLOAD_S3_ACCESS_KEYAccess key Scaleway(secret)
UPLOAD_S3_SECRET_ACCESS_KEYSecret key Scaleway(secret)
UPLOAD_S3_BUCKETNom du bucketheriade-strapi
FILE_SIZE_LIMITTaille max en Mo (defaut 50)50

Fichiers concernes

FichierRole
front/src/pages/admin/folders/AdminFoldersPage.vueListe des dossiers admin, point d'entree du parcours de depot
front/src/pages/admin/folders/AdminFolderDetailPage.vueInterface de depot + liste des fichiers deposes pour un dossier
front/src/stores/files-store.jsStore Pinia — POST /documents, GET /documents, progression et chargement
front/src/components/upload/FileUploadListItem.vueComposant de liste avec statut/progression
front/src/components/documents/DocumentListItem.vueComposant d'affichage d'un document depose + menu d'actions
front/src/pages/documents/DocumentAccessPage.vuePage d'ouverture d'un lien securise /files/:documentUuid
back/src/api/documents/document.controller.tsEndpoints GET /documents, GET /documents/:uuid/download, GET /documents/:uuid/access-url et POST /documents
back/src/api/documents/document.service.tsValidation + upload S3 + insert BDD + lecture/telechargement/acces des documents utilisateur
back/src/api/documents/document-access-policy.service.tsRegles d'autorisation document centralisees
back/src/domains/s3Helper/uploadFile.tsClient S3 — envoi du fichier
back/src/domains/s3Helper/getFileUrl.tsClient S3 — generation d'URL pre-signee
back/src/exceptions/document.exceptions.tsExceptions metier (fichier manquant, extension, taille)
sql/tool_01_add_size_to_documents.sqlMigration SQL ajoutant documents.size
sql/tool_02_documents_uuid_not_null.sqlMigration SQL rendant documents.uuid obligatoire