feat(docs): 문서 정리

This commit is contained in:
Kyush 2026-03-25 12:38:03 +09:00
commit c78338fc11
8 changed files with 454 additions and 667 deletions

67
AGENTS.md Normal file
View file

@ -0,0 +1,67 @@
# Kyush LLM Router
다중 사용자 LLM 라우팅 프록시. API 키 인증, 백엔드 라우팅, 스크립트 기반 요청/응답 조작, 사용량 모니터링을 제공한다.
## Tech Stack
pnpm monorepo | Express 5 + TypeScript | Solid.js + Vite | SQLite (better-sqlite3) | isolated-vm
## Project Structure
```
server/ Express 백엔드 (포트 3000)
client/ Solid.js 어드민 대시보드 (포트 3002)
shared/ 공유 TypeScript 타입
database/ SQL 스키마 원본
docs/ 세부 문서
scripts/ 개발 스크립트
```
## Entry Points
- Server: `server/src/index.ts`
- Client: `client/src/index.tsx`
- Shared types: `shared/types.ts`
- DB schema: `database/schema.sql`, `database/analytics-schema.sql`
## Key Concepts
**인증**: `Authorization: Bearer <api_key>``auth.ts` 미들웨어 → 사용자 식별 + 권한 로드
**요청 흐름**:
```
Client → Auth → Script(onRequest) → RouterService → Backend → Script(onResponse) → Response
→ AnalyticsService(log)
```
**Script Engine**: isolated-vm 샌드박스에서 JS 실행. 3가지 타입: per-user, per-backend, per-user-backend
**Database**: core.db (users, backends, permissions, user_scripts) + analytics.db (request_logs, usage_stats, backend_metrics)
## Development
```bash
pnpm install # 의존성 설치
pnpm run dev # 서버 + 클라이언트 동시 실행
pnpm test # 서버 테스트 실행
pnpm run bench # 벤치마크 실행
```
## Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `SERVER_PORT` | 3000 | Express 서버 포트 |
| `CLIENT_PORT` | 3001 | Vite 개발 서버 포트 |
| `CORE_DB_PATH` | ./data/core.db | Core DB 경로 |
| `ANALYTICS_DB_PATH` | ./data/analytics.db | Analytics DB 경로 |
| `ADMIN_PASSWORD` | (필수) | 어드민 비밀번호 |
| `CORS_ORIGINS` | localhost origins | 허용 CORS 오리진 |
## Detailed Docs
- [docs/server.md](docs/server.md) — 서버 구조, 서비스, 모델, 의존성
- [docs/client.md](docs/client.md) — 클라이언트 구조, 라우트, 컴포넌트
- [docs/database.md](docs/database.md) — DB 테이블 스키마 전체
- [docs/api.md](docs/api.md) — API 엔드포인트 레퍼런스
- [docs/scripts.md](docs/scripts.md) — Script Engine 사용법, 타입, 예제

View file

