Media Gallery

Photo galleries — local disk or a cloud bucket by default, real Adhara Media Gallery folders when configured.

/gallery is new in this port — not in the Python original. Photo galleries, one page with client-side category tabs. It’s the one feature whose “local” mode has two genuinely different sub-sources, chosen automatically rather than by a second env var.

Three sources

  • Local disk (the zero-config default) — drop image files into public/gallery/<name>/; each subfolder becomes a gallery, named from its folder name. No database, no upload step, no CLI command — Astro serves the files directly as static assets. This is build-time content like theme CSS, not runtime-written storage: commit the images (or add them before npm run build) so they end up in the deployed build.
  • Cloud bucket (automatic once this site’s ContentStore backend is a cloud one rather than local disk — see Storage) — galleries are read from upload/gallery/<name>/ in that same bucket instead, reusing the exact pluggable storage mechanism already proven by the block editor’s image uploads.
  • Adhara (GRAVITY_GALLERY_BACKEND=adhara, or auto-detected once both ADHARA_API_KEY and ADHARA_WORKSPACE_ID are set — unlike events/links/shop, a workspace slug alone isn’t enough) — every root-level folder in the workspace’s real Media Gallery becomes a gallery, auto-discovered via a listFolders() call. No per-folder env var to hand-maintain.

Why Adhara mode needs the API key

Confirmed against the real backend: there is no public, no-API-key, multi-folder gallery-browsing endpoint. The one anonymous route that exists (/public/gallery/{share_token}) is scoped to a single admin-shared folder for one-off client delivery — a different feature entirely, not general marketing-page gallery browsing. Real, site-wide gallery browsing always needs ADHARA_API_KEY server-side. Recommend scoping the key to the media:public permission — the backend then silently restricts results to files explicitly marked public, the safe default for anything embedded on a public page.

A resource the SDK didn’t have

The SDK had zero coverage of Adhara’s Media Gallery system before this feature — not a wrong-shaped method, a fully missing resource (its pre-existing AssetsResource/ DigitalAsset is a different system: gated downloadable files like PDFs/ebooks, zero overlap). A MediaResource was added at the SDK’s source — see The Adhara SDK for the full “layer on top, fix at the source” pattern this followed.

Architecture

sdk/node/src/resources/media.ts (SDK) → src/lib/adharaMedia.ts (thin wrapper) → src/services/galleryStore.ts (three-source resolution) → src/pages/gallery.astro. The cloud-bucket path adds a listMedia(prefix) method to every ContentStore backend (src/stores/*.ts) — the read-back counterpart to the writeMedia() every backend already had.