diff --git a/banatie-api-requirements.md b/banatie-api-requirements.md new file mode 100644 index 0000000..55a517e --- /dev/null +++ b/banatie-api-requirements.md @@ -0,0 +1,840 @@ +# Banatie REST API Implementation Plan + +**Version:** 2.0 +**Status:** Ready for Implementation +**Executor:** Claude Code +**Database Schema:** v2.0 (banatie-database-design.md) + +--- + +## Overview + +REST API for Banatie image generation service. All endpoints use `/api/v1/` prefix for versioning. + +**Core Features:** +- AI image generation with Google Gemini Flash +- Dual alias system (project-scoped + flow-scoped) +- Technical aliases (@last, @first, @upload) +- Flow-based generation chains +- Live generation endpoint with caching +- Upload and reference images + +**Authentication:** API keys only (`bnt_` prefix) + +--- + +## Authentication + +All endpoints require API key in header: + +``` +X-API-Key: bnt_xxx... +``` + +**API Key Types:** +- `master`: Full access to all projects in organization +- `project`: Access to specific project only + +**Unauthorized Response (401):** +```json +{ + "error": "Unauthorized", + "message": "Invalid or missing API key" +} +``` + +--- + +## Implementation Phases + +### Phase 1: Foundation +**Goal:** Core utilities and services + +**Tasks:** +- Create TypeScript type definitions for all models +- Build validation utilities (alias format, pagination, query params) +- Build helper utilities (pagination, hash, query helpers) +- Create `AliasService` with 3-tier resolution (technical → flow → project) + +**Git Commit:** +``` +feat: add foundation utilities and alias service +``` + +--- + +### Phase 2: Core Generation Flow +**Goal:** Main generation endpoints + +**Services:** +- `ImageService` - CRUD operations with soft delete +- `GenerationService` - Full lifecycle management + +**Endpoints:** +- `POST /api/v1/generations` - Create with reference images & dual aliases +- `GET /api/v1/generations` - List with filters +- `GET /api/v1/generations/:id` - Get details with related data + +**Git Commit:** +``` +feat: implement core generation endpoints +``` + +--- + +### Phase 3: Flow Management +**Goal:** Flow operations + +**Services:** +- `FlowService` - CRUD with computed counts & alias management + +**Endpoints:** +- `POST /api/v1/flows` - Create flow +- `GET /api/v1/flows` - List flows with computed counts +- `GET /api/v1/flows/:id` - Get details with generations and images +- `PUT /api/v1/flows/:id/aliases` - Update flow aliases +- `DELETE /api/v1/flows/:id/aliases/:alias` - Remove specific alias +- `DELETE /api/v1/flows/:id` - Delete flow + +**Git Commit:** +``` +feat: implement flow management endpoints +``` + +--- + +### Phase 4: Enhanced Image Management +**Goal:** Complete image operations + +**Endpoints:** +- `POST /api/v1/images/upload` - Upload with alias, flow, metadata +- `GET /api/v1/images` - List with filters +- `GET /api/v1/images/:id` - Get details with usage info +- `GET /api/v1/images/resolve/:alias` - Resolve alias with precedence +- `PUT /api/v1/images/:id` - Update metadata +- `DELETE /api/v1/images/:id` - Soft/hard delete + +**Git Commit:** +``` +feat: implement image management endpoints +``` + +--- + +### Phase 5: Generation Refinements +**Goal:** Additional generation operations + +**Endpoints:** +- `POST /api/v1/generations/:id/retry` - Retry failed generation +- `DELETE /api/v1/generations/:id` - Delete generation + +**Git Commit:** +``` +feat: add generation retry and delete endpoints +``` + +--- + +### Phase 6: Live Generation +**Goal:** URL-based generation with caching + +**Services:** +- `PromptCacheService` - SHA-256 caching with hit tracking + +**Endpoints:** +- `GET /api/v1/live` - Generate image via URL with streaming proxy + +**Important:** Stream image directly from MinIO (no 302 redirect) for better performance. + +**Git Commit:** +``` +feat: implement live generation endpoint with caching +``` + +--- + +### Phase 7: Analytics +**Goal:** Project statistics and metrics + +**Services:** +- `AnalyticsService` - Aggregation queries + +**Endpoints:** +- `GET /api/v1/analytics/summary` - Project statistics +- `GET /api/v1/analytics/generations/timeline` - Time-series data + +**Git Commit:** +``` +feat: add analytics endpoints +``` + +--- + +### Phase 8: Testing & Documentation +**Goal:** Quality assurance + +**Tasks:** +- Unit tests for all services (target >80% coverage) +- Integration tests for critical flows +- Error handling consistency review +- Update API documentation + +**Git Commit:** +``` +test: add comprehensive test coverage and documentation +``` + +--- + +## API Endpoints Specification + +### GENERATIONS + +#### POST /api/v1/generations + +Create new image generation. + +**Request Body:** +```typescript +{ + prompt: string; // Required: 1-2000 chars + aspectRatio?: string; // Optional: '16:9', '1:1', '4:3', '9:16' + width?: number; // Optional: 1-8192 + height?: number; // Optional: 1-8192 + referenceImages?: string[]; // Optional: ['@logo', '@product', '@last'] + flowId?: string; // Optional: Add to existing flow + assignAlias?: string; // Optional: Project-scoped alias '@brand' + assignFlowAlias?: string; // Optional: Flow-scoped alias '@hero' (requires flowId) + meta?: Record; +} +``` + +**Response (200):** +```typescript +{ + generation: Generation; + image?: Image; // If generation completed +} +``` + +**Errors:** 400, 401, 404, 422, 429, 500 + +--- + +#### GET /api/v1/generations + +List generations with filtering. + +**Query Params:** +```typescript +{ + flowId?: string; + status?: 'pending' | 'processing' | 'success' | 'failed'; + limit?: number; // Default: 20, max: 100 + offset?: number; // Default: 0 + sortBy?: 'createdAt' | 'updatedAt'; + order?: 'asc' | 'desc'; // Default: desc +} +``` + +**Response (200):** +```typescript +{ + generations: Generation[]; + pagination: PaginationInfo; +} +``` + +--- + +#### GET /api/v1/generations/:id + +Get generation details. + +**Response (200):** +```typescript +{ + generation: Generation; + image?: Image; + referencedImages: Image[]; + flow?: FlowSummary; +} +``` + +--- + +#### POST /api/v1/generations/:id/retry + +Retry failed generation. + +**Response (200):** +```typescript +{ + generation: Generation; // New generation with incremented retry_count +} +``` + +**Errors:** 404, 422 + +--- + +#### DELETE /api/v1/generations/:id + +Delete generation. + +**Query Params:** +```typescript +{ + hard?: boolean; // Default: false +} +``` + +**Response (204):** No content + +--- + +### IMAGES + +#### POST /api/v1/images/upload + +Upload image file. + +**Request:** multipart/form-data + +**Fields:** +```typescript +{ + file: File; // Required, max 5MB + alias?: string; // Project-scoped: '@logo' + flowAlias?: string; // Flow-scoped: '@hero' (requires flowId) + flowId?: string; + description?: string; + tags?: string[]; // JSON array as string + focalPoint?: string; // JSON: '{"x":0.5,"y":0.5}' + meta?: string; // JSON object as string +} +``` + +**Response (201):** +```typescript +{ + image: Image; + flow?: FlowSummary; // If flowAlias assigned +} +``` + +**Errors:** 400, 409, 422 + +--- + +#### GET /api/v1/images + +List images. + +**Query Params:** +```typescript +{ + flowId?: string; + source?: 'generated' | 'uploaded'; + alias?: string; + limit?: number; // Default: 20, max: 100 + offset?: number; + sortBy?: 'createdAt' | 'fileSize'; + order?: 'asc' | 'desc'; +} +``` + +**Response (200):** +```typescript +{ + images: Image[]; + pagination: PaginationInfo; +} +``` + +--- + +#### GET /api/v1/images/:id + +Get image details. + +**Response (200):** +```typescript +{ + image: Image; + generation?: Generation; + usedInGenerations: GenerationSummary[]; +} +``` + +--- + +#### GET /api/v1/images/resolve/:alias + +Resolve alias to image. + +**Query Params:** +```typescript +{ + flowId?: string; // Provide flow context +} +``` + +**Response (200):** +```typescript +{ + image: Image; + scope: 'flow' | 'project' | 'technical'; + flow?: FlowSummary; +} +``` + +**Resolution Order:** +1. Technical aliases (@last, @first, @upload) if flowId provided +2. Flow aliases from flows.aliases if flowId provided +3. Project aliases from images.alias + +**Errors:** 404 + +--- + +#### PUT /api/v1/images/:id + +Update image metadata. + +**Request Body:** +```typescript +{ + alias?: string; + description?: string; + tags?: string[]; + focalPoint?: { x: number; y: number }; + meta?: Record; +} +``` + +**Response (200):** +```typescript +{ + image: Image; +} +``` + +**Errors:** 404, 409, 422 + +--- + +#### DELETE /api/v1/images/:id + +Delete image. + +**Query Params:** +```typescript +{ + hard?: boolean; // Default: false +} +``` + +**Response (204):** No content + +--- + +### FLOWS + +#### POST /api/v1/flows + +Create new flow. + +**Request Body:** +```typescript +{ + meta?: Record; +} +``` + +**Response (201):** +```typescript +{ + flow: Flow; +} +``` + +--- + +#### GET /api/v1/flows + +List flows. + +**Query Params:** +```typescript +{ + limit?: number; // Default: 20, max: 100 + offset?: number; + sortBy?: 'createdAt' | 'updatedAt'; + order?: 'asc' | 'desc'; +} +``` + +**Response (200):** +```typescript +{ + flows: Array; + pagination: PaginationInfo; +} +``` + +--- + +#### GET /api/v1/flows/:id + +Get flow details. + +**Response (200):** +```typescript +{ + flow: Flow; + generations: Generation[]; // Ordered by created_at ASC + images: Image[]; + resolvedAliases: Record; +} +``` + +--- + +#### PUT /api/v1/flows/:id/aliases + +Update flow aliases. + +**Request Body:** +```typescript +{ + aliases: Record; // { "@hero": "image-uuid" } +} +``` + +**Response (200):** +```typescript +{ + flow: Flow; +} +``` + +**Validation:** +- Keys must match `^@[a-zA-Z0-9_-]+$` +- Values must be valid image UUIDs +- Cannot use reserved: @last, @first, @upload + +**Errors:** 404, 422 + +--- + +#### DELETE /api/v1/flows/:id/aliases/:alias + +Remove specific alias from flow. + +**Response (204):** No content + +**Errors:** 404 + +--- + +#### DELETE /api/v1/flows/:id + +Delete flow. + +**Response (204):** No content + +**Note:** Cascades to images, sets NULL on generations.flow_id + +--- + +### LIVE GENERATION + +#### GET /api/v1/live + +Generate image via URL with caching and streaming. + +**Query Params:** +```typescript +{ + prompt: string; // Required + aspectRatio?: string; + width?: number; + height?: number; + reference?: string | string[]; // '@logo' or ['@logo','@style'] +} +``` + +**Response:** Image stream with headers + +**Headers:** +``` +Content-Type: image/jpeg +Cache-Control: public, max-age=31536000 +X-Cache-Status: HIT | MISS +``` + +**Implementation:** +1. Compute cache key: SHA256(prompt + sorted params) +2. Check prompt_url_cache table +3. If HIT: increment hit_count, stream from MinIO +4. If MISS: generate, cache, stream from MinIO +5. Stream image bytes directly (no 302 redirect) + +**Errors:** 400, 404, 500 + +--- + +### ANALYTICS + +#### GET /api/v1/analytics/summary + +Get project statistics. + +**Query Params:** +```typescript +{ + startDate?: string; // ISO 8601 + endDate?: string; + flowId?: string; +} +``` + +**Response (200):** +```typescript +{ + period: { startDate: string; endDate: string }; + metrics: { + totalGenerations: number; + successfulGenerations: number; + failedGenerations: number; + successRate: number; + totalImages: number; + uploadedImages: number; + generatedImages: number; + avgProcessingTimeMs: number; + totalCacheHits: number; + cacheHitRate: number; + totalCost: number; + }; + flows: FlowSummary[]; +} +``` + +--- + +#### GET /api/v1/analytics/generations/timeline + +Get generation statistics over time. + +**Query Params:** +```typescript +{ + startDate?: string; + endDate?: string; + flowId?: string; + groupBy?: 'hour' | 'day' | 'week'; // Default: day +} +``` + +**Response (200):** +```typescript +{ + data: Array<{ + timestamp: string; + total: number; + successful: number; + failed: number; + avgProcessingTimeMs: number; + }>; +} +``` + +--- + +## Implementation Guidelines + +### Alias Resolution Algorithm + +**Priority Order:** +1. Technical aliases (@last, @first, @upload) - compute from flow data +2. Flow-scoped aliases - from flows.aliases JSONB +3. Project-scoped aliases - from images.alias column + +**Technical Aliases:** +- `@last`: Latest generation output in flow (any status) +- `@first`: First generation output in flow +- `@upload`: Latest uploaded image in flow + +### Dual Alias Assignment + +When creating generation or uploading image: +- `assignAlias` → set images.alias (project scope) +- `assignFlowAlias` → add to flows.aliases (flow scope) +- Both can be assigned simultaneously + +### Flow Updates + +Update `flows.updated_at` on: +- New generation created with flowId +- New image uploaded with flowId +- Flow aliases modified + +### Audit Trail + +Track `api_key_id` in: +- `images.api_key_id` - who uploaded/generated +- `generations.api_key_id` - who requested + +### Rate Limiting + +In-memory rate limiting (defer Redis for MVP): +- Master key: 1000 req/hour, 100 generations/hour +- Project key: 500 req/hour, 50 generations/hour + +**Headers:** +``` +X-RateLimit-Limit: 500 +X-RateLimit-Remaining: 487 +X-RateLimit-Reset: 1698765432 +``` + +### Error Response Format + +```typescript +{ + error: string; + message: string; + details?: unknown; + requestId?: string; +} +``` + +### MinIO Integration + +Use streaming for `/api/v1/live`: +```typescript +const stream = await minioClient.getObject(bucket, storageKey); +res.set('Content-Type', mimeType); +stream.pipe(res); +``` + +Generate presigned URLs for other endpoints: +```typescript +const url = await minioClient.presignedGetObject(bucket, storageKey, 24 * 60 * 60); +``` + +--- + +## Validation Rules + +**Alias Format:** +- Pattern: `^@[a-zA-Z0-9_-]+$` +- Reserved: @last, @first, @upload +- Length: 3-100 chars + +**File Upload:** +- Max size: 5MB +- MIME types: image/jpeg, image/png, image/webp +- Max dimensions: 8192x8192 + +**Prompt:** +- Min: 1 char +- Max: 2000 chars + +**Aspect Ratio:** +- Pattern: `^\d+:\d+$` +- Examples: 16:9, 1:1, 4:3, 9:16 + +--- + +## Service Architecture + +### Core Services + +**AliasService:** +- Resolve aliases with 3-tier precedence +- Compute technical aliases +- Validate alias format + +**ImageService:** +- CRUD operations +- Soft delete support +- Usage tracking + +**GenerationService:** +- Generation lifecycle +- Status transitions +- Error handling +- Retry logic + +**FlowService:** +- Flow CRUD +- Alias management +- Computed counts + +**PromptCacheService:** +- Cache key computation (SHA-256) +- Hit tracking +- Cache lookup + +**AnalyticsService:** +- Aggregation queries +- Time-series grouping + +### Reusable Utilities + +**Validators:** +- Alias format +- Pagination params +- Query filters + +**Helpers:** +- Pagination builder +- SHA-256 hashing +- Query helpers + +--- + +## Testing Requirements + +**Unit Tests:** +- All services must have unit tests +- Target coverage: >80% +- Mock database calls + +**Integration Tests:** +- Critical flows end-to-end +- Real database transactions +- API endpoint testing with supertest + +**Test Scenarios:** +- Alias resolution precedence +- Flow-scoped vs project-scoped aliases +- Technical alias computation +- Dual alias assignment +- Cache hit/miss behavior +- Error handling +- Rate limiting + +--- + +## Success Criteria + +✅ All endpoints functional per specification +✅ >80% test coverage on services +✅ Consistent error handling across all endpoints +✅ All validation rules implemented +✅ Rate limiting working +✅ Documentation updated +✅ Git commits after each phase + +--- + +*Document Version: 2.0* +*Created: 2025-11-09* +*Target: Claude Code Implementation* +*Database Schema: v2.0*