Passa al contenuto principale

Archiviazione file di estensione

L'archiviazione dei file dell'estensione consente a un'estensione abilitata di caricare file immagine appartenenti a una sessione di blocco dell'estensione.

Utilizzalo quando lo stato JSON dell'estensione non è sufficiente, ad esempio:

  • immagini di puzzle
  • anteprime generate
  • foto della sfida
  • media specifici dell'estensione che dovrebbero essere ripuliti con il blocco/sessione

Questo spazio di archiviazione è separato da state.*. Utilizzare state.* per piccoli dati JSON. Utilizzare l'archiviazione file solo per supporti binari.

Relazione con gli endpoint statali

Lo stato dell'estensione è per piccoli JSON con ambito di sessione. Da un iframe, utilizzare il comando di lettura del bridge invece di chiamare direttamente REST:

state.get

Quel comando bridge instrada a:

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

Le chiamate dirette al backend per scrivere lo stato utilizzano il modello di autenticazione API dell'estensione installata:

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

Non inviare le chiavi API per sviluppatori al codice iframe/del browser.

Utilizza lo stato scritto nel backend per i riferimenti persistenti e i dati dell'interfaccia utente, ad esempio:

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

Non memorizzare dati binari, immagini base64, URL blob: del browser o copie a lunga durata di signedUrl nello stato. Gli URL firmati scadono e devono essere aggiornati con files.get quando l'immagine viene visualizzata. Le scritture di stato sono limitate in termini di dimensione dall'impostazione stateMaxBytes dell'app di estensione, con un valore predefinito di 64 KiB.

Modello di archiviazione

Chastify memorizza i file di estensione nello storage R2 gestito da Chastify.

Ogni file caricato viene tracciato con:

  • scope: "extension"
  • appId
  • sessionId e lockId per i file di runtime/sessione
  • templateId per i file rivendicati da un modello di blocco salvato
  • staged, draftId e expiresAt per i caricamenti di configurazione non ancora rivendicati
  • extensionKey
  • purpose

Cancello amministrativo e quote

Configurazione degli endpoint di staging

Utilizzare la fase di configurazione solo quando l'interfaccia utente di configurazione viene eseguita prima che esista una sessione di estensione.

Si tratta di un flusso di caricamento temporaneo. È previsto per le schermate di configurazione in cui l'utente può caricare un file, quindi chiudere la finestra modale o disabilitare l'estensione prima di creare un blocco/sessione. Un file in attesa di conferma non è permanente finché non viene rivendicato salvando la configurazione dell'estensione che lo referenzia.

Le interfacce utente di configurazione possono verificare la disponibilità e l'utilizzo corrente prima di visualizzare i controlli di caricamento:

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

La risposta include stagedQuota per i file di staging non scaduti dell'utente corrente su quell'app di estensione:

{
"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

Campi del modulo:

  • file: file immagine richiesto
  • purpose: identificatore breve opzionale come jigsaw-config-image
  • draftId: ID bozza di configurazione opzionale; riutilizza lo stesso valore finché è aperta una finestra modale di configurazione.

La risposta contiene un codice file.id stabile e un URL firmato di breve durata per un'anteprima immediata.

Esempio di risposta:

{
"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
}
}

Per ripristinare i caricamenti di configurazione dopo un aggiornamento della pagina, elenca i file in attesa per l'utente corrente e l'app dell'estensione:

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

Parametri di query opzionali:

  • purpose: restituisce solo i file in fase di preparazione per un caso d'uso di configurazione
  • draftId: restituisce solo i file da una bozza di configurazione nota

Se draftId viene omesso, Chastify restituisce i file temporanei non scaduti dell'utente corrente per quell'app. Questo comportamento è intenzionale per le interfacce utente di configurazione: un utente può caricare file, ricaricare la pagina o cambiare dispositivo e la schermata di configurazione può ripristinare questi caricamenti temporanei prima che il blocco/modello venga salvato.

