Przejdź do głównej zawartości

Przechowywanie plików rozszerzeń

Przechowywanie plików rozszerzeń umożliwia włączonemu rozszerzeniu przesyłanie plików graficznych należących do sesji rozszerzenia blokady.

Użyj go, gdy stan rozszerzenia JSON jest niewystarczający, na przykład:

  • obrazy puzzli
  • wygenerowane podglądy
  • zdjęcia wyzwania
  • media specyficzne dla rozszerzenia, które należy oczyścić za pomocą blokady/sesji

To miejsce przechowywania jest oddzielne od state.*. Użyj state.* dla małych danych JSON. Używaj miejsca przechowywania plików tylko dla multimediów binarnych.

Relacja do punktów końcowych stanu

Stan rozszerzenia jest przeznaczony dla małych JSON-ów o zasięgu sesji. Z poziomu ramki iframe użyj polecenia bridge read zamiast bezpośredniego wywoływania REST:

state.get

Dowództwo mostu kieruje się do:

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

Bezpośrednie wywołania zaplecza służące do zapisu stanu korzystają z modelu uwierzytelniania API zainstalowanego rozszerzenia:

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

Nie wysyłaj kluczy API dla programistów do kodu iframe/przeglądarki.

Użyj stanu zapisanego w zapleczu dla trwałych odniesień i danych interfejsu użytkownika, na przykład:

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

Nie przechowuj danych binarnych, obrazów base64, adresów URL blob: przeglądarki ani długotrwałych kopii signedUrl w stanie. Podpisane adresy URL wygasają i należy je odświeżyć za pomocą files.get podczas renderowania obrazu. Rozmiar zapisów stanu jest ograniczony przez ustawienie stateMaxBytes w aplikacji rozszerzenia, domyślnie 64 KiB.

Model pamięci masowej

Chastify przechowuje pliki rozszerzeń w pamięci masowej R2 zarządzanej przez Chastify.

Każdy przesłany plik jest śledzony za pomocą:

  • scope: "extension"
  • appId
  • sessionId i lockId dla plików wykonawczych/sesyjnych
  • templateId dla plików zgłoszonych przez zapisany szablon blokady
  • staged, draftId i expiresAt w przypadku przesłanych plików konfiguracyjnych, które nie zostały jeszcze odebrane
  • extensionKey
  • purpose

Brama administracyjna i limity

Konfiguracja punktów końcowych przejściowych

Użyj konfiguracji przejściowej tylko wtedy, gdy interfejs użytkownika konfiguracji zostanie uruchomiony przed utworzeniem sesji rozszerzenia.

To tymczasowy proces przesyłania. Jest on przeznaczony dla ekranów konfiguracji, gdzie użytkownik może przesłać plik, a następnie zamknąć okno modalne lub wyłączyć rozszerzenie przed utworzeniem blokady/sesji. Plik przetworzony nie jest trwały, dopóki nie zostanie przejęty przez zapisanie konfiguracji rozszerzenia, która się do niego odwołuje.

Interfejsy użytkownika konfiguracji mogą sprawdzać dostępność i bieżące wykorzystanie etapów przed renderowaniem elementów sterujących przesyłaniem:

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

Odpowiedź zawiera kod stagedQuota dla niewygasłych plików tymczasowych bieżącego użytkownika w tej aplikacji rozszerzenia:

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

Pola formularza:

  • file: wymagany plik obrazu
  • purpose: opcjonalny krótki identyfikator, taki jak jigsaw-config-image
  • draftId: opcjonalny identyfikator projektu konfiguracji; użyj tej samej wartości, gdy otwarte jest jedno okno konfiguracji

Odpowiedź zawiera stabilny kod file.id i krótkotrwały podpisany adres URL umożliwiający natychmiastowy podgląd.

Przykładowa odpowiedź:

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

Aby przywrócić ustawienia przesyłania po odświeżeniu strony, wyświetl listę plików przygotowanych dla bieżącego użytkownika i aplikacji rozszerzenia:

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

Opcjonalne parametry zapytania:

  • purpose: zwraca tylko pliki przygotowane do instalacji w jednym przypadku użycia
  • draftId: zwraca tylko pliki ze znanego projektu instalacji

Jeśli parametr draftId zostanie pominięty, Chastify zwróci niewygasłe pliki przejściowe bieżącego użytkownika dla tej aplikacji. Jest to celowe w przypadku interfejsów użytkownika konfiguracji: użytkownik może przesyłać pliki, przeładować stronę lub zmienić urządzenie, a ekran konfiguracji może przywrócić te tymczasowe pliki przed zapisaniem blokady/szablonu.