@ -1,477 +0,0 @@
# Kyush LLM Router Architecture Document
## Overview
Kyush LLM Router is a proxy server that manages multiple users and routes their requests to various OpenAI-compatible backend APIs. It provides API key-based authentication, user permission management, and usage monitoring through an admin web dashboard.
## System Architecture
```
┌───────────────────────────────────────────────────────────────────────────┐
│ Client Layer │
├─────────────────────┬───────────────────────────────┬─────────────────────┤
│ LLM Clients │ Admin Dashboard │ Vite Dev Server │
│ (OpenAI SDK etc) │ (Solid.js + Vite) │ (Port 5173) │
└──────────┬──────────┴──────────────┬────────────────┴──────────┬──────────┘
│ │ │
│ OpenAI-Compatible API │ REST API │
│ (Port 3000) │ (Port 3001) │
▼ ▼ │
┌───────────────────────────────────────────────────────────────────────────┐
│ Router Server (Node.js/Express) │
│ (Port 3000) │
├───────────────────────────────────────────────────────────────────────────┤
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ Authentication Middleware │ │
│ │ - API Key Validation │ │
│ │ - Rate Limiting │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ Request Router │ │
│ │ - Backend Selection │ │
│ │ - Load Balancing (optional) │ │
│ │ - Request/Response Transformation │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ Admin API Endpoints (proxied to Vite dev server) │ │
│ │ - User Management │ │
│ │ - Backend Management │ │
│ │ - Permission Management │ │
│ │ - Usage Analytics │ │
│ └───────────────────────────────────────────────────────────────────┘ │
└────────────────────────────┬──────────────────────────────────────────────┘
┌─────────────────┴─────────────────┐
▼ ▼
┌───────────────────────┐ ┌─────────────────────────┐
│ Core Database │ │ Analytics Database │
│ (SQLite - core.db) │ │ (SQLite - analytics.db)│
├───────────────────────┤ ├─────────────────────────┤
│ - users │ │ - request_logs │
│ - backends │ │ - usage_stats │
│ - permissions │ │ - backend_metrics │
└───────────────────────┘ └─────────────────────────┘
│ │
└─────────────────┬─────────────────┘
┌───────────────────────────────────────────────────────────────────────────┐
│ Backend Layer │
├───────────────────────────────────────────────────────────────────────────┤
│ Multiple OpenAI-Compatible APIs (vLLM, SGLang, etc.) │
└───────────────────────────────────────────────────────────────────────────┘
```
## Project Structure
```
kyush-llm-router/
├── server/
│ ├── src/
│ │ ├── index.ts # Express app entry point
│ │ ├── config/
│ │ │ ├── database.ts # Core SQLite connection
│ │ │ └── analytics-db.ts # Analytics SQLite connection
│ │ ├── routes/
│ │ │ ├── api.ts # OpenAI-compatible API proxy
│ │ │ ├── admin.ts # Admin management API
│ │ │ └── auth.ts # Authentication middleware
│ │ ├── models/
│ │ │ ├── User.ts # User model
│ │ │ ├── Backend.ts # Backend model
│ │ │ └── Permission.ts # Permission model
│ │ ├── analytics/
│ │ │ ├── RequestLog.ts # Request log model (analytics DB)
│ │ │ ├── UsageStats.ts # Usage stats model (analytics DB)
│ │ │ └── BackendMetrics.ts # Backend metrics model
│ │ ├── services/
│ │ │ ├── AuthService.ts # API key validation
│ │ │ ├── RouterService.ts # Backend routing logic
│ │ │ └── AnalyticsService.ts # Usage tracking
│ │ └── utils/
│ │ └── logger.ts # Logging utility
│ ├── package.json
│ └── tsconfig.json
├── client/
│ ├── src/
│ │ ├── index.tsx # Solid.js entry point
│ │ ├── App.tsx # Main application component
│ │ ├── routes/
│ │ │ ├── Dashboard.tsx # Main dashboard
│ │ │ ├── Users.tsx # User management
│ │ │ ├── Backends.tsx # Backend management
│ │ │ ├── Permissions.tsx # Permission management
│ │ │ └── Analytics.tsx # Usage monitoring
│ │ ├── components/
│ │ │ ├── Layout.tsx # Admin layout
│ │ │ ├── UserTable.tsx # User list table
│ │ │ ├── BackendTable.tsx # Backend list table
│ │ │ └── StatsChart.tsx # Usage chart component
│ │ ├── api/
│ │ │ └── client.ts # API client for admin endpoints
│ │ └── types/
│ │ └── index.ts # TypeScript type definitions
│ ├── package.json
│ ├── vite.config.ts
│ └── tsconfig.json
├── shared/
│ └── types.ts # Shared type definitions
├── database/
│ ├── schema.sql # Core database schema
│ └── analytics-schema.sql # Analytics database schema
├── scripts/
│ └── dev.js # Dev server launcher (runs both)
├── docker-compose.yml # Docker Compose setup
├── package.json # Root package.json (scripts)
└── README.md
```
## Core Components
### 1. Server (Node.js/Express)
#### Authentication Middleware (`src/routes/auth.ts`)
- Validates API keys from incoming requests
- Extracts user identity from `Authorization: Bearer <api_key>` header
- Returns 401 if authentication fails
- Attaches user info to request object for downstream handlers
#### API Proxy Route (`src/routes/api.ts`)
```typescript
// OpenAI-compatible endpoints
POST /v1/chat/completions
POST /v1/completions
GET /v1/models
```
- Forwards requests to selected backend
- Handles request/response transformation
- Logs all requests for analytics
#### Admin API Route (`src/routes/admin.ts`)
```typescript
// User Management
POST /admin/users # Create user
GET /admin/users # List users
PUT /admin/users/:id # Update user
DELETE /admin/users/:id # Delete user
// Backend Management
POST /admin/backends # Add backend
GET /admin/backends # List backends
PUT /admin/backends/:id # Update backend
DELETE /admin/backends/:id # Delete backend
// Permission Management
POST /admin/permissions # Grant permission
DELETE /admin/permissions # Revoke permission
GET /admin/permissions # List permissions
// Analytics
GET /admin/analytics/usage # Usage statistics
GET /admin/analytics/requests # Request logs
```
#### Router Service (`src/services/RouterService.ts`)
- Selects appropriate backend based on:
- User's permissions
- Backend availability
- Load balancing strategy (round-robin, least-loaded)
- Handles backend failures with retry logic
### 2. Client (Solid.js + Vite)
#### Pages
- **Dashboard**: Overview of system status, recent activity
- **Users**: CRUD operations for users, API key generation
- **Backends**: Manage backend API configurations
- **Permissions**: Assign/revoke backend access per user
- **Analytics**: View usage statistics and request logs
#### Key Features
- Real-time updates via polling or WebSocket (optional)
- Form validation for user/backend input
- Error handling with user-friendly messages
- Responsive design for various screen sizes
### 3. Databases (SQLite)
#### Core Database Schema (core.db)
```sql
-- Users table
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
api_key TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
email TEXT,
is_active BOOLEAN DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Backends table
CREATE TABLE backends (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
base_url TEXT NOT NULL,
api_key TEXT,
is_active BOOLEAN DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Permissions table (many-to-many: users ↔ backends)
CREATE TABLE permissions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
backend_id INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (backend_id) REFERENCES backends(id),
UNIQUE(user_id, backend_id)
);
-- Indexes for performance
CREATE INDEX idx_users_api_key ON users(api_key);
CREATE INDEX idx_permissions_user ON permissions(user_id);
CREATE INDEX idx_permissions_backend ON permissions(backend_id);
```
#### Analytics Database Schema (analytics.db)
```sql
-- Request logs table
CREATE TABLE request_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
backend_id INTEGER NOT NULL,
endpoint TEXT NOT NULL,
request_model TEXT,
response_model TEXT,
prompt_tokens INTEGER,
completion_tokens INTEGER,
total_tokens INTEGER,
status_code INTEGER,
response_time_ms INTEGER,
error_message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (backend_id) REFERENCES backends(id)
);
-- Usage stats table (aggregated daily)
CREATE TABLE usage_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
backend_id INTEGER NOT NULL,
date DATE NOT NULL,
total_requests INTEGER DEFAULT 0,
total_tokens INTEGER DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (backend_id) REFERENCES backends(id),
UNIQUE(user_id, backend_id, date)
);
-- Backend metrics table (aggregated metrics per backend)
CREATE TABLE backend_metrics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
backend_id INTEGER NOT NULL,
date DATE NOT NULL,
total_requests INTEGER DEFAULT 0,
total_tokens INTEGER DEFAULT 0,
avg_response_time_ms REAL DEFAULT 0,
error_count INTEGER DEFAULT 0,
success_rate REAL DEFAULT 1.0,
FOREIGN KEY (backend_id) REFERENCES backends(id),
UNIQUE(backend_id, date)
);
-- Indexes for performance
CREATE INDEX idx_request_logs_user ON request_logs(user_id);
CREATE INDEX idx_request_logs_backend ON request_logs(backend_id);
CREATE INDEX idx_request_logs_date ON request_logs(created_at);
CREATE INDEX idx_usage_stats_user ON usage_stats(user_id);
CREATE INDEX idx_usage_stats_date ON usage_stats(date);
CREATE INDEX idx_backend_metrics_backend ON backend_metrics(backend_id);
CREATE INDEX idx_backend_metrics_date ON backend_metrics(date);
```
## API Design
### OpenAI-Compatible API Proxy
The router exposes the same API interface as OpenAI, making it easy for clients to integrate.
#### Example Request
```bash
curl http://localhost:3000/v1/chat/completions \
-H "Authorization: Bearer <user_api_key>" \
-H "Content-Type: application/json" \
-d '{
"model": "llama-3",
"messages": [{"role": "user", "content": "Hello"}]
}'
```
#### Example Response
```json
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1234567890,
"model": "llama-3",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you?"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 15,
"total_tokens": 25
}
}
```
### Admin API
#### Create User
```bash
POST /admin/users
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com"
}
Response: {
"id": 1,
"name": "John Doe",
"api_key": "sk-xxx...",
"created_at": "2024-01-01T00:00:00Z"
}
```
#### Add Backend
```bash
POST /admin/backends
Content-Type: application/json
{
"name": "vLLM Server 1",
"base_url": "http://localhost:8000/v1",
"api_key": "backend-key-xxx"
}
```
## Security Considerations
1. **API Key Storage**: API keys are stored hashed in the database
2. **Transport Security**: Use HTTPS in production
3. **Rate Limiting**: Implement per-user rate limits
4. **Input Validation**: Validate all admin API inputs
5. **CORS**: Configure CORS for admin dashboard only
## Deployment
### Development
Run both server and client with a single command:
```bash
npm run dev
```
This starts:
- Express server on port 3000
- Vite dev server on port 3001 (admin API routes proxied from Express)
### Docker Compose
```yaml
version: '3.8'
services:
router:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- SERVER_PORT=3000
- CLIENT_PORT=3001
- CORE_DB_PATH=/data/core.db
- ANALYTICS_DB_PATH=/data/analytics.db
volumes:
- router-data:/data
restart: unless-stopped
volumes:
router-data:
```
### Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `SERVER_PORT` | Express server port | `3000` |
| `CLIENT_PORT` | Vite dev server port | `3001` |
| `CORE_DB_PATH` | Core database path | `./data/core.db` |
| `ANALYTICS_DB_PATH` | Analytics database path | `./data/analytics.db` |
| `ADMIN_PASSWORD` | Admin dashboard password | (required) |
## Development Roadmap
### Phase 1: Core Infrastructure
- [ ] Set up Express server with TypeScript
- [ ] Implement SQLite database schema
- [ ] Create user and backend models
- [ ] Implement API key authentication
### Phase 2: API Proxy
- [ ] Implement OpenAI-compatible API endpoints
- [ ] Add request forwarding to backends
- [ ] Implement basic routing logic
- [ ] Add request logging
### Phase 3: Admin API
- [ ] Implement CRUD endpoints for users
- [ ] Implement CRUD endpoints for backends
- [ ] Implement permission management
- [ ] Add usage analytics endpoints
### Phase 4: Admin Dashboard
- [ ] Set up Solid.js + Vite project
- [ ] Create user management UI
- [ ] Create backend management UI
- [ ] Create permission management UI
- [ ] Create analytics dashboard
### Phase 5: Advanced Features
- [ ] Add rate limiting
- [ ] Implement load balancing
- [ ] Add WebSocket for real-time updates
- [ ] Implement backend health checks
## Technology Stack Summary
| Component | Technology |
|-----------|------------|
| Backend | Node.js 18+, Express.js, TypeScript |
| Core Database | SQLite (better-sqlite3) |
| Analytics Database | SQLite (better-sqlite3) |
| Frontend | Solid.js, Vite, TypeScript |
| HTTP Client | Axios/Fetch |
| Charts | Chart.js or Solid-chart |
| Styling | Tailwind CSS or CSS Modules |
| Validation | Zod |
| Testing | Vitest (frontend), Jest (backend) |
| Dev Server | Concurrently for running both servers |