La risposta dell'elenco di fasi include anche stagedQuota, che riporta l'utilizzo di fase non ancora scaduto dell'utente corrente per quell'app:

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

Le interfacce utente di configurazione possono anche aggiornare o eliminare un file in fase di preparazione:

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

Richiesta di accesso ai file in fase di elaborazione in un secondo momento

Non esiste un endpoint "claim" separato lato browser. Un file in fase di staging viene rivendicato automaticamente quando Chastify salva un blocco o una configurazione di estensione del modello che fa riferimento al file in fase di staging.

Questi percorsi acquisiscono i file in fase di staging dopo che il documento di blocco/modello ha un ID, quindi salvano nuovamente la configurazione dell'estensione con i riferimenti ai file acquisiti. Se l'acquisizione fallisce, il blocco/modello appena creato viene annullato in modo che i file in fase di staging non rimangano collegati a una configurazione danneggiata.

Nota sui modelli condivisi: i blocchi condivisi accettati mantengono l'ID del modello di origine sul blocco attivo clonato. I file rivendicati dal modello vengono quindi mantenuti finché qualsiasi blocco clonato fa ancora riferimento a tale modello. Se il modello di origine viene eliminato, Chastify ritarda l'eliminazione dei relativi file di estensione fino a quando l'ultimo blocco clonato che fa riferimento al modello eliminato non viene eliminato o archiviato.

Per rendere un file in fase di preparazione riscattabile, memorizza un riferimento come questo nella configurazione dell'estensione che viene inviata dall'interfaccia utente di installazione:

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

Al salvataggio, Chastify analizza la configurazione alla ricerca di record provider: "chastify_storage" con un fileId valido, quindi:

  • verifica che il file appartenga all'utente corrente e all'app di estensione
  • rifiuta i file in fase di preparazione scaduti
  • rifiuta i file già rivendicati da un altro contesto di blocco/modello
  • marchi di riferimento file di staging come affermato
  • collega i file rivendicati al blocco o al modello salvato
  • Rimuove i campi temporanei signedUrl, publicUrl e urlExpiresAt prima che la configurazione venga salvata.
  • elimina i file staging non referenziati dallo stesso storageDraftId

I file abbandonati in fase di staging che non vengono mai salvati vengono eliminati dal sistema di pulizia dopo la loro scadenza.

Per aggiornare l'anteprima di installazione per un ID file di proprietà dell'utente corrente e dell'app di estensione:

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

Endpoint di sessione

Questi endpoint richiedono la normale autorizzazione e gli ambiti di sessione dell'estensione.

Percorso base:

/api/extensions/sessions/:sessionId/files

Capacità

Verifica questo prima di visualizzare i controlli di caricamento.

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

Richiede locks:read.

Esempio di risposta:

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

Elenco dei file

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

Richiede locks:read.

Esempio di risposta:

{
"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"
}
]
}

Ottieni un file

Utilizza questa opzione quando la tua estensione ha già un ID file memorizzato e necessita solo di un nuovo collegamento R2 firmato.

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

Richiede locks:read.

Il file deve appartenere alla stessa estensione, sessione e blocco.

Esempio di risposta:

{
"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"
}
}

Carica file

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

Richiede locks:write.

Campi del modulo:

  • file: file immagine richiesto
  • purpose: identificatore breve opzionale come puzzle-image o preview

Esempio:

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

Esempio di risposta:

{
"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"
}
}

Errori comuni durante il caricamento:

  • 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

Elimina file

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

Richiede locks:write.

Il file deve appartenere alla stessa estensione, sessione e blocco.

Azioni di Iframe Bridge

Le estensioni iframe possono utilizzare il bridge web principale per la lettura dei file in fase di esecuzione. Il caricamento e l'eliminazione dei file in fase di esecuzione sono modifiche di sessione e devono essere eseguiti dal backend con una chiave API per sviluppatori con ambito app più 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;

