- 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>
203 lines
8.0 KiB
Markdown
203 lines
8.0 KiB
Markdown
# 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)
|