Skip to content

feat(directory): listing media upload against per-tenant storage - #152

Merged
OriginDevIT merged 2 commits into
mainfrom
feat/listing-media
Sep 8, 2026
Merged

feat(directory): listing media upload against per-tenant storage#152
OriginDevIT merged 2 commits into
mainfrom
feat/listing-media

Conversation

@OriginDevIT

Copy link
Copy Markdown
Owner

MediaAsset, per-tenant storage resolver (local only), attach/detach services emitting listing.updated, tenant-scoped media serving view, admin upload UI, public rendering.

Rejects media on listing.upsert per spec 7.1 (v0.7).

Cloud backends deferred, #150. Pillow added, closes #151.

OriginDevIT and others added 2 commits September 7, 2026 12:26
Adds MediaAsset and a per-tenant storage resolver, so an operator can
upload logo, cover and gallery images to a listing and the public detail
page renders them.

- MediaAsset: canonical for asset facts (bytes, dimensions, status,
  storage key, provenance). Listing.media is a projection rebuilt from
  the ready rows on every attach/detach.
- directory.storage.get_tenant_storage resolves the backend per request
  from tenant.settings["storage"]. Local disk only; s3/azure/gcp raise
  DeferredFeatureError (#150).
- directory.media.attach_media / detach_media: validate, orient, strip
  EXIF and re-encode with Pillow in-request (#151); no worker exists, so
  derivatives and abuse scanning are out of scope and no seam is left.
  MediaAsset.status carries an unused "quarantined" value.
- Both operations emit listing.updated only; media.* stays deferred
  (spec 3.4). Gallery changes are a whole-array replace at /media/gallery.
- media is a rejected key on listing.upsert (spec 7.1 v0.7): 422 naming
  the field when present, even null.
- Tenant-scoped /media/<asset_id> view streams local assets; "media" is a
  reserved slug.
- One migration (0005_mediaasset). pillow==11.3.0 pinned.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4DdoL1tjw53aVyHpwo4L3
Review follow-ups on the media PR.

- media_asset view now filters listing__visibility=PUBLISHED, so a draft
  or hidden listing's images are not publicly retrievable by asset id
  (decisions.md §4.1, ruling 13). Behavioural 404 test added.
- PublishedGuardTests widened: it walked only `Listing.objects.*` and so
  never saw the media view querying MediaAsset. It now AST-walks every
  `<Model>.objects` in public_views.py and requires a published()/
  visibility constraint on any model carrying visibility or a listing FK.
  Verified it fails on the pre-fix view.
- Media ref `url` is now absolute `https://<primary_domain>/media/<id>`
  once the tenant's domain is verified, relative until then. This value
  is written into the durable listing.updated outbox payload, where a
  relative path is unresolvable off-box. Scheme is always https and does
  not branch on DEBUG / OSDS_SECURE_COOKIES. decisions.md §4.1 row added.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4DdoL1tjw53aVyHpwo4L3
@OriginDevIT
OriginDevIT merged commit bc90ef8 into main Sep 8, 2026
1 check passed
@OriginDevIT
OriginDevIT deleted the feat/listing-media branch September 8, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adding Pillow as a runtime dependency

1 participant