Aller au contenu principal

Stockage de fichiers d'extension

Le stockage de fichiers d'extension permet à une extension activée de télécharger des fichiers image appartenant à une session d'extension de verrouillage.

Utilisez-le lorsque le JSON d'état d'extension ne suffit pas, par exemple :

  • images de puzzle
  • aperçus générés
  • photos de défi
  • médias spécifiques à l'extension qui doivent être nettoyés avec le verrouillage/la session

Ce stockage est distinct de state.*. Utilisez state.* pour les petites données JSON. Le stockage de fichiers est réservé aux données binaires.

Relation avec les points d'extrémité de l'état

L'état d'extension est destiné aux petits objets JSON à portée de session. Depuis une iframe, utilisez la commande de lecture du pont au lieu d'appeler directement l'API REST :

state.get

Cette commande de pont est acheminée vers :

GET /api/extensions/sessions/:sessionId/state

Les appels directs au backend pour écrire l'état utilisent le modèle d'authentification de l'API d'extension installée :

Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH

Ne transmettez pas les clés API développeur au code iframe/navigateur.

Utilisez un état écrit côté serveur pour les références durables et les données d'interface utilisateur, par exemple :

{
"puzzleImageFileId": "file_record_id",
"selectedImageIds": ["file_record_id"],
"lastOpenedTab": "images"
}

Ne stockez pas de données binaires, d'images base64, d'URL blob: du navigateur ni de copies persistantes de signedUrl dans l'état. Les URL signées expirent et doivent être actualisées avec files.get lors du rendu de l'image. La taille des écritures dans l'état est limitée par le paramètre stateMaxBytes de l'application d'extension, avec une valeur par défaut de 64 Kio.

Modèle de stockage

Chastify stocke les fichiers d'extension dans le stockage R2 géré par Chastify.

Chaque fichier téléchargé est suivi grâce à :

  • scope: "extension"
  • appId
  • sessionId et lockId pour les fichiers d'exécution/de session
  • templateId pour les fichiers revendiqués par un modèle de verrouillage enregistré
  • staged, draftId et expiresAt correspondent aux fichiers de configuration qui n'ont pas encore été réclamés.
  • extensionKey
  • purpose

Portail d'administration et quotas

Configurer les points de terminaison de préproduction

N’utilisez la phase de préparation que lorsqu’une interface utilisateur de configuration s’exécute avant l’existence d’une session d’extension.

Il s'agit d'un flux de chargement temporaire. Il est destiné aux écrans de configuration où l'utilisateur peut charger un fichier, puis fermer la fenêtre modale ou désactiver l'extension avant de créer un verrou/une session. Un fichier mis en attente n'est pas conservé tant qu'il n'est pas enregistré dans la configuration de l'extension qui y fait référence.

Les interfaces de configuration peuvent vérifier la disponibilité et l'utilisation actuelle avant d'afficher les commandes de téléchargement :

GET /api/extensions/apps/:appId/files/capabilities

La réponse inclut stagedQuota pour les fichiers intermédiaires non expirés de l'utilisateur actuel sur cette application d'extension :

{
"enabled": true,
"provider": "r2",
"r2Configured": true,
"supportsStagedSetupFiles": true,
"stagedQuota": {
"bytesUsed": 123456,
"fileCount": 1,
"maxBytes": 10485760,
"remainingBytes": 10362304
}
}
POST /api/extensions/apps/:appId/files/stage
Content-Type: multipart/form-data

Champs de formulaire :

  • file : fichier image requis
  • purpose : identifiant court facultatif tel que jigsaw-config-image
  • draftId : identifiant de brouillon de configuration facultatif ; réutilisez la même valeur tant qu’une fenêtre modale de configuration est ouverte.

La réponse contient un code file.id stable et une URL signée éphémère pour un aperçu immédiat.

Exemple de réponse :

{
"file": {
"id": "file_record_id",
"signedUrl": "https://chastify.<account-id>.r2.cloudflarestorage.com/extensions/abc.webp?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=...",
"publicUrl": "https://chastify.<account-id>.r2.cloudflarestorage.com/extensions/abc.webp?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=...",
"urlExpiresAt": "2026-05-28T12:10:00.000Z",
"sizeBytes": 123456,
"originalName": "puzzle.jpg",
"mimeType": "image/webp",
"purpose": "jigsaw-config-image",
"uploadedAt": "2026-05-28T12:00:00.000Z"
},
"stagedQuota": {
"bytesUsed": 123456,
"fileCount": 1,
"maxBytes": 10485760,
"remainingBytes": 10362304
}
}

