mirror of
https://github.com/saltbo/zpan.git
synced 2026-09-24 23:22:31 +08:00
On Cloudflare Workers deployments with PUBLIC_IMAGES (R2 binding) +
PUBLIC_IMAGES_URL (env) configured, writes go through the R2 binding
directly — zero auth overhead, zero egress cost, no AWS SDK in the hot
path. Reads are served straight from R2's managed public domain; no
Worker round-trip per image.
Non-CF deployments (Node/Docker/Lambda/Vercel/etc.) keep the existing
behavior: select the DB-configured mode='public' storage row and use
S3Service. The only requirement is that users still add a public
storage via Admin → Storages.
`uploadPublicImage(platform, prefix, id, file)` and
`deletePublicImageVariants(platform, prefix, id)` now take a Platform
rather than a bare Database. They internally pick the backend:
getBackend:
if getBinding('PUBLIC_IMAGES') && getEnv('PUBLIC_IMAGES_URL')
→ R2 backend
else if selectStorage(db, 'public') succeeds
→ S3 backend
else
→ none (returns 503)
The R2Bucket type is declared locally (minimal structural shape) so we
avoid pulling @cloudflare/workers-types into non-CF builds.
12 new unit tests in image-upload.test.ts cover both backends —
mime/size validation, URL construction (including trailing-slash
normalization + jpeg→jpg extension), delete-all-variants, and the
fallback precedence matrix (binding alone / URL alone / neither →
expected result).
Callers updated:
- server/routes/me.ts — PUT/DELETE /avatar
- server/routes/teams.ts — PUT/DELETE /:teamId/logo
Existing integration tests (54 cases across me + teams) continue to
pass via the S3 fallback path — they use mockPlatform without a binding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>