chore: update documentation
This commit is contained in:
parent
1236dd78e2
commit
df84e400f5
|
|
@ -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<string, unknown>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**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<string, unknown>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**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<string, unknown>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**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<Flow & {
|
||||||
|
generationCount: number; // Computed
|
||||||
|
imageCount: number; // Computed
|
||||||
|
}>;
|
||||||
|
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<string, Image>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### PUT /api/v1/flows/:id/aliases
|
||||||
|
|
||||||
|
Update flow aliases.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
aliases: Record<string, string>; // { "@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*
|
||||||
Loading…
Reference in New Issue