Compare commits
No commits in common. "839eeac2c2a065d9cfdc2d6fc2ee3ce8408b6eaf" and "3c922c861be54b2155ce2f0658c1e2c527a48632" have entirely different histories.
839eeac2c2
...
3c922c861b
|
|
@ -9,9 +9,6 @@ pnpm-debug.log*
|
||||||
dist/
|
dist/
|
||||||
build/
|
build/
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
apps/*/dist/
|
|
||||||
apps/*/.next/
|
|
||||||
.next/
|
|
||||||
|
|
||||||
# Environment files
|
# Environment files
|
||||||
.env
|
.env
|
||||||
|
|
@ -38,7 +35,6 @@ Thumbs.db
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs/
|
logs/
|
||||||
apps/*/logs/
|
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
# Runtime data
|
# Runtime data
|
||||||
|
|
|
||||||
132
CLAUDE.md
132
CLAUDE.md
|
|
@ -4,81 +4,44 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
Banatie is a comprehensive monorepo for AI-powered image generation platform featuring multiple applications:
|
Banatie is a REST API service for AI-powered image generation using the Gemini Flash Image model. It's built with Express.js and TypeScript, providing endpoints to generate images from text prompts with optional reference images.
|
||||||
|
|
||||||
- **API Service** (`apps/api-service`) - Core REST API using Express.js and TypeScript for image generation with Gemini AI
|
|
||||||
- **Landing Page** (`apps/landing`) - Next.js public website with demo functionality
|
|
||||||
- **Studio Platform** (`apps/studio`) - Next.js SaaS application with authentication, billing, and subscriptions. Based on https://github.com/nextjs/saas-starter
|
|
||||||
- **Admin Dashboard** (`apps/admin`) - Next.js administration interface for monitoring and management
|
|
||||||
|
|
||||||
The project uses MinIO for object storage and PostgreSQL for data persistence, all orchestrated with Docker Compose.
|
|
||||||
|
|
||||||
## Development Commands
|
## Development Commands
|
||||||
|
|
||||||
Use `docker compose` command for Docker services (v3 version).
|
use `docker compose` command for using docker-compose service (v3 version)
|
||||||
|
|
||||||
### Infrastructure
|
### Core Development
|
||||||
|
- `pnpm dev` - Start development server with auto-reload using tsx
|
||||||
|
- `pnpm start` - Start production server (runs build first)
|
||||||
|
- `pnpm build` - Build TypeScript to JavaScript in ./dist
|
||||||
|
- `pnpm typecheck` - Run TypeScript type checking without emitting files
|
||||||
|
|
||||||
- `docker compose up -d postgres minio storage-init` - Start database and storage services
|
### Code Quality
|
||||||
- `docker compose up -d` - Start all services including the API app container
|
- `pnpm lint` - Run ESLint on all TypeScript files in src/
|
||||||
- `docker compose down` - Stop all services
|
- `pnpm lint:fix` - Run ESLint with auto-fix
|
||||||
|
- `pnpm format` - Format code with Prettier
|
||||||
|
- `pnpm format:check` - Check if code is formatted correctly
|
||||||
|
|
||||||
### Monorepo Commands (from root)
|
### Testing
|
||||||
|
- `pnpm test` - Run Jest tests
|
||||||
- `pnpm dev` - Start all applications in development mode
|
- `pnpm test:watch` - Run tests in watch mode
|
||||||
- `pnpm dev:api` - Start API service only (`apps/api-service`)
|
- `pnpm test:coverage` - Run tests with coverage report
|
||||||
- `pnpm dev:landing` - Start landing page only (`apps/landing`)
|
|
||||||
- `pnpm dev:studio` - Start studio platform only (`apps/studio`)
|
|
||||||
- `pnpm dev:admin` - Start admin dashboard only (`apps/admin`)
|
|
||||||
|
|
||||||
### Build & Production
|
|
||||||
|
|
||||||
- `pnpm build` - Build all applications
|
|
||||||
- `pnpm build:api` - Build API service only
|
|
||||||
- `pnpm start:api` - Start API service in production mode
|
|
||||||
- `pnpm start:landing` - Start landing page in production mode
|
|
||||||
|
|
||||||
### Code Quality (runs across all apps)
|
|
||||||
|
|
||||||
- `pnpm lint` - Run ESLint on all applications
|
|
||||||
- `pnpm typecheck` - Run TypeScript checking on all applications
|
|
||||||
- `pnpm test` - Run tests (currently API service only)
|
|
||||||
- `pnpm clean` - Clean all build outputs and dependencies
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Monorepo Structure
|
### Core Structure
|
||||||
|
|
||||||
```
|
|
||||||
banatie-service/
|
|
||||||
├── apps/
|
|
||||||
│ ├── api-service/ # Express.js REST API (TypeScript)
|
|
||||||
│ ├── landing/ # Next.js landing page
|
|
||||||
│ ├── studio/ # Next.js SaaS platform
|
|
||||||
│ └── admin/ # Next.js admin dashboard
|
|
||||||
├── data/ # Docker volume data (postgres, minio)
|
|
||||||
├── docker-compose.yml # Infrastructure services
|
|
||||||
├── pnpm-workspace.yaml # Workspace configuration
|
|
||||||
└── package.json # Root workspace scripts
|
|
||||||
```
|
|
||||||
|
|
||||||
### API Service Architecture (`apps/api-service/`)
|
|
||||||
|
|
||||||
- **Express App**: Configured in `src/app.ts` with middleware, CORS, and route mounting
|
- **Express App**: Configured in `src/app.ts` with middleware, CORS, and route mounting
|
||||||
- **Server Entry**: `src/server.ts` starts the HTTP server
|
- **Server Entry**: `src/server.ts` starts the HTTP server
|
||||||
- **Image Generation**: `src/services/ImageGenService.ts` handles Gemini AI integration
|
- **Image Generation**: `src/services/ImageGenService.ts` handles Gemini AI integration
|
||||||
- **Storage**: `src/services/MinioStorageService.ts` handles file uploads to MinIO
|
|
||||||
- **Route Handling**: `src/routes/generate.ts` contains the main API endpoint logic
|
- **Route Handling**: `src/routes/generate.ts` contains the main API endpoint logic
|
||||||
|
|
||||||
### Middleware Stack (API Service)
|
### Middleware Stack
|
||||||
|
|
||||||
- `src/middleware/upload.ts` - Multer configuration for file uploads (max 3 files, 5MB each)
|
- `src/middleware/upload.ts` - Multer configuration for file uploads (max 3 files, 5MB each)
|
||||||
- `src/middleware/validation.ts` - Express-validator for request validation
|
- `src/middleware/validation.ts` - Express-validator for request validation
|
||||||
- `src/middleware/errorHandler.ts` - Centralized error handling and 404 responses
|
- `src/middleware/errorHandler.ts` - Centralized error handling and 404 responses
|
||||||
|
|
||||||
### TypeScript Configuration (API Service)
|
### TypeScript Configuration
|
||||||
|
- Path aliases configured in tsconfig.json:
|
||||||
- Path aliases configured in `apps/api-service/tsconfig.json`:
|
|
||||||
- `@/*` maps to `src/*`
|
- `@/*` maps to `src/*`
|
||||||
- `@/types/*` maps to `src/types/*`
|
- `@/types/*` maps to `src/types/*`
|
||||||
- `@/services/*` maps to `src/services/*`
|
- `@/services/*` maps to `src/services/*`
|
||||||
|
|
@ -86,66 +49,30 @@ banatie-service/
|
||||||
- `@/routes/*` maps to `src/routes/*`
|
- `@/routes/*` maps to `src/routes/*`
|
||||||
- `@/utils/*` maps to `src/utils/*`
|
- `@/utils/*` maps to `src/utils/*`
|
||||||
|
|
||||||
### Storage & Data
|
### File Handling
|
||||||
|
- **Uploads**: Temporary files stored in `./uploads/temp`
|
||||||
- **MinIO**: Object storage for generated images and uploads (port 9000)
|
- **Results**: Generated images saved to `./results`
|
||||||
- **PostgreSQL**: Database for user data, metadata (port 5434)
|
- **Logs**: Application logs in `./logs`
|
||||||
- **File Organization**: `orgId/projectId/category/year-month/filename.ext`
|
|
||||||
|
|
||||||
## Environment Configuration
|
## Environment Configuration
|
||||||
|
|
||||||
### Root Environment (`.env.docker`)
|
|
||||||
|
|
||||||
- `MINIO_ROOT_USER` - MinIO admin username
|
|
||||||
- `MINIO_ROOT_PASSWORD` - MinIO admin password
|
|
||||||
|
|
||||||
### API Service Environment (`apps/api-service/.env`)
|
|
||||||
|
|
||||||
Required environment variables:
|
Required environment variables:
|
||||||
|
|
||||||
- `GEMINI_API_KEY` - Google Gemini API key (required)
|
- `GEMINI_API_KEY` - Google Gemini API key (required)
|
||||||
- `MINIO_ENDPOINT` - MinIO endpoint (`localhost:9000` for local dev, `minio:9000` for Docker)
|
|
||||||
- `MINIO_ACCESS_KEY` - MinIO service account key
|
|
||||||
- `MINIO_SECRET_KEY` - MinIO service account secret
|
|
||||||
- `MINIO_BUCKET_NAME` - Storage bucket name (default: `banatie`)
|
|
||||||
- `PORT` - Server port (default: 3000)
|
- `PORT` - Server port (default: 3000)
|
||||||
- `NODE_ENV` - Environment mode
|
- `NODE_ENV` - Environment mode
|
||||||
- `CORS_ORIGIN` - CORS origin setting (default: \*)
|
- `CORS_ORIGIN` - CORS origin setting (default: *)
|
||||||
|
|
||||||
## Key Dependencies
|
## Key Dependencies
|
||||||
|
|
||||||
### API Service
|
|
||||||
|
|
||||||
- **@google/genai** - Google Gemini AI client
|
- **@google/genai** - Google Gemini AI client
|
||||||
- **express** v5 - Web framework
|
- **express** v5 - Web framework
|
||||||
- **multer** - File upload handling
|
- **multer** - File upload handling
|
||||||
- **minio** - MinIO client for object storage
|
|
||||||
- **express-validator** - Request validation
|
- **express-validator** - Request validation
|
||||||
- **winston** - Logging
|
- **winston** - Logging
|
||||||
- **helmet** - Security middleware
|
- **helmet** - Security middleware
|
||||||
- **express-rate-limit** - Rate limiting
|
- **express-rate-limit** - Rate limiting
|
||||||
|
|
||||||
### Frontend Apps (Next.js)
|
## API Endpoints
|
||||||
|
|
||||||
- **next** - React framework
|
|
||||||
- **tailwindcss** - Utility-first CSS framework
|
|
||||||
- **typescript** - Type safety
|
|
||||||
- **supabase** - Authentication and database (studio app)
|
|
||||||
- **stripe** - Payment processing (studio app)
|
|
||||||
|
|
||||||
## Service Ports
|
|
||||||
|
|
||||||
| Service | Port | Description |
|
|
||||||
| --------------- | ---- | ------------------ |
|
|
||||||
| API Service | 3000 | REST API endpoints |
|
|
||||||
| Landing Page | 3001 | Public website |
|
|
||||||
| Studio Platform | 3002 | SaaS application |
|
|
||||||
| Admin Dashboard | 3003 | Administration |
|
|
||||||
| PostgreSQL | 5434 | Database |
|
|
||||||
| MinIO API | 9000 | Object storage |
|
|
||||||
| MinIO Console | 9001 | Storage management |
|
|
||||||
|
|
||||||
## API Endpoints (API Service)
|
|
||||||
|
|
||||||
- `GET /health` - Health check with uptime and status
|
- `GET /health` - Health check with uptime and status
|
||||||
- `GET /api/info` - API information and limits
|
- `GET /api/info` - API information and limits
|
||||||
|
|
@ -153,9 +80,8 @@ Required environment variables:
|
||||||
|
|
||||||
## Development Notes
|
## Development Notes
|
||||||
|
|
||||||
- Uses pnpm workspaces for monorepo management (required >= 8.0.0)
|
- Uses pnpm as package manager (required >= 8.0.0)
|
||||||
- Node.js >= 18.0.0 required
|
- Node.js >= 18.0.0 required
|
||||||
- TypeScript with strict mode enabled across all apps
|
- TypeScript with strict mode enabled
|
||||||
- ESLint configured with TypeScript and Prettier integration
|
- ESLint configured with TypeScript and Prettier integration
|
||||||
- Jest for testing with ts-jest preset (API service)
|
- Jest for testing with ts-jest preset
|
||||||
- Each app can be developed and deployed independently
|
|
||||||
285
README.md
285
README.md
|
|
@ -1,142 +1,92 @@
|
||||||
# Banatie - AI Image Generation Service
|
# Banatie - Nano Banana Image Generation Service
|
||||||
|
|
||||||
A comprehensive monorepo for the Banatie AI image generation platform, featuring multiple applications for different use cases.
|
A REST API service for AI-powered image generation using the Gemini Flash Image model. Banatie provides a simple HTTP interface for generating high-quality images from text prompts with optional reference images.
|
||||||
|
|
||||||
## Architecture Overview
|
## 🚀 Quick Start
|
||||||
|
|
||||||
```
|
|
||||||
banatie-service/
|
|
||||||
├── apps/
|
|
||||||
│ ├── api-service/ # REST API for image generation (Express.js + TypeScript)
|
|
||||||
│ ├── landing/ # Landing page with demo (Next.js)
|
|
||||||
│ ├── studio/ # SaaS platform with billing (Next.js + Supabase + Stripe)
|
|
||||||
│ └── admin/ # Administration dashboard (Next.js)
|
|
||||||
├── data/ # Docker volumes (postgres, minio)
|
|
||||||
├── docker-compose.yml # Infrastructure services
|
|
||||||
└── package.json # Workspace scripts
|
|
||||||
```
|
|
||||||
|
|
||||||
## Applications
|
|
||||||
|
|
||||||
### 🚀 API Service (`apps/api-service`)
|
|
||||||
- **Port**: 3000
|
|
||||||
- **Tech**: Express.js, TypeScript, Gemini AI
|
|
||||||
- **Purpose**: Core REST API for AI image generation
|
|
||||||
- **Features**: Image generation, file upload, rate limiting, logging
|
|
||||||
|
|
||||||
### 🌐 Landing Page (`apps/landing`)
|
|
||||||
- **Port**: 3001
|
|
||||||
- **Tech**: Next.js 14, Tailwind CSS
|
|
||||||
- **Purpose**: Public landing page with demo
|
|
||||||
- **Features**: Service overview, image generation demo
|
|
||||||
|
|
||||||
### 🏢 Studio (`apps/studio`)
|
|
||||||
- **Port**: 3002
|
|
||||||
- **Tech**: Next.js 14, Supabase, Stripe
|
|
||||||
- **Purpose**: SaaS platform for paid users
|
|
||||||
- **Features**: Authentication, billing, subscriptions, usage tracking
|
|
||||||
|
|
||||||
### 🔧 Admin (`apps/admin`)
|
|
||||||
- **Port**: 3003
|
|
||||||
- **Tech**: Next.js 14, Dashboard components
|
|
||||||
- **Purpose**: Service administration and monitoring
|
|
||||||
- **Features**: System monitoring, user management, analytics
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Node.js >= 18.0.0
|
- Node.js >= 18.0.0
|
||||||
- pnpm >= 8.0.0
|
- pnpm >= 8.0.0
|
||||||
- Docker & Docker Compose
|
- Gemini API key from [Google AI Studio](https://aistudio.google.com/)
|
||||||
|
|
||||||
### Development Setup
|
### Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone and install dependencies
|
# Clone the repository
|
||||||
git clone <repository-url>
|
git clone <repository-url>
|
||||||
cd banatie-service
|
cd banatie
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
pnpm install
|
pnpm install
|
||||||
|
|
||||||
# Start infrastructure (PostgreSQL + MinIO)
|
# Setup environment variables
|
||||||
docker compose up -d postgres minio storage-init
|
cp .env.example .env
|
||||||
|
# Edit .env with your Gemini API key
|
||||||
# Start all applications in development mode
|
|
||||||
pnpm dev
|
|
||||||
|
|
||||||
# Or start individual apps
|
|
||||||
pnpm dev:api # API Service on :3000
|
|
||||||
pnpm dev:landing # Landing Page on :3001
|
|
||||||
pnpm dev:studio # Studio Platform on :3002
|
|
||||||
pnpm dev:admin # Admin Dashboard on :3003
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Production Deployment
|
### Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build all applications
|
# Start development server with auto-reload
|
||||||
pnpm build
|
|
||||||
|
|
||||||
# Start infrastructure
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
# Start production servers
|
|
||||||
pnpm start:api
|
|
||||||
pnpm start:landing
|
|
||||||
pnpm start:studio
|
|
||||||
pnpm start:admin
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install dependencies for all apps
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# Development (all apps in parallel)
|
|
||||||
pnpm dev
|
pnpm dev
|
||||||
|
|
||||||
# Build all apps
|
# Start production server
|
||||||
|
pnpm start
|
||||||
|
|
||||||
|
# Build for production
|
||||||
pnpm build
|
pnpm build
|
||||||
|
|
||||||
# Type checking
|
|
||||||
pnpm typecheck
|
|
||||||
|
|
||||||
# Linting
|
|
||||||
pnpm lint
|
|
||||||
|
|
||||||
# Testing (API service only)
|
|
||||||
pnpm test
|
|
||||||
|
|
||||||
# Clean all build outputs
|
|
||||||
pnpm clean
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment Configuration
|
## 📝 Environment Configuration
|
||||||
|
|
||||||
Each application has its own environment configuration:
|
Copy `.env.example` to `.env` and configure:
|
||||||
|
|
||||||
- **Root**: `.env.docker` (for Docker services)
|
```bash
|
||||||
- **API Service**: `apps/api-service/.env`
|
# Required
|
||||||
- **Studio**: `apps/studio/.env.local`
|
GEMINI_API_KEY=your_gemini_api_key_here
|
||||||
- **Admin**: `apps/admin/.env.local`
|
|
||||||
|
|
||||||
See individual app README files for specific environment variables.
|
# Optional (with defaults)
|
||||||
|
PORT=3000
|
||||||
|
NODE_ENV=development
|
||||||
|
CORS_ORIGIN=*
|
||||||
|
MAX_FILE_SIZE=5242880 # 5MB
|
||||||
|
MAX_FILES=3
|
||||||
|
RESULTS_DIR=./results
|
||||||
|
UPLOADS_DIR=./uploads/temp
|
||||||
|
LOG_LEVEL=info
|
||||||
|
```
|
||||||
|
|
||||||
## Services & Ports
|
## 🔌 API Endpoints
|
||||||
|
|
||||||
| Service | Port | Description |
|
### Health Check
|
||||||
|---------|------|-------------|
|
```
|
||||||
| API Service | 3000 | Core REST API |
|
GET /health
|
||||||
| Landing Page | 3001 | Public website |
|
```
|
||||||
| Studio Platform | 3002 | SaaS application |
|
Returns server status and uptime.
|
||||||
| Admin Dashboard | 3003 | Administration |
|
|
||||||
| PostgreSQL | 5434 | Database |
|
|
||||||
| MinIO API | 9000 | Object storage |
|
|
||||||
| MinIO Console | 9001 | Storage admin |
|
|
||||||
|
|
||||||
## API Usage
|
### API Information
|
||||||
|
```
|
||||||
|
GET /api/info
|
||||||
|
```
|
||||||
|
Returns API details and configuration.
|
||||||
|
|
||||||
### Generate Image
|
### Generate Image
|
||||||
|
```
|
||||||
|
POST /api/generate
|
||||||
|
Content-Type: multipart/form-data
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `prompt` (string, required): Text description for image generation
|
||||||
|
- `filename` (string, optional): Custom filename for the generated image
|
||||||
|
- `referenceImages` (files, optional): Up to 3 reference images (5MB max each)
|
||||||
|
|
||||||
|
**Supported formats:** PNG, JPEG, JPG, WebP
|
||||||
|
|
||||||
|
## 📖 Usage Examples
|
||||||
|
|
||||||
|
### cURL
|
||||||
```bash
|
```bash
|
||||||
# Basic text-to-image
|
# Basic text-to-image
|
||||||
curl -X POST http://localhost:3000/api/generate \
|
curl -X POST http://localhost:3000/api/generate \
|
||||||
|
|
@ -149,15 +99,114 @@ curl -X POST http://localhost:3000/api/generate \
|
||||||
-F "referenceImages=@./reference.jpg"
|
-F "referenceImages=@./reference.jpg"
|
||||||
```
|
```
|
||||||
|
|
||||||
See `apps/api-service/README.md` for detailed API documentation.
|
### JavaScript/Fetch
|
||||||
|
```javascript
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('prompt', 'A futuristic cityscape at sunset');
|
||||||
|
formData.append('filename', 'futuristic_city');
|
||||||
|
|
||||||
## Contributing
|
const response = await fetch('http://localhost:3000/api/generate', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
1. Each app has its own package.json and dependencies
|
const result = await response.json();
|
||||||
2. Use the root workspace commands for multi-app operations
|
console.log(result);
|
||||||
3. Follow the existing code style and patterns
|
```
|
||||||
4. Run `pnpm typecheck` and `pnpm lint` before committing
|
|
||||||
|
|
||||||
## License
|
### Response Format
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Image generated successfully",
|
||||||
|
"data": {
|
||||||
|
"filename": "generated_image_123.png",
|
||||||
|
"filepath": "./results/generated_image_123.png",
|
||||||
|
"description": "Generated image description",
|
||||||
|
"model": "gemini-2.0-flash-exp",
|
||||||
|
"generatedAt": "2024-01-01T12:00:00.000Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
MIT License - see individual app directories for more details.
|
## 🛠️ Development Scripts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Development
|
||||||
|
pnpm dev # Start with auto-reload
|
||||||
|
pnpm build # Build for production
|
||||||
|
pnpm start # Start production server
|
||||||
|
|
||||||
|
# Code Quality
|
||||||
|
pnpm typecheck # TypeScript type checking
|
||||||
|
pnpm lint # ESLint code linting
|
||||||
|
pnpm lint:fix # Fix ESLint issues
|
||||||
|
pnpm format # Format code with Prettier
|
||||||
|
pnpm format:check # Check code formatting
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
pnpm test # Run tests
|
||||||
|
pnpm test:watch # Run tests in watch mode
|
||||||
|
pnpm test:coverage # Run tests with coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🏗️ Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
banatie/
|
||||||
|
├── src/
|
||||||
|
│ ├── middleware/ # Express middleware
|
||||||
|
│ ├── routes/ # API route handlers
|
||||||
|
│ ├── services/ # Business logic services
|
||||||
|
│ ├── types/ # TypeScript type definitions
|
||||||
|
│ ├── utils/ # Utility functions
|
||||||
|
│ ├── app.ts # Express app configuration
|
||||||
|
│ └── server.ts # Server entry point
|
||||||
|
├── tests/ # Test files
|
||||||
|
├── results/ # Generated images output
|
||||||
|
├── uploads/ # Temporary file uploads
|
||||||
|
└── logs/ # Application logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Configuration
|
||||||
|
|
||||||
|
### File Limits
|
||||||
|
- **Max file size**: 5MB per image (configurable)
|
||||||
|
- **Max files**: 3 reference images per request (configurable)
|
||||||
|
- **Request size**: 10MB total request limit
|
||||||
|
|
||||||
|
### Supported Image Formats
|
||||||
|
- PNG
|
||||||
|
- JPEG/JPG
|
||||||
|
- WebP
|
||||||
|
|
||||||
|
## 🚦 Error Handling
|
||||||
|
|
||||||
|
The API provides detailed error responses:
|
||||||
|
|
||||||
|
**Validation Errors (400):**
|
||||||
|
- Missing required parameters
|
||||||
|
- Invalid file formats
|
||||||
|
- File size exceeded
|
||||||
|
- Too many files
|
||||||
|
|
||||||
|
**Server Errors (500):**
|
||||||
|
- Missing API key configuration
|
||||||
|
- Image generation failures
|
||||||
|
- Internal processing errors
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
MIT License - see LICENSE file for details.
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
1. Fork the repository
|
||||||
|
2. Create a feature branch
|
||||||
|
3. Make your changes
|
||||||
|
4. Run tests and linting
|
||||||
|
5. Submit a pull request
|
||||||
|
|
||||||
|
## 📞 Support
|
||||||
|
|
||||||
|
For issues and questions, please open an issue on the GitHub repository.
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
# Banatie API Configuration
|
|
||||||
BANATIE_API_URL=http://localhost:3000
|
|
||||||
|
|
||||||
# Database Configuration (Direct admin access)
|
|
||||||
POSTGRES_URL=postgresql://banatie_user:banatie_secure_password@localhost:5434/banatie
|
|
||||||
|
|
||||||
# MinIO Storage Configuration
|
|
||||||
MINIO_ENDPOINT=http://localhost:9000
|
|
||||||
MINIO_ACCESS_KEY=your_minio_access_key
|
|
||||||
MINIO_SECRET_KEY=your_minio_secret_key
|
|
||||||
|
|
||||||
# Admin Authentication
|
|
||||||
ADMIN_USERNAME=admin
|
|
||||||
ADMIN_PASSWORD=your_secure_admin_password
|
|
||||||
|
|
||||||
# Next.js Configuration
|
|
||||||
NEXTAUTH_URL=http://localhost:3003
|
|
||||||
NEXTAUTH_SECRET=your_nextauth_secret
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
# Banatie Admin
|
|
||||||
|
|
||||||
Administration dashboard for managing the entire Banatie AI image generation service.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- 📊 **System Monitoring**: Real-time service health and performance metrics
|
|
||||||
- 👥 **User Management**: Manage users, subscriptions, and permissions
|
|
||||||
- 🖼️ **Content Management**: Monitor generated images and usage patterns
|
|
||||||
- 🔧 **Service Control**: Start/stop services, update configurations
|
|
||||||
- 📈 **Analytics**: Detailed usage analytics and reporting
|
|
||||||
- 🚨 **Alerts**: System alerts and notifications
|
|
||||||
|
|
||||||
## Tech Stack
|
|
||||||
|
|
||||||
- **Framework**: Next.js 14 with App Router
|
|
||||||
- **Database**: Direct PostgreSQL connection for admin operations
|
|
||||||
- **Storage**: MinIO admin interface integration
|
|
||||||
- **Monitoring**: Integration with API service metrics
|
|
||||||
- **Styling**: Tailwind CSS with admin-focused components
|
|
||||||
- **Charts**: Recharts for data visualization
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install dependencies
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# Set up environment variables
|
|
||||||
cp .env.example .env.local
|
|
||||||
|
|
||||||
# Start development server
|
|
||||||
pnpm dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
```env
|
|
||||||
# Banatie API
|
|
||||||
BANATIE_API_URL=http://localhost:3000
|
|
||||||
|
|
||||||
# Database (Direct admin access)
|
|
||||||
POSTGRES_URL=postgresql://user:password@localhost:5434/banatie
|
|
||||||
|
|
||||||
# Storage
|
|
||||||
MINIO_ENDPOINT=http://localhost:9000
|
|
||||||
MINIO_ACCESS_KEY=admin_access_key
|
|
||||||
MINIO_SECRET_KEY=admin_secret_key
|
|
||||||
|
|
||||||
# Admin Authentication
|
|
||||||
ADMIN_USERNAME=admin
|
|
||||||
ADMIN_PASSWORD=secure_password
|
|
||||||
```
|
|
||||||
|
|
||||||
## TODO
|
|
||||||
|
|
||||||
- [ ] Implement real-time metrics dashboard
|
|
||||||
- [ ] Add user management interface
|
|
||||||
- [ ] Create service control panel
|
|
||||||
- [ ] Add database admin tools
|
|
||||||
- [ ] Implement log viewer
|
|
||||||
- [ ] Add configuration management
|
|
||||||
- [ ] Create backup/restore functionality
|
|
||||||
- [ ] Add alert system
|
|
||||||
- [ ] Implement audit logging
|
|
||||||
- [ ] Add API key management
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
/** @type {import('next').NextConfig} */
|
|
||||||
const nextConfig = {
|
|
||||||
experimental: {
|
|
||||||
appDir: true,
|
|
||||||
},
|
|
||||||
images: {
|
|
||||||
domains: ['localhost'],
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
BANATIE_API_URL: process.env.BANATIE_API_URL || 'http://localhost:3000',
|
|
||||||
POSTGRES_URL: process.env.POSTGRES_URL,
|
|
||||||
MINIO_ENDPOINT: process.env.MINIO_ENDPOINT,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = nextConfig
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@banatie/admin",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Banatie Admin - Administration dashboard for managing the entire Banatie service",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"dev": "next dev -p 3003",
|
|
||||||
"build": "next build",
|
|
||||||
"start": "next start -p 3003",
|
|
||||||
"lint": "next lint",
|
|
||||||
"typecheck": "tsc --noEmit"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"next": "^14.2.0",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"@types/node": "^20.0.0",
|
|
||||||
"@types/react": "^18.2.0",
|
|
||||||
"@types/react-dom": "^18.2.0",
|
|
||||||
"typescript": "^5.9.2",
|
|
||||||
"recharts": "^2.12.0",
|
|
||||||
"lucide-react": "^0.400.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"tailwindcss": "^3.4.0",
|
|
||||||
"autoprefixer": "^10.4.0",
|
|
||||||
"postcss": "^8.4.0",
|
|
||||||
"eslint": "^8.57.0",
|
|
||||||
"eslint-config-next": "^14.2.0",
|
|
||||||
"@tailwindcss/forms": "^0.5.0",
|
|
||||||
"@headlessui/react": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0",
|
|
||||||
"pnpm": ">=8.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
import type { Metadata } from 'next'
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: 'Banatie Admin - Service Administration',
|
|
||||||
description: 'Administration dashboard for managing the Banatie AI image generation service',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function RootLayout({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<html lang="en">
|
|
||||||
<body className="min-h-screen bg-gray-100">
|
|
||||||
<div className="min-h-screen">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
export default function AdminDashboard() {
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-100">
|
|
||||||
{/* Header */}
|
|
||||||
<header className="bg-white shadow">
|
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
||||||
<div className="flex justify-between h-16">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<h1 className="text-2xl font-bold text-gray-900">
|
|
||||||
Banatie Admin
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<span className="text-sm text-gray-500">System Status: Online</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{/* Main Content */}
|
|
||||||
<main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
|
||||||
<div className="px-4 py-6 sm:px-0">
|
|
||||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
|
||||||
|
|
||||||
{/* Stats Cards */}
|
|
||||||
<div className="bg-white overflow-hidden shadow rounded-lg">
|
|
||||||
<div className="p-5">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="flex-shrink-0">
|
|
||||||
<div className="w-8 h-8 bg-blue-500 rounded-md flex items-center justify-center">
|
|
||||||
<span className="text-white text-sm font-medium">API</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="ml-5 w-0 flex-1">
|
|
||||||
<dl>
|
|
||||||
<dt className="text-sm font-medium text-gray-500 truncate">
|
|
||||||
API Requests
|
|
||||||
</dt>
|
|
||||||
<dd className="text-lg font-medium text-gray-900">
|
|
||||||
1,234
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white overflow-hidden shadow rounded-lg">
|
|
||||||
<div className="p-5">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="flex-shrink-0">
|
|
||||||
<div className="w-8 h-8 bg-green-500 rounded-md flex items-center justify-center">
|
|
||||||
<span className="text-white text-sm font-medium">IMG</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="ml-5 w-0 flex-1">
|
|
||||||
<dl>
|
|
||||||
<dt className="text-sm font-medium text-gray-500 truncate">
|
|
||||||
Images Generated
|
|
||||||
</dt>
|
|
||||||
<dd className="text-lg font-medium text-gray-900">
|
|
||||||
567
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white overflow-hidden shadow rounded-lg">
|
|
||||||
<div className="p-5">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="flex-shrink-0">
|
|
||||||
<div className="w-8 h-8 bg-yellow-500 rounded-md flex items-center justify-center">
|
|
||||||
<span className="text-white text-sm font-medium">USR</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="ml-5 w-0 flex-1">
|
|
||||||
<dl>
|
|
||||||
<dt className="text-sm font-medium text-gray-500 truncate">
|
|
||||||
Active Users
|
|
||||||
</dt>
|
|
||||||
<dd className="text-lg font-medium text-gray-900">
|
|
||||||
89
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white overflow-hidden shadow rounded-lg">
|
|
||||||
<div className="p-5">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="flex-shrink-0">
|
|
||||||
<div className="w-8 h-8 bg-red-500 rounded-md flex items-center justify-center">
|
|
||||||
<span className="text-white text-sm font-medium">ERR</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="ml-5 w-0 flex-1">
|
|
||||||
<dl>
|
|
||||||
<dt className="text-sm font-medium text-gray-500 truncate">
|
|
||||||
Errors (24h)
|
|
||||||
</dt>
|
|
||||||
<dd className="text-lg font-medium text-gray-900">
|
|
||||||
3
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Services Status */}
|
|
||||||
<div className="mt-8">
|
|
||||||
<div className="bg-white shadow rounded-lg">
|
|
||||||
<div className="px-4 py-5 sm:p-6">
|
|
||||||
<h3 className="text-lg leading-6 font-medium text-gray-900 mb-4">
|
|
||||||
Service Status
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm font-medium text-gray-900">API Service</span>
|
|
||||||
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
|
|
||||||
Healthy
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm font-medium text-gray-900">PostgreSQL Database</span>
|
|
||||||
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
|
|
||||||
Connected
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm font-medium text-gray-900">MinIO Storage</span>
|
|
||||||
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
|
|
||||||
Available
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm font-medium text-gray-900">Gemini AI</span>
|
|
||||||
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">
|
|
||||||
Rate Limited
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Coming Soon Notice */}
|
|
||||||
<div className="mt-8">
|
|
||||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
|
||||||
<div className="flex">
|
|
||||||
<div className="ml-3">
|
|
||||||
<h3 className="text-sm font-medium text-blue-800">
|
|
||||||
Admin Dashboard Under Development
|
|
||||||
</h3>
|
|
||||||
<div className="mt-2 text-sm text-blue-700">
|
|
||||||
<p>
|
|
||||||
This admin dashboard is being developed to provide comprehensive
|
|
||||||
monitoring and management capabilities for the Banatie service.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2022",
|
|
||||||
"lib": ["dom", "dom.iterable", "ES2022"],
|
|
||||||
"allowJs": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"strict": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
"incremental": true,
|
|
||||||
"plugins": [
|
|
||||||
{
|
|
||||||
"name": "next"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["./src/*"],
|
|
||||||
"@/components/*": ["./src/components/*"],
|
|
||||||
"@/lib/*": ["./src/lib/*"],
|
|
||||||
"@/types/*": ["./src/types/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
||||||
"exclude": ["node_modules"]
|
|
||||||
}
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@banatie/api-service",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Nano Banana Image Generation Service - REST API for AI-powered image generation using Gemini Flash Image model",
|
|
||||||
"main": "dist/server.js",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "tsx --watch src/server.ts",
|
|
||||||
"start": "node dist/server.js",
|
|
||||||
"build": "tsc",
|
|
||||||
"typecheck": "tsc --noEmit",
|
|
||||||
"lint": "eslint src/**/*.ts",
|
|
||||||
"lint:fix": "eslint src/**/*.ts --fix",
|
|
||||||
"format": "prettier --write src/**/*.ts",
|
|
||||||
"format:check": "prettier --check src/**/*.ts",
|
|
||||||
"test": "jest",
|
|
||||||
"test:watch": "jest --watch",
|
|
||||||
"test:coverage": "jest --coverage",
|
|
||||||
"clean": "rm -rf dist",
|
|
||||||
"prebuild": "npm run clean",
|
|
||||||
"prestart": "npm run build"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"image-generation",
|
|
||||||
"ai",
|
|
||||||
"gemini",
|
|
||||||
"nano-banana",
|
|
||||||
"rest-api",
|
|
||||||
"express",
|
|
||||||
"typescript"
|
|
||||||
],
|
|
||||||
"author": "",
|
|
||||||
"license": "MIT",
|
|
||||||
"packageManager": "pnpm@10.11.0",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0",
|
|
||||||
"pnpm": ">=8.0.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@google/genai": "^1.17.0",
|
|
||||||
"cors": "^2.8.5",
|
|
||||||
"dotenv": "^17.2.2",
|
|
||||||
"express": "^5.1.0",
|
|
||||||
"express-rate-limit": "^7.4.1",
|
|
||||||
"express-validator": "^7.2.0",
|
|
||||||
"helmet": "^8.0.0",
|
|
||||||
"mime": "3.0.0",
|
|
||||||
"minio": "^8.0.6",
|
|
||||||
"multer": "^2.0.2",
|
|
||||||
"winston": "^3.17.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@eslint/js": "^9.36.0",
|
|
||||||
"@types/cors": "^2.8.19",
|
|
||||||
"@types/express": "^5.0.3",
|
|
||||||
"@types/jest": "^29.5.14",
|
|
||||||
"@types/multer": "^2.0.0",
|
|
||||||
"@types/node": "^24.3.1",
|
|
||||||
"@types/supertest": "^6.0.2",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^8.18.2",
|
|
||||||
"@typescript-eslint/parser": "^8.18.2",
|
|
||||||
"eslint": "^9.18.0",
|
|
||||||
"eslint-config-prettier": "^9.1.0",
|
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
|
||||||
"jest": "^29.7.0",
|
|
||||||
"nodemon": "^3.1.9",
|
|
||||||
"prettier": "^3.4.2",
|
|
||||||
"supertest": "^7.0.0",
|
|
||||||
"ts-jest": "^29.2.5",
|
|
||||||
"tsx": "^4.20.5",
|
|
||||||
"typescript": "^5.9.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
# Banatie Landing Page
|
|
||||||
|
|
||||||
Next.js landing page for the Banatie AI image generation service.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Landing page with service overview
|
|
||||||
- Demo page for image generation (TODO)
|
|
||||||
- Responsive design with Tailwind CSS
|
|
||||||
- Integration with Banatie API service
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install dependencies
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# Start development server
|
|
||||||
pnpm dev
|
|
||||||
|
|
||||||
# Build for production
|
|
||||||
pnpm build
|
|
||||||
```
|
|
||||||
|
|
||||||
## TODO
|
|
||||||
|
|
||||||
- [ ] Implement image generation demo
|
|
||||||
- [ ] Add API integration with ../api-service
|
|
||||||
- [ ] Design landing page content
|
|
||||||
- [ ] Add pricing section
|
|
||||||
- [ ] Add contact form
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
/** @type {import('next').NextConfig} */
|
|
||||||
const nextConfig = {
|
|
||||||
experimental: {
|
|
||||||
appDir: true,
|
|
||||||
},
|
|
||||||
images: {
|
|
||||||
domains: ['localhost'],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = nextConfig
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@banatie/landing",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Banatie Landing Page - Next.js landing page with image generation demo",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"dev": "next dev -p 3001",
|
|
||||||
"build": "next build",
|
|
||||||
"start": "next start -p 3001",
|
|
||||||
"lint": "next lint",
|
|
||||||
"typecheck": "tsc --noEmit"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"next": "^14.2.0",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"@types/node": "^20.0.0",
|
|
||||||
"@types/react": "^18.2.0",
|
|
||||||
"@types/react-dom": "^18.2.0",
|
|
||||||
"typescript": "^5.9.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"tailwindcss": "^3.4.0",
|
|
||||||
"autoprefixer": "^10.4.0",
|
|
||||||
"postcss": "^8.4.0",
|
|
||||||
"eslint": "^8.57.0",
|
|
||||||
"eslint-config-next": "^14.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0",
|
|
||||||
"pnpm": ">=8.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
import type { Metadata } from 'next'
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: 'Banatie - AI Image Generation',
|
|
||||||
description: 'Generate stunning images with AI using the Banatie service',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function RootLayout({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<html lang="en">
|
|
||||||
<body className="min-h-screen bg-gray-50">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
export default function HomePage() {
|
|
||||||
return (
|
|
||||||
<div className="py-12">
|
|
||||||
<div className="text-center">
|
|
||||||
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
|
||||||
Welcome to Banatie
|
|
||||||
</h1>
|
|
||||||
<p className="text-xl text-gray-600 mb-8">
|
|
||||||
AI-Powered Image Generation Service
|
|
||||||
</p>
|
|
||||||
<div className="bg-white rounded-lg shadow-md p-8 max-w-2xl mx-auto">
|
|
||||||
<h2 className="text-2xl font-semibold mb-4">Demo Coming Soon</h2>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
Experience the power of AI image generation with our Gemini Flash model integration.
|
|
||||||
</p>
|
|
||||||
<div className="mt-6">
|
|
||||||
<button
|
|
||||||
className="bg-blue-600 text-white px-6 py-3 rounded-lg font-medium hover:bg-blue-700 disabled:opacity-50"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
Try Demo (Coming Soon)
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2022",
|
|
||||||
"lib": ["dom", "dom.iterable", "ES2022"],
|
|
||||||
"allowJs": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"strict": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
"incremental": true,
|
|
||||||
"plugins": [
|
|
||||||
{
|
|
||||||
"name": "next"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["./src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
||||||
"exclude": ["node_modules"]
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
# Supabase Configuration
|
|
||||||
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
|
|
||||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
|
||||||
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key
|
|
||||||
|
|
||||||
# Stripe Configuration
|
|
||||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
|
|
||||||
STRIPE_SECRET_KEY=sk_test_...
|
|
||||||
STRIPE_WEBHOOK_SECRET=whsec_...
|
|
||||||
|
|
||||||
# App Configuration
|
|
||||||
NEXTAUTH_URL=http://localhost:3002
|
|
||||||
NEXTAUTH_SECRET=your_nextauth_secret
|
|
||||||
|
|
||||||
# Banatie API
|
|
||||||
BANATIE_API_URL=http://localhost:3000
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
# Banatie Studio
|
|
||||||
|
|
||||||
SaaS platform for the Banatie AI image generation service. Based on Vercel's Next.js SaaS starter template with Supabase authentication and Stripe billing.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- 🔐 **Authentication**: Supabase Auth with social login
|
|
||||||
- 💳 **Billing**: Stripe subscriptions and payment processing
|
|
||||||
- 🎨 **AI Generation**: Integration with Banatie API service
|
|
||||||
- 📊 **Dashboard**: User dashboard with usage analytics
|
|
||||||
- 🏢 **Multi-tenant**: Organization and team management
|
|
||||||
|
|
||||||
## Tech Stack
|
|
||||||
|
|
||||||
- **Framework**: Next.js 14 with App Router
|
|
||||||
- **Authentication**: Supabase Auth
|
|
||||||
- **Database**: Supabase (PostgreSQL)
|
|
||||||
- **Payments**: Stripe
|
|
||||||
- **Styling**: Tailwind CSS
|
|
||||||
- **TypeScript**: Full type safety
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install dependencies
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# Set up environment variables
|
|
||||||
cp .env.example .env.local
|
|
||||||
|
|
||||||
# Start development server
|
|
||||||
pnpm dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## Environment Setup
|
|
||||||
|
|
||||||
1. **Supabase**: Create a new project at [supabase.com](https://supabase.com)
|
|
||||||
2. **Stripe**: Set up your account at [stripe.com](https://stripe.com)
|
|
||||||
3. **Environment Variables**: Copy `.env.example` to `.env.local` and fill in your keys
|
|
||||||
|
|
||||||
## TODO
|
|
||||||
|
|
||||||
- [ ] Set up Supabase authentication
|
|
||||||
- [ ] Configure Stripe billing
|
|
||||||
- [ ] Implement user dashboard
|
|
||||||
- [ ] Add subscription tiers
|
|
||||||
- [ ] Integrate with Banatie API
|
|
||||||
- [ ] Add usage tracking
|
|
||||||
- [ ] Implement team management
|
|
||||||
- [ ] Add billing portal
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
/** @type {import('next').NextConfig} */
|
|
||||||
const nextConfig = {
|
|
||||||
experimental: {
|
|
||||||
appDir: true,
|
|
||||||
},
|
|
||||||
images: {
|
|
||||||
domains: ['localhost', 'avatars.githubusercontent.com'],
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
||||||
NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
|
||||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = nextConfig
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@banatie/studio",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Banatie Studio - SaaS platform for managing AI image generation subscriptions",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"dev": "next dev -p 3002",
|
|
||||||
"build": "next build",
|
|
||||||
"start": "next start -p 3002",
|
|
||||||
"lint": "next lint",
|
|
||||||
"typecheck": "tsc --noEmit"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"next": "^14.2.0",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"@types/node": "^20.0.0",
|
|
||||||
"@types/react": "^18.2.0",
|
|
||||||
"@types/react-dom": "^18.2.0",
|
|
||||||
"typescript": "^5.9.2",
|
|
||||||
"@supabase/supabase-js": "^2.45.0",
|
|
||||||
"@supabase/auth-helpers-nextjs": "^0.10.0",
|
|
||||||
"stripe": "^16.0.0",
|
|
||||||
"@stripe/stripe-js": "^4.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"tailwindcss": "^3.4.0",
|
|
||||||
"autoprefixer": "^10.4.0",
|
|
||||||
"postcss": "^8.4.0",
|
|
||||||
"eslint": "^8.57.0",
|
|
||||||
"eslint-config-next": "^14.2.0",
|
|
||||||
"@tailwindcss/forms": "^0.5.0",
|
|
||||||
"@tailwindcss/typography": "^0.5.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0",
|
|
||||||
"pnpm": ">=8.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
import type { Metadata } from 'next'
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: 'Banatie Studio - AI Image Generation SaaS',
|
|
||||||
description: 'Professional AI image generation platform with subscription management',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function RootLayout({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<html lang="en">
|
|
||||||
<body className="min-h-screen bg-gray-50">
|
|
||||||
<div className="min-h-screen">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
export default function StudioPage() {
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
|
||||||
<div className="container mx-auto px-4 py-12">
|
|
||||||
<div className="text-center">
|
|
||||||
<h1 className="text-5xl font-bold text-gray-900 mb-6">
|
|
||||||
Banatie Studio
|
|
||||||
</h1>
|
|
||||||
<p className="text-xl text-gray-600 mb-8 max-w-2xl mx-auto">
|
|
||||||
Professional AI image generation platform with subscription management,
|
|
||||||
user authentication, and billing integration.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="grid md:grid-cols-3 gap-8 mt-12 max-w-4xl mx-auto">
|
|
||||||
<div className="bg-white rounded-lg shadow-lg p-6">
|
|
||||||
<h3 className="text-xl font-semibold mb-4">🔐 Authentication</h3>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
Secure user authentication powered by Supabase with social login support.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white rounded-lg shadow-lg p-6">
|
|
||||||
<h3 className="text-xl font-semibold mb-4">💳 Billing</h3>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
Stripe integration for subscription management and payment processing.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white rounded-lg shadow-lg p-6">
|
|
||||||
<h3 className="text-xl font-semibold mb-4">🎨 AI Generation</h3>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
Professional image generation with usage tracking and limits.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-12">
|
|
||||||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 max-w-2xl mx-auto">
|
|
||||||
<p className="text-yellow-800">
|
|
||||||
<strong>Coming Soon:</strong> This SaaS platform is under development.
|
|
||||||
Based on Vercel's Next.js SaaS starter template.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2022",
|
|
||||||
"lib": ["dom", "dom.iterable", "ES2022"],
|
|
||||||
"allowJs": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"strict": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
"incremental": true,
|
|
||||||
"plugins": [
|
|
||||||
{
|
|
||||||
"name": "next"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["./src/*"],
|
|
||||||
"@/components/*": ["./src/components/*"],
|
|
||||||
"@/lib/*": ["./src/lib/*"],
|
|
||||||
"@/types/*": ["./src/types/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
||||||
"exclude": ["node_modules"]
|
|
||||||
}
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
build:
|
build:
|
||||||
context: ./apps/api-service
|
context: .
|
||||||
target: development
|
target: development
|
||||||
container_name: banatie-app
|
container_name: banatie-app
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
volumes:
|
volumes:
|
||||||
- ./apps/api-service/src:/app/src
|
- ./src:/app/src
|
||||||
- ./apps/api-service/logs:/app/logs
|
- ./logs:/app/logs
|
||||||
networks:
|
networks:
|
||||||
- banatie-network
|
- banatie-network
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|
|
||||||
103
package.json
103
package.json
|
|
@ -1,55 +1,72 @@
|
||||||
{
|
{
|
||||||
"name": "banatie-monorepo",
|
"name": "banatie",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Banatie AI Image Generation Service - Monorepo with API, Landing, Studio, and Admin apps",
|
"description": "Nano Banana Image Generation Service - REST API for AI-powered image generation using Gemini Flash Image model",
|
||||||
"private": true,
|
"main": "dist/server.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tsx --watch src/server.ts",
|
||||||
|
"start": "node dist/server.js",
|
||||||
|
"build": "tsc",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"lint": "eslint src/**/*.ts",
|
||||||
|
"lint:fix": "eslint src/**/*.ts --fix",
|
||||||
|
"format": "prettier --write src/**/*.ts",
|
||||||
|
"format:check": "prettier --check src/**/*.ts",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:coverage": "jest --coverage",
|
||||||
|
"clean": "rm -rf dist",
|
||||||
|
"prebuild": "npm run clean",
|
||||||
|
"prestart": "npm run build"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"image-generation",
|
||||||
|
"ai",
|
||||||
|
"gemini",
|
||||||
|
"nano-banana",
|
||||||
|
"rest-api",
|
||||||
|
"express",
|
||||||
|
"typescript"
|
||||||
|
],
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
"packageManager": "pnpm@10.11.0",
|
"packageManager": "pnpm@10.11.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0",
|
"node": ">=18.0.0",
|
||||||
"pnpm": ">=8.0.0"
|
"pnpm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"dependencies": {
|
||||||
"dev": "pnpm --parallel run dev",
|
"@google/genai": "^1.17.0",
|
||||||
"dev:api": "pnpm --filter @banatie/api-service dev",
|
"cors": "^2.8.5",
|
||||||
"dev:landing": "pnpm --filter @banatie/landing dev",
|
"dotenv": "^17.2.2",
|
||||||
"dev:studio": "pnpm --filter @banatie/studio dev",
|
"express": "^5.1.0",
|
||||||
"dev:admin": "pnpm --filter @banatie/admin dev",
|
"express-rate-limit": "^7.4.1",
|
||||||
"build": "pnpm -r build",
|
"express-validator": "^7.2.0",
|
||||||
"build:api": "pnpm --filter @banatie/api-service build",
|
"helmet": "^8.0.0",
|
||||||
"build:landing": "pnpm --filter @banatie/landing build",
|
"mime": "3.0.0",
|
||||||
"build:studio": "pnpm --filter @banatie/studio build",
|
"minio": "^8.0.6",
|
||||||
"build:admin": "pnpm --filter @banatie/admin build",
|
"multer": "^2.0.2",
|
||||||
"start:api": "pnpm --filter @banatie/api-service start",
|
"winston": "^3.17.0"
|
||||||
"start:landing": "pnpm --filter @banatie/landing start",
|
|
||||||
"start:studio": "pnpm --filter @banatie/studio start",
|
|
||||||
"start:admin": "pnpm --filter @banatie/admin start",
|
|
||||||
"lint": "pnpm -r lint",
|
|
||||||
"lint:api": "pnpm --filter @banatie/api-service lint",
|
|
||||||
"lint:landing": "pnpm --filter @banatie/landing lint",
|
|
||||||
"lint:studio": "pnpm --filter @banatie/studio lint",
|
|
||||||
"lint:admin": "pnpm --filter @banatie/admin lint",
|
|
||||||
"typecheck": "pnpm -r typecheck",
|
|
||||||
"typecheck:api": "pnpm --filter @banatie/api-service typecheck",
|
|
||||||
"typecheck:landing": "pnpm --filter @banatie/landing typecheck",
|
|
||||||
"typecheck:studio": "pnpm --filter @banatie/studio typecheck",
|
|
||||||
"typecheck:admin": "pnpm --filter @banatie/admin typecheck",
|
|
||||||
"test": "pnpm --filter @banatie/api-service test",
|
|
||||||
"clean": "pnpm -r clean && rm -rf node_modules",
|
|
||||||
"install:all": "pnpm install"
|
|
||||||
},
|
},
|
||||||
"keywords": [
|
|
||||||
"monorepo",
|
|
||||||
"image-generation",
|
|
||||||
"ai",
|
|
||||||
"gemini",
|
|
||||||
"rest-api",
|
|
||||||
"nextjs",
|
|
||||||
"saas",
|
|
||||||
"admin-dashboard"
|
|
||||||
],
|
|
||||||
"author": "",
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.36.0",
|
||||||
|
"@types/cors": "^2.8.19",
|
||||||
|
"@types/express": "^5.0.3",
|
||||||
|
"@types/jest": "^29.5.14",
|
||||||
|
"@types/multer": "^2.0.0",
|
||||||
|
"@types/node": "^24.3.1",
|
||||||
|
"@types/supertest": "^6.0.2",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.18.2",
|
||||||
|
"@typescript-eslint/parser": "^8.18.2",
|
||||||
|
"eslint": "^9.18.0",
|
||||||
|
"eslint-config-prettier": "^9.1.0",
|
||||||
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"nodemon": "^3.1.9",
|
||||||
|
"prettier": "^3.4.2",
|
||||||
|
"supertest": "^7.0.0",
|
||||||
|
"ts-jest": "^29.2.5",
|
||||||
|
"tsx": "^4.20.5",
|
||||||
"typescript": "^5.9.2"
|
"typescript": "^5.9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
3159
pnpm-lock.yaml
3159
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -1,2 +0,0 @@
|
||||||
packages:
|
|
||||||
- 'apps/*'
|
|
||||||
|
|
@ -8,7 +8,6 @@ import {
|
||||||
ReferenceImage,
|
ReferenceImage,
|
||||||
} from "../types/api";
|
} from "../types/api";
|
||||||
import { StorageFactory } from "./StorageFactory";
|
import { StorageFactory } from "./StorageFactory";
|
||||||
import { UploadResult } from "./StorageService";
|
|
||||||
|
|
||||||
export class ImageGenService {
|
export class ImageGenService {
|
||||||
private ai: GoogleGenAI;
|
private ai: GoogleGenAI;
|
||||||
|
|
@ -28,18 +27,20 @@ export class ImageGenService {
|
||||||
async generateImage(
|
async generateImage(
|
||||||
options: ImageGenerationOptions,
|
options: ImageGenerationOptions,
|
||||||
): Promise<ImageGenerationResult> {
|
): Promise<ImageGenerationResult> {
|
||||||
const { prompt, filename, referenceImages, orgId, projectId, userId } =
|
const { prompt, filename, referenceImages, orgId, projectId, userId } = options;
|
||||||
options;
|
|
||||||
const timestamp = new Date().toISOString();
|
const timestamp = new Date().toISOString();
|
||||||
|
|
||||||
// Use default values if not provided
|
// Use default values if not provided
|
||||||
const finalOrgId = orgId || process.env["DEFAULT_ORG_ID"] || "default";
|
const finalOrgId = orgId || process.env['DEFAULT_ORG_ID'] || 'default';
|
||||||
const finalProjectId =
|
const finalProjectId = projectId || process.env['DEFAULT_PROJECT_ID'] || 'main';
|
||||||
projectId || process.env["DEFAULT_PROJECT_ID"] || "main";
|
const finalUserId = userId || process.env['DEFAULT_USER_ID'] || 'system';
|
||||||
const finalUserId = userId || process.env["DEFAULT_USER_ID"] || "system";
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] Starting image generation: "${prompt.substring(0, 50)}..." for ${finalOrgId}/${finalProjectId}`,
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// First try the primary model (Nano Banana)
|
||||||
const result = await this.tryGeneration({
|
const result = await this.tryGeneration({
|
||||||
model: this.primaryModel,
|
model: this.primaryModel,
|
||||||
config: { responseModalities: ["IMAGE", "TEXT"] },
|
config: { responseModalities: ["IMAGE", "TEXT"] },
|
||||||
|
|
@ -49,13 +50,17 @@ export class ImageGenService {
|
||||||
projectId: finalProjectId,
|
projectId: finalProjectId,
|
||||||
userId: finalUserId,
|
userId: finalUserId,
|
||||||
...(referenceImages && { referenceImages }),
|
...(referenceImages && { referenceImages }),
|
||||||
modelName: "Primary Model",
|
modelName: "Nano Banana",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback to Imagen 4
|
||||||
|
console.log(
|
||||||
|
`[${new Date().toISOString()}] Primary model failed, trying fallback (Imagen 4)...`,
|
||||||
|
);
|
||||||
|
|
||||||
return await this.tryGeneration({
|
return await this.tryGeneration({
|
||||||
model: this.fallbackModel,
|
model: this.fallbackModel,
|
||||||
|
|
@ -66,9 +71,13 @@ export class ImageGenService {
|
||||||
projectId: finalProjectId,
|
projectId: finalProjectId,
|
||||||
userId: finalUserId,
|
userId: finalUserId,
|
||||||
...(referenceImages && { referenceImages }),
|
...(referenceImages && { referenceImages }),
|
||||||
modelName: "Fallback Model",
|
modelName: "Imagen 4",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`[${new Date().toISOString()}] Image generation failed:`,
|
||||||
|
error,
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
model: "none",
|
model: "none",
|
||||||
|
|
@ -78,6 +87,9 @@ export class ImageGenService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try generation with a specific model
|
||||||
|
*/
|
||||||
private async tryGeneration(params: {
|
private async tryGeneration(params: {
|
||||||
model: string;
|
model: string;
|
||||||
config: { responseModalities: string[] };
|
config: { responseModalities: string[] };
|
||||||
|
|
@ -89,22 +101,18 @@ export class ImageGenService {
|
||||||
referenceImages?: ReferenceImage[];
|
referenceImages?: ReferenceImage[];
|
||||||
modelName: string;
|
modelName: string;
|
||||||
}): Promise<ImageGenerationResult> {
|
}): Promise<ImageGenerationResult> {
|
||||||
const {
|
const { model, config, prompt, filename, orgId, projectId, userId, referenceImages, modelName } =
|
||||||
model,
|
params;
|
||||||
config,
|
|
||||||
prompt,
|
|
||||||
filename,
|
|
||||||
orgId,
|
|
||||||
projectId,
|
|
||||||
userId,
|
|
||||||
referenceImages,
|
|
||||||
modelName,
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Build content parts for the API request
|
||||||
const contentParts: any[] = [];
|
const contentParts: any[] = [];
|
||||||
|
|
||||||
|
// Add reference images if provided
|
||||||
if (referenceImages && referenceImages.length > 0) {
|
if (referenceImages && referenceImages.length > 0) {
|
||||||
|
console.log(
|
||||||
|
`[${new Date().toISOString()}] Adding ${referenceImages.length} reference image(s)`,
|
||||||
|
);
|
||||||
|
|
||||||
for (const refImage of referenceImages) {
|
for (const refImage of referenceImages) {
|
||||||
contentParts.push({
|
contentParts.push({
|
||||||
|
|
@ -116,6 +124,7 @@ export class ImageGenService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the text prompt
|
||||||
contentParts.push({
|
contentParts.push({
|
||||||
text: prompt,
|
text: prompt,
|
||||||
});
|
});
|
||||||
|
|
@ -127,6 +136,9 @@ export class ImageGenService {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[${new Date().toISOString()}] Making API request to ${modelName} (${model})...`,
|
||||||
|
);
|
||||||
|
|
||||||
const response = await this.ai.models.generateContent({
|
const response = await this.ai.models.generateContent({
|
||||||
model,
|
model,
|
||||||
|
|
@ -134,6 +146,9 @@ export class ImageGenService {
|
||||||
contents,
|
contents,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[${new Date().toISOString()}] Response received from ${modelName}`,
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
response.candidates &&
|
response.candidates &&
|
||||||
|
|
@ -142,7 +157,7 @@ export class ImageGenService {
|
||||||
) {
|
) {
|
||||||
const content = response.candidates[0].content;
|
const content = response.candidates[0].content;
|
||||||
let generatedDescription = "";
|
let generatedDescription = "";
|
||||||
let uploadResult: UploadResult | null = null;
|
let uploadResult = null;
|
||||||
|
|
||||||
for (let index = 0; index < (content.parts?.length || 0); index++) {
|
for (let index = 0; index < (content.parts?.length || 0); index++) {
|
||||||
const part = content.parts?.[index];
|
const part = content.parts?.[index];
|
||||||
|
|
@ -153,30 +168,36 @@ export class ImageGenService {
|
||||||
part.inlineData.mimeType || "",
|
part.inlineData.mimeType || "",
|
||||||
);
|
);
|
||||||
const finalFilename = `${filename}.${fileExtension}`;
|
const finalFilename = `${filename}.${fileExtension}`;
|
||||||
const contentType =
|
const contentType = part.inlineData.mimeType || `image/${fileExtension}`;
|
||||||
part.inlineData.mimeType || `image/${fileExtension}`;
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[${new Date().toISOString()}] Uploading image to MinIO: ${finalFilename}`,
|
||||||
|
);
|
||||||
|
|
||||||
const buffer = Buffer.from(part.inlineData.data || "", "base64");
|
const buffer = Buffer.from(part.inlineData.data || "", "base64");
|
||||||
|
|
||||||
|
// Upload to MinIO storage
|
||||||
const storageService = StorageFactory.getInstance();
|
const storageService = StorageFactory.getInstance();
|
||||||
const result = (await storageService).uploadFile(
|
uploadResult = await storageService.uploadFile(
|
||||||
orgId,
|
orgId,
|
||||||
projectId,
|
projectId,
|
||||||
"generated",
|
'generated',
|
||||||
finalFilename,
|
finalFilename,
|
||||||
buffer,
|
buffer,
|
||||||
contentType,
|
contentType
|
||||||
);
|
);
|
||||||
|
|
||||||
uploadResult = await result;
|
console.log(
|
||||||
|
`[${new Date().toISOString()}] Image uploaded successfully: ${uploadResult.path}`,
|
||||||
|
);
|
||||||
} else if (part.text) {
|
} else if (part.text) {
|
||||||
generatedDescription = part.text;
|
generatedDescription = part.text;
|
||||||
|
console.log(
|
||||||
|
`[${new Date().toISOString()}] Generated description: ${part.text.substring(0, 100)}...`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (uploadResult && uploadResult.success) {
|
if (uploadResult && uploadResult.success) {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -195,6 +216,10 @@ export class ImageGenService {
|
||||||
error: "No image data received from API",
|
error: "No image data received from API",
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`[${new Date().toISOString()}] ${modelName} generation failed:`,
|
||||||
|
error,
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
|
|
@ -203,6 +228,10 @@ export class ImageGenService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate reference images
|
||||||
|
*/
|
||||||
static validateReferenceImages(files: Express.Multer.File[]): {
|
static validateReferenceImages(files: Express.Multer.File[]): {
|
||||||
valid: boolean;
|
valid: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
|
|
@ -233,6 +262,9 @@ export class ImageGenService {
|
||||||
return { valid: true };
|
return { valid: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert Express.Multer.File[] to ReferenceImage[]
|
||||||
|
*/
|
||||||
static convertFilesToReferenceImages(
|
static convertFilesToReferenceImages(
|
||||||
files: Express.Multer.File[],
|
files: Express.Multer.File[],
|
||||||
): ReferenceImage[] {
|
): ReferenceImage[] {
|
||||||
|
|
@ -312,6 +312,7 @@ export class MinioStorageService implements StorageService {
|
||||||
createdAt: obj.lastModified || new Date()
|
createdAt: obj.lastModified || new Date()
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error(`Error processing file ${obj.name}:`, error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -327,6 +328,7 @@ export class MinioStorageService implements StorageService {
|
||||||
filename: string;
|
filename: string;
|
||||||
} | null {
|
} | null {
|
||||||
try {
|
try {
|
||||||
|
// Key format: banatie/orgId/projectId/category/year-month/filename
|
||||||
const match = key.match(/^banatie\/([^/]+)\/([^/]+)\/(uploads|generated|references)\/[^/]+\/(.+)$/);
|
const match = key.match(/^banatie\/([^/]+)\/([^/]+)\/(uploads|generated|references)\/[^/]+\/(.+)$/);
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
|
|
@ -350,6 +352,7 @@ export class MinioStorageService implements StorageService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MISSING METHODS FROM INTERFACE
|
||||||
|
|
||||||
async fileExists(
|
async fileExists(
|
||||||
orgId: string,
|
orgId: string,
|
||||||
|
|
@ -373,7 +376,7 @@ export class MinioStorageService implements StorageService {
|
||||||
category: 'uploads' | 'generated' | 'references',
|
category: 'uploads' | 'generated' | 'references',
|
||||||
prefix?: string
|
prefix?: string
|
||||||
): Promise<FileMetadata[]> {
|
): Promise<FileMetadata[]> {
|
||||||
this.validateFilePath(orgId, projectId, category, 'dummy.txt');
|
this.validateFilePath(orgId, projectId, category, 'dummy.txt'); // Validate path components
|
||||||
|
|
||||||
const basePath = `${orgId}/${projectId}/${category}/`;
|
const basePath = `${orgId}/${projectId}/${category}/`;
|
||||||
const searchPrefix = prefix ? `${basePath}${prefix}` : basePath;
|
const searchPrefix = prefix ? `${basePath}${prefix}` : basePath;
|
||||||
|
|
@ -403,6 +406,7 @@ export class MinioStorageService implements StorageService {
|
||||||
path: obj.name
|
path: obj.name
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error(`Error processing file ${obj.name}:`, error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -25,11 +25,13 @@ export class StorageFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Synchronous version for backward compatibility (with graceful degradation)
|
||||||
static getInstanceSync(): StorageService {
|
static getInstanceSync(): StorageService {
|
||||||
if (!this.instance) {
|
if (!this.instance) {
|
||||||
try {
|
try {
|
||||||
this.instance = this.createStorageService();
|
this.instance = this.createStorageService();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('Failed to create storage service:', error);
|
||||||
throw new Error('Storage service unavailable. Please check MinIO configuration.');
|
throw new Error('Storage service unavailable. Please check MinIO configuration.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -43,14 +45,18 @@ export class StorageFactory {
|
||||||
|
|
||||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||||
try {
|
try {
|
||||||
|
console.log(`Attempting to create storage service (attempt ${attempt}/${maxRetries})`);
|
||||||
|
|
||||||
const service = this.createStorageService();
|
const service = this.createStorageService();
|
||||||
|
|
||||||
|
// Test the connection by checking if bucket exists
|
||||||
await service.bucketExists();
|
await service.bucketExists();
|
||||||
|
|
||||||
|
console.log('Storage service created successfully');
|
||||||
return service;
|
return service;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error(`Storage service creation attempt ${attempt} failed:`, error);
|
||||||
|
|
||||||
if (attempt === maxRetries) {
|
if (attempt === maxRetries) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -59,7 +65,9 @@ export class StorageFactory {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exponential backoff
|
||||||
const delay = baseDelay * Math.pow(2, attempt - 1);
|
const delay = baseDelay * Math.pow(2, attempt - 1);
|
||||||
|
console.log(`Waiting ${delay}ms before retry...`);
|
||||||
await this.sleep(delay);
|
await this.sleep(delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -90,6 +98,11 @@ export class StorageFactory {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`Initializing MinIO Storage Service:`);
|
||||||
|
console.log(` Endpoint: ${endpoint}`);
|
||||||
|
console.log(` Bucket: ${bucketName}`);
|
||||||
|
console.log(` SSL: ${useSSL}`);
|
||||||
|
console.log(` Public URL: ${publicUrl}`);
|
||||||
|
|
||||||
return new MinioStorageService(
|
return new MinioStorageService(
|
||||||
endpoint,
|
endpoint,
|
||||||
|
|
@ -105,10 +118,12 @@ export class StorageFactory {
|
||||||
throw new Error(`Unsupported storage type: ${storageType}`);
|
throw new Error(`Unsupported storage type: ${storageType}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('Error creating storage service:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset instance for testing
|
||||||
static resetInstance(): void {
|
static resetInstance(): void {
|
||||||
this.instance = null;
|
this.instance = null;
|
||||||
this.initializationPromise = null;
|
this.initializationPromise = null;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Readable } from "stream";
|
import { Readable } from 'stream';
|
||||||
|
|
||||||
export interface FileMetadata {
|
export interface FileMetadata {
|
||||||
filename: string;
|
filename: string;
|
||||||
|
|
@ -42,10 +42,10 @@ export interface StorageService {
|
||||||
uploadFile(
|
uploadFile(
|
||||||
orgId: string,
|
orgId: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
category: "uploads" | "generated" | "references",
|
category: 'uploads' | 'generated' | 'references',
|
||||||
filename: string,
|
filename: string,
|
||||||
buffer: Buffer,
|
buffer: Buffer,
|
||||||
contentType: string,
|
contentType: string
|
||||||
): Promise<UploadResult>;
|
): Promise<UploadResult>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -58,8 +58,8 @@ export interface StorageService {
|
||||||
downloadFile(
|
downloadFile(
|
||||||
orgId: string,
|
orgId: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
category: "uploads" | "generated" | "references",
|
category: 'uploads' | 'generated' | 'references',
|
||||||
filename: string,
|
filename: string
|
||||||
): Promise<Buffer>;
|
): Promise<Buffer>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -72,8 +72,8 @@ export interface StorageService {
|
||||||
streamFile(
|
streamFile(
|
||||||
orgId: string,
|
orgId: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
category: "uploads" | "generated" | "references",
|
category: 'uploads' | 'generated' | 'references',
|
||||||
filename: string,
|
filename: string
|
||||||
): Promise<Readable>;
|
): Promise<Readable>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -87,9 +87,9 @@ export interface StorageService {
|
||||||
getPresignedDownloadUrl(
|
getPresignedDownloadUrl(
|
||||||
orgId: string,
|
orgId: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
category: "uploads" | "generated" | "references",
|
category: 'uploads' | 'generated' | 'references',
|
||||||
filename: string,
|
filename: string,
|
||||||
expirySeconds: number,
|
expirySeconds: number
|
||||||
): Promise<string>;
|
): Promise<string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -104,10 +104,10 @@ export interface StorageService {
|
||||||
getPresignedUploadUrl(
|
getPresignedUploadUrl(
|
||||||
orgId: string,
|
orgId: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
category: "uploads" | "generated" | "references",
|
category: 'uploads' | 'generated' | 'references',
|
||||||
filename: string,
|
filename: string,
|
||||||
expirySeconds: number,
|
expirySeconds: number,
|
||||||
contentType: string,
|
contentType: string
|
||||||
): Promise<string>;
|
): Promise<string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -120,8 +120,8 @@ export interface StorageService {
|
||||||
listFiles(
|
listFiles(
|
||||||
orgId: string,
|
orgId: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
category: "uploads" | "generated" | "references",
|
category: 'uploads' | 'generated' | 'references',
|
||||||
prefix?: string,
|
prefix?: string
|
||||||
): Promise<FileMetadata[]>;
|
): Promise<FileMetadata[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -134,8 +134,8 @@ export interface StorageService {
|
||||||
deleteFile(
|
deleteFile(
|
||||||
orgId: string,
|
orgId: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
category: "uploads" | "generated" | "references",
|
category: 'uploads' | 'generated' | 'references',
|
||||||
filename: string,
|
filename: string
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -148,7 +148,7 @@ export interface StorageService {
|
||||||
fileExists(
|
fileExists(
|
||||||
orgId: string,
|
orgId: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
category: "uploads" | "generated" | "references",
|
category: 'uploads' | 'generated' | 'references',
|
||||||
filename: string,
|
filename: string
|
||||||
): Promise<boolean>;
|
): Promise<boolean>;
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue