Assets Library
Assets Library to substrat zasobów cyfrowych platformy. Posiada:
- Centralny katalog plików (obrazy, wideo, PDF, dokumenty) wielokrotnie używanych w mediach produktu, załącznikach produktu, głównych obrazach kategorii i stronach CMS.
- Drzewo folderów do organizacji assetów.
- Wymienne backendy storage — lokalny filesystem, Amazon S3, GCP Cloud Storage — konfigurowane z Settings, dokładnie jeden aktywny naraz.
- Widoczność public/private per asset.
- Ochronę referencji: assetów używanych przez Catalog lub CMS nie można usunąć, dopóki każda referencja nie zostanie odłączona.
Szybki start
- Aktywny adapter — otwórz Settings → Storage i wybierz jeden z
local,s3lubgcs. Domyślnielocal. - Local FS — ustaw
assets.local.base_dir(domyślnievar/assets). Serwer admin tworzy drzewo katalogów w czasie uploadu. Ustawassets.local.public_url_base, gdy backend stoi za innym publicznym hostem (np. CDN lub reverse proxy); zostaw puste, a każdy URL budowany jest na publicznym origin API tego wdrożenia. - Amazon S3 — ustaw
assets.s3.bucket,assets.s3.region,assets.s3.access_key_id,assets.s3.secret_access_key. Opcjonalnie:assets.s3.endpoint(dla providerów kompatybilnych z S3 jak MinIO),assets.s3.prefix,assets.s3.public_base_url(prefiks bazowego URL CDN). - GCP Cloud Storage — ustaw
assets.gcs.bucketi alboassets.gcs.service_account_json(wklej pełny JSON), albo polegaj na ambient credentials. Opcjonalnie:assets.gcs.prefix,assets.gcs.public_base_url. - Test połączenia —
POST /api/v1/admin/assets/storage/self-check. Zwraca{ ok: true }przy sukcesie lub{ ok: false, reason: ... }przy błędnej konfiguracji. Dashboard jest podGET /api/v1/admin/assets/storage/state.
Ograniczenia uploadu
Dwa ustawienia bramkują uploady — oba sprawdzane, zanim jakikolwiek bajt dotrze do aktywnego adaptera:
assets.allowed_file_types— lista rozszerzeń plików lub typów MIME (np.["jpg", "png", "image/jpeg", "application/pdf"]). Sentinel["*"](domyślnie) wyłącza bramkę. Wildcardi jakimage/*pasują do każdego MIME pod tym prefiksem. Sprawdzenie uwzględnia zarówno zadeklarowane MIME, jak i MIME wykryte magic-number (file-type) i odrzuca, gdy którekolwiek zawiedzie.assets.max_file_size_mb— integer cap w MB.0(domyślnie) wyłącza cap.
Widoczność i prywatne URL-e
Każdy asset ma visibility ∈ {public, private}. Publiczne URL-e są stabilne;
prywatne URL-e są krótkotrwałe (TTL = assets.private_url_ttl_sec, domyślnie
300s) i ponownie wydawane przez admin lub storefront przy każdym odczycie.
Dla adaptera local FS prywatne URL-e są podpisywane HMAC przez backend.
Klucz podpisu pochodzi ze zmiennej env ASSETS_LIBRARY_HMAC_KEY
(wygeneruj przez openssl rand -hex 32); rotacja unieważnia każdy
wygasający prywatny URL.
Dla S3 i GCS prywatne URL-e są podpisywane V4 przez vendor SDK; backend nie wymaga nic poza credentials.
Soft-delete i hard-delete
Usunięcia przechodzą dwuetapowy cykl życia:
DELETE /api/v1/admin/assets/:id→ soft-delete. UstawiadeletedAtipurgeAfterAt = now + assets.soft_delete_retention_days(domyślnie 30). Asset znika z list i pickerów, ale pozostaje odzyskiwalny przezPOST /api/v1/admin/assets/:id/restore.- Powtarzający się
HardDeleteAssetWorkerfinalizuje popurgeAfterAt: usuwa wiersz i prosi adapter o usunięcie pliku źródłowego. Gdy backend nie może usunąć pliku (read-only mount, unieważnione credentials, partycja sieci), wiersz zostaje, apendingCleanupustawia się natrue; worker ponawia przy następnym ticku.
Ochrona referencji
Soft-delete jest odrzucany z 409 ASSET_REFERENCED, gdy którykolwiek z poniższych wskazuje asset:
gallery_items.asset_id(galeria produktu)product_attachments.asset_id(załączniki produktu)products.download_asset_id(produkty virtual-download)categories.main_image_asset_id(główny obraz kategorii)cms_pages.bodyzawierający węzeł{ type: "asset_ref", assetId }
Lista referencji jest dołączona do tablicy details envelope błędu, żeby
UI admina mogło pokazać monity „używany przez”.
Wymienne adaptery storage
Każdy backend implementuje SPI StorageAdapter w
packages/modules/assets_library/src/backend/services/storage/. Dodanie czwartego
backendu (Azure Blob, Backblaze B2, …) to dodanie nowej
klasy implementującej:
selfCheck(): Promise<{ ok: boolean; reason?: string }>;
newLocator({ assetId, originalFilename }): string;
put({ locator, mimeType, visibility, stream, sizeBytes }): Promise<void>;
resolveUrl({ locator, visibility, ttlSec? }): Promise<{ url; expiresAt }>;
open({ locator }): Promise<NodeJS.ReadableStream>;
delete({ locator }): Promise<void>;
setVisibility?({ locator, visibility }): Promise<void>;
…i rejestracja w AdapterRegistry.build. Zob. istniejące
LocalFsStorageAdapter, S3StorageAdapter i GcsStorageAdapter jako
szablony.
Legacy escape hatch
Wcześniej istniejące wiersze assets, których storage_url był URL-em, którego ta platforma nie
wydała, są tagowane storage_backend = 'legacy' w czasie migracji. Legacy
resolver zwraca URL verbatim dla assetów public — rebazując go na
publicznym origin API, gdy zapisana wartość jest host-relative — i odmawia przełączenia
ich na private (nie możemy podpisać URL-i, których nie wydaliśmy), co na adminie
powierzchnia się jako
409 ASSET_LEGACY_LOCATOR_CANNOT_HARDEN. Prze-uploaduj przez
aktywny adapter, żeby zupgrade'ować.