refactor: migrate API routes to Hono sub-router

- Create Hono router in api.ts
- Convert 6 API endpoints to Hono format:
  - GET /api/deposit/balance
  - POST /api/deposit/deduct
  - POST /api/test
  - POST /api/chat
  - POST /api/contact
  - GET /api/metrics
- Use Hono CORS middleware for /contact
- Remove manual handleApiRequest and handleContactPreflight
- Integrate with main app via app.route('/api', apiRouter)

Benefits:
- Cleaner declarative routing (44 insertions, 48 deletions)
- Built-in CORS middleware
- Better code organization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-29 09:48:07 +09:00
parent 2756dbe804
commit 86af187aa1
2 changed files with 43 additions and 47 deletions

View File

@@ -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));

View File

@@ -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<Response>
}
}
/**
* OPTIONS /api/contact - CORS preflight for contact API
*
* @param env - Environment bindings
* @returns Response with CORS headers
*/
async function handleContactPreflight(env: Env): Promise<Response> {
return new Response(null, {
headers: getCorsHeaders(env),
});
}
/**
* GET /api/metrics - Circuit Breaker 상태 조회 (관리자 전용)
*
@@ -837,7 +827,7 @@ async function handleMetrics(request: Request, env: Env): Promise<Response> {
}
/**
* API 엔드포인트 처리 (라우터)
* API Router (Hono)
*
* Manual Test:
* 1. wrangler dev
@@ -867,41 +857,47 @@ async function handleMetrics(request: Request, env: Env): Promise<Response> {
* curl http://localhost:8787/api/metrics \
* -H "Authorization: Bearer your-webhook-secret"
*/
export async function handleApiRequest(request: Request, env: Env, url: URL): Promise<Response> {
// 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 };