The queue a member works through, and the queue an admin reviews

FeatureMediaService
Shipped
September 7, 2026 at 5:07 AM UTC
Author
Kamo
Commit
f1be8b9

MediaService's Growth Hub surface at /api/media/marketing/growth. It owns this for the same reason it owns tracking, the scoreboard, materials and Canva — and because APIApp already forwards /api/media/**, so there is no gateway work. Submitting moves the member on IMMEDIATELY: the pending request leaves their queue and only comes back if it is rejected. That is what stops one slow reviewer blocking every member's whole queue, and it is why the queue is DERIVED from each member's latest submission rather than stored. Nine tests pin it from every side, because each way of getting it wrong renders a perfectly convincing widget: reading the first submission keeps handing somebody work they have already redone; treating PENDING as "in the queue" is the blockage this design exists to avoid; treating REJECTED as done loses the correction and the member never learns anything came back. The uploader is raster-only and SVG is refused, which is the assertion worth reading twice. An SVG is an XML document that can carry script, and a request's artwork is served back inline to every member of the organisation on their home page — so accepting one would turn "upload an image" into a way to run code on every colleague's launchpad. normalizeUrl refuses javascript:/data:/vbscript: for the same reason at the other end: that field becomes a link every member is invited to click. Evidence is readable by a reviewer or by the member who uploaded it, and by nobody else — a colleague's screenshot is a picture of their account on someone else's site. Anyone else gets 404 rather than 403, so the refusal is not an oracle for which submission ids exist. PATCH merges the raw JSON body, never a bound entity, which would silently reset every field the dialog did not send. create_growth_hub.sql is the DDL, applied by hand — InitializerService is not deployed, so a new @Entity creates nothing and the service fails at the first query. It records the pg_constraint check for the two appended ImageAssocType ordinals too, because "we checked" is the part that gets skipped.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing