diff --git a/infrastructure.md b/infrastructure.md new file mode 100644 index 0000000..3c696e2 --- /dev/null +++ b/infrastructure.md @@ -0,0 +1,937 @@ +# Banatie Service Infrastructure Documentation + +## Overview + +This document defines the complete containerization and deployment architecture for the Banatie AI image generation service with MinIO object storage and PostgreSQL database integration. + +## Architecture Summary + +### Core Principles +- **Complete Service Isolation**: Banatie ecosystem is fully isolated from core VPS services +- **Dedicated Database**: Separate PostgreSQL container exclusively for Banatie +- **S3-Compatible Storage**: MinIO for scalable object storage with multi-tenant support +- **Container-First Approach**: All components run as Docker containers +- **Network Segregation**: Internal communication via dedicated Docker networks + +### Service Components + +``` +Banatie Ecosystem (Isolated) +├── Banatie App Container (Node.js/TypeScript/Express) +├── PostgreSQL Container (Dedicated instance) +└── MinIO Container (S3-compatible object storage) +``` + +## Network Architecture + +### Production Networks + +```yaml +networks: + banatie-network: + driver: bridge + internal: true # No external internet access + + proxy-network: + external: true # Existing Caddy reverse proxy network +``` + +### Network Access Matrix + +| Service | banatie-network | proxy-network | External Access | +|---------|----------------|---------------|-----------------| +| Banatie App | ✅ Internal | ✅ HTTP only | ❌ Direct | +| PostgreSQL | ✅ Internal | ❌ None | ❌ None | +| MinIO | ✅ Internal | ✅ Console only | ❌ Direct | +| Caddy Proxy | ❌ None | ✅ Routing | ✅ Internet | + +### VPS Integration Points + +**Isolation from Core Services:** +- **NO** shared resources with existing PostgreSQL (`/opt/services/`) +- **NO** access to NextCloud, Gitea, or other core services +- **ONLY** connection point: Caddy reverse proxy for HTTP routing + +**Shared Infrastructure:** +- Caddy reverse proxy for SSL termination and routing +- Host filesystem for persistent data storage +- UFW firewall rules and security policies + +## Directory Structure + +### Development Environment +``` +banatie-service/ +├── src/ # Application source code +├── Dockerfile # Multi-stage container build +├── docker-compose.yml # Local development setup +├── .env.example # Environment template +├── .dockerignore # Build exclusions +└── data/ # Local development data + ├── postgres/ # PostgreSQL data + └── minio/ # MinIO data +``` + +### Production Environment (VPS) +``` +/opt/banatie/ # Isolated deployment directory +├── docker-compose.yml # Production configuration +├── .env # Production environment (secure) +├── Dockerfile # Production Dockerfile +├── data/ # Persistent data storage +│ ├── postgres/ # PostgreSQL data +│ └── minio/ # MinIO data +├── configs/ # Service configurations +├── logs/ # Application logs +└── scripts/ # Deployment scripts +``` + +## Container Specifications + +### 1. Banatie Application Container + +**Base Image**: `node:20-alpine` +**Build Strategy**: Multi-stage for optimization +**Runtime**: Node.js with TypeScript compilation + +```dockerfile +# Multi-stage build for production +FROM node:20-alpine AS builder +WORKDIR /app +COPY package*.json pnpm-lock.yaml ./ +RUN npm install -g pnpm && pnpm install --frozen-lockfile +COPY . . +RUN pnpm build + +FROM node:20-alpine +WORKDIR /app +RUN npm install -g pnpm +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/package*.json ./ +COPY --from=builder /app/pnpm-lock.yaml ./ +RUN pnpm install --prod --frozen-lockfile +EXPOSE 3000 +CMD ["node", "dist/server.js"] +``` + +**Key Features:** +- Hot-reload support in development +- Production-optimized build with dependencies pruning +- Health check endpoints for monitoring +- Structured logging to stdout + +### 2. PostgreSQL Container + +**Image**: `postgres:15-alpine` +**Purpose**: Metadata storage for user sessions, image metadata, organization data +**Data**: User accounts, image metadata, upload sessions, organization settings + +**Database Schema:** +- `users` - User authentication and profiles +- `organizations` - Multi-tenant organization data +- `images` - Generated image metadata and references +- `sessions` - User session management +- `uploads` - Temporary upload tracking + +### 3. MinIO Container + +**Image**: `minio/minio:latest` +**Purpose**: S3-compatible object storage for images and files +**Ports**: 9000 (S3 API), 9001 (Web Console) + +**Storage Strategy:** +- Persistent volumes for data durability +- Bucket-per-organization architecture +- Lifecycle policies for temporary file cleanup +- Presigned URLs for direct browser uploads + +## Multi-Tenant Storage Architecture + +### MinIO Bucket Organization + +``` +banatie-{org-id}/ # Organization bucket (e.g., banatie-demo) +├── users/ +│ └── {user-id}/ # User-specific namespace +│ ├── generated/ # AI-generated images +│ │ ├── 2024/01/ # Date-based organization +│ │ └── thumbnails/ # Generated thumbnails +│ ├── references/ # User-uploaded reference images +│ └── temp/ # Temporary processing files (7-day TTL) +├── shared/ # Organization-wide shared resources +│ ├── templates/ # Shared image templates +│ └── public/ # Public organization assets +└── metadata/ # JSON metadata files + ├── users.json # User directory metadata + └── policies.json # Access control policies +``` + +### Demo Organization Setup + +**Default Configuration:** +- **Organization**: `demo` (org-id: demo) +- **User**: `guest` (user-id: guest) +- **Bucket**: `banatie-demo` +- **Access**: Public read for demo images + +**Demo Bucket Structure:** +``` +banatie-demo/ +├── users/ +│ └── guest/ +│ ├── generated/ # Guest user's generated images +│ └── references/ # Guest user's reference uploads +└── shared/ + └── examples/ # Example images for UI +``` + +## Environment Configuration + +### Development Environment (.env) + +```env +# Application Configuration +NODE_ENV=development +PORT=3000 +LOG_LEVEL=debug + +# Database Configuration +DB_HOST=postgres +DB_PORT=5432 +DB_NAME=banatie_db +DB_USER=banatie_user +DB_PASSWORD=development_password + +# MinIO Configuration +STORAGE_TYPE=minio +MINIO_ENDPOINT=minio:9000 +MINIO_ACCESS_KEY=minioadmin +MINIO_SECRET_KEY=minioadmin +MINIO_USE_SSL=false +MINIO_BUCKET_PREFIX=banatie + +# AI Service Configuration +GEMINI_API_KEY=your_gemini_api_key_here + +# Multi-tenancy Configuration +DEFAULT_ORG_ID=demo +DEFAULT_USER_ID=guest +``` + +### Production Environment (.env.production) + +```env +# Application Configuration +NODE_ENV=production +PORT=3000 +LOG_LEVEL=info + +# Database Configuration +DB_HOST=banatie-postgres +DB_PORT=5432 +DB_NAME=banatie_db +DB_USER=banatie_user +DB_PASSWORD=${SECURE_DB_PASSWORD} + +# MinIO Configuration +STORAGE_TYPE=minio +MINIO_ENDPOINT=banatie-minio:9000 +MINIO_ACCESS_KEY=${SECURE_MINIO_ACCESS_KEY} +MINIO_SECRET_KEY=${SECURE_MINIO_SECRET_KEY} +MINIO_USE_SSL=false +MINIO_BUCKET_PREFIX=banatie + +# AI Service Configuration +GEMINI_API_KEY=${SECURE_GEMINI_API_KEY} + +# Multi-tenancy Configuration +DEFAULT_ORG_ID=demo +DEFAULT_USER_ID=guest + +# Security +JWT_SECRET=${SECURE_JWT_SECRET} +SESSION_SECRET=${SECURE_SESSION_SECRET} +``` + +## Docker Compose Configurations + +### Development Configuration + +```yaml +# docker-compose.yml (Local Development) +version: '3.8' + +services: + app: + build: + context: . + target: development + container_name: banatie-app-dev + ports: + - "3000:3000" + volumes: + - ./src:/app/src # Hot reload + - ./logs:/app/logs + networks: + - banatie-dev + depends_on: + - postgres + - minio + environment: + - NODE_ENV=development + env_file: + - .env + + postgres: + image: postgres:15-alpine + container_name: banatie-postgres-dev + ports: + - "5433:5432" # Avoid conflicts with system PostgreSQL + volumes: + - ./data/postgres:/var/lib/postgresql/data + - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/01-init.sql + networks: + - banatie-dev + environment: + POSTGRES_DB: banatie_db + POSTGRES_USER: banatie_user + POSTGRES_PASSWORD: development_password + healthcheck: + test: ["CMD-SHELL", "pg_isready -U banatie_user -d banatie_db"] + interval: 30s + timeout: 10s + retries: 3 + + minio: + image: minio/minio:latest + container_name: banatie-minio-dev + ports: + - "9000:9000" # S3 API + - "9001:9001" # Web Console + volumes: + - ./data/minio:/data + networks: + - banatie-dev + environment: + MINIO_ROOT_USER: minioadmin + MINIO_ROOT_PASSWORD: minioadmin + command: server /data --console-address ":9001" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + interval: 30s + timeout: 10s + retries: 3 + +networks: + banatie-dev: + driver: bridge +``` + +### Production Configuration + +```yaml +# docker-compose.yml (Production on VPS) +version: '3.8' + +services: + banatie-app: + build: + context: . + target: production + container_name: banatie-app + restart: unless-stopped + networks: + - banatie-network + - proxy-network + depends_on: + banatie-postgres: + condition: service_healthy + banatie-minio: + condition: service_healthy + environment: + - NODE_ENV=production + env_file: + - .env + volumes: + - ./logs:/app/logs + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + + banatie-postgres: + image: postgres:15-alpine + container_name: banatie-postgres + restart: unless-stopped + networks: + - banatie-network + volumes: + - ./data/postgres:/var/lib/postgresql/data + - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/01-init.sql + environment: + POSTGRES_DB: banatie_db + POSTGRES_USER: banatie_user + POSTGRES_PASSWORD: ${DB_PASSWORD} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U banatie_user -d banatie_db"] + interval: 30s + timeout: 10s + retries: 3 + + banatie-minio: + image: minio/minio:latest + container_name: banatie-minio + restart: unless-stopped + networks: + - banatie-network + - proxy-network + volumes: + - ./data/minio:/data + environment: + MINIO_ROOT_USER: ${MINIO_ACCESS_KEY} + MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY} + command: server /data --console-address ":9001" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + interval: 30s + timeout: 10s + retries: 3 + +networks: + banatie-network: + driver: bridge + internal: true # No external internet access + + proxy-network: + external: true # Existing Caddy network +``` + +## Caddy Integration + +### Caddy Configuration Addition + +Add to existing Caddyfile in `/opt/services/configs/caddy/Caddyfile`: + +```caddy +# Banatie App - Main API +banatie.app { + reverse_proxy banatie-app:3000 + + # Security headers + header { + Strict-Transport-Security "max-age=31536000; includeSubDomains" + X-Content-Type-Options "nosniff" + X-Frame-Options "DENY" + X-XSS-Protection "1; mode=block" + Referrer-Policy "strict-origin-when-cross-origin" + } + + # Rate limiting + rate_limit { + zone banatie_api { + key {remote_host} + events 100 + window 1m + } + } + + # Logging + log { + output file /opt/services/logs/banatie_access.log + format json + } +} + +# MinIO Console - Admin Interface +minio.banatie.app { + reverse_proxy banatie-minio:9001 + + # Security headers for console + header { + Strict-Transport-Security "max-age=31536000; includeSubDomains" + X-Content-Type-Options "nosniff" + X-Frame-Options "SAMEORIGIN" + } + + # Logging + log { + output file /opt/services/logs/minio_console_access.log + format json + } +} + +# MinIO S3 API - Direct file access +s3.banatie.app { + reverse_proxy banatie-minio:9000 + + # CORS for browser uploads + header { + Access-Control-Allow-Origin "*" + Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" + Access-Control-Allow-Headers "Content-Type, Authorization" + } + + # Rate limiting for uploads + rate_limit { + zone banatie_s3 { + key {remote_host} + events 50 + window 1m + } + } + + # Logging + log { + output file /opt/services/logs/minio_s3_access.log + format json + } +} +``` + +## Service Implementation Strategy + +### 1. Storage Service Abstraction + +Create `src/services/StorageService.ts`: + +```typescript +export interface StorageService { + // Bucket management + createBucket(orgId: string): Promise; + deleteBucket(orgId: string): Promise; + listBuckets(): Promise; + + // File operations + uploadFile(orgId: string, userId: string, fileName: string, buffer: Buffer, contentType: string): Promise; + downloadFile(orgId: string, userId: string, fileName: string): Promise; + deleteFile(orgId: string, userId: string, fileName: string): Promise; + + // URL generation + getPresignedUploadUrl(orgId: string, userId: string, fileName: string, expirySeconds: number): Promise; + getPresignedDownloadUrl(orgId: string, userId: string, fileName: string, expirySeconds: number): Promise; + + // File management + listUserFiles(orgId: string, userId: string): Promise; + moveFile(fromPath: string, toPath: string): Promise; + copyFile(fromPath: string, toPath: string): Promise; +} +``` + +### 2. MinIO Implementation + +Create `src/services/MinioStorageService.ts`: + +```typescript +import { Client as MinioClient } from 'minio'; +import { StorageService } from './StorageService'; + +export class MinioStorageService implements StorageService { + private client: MinioClient; + private bucketPrefix: string; + + constructor( + endpoint: string, + accessKey: string, + secretKey: string, + useSSL: boolean = false, + bucketPrefix: string = 'banatie' + ) { + this.client = new MinioClient({ + endPoint: endpoint.replace(/^https?:\/\//, ''), + port: useSSL ? 443 : 9000, + useSSL, + accessKey, + secretKey + }); + this.bucketPrefix = bucketPrefix; + } + + private getBucketName(orgId: string): string { + return `${this.bucketPrefix}-${orgId}`; + } + + private getFilePath(userId: string, category: 'generated' | 'references' | 'temp', fileName: string): string { + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + + return `users/${userId}/${category}/${year}/${month}/${fileName}`; + } + + // Implementation methods... +} +``` + +### 3. Database Schema + +Create `scripts/init-db.sql`: + +```sql +-- Banatie Database Initialization +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +-- Organizations table +CREATE TABLE organizations ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name VARCHAR(255) NOT NULL, + slug VARCHAR(100) UNIQUE NOT NULL, + settings JSONB DEFAULT '{}', + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Users table +CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE, + username VARCHAR(100) NOT NULL, + email VARCHAR(255), + role VARCHAR(50) DEFAULT 'user', + settings JSONB DEFAULT '{}', + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + UNIQUE(organization_id, username) +); + +-- Images table +CREATE TABLE images ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE, + filename VARCHAR(255) NOT NULL, + file_path VARCHAR(500) NOT NULL, + original_prompt TEXT, + enhanced_prompt TEXT, + model_used VARCHAR(100), + file_size BIGINT, + content_type VARCHAR(100), + metadata JSONB DEFAULT '{}', + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Upload sessions table (for tracking multi-part uploads) +CREATE TABLE upload_sessions ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE, + session_data JSONB NOT NULL, + expires_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Create indexes +CREATE INDEX idx_users_org_id ON users(organization_id); +CREATE INDEX idx_images_user_id ON images(user_id); +CREATE INDEX idx_images_created_at ON images(created_at); +CREATE INDEX idx_upload_sessions_user_id ON upload_sessions(user_id); +CREATE INDEX idx_upload_sessions_expires_at ON upload_sessions(expires_at); + +-- Insert demo organization and user +INSERT INTO organizations (id, name, slug) VALUES + ('00000000-0000-0000-0000-000000000001', 'Demo Organization', 'demo'); + +INSERT INTO users (id, organization_id, username, role) VALUES + ('00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000001', 'guest', 'user'); +``` + +## Development Workflow + +### Local Development Setup + +1. **Clone Repository** +```bash +git clone +cd banatie-service +``` + +2. **Environment Configuration** +```bash +cp .env.example .env +# Edit .env with development settings +``` + +3. **Start Development Environment** +```bash +docker-compose up -d +``` + +4. **Verify Services** +```bash +# Check service status +docker-compose ps + +# View logs +docker-compose logs -f app + +# Access services +# App: http://localhost:3000 +# MinIO Console: http://localhost:9001 (minioadmin/minioadmin) +# PostgreSQL: localhost:5433 +``` + +5. **Development with Hot Reload** +```bash +# Edit source files in src/ +# Changes automatically reload in container +``` + +### Testing Workflow + +```bash +# Run tests in development environment +docker-compose exec app pnpm test + +# Run with coverage +docker-compose exec app pnpm test:coverage + +# Run linting +docker-compose exec app pnpm lint +``` + +## Production Deployment + +### VPS Deployment Process + +1. **Prepare VPS Directory** +```bash +# SSH to VPS +ssh usul-vps + +# Create Banatie directory +sudo mkdir -p /opt/banatie +sudo chown usul:usul /opt/banatie +cd /opt/banatie +``` + +2. **Clone and Configure** +```bash +# Clone repository +git clone . + +# Create production environment +cp .env.example .env +# Edit .env with production settings and secure passwords +``` + +3. **Generate Secure Credentials** +```bash +# Generate secure passwords +DB_PASSWORD=$(openssl rand -base64 32 | tr -d '\n\r ') +MINIO_ACCESS_KEY=$(openssl rand -base64 20 | tr -d '\n\r ') +MINIO_SECRET_KEY=$(openssl rand -base64 40 | tr -d '\n\r ') +JWT_SECRET=$(openssl rand -base64 64 | tr -d '\n\r ') + +# Add to .env file +echo "DB_PASSWORD=$DB_PASSWORD" >> .env +echo "MINIO_ACCESS_KEY=$MINIO_ACCESS_KEY" >> .env +echo "MINIO_SECRET_KEY=$MINIO_SECRET_KEY" >> .env +echo "JWT_SECRET=$JWT_SECRET" >> .env + +# Secure the file +chmod 600 .env +``` + +4. **Update Caddy Configuration** +```bash +# Add Banatie routes to Caddyfile +sudo nano /opt/services/configs/caddy/Caddyfile +# Add the Banatie configuration from above + +# Reload Caddy +cd /opt/services +./manage-services.sh caddy reload +``` + +5. **Deploy Services** +```bash +cd /opt/banatie + +# Build and start services +docker-compose up -d + +# Verify deployment +docker-compose ps +docker-compose logs -f +``` + +6. **Verify Deployment** +```bash +# Check health endpoints +curl https://banatie.app/health +curl https://minio.banatie.app/minio/health/live + +# Check logs +docker-compose logs banatie-app +docker-compose logs banatie-postgres +docker-compose logs banatie-minio +``` + +### Manual Update Process + +```bash +# SSH to VPS +ssh usul-vps +cd /opt/banatie + +# Pull latest changes +git pull origin main + +# Rebuild and restart +docker-compose build banatie-app +docker-compose up -d + +# Verify update +docker-compose logs -f banatie-app +curl https://banatie.app/health +``` + +## Monitoring and Maintenance + +### Health Checks + +**Application Health Check** (`/health`): +```json +{ + "status": "healthy", + "timestamp": "2024-01-01T12:00:00.000Z", + "services": { + "database": "connected", + "minio": "connected", + "gemini_api": "accessible" + }, + "version": "1.0.0" +} +``` + +**MinIO Health Check** (`/minio/health/live`): +- Returns 200 OK when MinIO is operational +- Used by Docker healthcheck and monitoring + +### Log Management + +**Application Logs**: +- Location: `/opt/banatie/logs/` +- Format: Structured JSON +- Rotation: Daily with 30-day retention + +**Access Logs**: +- Caddy logs: `/opt/services/logs/banatie_*.log` +- Format: JSON with request/response details + +### Backup Strategy + +**Database Backup**: +```bash +# Create backup +docker exec banatie-postgres pg_dump -U banatie_user banatie_db > banatie_db_backup.sql + +# Restore backup +docker exec -i banatie-postgres psql -U banatie_user banatie_db < banatie_db_backup.sql +``` + +**MinIO Data Backup**: +```bash +# Backup MinIO data +sudo tar -czf banatie_minio_backup.tar.gz -C /opt/banatie/data minio/ + +# Restore MinIO data +sudo tar -xzf banatie_minio_backup.tar.gz -C /opt/banatie/data +``` + +## Security Considerations + +### Container Security +- Non-root users in containers +- Read-only root filesystems where possible +- Resource limits (memory, CPU) +- Health checks for automatic restart + +### Network Security +- Internal network isolation +- No direct external access to database or MinIO +- Rate limiting at proxy level +- HTTPS-only external access + +### Data Security +- Encrypted environment variables +- Secure secret generation +- Regular credential rotation +- Audit logging + +### Access Control +- MinIO bucket policies per organization +- PostgreSQL row-level security +- JWT-based API authentication +- Role-based access control + +## Future Scalability + +### Horizontal Scaling Options +- Multiple Banatie app containers behind load balancer +- MinIO distributed mode for storage scaling +- PostgreSQL read replicas for read scaling +- Redis for session storage and caching + +### Multi-Region Deployment +- Regional MinIO clusters +- Database replication +- CDN integration for static assets +- Geo-distributed deployment + +## Troubleshooting + +### Common Issues + +**Container Won't Start**: +```bash +# Check logs +docker-compose logs [service-name] + +# Check resource usage +docker stats + +# Rebuild container +docker-compose build [service-name] +docker-compose up -d [service-name] +``` + +**Database Connection Issues**: +```bash +# Test database connectivity +docker exec banatie-postgres pg_isready -U banatie_user -d banatie_db + +# Check environment variables +docker exec banatie-app env | grep DB_ + +# Restart database +docker-compose restart banatie-postgres +``` + +**MinIO Access Issues**: +```bash +# Check MinIO status +docker exec banatie-minio mc admin info local + +# Test S3 API +curl -f http://localhost:9000/minio/health/live + +# Check access keys +docker exec banatie-app env | grep MINIO_ +``` + +**Network Connectivity**: +```bash +# Check networks +docker network ls +docker network inspect banatie_banatie-network + +# Test internal connectivity +docker exec banatie-app ping banatie-postgres +docker exec banatie-app ping banatie-minio +``` + +--- + +**Document Version**: 1.0 +**Last Updated**: 2024-09-26 +**Maintained By**: Banatie Development Team \ No newline at end of file