Files
cloud-orchestrator/CLAUDE.md
kappa 67d86be5d5 feat: add flexible region matching to servers API
- Add shared buildFlexibleRegionConditions() in utils.ts
- Add COUNTRY_NAME_TO_REGIONS mapping for country/city expansion
- Update servers.ts to use flexible region matching (korea, tokyo, japan, etc.)
- Update recommend.ts to use shared function (remove duplicate code)
- Fix servers GROUP BY to show all regions (it.id, r.id)
- Update CLAUDE.md with single-line curl examples

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 19:36:34 +09:00

203 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Cloudflare Worker-based AI server recommendation service. Uses OpenAI GPT-4o-mini (via AI Gateway), D1 database, KV cache, and VPS benchmark data to recommend cost-effective servers based on natural language requirements.
**Production URL**: `https://server-recommend.kappa-d8e.workers.dev`
## Commands
```bash
# Development
npm run dev # Start local development server (wrangler dev)
npm run deploy # Deploy to Cloudflare Workers
npm run typecheck # TypeScript type checking
# Database operations (D1)
npx wrangler d1 execute cloud-instances-db --file=schema.sql # Apply schema
npx wrangler d1 execute cloud-instances-db --file=seed.sql # Seed data
npx wrangler d1 execute cloud-instances-db --file=fix-tech-specs.sql # Update tech specs
npx wrangler d1 execute cloud-instances-db --command="SELECT ..." # Ad-hoc queries
# View logs
npx wrangler tail
```
## Architecture
```
src/
├── index.ts # Main router, CORS, request handling
├── config.ts # Configuration constants
├── types.ts # TypeScript type definitions
├── utils.ts # Utilities (bandwidth, response, AI, benchmarks, candidates, techSpecs)
└── handlers/
├── health.ts # GET /api/health
├── servers.ts # GET /api/servers - List servers with filtering
└── recommend.ts # POST /api/recommend - AI-powered recommendations
```
### Key Data Flow
1. User sends request (`tech_stack`, `expected_users`, `use_case`, `region_preference`)
2. Tech specs calculation with **DB workload multiplier** based on use_case
3. Candidate filtering with **flexible region matching**
4. VPS benchmarks retrieval (Geekbench 6), **prioritizing same provider**
5. AI analysis returns 3 tiers: Budget, Balanced, Premium
6. Results cached in KV (5 min TTL, empty results not cached)
### D1 Database Tables (cloud-instances-db)
- `providers` - Cloud providers (50+)
- `instance_types` - Server specifications
- `pricing` - Regional pricing
- `regions` - Geographic regions
- `tech_specs` - Resource requirements per technology (vcpu_per_users, min_memory_mb)
- `vps_benchmarks` - Geekbench 6 benchmark data (269 records)
- `benchmark_results` / `benchmark_types` / `processors` - Phoronix benchmark data
## Key Implementation Details
### DB Workload Multiplier (`recommend.ts`)
Database resource requirements vary by workload type, not just user count:
| Workload Type | Multiplier | Example Use Cases |
|---------------|------------|-------------------|
| Heavy | 0.3x | analytics, log server, reporting, dashboard |
| Medium-Heavy | 0.5x | e-commerce, ERP, CRM, community forum |
| Medium | 0.7x | API, SaaS, app backend |
| Light | 1.0x | blog, portfolio, documentation, wiki |
**Example**: PostgreSQL (vcpu_per_users: 200) with 1000 users
- Analytics dashboard: 1000 / (200 × 0.3) = 17 vCPU
- E-commerce: 1000 / (200 × 0.5) = 10 vCPU
- Personal blog: 1000 / (200 × 1.0) = 5 vCPU
### Bandwidth Estimation (`bandwidth.ts`)
Estimates monthly bandwidth based on use_case patterns:
| Pattern | Page Size | Pages/Day | Active Ratio |
|---------|-----------|-----------|--------------|
| E-commerce | 2.5MB | 20 | 40% |
| Streaming | 50MB | 5 | 20% |
| Analytics | 0.7MB | 30 | 50% |
| Blog/Content | 1.5MB | 4 | 30% |
Heavy bandwidth (>1TB/month) prefers Linode for included bandwidth.
### Flexible Region Matching (`utils.ts`)
Both `/api/recommend` and `/api/servers` use shared `buildFlexibleRegionConditions()`:
```sql
LOWER(r.region_code) = ? OR
LOWER(r.region_code) LIKE ? OR
LOWER(r.region_name) LIKE ? OR
LOWER(r.country_code) = ?
```
Valid inputs: `"korea"`, `"KR"`, `"seoul"`, `"tokyo"`, `"japan"`, `"ap-northeast-2"`, `"icn"`
Country names are auto-expanded via `COUNTRY_NAME_TO_REGIONS` mapping.
### AI Prompt Strategy (`recommend.ts`)
- Uses OpenAI GPT-4o-mini via Cloudflare AI Gateway (bypasses regional restrictions)
- Server list format: `[server_id=XXXX] Provider Name...` for accurate ID extraction
- Scoring: Cost efficiency (40%) + Capacity fit (30%) + Scalability (30%)
- Capacity response in Korean for Korean users
- **Prompt injection protection**: User inputs sanitized via `sanitizeForAIPrompt()`
- **Token optimization**: Candidates limited to top 15 by price
### Security Features
- **Input validation**: Comprehensive checks with length limits (tech_stack ≤20, use_case ≤500 chars)
- **Rate limiting**: 60 req/min per IP with in-memory fallback when KV unavailable
- **SQL injection prevention**: All queries use parameterized statements
- **Security headers**: CSP, HSTS, X-Frame-Options, X-Content-Type-Options
- **API key protection**: Keys never logged, sanitized from error messages
### Configuration (`config.ts`)
Centralized limits and constants:
```typescript
LIMITS = {
MAX_REQUEST_BODY_BYTES: 10240, // 10KB
CACHE_TTL_SECONDS: 300, // 5 minutes
RATE_LIMIT_MAX_REQUESTS: 60, // per minute
MAX_AI_CANDIDATES: 15, // reduce API cost
MAX_TECH_STACK: 20,
MAX_USE_CASE_LENGTH: 500,
}
```
## Bindings (wrangler.toml)
```toml
[[kv_namespaces]]
binding = "CACHE"
id = "c68cdb477022424cbe4594f491390c8a"
[[d1_databases]]
binding = "DB"
database_name = "cloud-instances-db"
database_id = "bbcb472d-b25e-4e48-b6ea-112f9fffb4a8"
[vars]
OPENAI_API_KEY = "sk-..." # Set via wrangler secret
```
## Testing
**Note**: Use single-line curl commands. Backslash line continuation (`\`) may not work in some environments.
```bash
# Health check
curl -s https://server-recommend.kappa-d8e.workers.dev/api/health | jq .
# Recommendation - nodejs/redis real-time chat (Japan)
curl -s -X POST https://server-recommend.kappa-d8e.workers.dev/api/recommend -H "Content-Type: application/json" -d '{"tech_stack":["nodejs","redis"],"expected_users":1000,"use_case":"real-time chat","region_preference":["japan"]}' | jq .
# Recommendation - php/mysql community forum (Korea)
curl -s -X POST https://server-recommend.kappa-d8e.workers.dev/api/recommend -H "Content-Type: application/json" -d '{"tech_stack":["php","mysql"],"expected_users":800,"use_case":"community forum","region_preference":["korea"]}' | jq .
# Recommendation - analytics dashboard (heavier DB workload)
curl -s -X POST https://server-recommend.kappa-d8e.workers.dev/api/recommend -H "Content-Type: application/json" -d '{"tech_stack":["postgresql"],"expected_users":500,"use_case":"analytics dashboard","region_preference":["japan"]}' | jq .
# Server list with filters (supports flexible region: korea, seoul, tokyo, etc.)
curl -s "https://server-recommend.kappa-d8e.workers.dev/api/servers?region=korea&minCpu=4" | jq .
```
## Recent Changes
### Architecture
- **Modular architecture**: Split from single 2370-line file into organized modules
- **Centralized config**: All magic numbers moved to `LIMITS` in config.ts
- **Type safety**: `parseAIResponse(unknown)` with proper type guards
### Features
- **DB workload multiplier**: Database resource calculation based on use_case
- **Bandwidth estimation**: Automatic bandwidth category detection for provider filtering
- **Tech specs update**: Realistic vcpu_per_users values for 150+ technologies
### Security
- **Prompt injection protection**: `sanitizeForAIPrompt()` filters malicious patterns
- **Rate limiting fallback**: In-memory Map when KV unavailable
- **Input sanitization**: All user inputs validated and length-limited
### Performance
- **O(1) VPS lookup**: Map-based benchmark matching (was O(n×m))
- **AI token optimization**: Candidates limited to 15 (was 50)
- **KV caching**: 5-minute TTL, empty results not cached
- **Parallel queries**: Promise.all for independent DB operations
### Code Quality
- **Dead code removed**: Unused `queryVPSBenchmarks` function deleted
- **DRY**: `DEFAULT_REGION_FILTER_SQL` and `buildFlexibleRegionConditions()` shared between handlers
- **Name-based filtering**: Provider queries use names, not hardcoded IDs
- **Flexible region matching**: Both endpoints support country/city/code inputs (korea, seoul, icn)