# 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