Cloudflare R2
Overview
Section titled “Overview”Cloudflare R2 provides S3-compatible object storage for:
- Unit images (floor plans, exterior, interior, site plans)
- Agent avatars
- Brochures and exposes
- Energy certificates
- Portal logos
- Backups
Architecture
Section titled “Architecture”Files are stored in private R2 buckets. The server never proxies file bytes — browsers interact with R2 directly via presigned URLs:
- Upload: Client requests a presigned PUT URL from the API → client uploads directly to R2 → client confirms upload to server
- Download: Client requests a presigned GET URL (with TTL) → client fetches directly from R2
R2 Client
Section titled “R2 Client”The S3 client is configured in src/lib/r2/client.ts:
import { S3Client } from "@aws-sdk/client-s3";
const client = new S3Client({ region: "auto", endpoint: `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { accessKeyId: R2_ACCESS_KEY_ID, secretAccessKey: R2_SECRET_ACCESS_KEY, }, requestHandler: new FetchHttpHandler(), // Works in Workers});Presigned URL Helpers
Section titled “Presigned URL Helpers”| Function | Module | Purpose |
|---|---|---|
presignPut(key, contentType) | src/lib/r2/presign.ts | Get upload URL |
presignGet(key, ttlSeconds) | src/lib/r2/presign.ts | Get download URL |
headObject(key) | src/lib/r2/presign.ts | Check object existence |
Buckets
Section titled “Buckets”| Bucket | Purpose | Access |
|---|---|---|
convertella-documents | Unit images, documents, brochures | Private (presigned URLs) |
convertella-backups | Database and file backups | Private (operator only) |
Lock-in Mitigation
Section titled “Lock-in Mitigation”R2 is accessed via the S3 SDK over HTTPS, not R2 Workers bindings. This means switching to S3, Backblaze B2, or Tigris is just a configuration change:
- Change
endpointURL - Update credentials
- No code changes needed
Directus Integration (Legacy)
Section titled “Directus Integration (Legacy)”On main, Directus manages file uploads (stored on disk or in R2 via Directus S3 adapter). The file_id column in unit_images references Directus file IDs. On spike/cf-runtime, the r2_key column is used instead (migration 0018).