Pour restaurer les chargements de configuration après une actualisation de la page, listez les fichiers préparés pour l'utilisateur actuel et l'application d'extension :

GET /api/extensions/apps/:appId/files/staged?purpose=jigsaw-config-image

Paramètres de requête facultatifs :

  • purpose : ne renvoyer que les fichiers préparés pour un cas d’utilisation de configuration
  • draftId : ne renvoyer que les fichiers d'une configuration brouillon connue

Si draftId est omis, Chastify renvoie les fichiers temporaires non expirés de l'utilisateur actuel pour cette application. Ce comportement est intentionnel pour les interfaces de configuration : un utilisateur peut charger des fichiers, recharger la page ou changer d'appareil, et l'écran de configuration peut restaurer ces chargements temporaires avant l'enregistrement du verrouillage/modèle.

La réponse de la liste des sessions intermédiaires inclut également stagedQuota, qui indique l'utilisation intermédiaire non expirée de l'utilisateur actuel pour cette application :

{
"items": [],
"stagedQuota": {
"bytesUsed": 123456,
"fileCount": 1,
"maxBytes": 10485760,
"remainingBytes": 10362304
}
}

Les interfaces de configuration peuvent également actualiser ou supprimer un fichier intermédiaire :

GET /api/extensions/apps/:appId/files/:fileId
DELETE /api/extensions/apps/:appId/files/staged/:fileId

Réclamation ultérieure des dossiers mis en scène

Il n'existe pas de point de terminaison « revendication » distinct côté navigateur. Un fichier préparé est revendiqué automatiquement lorsque Chastify enregistre une configuration de verrouillage ou d'extension de modèle qui référence ce fichier.

Ces routes permettent de récupérer les fichiers intermédiaires une fois que le document de verrouillage/modèle possède un identifiant, puis d'enregistrer à nouveau la configuration de l'extension avec les références aux fichiers récupérés. En cas d'échec de la récupération, le document de verrouillage/modèle nouvellement créé est annulé afin que les fichiers intermédiaires ne restent pas associés à une configuration corrompue.

Note concernant les modèles partagés : les verrous partagés acceptés conservent l’identifiant du modèle source sur le verrou actif cloné. Les fichiers associés au modèle sont donc conservés tant qu’un verrou cloné référence ce modèle. Si le modèle source est supprimé, Chastify retarde la suppression de ses fichiers d’extension jusqu’à ce que le dernier verrou cloné référençant le modèle supprimé soit supprimé ou archivé.

Pour qu'un fichier préparé puisse être revendiqué, stockez une référence comme celle-ci dans la configuration de l'extension soumise par l'interface utilisateur d'installation :

{
"storageDraftId": "draft_123",
"images": [
{
"id": "file_record_id",
"provider": "chastify_storage",
"fileId": "file_record_id",
"url": "extension-file:file_record_id",
"title": "Puzzle image"
}
]
}

Lors de l'enregistrement, Chastify analyse la configuration à la recherche d'enregistrements provider: "chastify_storage" avec un fileId valide, puis :

  • vérifie que le fichier appartient à l'utilisateur actuel et à l'application d'extension
  • rejette les fichiers intermédiaires expirés
  • rejette les fichiers déjà revendiqués par un autre contexte de verrouillage/modèle
  • marques référencées fichiers mis en scène comme revendiqué
  • associe les fichiers revendiqués au verrou ou au modèle enregistré
  • Supprime les champs temporaires signedUrl, publicUrl et urlExpiresAt avant l'enregistrement de la configuration.
  • supprime les fichiers intermédiaires non référencés du même storageDraftId

Les fichiers intermédiaires abandonnés qui ne sont jamais enregistrés sont supprimés par le processus de nettoyage après leur expiration.

Pour actualiser l'aperçu de configuration d'un fichier dont l'identifiant appartient à l'utilisateur actuel et à l'application d'extension :

GET /api/extensions/apps/:appId/files/:fileId

Points de terminaison de session

Ces points de terminaison nécessitent l'autorisation et les étendues de session d'extension normales.

Chemin de base :

