Yayaw
Documentation

Médiathèque

Ressources média bornées par organisation, dossiers, quotas, miniatures et génération d'images IA.

Vue D'Ensemble

La médiathèque est le magasin partagé de ressources d'organisation pour les workflows de contenu. Elle est disponible depuis:

  • /dashboard/content/media, la médiathèque

  • /dashboard/content/media/trash, sa corbeille, pour les gestionnaires des médias

Les ressources média sont bornées par organisation. Elles sont utilisées par l'éditeur de page, Page AI et les workflows de contenu qui ont besoin d'URL durables pour images, vidéos, audio, PDF ou autres fichiers supportés.

Rôle CMS

Les médias sont la couche de ressources du CMS. Elle possède les fichiers binaires, miniatures générées, images générées, URL publiques, l'application des quotas et l'organisation en dossiers. Les données CMS structurées peuvent référencer des URL média, et les pages ou sections peuvent lier des champs média à des ressources sélectionnées, mais la ressource elle-même reste dans la médiathèque.

Utilisez les médias lorsqu'un workflow a besoin d'un fichier stocké. Utilisez les données CMS pour le contenu typé, les sections pour le layout réutilisable et les documents de page pour la composition.

Modèle De Données

Les médias utilisent cinq tables système:

  • media_folders

  • media_assets

  • media_asset_activity

  • media_tags

  • media_asset_tags

Les ressources stockent:

  • organization_id propriétaire

  • folder_id optionnel

  • nom d'affichage et nom d'affichage normalisé

  • type MIME et kind de ressource

  • taille en octets

  • bucket de stockage objet et clé de stockage

  • URL publique

  • métadonnées optionnelles de miniature

  • métadonnées de téléversement ou génération

La propriété des dossiers est bornée à la même organisation que la ressource. Les actions serveur doivent rejeter les mutations de dossiers ou ressources inter-organisations.

Les tags (migration 0093) appartiennent eux aussi à l'organisation : media_tags porte le nom de chaque tag, unique dans l'organisation sans tenir compte de la casse, des accents ni des espaces en trop, et sa couleur ; media_asset_tags porte une ligne par fichier et par tag. Un lien nomme son organisation et référence le fichier et le tag à travers elle : la base refuse de donner à un fichier le tag d'une autre organisation, et supprimer un fichier, un tag ou l'organisation supprime ses liens.

Stockage

Le stockage média utilise un fournisseur compatible S3 comme MinIO, AWS S3 ou R2. Les clés de stockage suivent la convention de chemin d'organisation:

organizations/<organizationId>/media/<asset>
organizations/<organizationId>/media/thumbnails/<assetId>.webp
global/site-variables/<asset>.<content-hash>.<ext>

La base stocke publicUrl parce que les pages publiées et le contenu rendu par le CMS ont besoin d'URL publiques stables. Les URL média existantes ne sont pas réécrites lorsque STORAGE_PROVIDER change; les opérateurs doivent donc garder les anciennes URL publiques joignables ou prévoir une migration ultérieure.

Fournisseur de stockage:

  • STORAGE_PROVIDER=s3

Le stockage compatible S3 requiert:

  • STORAGE_MEDIA_BUCKET

  • STORAGE_PUBLIC_BASE_URL

  • S3_ENDPOINT

  • S3_REGION

  • S3_ACCESS_KEY_ID

  • S3_SECRET_ACCESS_KEY

  • S3_FORCE_PATH_STYLE

La médiathèque écrit dans le bucket média configuré, et les uploads de logo d'organisation utilisent le bucket organization-logos. Lorsque l'URL publique de base est l'origine app ou CDN, les deux préfixes de bucket doivent être routés vers le stockage objet compatible S3 avant le repli vers l'app.

Le script seed téléverse les ressources par défaut de variables globales de site, comme le logo et l'image sociale, dans le bucket média configuré avant de publier l'entrée de données globales site-variables/main. Les URL de ressources personnalisées existantes sont préservées; seuls les anciens défauts seed ou les anciennes URL de stockage possédées par le seed sont réparés.

Voir Configuration de l'environnement de déploiement pour les étapes de setup fournisseur.

Types De Ressources Supportés

