refactor: centralize auth middleware and standardize logging
1. API Key Middleware (api.ts) - Create apiKeyAuth Hono middleware with timing-safe comparison - Apply to /deposit/balance and /deposit/deduct routes - Remove duplicate requireApiKey() calls from handlers - Reduce ~15 lines of duplicated code 2. Logger Standardization (6 files, 27 replacements) - webhook.ts: 2 console.error → logger - message-handler.ts: 7 console → logger - deposit-matcher.ts: 4 console → logger - n8n-service.ts: 3 console.error → logger - circuit-breaker.ts: 8 console → logger - retry.ts: 3 console → logger Benefits: - Single point of auth change - Structured logging with context - Better observability in production Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { z } from 'zod';
|
||||
import { Hono } from 'hono';
|
||||
import { cors } from 'hono/cors';
|
||||
import { createMiddleware } from 'hono/factory';
|
||||
import { Env } from '../types';
|
||||
import { sendMessage } from '../telegram';
|
||||
import {
|
||||
@@ -44,17 +45,20 @@ const ChatApiBodySchema = z.object({
|
||||
});
|
||||
|
||||
/**
|
||||
* API Key 인증 검증 (Timing-safe comparison으로 타이밍 공격 방지)
|
||||
* @returns 인증 실패 시 Response, 성공 시 null
|
||||
* API Key 인증 미들웨어 (Timing-safe comparison으로 타이밍 공격 방지)
|
||||
* X-API-Key 헤더로 DEPOSIT_API_SECRET 검증
|
||||
*/
|
||||
function requireApiKey(request: Request, env: Env): Response | null {
|
||||
const apiSecret = env.DEPOSIT_API_SECRET;
|
||||
const authHeader = request.headers.get('X-API-Key');
|
||||
const apiKeyAuth = createMiddleware<{ Bindings: Env }>(async (c, next) => {
|
||||
const apiSecret = c.env.DEPOSIT_API_SECRET;
|
||||
const authHeader = c.req.header('X-API-Key');
|
||||
|
||||
if (!apiSecret || !timingSafeEqual(authHeader, apiSecret)) {
|
||||
return Response.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
logger.warn('API Key 인증 실패', { hasApiKey: !!authHeader });
|
||||
return c.json({ error: 'Unauthorized' }, 401);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return await next();
|
||||
});
|
||||
|
||||
/**
|
||||
* CORS 헤더 생성
|
||||
@@ -101,17 +105,12 @@ async function getOrCreateUser(
|
||||
/**
|
||||
* GET /api/deposit/balance - 잔액 조회 (namecheap-api 전용)
|
||||
*
|
||||
* @param request - HTTP Request
|
||||
* @param env - Environment bindings
|
||||
* @param url - Parsed URL
|
||||
* @returns JSON response with balance
|
||||
*/
|
||||
async function handleDepositBalance(request: Request, env: Env, url: URL): Promise<Response> {
|
||||
async function handleDepositBalance(env: Env, url: URL): Promise<Response> {
|
||||
try {
|
||||
// API Key 인증
|
||||
const authError = requireApiKey(request, env);
|
||||
if (authError) return authError;
|
||||
|
||||
const telegramId = url.searchParams.get('telegram_id');
|
||||
if (!telegramId) {
|
||||
return Response.json({ error: 'telegram_id required' }, { status: 400 });
|
||||
@@ -150,10 +149,6 @@ async function handleDepositBalance(request: Request, env: Env, url: URL): Promi
|
||||
*/
|
||||
async function handleDepositDeduct(request: Request, env: Env): Promise<Response> {
|
||||
try {
|
||||
// API Key 인증
|
||||
const authError = requireApiKey(request, env);
|
||||
if (authError) return authError;
|
||||
|
||||
// JSON 파싱 (별도 에러 핸들링)
|
||||
let jsonData: unknown;
|
||||
try {
|
||||
@@ -869,14 +864,14 @@ api.use('/contact', cors({
|
||||
allowHeaders: ['Content-Type'],
|
||||
}));
|
||||
|
||||
// GET /deposit/balance - 잔액 조회 (namecheap-api 전용)
|
||||
api.get('/deposit/balance', async (c) => {
|
||||
// GET /deposit/balance - 잔액 조회 (namecheap-api 전용, API Key 필요)
|
||||
api.get('/deposit/balance', apiKeyAuth, async (c) => {
|
||||
const url = new URL(c.req.url);
|
||||
return await handleDepositBalance(c.req.raw, c.env, url);
|
||||
return await handleDepositBalance(c.env, url);
|
||||
});
|
||||
|
||||
// POST /deposit/deduct - 잔액 차감 (namecheap-api 전용)
|
||||
api.post('/deposit/deduct', async (c) => {
|
||||
// POST /deposit/deduct - 잔액 차감 (namecheap-api 전용, API Key 필요)
|
||||
api.post('/deposit/deduct', apiKeyAuth, async (c) => {
|
||||
return await handleDepositDeduct(c.req.raw, c.env);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user