Merge branch 'init-monorepo'
This commit is contained in:
commit
839eeac2c2
|
|
@ -9,6 +9,9 @@ pnpm-debug.log*
|
|||
dist/
|
||||
build/
|
||||
*.tsbuildinfo
|
||||
apps/*/dist/
|
||||
apps/*/.next/
|
||||
.next/
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
|
|
@ -35,6 +38,7 @@ Thumbs.db
|
|||
|
||||
# Logs
|
||||
logs/
|
||||
apps/*/logs/
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
|
|
|
|||
132
CLAUDE.md
132
CLAUDE.md
|
|
@ -4,44 +4,81 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||
|
||||
## Project Overview
|
||||
|
||||
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.
|
||||
Banatie is a comprehensive monorepo for AI-powered image generation platform featuring multiple applications:
|
||||
|
||||
- **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
|
||||
|
||||
use `docker compose` command for using docker-compose service (v3 version)
|
||||
Use `docker compose` command for Docker services (v3 version).
|
||||
|
||||
### 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
|
||||
### Infrastructure
|
||||
|
||||
### Code Quality
|
||||
- `pnpm lint` - Run ESLint on all TypeScript files in src/
|
||||
- `pnpm lint:fix` - Run ESLint with auto-fix
|
||||
- `pnpm format` - Format code with Prettier
|
||||
- `pnpm format:check` - Check if code is formatted correctly
|
||||
- `docker compose up -d postgres minio storage-init` - Start database and storage services
|
||||
- `docker compose up -d` - Start all services including the API app container
|
||||
- `docker compose down` - Stop all services
|
||||
|
||||
### Testing
|
||||
- `pnpm test` - Run Jest tests
|
||||
- `pnpm test:watch` - Run tests in watch mode
|
||||
- `pnpm test:coverage` - Run tests with coverage report
|
||||
### Monorepo Commands (from root)
|
||||
|
||||
- `pnpm dev` - Start all applications in development mode
|
||||
- `pnpm dev:api` - Start API service only (`apps/api-service`)
|
||||
- `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
|
||||
|
||||
### Core Structure
|
||||
### Monorepo 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
|
||||
- **Server Entry**: `src/server.ts` starts the HTTP server
|
||||
- **Image Generation**: `src/services/ImageGenService.ts` handles Gemini AI integration
|
||||
- **Storage**: `src/services/MinioStorageService.ts` handles file uploads to MinIO
|
||||
- **Route Handling**: `src/routes/generate.ts` contains the main API endpoint logic
|
||||
|
||||
### Middleware Stack
|
||||
### Middleware Stack (API Service)
|
||||
|
||||
- `src/middleware/upload.ts` - Multer configuration for file uploads (max 3 files, 5MB each)
|
||||
- `src/middleware/validation.ts` - Express-validator for request validation
|
||||
- `src/middleware/errorHandler.ts` - Centralized error handling and 404 responses
|
||||
|
||||
### TypeScript Configuration
|
||||
- Path aliases configured in tsconfig.json:
|
||||
### TypeScript Configuration (API Service)
|
||||
|
||||
- Path aliases configured in `apps/api-service/tsconfig.json`:
|
||||
- `@/*` maps to `src/*`
|
||||
- `@/types/*` maps to `src/types/*`
|
||||
- `@/services/*` maps to `src/services/*`
|
||||
|
|
@ -49,30 +86,66 @@ use `docker compose` command for using docker-compose service (v3 version)
|
|||
- `@/routes/*` maps to `src/routes/*`
|
||||
- `@/utils/*` maps to `src/utils/*`
|
||||
|
||||
### File Handling
|
||||
- **Uploads**: Temporary files stored in `./uploads/temp`
|
||||
- **Results**: Generated images saved to `./results`
|
||||
- **Logs**: Application logs in `./logs`
|
||||
### Storage & Data
|
||||
|
||||
- **MinIO**: Object storage for generated images and uploads (port 9000)
|
||||
- **PostgreSQL**: Database for user data, metadata (port 5434)
|
||||
- **File Organization**: `orgId/projectId/category/year-month/filename.ext`
|
||||
|
||||
## 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:
|
||||
|
||||
- `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)
|
||||
- `NODE_ENV` - Environment mode
|
||||
- `CORS_ORIGIN` - CORS origin setting (default: *)
|
||||
- `CORS_ORIGIN` - CORS origin setting (default: \*)
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
### API Service
|
||||
|
||||
- **@google/genai** - Google Gemini AI client
|
||||
- **express** v5 - Web framework
|
||||
- **multer** - File upload handling
|
||||
- **minio** - MinIO client for object storage
|
||||
- **express-validator** - Request validation
|
||||
- **winston** - Logging
|
||||
- **helmet** - Security middleware
|
||||
- **express-rate-limit** - Rate limiting
|
||||
|
||||
## API Endpoints
|
||||
### Frontend Apps (Next.js)
|
||||
|
||||
- **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 /api/info` - API information and limits
|
||||
|
|
@ -80,8 +153,9 @@ Required environment variables:
|
|||
|
||||
## Development Notes
|
||||
|
||||
- Uses pnpm as package manager (required >= 8.0.0)
|
||||
- Uses pnpm workspaces for monorepo management (required >= 8.0.0)
|
||||
- Node.js >= 18.0.0 required
|
||||
- TypeScript with strict mode enabled
|
||||
- TypeScript with strict mode enabled across all apps
|
||||
- ESLint configured with TypeScript and Prettier integration
|
||||
- Jest for testing with ts-jest preset
|
||||
- Jest for testing with ts-jest preset (API service)
|
||||
- Each app can be developed and deployed independently
|
||||
|
|
|
|||
289
README.md
289
README.md
|
|
@ -1,92 +1,142 @@
|
|||
# Banatie - Nano Banana Image Generation Service
|
||||
# Banatie - AI Image Generation Service
|
||||
|
||||
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.
|
||||
A comprehensive monorepo for the Banatie AI image generation platform, featuring multiple applications for different use cases.
|
||||
|
||||
## 🚀 Quick Start
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
- Node.js >= 18.0.0
|
||||
- pnpm >= 8.0.0
|
||||
- Gemini API key from [Google AI Studio](https://aistudio.google.com/)
|
||||
- Docker & Docker Compose
|
||||
|
||||
### Installation
|
||||
### Development Setup
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
# Clone and install dependencies
|
||||
git clone <repository-url>
|
||||
cd banatie
|
||||
|
||||
# Install dependencies
|
||||
cd banatie-service
|
||||
pnpm install
|
||||
|
||||
# Setup environment variables
|
||||
cp .env.example .env
|
||||
# Edit .env with your Gemini API key
|
||||
```
|
||||
# Start infrastructure (PostgreSQL + MinIO)
|
||||
docker compose up -d postgres minio storage-init
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
# Start development server with auto-reload
|
||||
# Start all applications in development mode
|
||||
pnpm dev
|
||||
|
||||
# Start production server
|
||||
pnpm start
|
||||
|
||||
# Build for production
|
||||
pnpm build
|
||||
# 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
|
||||
```
|
||||
|
||||
## 📝 Environment Configuration
|
||||
|
||||
Copy `.env.example` to `.env` and configure:
|
||||
### Production Deployment
|
||||
|
||||
```bash
|
||||
# Required
|
||||
GEMINI_API_KEY=your_gemini_api_key_here
|
||||
# Build all applications
|
||||
pnpm build
|
||||
|
||||
# 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
|
||||
# Start infrastructure
|
||||
docker compose up -d
|
||||
|
||||
# Start production servers
|
||||
pnpm start:api
|
||||
pnpm start:landing
|
||||
pnpm start:studio
|
||||
pnpm start:admin
|
||||
```
|
||||
|
||||
## 🔌 API Endpoints
|
||||
## Development Commands
|
||||
|
||||
### Health Check
|
||||
```
|
||||
GET /health
|
||||
```
|
||||
Returns server status and uptime.
|
||||
```bash
|
||||
# Install dependencies for all apps
|
||||
pnpm install
|
||||
|
||||
### API Information
|
||||
# Development (all apps in parallel)
|
||||
pnpm dev
|
||||
|
||||
# Build all apps
|
||||
pnpm build
|
||||
|
||||
# Type checking
|
||||
pnpm typecheck
|
||||
|
||||
# Linting
|
||||
pnpm lint
|
||||
|
||||
# Testing (API service only)
|
||||
pnpm test
|
||||
|
||||
# Clean all build outputs
|
||||
pnpm clean
|
||||
```
|
||||
GET /api/info
|
||||
```
|
||||
Returns API details and configuration.
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
Each application has its own environment configuration:
|
||||
|
||||
- **Root**: `.env.docker` (for Docker services)
|
||||
- **API Service**: `apps/api-service/.env`
|
||||
- **Studio**: `apps/studio/.env.local`
|
||||
- **Admin**: `apps/admin/.env.local`
|
||||
|
||||
See individual app README files for specific environment variables.
|
||||
|
||||
## Services & Ports
|
||||
|
||||
| Service | Port | Description |
|
||||
|---------|------|-------------|
|
||||
| API Service | 3000 | Core REST API |
|
||||
| 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 admin |
|
||||
|
||||
## API Usage
|
||||
|
||||
### 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
|
||||
# Basic text-to-image
|
||||
curl -X POST http://localhost:3000/api/generate \
|
||||
|
|
@ -99,114 +149,15 @@ curl -X POST http://localhost:3000/api/generate \
|
|||
-F "referenceImages=@./reference.jpg"
|
||||
```
|
||||
|
||||
### JavaScript/Fetch
|
||||
```javascript
|
||||
const formData = new FormData();
|
||||
formData.append('prompt', 'A futuristic cityscape at sunset');
|
||||
formData.append('filename', 'futuristic_city');
|
||||
See `apps/api-service/README.md` for detailed API documentation.
|
||||
|
||||
const response = await fetch('http://localhost:3000/api/generate', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
## Contributing
|
||||
|
||||
const result = await response.json();
|
||||
console.log(result);
|
||||
```
|
||||
1. Each app has its own package.json and dependencies
|
||||
2. Use the root workspace commands for multi-app operations
|
||||
3. Follow the existing code style and patterns
|
||||
4. Run `pnpm typecheck` and `pnpm lint` before committing
|
||||
|
||||
### 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"
|
||||
}
|
||||
}
|
||||
```
|
||||
## License
|
||||
|
||||
## 🛠️ 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.
|
||||
MIT License - see individual app directories for more details.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/** @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
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
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>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
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>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"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"]
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import {
|
|||
ReferenceImage,
|
||||
} from "../types/api";
|
||||
import { StorageFactory } from "./StorageFactory";
|
||||
import { UploadResult } from "./StorageService";
|
||||
|
||||
export class ImageGenService {
|
||||
private ai: GoogleGenAI;
|
||||
|
|
@ -27,20 +28,18 @@ export class ImageGenService {
|
|||
async generateImage(
|
||||
options: ImageGenerationOptions,
|
||||
): Promise<ImageGenerationResult> {
|
||||
const { prompt, filename, referenceImages, orgId, projectId, userId } = options;
|
||||
const { prompt, filename, referenceImages, orgId, projectId, userId } =
|
||||
options;
|
||||
const timestamp = new Date().toISOString();
|
||||
|
||||
// Use default values if not provided
|
||||
const finalOrgId = orgId || process.env['DEFAULT_ORG_ID'] || 'default';
|
||||
const finalProjectId = projectId || process.env['DEFAULT_PROJECT_ID'] || 'main';
|
||||
const finalUserId = userId || process.env['DEFAULT_USER_ID'] || 'system';
|
||||
const finalOrgId = orgId || process.env["DEFAULT_ORG_ID"] || "default";
|
||||
const finalProjectId =
|
||||
projectId || process.env["DEFAULT_PROJECT_ID"] || "main";
|
||||
const finalUserId = userId || process.env["DEFAULT_USER_ID"] || "system";
|
||||
|
||||
console.log(
|
||||
`[${timestamp}] Starting image generation: "${prompt.substring(0, 50)}..." for ${finalOrgId}/${finalProjectId}`,
|
||||
);
|
||||
|
||||
try {
|
||||
// First try the primary model (Nano Banana)
|
||||
const result = await this.tryGeneration({
|
||||
model: this.primaryModel,
|
||||
config: { responseModalities: ["IMAGE", "TEXT"] },
|
||||
|
|
@ -50,17 +49,13 @@ export class ImageGenService {
|
|||
projectId: finalProjectId,
|
||||
userId: finalUserId,
|
||||
...(referenceImages && { referenceImages }),
|
||||
modelName: "Nano Banana",
|
||||
modelName: "Primary Model",
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Fallback to Imagen 4
|
||||
console.log(
|
||||
`[${new Date().toISOString()}] Primary model failed, trying fallback (Imagen 4)...`,
|
||||
);
|
||||
|
||||
return await this.tryGeneration({
|
||||
model: this.fallbackModel,
|
||||
|
|
@ -71,13 +66,9 @@ export class ImageGenService {
|
|||
projectId: finalProjectId,
|
||||
userId: finalUserId,
|
||||
...(referenceImages && { referenceImages }),
|
||||
modelName: "Imagen 4",
|
||||
modelName: "Fallback Model",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[${new Date().toISOString()}] Image generation failed:`,
|
||||
error,
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
model: "none",
|
||||
|
|
@ -87,9 +78,6 @@ export class ImageGenService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try generation with a specific model
|
||||
*/
|
||||
private async tryGeneration(params: {
|
||||
model: string;
|
||||
config: { responseModalities: string[] };
|
||||
|
|
@ -101,18 +89,22 @@ export class ImageGenService {
|
|||
referenceImages?: ReferenceImage[];
|
||||
modelName: string;
|
||||
}): Promise<ImageGenerationResult> {
|
||||
const { model, config, prompt, filename, orgId, projectId, userId, referenceImages, modelName } =
|
||||
params;
|
||||
const {
|
||||
model,
|
||||
config,
|
||||
prompt,
|
||||
filename,
|
||||
orgId,
|
||||
projectId,
|
||||
userId,
|
||||
referenceImages,
|
||||
modelName,
|
||||
} = params;
|
||||
|
||||
try {
|
||||
// Build content parts for the API request
|
||||
const contentParts: any[] = [];
|
||||
|
||||
// Add reference images if provided
|
||||
if (referenceImages && referenceImages.length > 0) {
|
||||
console.log(
|
||||
`[${new Date().toISOString()}] Adding ${referenceImages.length} reference image(s)`,
|
||||
);
|
||||
|
||||
for (const refImage of referenceImages) {
|
||||
contentParts.push({
|
||||
|
|
@ -124,7 +116,6 @@ export class ImageGenService {
|
|||
}
|
||||
}
|
||||
|
||||
// Add the text prompt
|
||||
contentParts.push({
|
||||
text: prompt,
|
||||
});
|
||||
|
|
@ -136,9 +127,6 @@ export class ImageGenService {
|
|||
},
|
||||
];
|
||||
|
||||
console.log(
|
||||
`[${new Date().toISOString()}] Making API request to ${modelName} (${model})...`,
|
||||
);
|
||||
|
||||
const response = await this.ai.models.generateContent({
|
||||
model,
|
||||
|
|
@ -146,9 +134,6 @@ export class ImageGenService {
|
|||
contents,
|
||||
});
|
||||
|
||||
console.log(
|
||||
`[${new Date().toISOString()}] Response received from ${modelName}`,
|
||||
);
|
||||
|
||||
if (
|
||||
response.candidates &&
|
||||
|
|
@ -157,7 +142,7 @@ export class ImageGenService {
|
|||
) {
|
||||
const content = response.candidates[0].content;
|
||||
let generatedDescription = "";
|
||||
let uploadResult = null;
|
||||
let uploadResult: UploadResult | null = null;
|
||||
|
||||
for (let index = 0; index < (content.parts?.length || 0); index++) {
|
||||
const part = content.parts?.[index];
|
||||
|
|
@ -168,36 +153,30 @@ export class ImageGenService {
|
|||
part.inlineData.mimeType || "",
|
||||
);
|
||||
const finalFilename = `${filename}.${fileExtension}`;
|
||||
const contentType = part.inlineData.mimeType || `image/${fileExtension}`;
|
||||
const contentType =
|
||||
part.inlineData.mimeType || `image/${fileExtension}`;
|
||||
|
||||
console.log(
|
||||
`[${new Date().toISOString()}] Uploading image to MinIO: ${finalFilename}`,
|
||||
);
|
||||
|
||||
const buffer = Buffer.from(part.inlineData.data || "", "base64");
|
||||
|
||||
// Upload to MinIO storage
|
||||
const storageService = StorageFactory.getInstance();
|
||||
uploadResult = await storageService.uploadFile(
|
||||
const result = (await storageService).uploadFile(
|
||||
orgId,
|
||||
projectId,
|
||||
'generated',
|
||||
"generated",
|
||||
finalFilename,
|
||||
buffer,
|
||||
contentType
|
||||
contentType,
|
||||
);
|
||||
|
||||
console.log(
|
||||
`[${new Date().toISOString()}] Image uploaded successfully: ${uploadResult.path}`,
|
||||
);
|
||||
uploadResult = await result;
|
||||
|
||||
} else if (part.text) {
|
||||
generatedDescription = part.text;
|
||||
console.log(
|
||||
`[${new Date().toISOString()}] Generated description: ${part.text.substring(0, 100)}...`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (uploadResult && uploadResult.success) {
|
||||
return {
|
||||
success: true,
|
||||
|
|
@ -216,10 +195,6 @@ export class ImageGenService {
|
|||
error: "No image data received from API",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[${new Date().toISOString()}] ${modelName} generation failed:`,
|
||||
error,
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
model: modelName,
|
||||
|
|
@ -228,10 +203,6 @@ export class ImageGenService {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate reference images
|
||||
*/
|
||||
static validateReferenceImages(files: Express.Multer.File[]): {
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
|
|
@ -262,9 +233,6 @@ export class ImageGenService {
|
|||
return { valid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Express.Multer.File[] to ReferenceImage[]
|
||||
*/
|
||||
static convertFilesToReferenceImages(
|
||||
files: Express.Multer.File[],
|
||||
): ReferenceImage[] {
|
||||
|
|
@ -312,7 +312,6 @@ export class MinioStorageService implements StorageService {
|
|||
createdAt: obj.lastModified || new Date()
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error processing file ${obj.name}:`, error);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -328,7 +327,6 @@ export class MinioStorageService implements StorageService {
|
|||
filename: string;
|
||||
} | null {
|
||||
try {
|
||||
// Key format: banatie/orgId/projectId/category/year-month/filename
|
||||
const match = key.match(/^banatie\/([^/]+)\/([^/]+)\/(uploads|generated|references)\/[^/]+\/(.+)$/);
|
||||
|
||||
if (!match) {
|
||||
|
|
@ -352,7 +350,6 @@ export class MinioStorageService implements StorageService {
|
|||
}
|
||||
}
|
||||
|
||||
// MISSING METHODS FROM INTERFACE
|
||||
|
||||
async fileExists(
|
||||
orgId: string,
|
||||
|
|
@ -376,7 +373,7 @@ export class MinioStorageService implements StorageService {
|
|||
category: 'uploads' | 'generated' | 'references',
|
||||
prefix?: string
|
||||
): Promise<FileMetadata[]> {
|
||||
this.validateFilePath(orgId, projectId, category, 'dummy.txt'); // Validate path components
|
||||
this.validateFilePath(orgId, projectId, category, 'dummy.txt');
|
||||
|
||||
const basePath = `${orgId}/${projectId}/${category}/`;
|
||||
const searchPrefix = prefix ? `${basePath}${prefix}` : basePath;
|
||||
|
|
@ -406,7 +403,6 @@ export class MinioStorageService implements StorageService {
|
|||
path: obj.name
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error processing file ${obj.name}:`, error);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -25,13 +25,11 @@ export class StorageFactory {
|
|||
}
|
||||
}
|
||||
|
||||
// Synchronous version for backward compatibility (with graceful degradation)
|
||||
static getInstanceSync(): StorageService {
|
||||
if (!this.instance) {
|
||||
try {
|
||||
this.instance = this.createStorageService();
|
||||
} catch (error) {
|
||||
console.error('Failed to create storage service:', error);
|
||||
throw new Error('Storage service unavailable. Please check MinIO configuration.');
|
||||
}
|
||||
}
|
||||
|
|
@ -45,18 +43,14 @@ export class StorageFactory {
|
|||
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
console.log(`Attempting to create storage service (attempt ${attempt}/${maxRetries})`);
|
||||
|
||||
const service = this.createStorageService();
|
||||
|
||||
// Test the connection by checking if bucket exists
|
||||
await service.bucketExists();
|
||||
|
||||
console.log('Storage service created successfully');
|
||||
return service;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Storage service creation attempt ${attempt} failed:`, error);
|
||||
|
||||
if (attempt === maxRetries) {
|
||||
throw new Error(
|
||||
|
|
@ -65,9 +59,7 @@ export class StorageFactory {
|
|||
);
|
||||
}
|
||||
|
||||
// Exponential backoff
|
||||
const delay = baseDelay * Math.pow(2, attempt - 1);
|
||||
console.log(`Waiting ${delay}ms before retry...`);
|
||||
await this.sleep(delay);
|
||||
}
|
||||
}
|
||||
|
|
@ -98,11 +90,6 @@ 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(
|
||||
endpoint,
|
||||
|
|
@ -118,12 +105,10 @@ export class StorageFactory {
|
|||
throw new Error(`Unsupported storage type: ${storageType}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating storage service:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset instance for testing
|
||||
static resetInstance(): void {
|
||||
this.instance = null;
|
||||
this.initializationPromise = null;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Readable } from 'stream';
|
||||
import { Readable } from "stream";
|
||||
|
||||
export interface FileMetadata {
|
||||
filename: string;
|
||||
|
|
@ -42,10 +42,10 @@ export interface StorageService {
|
|||
uploadFile(
|
||||
orgId: string,
|
||||
projectId: string,
|
||||
category: 'uploads' | 'generated' | 'references',
|
||||
category: "uploads" | "generated" | "references",
|
||||
filename: string,
|
||||
buffer: Buffer,
|
||||
contentType: string
|
||||
contentType: string,
|
||||
): Promise<UploadResult>;
|
||||
|
||||
/**
|
||||
|
|
@ -58,8 +58,8 @@ export interface StorageService {
|
|||
downloadFile(
|
||||
orgId: string,
|
||||
projectId: string,
|
||||
category: 'uploads' | 'generated' | 'references',
|
||||
filename: string
|
||||
category: "uploads" | "generated" | "references",
|
||||
filename: string,
|
||||
): Promise<Buffer>;
|
||||
|
||||
/**
|
||||
|
|
@ -72,8 +72,8 @@ export interface StorageService {
|
|||
streamFile(
|
||||
orgId: string,
|
||||
projectId: string,
|
||||
category: 'uploads' | 'generated' | 'references',
|
||||
filename: string
|
||||
category: "uploads" | "generated" | "references",
|
||||
filename: string,
|
||||
): Promise<Readable>;
|
||||
|
||||
/**
|
||||
|
|
@ -87,9 +87,9 @@ export interface StorageService {
|
|||
getPresignedDownloadUrl(
|
||||
orgId: string,
|
||||
projectId: string,
|
||||
category: 'uploads' | 'generated' | 'references',
|
||||
category: "uploads" | "generated" | "references",
|
||||
filename: string,
|
||||
expirySeconds: number
|
||||
expirySeconds: number,
|
||||
): Promise<string>;
|
||||
|
||||
/**
|
||||
|
|
@ -104,10 +104,10 @@ export interface StorageService {
|
|||
getPresignedUploadUrl(
|
||||
orgId: string,
|
||||
projectId: string,
|
||||
category: 'uploads' | 'generated' | 'references',
|
||||
category: "uploads" | "generated" | "references",
|
||||
filename: string,
|
||||
expirySeconds: number,
|
||||
contentType: string
|
||||
contentType: string,
|
||||
): Promise<string>;
|
||||
|
||||
/**
|
||||
|
|
@ -120,8 +120,8 @@ export interface StorageService {
|
|||
listFiles(
|
||||
orgId: string,
|
||||
projectId: string,
|
||||
category: 'uploads' | 'generated' | 'references',
|
||||
prefix?: string
|
||||
category: "uploads" | "generated" | "references",
|
||||
prefix?: string,
|
||||
): Promise<FileMetadata[]>;
|
||||
|
||||
/**
|
||||
|
|
@ -134,8 +134,8 @@ export interface StorageService {
|
|||
deleteFile(
|
||||
orgId: string,
|
||||
projectId: string,
|
||||
category: 'uploads' | 'generated' | 'references',
|
||||
filename: string
|
||||
category: "uploads" | "generated" | "references",
|
||||
filename: string,
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
|
|
@ -148,7 +148,7 @@ export interface StorageService {
|
|||
fileExists(
|
||||
orgId: string,
|
||||
projectId: string,
|
||||
category: 'uploads' | 'generated' | 'references',
|
||||
filename: string
|
||||
category: "uploads" | "generated" | "references",
|
||||
filename: string,
|
||||
): Promise<boolean>;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
experimental: {
|
||||
appDir: true,
|
||||
},
|
||||
images: {
|
||||
domains: ['localhost'],
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
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>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
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>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"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"]
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/** @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
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
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>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
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>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"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:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
context: ./apps/api-service
|
||||
target: development
|
||||
container_name: banatie-app
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- ./src:/app/src
|
||||
- ./logs:/app/logs
|
||||
- ./apps/api-service/src:/app/src
|
||||
- ./apps/api-service/logs:/app/logs
|
||||
networks:
|
||||
- banatie-network
|
||||
depends_on:
|
||||
|
|
|
|||
103
package.json
103
package.json
|
|
@ -1,72 +1,55 @@
|
|||
{
|
||||
"name": "banatie",
|
||||
"name": "banatie-monorepo",
|
||||
"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",
|
||||
"description": "Banatie AI Image Generation Service - Monorepo with API, Landing, Studio, and Admin apps",
|
||||
"private": true,
|
||||
"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"
|
||||
"scripts": {
|
||||
"dev": "pnpm --parallel run dev",
|
||||
"dev:api": "pnpm --filter @banatie/api-service dev",
|
||||
"dev:landing": "pnpm --filter @banatie/landing dev",
|
||||
"dev:studio": "pnpm --filter @banatie/studio dev",
|
||||
"dev:admin": "pnpm --filter @banatie/admin dev",
|
||||
"build": "pnpm -r build",
|
||||
"build:api": "pnpm --filter @banatie/api-service build",
|
||||
"build:landing": "pnpm --filter @banatie/landing build",
|
||||
"build:studio": "pnpm --filter @banatie/studio build",
|
||||
"build:admin": "pnpm --filter @banatie/admin build",
|
||||
"start:api": "pnpm --filter @banatie/api-service start",
|
||||
"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": {
|
||||
"@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"
|
||||
}
|
||||
}
|
||||
3159
pnpm-lock.yaml
3159
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,2 @@
|
|||
packages:
|
||||
- 'apps/*'
|
||||
Loading…
Reference in New Issue