/api/extensions/sessions/:sessionId/files

Capacités

Vérifiez ceci avant de générer les commandes de téléchargement.

GET /api/extensions/sessions/:sessionId/files/capabilities

Nécessite locks:read.

Exemple de réponse :

{
"enabled": true,
"provider": "r2",
"r2Configured": true,
"settings": {
"enabled": true,
"maxFileBytes": 10485760,
"maxBytesPerApp": 524288000,
"maxBytesPerSession": 52428800,
"maxStagedBytesPerUserPerApp": 10485760,
"allowedMimePrefixes": ["image/"]
}
}

Liste des fichiers

GET /api/extensions/sessions/:sessionId/files

Nécessite locks:read.

Exemple de réponse :

{
"items": [
{
"id": "file_record_id",
"signedUrl": "https://chastify.<account-id>.r2.cloudflarestorage.com/extensions/abc.webp?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=...",
"publicUrl": "https://chastify.<account-id>.r2.cloudflarestorage.com/extensions/abc.webp?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=...",
"urlExpiresAt": "2026-05-28T12:10:00.000Z",
"sizeBytes": 123456,
"originalName": "puzzle.jpg",
"mimeType": "image/webp",
"purpose": "puzzle-image",
"uploadedAt": "2026-05-28T12:00:00.000Z"
}
]
}

Obtenir un fichier

Utilisez cette option lorsque votre extension possède déjà un identifiant de fichier enregistré et qu'elle a uniquement besoin d'un lien R2 signé et récent.

GET /api/extensions/sessions/:sessionId/files/:fileId

Nécessite locks:read.

Le fichier doit appartenir à la même application d'extension, à la même session et au même verrou.

Exemple de réponse :

{
"file": {
"id": "file_record_id",
"signedUrl": "https://chastify.<account-id>.r2.cloudflarestorage.com/extensions/abc.webp?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=...",
"publicUrl": "https://chastify.<account-id>.r2.cloudflarestorage.com/extensions/abc.webp?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=...",
"urlExpiresAt": "2026-05-28T12:10:00.000Z",
"sizeBytes": 123456,
"originalName": "puzzle.jpg",
"mimeType": "image/webp",
"purpose": "puzzle-image",
"uploadedAt": "2026-05-28T12:00:00.000Z"
}
}

Téléverser un fichier

POST /api/extensions/sessions/:sessionId/files
Content-Type: multipart/form-data

Nécessite locks:write.

Champs de formulaire :

  • file : fichier image requis
  • purpose : identifiant court facultatif tel que puzzle-image ou preview

Exemple:

curl "https://chastify.net/api/extensions/sessions/SESSION_ID/files" \
-X POST \
-F "purpose=puzzle-image" \

Exemple de réponse :

{
"file": {
"id": "file_record_id",
"signedUrl": "https://chastify.<account-id>.r2.cloudflarestorage.com/extensions/abc.webp?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=...",
"publicUrl": "https://chastify.<account-id>.r2.cloudflarestorage.com/extensions/abc.webp?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=...",
"urlExpiresAt": "2026-05-28T12:10:00.000Z",
"sizeBytes": 123456,
"originalName": "puzzle.jpg",
"mimeType": "image/webp",
"purpose": "puzzle-image",
"uploadedAt": "2026-05-28T12:00:00.000Z"
}
}

Erreurs de chargement courantes :

  • extension_file_storage_disabled
  • extension_file_storage_requires_r2
  • file_too_large
  • invalid_file_type
  • extension_app_storage_quota_exceeded
  • extension_session_storage_quota_exceeded
  • extension_staged_user_app_storage_quota_exceeded

Supprimer le fichier

DELETE /api/extensions/sessions/:sessionId/files/:fileId

Nécessite locks:write.

Le fichier doit appartenir à la même application d'extension, à la même session et au même verrou.

Actions du pont Iframe

Les extensions iframe peuvent utiliser le pont web parent pour la lecture de fichiers en cours d'exécution. Le chargement et la suppression de fichiers en cours d'exécution sont des modifications de session et doivent être effectués par votre serveur avec une clé API développeur au niveau de l'application, suivie de x-chastify-main-token.

const capabilities = await bridge.request("files.capabilities", {});

const refreshed = await bridge.request("files.get", {
fileId: "file_record_id"
});