Le kind de ressource est dérivé du type MIME:

  • image

  • video

  • audio

  • pdf

  • document

  • archive

  • other

Les ressources visuelles peuvent recevoir des miniatures WebP. Les miniatures manquantes sont générées pour les fichiers de la page listée (jamais dans la corbeille) si la ressource est éligible et que le délai de nouvelle tentative le permet.

Permissions

Les opérations média sont bornées par organisation.

Accès typique:

  • les membres d'organisation peuvent lister/lire les ressources

  • les managers/admins/propriétaires peuvent téléverser, éditer, déplacer et supprimer des ressources

  • les superadmins peuvent opérer globalement lorsque l'autorisation sous-jacente le permet

Toutes les actions serveur doivent inclure le périmètre d'organisation active. La visibilité navigation ou UI ne remplace pas l'autorisation serveur.

Chaque action média, dans le tableau de bord comme via le MCP, est autorisée par can() sur la ressource media dans l'organisation, et par rien d'autre. Les accès ci-dessus viennent des rôles initiaux liés aux groupes de l'organisation : Organization Member liste et lit les médias, Organization Manager et Organization Admin les gèrent. Un rôle de membre Better Auth (owner, admin, manager ou member) ne donne jamais à lui seul accès aux médias : une personne n'atteint la bibliothèque que par un groupe dont le rôle porte une politique media.

Quotas

Les quotas de téléversement viennent des limites runtime du plan de facturation. Les défauts sont définis dans la configuration de facturation et peuvent être surchargés via les données de plan:

PlanTaille max de fichierStockage max d'organisation
Free25 MB1 GB
Pro100 MB10 GB
Business250 MB50 GB

Les contrôles de quota ont lieu avant la persistance. Les images générées par IA utilisent le même chemin de quota que les uploads manuels, donc la génération IA ne peut pas contourner les limites du plan.

UX Dashboard

La médiathèque est un écran du tableau de bord (dashboard.content.media, voir Tableau de bord). Son défaut affiche le résumé du stockage (le stockage du forfait face à ce que l'organisation utilise, corbeille comprise), puis la médiathèque en table pleine page avec trois modes d'affichage des mêmes fichiers : Galerie (par défaut), Tableau et Arborescence (voir plus bas).

  • Vues système. La table propose trois vues d'emblée, avant les vues que les membres enregistrent : Médiathèque (la Galerie, les plus récents d'abord : la vue de l'écran, au nom de sa table), Arborescence (dossiers et fichiers par nom) et Table (les plus récents d'abord). Elles sont nommées dans la langue du lecteur (Media library, File tree et Table en anglais) et ne peuvent être ni modifiées ni supprimées ; un membre en enregistre une copie pour la changer. Les vues Arborescence et Table viennent avec la source des médias, pas avec le document de l'écran : toute organisation les a, que son écran soit personnalisé ou non.

  • Dossiers. Les dossiers s'ouvrent, se créent, se renomment, se déplacent et se suppriment dans l'arborescence, et Galerie et Tableau filtrent sur un dossier, racine de la médiathèque comprise.

  • Tags et facettes. Les fichiers portent des tags du catalogue propre à l'organisation : la colonne Tags, les cartes de la Galerie (version 2 de l'écran), la fiche d'un fichier et le volet de détails de l'arborescence, et le panneau de facettes à côté de chaque vue, avec Tags et Dossiers. Voir Tags.

  • Recherche. Le serveur cherche dans toute la médiathèque, quel que soit le dossier : nom affiché, nom de fichier d'origine et type MIME. Filtres, tris et pages s'exécutent en base, les tris de texte dans sa collation.

  • Barre d'outils. Les gestionnaires des médias téléversent des fichiers, importent une image depuis une URL publique, génèrent une image quand la génération d'images est active et ouvrent la corbeille. Des fichiers peuvent aussi être déposés sur l'arborescence.

  • Modification. Le formulaire de modification change le nom, le dossier et les tags d'un fichier, ou le dossier d'une sélection. Les fichiers peuvent être dupliqués ou mis à la corbeille, et les aperçus et fiches gardent leur rendu adapté au kind.

  • Corbeille. La corbeille est une page à part, /dashboard/content/media/trash, ouverte aux gestionnaires des médias (media:manage) : sa vue Corbeille (la Galerie) et une vue système Table, les derniers fichiers supprimés d'abord avec le jour où chacun part définitivement, et la restauration.

  • Vues enregistrées. Les vues de la médiathèque sont enregistrées sous media-assets-v1:all et celles de la corbeille sous media-assets-v1:trash. Les vues enregistrées pour un dossier sont devenues des vues de toute la médiathèque filtrées sur ce dossier, à son nom (migration 0092).

