refactor: apply new utilities and constants across codebase

P0 fixes:
- KV Cache migration: security.ts now delegates to kv-cache.ts (74% code reduction)
- Environment validation: index.ts validates env on first request
- Type safety: optimistic-lock.ts removes `as any` with proper interface

P1 improvements:
- Constants applied to deposit-agent.ts (TRANSACTION_STATUS, TRANSACTION_TYPE)
- Constants applied to callback-handler.ts (CALLBACK_PREFIXES)
- Constants applied to domain-tool.ts (MESSAGE_MARKERS)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-29 10:49:31 +09:00
parent 699eed1530
commit f304c6a7d4
7 changed files with 87 additions and 90 deletions

View File

@@ -138,12 +138,8 @@ export async function validateWebhookRequest(
}
// Rate Limiting (Cloudflare KV 기반)
// NOTE: Future migration to KVCache abstraction layer (kv-cache.ts) planned
// Current implementation kept for backward compatibility
interface RateLimitData {
count: number;
resetAt: number;
}
// Migrated to use KVCache abstraction layer (kv-cache.ts)
import { createRateLimitCache, checkRateLimitWithCache } from './services/kv-cache';
export async function checkRateLimit(
kv: KVNamespace,
@@ -151,61 +147,12 @@ export async function checkRateLimit(
maxRequests: number = 30,
windowMs: number = 60000
): Promise<boolean> {
const key = `ratelimit:${userId}`;
const now = Date.now();
const logger = createLogger('rate-limit');
// Convert windowMs (milliseconds) to windowSeconds (seconds)
const windowSeconds = Math.ceil(windowMs / 1000);
try {
// KV에서 기존 데이터 조회
const dataStr = await kv.get(key);
const data: RateLimitData | null = dataStr ? JSON.parse(dataStr) : null;
// Create KVCache instance with 'rate:' prefix
const cache = createRateLimitCache(kv);
// 윈도우 만료 또는 첫 요청
if (!data || now > data.resetAt) {
const newData: RateLimitData = {
count: 1,
resetAt: now + windowMs,
};
await kv.put(key, JSON.stringify(newData), {
expirationTtl: Math.ceil(windowMs / 1000), // 초 단위
});
logger.info('Rate limit 윈도우 시작', {
userId,
resetAt: new Date(newData.resetAt).toISOString(),
maxRequests,
});
return true;
}
// Rate limit 초과
if (data.count >= maxRequests) {
const resetInSeconds = Math.ceil((data.resetAt - now) / 1000);
logger.warn('Rate limit 초과', {
userId,
currentCount: data.count,
maxRequests,
resetInSeconds,
resetAt: new Date(data.resetAt).toISOString(),
});
return false;
}
// 카운트 증가
const updatedData: RateLimitData = {
count: data.count + 1,
resetAt: data.resetAt,
};
const remainingTtl = Math.ceil((data.resetAt - now) / 1000);
await kv.put(key, JSON.stringify(updatedData), {
expirationTtl: Math.max(remainingTtl, 1), // 최소 1초
});
return true;
} catch (error) {
// KV 오류 시 요청 허용 (fail-open)
// Rate limiting은 abuse 방지 목적이므로 가용성 우선
// 심각한 abuse는 Cloudflare WAF/Firewall Rules로 별도 대응
logger.warn('KV 오류 - 요청 허용 (fail-open)', { userId, error: (error as Error).message });
return true;
}
// Delegate to unified KV cache implementation
return checkRateLimitWithCache(cache, userId, maxRequests, windowSeconds);
}