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:
kappa
2026-01-29 09:58:15 +09:00
parent 86af187aa1
commit 3cfcb06f27
7 changed files with 104 additions and 61 deletions

View File

@@ -1,5 +1,8 @@
import { metrics } from './metrics';
import { notifyAdmin, NotificationOptions } from '../services/notification';
import { createLogger } from './logger';
const logger = createLogger('circuit-breaker');
/**
* Circuit Breaker pattern implementation
@@ -99,7 +102,7 @@ export class CircuitBreaker {
this.serviceName = options?.serviceName ?? 'unknown';
this.notification = options?.notification;
console.log('[CircuitBreaker] Initialized', {
logger.info('Initialized', {
serviceName: this.serviceName,
failureThreshold: this.failureThreshold,
resetTimeoutMs: this.resetTimeoutMs,
@@ -146,7 +149,7 @@ export class CircuitBreaker {
* Manually reset the circuit to closed state
*/
reset(): void {
console.log('[CircuitBreaker] Manual reset');
logger.info('Manual reset', { service: this.serviceName });
this.state = CircuitState.CLOSED;
this.failures = [];
this.openedAt = null;
@@ -178,7 +181,10 @@ export class CircuitBreaker {
const elapsed = now - this.openedAt;
if (elapsed >= this.resetTimeoutMs) {
console.log('[CircuitBreaker] Reset timeout reached, transitioning to HALF_OPEN');
logger.info('Reset timeout reached, transitioning to HALF_OPEN', {
service: this.serviceName,
elapsedMs: elapsed
});
this.state = CircuitState.HALF_OPEN;
// 상태 메트릭 기록 (HALF_OPEN)
@@ -194,7 +200,9 @@ export class CircuitBreaker {
this.successCount++;
if (this.state === CircuitState.HALF_OPEN) {
console.log('[CircuitBreaker] Half-open test succeeded, closing circuit');
logger.info('Half-open test succeeded, closing circuit', {
service: this.serviceName
});
this.state = CircuitState.CLOSED;
this.failures = [];
this.openedAt = null;
@@ -218,7 +226,10 @@ export class CircuitBreaker {
// If in half-open state, one failure reopens the circuit
if (this.state === CircuitState.HALF_OPEN) {
console.log('[CircuitBreaker] Half-open test failed, reopening circuit');
logger.warn('Half-open test failed, reopening circuit', {
service: this.serviceName,
error: error.message
});
this.state = CircuitState.OPEN;
this.openedAt = now;
@@ -246,9 +257,11 @@ export class CircuitBreaker {
// Check if we should open the circuit
if (this.state === CircuitState.CLOSED) {
if (this.failures.length >= this.failureThreshold) {
console.log(
`[CircuitBreaker] Failure threshold (${this.failureThreshold}) exceeded, opening circuit`
);
logger.warn('Failure threshold exceeded, opening circuit', {
service: this.serviceName,
failureThreshold: this.failureThreshold,
currentFailures: this.failures.length
});
this.state = CircuitState.OPEN;
this.openedAt = now;
@@ -291,7 +304,9 @@ export class CircuitBreaker {
'Circuit breaker is open - service unavailable',
this.state
);
console.log('[CircuitBreaker] Request blocked - circuit is OPEN');
logger.warn('Request blocked - circuit is OPEN', {
service: this.serviceName
});
throw error;
}
@@ -315,10 +330,11 @@ export class CircuitBreaker {
this.onFailure(err);
// Log failure
console.error(
`[CircuitBreaker] Operation failed (${this.failures.length}/${this.failureThreshold} failures):`,
err.message
);
logger.error('Operation failed', err, {
service: this.serviceName,
failures: this.failures.length,
threshold: this.failureThreshold
});
// Re-throw the original error
throw err;

View File

@@ -12,6 +12,9 @@
import { metrics } from './metrics';
import { notifyAdmin, NotificationOptions } from '../services/notification';
import { createLogger } from './logger';
const logger = createLogger('retry');
/**
* Configuration options for retry behavior
@@ -123,7 +126,11 @@ export async function retryWithBackoff<T>(
// Log success if this was a retry
if (attempt > 0) {
console.log(`[Retry] Success on attempt ${attempt + 1}/${maxRetries + 1}`);
logger.info('Success on retry', {
service: serviceName,
attempt: attempt + 1,
totalAttempts: maxRetries + 1
});
}
return result;
@@ -132,10 +139,10 @@ export async function retryWithBackoff<T>(
// If this was the last attempt, throw RetryError
if (attempt === maxRetries) {
console.error(
`[Retry] All ${maxRetries + 1} attempts failed. Last error:`,
lastError.message
);
logger.error('All attempts failed', lastError, {
service: serviceName,
totalAttempts: maxRetries + 1
});
// Send admin notification if configured
if (notification) {
@@ -176,10 +183,13 @@ export async function retryWithBackoff<T>(
jitter
);
console.log(
`[Retry] Attempt ${attempt + 1}/${maxRetries + 1} failed. Retrying in ${delay}ms...`,
lastError.message
);
logger.warn('Attempt failed, retrying', {
service: serviceName,
attempt: attempt + 1,
totalAttempts: maxRetries + 1,
delayMs: delay,
error: lastError.message
});
// Wait before next retry
await sleep(delay);