Compare commits

..

No commits in common. "23c1d33adb123b39fb2af7fb0dc0f7cf5392fe92" and "1c6dfc4f8ba5fd2b8eed5553c60ff365168a9798" have entirely different histories.

12 changed files with 10 additions and 1728 deletions

View File

@ -1,79 +0,0 @@
# Dependencies
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Build outputs
dist
# Environment files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.env.docker
# IDE files
.vscode
.idea
*.swp
*.swo
*~
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Coverage directory
coverage
*.lcov
.nyc_output
# Git
.git
.gitignore
# Docker
Dockerfile*
docker-compose*
.dockerignore
# Data directories (will be mounted as volumes)
data
uploads
results
# Temporary files
temp
tmp
# Test files
tests
test-*.sh
# Documentation
README.md
docs
infrastructure.md
# Other
.claude
.mcp.json

View File

@ -1,41 +0,0 @@
# Application Configuration
NODE_ENV=development
PORT=3000
LOG_LEVEL=debug
# CORS Configuration
CORS_ORIGIN=*
# Database Configuration
DB_HOST=postgres
DB_PORT=5432
DB_NAME=banatie_db
DB_USER=banatie_user
DB_PASSWORD=development_password
# MinIO Storage Configuration
STORAGE_TYPE=minio
MINIO_ENDPOINT=minio:9000
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
MINIO_USE_SSL=false
MINIO_BUCKET_PREFIX=banatie
MINIO_PUBLIC_URL=http://localhost:9000
# AI Service Configuration (using the existing API key from .env)
GEMINI_API_KEY=AIzaSyBaOt9JMPGKA3811FL-ssf1n5Hh9Jauly8
# File Upload Configuration
MAX_FILE_SIZE=5242880
MAX_FILES=3
# Multi-tenancy Configuration
DEFAULT_ORG_ID=demo
DEFAULT_USER_ID=guest
# Directory Configuration (for Docker containers)
RESULTS_DIR=/app/results
UPLOADS_DIR=/app/uploads/temp
# Logging Configuration
LOG_LEVEL=info

11
.gitignore vendored
View File

@ -77,13 +77,4 @@ uploads/
# Temporary files # Temporary files
temp/ temp/
tmp/ tmp/
# Docker data directories
data/
.env.docker
.env.production
# Docker volumes (persistent data)
postgres-data/
minio-data/

View File

@ -1,44 +0,0 @@
{
"mcpServers": {
"context7": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@upstash/context7-mcp",
"--api-key",
"ctx7sk-48cb1995-935a-4cc5-b9b0-535d600ea5e6"
],
"env": {}
},
"brave-search": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-brave-search"
],
"env": {
"BRAVE_API_KEY": "BSAcRGGikEzY4B2j3NZ8Qy5NYh9l4HZ"
}
},
"postgres": {
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "DATABASE_URI", "crystaldba/postgres-mcp", "--access-mode=unrestricted"],
"env": {
"DATABASE_URI": "postgresql://postgres:postgres@localhost:5433/prime_db"
}
},
"mastra": {
"type": "stdio",
"command": "npx",
"args": ["@mastra/mcp-docs-server@latest"],
"env": {}
},
"browsermcp": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@browsermcp/mcp@latest"],
"env": {}
}
}
}

View File