195
README.md
View file

@ -1,202 +1,17 @@
# Kyush LLM Router
Multi-user LLM routing proxy with API key management, usage monitoring, and script-based request/response manipulation.
## Features
- **Multi-user support**: Manage multiple users with individual API keys
- **Backend routing**: Route requests to multiple OpenAI-compatible backends (vLLM, SGLang, etc.) with load balancing
- **Permission management**: Control which users can access which backends
- **Usage monitoring**: Track request logs, token usage, and backend metrics
- **Script engine**: Execute JavaScript code in secure isolates to manipulate requests/responses
- `onRequest`: Modify requests before forwarding to backend
- `onResponse`: Modify responses before returning to client
- Script types: `per-user`, `per-backend`, `per-user-backend`
- **Admin dashboard**: Web UI for managing users, backends, permissions, and scripts
## Architecture
```
┌─────────────────┐ ┌──────────────────────────────────────┐
│ LLM Clients │────▶│ Router Server (Port 3000) │
│ (OpenAI SDK) │ │ - Authentication │
└─────────────────┘ │ - Request Routing │
│ - Script Execution (isolated-vm) │
│ - Admin API │
└─────────────────┬────────────────────┘
┌─────────────────┴────────────────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ core.db │ │ analytics.db │
│ - users │ │ - request_logs │
│ - backends │ │ - usage_stats │
│ - permissions │ │ - metrics │
│ - user_scripts │ │ │
└─────────────────┘ └─────────────────┘
```
다중 사용자 LLM 라우팅 프록시 — API 키 인증, 백엔드 라우팅, 스크립트 기반 요청/응답 조작, 사용량 모니터링.
## Quick Start
### Development
1. Install dependencies:
```bash
pnpm install
```
2. Start development servers:
```bash
pnpm run dev
```
This starts:
- Express API server on http://localhost:3000
- Vite admin dashboard on http://localhost:3001
- API 서버: http://localhost:3000
- 어드민 대시보드: http://localhost:3002
### Production with Docker
## Documentation
1. Build and run:
```bash
docker-compose up -d
```
2. Set admin password via environment variable:
```bash
ADMIN_PASSWORD=your_secure_password docker-compose up -d
```
## Project Structure
```
kyush-llm-router/
├── server/ # Express backend
│ ├── src/
│ │ ├── config/ # Database configuration
│ │ ├── models/ # Data models (User, Backend, Permission, Script)
│ │ ├── routes/ # API routes (admin, api, scripts, analytics)
│ │ ├── services/ # RouterService, ScriptEngine, AnalyticsService
│ │ └── utils/ # Utilities (apiKey, logger)
│ ├── tests/ # Integration tests
│ └── benchmarks/ # Performance benchmarks
├── client/ # Solid.js admin dashboard
│ └── src/
│ ├── api/ # API client
│ ├── components/ # UI components
│ └── types/ # TypeScript types
├── shared/ # Shared TypeScript types
├── scripts/ # Development scripts
└── docs/ # Documentation
```
## API Endpoints
### OpenAI-Compatible API (Port 3000)
```
POST /v1/chat/completions # Chat completions with routing & scripting
GET /v1/models # List available models
```
### Admin API (Port 3001)
```
# User Management
POST /admin/users # Create user
GET /admin/users # List all users
GET /admin/users/:id # Get user by ID
PUT /admin/users/:id # Update user
DELETE /admin/users/:id # Delete user
POST /admin/users/:id/regenerate-api-key # Regenerate API key
# Backend Management
POST /admin/backends # Create backend
GET /admin/backends # List all backends
GET /admin/backends/:id # Get backend by ID
PUT /admin/backends/:id # Update backend
DELETE /admin/backends/:id # Delete backend
# Permission Management
POST /admin/permissions # Create permission
GET /admin/permissions # List all permissions
GET /admin/permissions/user/:userId # Get permissions by user
GET /admin/permissions/backend/:backendId # Get permissions by backend
DELETE /admin/permissions # Delete permission (query params: user_id, backend_id)
# Script Management
GET /admin/scripts # List all scripts
GET /admin/scripts/active # List active scripts
GET /admin/scripts/type/:type # List scripts by type
GET /admin/scripts/:id # Get script by ID
POST /admin/scripts # Create script
PUT /admin/scripts/:id # Update script
DELETE /admin/scripts/:id # Delete script
POST /admin/scripts/:id/activate # Activate script
POST /admin/scripts/:id/deactivate # Deactivate script
POST /admin/scripts/:id/test # Test script
# Analytics
GET /admin/analytics/usage # Get usage statistics
GET /admin/analytics/requests # Get request logs
```
## Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `SERVER_PORT` | Express server port | `3000` |
| `CLIENT_PORT` | Vite dev server port | `3001` |
| `CORE_DB_PATH` | Core database path | `./data/core.db` |
| `ANALYTICS_DB_PATH` | Analytics database path | `./data/analytics.db` |
| `ADMIN_PASSWORD` | Admin dashboard password | Required |
| `CORS_ORIGINS` | Comma-separated allowed origins | `http://localhost:5173,http://localhost:3001` |
## Script Engine
The router supports JavaScript code execution in secure isolated VMs for request/response manipulation.
### Script Types
- **per-user**: Applied to all requests from a specific user
- **per-backend**: Applied to all requests going to a specific backend
- **per-user-backend**: Applied to requests from a specific user to a specific backend
### Script Context
Scripts have access to:
- `user`: User information (id, name, email)
- `backend`: Backend information (id, name, base_url)
- `request`: Request details (method, path, headers, body, isStream)
- `response`: Response details (status, headers, body, isStream)
### Example Script
```javascript
export async function onRequest(context) {
// Add custom headers to all requests
context.request.headers['X-Custom-Header'] = 'value';
return context;
}
export async function onResponse(context) {
// Log response status
console.log(`Response status: ${context.response.status}`);
return context;
}
```
## Testing
Run tests:
```bash
pnpm test
```
Run benchmarks:
```bash
pnpm run bench
```
## License
MIT
프로젝트 구조와 개발 가이드는 [AGENTS.md](AGENTS.md)를 참조.

