diff --git a/src/index.ts b/src/index.ts index 59be435..2f3d31a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import { Env, EmailMessage, ProvisionMessage, MessageBatch } from './types'; import { sendMessage, setWebhook, getWebhookInfo } from './telegram'; import { handleWebhook } from './routes/webhook'; -import { handleApiRequest } from './routes/api'; +import { apiRouter } from './routes/api'; import { handleHealthCheck } from './routes/health'; import { parseBankSMS } from './services/bank-sms-parser'; import { matchPendingDeposit } from './services/deposit-matcher'; @@ -69,8 +69,8 @@ app.get('/webhook-info', async (c) => { return c.json(result); }); -// API routes -app.all('/api/*', (c) => handleApiRequest(c.req.raw, c.env, new URL(c.req.url))); +// API routes - use Hono router +app.route('/api', apiRouter); // Telegram Webhook app.post('/webhook', (c) => handleWebhook(c.req.raw, c.env)); diff --git a/src/routes/api.ts b/src/routes/api.ts index 969aa5a..6345d5a 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -1,4 +1,6 @@ import { z } from 'zod'; +import { Hono } from 'hono'; +import { cors } from 'hono/cors'; import { Env } from '../types'; import { sendMessage } from '../telegram'; import { @@ -762,18 +764,6 @@ async function handleContactForm(request: Request, env: Env): Promise } } -/** - * OPTIONS /api/contact - CORS preflight for contact API - * - * @param env - Environment bindings - * @returns Response with CORS headers - */ -async function handleContactPreflight(env: Env): Promise { - return new Response(null, { - headers: getCorsHeaders(env), - }); -} - /** * GET /api/metrics - Circuit Breaker 상태 조회 (관리자 전용) * @@ -837,7 +827,7 @@ async function handleMetrics(request: Request, env: Env): Promise { } /** - * API 엔드포인트 처리 (라우터) + * API Router (Hono) * * Manual Test: * 1. wrangler dev @@ -867,41 +857,47 @@ async function handleMetrics(request: Request, env: Env): Promise { * curl http://localhost:8787/api/metrics \ * -H "Authorization: Bearer your-webhook-secret" */ -export async function handleApiRequest(request: Request, env: Env, url: URL): Promise { - // Deposit API - 잔액 조회 (namecheap-api 전용) - if (url.pathname === '/api/deposit/balance' && request.method === 'GET') { - return handleDepositBalance(request, env, url); - } +const api = new Hono<{ Bindings: Env }>(); - // Deposit API - 잔액 차감 (namecheap-api 전용) - if (url.pathname === '/api/deposit/deduct' && request.method === 'POST') { - return handleDepositDeduct(request, env); - } +// CORS middleware for /contact endpoint +api.use('/contact', cors({ + origin: (origin, c) => { + const allowedOrigin = c.env.HOSTING_SITE_URL || 'https://hosting.anvil.it.com'; + return origin === allowedOrigin ? origin : null; + }, + allowMethods: ['POST', 'OPTIONS'], + allowHeaders: ['Content-Type'], +})); - // 테스트 API - 메시지 처리 후 응답 직접 반환 - if (url.pathname === '/api/test' && request.method === 'POST') { - return handleTestApi(request, env); - } +// GET /deposit/balance - 잔액 조회 (namecheap-api 전용) +api.get('/deposit/balance', async (c) => { + const url = new URL(c.req.url); + return await handleDepositBalance(c.req.raw, c.env, url); +}); - // Chat API - 인증된 채팅 API (프로덕션 활성화) - if (url.pathname === '/api/chat' && request.method === 'POST') { - return handleChatApi(request, env); - } +// POST /deposit/deduct - 잔액 차감 (namecheap-api 전용) +api.post('/deposit/deduct', async (c) => { + return await handleDepositDeduct(c.req.raw, c.env); +}); - // 문의 폼 API (웹사이트용) - if (url.pathname === '/api/contact' && request.method === 'POST') { - return handleContactForm(request, env); - } +// POST /test - 테스트 API (개발 환경 전용) +api.post('/test', async (c) => { + return await handleTestApi(c.req.raw, c.env); +}); - // CORS preflight for contact API - if (url.pathname === '/api/contact' && request.method === 'OPTIONS') { - return handleContactPreflight(env); - } +// POST /chat - 인증된 채팅 API (프로덕션) +api.post('/chat', async (c) => { + return await handleChatApi(c.req.raw, c.env); +}); - // Metrics API - Circuit Breaker 상태 조회 (관리자 전용) - if (url.pathname === '/api/metrics' && request.method === 'GET') { - return handleMetrics(request, env); - } +// POST /contact - 문의 폼 API (웹사이트용) +api.post('/contact', async (c) => { + return await handleContactForm(c.req.raw, c.env); +}); - return new Response('Not Found', { status: 404 }); -} +// GET /metrics - Circuit Breaker 상태 조회 (관리자 전용) +api.get('/metrics', async (c) => { + return await handleMetrics(c.req.raw, c.env); +}); + +export { api as apiRouter };