Opslag van extensiebestanden
TLDR: store the file id, render with a signed URL.
When an extension uploads a file, Chastify returns a stable id and a short-lived signedUrl. Store the id in your extension config, state, or own database.
When your extension page loads later, call bridge.request("files.get", { fileId }) or GET /api/extensions/sessions/:sessionId/files/:fileId with the stored id. Chastify verifies that the file belongs to the same extension app, session, and lock, then returns a fresh signed R2 URL.
Render the image with <img src={file.signedUrl} />. Do not store signed URLs long-term because they expire.
Met de bestandsopslag van een extensie kan een ingeschakelde extensie afbeeldingsbestanden uploaden die bij een vergrendelde extensiesessie horen.
Gebruik dit wanneer de JSON-status van een extensie niet voldoende is, bijvoorbeeld:
- puzzelafbeeldingen
- gegenereerde voorbeelden
- uitdagingsfoto's
- extensiespecifieke media die samen met de vergrendeling/sessie moeten worden opgeruimd.
Deze opslag is gescheiden van state.*. Gebruik state.* voor kleine JSON-gegevens. Gebruik bestandsopslag alleen voor binaire media.
Relatie tot eindpunten van de status
De extensiestatus is bedoeld voor kleine, sessiegebonden JSON-bestanden. Gebruik vanuit een iframe de bridge read-opdracht in plaats van de REST-aanroep direct:
state.get
Dat brugcommando wordt doorgestuurd naar:
GET /api/extensions/sessions/:sessionId/state
Directe backend-aanroepen om de status te schrijven, maken gebruik van het authenticatiemodel van de installed-extension API:
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH
Verstuur geen API-sleutels voor ontwikkelaars naar iframe-/browsercode.
Gebruik door de backend geschreven statusinformatie voor duurzame verwijzingen en UI-gegevens, bijvoorbeeld:
{
"puzzleImageFileId": "file_record_id",
"selectedImageIds": ["file_record_id"],
"lastOpenedTab": "images"
}
Sla geen binaire gegevens, base64-afbeeldingen, browser-URL's met blob: of langdurige kopieën van signedUrl op in de status. Ondertekende URL's verlopen en moeten worden vernieuwd met files.get wanneer de afbeelding wordt weergegeven. De grootte van schrijfbewerkingen naar de status wordt beperkt door de stateMaxBytes-instelling van de extensie-app, met een standaardwaarde van 64 KiB.
Opslagmodel
Chastify slaat extensiebestanden op in door Chastify beheerde R2-opslag.
Elk geüpload bestand wordt gevolgd met:
scope: "extension"appIdsessionIdenlockIdvoor runtime-/sessiebestandentemplateIdvoor bestanden die zijn geclaimd door een opgeslagen vergrendelingssjabloonstaged,draftIdenexpiresAtvoor installatie-uploads die nog niet zijn geclaimd.extensionKeypurpose
Beheerpoort en quota
Admin controlled
Extension file storage is disabled by default.
Admins can enable it and configure:
- maximum bytes per file
- maximum total bytes per extension app
- maximum total bytes per active extension session
- maximum staged bytes per user and extension app
- allowed MIME prefixes
The first implementation is image-focused and should use image/* uploads. SVG is not accepted by the storage service.
If storage is disabled or R2 is not configured, upload requests fail before the server reads the multipart file body.
Staged setup uploads count toward the current user's per-app staged quota until they are claimed. Claimed files count toward the per-extension app quota. Runtime files with a session id also count toward the per-session quota.
Staging-eindpunten instellen
Gebruik de installatieomgeving alleen wanneer de installatie-UI wordt uitgevoerd voordat een extensiesessie bestaat.
Dit is een tijdelijke uploadprocedure. Deze is bedoeld voor instel-/configuratieschermen waar de gebruiker een bestand kan uploaden en vervolgens het modale venster kan sluiten of de extensie kan uitschakelen voordat een vergrendeling/sessie wordt aangemaakt. Een tijdelijk bestand is pas permanent beschikbaar nadat het is geclaimd door de extensieconfiguratie op te slaan die ernaar verwijst.
De installatie-UI's kunnen de beschikbaarheid en het huidige gebruik in de testomgeving controleren voordat de uploadbesturingselementen worden weergegeven:
GET /api/extensions/apps/:appId/files/capabilities
Het antwoord bevat stagedQuota voor de nog niet verlopen, in de staging-area opgeslagen bestanden van de huidige gebruiker in die extensie-app:
{
"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
Formuliervelden:
file: vereist afbeeldingsbestandpurpose: optionele korte identificatiecode, bijvoorbeeldjigsaw-config-imagedraftId: optionele concept-ID voor de configuratie; hergebruik dezelfde waarde zolang er één configuratiemodal geopend is.
Het antwoord bevat een stabiele file.id en een kortstondige, ondertekende URL voor onmiddellijke preview.
Voorbeeldantwoord:
{
"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
}
}
Om de configuratie-uploads na een paginaverversing te herstellen, kunt u de klaargezette bestanden voor de huidige gebruiker en extensie-app weergeven:
GET /api/extensions/apps/:appId/files/staged?purpose=jigsaw-config-image
Optionele queryparameters:
purpose: retourneer alleen de klaargezette bestanden voor één configuratiegebruiksscenariodraftId: retourneer alleen bestanden uit een bekend configuratiebestand
Als draftId wordt weggelaten, retourneert Chastify de niet-verlopen, in de staging-area opgeslagen bestanden van de huidige gebruiker voor die app. Dit is opzettelijk voor setup-UI's: een gebruiker kan bestanden uploaden, de pagina opnieuw laden of van apparaat wisselen, en het setup-scherm kan die tijdelijke uploads herstellen voordat de vergrendeling/sjabloon wordt opgeslagen.
Het antwoord op de staged list bevat ook stagedQuota, dat het nog niet verlopen staged gebruik van de huidige gebruiker voor die app rapporteert:
{
"items": [],
"stagedQuota": {
"bytesUsed": 123456,
"fileCount": 1,
"maxBytes": 10485760,
"remainingBytes": 10362304
}
}
De configuratie-UI's kunnen ook een voorbereid bestand vernieuwen of verwijderen:
GET /api/extensions/apps/:appId/files/:fileId
DELETE /api/extensions/apps/:appId/files/staged/:fileId
Later de geënsceneerde bestanden opeisen
Er is geen apart "claim"-eindpunt aan de browserzijde. Een klaargezet bestand wordt automatisch geclaimd wanneer Chastify een vergrendeling of sjabloonuitbreidingsconfiguratie opslaat die naar het klaargezette bestand verwijst.
Deze routes claimen de klaargezette bestanden nadat het lock-/template-document een ID heeft gekregen, en slaan vervolgens de extensieconfiguratie opnieuw op met de geclaimde bestandsverwijzingen. Als het claimen mislukt, wordt het nieuw aangemaakte lock-/template-document teruggedraaid, zodat de klaargezette bestanden niet aan een defecte configuratie blijven hangen.
Opmerking over gedeelde sjablonen: geaccepteerde gedeelde vergrendelingen behouden de bronsjabloon-ID op de gekloonde actieve vergrendeling. Sjabloon-geclaimde bestanden blijven daarom behouden zolang een gekloonde vergrendeling nog naar die sjabloon verwijst. Als de bronsjabloon wordt verwijderd, stelt Chastify het verwijderen van de extensiebestanden uit totdat de laatste gekloonde vergrendeling die naar de verwijderde sjabloon verwijst, is verwijderd of gearchiveerd.
Om een bestand dat in de staging-area is geplaatst te kunnen claimen, moet je een verwijzing zoals deze opslaan in de extensieconfiguratie die door de installatie-interface wordt ingediend:
{
"storageDraftId": "draft_123",
"images": [
{
"id": "file_record_id",
"provider": "chastify_storage",
"fileId": "file_record_id",
"url": "extension-file:file_record_id",
"title": "Puzzle image"
}
]
}
Bij het opslaan scant Chastify de configuratie op provider: "chastify_storage"-records met een geldige fileId, waarna:
- controleert of het bestand toebehoort aan de huidige gebruiker en extensie-app.
- Verwerpt verlopen staged bestanden
- weigert bestanden die al door een andere vergrendelings-/sjablooncontext in beslag zijn genomen
- markeringen waarnaar wordt verwezen in de gefaseerde bestanden zoals beweerd
- koppelt de geclaimde bestanden aan het opgeslagen slot of sjabloon.
- Verwijdert de tijdelijke velden
signedUrl,publicUrlenurlExpiresAtvoordat de configuratie wordt opgeslagen. - Verwijdert niet-gebruikte bestanden in de staging-area van dezelfde
storageDraftId.
Verlaten, in de stagingomgeving opgeslagen bestanden die nooit worden opgeslagen, worden na afloop van hun geldigheidsperiode automatisch verwijderd.
Om een voorbeeld van de instellingen voor een bestand-ID dat eigendom is van de huidige gebruiker en extensie-app te vernieuwen:
GET /api/extensions/apps/:appId/files/:fileId
Sessie-eindpunten
Deze eindpunten vereisen de gebruikelijke autorisatie en scopes voor extensiesessies.
Basispad:
/api/extensions/sessions/:sessionId/files
Mogelijkheden
Controleer dit voordat u de uploadbesturingselementen weergeeft.
GET /api/extensions/sessions/:sessionId/files/capabilities
Vereist locks:read.
Voorbeeldantwoord:
{
"enabled": true,
"provider": "r2",
"r2Configured": true,
"settings": {
"enabled": true,
"maxFileBytes": 10485760,
"maxBytesPerApp": 524288000,
"maxBytesPerSession": 52428800,
"maxStagedBytesPerUserPerApp": 10485760,
"allowedMimePrefixes": ["image/"]
}
}
Lijst bestanden
GET /api/extensions/sessions/:sessionId/files
Vereist locks:read.
Voorbeeldantwoord:
{
"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"
}
]
}
Eén bestand verkrijgen
Gebruik dit wanneer uw extensie al een opgeslagen bestands-ID heeft en alleen een nieuwe ondertekende R2-link nodig heeft.
GET /api/extensions/sessions/:sessionId/files/:fileId
Vereist locks:read.
Het bestand moet tot dezelfde extensie-app, sessie en vergrendeling behoren.
Voorbeeldantwoord:
{
"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"
}
}
Bestand uploaden
POST /api/extensions/sessions/:sessionId/files
Content-Type: multipart/form-data
Vereist locks:write.
Formuliervelden:
file: vereist afbeeldingsbestandpurpose: optionele korte identificatiecode zoalspuzzle-imageofpreview
Voorbeeld:
curl "https://chastify.net/api/extensions/sessions/SESSION_ID/files" \
-X POST \
-F "purpose=puzzle-image" \
-F "[email protected]"
Voorbeeldantwoord:
{
"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"
}
}
Veelvoorkomende uploadfouten:
extension_file_storage_disabledextension_file_storage_requires_r2file_too_largeinvalid_file_typeextension_app_storage_quota_exceededextension_session_storage_quota_exceededextension_staged_user_app_storage_quota_exceeded
Bestand verwijderen
DELETE /api/extensions/sessions/:sessionId/files/:fileId
Vereist locks:write.
Het bestand moet tot dezelfde extensie-app, sessie en vergrendeling behoren.
Iframe Bridge-acties
Iframe-extensies kunnen de bovenliggende webbridge gebruiken voor het lezen van bestanden tijdens runtime. Het uploaden en verwijderen van bestanden tijdens runtime zijn sessiemutaties en moeten door uw backend worden uitgevoerd met een app-specifieke Developer API-sleutel plus 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;
Ondersteunde brugacties:
files.capabilities-> controleer of bestandsopslag is ingeschakeld en welke quota van toepassing zijnfiles.list-> lijst bestanden die eigendom zijn van de huidige extensiesessiefiles.get-> vernieuw een ondertekende R2-URL vanuit een opgeslagen bestands-ID
Setup-iframes kunnen extra bridge-namen gebruiken voordat een runtime-sessie bestaat. In de setup-modus maakt files.upload bestanden aan die in de staging-area staan, files.list/files.staged.list geeft een lijst weer van de bestanden die nog niet verlopen zijn voor de huidige gebruiker en app, en files.delete verwijdert een bestand dat in de staging-area staat. De setup-initialisatiecontext bevat storageDraftId; neem deze waarde op in uw opgeslagen configuratie als storageDraftId als u wilt dat Chastify bestanden die niet meer worden gebruikt uit hetzelfde concept direct na het opslaan verwijdert.
De configuratie files.upload accepteert ook dataUrl voor eenvoudige iframe-clients:
await bridge.request("files.upload", {
dataUrl: canvas.toDataURL("image/webp", 0.9),
filename: "preview.webp",
purpose: "preview"
}, 60000);
Geef de voorkeur aan uploads met File of Blob wanneer mogelijk. Gebruik dataUrl alleen voor kleine gegenereerde afbeeldingen, omdat base64-payloads meer geheugen in beslag nemen.
Ondertekende links
De stabiele bestandsidentificatiecode is id.
Gebruik signedUrl om het bestand weer te geven of te downloaden. Ondertekende links zijn kortstondige R2 GetObject-URL's die gegenereerd worden door Chastify. publicUrl wordt bewaard als compatibiliteitsalias en bevat momenteel dezelfde ondertekende URL.
Wanneer een ondertekende link verloopt, roept u GET /api/extensions/sessions/:sessionId/files/:fileId aan om een nieuwe link voor één opgeslagen bestand te ontvangen, of GET /api/extensions/sessions/:sessionId/files om alle sessiebestandslinks te vernieuwen.
Opruimlevenscyclus
Extensiebestanden worden opgeruimd via een op BullMQ gebaseerde opschoonwachtrij.
De schoonmaak is gepland wanneer:
- een extensie-app wordt verwijderd
- Vergrendelings-/sessieverlengingsdocumenten worden verwijderd tijdens het verwijderen van de vergrendeling/het opschonen van het archief.
- Een bestand wordt expliciet verwijderd via het sessiebestand-eindpunt.
De wachtrij zorgt ervoor dat kostbare R2-verwijderingen en databaseopschoning buiten het aanvraagpad blijven. Als het toevoegen aan de wachtrij mislukt, schakelt de server na het antwoord over op opschoning binnen het proces, indien er een aanvraagcontext bestaat, of op directe opschoning op een lager niveau in de levenscyclus.
Prestatie-aantekeningen
- Voordat de uploadinterface wordt weergegeven, moeten er controles op de compatibiliteit plaatsvinden.
- Uploads zijn gebonden aan limieten per bestand, per sessie en per app.
- Quota-controles gebruiken geïndexeerde
UserFile-metadata voorscope + appId,scope + sessionIdenscope + lockId. - De streams worden opgeschoond door overeenkomende bestandsrecords te gebruiken in plaats van alle URL's in het geheugen te laden.
- Geüploade rasterafbeeldingen worden verwerkt en opgeslagen als geoptimaliseerde webafbeeldingen.
Beveiligingsnotities
- Beschouw URL's van afbeeldingen die via een extensie worden aangeleverd niet als betrouwbaar bewijs van voltooiing.
- Bewaar alleen bestandsrecords met de extensie Chastify als vertrouwde extensiebestanden.
- Koppel bestandsrecords aan
appId,sessionIdenlockId. - Dwing
locks:writeaf voor uploads en verwijderingen. - Controleer het MIME-type en wijs SVG af.
- Houd door de beheerder ingestelde quota ingeschakeld voordat u breed publiek gebruik toestaat.
- Gebruik server-side validatie voor elke workflow die afhankelijk is van geüploade bestanden.
Directe routes versus brug
Gebruik de iframe-brug wanneer uw extensie binnen Chastify draait. Hierdoor blijft de authenticatie van Chastify binnen de hoofdpagina en worden routegegevens niet aan de iframe blootgesteld.
Gebruik alleen directe sessieroutes vanuit de eigen Chastify UI of vertrouwde backend-flows die al een geldige extensie-sessieautorisatie hebben.