Odpowiedź listy etapowej zawiera również kod stagedQuota, który raportuje bieżący czas wykorzystania etapowego danej aplikacji przez użytkownika:

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

Interfejsy użytkownika instalacji mogą również odświeżać lub usuwać jeden plik przygotowawczy:

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

Późniejsze zgłaszanie roszczeń do plików przygotowanych do etapu przygotowawczego

Nie ma oddzielnego punktu końcowego „deklaracji” po stronie przeglądarki. Plik przeniesiony jest deklarowany automatycznie, gdy Chastify zapisuje konfigurację blokady lub rozszerzenia szablonu, która odwołuje się do pliku przeniesionego.

Te trasy przejmują pliki przeniesione po nadaniu identyfikatora dokumentowi blokady/szablonu, a następnie ponownie zapisują konfigurację rozszerzenia z odwołaniami do przeniesionych plików. Jeśli przejęcie się nie powiedzie, nowo utworzona blokada/szablon jest wycofywana, aby pliki przeniesione nie pozostały dołączone do uszkodzonej konfiguracji.

Uwaga dotycząca współdzielonego szablonu: zaakceptowane współdzielone blokady zachowują identyfikator szablonu źródłowego dla sklonowanej aktywnej blokady. Pliki zgłoszone przez szablon są zatem zachowywane, dopóki każda sklonowana blokada nadal odwołuje się do tego szablonu. Jeśli szablon źródłowy zostanie usunięty, Chastify opóźnia usuwanie plików rozszerzeń do momentu usunięcia lub zarchiwizowania ostatniej sklonowanej blokady odwołującej się do usuniętego szablonu.

Aby plik przygotowany do przetworzenia mógł zostać uznany za możliwy do przejęcia, należy zapisać odwołanie w taki sposób w konfiguracji rozszerzenia przesłanej przez interfejs użytkownika konfiguracji:

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

Podczas zapisywania Chastify skanuje konfigurację w poszukiwaniu rekordów provider: "chastify_storage" z prawidłowym fileId, a następnie:

  • sprawdza, czy plik należy do bieżącego użytkownika i aplikacji rozszerzenia
  • odrzuca wygasłe pliki przygotowane do realizacji
  • odrzuca pliki już zgłoszone przez inny kontekst blokady/szablonu
  • oznacza pliki przygotowane do etapu, jak twierdzi
  • wiąże zgłoszone pliki z zapisaną blokadą lub szablonem
  • usuwa tymczasowe pola signedUrl, publicUrl i urlExpiresAt przed utrwaleniem konfiguracji
  • usuwa nieodwołane pliki etapowe z tego samego storageDraftId

Porzucone pliki tymczasowe, które nigdy nie zostały zapisane, są usuwane podczas czyszczenia po upływie ich ważności.

Aby odświeżyć podgląd konfiguracji dla identyfikatora pliku należącego do bieżącego użytkownika i aplikacji rozszerzenia:

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

Punkty końcowe sesji

Te punkty końcowe wymagają standardowej autoryzacji i zakresów sesji rozszerzenia.

Ścieżka bazowa:

/api/extensions/sessions/:sessionId/files

Możliwości

Sprawdź to przed renderowaniem elementów sterujących przesyłaniem.

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

Wymagany jest locks:read.

Przykładowa odpowiedź:

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

Lista plików

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

Wymagany jest locks:read.

Przykładowa odpowiedź:

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

Pobierz jeden plik

Użyj tej opcji, jeśli rozszerzenie ma już zapisany identyfikator pliku i potrzebuje tylko nowego, podpisanego łącza R2.

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

Wymagany jest locks:read.

Plik musi należeć do tej samej aplikacji rozszerzenia, sesji i blokady.

Przykładowa odpowiedź:

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

Prześlij plik

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

Wymagany jest locks:write.

Pola formularza:

  • file: wymagany plik obrazu
  • purpose: opcjonalny krótki identyfikator, taki jak puzzle-image lub preview

Przykład:

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

Przykładowa odpowiedź:

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

Typowe błędy przesyłania:

  • 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

Usuń plik

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

Wymagany jest locks:write.

Plik musi należeć do tej samej aplikacji rozszerzenia, sesji i blokady.

Akcje mostu iframe

Rozszerzenia iframe mogą korzystać z nadrzędnego mostu internetowego do odczytu plików w czasie wykonywania. Przesyłanie i usuwanie plików w czasie wykonywania to mutacje sesji i powinny być wykonywane przez zaplecze za pomocą klucza API dla programistów o zasięgu aplikacji oraz kodu 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;

