- Split monolithic index.ts (2370 lines) into modular structure: - src/handlers/ for route handlers - src/utils.ts for shared utilities - src/config.ts for configuration - src/types.ts for TypeScript definitions - Add DB workload multiplier for smarter database resource calculation: - Heavy (analytics, logs): 0.3x multiplier - Medium-heavy (e-commerce, transactional): 0.5x - Medium (API, SaaS): 0.7x - Light (blog, portfolio): 1.0x - Fix tech_specs with realistic vcpu_per_users values (150+ technologies) - Fix "blog" matching "log" regex bug - Update documentation to reflect new architecture Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
91 lines
2.5 KiB
TypeScript
91 lines
2.5 KiB
TypeScript
/**
|
|
* Cloudflare Worker - Server Recommendation System Entry Point
|
|
*
|
|
* AI-powered server recommendation service using Workers AI, D1, and KV.
|
|
*/
|
|
|
|
import type { Env } from './types';
|
|
import { getAllowedOrigin, checkRateLimit, jsonResponse } from './utils';
|
|
import { handleHealth } from './handlers/health';
|
|
import { handleGetServers } from './handlers/servers';
|
|
import { handleRecommend } from './handlers/recommend';
|
|
|
|
/**
|
|
* Main request handler
|
|
*/
|
|
export default {
|
|
async fetch(request: Request, env: Env): Promise<Response> {
|
|
const requestId = crypto.randomUUID();
|
|
|
|
try {
|
|
const url = new URL(request.url);
|
|
const path = url.pathname;
|
|
|
|
// Rate limiting (except for health checks)
|
|
if (path !== '/api/health') {
|
|
const clientIP = request.headers.get('CF-Connecting-IP') || 'unknown';
|
|
const rateCheck = await checkRateLimit(clientIP, env);
|
|
|
|
if (!rateCheck.allowed) {
|
|
const origin = getAllowedOrigin(request);
|
|
return jsonResponse(
|
|
{ error: 'Too many requests', request_id: rateCheck.requestId },
|
|
429,
|
|
{
|
|
'Access-Control-Allow-Origin': origin,
|
|
'Vary': 'Origin',
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
// CORS headers for all responses
|
|
const origin = getAllowedOrigin(request);
|
|
const corsHeaders = {
|
|
'Access-Control-Allow-Origin': origin,
|
|
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
'Access-Control-Allow-Headers': 'Content-Type',
|
|
'Vary': 'Origin',
|
|
};
|
|
|
|
// Handle preflight requests
|
|
if (request.method === 'OPTIONS') {
|
|
return new Response(null, { headers: corsHeaders });
|
|
}
|
|
|
|
// Route handling
|
|
if (path === '/api/health') {
|
|
return handleHealth(corsHeaders);
|
|
}
|
|
|
|
if (path === '/api/servers' && request.method === 'GET') {
|
|
return handleGetServers(request, env, corsHeaders);
|
|
}
|
|
|
|
if (path === '/api/recommend' && request.method === 'POST') {
|
|
return handleRecommend(request, env, corsHeaders);
|
|
}
|
|
|
|
return jsonResponse(
|
|
{ error: 'Not found', request_id: requestId },
|
|
404,
|
|
corsHeaders
|
|
);
|
|
} catch (error) {
|
|
console.error('[Worker] Unhandled error:', error);
|
|
const origin = getAllowedOrigin(request);
|
|
return jsonResponse(
|
|
{
|
|
error: 'Internal server error',
|
|
request_id: requestId,
|
|
},
|
|
500,
|
|
{
|
|
'Access-Control-Allow-Origin': origin,
|
|
'Vary': 'Origin',
|
|
}
|
|
);
|
|
}
|
|
},
|
|
};
|