Three independent feature specs covering: - API endpoint for fetching images from URLs with local storage - Public-read/authenticated-write auth with sessions and API keys - Built-in MCP server for Claude Code/Desktop integration Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
88 lines
2.2 KiB
Markdown
88 lines
2.2 KiB
Markdown
# Image URL Fetching — Design Spec
|
|
|
|
## Overview
|
|
|
|
Add the ability to fetch images from external URLs via the API, download them to local storage, and preserve the original source URL as metadata. API-only feature — no frontend changes.
|
|
|
|
## New Endpoint
|
|
|
|
### `POST /api/images/from-url`
|
|
|
|
**Request:**
|
|
|
|
```json
|
|
{ "url": "https://example.com/photo.jpg" }
|
|
```
|
|
|
|
**Validation (Zod):**
|
|
|
|
- `url` — valid URL string, required
|
|
|
|
**Server-side behavior:**
|
|
|
|
1. Fetch the URL with a 10-second timeout
|
|
2. Check response `Content-Type` is one of: `image/jpeg`, `image/png`, `image/webp`
|
|
3. Check `Content-Length` does not exceed 5MB (match existing upload limit)
|
|
4. Stream response body to `uploads/` directory using existing naming: `${Date.now()}-${randomUUID()}.${ext}`
|
|
5. If any check fails, return 400 with descriptive error
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{ "filename": "1712160000000-abc123.jpg", "sourceUrl": "https://example.com/photo.jpg" }
|
|
```
|
|
|
|
Callers can use `filename` for `imageFilename` and `sourceUrl` for `imageSourceUrl` when creating/updating items or candidates.
|
|
|
|
**Error responses:**
|
|
|
|
- `400` — invalid URL, unsupported content type, file too large, fetch failed
|
|
- `500` — server error during download/save
|
|
|
|
## Schema Changes
|
|
|
|
### `items` table
|
|
|
|
Add column:
|
|
|
|
```
|
|
imageSourceUrl: text("image_source_url") // nullable
|
|
```
|
|
|
|
### `threadCandidates` table
|
|
|
|
Add column:
|
|
|
|
```
|
|
imageSourceUrl: text("image_source_url") // nullable
|
|
```
|
|
|
|
### Zod schemas
|
|
|
|
Add `imageSourceUrl: z.string().url().optional()` to:
|
|
|
|
- `createItemSchema`
|
|
- `updateItemSchema`
|
|
- `createCandidateSchema`
|
|
- `updateCandidateSchema`
|
|
|
|
### Types
|
|
|
|
Types are inferred from Zod schemas and Drizzle tables — no manual updates needed.
|
|
|
|
## Existing Behavior Unchanged
|
|
|
|
- `POST /api/images` (file upload) remains as-is
|
|
- All existing image display, cleanup, and serving logic unchanged
|
|
- `imageFilename` continues to work identically
|
|
|
|
## Test Helper Updates
|
|
|
|
Add `image_source_url TEXT` column to the `items` and `thread_candidates` CREATE TABLE statements in `tests/helpers/db.ts`.
|
|
|
|
## Testing
|
|
|
|
- Service test: fetch from a valid URL, verify file saved and filename returned
|
|
- Route test: POST to `/api/images/from-url` with valid/invalid URLs
|
|
- Validation tests: wrong content type, oversized image, invalid URL format, timeout
|