docs: add provisioning API documentation to CLAUDE.md

- Add provisioning service files to Architecture section
- Document telegram-conversations DB tables (users, user_deposits, server_orders)
- Add Server Provisioning API section with endpoints and security features
- Update Bindings with USER_DB and PROVISION_QUEUE
- Add provisioning API test examples
- Include schema-provisioning.sql for reference

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-27 17:22:36 +09:00
parent 9b51b8d427
commit 91a6e227ed
2 changed files with 182 additions and 7 deletions

119
CLAUDE.md
View File

@@ -32,12 +32,12 @@ npx wrangler tail
```
src/
├── index.ts # Main router, CORS, request handling
├── index.ts # Main router, CORS, Queue consumer
├── config.ts # Configuration constants
├── types.ts # TypeScript type definitions
├── region-utils.ts # Region matching utilities
├── utils.ts # Re-exports from utils/ (backward compatibility)
├── utils/ # Modular utilities (split from monolithic utils.ts)
├── utils/ # Modular utilities
│ ├── index.ts # Central export point
│ ├── http.ts # HTTP responses, CORS, escapeHtml
│ ├── validation.ts # Input validation, type guards
@@ -46,14 +46,21 @@ src/
│ ├── ai.ts # AI prompt sanitization
│ └── exchange-rate.ts # Currency conversion
├── repositories/
── AnvilServerRepository.ts # DB queries for Anvil servers
── AnvilServerRepository.ts # DB queries for Anvil servers
│ └── ProvisioningRepository.ts # Users, deposits, orders (telegram-conversations)
├── services/
── ai-service.ts # AI recommendations & fallback logic
── ai-service.ts # AI recommendations & fallback logic
│ ├── provisioning-service.ts # Server provisioning workflow
│ ├── vps-provider.ts # VPS provider abstract base class
│ ├── linode-provider.ts # Linode API implementation
│ └── vultr-provider.ts # Vultr API implementation
├── handlers/
│ ├── health.ts # GET /api/health
│ ├── servers.ts # GET /api/servers
│ ├── recommend.ts # POST /api/recommend
── report.ts # GET /api/recommend/report
── report.ts # GET /api/recommend/report
│ ├── provision.ts # POST/GET/DELETE /api/provision/*
│ └── queue.ts # Queue consumer for async provisioning
└── __tests__/
├── utils.test.ts # Validation & security tests (27 tests)
└── bandwidth.test.ts # Bandwidth estimation tests (32 tests, including CDN)
@@ -84,6 +91,13 @@ src/
**Legacy tables (no longer used)**:
- `providers`, `instance_types`, `pricing`, `regions` - Old Linode/Vultr data
### D1 Database Tables (telegram-conversations)
**User & Payment tables**:
- `users` - Telegram users (id, telegram_id, username)
- `user_deposits` - User balance in KRW (user_id, balance)
- `server_orders` - Server provisioning orders (status, price_paid, provider_instance_id, ip_address)
## Key Implementation Details
### DB Workload Multiplier (`recommend.ts`)
@@ -254,6 +268,62 @@ When OpenAI API is unavailable, the system automatically falls back to rule-base
- Provides basic capacity estimates based on vCPU count
- Warns user that AI is temporarily unavailable
### Server Provisioning API (`handlers/provision.ts`)
Queue-based async server provisioning with automatic balance management.
**Endpoints** (require `X-API-Key` header):
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/provision` | POST | Create server order (sends to Queue) |
| `/api/provision/orders` | GET | List user's orders |
| `/api/provision/orders/:id` | GET | Get order details (includes root_password) |
| `/api/provision/orders/:id` | DELETE | Terminate server |
| `/api/provision/balance` | GET | Get user balance (KRW) |
**Provisioning Flow**:
```
POST /api/provision
1. Validate user (telegram_id)
2. Get pricing (anvil_pricing)
3. Calculate KRW price (exchange rate × 500원 rounding)
4. Deduct balance (atomic query)
5. Create order (status: provisioning)
6. Send to Queue → Return immediately
[Queue Consumer]
7. Fetch order (get root_password from DB)
8. Call provider API (Linode/Vultr)
9. Wait for IP assignment (polling, max 2min)
10. Update order (status: active, ip_address)
On failure: Refund balance + status: failed
```
**Request Body**:
```json
{
"user_id": "telegram_id",
"pricing_id": 26,
"label": "my-server",
"image": "ubuntu_22_04",
"dry_run": false
}
```
**Security Features**:
- API key authentication (`X-API-Key` header)
- Origin validation for browser requests
- Atomic balance deduction (prevents race condition)
- Root password stored in DB only (not in Queue message)
- Automatic refund on any failure
**Supported Providers**:
- Linode (`linode-provider.ts`)
- Vultr (`vultr-provider.ts`)
### Configuration (`config.ts`)
Centralized limits and constants:
@@ -280,8 +350,23 @@ binding = "DB"
database_name = "cloud-instances-db"
database_id = "bbcb472d-b25e-4e48-b6ea-112f9fffb4a8"
[vars]
OPENAI_API_KEY = "sk-..." # Set via wrangler secret
[[d1_databases]]
binding = "USER_DB"
database_name = "telegram-conversations"
database_id = "c285bb5b-888b-405d-b36f-475ae5aed20e"
[[queues.producers]]
queue = "provision-queue"
binding = "PROVISION_QUEUE"
[[queues.consumers]]
queue = "provision-queue"
max_batch_size = 1
max_retries = 3
dead_letter_queue = "provision-queue-dlq"
# Secrets (via wrangler secret put)
# OPENAI_API_KEY, LINODE_API_KEY, VULTR_API_KEY, PROVISION_API_KEY
```
## Testing
@@ -320,6 +405,26 @@ RESULT=$(curl -s -X POST https://cloud-orchestrator.kappa-d8e.workers.dev/api/re
REPORT_URL="https://cloud-orchestrator.kappa-d8e.workers.dev/api/recommend/report?data=$(echo $RESULT | base64 | tr -d '\n')&lang=ko"
# 3. Open in browser or fetch
echo $REPORT_URL
# === Provisioning API (requires X-API-Key header) ===
# Dry run - validate without creating server
curl -s -X POST https://cloud-orchestrator.kappa-d8e.workers.dev/api/provision -H "Content-Type: application/json" -H "X-API-Key: $PROVISION_API_KEY" -d '{"user_id":"821596605","pricing_id":26,"label":"test","dry_run":true}' | jq .
# Create server (async via Queue)
curl -s -X POST https://cloud-orchestrator.kappa-d8e.workers.dev/api/provision -H "Content-Type: application/json" -H "X-API-Key: $PROVISION_API_KEY" -d '{"user_id":"821596605","pricing_id":26,"label":"my-server"}' | jq .
# Get user balance
curl -s "https://cloud-orchestrator.kappa-d8e.workers.dev/api/provision/balance?user_id=821596605" -H "X-API-Key: $PROVISION_API_KEY" | jq .
# List user orders
curl -s "https://cloud-orchestrator.kappa-d8e.workers.dev/api/provision/orders?user_id=821596605" -H "X-API-Key: $PROVISION_API_KEY" | jq .
# Get specific order (includes root_password)
curl -s "https://cloud-orchestrator.kappa-d8e.workers.dev/api/provision/orders/15?user_id=821596605" -H "X-API-Key: $PROVISION_API_KEY" | jq .
# Terminate server
curl -s -X DELETE "https://cloud-orchestrator.kappa-d8e.workers.dev/api/provision/orders/15?user_id=821596605" -H "X-API-Key: $PROVISION_API_KEY" | jq .
```
## Recent Changes