Azioni del ponte supportate:

  • files.capabilities -> verifica se l'archiviazione dei file è abilitata e quali quote si applicano.
  • files.list -> elenca i file di proprietà della sessione di estensione corrente
  • files.get -> aggiorna un URL R2 firmato da un ID file memorizzato

Gli iframe di configurazione possono utilizzare nomi di bridge aggiuntivi prima che esista una sessione di runtime. In modalità di configurazione, files.upload crea file in fase di staging, files.list/files.staged.list elenca i file in fase di staging non scaduti per l'utente e l'app correnti e files.delete elimina un file in fase di staging. Il contesto di inizializzazione della configurazione include storageDraftId; includi questo valore nella configurazione salvata come storageDraftId se desideri che Chastify elimini immediatamente i file non referenziati dalla stessa bozza al salvataggio.

La configurazione files.upload accetta anche dataUrl per i client iframe semplici:

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

Quando possibile, preferire i caricamenti con File o Blob. Utilizzare dataUrl solo per immagini generate di piccole dimensioni, poiché i payload base64 occupano più memoria.

L'identificativo stabile del file è id.

Utilizzare signedUrl per visualizzare o scaricare il file. I link firmati sono URL R2 GetObject di breve durata generati da Chastify. publicUrl viene mantenuto come alias di compatibilità e attualmente contiene lo stesso URL firmato.

Quando un collegamento firmato scade, chiama GET /api/extensions/sessions/:sessionId/files/:fileId per ricevere un nuovo collegamento per un file memorizzato, oppure GET /api/extensions/sessions/:sessionId/files per aggiornare tutti i collegamenti ai file della sessione.

Ciclo di vita della pulizia

I file di estensione vengono eliminati tramite una coda di pulizia basata su BullMQ.

La pulizia è programmata quando:

  • un'app di estensione viene eliminata
  • I documenti di blocco/estensione della sessione vengono eliminati durante la rimozione del blocco/pulizia dell'archivio.
  • un file viene eliminato esplicitamente tramite l'endpoint del file di sessione

La coda impedisce che costose operazioni di eliminazione R2 e pulizia del database influiscano sul percorso della richiesta. Se l'inserimento nella coda fallisce, il server ripiega sulla pulizia in-process dopo la risposta, laddove esista un contesto di richiesta, oppure sulla pulizia diretta "best-effort" nella pulizia del ciclo di vita di livello inferiore.

Note sulle prestazioni

  • Prima di mostrare l'interfaccia utente di caricamento, è necessario effettuare dei controlli di compatibilità.
  • Il numero di caricamenti è soggetto a limiti per singolo file, per sessione e per singola applicazione.
  • I controlli delle quote utilizzano i metadati indicizzati UserFile per scope + appId, scope + sessionId e scope + lockId.
  • La pulizia dei flussi si basa sulla corrispondenza dei record dei file anziché sul caricamento di tutti gli URL in memoria.
  • Le immagini raster caricate vengono elaborate e memorizzate come immagini web ottimizzate.

Note sulla sicurezza

  • Non considerare gli URL delle immagini forniti dalle estensioni come prove attendibili di completamento.
  • Archivia come file di estensione attendibili solo i record di file emessi con l'estensione Chastify.
  • Associa i record del file a appId, sessionId e lockId.
  • Applicare il codice locks:write per caricamenti ed eliminazioni.
  • Convalida il tipo MIME e rifiuta l'SVG.
  • Prima di consentire l'utilizzo pubblico su larga scala, mantieni attive le quote controllate dagli amministratori.
  • Utilizza la convalida lato server per qualsiasi flusso di lavoro che dipenda dal caricamento di file.

Percorsi diretti vs. ponti

Utilizza il bridge iframe quando la tua estensione è in esecuzione all'interno di Chastify. In questo modo, l'autenticazione di Chastify rimane nella pagina principale ed evita di esporre i dettagli del percorso all'iframe.

Utilizzare percorsi di sessione diretti solo dall'interfaccia utente Chastify proprietaria o da flussi backend affidabili che dispongono già di un'autorizzazione di sessione di estensione valida.