Swagger inferred these SerializerMethodFields as untyped strings and
warned on generation; annotate each with @extend_schema_field so the
generated OpenAPI schema declares them as nullable URLs.
The prior commit wired MinIO as Django's default storage backend but
kept plain ImageField multipart uploads through the Django backend and
public-bucket direct URLs — not how campaign/advertising/promotions do
it. Rework to match: image fields (ProductCategory.icon, Product.image,
StoreCategory.icon, Store.logo, Store.cover_image) now store an opaque
MinIO object_key (CharField) instead of a Django-managed file.
- utils/clients/minio_client.py — raw Minio SDK client, same shape as
the other services.
- apps/core/views/media.py — POST /api/media/presign/ mints a 30-minute
presigned PUT URL + object_key; the client uploads bytes directly to
MinIO, then submits the object_key on the owning resource.
- apps/core/media.py — presigned_media_url() helper; every serializer
with an image field now also exposes a `<field>_url` computed from a
fresh 1-hour presigned GET, rather than trusting a stored/direct URL.
- FRONTEND_GUIDE.md updated to document the new upload flow and field
shapes (object_key vs `_url`), replacing the stale "no upload
endpoint yet" note.
Verified against a live local MinIO container: presign → direct PUT
upload → object_key stored on the model → serializer mints a working
presigned GET URL → URL fetches the uploaded bytes. Also exercised the
actual DRF view (POST /api/media/presign/) end-to-end, including the
401 on an unauthenticated request.
Swaps default media storage from local FileSystemStorage to MinIO via
django-minio-backend, matching the rest of the Winsoo ecosystem
(advertising, campaign, wallet). Points at a local MinIO container
(added to docker-compose.yml) by default since no shared bucket is
provisioned yet. Existing ImageFields pick up the new storage backend
automatically — no model changes needed.
Verified against a live local MinIO container: bucket auto-creates on
first save, object lands under the expected key, URL resolves.
Django 6 + DRF resource server against the Gooyal accounts OAuth2 service,
matching the Winsoo ecosystem's conventions. Covers locations, stores,
catalog, cart, checkout/orders (with the multi-store-cart split and the
status stepper), reviews, and notifications, plus a demo-data seed command.
Payment integration (wallet debits, online gateway, seller payouts) is
intentionally left out here — see feature/payment.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>