@ -1,76 +0,0 @@
# Development stage - for docker-compose development with hot reload
FROM node:20-alpine AS development
WORKDIR /app
# Install pnpm globally
RUN npm install -g pnpm
# Copy package files for dependency installation
COPY package*.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
# Copy source code
COPY . .
# Expose port
EXPOSE 3000
# Use development command with hot reload
CMD ["pnpm", "dev"]
# Builder stage - for production build
FROM node:20-alpine AS builder
WORKDIR /app
# Install pnpm globally
RUN npm install -g pnpm
# Copy package files
COPY package*.json pnpm-lock.yaml ./
# Install all dependencies (including dev dependencies for build)
RUN pnpm install --frozen-lockfile
# Copy source code
COPY . .
# Build TypeScript to JavaScript
RUN pnpm build
# Production stage - optimized final image
FROM node:20-alpine AS production
WORKDIR /app
# Create non-root user for security
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
# Install pnpm globally
RUN npm install -g pnpm
# Copy package files
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/pnpm-lock.yaml ./
# Install only production dependencies
RUN pnpm install --prod --frozen-lockfile
# Copy built application
COPY --from=builder /app/dist ./dist
# Create required directories and set ownership
RUN mkdir -p logs && chown -R nodejs:nodejs /app
# Switch to non-root user
USER nodejs
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD node -e "require('http').request('http://localhost:3000/health', (res) => { \
process.exit(res.statusCode === 200 ? 0 : 1) \
}).on('error', () => process.exit(1)).end()"
# Start the application
CMD ["node", "dist/server.js"]

View File

@ -1,129 +0,0 @@
version: '3.8'
services:
app:
build:
context: .
target: development
container_name: banatie-app-dev
ports:
- "3000:3000"
volumes:
- ./src:/app/src # Hot reload for development
- ./logs:/app/logs # Persistent logs
networks:
- banatie-dev
depends_on:
postgres:
condition: service_healthy
minio:
condition: service_healthy
environment:
- NODE_ENV=development
env_file:
- .env.docker
restart: unless-stopped
postgres:
image: postgres:15-alpine
container_name: banatie-postgres-dev
ports:
- "5434:5432" # Avoid conflicts with other PostgreSQL instances
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
start_period: 40s
restart: unless-stopped
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
start_period: 40s
restart: unless-stopped
minio-init:
image: minio/mc:latest
container_name: banatie-minio-init
networks:
- banatie-dev
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
echo 'Setting up MinIO alias...';
mc alias set minio http://minio:9000 minioadmin minioadmin;
echo 'Creating demo bucket...';
mc mb --ignore-existing minio/banatie-demo;
echo 'Setting up public read policy for generated images...';
mc anonymous set download minio/banatie-demo/users/guest/generated;
echo 'Creating banatie service user...';
mc admin user add minio banatie-user banatie-password;
echo 'Attaching readwrite policy to banatie user...';
mc admin policy attach minio readwrite --user=banatie-user;
echo 'Creating lifecycle policy for temp files (7 days retention)...';
cat > /tmp/lifecycle.json << EOF
{
\"Rules\": [
{
\"ID\": \"temp-files-cleanup\",
\"Status\": \"Enabled\",
\"Filter\": {
\"Prefix\": \"users/\"
},
\"Expiration\": {
\"Days\": 7
}
}
]
}
EOF
mc ilm import minio/banatie-demo < /tmp/lifecycle.json;
echo 'MinIO initialization completed successfully!';
exit 0;
"
restart: "no"
networks:
banatie-dev:
driver: bridge
name: banatie-dev-network
volumes:
postgres-data:
driver: local
minio-data:
driver: local

View File

