diff --git a/docs/sandbox-mode.md b/docs/sandbox-mode.md index 033679a..93462cb 100644 --- a/docs/sandbox-mode.md +++ b/docs/sandbox-mode.md @@ -292,18 +292,67 @@ preview continues to work normally. | `mps_sandbox_panel` | "0" or "1" | Panel open/closed | | `mps_sandbox_mode` | "preset" or "custom" | Filter mode | +## Artifact Storage (User S3 Bucket) + +Users can configure their own S3-compatible bucket to upload sandbox artifacts. + +``` +User Settings (/u/settings) + │ + │ Artifact Storage form: + │ - S3 Endpoint URL + │ - Region (optional) + │ - Bucket Name + │ - Access Key + │ - Secret Key + │ + ▼ +POST /u/settings/storage + │ + │ Saves credentials on User model + │ (s3_endpoint, s3_region, s3_bucket, + │ s3_access_key, s3_secret_key) + │ + ▼ +Sandbox toolbar shows "Upload to Bucket" button + │ (only when data-has-bucket="1") + │ + │ 1. User exports image/video (download) + │ 2. User clicks "Upload to Bucket" + │ 3. JS POSTs to /u/sandbox/upload + │ 4. Server generates presigned POST with user's credentials + │ 5. JS uploads blob directly to user's S3 bucket + │ + ▼ +User's S3 Bucket + sandbox/{user_id}/{timestamp}-{filename} +``` + +**Supported services:** DigitalOcean Spaces, AWS S3, MinIO, Backblaze B2, +any S3-compatible endpoint. + +**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. + ## Files | File | Role | |------|------| | `models/shop.py` | `sandbox_mode` Boolean column | +| `models/user.py` | S3 credential columns + `has_s3_bucket` property | | `views/shop.py` | Toggle handler in ribbon-settings | +| `views/user.py` | Storage settings POST handler + S3 fields in settings dict | +| `views/user_sandbox.py` | Presigned upload endpoint (`/u/sandbox/upload`) | | `templates/shop_settings.j2` | Radio buttons for enable/disable | -| `templates/base.j2` | Conditional toolbar HTML + SVG defs + script tag | -| `static/js/sandbox.js` | Client-side filter engine | +| `templates/user_settings.j2` | Artifact Storage form | +| `templates/base.j2` | Conditional toolbar HTML + SVG defs + `data-has-bucket` | +| `static/js/sandbox.js` | Client-side filter engine + upload-to-bucket | | `static/css/common.css` | Toolbar layout (CSS Grid) | | `static/js/watch.js` | `sandboxReapply()` hook in SPA navigation | -| `scripts/alembic/versions/a2c13d3117f2_*.py` | Migration | +| `routes.py` | `user_storage_settings`, `user_sandbox_upload` routes | +| `scripts/alembic/versions/a2c13d3117f2_*.py` | Shop sandbox_mode migration | +| `scripts/alembic/versions/f898ba460612_*.py` | User S3 credentials migration | ## Mobile Behavior