Asset service
Storage is a separate service. It is the only thing that holds AWS credentials or knows the bucket layout; everything else deals in ids and urls.
POST /assets?folder=products multipart "file" -> { id, url, contentType, size }
GET /assets?folder=&limit= list (media library)
GET /assets/:id metadata
GET /assets/:id/raw 302 to the CDN
DELETE /assets/:id removes the object and the record
Interactive docs: http://localhost:3003/docs (OpenAPI JSON at
/docs/json). The upload form there accepts a real file, so the service can
be exercised without the admin app. Swagger is mounted outside production
only — set SWAGGER=true to force it on.
Upload flow
admin --file--> asset service --> S3
<--{id,url}--
admin --{assetId,url}--> API --> stores the reference
storefront <-- url
The API never sees the bytes and has no AWS SDK dependency. product_images
and categories store asset_id + url; when a reference is replaced and
no other row points at the old asset, the API asks the asset service to
delete it.