diff --git a/CLAUDE.md b/CLAUDE.md index 5cbea39..6a863c2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -56,6 +56,8 @@ banatie-service/ │ ├── landing/ # Next.js landing page │ ├── studio/ # Next.js SaaS platform │ └── admin/ # Next.js admin dashboard +├── packages/ +│ └── database/ # Shared database package (Drizzle ORM) ├── data/ # Docker volume data (postgres, minio) ├── docker-compose.yml # Infrastructure services ├── pnpm-workspace.yaml # Workspace configuration @@ -66,15 +68,23 @@ banatie-service/ - **Express App**: Configured in `src/app.ts` with middleware, CORS, and route mounting - **Server Entry**: `src/server.ts` starts the HTTP server -- **Image Generation**: `src/services/ImageGenService.ts` handles Gemini AI integration -- **Storage**: `src/services/MinioStorageService.ts` handles file uploads to MinIO -- **Route Handling**: `src/routes/generate.ts` contains the main API endpoint logic +- **Database Client**: `src/db.ts` - Drizzle ORM connection to PostgreSQL +- **Authentication**: `src/services/ApiKeyService.ts` - API key management and validation +- **Image Generation**: `src/services/ImageGenService.ts` - Gemini AI integration +- **Storage**: `src/services/MinioStorageService.ts` - File uploads to MinIO +- **Route Handling**: + - `src/routes/bootstrap.ts` - Bootstrap initial master key (one-time) + - `src/routes/admin/keys.ts` - API key management (master key required) + - `src/routes/generate.ts` - Image generation endpoint (API key required) ### Middleware Stack (API Service) - `src/middleware/upload.ts` - Multer configuration for file uploads (max 3 files, 5MB each) - `src/middleware/validation.ts` - Express-validator for request validation - `src/middleware/errorHandler.ts` - Centralized error handling and 404 responses +- `src/middleware/auth/validateApiKey.ts` - API key authentication +- `src/middleware/auth/requireMasterKey.ts` - Master key authorization +- `src/middleware/auth/rateLimiter.ts` - Rate limiting per API key (100 req/hour) ### TypeScript Configuration (API Service) @@ -89,13 +99,32 @@ banatie-service/ ### Storage & Data - **MinIO**: Object storage for generated images and uploads (port 9000) -- **PostgreSQL**: Database for user data, metadata (port 5434) +- **PostgreSQL**: Database for API keys, user data, and metadata (port 5434) + - Database name: `banatie_db` + - User: `banatie_user` + - Tables: `api_keys`, `organizations`, `projects`, `users`, `images`, `upload_sessions` - **File Organization**: `orgId/projectId/category/year-month/filename.ext` +### Database Package (`packages/database/`) + +Shared Drizzle ORM package used by API service and future apps: + +- **Schema**: `src/schema/apiKeys.ts` - API keys table definition +- **Client Factory**: `src/client.ts` - Database connection creator +- **Migrations**: `migrations/` - SQL migration files +- **Configuration**: `drizzle.config.ts` - Drizzle Kit configuration + +Key table: `api_keys` +- Stores hashed API keys (SHA-256) +- Two types: `master` (admin, never expires) and `project` (90-day expiration) +- Soft delete via `is_active` flag +- Audit trail with `createdBy`, `lastUsedAt`, `createdAt` + ## Environment Configuration ### Root Environment (`.env.docker`) +- `DATABASE_URL` - PostgreSQL connection string (for Docker: `postgresql://banatie_user:banatie_secure_password@postgres:5432/banatie_db`) - `MINIO_ROOT_USER` - MinIO admin username - `MINIO_ROOT_PASSWORD` - MinIO admin password @@ -103,6 +132,7 @@ banatie-service/ Required environment variables: +- `DATABASE_URL` - PostgreSQL connection string (for local dev: `postgresql://banatie_user:banatie_secure_password@localhost:5434/banatie_db`) - `GEMINI_API_KEY` - Google Gemini API key (required) - `MINIO_ENDPOINT` - MinIO endpoint (`localhost:9000` for local dev, `minio:9000` for Docker) - `MINIO_ACCESS_KEY` - MinIO service account key @@ -110,20 +140,28 @@ Required environment variables: - `MINIO_BUCKET_NAME` - Storage bucket name (default: `banatie`) - `PORT` - Server port (default: 3000) - `NODE_ENV` - Environment mode -- `CORS_ORIGIN` - CORS origin setting (default: \*) +- `CORS_ORIGIN` - CORS origin setting (default: multiple localhost URLs for frontend apps) ## Key Dependencies ### API Service +- **@banatie/database** - Shared database package (workspace dependency) - **@google/genai** - Google Gemini AI client +- **drizzle-orm** - TypeScript ORM (via database package) +- **postgres** - PostgreSQL client for Node.js (via database package) - **express** v5 - Web framework - **multer** - File upload handling - **minio** - MinIO client for object storage - **express-validator** - Request validation - **winston** - Logging - **helmet** - Security middleware -- **express-rate-limit** - Rate limiting + +### Database Package + +- **drizzle-orm** - TypeScript ORM for SQL databases +- **drizzle-kit** - CLI tools for migrations +- **postgres** - PostgreSQL client for Node.js ### Frontend Apps (Next.js) @@ -147,9 +185,59 @@ Required environment variables: ## API Endpoints (API Service) +### Public Endpoints (No Authentication) - `GET /health` - Health check with uptime and status - `GET /api/info` - API information and limits -- `POST /api/generate` - Main image generation endpoint (multipart/form-data) +- `POST /api/bootstrap/initial-key` - Create first master key (one-time only) + +### Admin Endpoints (Master Key Required) +- `POST /api/admin/keys` - Create new API keys (master or project) +- `GET /api/admin/keys` - List all API keys +- `DELETE /api/admin/keys/:keyId` - Revoke an API key + +### Protected Endpoints (API Key Required) +- `POST /api/generate` - Generate images from text + optional reference images +- `POST /api/text-to-image` - Generate images from text only (JSON) +- `POST /api/enhance` - Enhance and optimize text prompts +- `GET /api/images` - List generated images + +**Authentication**: All protected endpoints require `X-API-Key` header + +## Authentication Setup + +### First-Time Setup + +1. **Start Services**: `docker compose up -d` +2. **Create Master Key**: + ```bash + curl -X POST http://localhost:3000/api/bootstrap/initial-key + ``` + Save the returned key securely! + +3. **Create Project Key** (for testing): + ```bash + curl -X POST http://localhost:3000/api/admin/keys \ + -H "X-API-Key: YOUR_MASTER_KEY" \ + -H "Content-Type: application/json" \ + -d '{"type": "project", "projectId": "test", "name": "Test Key"}' + ``` + +### Using API Keys + +```bash +# Image generation with project key +curl -X POST http://localhost:3000/api/generate \ + -H "X-API-Key: YOUR_PROJECT_KEY" \ + -F "prompt=a sunset" \ + -F "filename=test_image" +``` + +### Key Management + +- **Master Keys**: Never expire, can create/revoke other keys, admin access +- **Project Keys**: Expire in 90 days, for image generation only +- **Rate Limits**: 100 requests per hour per key +- **Revocation**: Soft delete via `is_active` flag ## Development Notes @@ -159,3 +247,4 @@ Required environment variables: - ESLint configured with TypeScript and Prettier integration - Jest for testing with ts-jest preset (API service) - Each app can be developed and deployed independently +- **Docker**: Uses monorepo-aware Dockerfile (`Dockerfile.mono`) that includes workspace packages diff --git a/docs/api/README.md b/docs/api/README.md index bea7ab5..1efca50 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -10,7 +10,40 @@ http://localhost:3000 ## Authentication -API key required via `GEMINI_API_KEY` environment variable (server-side configuration). +All API endpoints (except `/health`, `/api/info`, and `/api/bootstrap/*`) require authentication via API key. + +### API Key Types + +1. **Master Keys** - Full admin access, never expire, can create/revoke other keys +2. **Project Keys** - Standard access for image generation, expire in 90 days + +### Using API Keys + +Include your API key in the `X-API-Key` header: + +```bash +curl -X POST http://localhost:3000/api/generate \ + -H "X-API-Key: bnt_your_key_here" \ + -F "prompt=..." \ + -F "filename=..." +``` + +### Getting Your First API Key + +1. **Bootstrap** - Create initial master key (one-time only): + ```bash + curl -X POST http://localhost:3000/api/bootstrap/initial-key + ``` + +2. **Create Project Key** - Use master key to create project keys: + ```bash + curl -X POST http://localhost:3000/api/admin/keys \ + -H "X-API-Key: YOUR_MASTER_KEY" \ + -H "Content-Type: application/json" \ + -d '{"type": "project", "projectId": "my-project", "name": "My Project Key"}' + ``` + +**Important:** Save keys securely when created - they cannot be retrieved later! ## Content Types @@ -19,12 +52,126 @@ API key required via `GEMINI_API_KEY` environment variable (server-side configur ## Rate Limits -Standard Express rate limiting applies. Configure via environment variables. +- **Per API Key:** 100 requests per hour +- Rate limit information included in response headers: + - `X-RateLimit-Limit`: Maximum requests per window + - `X-RateLimit-Remaining`: Requests remaining + - `X-RateLimit-Reset`: When the limit resets (ISO 8601) +- **429 Too Many Requests:** Returned when limit exceeded --- ## Endpoints +### Authentication & Admin + +#### `POST /api/bootstrap/initial-key` + +Create the first master API key. This endpoint works only once when no keys exist. + +**Authentication:** None required (public endpoint, one-time use) + +**Response (201):** +```json +{ + "apiKey": "bnt_...", + "type": "master", + "name": "Initial Master Key", + "expiresAt": null, + "message": "IMPORTANT: Save this key securely. You will not see it again!" +} +``` + +**Error (403):** +```json +{ + "error": "Bootstrap not allowed", + "message": "API keys already exist. Use /api/admin/keys to create new keys." +} +``` + +--- + +#### `POST /api/admin/keys` + +Create a new API key (master or project). + +**Authentication:** Master key required via `X-API-Key` header + +**Request Body:** +```json +{ + "type": "master | project", + "projectId": "required-for-project-keys", + "name": "optional-friendly-name", + "expiresInDays": 90 +} +``` + +**Response (201):** +```json +{ + "apiKey": "bnt_...", + "metadata": { + "id": "uuid", + "type": "project", + "projectId": "my-project", + "name": "My Project Key", + "expiresAt": "2025-12-29T17:08:02.536Z", + "scopes": ["generate", "read"], + "createdAt": "2025-09-30T17:08:02.553Z" + }, + "message": "IMPORTANT: Save this key securely. You will not see it again!" +} +``` + +--- + +#### `GET /api/admin/keys` + +List all API keys. + +**Authentication:** Master key required + +**Response (200):** +```json +{ + "keys": [ + { + "id": "uuid", + "type": "master", + "projectId": null, + "name": "Initial Master Key", + "scopes": ["*"], + "isActive": true, + "createdAt": "2025-09-30T17:01:23.456Z", + "expiresAt": null, + "lastUsedAt": "2025-09-30T17:08:45.123Z", + "createdBy": null + } + ], + "total": 1 +} +``` + +--- + +#### `DELETE /api/admin/keys/:keyId` + +Revoke an API key (soft delete). + +**Authentication:** Master key required + +**Response (200):** +```json +{ + "message": "API key revoked successfully", + "keyId": "uuid" +} +``` + +--- + ### Health Check #### `GET /health` @@ -79,6 +226,8 @@ Returns API metadata and configuration limits. Generate images from text prompts with optional reference images. +**Authentication:** API key required (master or project) + **Content-Type:** `multipart/form-data` **Parameters:** @@ -105,6 +254,7 @@ Generate images from text prompts with optional reference images. **Example Request:** ```bash curl -X POST http://localhost:3000/api/generate \ + -H "X-API-Key: bnt_your_api_key_here" \ -F "prompt=A majestic mountain landscape at sunset" \ -F "filename=mountain-sunset" \ -F "autoEnhance=true" \ @@ -151,6 +301,8 @@ curl -X POST http://localhost:3000/api/generate \ Generate images from text prompts only using JSON payload. Simplified endpoint for text-only requests without file uploads. +**Authentication:** API key required (master or project) + **Content-Type:** `application/json` **Request Body:** @@ -180,6 +332,7 @@ Generate images from text prompts only using JSON payload. Simplified endpoint f **Example Request:** ```bash curl -X POST http://localhost:3000/api/text-to-image \ + -H "X-API-Key: bnt_your_api_key_here" \ -H "Content-Type: application/json" \ -d '{ "prompt": "A beautiful sunset over mountains with golden clouds", @@ -237,6 +390,8 @@ curl -X POST http://localhost:3000/api/text-to-image \ Enhance and optimize text prompts for better image generation results. +**Authentication:** API key required (master or project) + **Content-Type:** `application/json` **Request Body:** @@ -299,17 +454,33 @@ Enhance and optimize text prompts for better image generation results. | Code | Description | |------|-------------| | 400 | Bad Request - Invalid parameters or validation failure | -| 404 | Not Found - Endpoint does not exist | +| 401 | Unauthorized - Missing, invalid, expired, or revoked API key | +| 403 | Forbidden - Insufficient permissions (e.g., master key required) | +| 404 | Not Found - Endpoint or resource does not exist | +| 429 | Too Many Requests - Rate limit exceeded | | 500 | Internal Server Error - Server configuration or processing error | ## Common Error Messages +### Authentication Errors +- `"Missing API key"` - No X-API-Key header provided +- `"Invalid API key"` - The provided API key is invalid, expired, or revoked +- `"Master key required"` - This endpoint requires a master API key +- `"Bootstrap not allowed"` - API keys already exist, cannot bootstrap again + +### Validation Errors - `"Prompt is required"` - Missing or empty prompt parameter - `"Reference image validation failed"` - Invalid file format or size -- `"Server configuration error"` - Missing GEMINI_API_KEY -- `"Image generation failed"` - AI service error - `"Validation failed"` - Parameter validation error +### Rate Limiting +- `"Rate limit exceeded"` - Too many requests, retry after specified time + +### Server Errors +- `"Server configuration error"` - Missing GEMINI_API_KEY or database connection +- `"Image generation failed"` - AI service error +- `"Authentication failed"` - Error during authentication process + --- ## File Upload Specifications @@ -323,8 +494,23 @@ Enhance and optimize text prompts for better image generation results. | Header | Value | Description | |--------|-------|-------------| -| `X-Request-ID` | string | Unique request identifier (auto-generated) | +| `X-API-Key` | string | API key for authentication (required for most endpoints) | +| `X-Request-ID` | string | Unique request identifier (auto-generated by server) | + +## Response Headers + +| Header | Description | +|--------|-------------| +| `X-Request-ID` | Request identifier for tracking | +| `X-RateLimit-Limit` | Maximum requests allowed per window | +| `X-RateLimit-Remaining` | Requests remaining in current window | +| `X-RateLimit-Reset` | When the rate limit resets (ISO 8601) | ## CORS -Cross-origin requests supported. Configure via `CORS_ORIGIN` environment variable. \ No newline at end of file +Cross-origin requests supported from: +- `http://localhost:3001` (Landing Page) +- `http://localhost:3002` (Studio Platform) +- `http://localhost:3003` (Admin Dashboard) + +Configure additional origins via `CORS_ORIGIN` environment variable. \ No newline at end of file