Pohrana datoteka proširenja
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.
Pohrana datoteka proširenja omogućuje omogućenom proširenju prijenos slikovnih datoteka koje pripadaju sesiji zaključanog proširenja.
Koristite ga kada JSON stanja ekstenzije nije dovoljan, na primjer:
- slike zagonetki
- generirani pregledi
- fotografije izazova
- Mediji specifični za ekstenziju koji bi trebali biti očišćeni pomoću zaključavanja/sesije
Ova pohrana je odvojena od state.*. Za male JSON podatke koristite state.*. Pohranu datoteka koristite samo za binarne medije.
Odnos prema krajnjim točkama stanja
Stanje proširenja je za mali JSON s opsegom sesije. Iz iframea upotrijebite naredbu bridge read umjesto izravnog pozivanja REST-a:
state.get
Ta naredba mosta usmjerava na:
GET /api/extensions/sessions/:sessionId/state
Izravni backend pozivi za pisanje stanja koriste model autentifikacije API-ja instaliranog proširenja:
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH
Ne šaljite ključeve API-ja razvojnih programera u iframe/kod preglednika.
Koristite stanje napisano u pozadini za trajne reference i podatke korisničkog sučelja, na primjer:
{
"puzzleImageFileId": "file_record_id",
"selectedImageIds": ["file_record_id"],
"lastOpenedTab": "images"
}
Ne pohranjujte binarne podatke, base64 slike, URL-ove preglednika blob: ili dugotrajne kopije signedUrl u stanju. Potpisani URL-ovi istječu i treba ih osvježiti s files.get kada se slika renderira. Zapisivanje stanja ograničeno je veličinom postavke stateMaxBytes aplikacije proširenja, koja je zadana na 64 KiB.
Model pohrane
Chastify pohranjuje datoteke ekstenzija u R2 pohranu kojom upravlja Chastify.
Svaka prenesena datoteka prati se pomoću:
scope: "extension"appIdsessionIdilockIdza datoteke vremena izvođenja/sesijetemplateIdza datoteke koje je zatražio spremljeni predložak zaključavanjastaged,draftIdiexpiresAtza prijenose postavki koji još nisu preuzetiextensionKeypurpose
Administratorska vrata i kvote
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.
Postavljanje krajnjih točaka pripreme
Koristite pripremu za postavljanje samo kada se korisničko sučelje za postavljanje pokrene prije nego što postoji sesija proširenja.
Ovo je privremeni tok prijenosa. Postoji za zaslone za postavljanje/konfiguraciju gdje korisnik može prenijeti datoteku, a zatim zatvoriti modalni prozor ili onemogućiti proširenje prije stvaranja zaključavanja/sesije. Pripremljena datoteka nije trajna dok se ne zatraži spremanjem konfiguracije proširenja koja se na nju poziva.
Korisnička sučelja za postavljanje mogu provjeriti dostupnost i trenutnu upotrebu u fazama prije prikazivanja kontrola prijenosa:
GET /api/extensions/apps/:appId/files/capabilities
Odgovor uključuje stagedQuota za neistekle pripremljene datoteke trenutnog korisnika na toj aplikaciji proširenja:
{
"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
Polja obrasca:
file: potrebna slikovna datotekapurpose: opcionalni kratki identifikator kao što jejigsaw-config-imagedraftId: opcionalni ID nacrta postavki; ponovno upotrijebite istu vrijednost dok je otvoren jedan modalni prozor za postavke
Odgovor sadrži stabilan file.id i kratkotrajni potpisani URL za trenutni pregled.
Primjer odgovora:
{
"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
}
}
Za vraćanje postavki nakon osvježavanja stranice, navedite pripremljene datoteke za trenutnog korisnika i aplikaciju proširenja:
GET /api/extensions/apps/:appId/files/staged?purpose=jigsaw-config-image
Neobavezni parametri upita:
purpose: vrati samo pripremljene datoteke za jedan slučaj upotrebe postavljanjadraftId: vrati samo datoteke iz poznatog nacrta postavki
Ako se izostavi draftId, Chastify vraća neistekle pripremljene datoteke trenutnog korisnika za tu aplikaciju. To je namjerno za korisnička sučelja za postavljanje: korisnik može prenijeti datoteke, ponovno učitati stranicu ili promijeniti uređaj, a zaslon za postavljanje može vratiti te privremene prijenose prije nego što se spremi brava/predložak.
Odgovor s popisa postupaka također uključuje stagedQuota, koji izvještava o neistekloj upotrebi postupaka trenutnog korisnika za tu aplikaciju:
{
"items": [],
"stagedQuota": {
"bytesUsed": 123456,
"fileCount": 1,
"maxBytes": 10485760,
"remainingBytes": 10362304
}
}
Korisnička sučelja za postavljanje također mogu osvježiti ili izbrisati jednu pripremljenu datoteku:
GET /api/extensions/apps/:appId/files/:fileId
DELETE /api/extensions/apps/:appId/files/staged/:fileId
Naknadno preuzimanje pripremljenih datoteka
Ne postoji zasebna krajnja točka "zahtjeva" na strani preglednika. Pripremljena datoteka se automatski zahtijeva kada Chastify spremi konfiguraciju proširenja brave ili predloška koja referencira pripremljenu datoteku.
Te rute preuzimaju pripremljene datoteke nakon što dokument zaključavanja/predloška dobije ID, a zatim ponovno spremaju konfiguraciju proširenja s referencama preuzetih datoteka. Ako preuzimanje ne uspije, novostvoreni zaključavanje/predložak se poništava kako pripremljene datoteke ne bi ostale pričvršćene na neispravnu konfiguraciju.
Napomena o dijeljenom predlošku: prihvaćeni dijeljeni brave zadržavaju ID izvornog predloška na kloniranom aktivnom bravi. Datoteke za koje je zatražen predložak stoga se čuvaju dok se bilo koja klonirana brava još uvijek referencira na taj predložak. Ako se izvorni predložak izbriše, Chastify odgađa brisanje svojih datoteka proširenja dok se posljednja klonirana brava koja referencira izbrisani predložak ne izbriše ili arhivira.
Da biste omogućili zahtjev za pripremljenu datoteku, pohranite referencu poput ove u konfiguraciji proširenja koju šalje korisničko sučelje za postavljanje:
{
"storageDraftId": "draft_123",
"images": [
{
"id": "file_record_id",
"provider": "chastify_storage",
"fileId": "file_record_id",
"url": "extension-file:file_record_id",
"title": "Puzzle image"
}
]
}
Prilikom spremanja, Chastify skenira konfiguraciju za provider: "chastify_storage" zapise s valjanim fileId, a zatim:
- provjerava pripada li datoteka trenutnom korisniku i aplikaciji proširenja
- odbacuje istekle pripremljene datoteke
- odbacuje datoteke koje je već zatražio drugi kontekst zaključavanja/predloška
- označava referencirane pripremljene datoteke kao što je navedeno
- veže zatražene datoteke sa spremljenim zaključavanjem ili predloškom
- uklanja privremena polja
signedUrl,publicUrliurlExpiresAtprije nego što se konfiguracija sačuva - briše nereferencirane pripremljene datoteke iz istog
storageDraftId
Napuštene pripremljene datoteke koje se nikada ne spremaju brišu se čišćenjem nakon isteka roka.
Za osvježavanje pregleda postavki za ID datoteke u vlasništvu trenutnog korisnika i aplikacije proširenja:
GET /api/extensions/apps/:appId/files/:fileId
Krajnje točke sesije
Ove krajnje točke zahtijevaju normalnu autorizaciju i opsege sesije proširenja.
Osnovni put:
/api/extensions/sessions/:sessionId/files
Mogućnosti
Provjerite ovo prije renderiranja kontrola prijenosa.
GET /api/extensions/sessions/:sessionId/files/capabilities
Zahtijeva locks:read.
Primjer odgovora:
{
"enabled": true,
"provider": "r2",
"r2Configured": true,
"settings": {
"enabled": true,
"maxFileBytes": 10485760,
"maxBytesPerApp": 524288000,
"maxBytesPerSession": 52428800,
"maxStagedBytesPerUserPerApp": 10485760,
"allowedMimePrefixes": ["image/"]
}
}
Popis datoteka
GET /api/extensions/sessions/:sessionId/files
Zahtijeva locks:read.
Primjer odgovora:
{
"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"
}
]
}
Nabavi jednu datoteku
Koristite ovo kada vaša ekstenzija već ima pohranjeni ID datoteke i potrebna joj je samo nova potpisana R2 veza.
GET /api/extensions/sessions/:sessionId/files/:fileId
Zahtijeva locks:read.
Datoteka mora pripadati istoj aplikaciji, sesiji i bravi s istim proširenjem.
Primjer odgovora:
{
"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"
}
}
Prenesi datoteku
POST /api/extensions/sessions/:sessionId/files
Content-Type: multipart/form-data
Zahtijeva locks:write.
Polja obrasca:
file: potrebna slikovna datotekapurpose: opcionalni kratki identifikator kao što jepuzzle-imageilipreview
Primjer:
curl "https://chastify.net/api/extensions/sessions/SESSION_ID/files" \
-X POST \
-F "purpose=puzzle-image" \
-F "[email protected]"
Primjer odgovora:
{
"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"
}
}
Uobičajene pogreške pri prijenosu:
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
Izbriši datoteku
DELETE /api/extensions/sessions/:sessionId/files/:fileId
Zahtijeva locks:write.
Datoteka mora pripadati istoj aplikaciji, sesiji i bravi s istim proširenjem.
Radnje mosta Iframe
Iframe ekstenzije mogu koristiti nadređeni web most za čitanje datoteka tijekom izvođenja. Prijenos i brisanje datoteka tijekom izvođenja su mutacije sesije i trebao bi ih izvoditi vaš backend s ključem API-ja razvojnog programera s opsegom aplikacije i 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;
Podržane radnje mosta:
files.capabilities-> provjeri je li omogućeno pohranjivanje datoteka i koje se kvote primjenjujufiles.list-> popis datoteka u vlasništvu trenutne sesije proširenjafiles.get-> osvježi jedan potpisani R2 URL iz pohranjenog ID-a datoteke
iframeovi za postavljanje mogu koristiti dodatna imena mostova prije nego što postoji sesija izvođenja. U načinu rada za postavljanje, files.upload stvara pripremljene datoteke, files.list/files.staged.list navodi neistekle pripremljene datoteke za trenutnog korisnika i aplikaciju, a files.delete briše pripremljenu datoteku. Početni kontekst postavljanja uključuje storageDraftId; uključite tu vrijednost u spremljenu konfiguraciju kao storageDraftId ako želite da Chastify odmah prilikom spremanja očisti nereferencirane datoteke iz istog nacrta.
Postavljanje files.upload također prihvaća dataUrl za jednostavne iframe klijente:
await bridge.request("files.upload", {
dataUrl: canvas.toDataURL("image/webp", 0.9),
filename: "preview.webp",
purpose: "preview"
}, 60000);
Kad god je moguće, preferirajte prijenose File ili Blob. dataUrl koristite samo za male generirane slike jer su base64 korisni sadržaji veći u memoriji.
Potpisane poveznice
Stabilni identifikator datoteke je id.
Za prikaz ili preuzimanje datoteke koristite signedUrl. Potpisane veze su kratkotrajni R2 GetObject URL-ovi generirani od strane Chastify. publicUrl se čuva kao pseudonim kompatibilnosti i trenutno sadrži isti potpisani URL.
Kada potpisana veza istekne, pozovite GET /api/extensions/sessions/:sessionId/files/:fileId za primanje nove veze za jednu pohranjenu datoteku ili GET /api/extensions/sessions/:sessionId/files za osvježavanje svih veza datoteka sesije.
Životni ciklus čišćenja
Datoteke proširenja čiste se putem reda za čišćenje koji podržava BullMQ.
Čišćenje je planirano kada:
- aplikacija proširenja je izbrisana
- Dokumenti o proširenju zaključavanja/sesije brišu se tijekom uklanjanja zaključavanja/čišćenja arhive
- datoteka se eksplicitno briše putem krajnje točke datoteke sesije
Red čekanja sprječava skupo brisanje R2 i čišćenje baze podataka u putanji zahtjeva. Ako stavljanje u red čekanja ne uspije, poslužitelj se vraća na čišćenje unutar procesa nakon odgovora gdje postoji kontekst zahtjeva ili izravno čišćenje u najboljem mogućem smjeru u čišćenju životnog ciklusa niže razine.
Bilješke o izvedbi
- Provjere mogućnosti trebale bi se provesti prije prikaza korisničkog sučelja za prijenos.
- Prijenosi su ograničeni po datoteci, po sesiji i po aplikaciji.
- Provjere kvota koriste indeksirane metapodatke
UserFilezascope + appId,scope + sessionIdiscope + lockId. - Čišćenje tokova podudaranja zapisa datoteka umjesto učitavanja svih URL-ova u memoriju.
- Prenesene rasterske slike obrađuju se i pohranjuju kao optimizirane web slike.
Sigurnosne napomene
- Ne tretirajte URL-ove slika koje pruža proširenje kao pouzdani dokaz dovršetka.
- Pohrani samo zapise datoteka izdanih od strane Chastify kao pouzdane datoteke ekstenzija.
- Poveži zapise datoteke sa
appId,sessionIdilockId. - Primijeni
locks:writeza prijenose i brisanja. - Validiraj MIME tip i odbaci SVG.
- Ostavite administratorski kontrolirane kvote omogućenima prije nego što dopustite široku javnu upotrebu.
- Koristite validaciju na strani poslužitelja za bilo koji tijek rada koji ovisi o prenesenim datotekama.
Izravne rute u odnosu na most
Koristite iframe most kada se vaše proširenje izvršava unutar Chastify. To zadržava Chastify autentifikaciju na nadređenoj stranici i izbjegava izlaganje detalja rute iframeu.
Koristite izravne rute sesije samo iz korisničkog sučelja Chastify prve strane ili pouzdanih pozadinskih tokova koji već imaju valjanu autorizaciju proširenja sesije.