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:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user