Upload API
Base path: /api/upload
File: ayts-api/src/routes/upload.ts
Auth required: Yes
Storage: Cloudflare R2 bucket ayts
POST /api/upload/image
Upload a single image to R2.
Request: multipart/form-data
file: {image file}
Accepted MIME types:
image/jpegimage/pngimage/webpimage/gif
Max file size: 5 MB
Response 200:
{
"success": true,
"url": "https://pub-cfc3656f8fe64c31862884b05a8159ad.r2.dev/images/uuid-filename.jpg",
"key": "images/uuid-filename.jpg"
}
Response 400: Invalid file type or size exceeded.
POST /api/upload/bulk
Upload multiple images at once (product gallery).
Request: multipart/form-data
files: {image1, image2, image3}
Response 200:
{
"success": true,
"urls": [
"https://pub-cfc3656f8fe64c31862884b05a8159ad.r2.dev/...",
"https://pub-cfc3656f8fe64c31862884b05a8159ad.r2.dev/..."
]
}
R2 Configuration
# wrangler.toml
[[r2_buckets]]
binding = "AYTS_STORAGE_BUCKET"
bucket_name = "ayts"
The public R2 URL prefix: https://pub-cfc3656f8fe64c31862884b05a8159ad.r2.dev
note
The R2 bucket must exist in your Cloudflare dashboard. Verify at: Cloudflare Dashboard → R2 → Buckets → ayts
Image Processing
Uploaded images are processed by Sharp before storage:
- Resize to max 1200px wide (maintaining aspect ratio)
- Convert to WebP for smaller file size
- Strip EXIF metadata for privacy
Frontend Upload Component
The SingleMediaUpload component in ayts-admin/components/single-media-upload.tsx handles the upload flow:
- User drags/drops or selects image
- Client validates file type + size
POST /api/upload/image- On success, the returned URL is saved to the form field
- Form submits with the R2 URL
// Example usage
<SingleMediaUpload
value={imageUrl}
onChange={(url) => setValue('imageUrl', url)}
label="Product Image"
/>