@ -1,937 +0,0 @@
# 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<void>;
deleteBucket(orgId: string): Promise<void>;
listBuckets(): Promise<string[]>;
// File operations
uploadFile(orgId: string, userId: string, fileName: string, buffer: Buffer, contentType: string): Promise<string>;
downloadFile(orgId: string, userId: string, fileName: string): Promise<Buffer>;
deleteFile(orgId: string, userId: string, fileName: string): Promise<void>;
// URL generation
getPresignedUploadUrl(orgId: string, userId: string, fileName: string, expirySeconds: number): Promise<string>;
getPresignedDownloadUrl(orgId: string, userId: string, fileName: string, expirySeconds: number): Promise<string>;
// File management
listUserFiles(orgId: string, userId: string): Promise<FileMetadata[]>;
moveFile(fromPath: string, toPath: string): Promise<void>;
copyFile(fromPath: string, toPath: string): Promise<void>;
}
```
### 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 <repository-url>
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 <repository-url> .
# 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

View File

@ -43,8 +43,7 @@
"express-rate-limit": "^7.4.1", "express-rate-limit": "^7.4.1",
"express-validator": "^7.2.0", "express-validator": "^7.2.0",
"helmet": "^8.0.0", "helmet": "^8.0.0",
"mime": "3.0.0", "mime": "^4.1.0",
"minio": "^8.0.6",
"multer": "^2.0.2", "multer": "^2.0.2",
"winston": "^3.17.0" "winston": "^3.17.0"
}, },

View File

@ -30,11 +30,8 @@ importers:
specifier: ^8.0.0 specifier: ^8.0.0
version: 8.1.0 version: 8.1.0
mime: mime:
specifier: 3.0.0 specifier: ^4.1.0
version: 3.0.0 version: 4.1.0
minio:
specifier: ^8.0.6
version: 8.0.6
multer: multer:
specifier: ^2.0.2 specifier: ^2.0.2
version: 2.0.2 version: 2.0.2
@ -770,9 +767,6 @@ packages:
resolution: {integrity: sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==} resolution: {integrity: sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@zxing/text-encoding@0.9.0':
resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==}
accepts@2.0.0: accepts@2.0.0:
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
@ -832,10 +826,6 @@ packages:
asynckit@0.4.0: asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
babel-jest@29.7.0: babel-jest@29.7.0:
resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@ -878,9 +868,6 @@ packages:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'} engines: {node: '>=8'}
block-stream2@2.1.0:
resolution: {integrity: sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==}
body-parser@2.2.0: body-parser@2.2.0:
resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -895,9 +882,6 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'} engines: {node: '>=8'}
browser-or-node@2.1.1:
resolution: {integrity: sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==}
browserslist@4.26.2: browserslist@4.26.2:
resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==} resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@ -910,10 +894,6 @@ packages:
bser@2.1.1: bser@2.1.1:
resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
buffer-crc32@1.0.0:
resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==}
engines: {node: '>=8.0.0'}
buffer-equal-constant-time@1.0.1: buffer-equal-constant-time@1.0.1:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
@ -932,10 +912,6 @@ packages:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
call-bind@1.0.8:
resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
engines: {node: '>= 0.4'}
call-bound@1.0.4: call-bound@1.0.4:
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1065,10 +1041,6 @@ packages:
supports-color: supports-color:
optional: true optional: true
decode-uri-component@0.2.2:
resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
engines: {node: '>=0.10'}
dedent@1.7.0: dedent@1.7.0:
resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==}
peerDependencies: peerDependencies:
@ -1084,10 +1056,6 @@ packages:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
delayed-stream@1.0.0: delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
@ -1248,9 +1216,6 @@ packages:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
execa@5.1.1: execa@5.1.1:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -1299,10 +1264,6 @@ packages:
fast-safe-stringify@2.1.1: fast-safe-stringify@2.1.1:
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
fast-xml-parser@4.5.3:
resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==}
hasBin: true
fastq@1.19.1: fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
@ -1320,10 +1281,6 @@ packages:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'} engines: {node: '>=8'}
filter-obj@1.1.0:
resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==}
engines: {node: '>=0.10.0'}
finalhandler@2.1.0: finalhandler@2.1.0:
resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@ -1346,10 +1303,6 @@ packages:
fn.name@1.1.0: fn.name@1.1.0:
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
for-each@0.3.5:
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
engines: {node: '>= 0.4'}
form-data@4.0.4: form-data@4.0.4:
resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
@ -1463,9 +1416,6 @@ packages:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
has-property-descriptors@1.0.2:
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
has-symbols@1.1.0: has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1540,14 +1490,6 @@ packages:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}
ipaddr.js@2.2.0:
resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==}
engines: {node: '>= 10'}
is-arguments@1.2.0:
resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==}
engines: {node: '>= 0.4'}
is-arrayish@0.2.1: is-arrayish@0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
@ -1558,10 +1500,6 @@ packages:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'} engines: {node: '>=8'}
is-callable@1.2.7:
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
engines: {node: '>= 0.4'}
is-core-module@2.16.1: is-core-module@2.16.1:
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1578,10 +1516,6 @@ packages:
resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
is-generator-function@1.1.0:
resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
engines: {node: '>= 0.4'}
is-glob@4.0.3: is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -1593,18 +1527,10 @@ packages:
is-promise@4.0.0: is-promise@4.0.0:
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
is-regex@1.2.1:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'}
is-stream@2.0.1: is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'} engines: {node: '>=8'}
is-typed-array@1.1.15:
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
engines: {node: '>= 0.4'}
isexe@2.0.0: isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@ -1910,9 +1836,9 @@ packages:
engines: {node: '>=4.0.0'} engines: {node: '>=4.0.0'}
hasBin: true hasBin: true
mime@3.0.0: mime@4.1.0:
resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==}
engines: {node: '>=10.0.0'} engines: {node: '>=16'}
hasBin: true hasBin: true
mimic-fn@2.1.0: mimic-fn@2.1.0:
@ -1929,10 +1855,6 @@ packages:
minimist@1.2.8: minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
minio@8.0.6:
resolution: {integrity: sha512-sOeh2/b/XprRmEtYsnNRFtOqNRTPDvYtMWh+spWlfsuCV/+IdxNeKVUMKLqI7b5Dr07ZqCPuaRGU/rB9pZYVdQ==}
engines: {node: ^16 || ^18 || >=20}
mkdirp@0.5.6: mkdirp@0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true hasBin: true
@ -2073,10 +1995,6 @@ packages:
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
prelude-ls@1.2.1: prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
@ -2116,10 +2034,6 @@ packages:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'} engines: {node: '>=0.6'}
query-string@7.1.3:
resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==}
engines: {node: '>=6'}
queue-microtask@1.2.3: queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@ -2184,10 +2098,6 @@ packages:
safe-buffer@5.2.1: safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
safe-regex-test@1.1.0:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
safe-stable-stringify@2.5.0: safe-stable-stringify@2.5.0:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -2195,9 +2105,6 @@ packages:
safer-buffer@2.1.2: safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
sax@1.4.1:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
semver@6.3.1: semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true hasBin: true
@ -2215,10 +2122,6 @@ packages:
resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==}
engines: {node: '>= 18'} engines: {node: '>= 18'}
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
setprototypeof@1.2.0: setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
@ -2270,10 +2173,6 @@ packages:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
split-on-first@1.1.0:
resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==}
engines: {node: '>=6'}
sprintf-js@1.0.3: sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
@ -2292,20 +2191,10 @@ packages:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
stream-chain@2.2.5:
resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==}
stream-json@1.9.1:
resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==}
streamsearch@1.1.0: streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
strict-uri-encode@2.0.0:
resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==}
engines: {node: '>=4'}
string-length@4.0.2: string-length@4.0.2:
resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -2333,9 +2222,6 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'} engines: {node: '>=8'}
strnum@1.1.2:
resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==}
superagent@10.2.3: superagent@10.2.3:
resolution: {integrity: sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==} resolution: {integrity: sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==}
engines: {node: '>=14.18.0'} engines: {node: '>=14.18.0'}
@ -2371,9 +2257,6 @@ packages:
text-hex@1.0.0: text-hex@1.0.0:
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
through2@4.0.2:
resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
tmpl@1.0.5: tmpl@1.0.5:
resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
@ -2493,9 +2376,6 @@ packages:
util-deprecate@1.0.2: util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
util@0.12.5:
resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
uuid@9.0.1: uuid@9.0.1:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
hasBin: true hasBin: true
@ -2515,19 +2395,12 @@ packages:
walker@1.0.8: walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
web-encoding@1.1.5:
resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==}
webidl-conversions@3.0.1: webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
whatwg-url@5.0.0: whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
which-typed-array@1.1.19:
resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
engines: {node: '>= 0.4'}
which@2.0.2: which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -2571,14 +2444,6 @@ packages:
utf-8-validate: utf-8-validate:
optional: true optional: true
xml2js@0.6.2:
resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
engines: {node: '>=4.0.0'}
xmlbuilder@11.0.1:
resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
engines: {node: '>=4.0'}
xtend@4.0.2: xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'} engines: {node: '>=0.4'}
@ -3381,9 +3246,6 @@ snapshots:
'@typescript-eslint/types': 8.44.0 '@typescript-eslint/types': 8.44.0
eslint-visitor-keys: 4.2.1 eslint-visitor-keys: 4.2.1
'@zxing/text-encoding@0.9.0':
optional: true
accepts@2.0.0: accepts@2.0.0:
dependencies: dependencies:
mime-types: 3.0.1 mime-types: 3.0.1
@ -3435,10 +3297,6 @@ snapshots:
asynckit@0.4.0: {} asynckit@0.4.0: {}
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.1.0
babel-jest@29.7.0(@babel/core@7.28.4): babel-jest@29.7.0(@babel/core@7.28.4):
dependencies: dependencies:
'@babel/core': 7.28.4 '@babel/core': 7.28.4
@ -3504,10 +3362,6 @@ snapshots:
binary-extensions@2.3.0: {} binary-extensions@2.3.0: {}
block-stream2@2.1.0:
dependencies:
readable-stream: 3.6.2
body-parser@2.2.0: body-parser@2.2.0:
dependencies: dependencies:
bytes: 3.1.2 bytes: 3.1.2
@ -3535,8 +3389,6 @@ snapshots:
dependencies: dependencies:
fill-range: 7.1.1 fill-range: 7.1.1
browser-or-node@2.1.1: {}
browserslist@4.26.2: browserslist@4.26.2:
dependencies: dependencies:
baseline-browser-mapping: 2.8.6 baseline-browser-mapping: 2.8.6
@ -3553,8 +3405,6 @@ snapshots:
dependencies: dependencies:
node-int64: 0.4.0 node-int64: 0.4.0
buffer-crc32@1.0.0: {}
buffer-equal-constant-time@1.0.1: {} buffer-equal-constant-time@1.0.1: {}
buffer-from@1.1.2: {} buffer-from@1.1.2: {}
@ -3570,13 +3420,6 @@ snapshots:
es-errors: 1.3.0 es-errors: 1.3.0
function-bind: 1.1.2 function-bind: 1.1.2
call-bind@1.0.8:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
get-intrinsic: 1.3.0
set-function-length: 1.2.2
call-bound@1.0.4: call-bound@1.0.4:
dependencies: dependencies:
call-bind-apply-helpers: 1.0.2 call-bind-apply-helpers: 1.0.2
@ -3711,20 +3554,12 @@ snapshots:
optionalDependencies: optionalDependencies:
supports-color: 5.5.0 supports-color: 5.5.0
decode-uri-component@0.2.2: {}
dedent@1.7.0: {} dedent@1.7.0: {}
deep-is@0.1.4: {} deep-is@0.1.4: {}
deepmerge@4.3.1: {} deepmerge@4.3.1: {}
define-data-property@1.1.4:
dependencies:
es-define-property: 1.0.1
es-errors: 1.3.0
gopd: 1.2.0
delayed-stream@1.0.0: {} delayed-stream@1.0.0: {}
depd@2.0.0: {} depd@2.0.0: {}
@ -3902,8 +3737,6 @@ snapshots:
etag@1.8.1: {} etag@1.8.1: {}
eventemitter3@5.0.1: {}
execa@5.1.1: execa@5.1.1:
dependencies: dependencies:
cross-spawn: 7.0.6 cross-spawn: 7.0.6
@ -3987,10 +3820,6 @@ snapshots:
fast-safe-stringify@2.1.1: {} fast-safe-stringify@2.1.1: {}
fast-xml-parser@4.5.3:
dependencies:
strnum: 1.1.2
fastq@1.19.1: fastq@1.19.1:
dependencies: dependencies:
reusify: 1.1.0 reusify: 1.1.0
@ -4009,8 +3838,6 @@ snapshots:
dependencies: dependencies:
to-regex-range: 5.0.1 to-regex-range: 5.0.1
filter-obj@1.1.0: {}
finalhandler@2.1.0: finalhandler@2.1.0:
dependencies: dependencies:
debug: 4.4.3(supports-color@5.5.0) debug: 4.4.3(supports-color@5.5.0)
@ -4041,10 +3868,6 @@ snapshots:
fn.name@1.1.0: {} fn.name@1.1.0: {}
for-each@0.3.5:
dependencies:
is-callable: 1.2.7
form-data@4.0.4: form-data@4.0.4:
dependencies: dependencies:
asynckit: 0.4.0 asynckit: 0.4.0
@ -4180,10 +4003,6 @@ snapshots:
has-flag@4.0.0: {} has-flag@4.0.0: {}
has-property-descriptors@1.0.2:
dependencies:
es-define-property: 1.0.1
has-symbols@1.1.0: {} has-symbols@1.1.0: {}
has-tostringtag@1.0.2: has-tostringtag@1.0.2:
@ -4250,13 +4069,6 @@ snapshots:
ipaddr.js@1.9.1: {} ipaddr.js@1.9.1: {}
ipaddr.js@2.2.0: {}
is-arguments@1.2.0:
dependencies:
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-arrayish@0.2.1: {} is-arrayish@0.2.1: {}
is-arrayish@0.3.4: {} is-arrayish@0.3.4: {}
@ -4265,8 +4077,6 @@ snapshots:
dependencies: dependencies:
binary-extensions: 2.3.0 binary-extensions: 2.3.0
is-callable@1.2.7: {}
is-core-module@2.16.1: is-core-module@2.16.1:
dependencies: dependencies:
hasown: 2.0.2 hasown: 2.0.2
@ -4277,13 +4087,6 @@ snapshots:
is-generator-fn@2.1.0: {} is-generator-fn@2.1.0: {}
is-generator-function@1.1.0:
dependencies:
call-bound: 1.0.4
get-proto: 1.0.1
has-tostringtag: 1.0.2
safe-regex-test: 1.1.0
is-glob@4.0.3: is-glob@4.0.3:
dependencies: dependencies:
is-extglob: 2.1.1 is-extglob: 2.1.1
@ -4292,19 +4095,8 @@ snapshots:
is-promise@4.0.0: {} is-promise@4.0.0: {}
is-regex@1.2.1:
dependencies:
call-bound: 1.0.4
gopd: 1.2.0
has-tostringtag: 1.0.2
hasown: 2.0.2
is-stream@2.0.1: {} is-stream@2.0.1: {}
is-typed-array@1.1.15:
dependencies:
which-typed-array: 1.1.19
isexe@2.0.0: {} isexe@2.0.0: {}
istanbul-lib-coverage@3.2.2: {} istanbul-lib-coverage@3.2.2: {}
@ -4781,7 +4573,7 @@ snapshots:
mime@2.6.0: {} mime@2.6.0: {}
mime@3.0.0: {} mime@4.1.0: {}
mimic-fn@2.1.0: {} mimic-fn@2.1.0: {}
@ -4795,23 +4587,6 @@ snapshots:
minimist@1.2.8: {} minimist@1.2.8: {}
minio@8.0.6:
dependencies:
async: 3.2.6
block-stream2: 2.1.0
browser-or-node: 2.1.1
buffer-crc32: 1.0.0
eventemitter3: 5.0.1
fast-xml-parser: 4.5.3
ipaddr.js: 2.2.0
lodash: 4.17.21
mime-types: 2.1.35
query-string: 7.1.3
stream-json: 1.9.1
through2: 4.0.2
web-encoding: 1.1.5
xml2js: 0.6.2
mkdirp@0.5.6: mkdirp@0.5.6:
dependencies: dependencies:
minimist: 1.2.8 minimist: 1.2.8
@ -4941,8 +4716,6 @@ snapshots:
dependencies: dependencies:
find-up: 4.1.0 find-up: 4.1.0
possible-typed-array-names@1.1.0: {}
prelude-ls@1.2.1: {} prelude-ls@1.2.1: {}
prettier-linter-helpers@1.0.0: prettier-linter-helpers@1.0.0:
@ -4977,13 +4750,6 @@ snapshots:
dependencies: dependencies:
side-channel: 1.1.0 side-channel: 1.1.0
query-string@7.1.3:
dependencies:
decode-uri-component: 0.2.2
filter-obj: 1.1.0
split-on-first: 1.1.0
strict-uri-encode: 2.0.0
queue-microtask@1.2.3: {} queue-microtask@1.2.3: {}
range-parser@1.2.1: {} range-parser@1.2.1: {}
@ -5045,18 +4811,10 @@ snapshots:
safe-buffer@5.2.1: {} safe-buffer@5.2.1: {}
safe-regex-test@1.1.0:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
is-regex: 1.2.1
safe-stable-stringify@2.5.0: {} safe-stable-stringify@2.5.0: {}
safer-buffer@2.1.2: {} safer-buffer@2.1.2: {}
sax@1.4.1: {}
semver@6.3.1: {} semver@6.3.1: {}
semver@7.7.2: {} semver@7.7.2: {}
@ -5086,15 +4844,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
set-function-length@1.2.2:
dependencies:
define-data-property: 1.1.4
es-errors: 1.3.0
function-bind: 1.1.2
get-intrinsic: 1.3.0
gopd: 1.2.0
has-property-descriptors: 1.0.2
setprototypeof@1.2.0: {} setprototypeof@1.2.0: {}
shebang-command@2.0.0: shebang-command@2.0.0:
@ -5152,8 +4901,6 @@ snapshots:
source-map@0.6.1: {} source-map@0.6.1: {}
split-on-first@1.1.0: {}
sprintf-js@1.0.3: {} sprintf-js@1.0.3: {}
stack-trace@0.0.10: {} stack-trace@0.0.10: {}
@ -5166,16 +4913,8 @@ snapshots:
statuses@2.0.2: {} statuses@2.0.2: {}
stream-chain@2.2.5: {}
stream-json@1.9.1:
dependencies:
stream-chain: 2.2.5
streamsearch@1.1.0: {} streamsearch@1.1.0: {}
strict-uri-encode@2.0.0: {}
string-length@4.0.2: string-length@4.0.2:
dependencies: dependencies:
char-regex: 1.0.2 char-regex: 1.0.2
@ -5201,8 +4940,6 @@ snapshots:
strip-json-comments@3.1.1: {} strip-json-comments@3.1.1: {}
strnum@1.1.2: {}
superagent@10.2.3: superagent@10.2.3:
dependencies: dependencies:
component-emitter: 1.3.1 component-emitter: 1.3.1
@ -5250,10 +4987,6 @@ snapshots:
text-hex@1.0.0: {} text-hex@1.0.0: {}
through2@4.0.2:
dependencies:
readable-stream: 3.6.2
tmpl@1.0.5: {} tmpl@1.0.5: {}
to-regex-range@5.0.1: to-regex-range@5.0.1:
@ -5345,14 +5078,6 @@ snapshots:
util-deprecate@1.0.2: {} util-deprecate@1.0.2: {}
util@0.12.5:
dependencies:
inherits: 2.0.4
is-arguments: 1.2.0
is-generator-function: 1.1.0
is-typed-array: 1.1.15
which-typed-array: 1.1.19
uuid@9.0.1: {} uuid@9.0.1: {}
v8-to-istanbul@9.3.0: v8-to-istanbul@9.3.0:
@ -5369,12 +5094,6 @@ snapshots:
dependencies: dependencies:
makeerror: 1.0.12 makeerror: 1.0.12
web-encoding@1.1.5:
dependencies:
util: 0.12.5
optionalDependencies:
'@zxing/text-encoding': 0.9.0
webidl-conversions@3.0.1: {} webidl-conversions@3.0.1: {}
whatwg-url@5.0.0: whatwg-url@5.0.0:
@ -5382,16 +5101,6 @@ snapshots:
tr46: 0.0.3 tr46: 0.0.3
webidl-conversions: 3.0.1 webidl-conversions: 3.0.1
which-typed-array@1.1.19:
dependencies:
available-typed-arrays: 1.0.7
call-bind: 1.0.8
call-bound: 1.0.4
for-each: 0.3.5
get-proto: 1.0.1
gopd: 1.2.0
has-tostringtag: 1.0.2
which@2.0.2: which@2.0.2:
dependencies: dependencies:
isexe: 2.0.0 isexe: 2.0.0
@ -5435,13 +5144,6 @@ snapshots:
ws@8.18.3: {} ws@8.18.3: {}
xml2js@0.6.2:
dependencies:
sax: 1.4.1
xmlbuilder: 11.0.1
xmlbuilder@11.0.1: {}
xtend@4.0.2: {} xtend@4.0.2: {}
y18n@5.0.8: {} y18n@5.0.8: {}

View File

@ -1,103 +0,0 @@
-- Banatie Database Initialization Script
-- This script creates the database schema for the Banatie image generation service
-- Enable UUID extension for generating UUIDs
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Organizations table - for multi-tenant support
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 - users within organizations
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 - metadata for generated and uploaded images
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), -- Legacy: local file path (for backward compatibility)
minio_key VARCHAR(500), -- MinIO object key
url VARCHAR(1000), -- Public or presigned URL to access the image
original_prompt TEXT,
enhanced_prompt TEXT,
model_used VARCHAR(100),
file_size BIGINT,
content_type VARCHAR(100),
category VARCHAR(50) DEFAULT 'generated', -- 'generated', 'references', 'temp'
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Sessions table - for tracking user upload sessions
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 for better query performance
CREATE INDEX idx_users_org_id ON users(organization_id);
CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_images_user_id ON images(user_id);
CREATE INDEX idx_images_created_at ON images(created_at);
CREATE INDEX idx_images_category ON images(category);
CREATE INDEX idx_images_minio_key ON images(minio_key);
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 for development/testing
INSERT INTO organizations (id, name, slug, settings) VALUES
('00000000-0000-0000-0000-000000000001', 'Demo Organization', 'demo', '{"description": "Default demo organization for testing"}');
INSERT INTO users (id, organization_id, username, email, role, settings) VALUES
('00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000001', 'guest', 'guest@demo.banatie.app', 'user', '{"description": "Default guest user for testing"}');
-- Create a function to update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Create triggers to automatically update updated_at
CREATE TRIGGER update_organizations_updated_at
BEFORE UPDATE ON organizations
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_images_updated_at
BEFORE UPDATE ON images
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Display initialization completion message
DO $$
BEGIN
RAISE NOTICE 'Banatie database initialization completed successfully!';
RAISE NOTICE 'Created tables: organizations, users, images, upload_sessions';
RAISE NOTICE 'Created demo organization (id: 00000000-0000-0000-0000-000000000001) with guest user';
END $$;

View File

@ -48,7 +48,7 @@ export const validateGenerateRequest = (
try { try {
enhancementOptions = JSON.parse(enhancementOptions); enhancementOptions = JSON.parse(enhancementOptions);
req.body.enhancementOptions = enhancementOptions; req.body.enhancementOptions = enhancementOptions;
} catch { } catch (error) {
errors.push("enhancementOptions must be valid JSON"); errors.push("enhancementOptions must be valid JSON");
} }
} }

View File

@ -1,6 +1,5 @@
import { GoogleGenAI } from "@google/genai"; import { GoogleGenAI } from "@google/genai";
// eslint-disable-next-line @typescript-eslint/no-var-requires import mime from "mime";
const mime = require("mime") as any;
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import { import {