76
docs/api.md Normal file
View file

@ -0,0 +1,76 @@
# API Reference
서버 포트: `SERVER_PORT` (기본 3000)
---
## Health
| Method | Path | Description |
|--------|------|-------------|
| GET | `/health` | 서버 상태 확인 (status, timestamp) |
## OpenAI-Compatible Proxy (인증 필요)
`Authorization: Bearer <user_api_key>` 헤더 필수.
| Method | Path | Description |
|--------|------|-------------|
| POST | `/v1/chat/completions` | Chat completions 프록시 (스크립트 적용, 분석 로깅) |
| GET | `/v1/models` | 사용 가능한 모델 목록 |
## Admin API
### Users
| Method | Path | Description |
|--------|------|-------------|
| GET | `/admin/users` | 전체 사용자 목록 |
| POST | `/admin/users` | 사용자 생성 (API 키 자동 발급) |
| GET | `/admin/users/:id` | 사용자 조회 |
| PUT | `/admin/users/:id` | 사용자 수정 (name, email, is_active) |
| DELETE | `/admin/users/:id` | 사용자 삭제 |
| POST | `/admin/users/:id/regenerate-api-key` | API 키 재발급 |
### Backends
| Method | Path | Description |
|--------|------|-------------|
| GET | `/admin/backends` | 전체 백엔드 목록 |
| POST | `/admin/backends` | 백엔드 생성 (name, base_url, api_key) |
| GET | `/admin/backends/:id` | 백엔드 조회 |
| PUT | `/admin/backends/:id` | 백엔드 수정 |
| DELETE | `/admin/backends/:id` | 백엔드 삭제 |
### Permissions
| Method | Path | Description |
|--------|------|-------------|
| GET | `/admin/permissions` | 전체 권한 목록 |
| GET | `/admin/permissions/user/:userId` | 사용자별 권한 조회 |
| GET | `/admin/permissions/backend/:backendId` | 백엔드별 권한 조회 |
| POST | `/admin/permissions` | 권한 부여 (user_id, backend_id) |
| DELETE | `/admin/permissions?user_id=X&backend_id=Y` | 권한 삭제 |
### Scripts
| Method | Path | Description |
|--------|------|-------------|
| GET | `/admin/scripts` | 전체 스크립트 목록 |
| GET | `/admin/scripts/active` | 활성 스크립트 목록 |
| GET | `/admin/scripts/type/:type` | 타입별 스크립트 목록 |
| GET | `/admin/scripts/:id` | 스크립트 조회 |
| POST | `/admin/scripts` | 스크립트 생성 |
| PUT | `/admin/scripts/:id` | 스크립트 수정 |
| DELETE | `/admin/scripts/:id` | 스크립트 삭제 |
| POST | `/admin/scripts/:id/activate` | 스크립트 활성화 |
| POST | `/admin/scripts/:id/deactivate` | 스크립트 비활성화 |
| POST | `/admin/scripts/:id/test` | 스크립트 테스트 실행 |
### Analytics
| Method | Path | Query Params | Description |
|--------|------|-------------|-------------|
| GET | `/admin/analytics/usage` | userId, backendId, days | 사용량 통계 |
| GET | `/admin/analytics/requests` | limit, offset | 요청 로그 (페이지네이션) |
| GET | `/admin/analytics/metrics` | backendId, days | 백엔드 성능 메트릭 |