Gardez l'interface dense et opérationnelle. La médiathèque est une bibliothèque de travail, pas une galerie marketing.

Tags

Les tags classent les fichiers au-delà des dossiers : un fichier est dans un seul dossier et porte autant de tags qu'il faut. Chaque organisation a son propre catalogue, pour sa médiathèque seulement, et tout gestionnaire des médias peut l'enrichir.

  • Taguer. Les gestionnaires des médias (media:manage) taguent un fichier dans la colonne Tags (double-clic sur la cellule : une liste qui cherche les tags sans tenir compte de la casse ni des accents et crée un tag à partir de la saisie), dans le formulaire de modification, ou toute une sélection avec Ajouter des tags et Retirer des tags dans la barre de sélection. Ajouter et retirer gardent les autres tags de chaque fichier, y compris ceux qu'une autre personne a ajoutés entre-temps. Les membres qui ne font que consulter les médias voient les tags.

  • Facettes. Le panneau de facettes de la médiathèque se trouve à gauche de chaque vue (en feuille sur téléphone ; le bouton de la barre d'outils l'affiche et le masque). Il liste les Tags et les Dossiers avec le nombre de fichiers de chacun, sous la recherche et les autres filtres de la vue. Un clic filtre sur les valeurs choisies : les fichiers qui ont l'un des tags choisis, dans l'un des dossiers choisis ; Aucune valeur liste les fichiers sans tag (pour les dossiers, la racine de la médiathèque). Le choix est un filtre ordinaire : dans l'URL, enregistré avec une vue et listé dans le menu des filtres, où un filtre sur les tags peut aussi exiger tous les tags choisis, ou aucun.

  • Gérer les tags. Le menu de la colonne Tags › Gérer les tags renomme un tag, lui donne une couleur de la palette (ou aucune : sa couleur automatique), le fusionne dans un autre tag (chaque fichier qui le portait porte l'autre à la place) ou le supprime (chaque fichier le perd), avec pour chaque tag le nombre de fichiers hors corbeille qui le portent. Fusionner et supprimer sont irréversibles. Réservé aux gestionnaires des médias.

  • Annuler. Une modification des tags de fichiers est inscrite dans l'Activité de chaque fichier, avec les tags avant et après. Son auteur l'annule depuis la fiche du fichier ou avec Ctrl/Cmd+Z, comme une suppression, tant qu'elle est la dernière modification des tags du fichier et que chaque tag qu'elle rétablit existe encore ; la modification d'une sélection s'annule d'un bloc.

  • Les fichiers ne changent pas. Taguer ne modifie aucun fichier : son updatedAt reste, et les fichiers d'une ancienne maquette de page, en lecture seule, peuvent être tagués. Un fichier garde ses tags dans la corbeille et les retrouve à la restauration ; la corbeille les affiche en lecture seule.

  • Limites. 500 tags par organisation, 64 caractères par nom, 500 fichiers et 50 tags par modification.

  • Serveur. Le service des tags des médias (src/lib/server/services/media/media-tags.ts) vérifie can() sur les médias dans l'organisation pour chaque lecture et chaque modification : media:list lit le catalogue, media:manage le modifie et tague les fichiers. Les modifications du catalogue se suivent une à une par organisation ; une fusion déplace les liens et supprime les tags fusionnés dans une seule transaction. Une modification des tags de fichiers verrouille les fichiers, refuse un tag qui n'existe plus et signale les fichiers hors de la médiathèque ou dans la corbeille. La colonne tags de system:media filtre sur l'un, tous ou aucun des tags et sur les fichiers sans tag, et compte chaque fichier une fois sous chacun de ses tags ; les agrégats des sources médias sont lus en direct, jamais mis en cache, si bien que les facettes et les nombres de fichiers par tag suivent chaque modification.

  • MCP. yayaw_media_tags_list lit le catalogue avec le nombre de fichiers de chaque tag ; yayaw_media_tags_apply ajoute, retire ou fixe les tags de fichiers (par identifiant ou par nom, createMissing pour créer les noms, un motif, une simulation par défaut) ; yayaw_media_list et yayaw_media_search renvoient les tags de chaque fichier et filtrent sur tagIds. Renommer, recolorer, fusionner et supprimer des tags restent dans le tableau de bord. Voir Plan de contrôle.

Intégration Page Builder

L'éditeur de page peut lier des champs média à des ressources existantes de l'organisation. Les bindings gardent le publicUrl original de la ressource afin que les pages publiées résolvent le même fichier que celui sélectionné dans le picker.

Page AI peut générer au plus une image pour un prompt qui demande un média ou implique fortement un visuel riche de landing page. L'image générée est persistée par le pipeline médiathèque puis liée à un champ de section générée compatible comme heroImage.

Les pages publiques globales peuvent utiliser des ressources générées depuis la médiathèque privée de l'organisation active parce que l'URL stockée est publique. Si aucune organisation active ou permission média n'est disponible, la génération d'image est ignorée avec un avertissement et le brouillon de page réussit quand même.

Pipeline De Miniatures

La génération de miniature:

  • télécharge la ressource visuelle source

  • convertit en WebP

  • téléverse dans le bucket media sous le chemin de miniatures

  • stocke l'URL et les métadonnées de stockage de miniature sur media_assets

  • enregistre les métadonnées d'échec en cas d'erreur de conversion ou upload

Les échecs de miniature ne doivent pas bloquer le listing ni le rendu de la ressource originale. Le cooldown de retry évite les tentatives coûteuses répétées sur la même source cassée.

Génération D'Images IA

La génération d'images est gardée par:

  • OPENAI_API_KEY

  • OPENAI_IMAGE_GENERATION_ENABLED

  • le réglage de site géré media-image-generation-enabled

  • permissions et quotas média

Les images générées utilisent le modèle configuré via OPENAI_IMAGE_MODEL et sont sauvegardées comme ressources média WebP.

Points D'Entrée Serveur

Fichiers clés:

  • src/lib/server/services/screens/defaults/dashboard-content-media.ts et dashboard-content-media-trash.ts (les écrans par défaut)

  • src/lib/server/services/screens/sources/system/media.ts et src/lib/server/services/media/media-catalog-query.ts (la médiathèque et la corbeille comme sources, en SQL)

  • src/blocks/dashboard/screens/sources/media-extensions.tsx (la table des médias, sa barre d'outils, ses écritures et ses fenêtres)

  • src/lib/server/actions/media/media-library-actions.ts

  • src/lib/server/actions/media/media-screen-actions.ts

  • src/lib/server/services/media/media-trash.ts (la corbeille, ses lots et la restauration)

  • src/lib/server/services/media/media-tags.ts et src/lib/server/actions/media/media-tag-actions.ts (les tags)

  • src/lib/shared/media-tags.ts (noms, couleurs et limites des tags)

  • src/lib/server/services/media/media-permissions.ts

  • src/lib/server/services/media/media-asset-storage.ts

  • src/lib/server/services/media/media-asset-thumbnails.ts

  • src/lib/server/services/media/media-image-asset-generation.ts

  • src/lib/server/services/media/media-quotas.ts

  • src/lib/server/services/media/media-folder-move.ts

  • src/lib/server/services/media/media-folder-create.ts

  • src/lib/server/services/media/media-folder-store.ts

  • src/lib/shared/media-folder-rules.ts

  • src/lib/shared/media-tree-ids.ts

Notes Opérationnelles

  • Ne stockez pas les binaires média dans Postgres.

  • N'utilisez pas d'URL privée pour les ressources de pages publiées sauf si le runtime implémente aussi le renouvellement d'URL signées.

  • Ne comptez pas les miniatures générées dans le quota d'upload utilisateur.

  • Ne laissez pas une organisation sélectionner, muter ou supprimer le dossier ou la ressource d'une autre organisation.

  • Gardez les identifiants S3 server-only.

Validation

Checks utiles après des changements média:

bun test src/lib/server/services/media/media-mappers.test.ts
bun test src/lib/server/services/media/media-quotas.test.ts
bun test src/lib/server/services/media/media-asset-storage.test.ts
bun test src/lib/server/services/media/media-asset-thumbnails.test.ts
bun test src/lib/server/services/media/media-folder-move.test.ts src/lib/server/services/media/media-folder-create.test.ts
bun test src/lib/server/services/media/media-permissions.test.ts
.github/scripts/with-throwaway-postgres.sh env MEDIA_FOLDERS_TEST_THROWAWAY=1 bun test src/lib/server/services/media/media-folder-store.postgres.test.ts
.github/scripts/with-throwaway-postgres.sh env MEDIA_TRASH_TEST_THROWAWAY=1 bun test src/lib/server/services/media/media-trash.postgres.test.ts
bun test src/lib/shared/media-tags.test.ts src/lib/server/services/media/media-tags.test.ts
bun test src/lib/server/actions/media/media-tag-actions.test.ts
.github/scripts/with-throwaway-postgres.sh env MEDIA_TAGS_TEST_THROWAWAY=1 bun test src/lib/db/migrations/media-tags.postgres.test.ts src/lib/server/services/media/media-tags.postgres.test.ts
bun test src/lib/server/services/screens/sources/system/media.test.ts src/lib/shared/media-tree-ids.test.ts
bun test src/lib/server/actions/media/media-library-actions.test.ts
bun test src/lib/server/actions/media/media-screen-actions.test.ts
bun test src/blocks/dashboard/screens/sources/media-source-actions.test.ts src/blocks/dashboard/screens/sources/media-source-config.test.ts
bun test src/blocks/dashboard/screens/sources/media-extensions.test.ts
.github/scripts/with-throwaway-postgres.sh env SCREENS_TEST_THROWAWAY=1 bun test src/lib/server/services/screens/sources/system/media.postgres.test.ts
bun run check
bunx tsc --noEmit

Corbeille restaurable et activité

Supprimer un média le place dans la Corbeille pendant 30 jours. Le fichier original et sa miniature restent stockés, conservent leurs URL publiques et comptent toujours dans le quota. Les listes actives et les nouvelles recherches de médias CMS excluent la corbeille. Les réservations de dossier et de nom restent en place jusqu’à la restauration ou la purge.

Les gestionnaires peuvent restaurer un fichier ou une sélection depuis la corbeille. Ctrl/Cmd+Z dans le tableau ou la galerie média actif annule la dernière suppression admissible de l’utilisateur courant en utilisant l’historique Activité de la fiche. Une suppression en lot partage un identifiant de transaction ; un raccourci restaure ses membres et, après un échec partiel, seuls les échecs restent à retenter. La restauration conserve l’historique : un événement inverse référence la suppression initiale.

Supprimer une sélection, c’est une seule requête et une seule transaction en base (trashMediaAssetsAction, 500 fichiers au plus ; une sélection plus grande part en plusieurs requêtes sous le même identifiant de transaction). Les fichiers qu’elle ne peut pas déplacer restent dans la médiathèque, et la table dit combien et pourquoi : fichiers qui ne sont plus dans la médiathèque, et fichiers qui appartiennent à une ancienne maquette de page, que la base garde en lecture seule depuis que le runtime exact des pages a pris le relais (les renommer, les déplacer ou les restaurer est refusé de la même façon). Les autres fichiers partent ensemble à la corbeille. Les dossiers gardent leur règle et sont supprimés un par un, seulement vides. Aucune action média ne refait le rendu de la page dans sa réponse : l’écran recharge lui-même ses données, et l’échec d’un rechargement des dossiers ou de l’historique ne remplace jamais la réponse du changement lui-même.

media_assets.deleted_at fixe l’expiration ; purge_started_at empêche la restauration pendant le nettoyage. media_asset_activity conserve l’acteur, les valeurs avant/après et les références d’annulation. Le worker page-AI existant contrôle régulièrement la corbeille et retire le fichier et sa miniature après 30 jours. Un échec du stockage conserve la ligne en base pour une nouvelle tentative. La restauration expire exactement 30 jours après la suppression, même si le worker n’a pas encore retiré le fichier. Appliquez la migration et lancez le worker lors du déploiement.

Le tableau de bord et MCP utilisent le même service avec contrôle des droits. yayaw_media_list liste les médias actifs par défaut et accepte trashed: true. yayaw_media_trash et yayaw_media_restore exigent orgId, un reason non vide, le droit d’écriture et media:manage dans l’organisation ; dryRun: true est la valeur par défaut. Ils prennent un fichier ou un lot :

  • Un fichier : assetId avec expectedUpdatedAt ; la restauration accepte aussi revertEventId. La version et l’organisation sont vérifiées dans une transaction verrouillée, et la réponse est { assetId, dryRun }.

  • Un lot : assetIds, de 1 à 500 identifiants distincts. Une liste vide, un identifiant répété, plus de 500 identifiants, ou assetId, expectedUpdatedAt ou revertEventId en plus sont refusés avant tout changement. Le lot passe par le service par lot du tableau de bord : une transaction, les lignes verrouillées dans l’ordre des identifiants, un savepoint par fichier, une entrée d’activité par fichier sous l’identifiant de transaction du lot. Une restauration annule le dernier passage de chaque fichier à la corbeille. Un fichier que le lot ne peut pas changer n’arrête jamais les autres.

Un lot répond results, un { assetId, status } par fichier dans l’ordre d’envoi : trashed ou restored (y compris un fichier déjà dans la corbeille, ou déjà sorti), refused avec un code et une raison (reason), ou not_found (hors de la médiathèque de l’organisation). Les codes sont read_only (un fichier d’une ancienne maquette de page), failed (réessayer) et, pour une restauration, not_restorable (ses 30 jours sont passés ou son retrait a commencé) et not_reversible (son passage à la corbeille ne peut plus être annulé). Viennent ensuite counts (requested, trashed ou restored, refused, notFound) et, une fois appliqué, transactionId : dans le tableau de bord, une seule annulation par la même personne restaure ensemble un lot mis à la corbeille. La simulation d’un lot fait les changements dans une transaction qu’elle annule : elle répond exactement ce que ferait l’appel, refus de la base compris.

Dupliquer des médias

Utilisez Dupliquer dans le menu ou Ctrl/Cmd+D sur les médias sélectionnés. Chaque copie reçoit un fichier indépendant et un nom unique dans le même dossier via le mécanisme d’import existant. Les copies comptent dans le quota de stockage. Les droits de lecture et de création sont vérifiés côté serveur, et les éléments de la corbeille ne peuvent pas être dupliqués. Un lot réussi sélectionne les copies et affiche un nombre traduit.

Vue Arborescence

Le tableau média (media-assets-v1) propose la vue Arborescence de YaYaw Table à côté de Galerie et Tableau. La galerie reste la vue par défaut ; le choix est enregistré par vue comme tout mode d’affichage. La corbeille ne garde que Galerie et Tableau, car un fichier supprimé n’a plus de dossier à parcourir.

  • Lignes. Galerie et Tableau ne listent que les fichiers. L’arborescence liste dossiers et fichiers ensemble : un dossier devient une ligne folder:<id> avec nodeKind: "folder", un fichier garde son identifiant avec nodeKind: "file", et tous deux portent parentId (folder:<id> de leur dossier, ou vide à la racine). L’arborescence demande ses lignes à system:media avec un scope, traité sur le serveur : children (les sous-dossiers d’un dossier, puis une page de ses fichiers), subtree (tous les dossiers et fichiers sous un dossier, les niveaux supérieurs d’abord) et tree-matches (les fichiers que la recherche ou les filtres retiennent et, pour une simple recherche, les dossiers qui portent ce nom, avec leurs dossiers dans ancestors). subtree et tree-matches s’arrêtent à 200 lignes, puis indiquent truncated.

  • Tailles et nombres. La taille d’un dossier est la somme de tous les fichiers qu’il contient, sous-dossiers compris (fichiers actifs seulement) ; chaque réponse fournit les childCounts et sizes de ses lignes de dossier.

  • Racine. L’arborescence part de la racine de la médiathèque : créer un dossier ou déposer des fichiers à la racine de l’arborescence vise la racine de la médiathèque.

  • Déplacements. Les fichiers se déplacent avec l’action existante (un nom déjà pris reçoit un suffixe unique). Les dossiers se déplacent avec moveMediaFolderAction, qui exige le droit média update et refuse, avec un code typé traduit dans l’interface : un déplacement dans le dossier lui-même ou l’un de ses sous-dossiers (cycle), au-delà de deux niveaux de dossiers pour tout le sous-arbre déplacé (depth_limit), à côté d’un dossier du même nom (name_conflict, y compris quand une modification simultanée heurte l’index unique), ou un dossier ou une cible hors de l’organisation active (not_found, target_not_found). Les dossiers de l’organisation sont verrouillés pendant la vérification : deux déplacements simultanés ne peuvent pas former un cycle ni un troisième niveau.

  • Autres actions. Nouveau dossier utilise l’action de création (deux niveaux au plus), qui exige le droit média create et prend le même verrou qu’un déplacement : le parent, la limite de profondeur et le nom sont vérifiés sur les dossiers verrouillés, si bien qu’un sous-dossier créé pendant le déplacement de son parent ne peut pas se retrouver au troisième niveau. Le renommage utilise l’action de renommage du dossier ou du fichier, et la suppression garde ses règles : les fichiers vont à la corbeille, un dossier n’est supprimé que s’il est vide. Les fichiers déposés depuis le bureau sur un dossier passent par le téléversement habituel, avec les limites de la fenêtre de téléversement (20 fichiers, la taille maximale du forfait) vérifiées d’abord ; droits, types et quotas sont vérifiés de nouveau sur le serveur.

  • Droits. Les membres qui ne gèrent pas les médias parcourent l’arborescence sans déplacer, renommer, créer ni déposer. L’interface ne fait que refléter les règles du serveur ; chaque action serveur les vérifie de nouveau.

  • MCP. yayaw_media_list n’a pas encore d’argument de dossier ; exposer les dossiers et leurs déplacements au MCP est une suite prévue. Les tags sont exposés (voir Tags).

Médias privés

Les médias privés sont les fichiers qu'une personne garde pour elle dans une organisation, comme un avatar ou une icône qu'elle a choisie. Ils ne font pas partie de la médiathèque ci-dessus : ils n'apparaissent jamais dans le dashboard, la vue arborescence, la recherche, les outils média MCP ni dans la liste de quelqu'un d'autre. Seul leur propriétaire les liste, les lit et les supprime, via /api/account/media, avec le jeton bearer que son application utilise déjà (jeton d'accès OAuth pour /api/mcp ou session d'appareil) et les origines CORS des clients publics.

  • Autorisation : can() sur la ressource private-media dans l'organisation, pour un membre de celle-ci. Les rôles Organization Member, Manager et Admin fournis par le seed, ainsi que Super Admin, détiennent private-media:manage ; Super Admin ne garde de médias privés que dans les organisations dont il est membre. Le service n'atteint ensuite que les lignes de l'appelant, donc un administrateur de l'organisation voit ses propres médias privés, pas ceux de ses membres.

  • Types : PNG, JPEG et WebP sont réencodés avec sharp ; le SVG est contrôlé (pas de DOCTYPE, de script, de contenu étranger ni de référence externe) puis rastérisé en PNG. Seules des images matricielles sont stockées et servies.

  • Limites : 5 Mio par fichier et 25 Mio par personne et par organisation par défaut (PRIVATE_MEDIA_MAX_FILE_BYTES, PRIVATE_MEDIA_MAX_USER_BYTES). Les médias privés comptent dans le quota de stockage du forfait de l'organisation, sous le même verrou que les téléversements de la médiathèque.

  • Stockage et diffusion : les objets sont dans un bucket privé (STORAGE_PRIVATE_MEDIA_BUCKET, ou le bucket de staging). Il n'y a ni URL publique ni URL signée : GET /api/account/media/{id}/content renvoie les octets au propriétaire authentifié avec Cache-Control: private, no-cache, un ETag et une Content-Security-Policy de bac à sable.

  • Nettoyage : quitter une organisation supprime les médias privés du membre dans celle-ci, et supprimer une organisation supprime d'abord les fichiers privés de ses membres ; le worker page AI supprime aussi les téléversements expirés, les comptes supprimés et les anciens membres.

Le contrat des routes pour les applications clientes (requêtes, réponses, erreurs) est dans docs/private-media.md.