70
schema-provisioning.sql Normal file
View File

@@ -0,0 +1,70 @@
-- Provisioning System Schema
-- Users, Payment Holds, Server Orders
-- 1. Users table with deposit balance
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY, -- UUID
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
balance_usd REAL NOT NULL DEFAULT 0.0 CHECK(balance_usd >= 0),
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'suspended', 'deleted')),
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_users_status ON users(status);
-- 2. Payment holds table (hold → capture/release)
CREATE TABLE IF NOT EXISTS payment_holds (
id TEXT PRIMARY KEY, -- UUID
user_id TEXT NOT NULL,
order_id TEXT NOT NULL, -- References server_orders.id
amount_usd REAL NOT NULL CHECK(amount_usd > 0),
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'captured', 'released')),
reason TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
resolved_at TEXT, -- When captured or released
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_payment_holds_user ON payment_holds(user_id);
CREATE INDEX IF NOT EXISTS idx_payment_holds_order ON payment_holds(order_id);
CREATE INDEX IF NOT EXISTS idx_payment_holds_status ON payment_holds(status);
-- 3. Server orders table
CREATE TABLE IF NOT EXISTS server_orders (
id TEXT PRIMARY KEY, -- UUID
user_id TEXT NOT NULL,
pricing_id INTEGER NOT NULL, -- References pricing.id (instance_type + region)
provider_name TEXT NOT NULL, -- linode, vultr, etc.
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN (
'pending', -- Order created, hold placed
'provisioning', -- API call in progress
'active', -- Server running
'failed', -- Provisioning failed
'deleted' -- Server terminated
)),
-- Provider response data
provider_instance_id TEXT, -- Linode/Vultr instance ID
server_ip TEXT, -- IPv4 address
server_ipv6 TEXT, -- IPv6 address
root_password TEXT, -- Encrypted/hashed
-- Cost tracking
monthly_cost_usd REAL NOT NULL,
-- Metadata
label TEXT, -- User-defined server label
os_image TEXT NOT NULL DEFAULT 'ubuntu_22_04',
error_message TEXT,
-- Timestamps
created_at TEXT NOT NULL DEFAULT (datetime('now')),
provisioned_at TEXT,
deleted_at TEXT,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (pricing_id) REFERENCES pricing(id)
);
CREATE INDEX IF NOT EXISTS idx_server_orders_user ON server_orders(user_id);
CREATE INDEX IF NOT EXISTS idx_server_orders_status ON server_orders(status);
CREATE INDEX IF NOT EXISTS idx_server_orders_provider ON server_orders(provider_name, provider_instance_id);
CREATE INDEX IF NOT EXISTS idx_server_orders_created ON server_orders(created_at);