Obsługiwane działania mostu:

  • files.capabilities -> sprawdź, czy przechowywanie plików jest włączone i jakie limity obowiązują
  • files.list -> wyświetl listę plików będących własnością bieżącej sesji rozszerzenia
  • files.get -> odśwież jeden podpisany adres URL R2 z zapisanego identyfikatora pliku

Ramki iframe konfiguracji mogą używać dodatkowych nazw mostów przed utworzeniem sesji wykonawczej. W trybie konfiguracji polecenie files.upload tworzy pliki etapowe, polecenie files.list/files.staged.list wyświetla listę niewygasłych plików etapowych dla bieżącego użytkownika i aplikacji, a polecenie files.delete usuwa plik etapowy. Kontekst inicjalizacji konfiguracji zawiera polecenie storageDraftId; należy dodać tę wartość do zapisanej konfiguracji jako storageDraftId, jeśli polecenie Chastify ma natychmiast po zapisaniu usuwać pliki bez odniesień z tej samej wersji roboczej.

Konfiguracja files.upload akceptuje również dataUrl dla prostych klientów iframe:

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

W miarę możliwości preferuj przesyłanie File lub Blob. Używaj dataUrl tylko w przypadku małych generowanych obrazów, ponieważ ładunki base64 zajmują więcej pamięci.

Stabilny identyfikator pliku to id.

Użyj signedUrl, aby wyświetlić lub pobrać plik. Podpisane linki to krótkotrwałe adresy URL R2 GetObject wygenerowane przez Chastify. publicUrl jest przechowywany jako alias zgodności i obecnie zawiera ten sam podpisany adres URL.

Gdy podpisany link wygaśnie, wywołaj GET /api/extensions/sessions/:sessionId/files/:fileId, aby otrzymać nowy link dla jednego zapisanego pliku lub GET /api/extensions/sessions/:sessionId/files, aby odświeżyć wszystkie linki plików sesji.

Cykl życia czyszczenia

Pliki rozszerzeń są czyszczone za pomocą kolejki czyszczącej obsługiwanej przez BullMQ.

Sprzątanie jest planowane, gdy:

  • aplikacja rozszerzenia została usunięta
  • dokumenty blokady/rozszerzenia sesji są usuwane podczas usuwania blokady/czyszczenia archiwum
  • plik jest jawnie usuwany za pomocą punktu końcowego pliku sesji

Kolejka utrzymuje kosztowne usuwanie R2 i czyszczenie bazy danych poza ścieżką żądania. Jeśli kolejkowanie kolejki się nie powiedzie, serwer powraca do czyszczenia w trakcie procesu po odpowiedzi, w której istnieje kontekst żądania, lub do czyszczenia z najlepszym wysiłkiem w ramach czyszczenia cyklu życia niższego poziomu.

Notatki dotyczące wydajności

  • Sprawdzenie możliwości powinno nastąpić przed wyświetleniem interfejsu użytkownika umożliwiającego przesyłanie.
  • Przesyłanie danych jest ograniczone limitami dotyczącymi pliku, sesji i aplikacji.
  • Sprawdzanie kwot wykorzystuje indeksowane metadane UserFile dla scope + appId, scope + sessionId i scope + lockId.
  • Oczyszczaj strumienie odpowiadające rekordom plików zamiast ładować wszystkie adresy URL do pamięci.
  • Przesłane obrazy rastrowe są przetwarzane i przechowywane jako zoptymalizowane obrazy internetowe.

Notatki bezpieczeństwa

  • Nie traktuj adresów URL obrazów udostępnianych przez rozszerzenie jako wiarygodnego dowodu ukończenia.
  • Przechowuj tylko rekordy plików wydane przez Chastify jako zaufane pliki rozszerzeń.
  • Powiąż rekordy pliku z appId, sessionId i lockId.
  • Wymuś locks:write podczas przesyłania i usuwania.
  • Sprawdź typ MIME i odrzuć SVG.
  • Przed zezwoleniem na szerokie publiczne korzystanie z Internetu należy ustawić limity kontrolowane przez administratora.
  • Korzystaj z walidacji po stronie serwera w przypadku każdego przepływu pracy zależnego od przesłanych plików.

Trasy bezpośrednie kontra mostowe

Użyj mostu iframe, gdy rozszerzenie działa w Chastify. Utrzymuje on uwierzytelnianie Chastify na stronie nadrzędnej i zapobiega ujawnianiu szczegółów trasy w ramce iframe.

Używaj bezpośrednich tras sesji wyłącznie z interfejsu użytkownika Chastify pierwszej strony lub zaufanych przepływów zaplecza, które mają już ważną autoryzację sesji rozszerzenia.