image.src = refreshed.file.signedUrl;

Actions de pont prises en charge :

  • files.capabilities -> Vérifier si le stockage de fichiers est activé et quels quotas s'appliquent.
  • files.list -> liste des fichiers appartenant à la session d'extension actuelle
  • files.get -> actualiser une URL R2 signée à partir d'un identifiant de fichier stocké

Les iframes de configuration peuvent utiliser des noms de pont supplémentaires avant l'établissement d'une session d'exécution. En mode configuration, files.upload crée des fichiers intermédiaires, files.list/files.staged.list liste les fichiers intermédiaires non expirés pour l'utilisateur et l'application en cours, et files.delete supprime un fichier intermédiaire. Le contexte d'initialisation de la configuration inclut storageDraftId ; incluez cette valeur dans votre configuration enregistrée sous la forme storageDraftId si vous souhaitez que Chastify supprime immédiatement les fichiers non référencés du même brouillon lors de l'enregistrement.

Le programme d'installation files.upload accepte également dataUrl pour les clients iframe simples :

await bridge.request("files.upload", {
dataUrl: canvas.toDataURL("image/webp", 0.9),
filename: "preview.webp",
purpose: "preview"
}, 60000);

Privilégiez les formats File ou Blob pour les chargements, lorsque cela est possible. Utilisez dataUrl uniquement pour les petites images générées, car les données base64 sont plus volumineuses en mémoire.

L'identifiant du fichier stable est id.

Utilisez signedUrl pour afficher ou télécharger le fichier. Les liens signés sont des URL R2 GetObject éphémères générées par Chastify. publicUrl est conservé comme alias de compatibilité et contient actuellement la même URL signée.

Lorsqu'un lien signé expire, appelez GET /api/extensions/sessions/:sessionId/files/:fileId pour recevoir un nouveau lien pour un fichier stocké, ou GET /api/extensions/sessions/:sessionId/files pour actualiser tous les liens de fichiers de session.

Cycle de vie du nettoyage

Les fichiers d'extension sont nettoyés via une file d'attente de nettoyage gérée par BullMQ.

Le nettoyage est prévu à la date suivante :

  • Une application d'extension a été supprimée.
  • Les documents de verrouillage/d'extension de session sont supprimés lors de la suppression du verrouillage/du nettoyage des archives.
  • un fichier est explicitement supprimé via le point de terminaison de fichier de session

La file d'attente permet d'éviter les opérations coûteuses de suppression R2 et de nettoyage de la base de données lors du traitement de la requête. En cas d'échec de l'ajout à la file d'attente, le serveur effectue un nettoyage en cours de traitement après la réponse lorsqu'un contexte de requête existe, ou un nettoyage direct au mieux lors du nettoyage du cycle de vie de niveau inférieur.

Notes de performance

  • Les vérifications de compatibilité doivent être effectuées avant l'affichage de l'interface de téléchargement.
  • Les chargements sont limités par fichier, par session et par application.
  • Les contrôles de quotas utilisent les métadonnées indexées UserFile pour scope + appId, scope + sessionId et scope + lockId.
  • Nettoyer les flux correspondant aux enregistrements de fichiers au lieu de charger toutes les URL en mémoire.
  • Les images raster téléchargées sont traitées et stockées sous forme d'images web optimisées.

Notes de sécurité

  • Ne considérez pas les URL d'images fournies par l'extension comme une preuve de complétion fiable.
  • Ne conservez que les enregistrements de fichiers émis par Chastify comme fichiers d'extension de confiance.
  • Lier les enregistrements de fichiers à appId, sessionId et lockId.
  • Appliquer le code locks:write pour les chargements et les suppressions.
  • Valider le type MIME et rejeter les SVG.
  • Activez les quotas contrôlés par l'administrateur avant d'autoriser une utilisation publique généralisée.
  • Utilisez la validation côté serveur pour tout flux de travail dépendant de fichiers téléchargés.

Routes directes ou ponts

Utilisez le pont iframe lorsque votre extension s'exécute dans Chastify. Cela permet de conserver l'authentification Chastify sur la page parente et d'éviter d'exposer les détails de routage à l'iframe.

Utilisez uniquement les routes de session directes à partir de l'interface utilisateur Chastify de première partie ou des flux backend de confiance qui disposent déjà d'une autorisation de session d'extension valide.