- MPS-6: referrer analytics (domain, query, trend line charts) - MPS-7: sandbox mode creative filter system - MPS-8: user S3 bucket + artifact storage - MPS-9: shop S3 mirror bucket - architecture.md: system diagram, request flow, data pipeline, S3 layout - JAVASCRIPT.md: add sandbox.js, signals.js, MediaPipe SDK entries - sandbox-mode.md: mark S3 upload as implemented - mps-2.md: document referrer_domain + referrer_query columns Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.6 KiB
MPS-8: User S3 Bucket + Artifact Storage
Problem
Sandbox mode (MPS-7) lets visitors export filtered images and videos, but exports download to the user's local device. Creators want a way to automatically upload sandbox artifacts to their own cloud storage — a personal S3 bucket where filtered content accumulates without manual file management.
Solution
User-level S3 credentials
Add S3-compatible storage credentials to the User model (not Shop — a user may own multiple shops but uses one storage bucket):
| Column | Type | Example |
|---|---|---|
s3_endpoint |
Unicode(256) |
"https://nyc3.digitaloceanspaces.com" |
s3_region |
Unicode(64) |
"nyc3" |
s3_bucket |
Unicode(128) |
"my-sandbox-exports" |
s3_access_key |
Unicode(128) |
"DO00..." |
s3_secret_key |
Unicode(128) |
"wJalr..." |
Property has_s3_bucket returns True when endpoint, bucket, access_key, and
secret_key are all non-empty.
Storage settings form
New section on user settings page (/u/settings) with an "Artifact Storage"
form that POSTs to /u/settings/storage. Fields: endpoint URL, region
(optional), bucket name, access key, secret key (password field).
Presigned upload endpoint
POST /u/sandbox/upload — accepts filename and content_type parameters,
generates a presigned POST using the user's stored S3 credentials. Returns
{presigned: {url, fields}, key}. The browser uploads the blob directly to the
user's bucket — the file never touches the MPS server.
S3 key format: sandbox/{user_id}/{timestamp}-{filename}
Upload limit: 50MB per file (enforced via presigned POST conditions).
Toolbar integration
When a user has has_s3_bucket = True, the sandbox toolbar in base.j2
renders with data-has-bucket="1". The sandbox.js upload-to-bucket button
appears only when this attribute is present. The upload flow:
- User exports image/video (canvas toBlob)
- User clicks "Upload to Bucket"
- JS POSTs to
/u/sandbox/uploadwith filename + content_type - Server returns presigned POST URL + fields
- JS uploads blob directly to user's S3 bucket via FormData POST
Supported services
Any S3-compatible endpoint: DigitalOcean Spaces, AWS S3, MinIO, Backblaze B2, Cloudflare R2.
Security
Credentials stored as plain Unicode columns (same pattern as Stripe/PayPal/Adyen
keys on Shop model). Secret key field uses type="password" in the form.
Credentials are only used server-side to generate presigned URLs — they are
never exposed to the browser.
Shop-Level S3 Mirror (MPS-9)
A separate but related feature: shops can configure their own S3 bucket as a mirror of the MPS main bucket. Every file uploaded to a shop (product files, thumbnails, shop assets) is automatically copied to the shop's mirror bucket in a background thread. See MPS-9.
Files Changed
| File | Change |
|---|---|
models/user.py |
S3 credential columns + has_s3_bucket property |
views/user.py |
Storage settings POST handler + S3 fields in settings dict |
views/user_sandbox.py |
Presigned upload endpoint |
templates/user_settings.j2 |
Artifact Storage form |
templates/base.j2 |
data-has-bucket attribute on sandbox toolbar |
static/js/sandbox.js |
Upload-to-bucket button + presigned POST flow |
routes.py |
user_storage_settings, user_sandbox_upload routes |
scripts/alembic/versions/f898ba460612_*.py |
Migration: user S3 columns |
tests/test_functional.py |
S3 bucket save/clear, presigned upload, toolbar attribute tests |
Depends On
MPS-7 (sandbox mode — toolbar must exist to add upload button)