feat: update testing
This commit is contained in:
parent
ca112886e5
commit
e55c02d158
|
|
@ -40,6 +40,8 @@
|
||||||
"kill-port": "^2.0.1",
|
"kill-port": "^2.0.1",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.2",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4",
|
||||||
|
"tsx": "^4.7.0",
|
||||||
|
"@types/node": "^20.11.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,840 +0,0 @@
|
||||||
# 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*
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
|
|
@ -2,17 +2,17 @@
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
// API Configuration
|
// API Configuration
|
||||||
baseURL: process.env.API_BASE_URL || 'http://localhost:3000',
|
baseURL: 'http://localhost:3000',
|
||||||
apiKey: process.env.API_KEY || 'bnt_test_key_change_me',
|
apiKey: 'bnt_71e7e16732ac5e21f597edc56e99e8c3696e713552ec9d1f44dfeffb2ef7c495',
|
||||||
|
|
||||||
// Paths
|
// Paths
|
||||||
resultsDir: '../../results',
|
resultsDir: '../../results',
|
||||||
fixturesDir: './fixtures',
|
fixturesDir: './fixtures',
|
||||||
|
|
||||||
// Timeouts
|
// Timeouts
|
||||||
requestTimeout: 30000,
|
requestTimeout: 30000,
|
||||||
generationTimeout: 60000,
|
generationTimeout: 60000,
|
||||||
|
|
||||||
// Test settings
|
// Test settings
|
||||||
verbose: true,
|
verbose: true,
|
||||||
saveImages: true,
|
saveImages: true,
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB |
Loading…
Reference in New Issue