4.8 KiB
4.8 KiB
Phase 17: Object Storage - Context
Gathered: 2026-04-05 Status: Ready for planning
## Phase BoundaryMove image storage from local filesystem (uploads/ directory) to MinIO (S3-compatible object storage). All image uploads go to MinIO. Existing images are migrated. Image URLs are served via presigned URLs or a proxy. Docker Compose includes MinIO for local development.
S3 Client
- D-01: Use
@aws-sdk/client-s3(AWS SDK v3) for MinIO communication. Works with any S3-compatible service. Tree-shakeable, well-maintained. - D-02: Use
@aws-sdk/s3-request-presignerfor generating presigned URLs.
Storage Service
- D-03: Create
src/server/services/storage.service.ts— thin wrapper around S3 SDK with functions:uploadImage(buffer, filename, contentType),deleteImage(filename),getImageUrl(filename). - D-04:
getImageUrl()returns a presigned URL with configurable expiry (default 1 hour). No proxy — client fetches directly from MinIO. - D-05: Environment variables:
S3_ENDPOINT,S3_ACCESS_KEY,S3_SECRET_KEY,S3_BUCKET(default:gearbox-images),S3_REGION(default:us-east-1).
Image Upload Changes
- D-06:
POST /api/imagesandPOST /api/images/from-urlupload to MinIO instead of local filesystem. Same filename pattern (UUID-based). - D-07:
fetchImageFromUrl()in image.service.ts uploads the fetched buffer to MinIO instead of writing to disk. - D-08: Remove the static file serving for
/uploads/*from the server — images are served via presigned URLs from MinIO.
Image URL Serving
- D-09: When returning item/candidate data with
imageFilename, the API resolves it to a presigned MinIO URL. Add aimageUrlfield to API responses (or replaceimageFilenamewith the URL). - D-10: Client components use the presigned URL directly. No changes to image display components beyond using the URL field.
Migration
- D-11: One-time migration script (
scripts/migrate-images-to-minio.ts) reads all files fromuploads/, uploads each to MinIO bucket, verifies upload, logs progress. - D-12: No filename changes during migration — existing
imageFilenamevalues in the database remain valid as MinIO object keys.
Docker Compose
- D-13: MinIO service in docker-compose.yml (both dev and prod) with automatic bucket creation on startup.
- D-14: Dev compose uses fixed credentials for simplicity. Prod compose uses env vars.
Claude's Discretion
- Presigned URL expiry duration (1h default, configurable)
- Whether to add a GET /api/images/:filename proxy endpoint as fallback
- MinIO Docker image version
- Bucket policy (private with presigned URLs vs public-read)
- Whether to delete local files after successful migration
- Error handling strategy for upload failures
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Image Handling (to be refactored)
src/server/services/image.service.ts— Current local file storage logicsrc/server/routes/images.ts— Upload endpoints (file + URL)src/server/index.ts— Static file serving for uploads/
Schema (imageFilename columns)
src/db/schema.ts—imageFilenameon items and threadCandidates tables
Docker
docker-compose.yml— Production compose (add MinIO)docker-compose.dev.yml— Dev compose (add MinIO).env.example— Add S3 env vars
Client (image display)
src/client/lib/api.ts— API wrappersrc/client/components/— Components that display images
Requirements
.planning/REQUIREMENTS.md— IMG-01 through IMG-04
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
image.service.ts— refactor to use storage service instead of local fs- UUID filename generation pattern — keep as-is for MinIO object keys
- Content type validation — keep in image routes
Established Patterns
- Service DI pattern (db, userId params) — storage service is stateless, no db needed
- Async operations — all storage ops will be async (already async pattern)
- Environment config via
process.env— same for S3 config
Integration Points
src/server/routes/images.ts— Replace Bun.write with storage.uploadsrc/server/services/image.service.ts— Replace Bun.write with storage.uploadsrc/server/index.ts— Remove static file serving for uploads/- API response serialization — add imageUrl field from presigned URL
</code_context>
## Specific IdeasNo specific requirements — open to standard approaches for S3-compatible object storage with MinIO.
## Deferred IdeasNone — discussion stayed within phase scope
Phase: 17-object-storage Context gathered: 2026-04-05