12 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 17-object-storage | 02 | execute | 2 |
|
|
true |
|
|
Purpose: Replace every Bun.write, unlink, and /uploads/ reference on the server with storage service calls. Enrich API responses with presigned URLs so clients can fetch images directly from MinIO. Output: All server image operations go through storage.service.ts. API responses include imageUrl field. Static /uploads/* serving removed.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/17-object-storage/17-CONTEXT.md @.planning/phases/17-object-storage/17-RESEARCH.md @.planning/phases/17-object-storage/17-01-SUMMARY.md@src/server/services/image.service.ts @src/server/routes/images.ts @src/server/routes/items.ts @src/server/routes/threads.ts @src/server/index.ts @src/server/mcp/tools/images.ts @src/server/mcp/tools/items.ts @src/server/mcp/tools/threads.ts
From src/server/services/storage.service.ts: ```typescript export async function uploadImage(buffer: Buffer | ArrayBuffer, filename: string, contentType: string): Promise; export async function deleteImage(filename: string): Promise; export async function getImageUrl(filename: string): Promise; export async function withImageUrl(record: T): Promise; export async function withImageUrls(records: T[]): Promise<(T & { imageUrl: string | null })[]>; ``` Task 1: Refactor image service and image routes to use storage service src/server/services/image.service.ts, src/server/routes/images.ts, tests/services/image.service.test.ts, tests/routes/images.test.ts - src/server/services/storage.service.ts (created in Plan 01 — the storage API) - src/server/services/image.service.ts (current local fs logic to replace) - src/server/routes/images.ts (current upload routes) - tests/services/image.service.test.ts (existing tests to update) - tests/routes/images.test.ts (existing tests to update) 1. Refactor `src/server/services/image.service.ts` per D-07: - Remove `mkdir` and `Bun.write` imports - Remove `uploadsDir` parameter from `fetchImageFromUrl` - Import `uploadImage` from `./storage.service` - After fetching and validating the image buffer, call `await uploadImage(Buffer.from(buffer), filename, contentType)` instead of `Bun.write` - Keep ALL validation logic unchanged (URL parsing, protocol check, content type, size limits, timeout) - Keep UUID filename generation unchanged per D-12-
Refactor
src/server/routes/images.tsper D-06:- Remove
mkdir,join,Bun.writeusage - Import
uploadImagefrom../services/storage.service - In POST
/handler: after validation, callawait uploadImage(Buffer.from(buffer), filename, file.type)instead of mkdir + Bun.write - In POST
/from-urlhandler: no changes needed (delegates to image.service.ts which is already refactored) - Keep content type validation and size validation unchanged
- Keep filename generation pattern unchanged
- Remove
-
Update
tests/services/image.service.test.ts:- Mock the storage.service module using
mock.modulesouploadImageis a mock function - Update assertions: verify uploadImage was called with correct buffer, filename, contentType instead of checking Bun.write
- Remove any assertions about local filesystem writes
- Mock the storage.service module using
-
Update
tests/routes/images.test.ts:- Mock storage.service module
- Update assertions to verify uploadImage calls instead of filesystem writes
- Test that POST /api/images returns { filename } with 201 status
- Test that POST /api/images/from-url returns { filename, sourceUrl } with 201 status bun test tests/services/image.service.test.ts tests/routes/images.test.ts <acceptance_criteria>
- grep -q "import.*uploadImage.*storage" src/server/services/image.service.ts
- grep -qv "Bun.write" src/server/services/image.service.ts
- grep -qv "mkdir" src/server/services/image.service.ts
- grep -q "import.*uploadImage.*storage" src/server/routes/images.ts
- grep -qv "Bun.write" src/server/routes/images.ts
- grep -qv "mkdir" src/server/routes/images.ts
- bun test tests/services/image.service.test.ts passes
- bun test tests/routes/images.test.ts passes </acceptance_criteria> Image service and routes use storage service for uploads. No local filesystem writes remain. Tests pass with mocked storage.
-
Refactor
src/server/routes/threads.tsper D-08, D-09:- Remove
unlinkandjoinimports related to uploads - Import
deleteImage,withImageUrl,withImageUrlsfrom../services/storage.service - On thread delete (where candidate images are cleaned up): replace
unlink(join("uploads", filename))withawait deleteImage(filename)in the loop - On candidate delete: replace
unlink(join("uploads", deleted.imageFilename))withawait deleteImage(deleted.imageFilename) - On GET thread with candidates: enrich candidate records with
withImageUrls()before returning - On GET thread list: if threads include image data, enrich accordingly
- Remove
-
Refactor
src/server/routes/setups.tsper D-09:- Import
withImageUrlsfrom../services/storage.service - On GET setup detail (which includes items with imageFilename): enrich the items array with
withImageUrls()before returning - On GET setup list: if list includes items with images, enrich accordingly
- Import
-
Update
src/server/index.tsper D-08:- Remove the line
app.use("/uploads/*", serveStatic({ root: "./" }))entirely - Remove
serveStaticimport if no longer used elsewhere (check — it IS still used for production SPA serving) - Actually:
serveStaticis still used for SPA serving in production. Only remove the/uploads/*line.
- Remove the line
-
Update MCP tools per D-09:
src/server/mcp/tools/items.ts: After getting items from service, enrich withwithImageUrl/withImageUrlsbefore returning in tool responsesrc/server/mcp/tools/threads.ts: After getting thread with candidates, enrich candidate images withwithImageUrlssrc/server/mcp/tools/images.ts: No changes needed if it calls fetchImageFromUrl (already refactored in Task 1). Verify it does not reference local filesystem directly. grep -rn "unlink.*uploads|Bun.write.*uploads|/uploads/" src/server/ | grep -v node_modules | grep -v ".test." && echo "FAIL: still has uploads references" || echo "PASS: no uploads references in server" <acceptance_criteria>- grep -qv "unlink.*uploads" src/server/routes/items.ts
- grep -q "deleteImage" src/server/routes/items.ts
- grep -q "withImageUrl" src/server/routes/items.ts
- grep -qv "unlink.*uploads" src/server/routes/threads.ts
- grep -q "deleteImage" src/server/routes/threads.ts
- grep -qv 'uploads/*.*serveStatic' src/server/index.ts
- grep -q "withImageUrl" src/server/mcp/tools/items.ts
- No remaining references to "unlink.uploads" or "/uploads/" in src/server/ (excluding test files) </acceptance_criteria> All server routes use storage service for image deletion and URL generation. Static /uploads/ serving removed. MCP tools return presigned URLs. Zero local filesystem image references remain in server code.
<success_criteria>
- All image uploads go through storage.service.ts (no Bun.write to uploads/)
- All image deletions go through storage.service.ts (no unlink of uploads/)
- All API responses with images include imageUrl presigned URL field
- Static /uploads/* serving removed from server
- MCP tools return presigned URLs
- All existing image-related tests pass with mocked storage </success_criteria>