56
docs/client.md Normal file
View file

@ -0,0 +1,56 @@
# Client (Solid.js + Vite)
Admin 대시보드. 진입점: `client/src/index.tsx`
## Directory Structure
```
client/src/
index.tsx # DOM 렌더링 진입점
App.tsx # 라우터 정의 (6개 라우트)
api/
client.ts # Admin API 클라이언트 (users, backends, permissions, scripts, analytics)
types/
index.ts # TypeScript 타입 정의
routes/
Dashboard.tsx # 홈 — 요약 카드 (사용자 수, 활성 백엔드, 최근 요청) + 최근 요청 테이블
Users.tsx # 사용자 관리 — CRUD, API 키 발급/재발급, 활성화 토글
Backends.tsx # 백엔드 관리 — CRUD (name, base_url, api_key)
Permissions.tsx # 권한 관리 — user-backend 매핑
Analytics.tsx # 분석 — 요청 로그, 사용량 통계, 백엔드 메트릭
Scripts.tsx # 스크립트 관리 — CRUD, 테스트, 활성화/비활성화
components/
Layout.tsx # 사이드바 네비게이션 + 메인 콘텐츠 레이아웃
EditModal.tsx # 범용 편집 모달 (동적 폼 필드)
ScriptEditor.tsx # Monaco 에디터 래퍼 (TypeScript 하이라이팅)
```
## Routes
| URL | Component | Description |
|-----|-----------|-------------|
| `/` | Dashboard | 시스템 개요 |
| `/users` | Users | 사용자 관리 |
| `/backends` | Backends | 백엔드 관리 |
| `/permissions` | Permissions | 권한 관리 |
| `/analytics` | Analytics | 분석 대시보드 |
| `/scripts` | Scripts | 스크립트 관리 |
## Styling
CSS 프레임워크 없음. 인라인 `style` props 사용.
- 다크 사이드바 (#1e293b), 라이트 배경 (#f1f5f9), 화이트 카드
- Flexbox / Grid 레이아웃
## Dependencies
| Package | Purpose |
|---------|---------|
| solid-js | UI 프레임워크 |
| @solidjs/router | 클라이언트 사이드 라우팅 |
| solid-monaco | Monaco 에디터 통합 |
| vite | 빌드 도구 + 개발 서버 |
## Dev Server
포트: 3002 (vite.config.ts), API 프록시: `/api``http://localhost:3000`

123
docs/database.md Normal file
View file

@ -0,0 +1,123 @@
# Database Schema
두 개의 SQLite 데이터베이스를 사용한다. 설정 데이터는 `core.db`, 운영 데이터는 `analytics.db`에 저장된다.
스키마 원본: [database/schema.sql](../database/schema.sql), [database/analytics-schema.sql](../database/analytics-schema.sql)
---
## Core Database (core.db)
### users
| Column | Type | Constraints |
|--------|------|-------------|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT |
| api_key | TEXT | UNIQUE NOT NULL |
| name | TEXT | NOT NULL |
| email | TEXT | |
| is_active | BOOLEAN | DEFAULT 1 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
Indexes: `idx_users_api_key(api_key)`
### backends
| Column | Type | Constraints |
|--------|------|-------------|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT |
| name | TEXT | UNIQUE NOT NULL |
| base_url | TEXT | NOT NULL |
| api_key | TEXT | |
| is_active | BOOLEAN | DEFAULT 1 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
### permissions
users와 backends의 many-to-many 관계.
| Column | Type | Constraints |
|--------|------|-------------|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT |
| user_id | INTEGER | NOT NULL, FK → users(id) |
| backend_id | INTEGER | NOT NULL, FK → backends(id) |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
Unique: `(user_id, backend_id)`
Indexes: `idx_permissions_user(user_id)`, `idx_permissions_backend(backend_id)`
### user_scripts
| Column | Type | Constraints |
|--------|------|-------------|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT |
| name | TEXT | UNIQUE NOT NULL |
| script_type | TEXT | NOT NULL, CHECK IN ('per-user-backend', 'per-backend', 'per-user') |
| target_user_id | INTEGER | FK → users(id) |
| target_backend_id | INTEGER | FK → backends(id) |
| script_code | TEXT | NOT NULL |
| is_active | BOOLEAN | DEFAULT 1 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
Indexes: `idx_user_scripts_type`, `idx_user_scripts_active`, `idx_user_scripts_target_user`, `idx_user_scripts_target_backend`
---
## Analytics Database (analytics.db)
### request_logs
| Column | Type | Constraints |
|--------|------|-------------|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT |
| user_id | INTEGER | NOT NULL |
| backend_id | INTEGER | NOT NULL |
| endpoint | TEXT | NOT NULL |
| request_model | TEXT | |
| response_model | TEXT | |
| prompt_tokens | INTEGER | |
| completion_tokens | INTEGER | |
| total_tokens | INTEGER | |
| status_code | INTEGER | |
| response_time_ms | INTEGER | |
| error_message | TEXT | |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
Indexes: `idx_request_logs_user`, `idx_request_logs_backend`, `idx_request_logs_date(created_at)`, `idx_request_logs_user_backend`
### usage_stats
일별 집계 테이블.
| Column | Type | Constraints |
|--------|------|-------------|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT |
| user_id | INTEGER | NOT NULL |
| backend_id | INTEGER | NOT NULL |
| date | DATE | NOT NULL |
| total_requests | INTEGER | DEFAULT 0 |
| total_tokens | INTEGER | DEFAULT 0 |
Unique: `(user_id, backend_id, date)`
Indexes: `idx_usage_stats_user`, `idx_usage_stats_date`
### backend_metrics
백엔드별 일별 성능 집계.
| Column | Type | Constraints |
|--------|------|-------------|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT |
| backend_id | INTEGER | NOT NULL |
| date | DATE | NOT NULL |
| total_requests | INTEGER | DEFAULT 0 |
| total_tokens | INTEGER | DEFAULT 0 |
| avg_response_time_ms | REAL | DEFAULT 0 |
| error_count | INTEGER | DEFAULT 0 |
| success_rate | REAL | DEFAULT 1.0 |
Unique: `(backend_id, date)`
Indexes: `idx_backend_metrics_backend`, `idx_backend_metrics_date`

58
docs/scripts.md Normal file
View file

@ -0,0 +1,58 @@
# Script Engine
isolated-vm 기반 JavaScript 샌드박스에서 요청/응답을 조작하는 미들웨어 시스템.
## Script Types
| Type | Target | Description |
|------|--------|-------------|
| `per-user` | target_user_id | 특정 사용자의 모든 요청에 적용 |
| `per-backend` | target_backend_id | 특정 백엔드로 가는 모든 요청에 적용 |
| `per-user-backend` | target_user_id + target_backend_id | 특정 사용자-백엔드 조합에 적용 |
## Hooks
### onRequest
백엔드 포워딩 전에 실행. context를 수정하여 요청을 변조할 수 있다.
### onResponse
백엔드 응답 수신 후 실행. context를 수정하여 응답을 변조할 수 있다.
## Script Context
스크립트에서 접근 가능한 데이터:
```typescript
interface ScriptContextData {
user: { id, name, email }
backend: { id, name, base_url }
request: { method, path, headers, body, isStream }
response?: { status, headers, body, isStream } // onResponse에서만
}
```
## Example
```javascript
export async function onRequest(context) {
// 요청 헤더 추가
context.request.headers['X-Custom-Header'] = 'value';
return context;
}
export async function onResponse(context) {
// 응답 로깅
console.log(`Status: ${context.response.status}`);
return context;
}
```
## Execution Environment
- **Runtime**: isolated-vm (V8 isolate)
- **Timeout**: 5초
- **Memory**: 50MB per isolate
- **Console**: logger에 바인딩됨
- 관련 코드: `server/src/services/ScriptEngine.ts`, `server/src/services/ScriptExecutor.ts`

69
docs/server.md Normal file
View file

@ -0,0 +1,69 @@
# Server (Express + TypeScript)
진입점: `server/src/index.ts`
## Directory Structure
```
server/src/
index.ts # Express 앱 팩토리 (CORS, 라우트 설정, health 엔드포인트)
config/
database.ts # Core SQLite 연결 및 스키마 초기화
analytics-db.ts # Analytics SQLite 연결 및 스키마 초기화
models/
User.ts # 사용자 CRUD (create, findById, findByApiKey, update, delete, regenerateApiKey)
Backend.ts # 백엔드 CRUD (create, findById, findAll, activate/deactivate)
Permission.ts # 권한 관리 (user-backend 매핑)
Script.ts # 스크립트 CRUD (타입별 필터링, 활성화/비활성화)
routes/
index.ts # 라우트 마운팅
auth.ts # Bearer 토큰 인증 미들웨어 (API 키 검증, 권한 로드)
api.ts # OpenAI 호환 프록시 엔드포인트 (/v1/chat/completions, /v1/models)
admin.ts # Admin CRUD 엔드포인트 (users, backends, permissions)
scripts.ts # Script 관리 엔드포인트
analytics.ts # Analytics 조회 엔드포인트
services/
RouterService.ts # 백엔드 선택 (랜덤 로드밸런싱) 및 HTTP 요청 포워딩
AnalyticsService.ts # 요청 로깅, 일별 사용량/메트릭 집계
ScriptEngine.ts # 스크립트 체인 오케스트레이션 (onRequest/onResponse 훅 적용)
ScriptExecutor.ts # isolated-vm 기반 스크립트 컴파일/실행 (5s timeout, 50MB memory)
utils/
apiKey.ts # API 키 생성 (sk-{timestamp}-{random}, crypto.randomBytes)
logger.ts # 컬러 콘솔 로거
```
## Request Flow
```
Client → auth.ts (API 키 검증, 권한 로드)
→ RouterService.selectBackend (랜덤 선택)
→ ScriptEngine.applyOnRequestScripts (요청 변조)
→ RouterService.forwardRequest (백엔드 프록시)
→ ScriptEngine.applyOnResponseScripts (응답 변조)
→ AnalyticsService.logRequest (로깅)
→ Response
```
## Dependencies
| Package | Purpose |
|---------|---------|
| express@5 | 웹 프레임워크 |
| better-sqlite3 | SQLite 드라이버 |
| isolated-vm | 스크립트 샌드박스 실행 |
| zod | 입력 검증 |
| node-fetch | 백엔드 HTTP 요청 |
| cors | CORS 미들웨어 |
| dotenv | 환경변수 로딩 |
## Tests
통합 테스트: `server/tests/integration/`
- `api.test.ts` — 인증, 인가, 프록시 엔드포인트
- `admin.test.ts` — Admin CRUD
- `routing.test.ts` — 백엔드 선택, 요청 포워딩
- `scripts.test.ts` — 스크립트 생성, 실행, 훅
테스트 유틸: `server/tests/integration/utils/` (testApp.ts, mockBackend.ts)
벤치마크: `server/benchmarks/` (runner.ts, scenarios.ts, report.ts, stats.ts)