refactor: replace console.log/error with structured logger across server

Migrate all server modules to use the centralized logger (lib/logger.ts)
instead of raw console calls. Lint warnings reduced from 74 to 25.
Remaining warnings are only no-floating-promises in game engine code.

Files updated: orchestrator.ts, ranked-queue.ts, human-responses.ts,
payments.ts, fight-loop.ts, app.ts, routes/payments.ts
Files suppressed: logger.ts, fight-loop-cli.ts, migrate.ts (legitimate console use)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-09 09:06:36 +00:00
co-authored by Claude Opus 4.6
parent 1f532681df
commit 4cc18048e8
10 changed files with 70 additions and 62 deletions
+19 -18
View File
@@ -1,5 +1,6 @@
import { z } from 'zod'
import { nanoid } from 'nanoid'
import { logger } from '../lib/logger.js'
import { toError } from '../lib/utils.js'
import { db, schema, sqlite } from '../db/index.js'
import { eq, sql } from 'drizzle-orm'
@@ -156,11 +157,11 @@ async function callWebhook(
})
const start = Date.now()
console.log(`[webhook] POST ${url} round=${roundNumber} type=${challenge.type}`)
logger.info('webhook', `POST ${url} round=${roundNumber} type=${challenge.type}`)
// SSRF check
if (!isAllowedWebhookUrl(url)) {
console.log(`[webhook] ${url} BLOCKED (private/internal URL)`)
logger.warn('webhook', `${url} BLOCKED (private/internal URL)`)
return { answer: null, timeMs: 0, timedOut: false, error: true }
}
@@ -179,7 +180,7 @@ async function callWebhook(
const elapsed = Date.now() - start
if (!res.ok) {
console.log(`[webhook] ${url} returned ${res.status} in ${elapsed}ms`)
logger.warn('webhook', `${url} returned ${res.status} in ${elapsed}ms`)
return { answer: null, timeMs: elapsed, timedOut: false, error: true }
}
@@ -187,7 +188,7 @@ async function callWebhook(
try {
text = await readLimitedBody(res, MAX_RESPONSE_BYTES)
} catch {
console.log(`[webhook] ${url} response too large (>${MAX_RESPONSE_BYTES} bytes)`)
logger.warn('webhook', `${url} response too large (>${MAX_RESPONSE_BYTES} bytes)`)
return { answer: null, timeMs: elapsed, timedOut: false, error: true }
}
@@ -195,13 +196,13 @@ async function callWebhook(
try {
parsed = JSON.parse(text)
} catch {
console.log(`[webhook] ${url} returned non-JSON in ${elapsed}ms: ${text.slice(0, 200)}`)
logger.warn('webhook', `${url} returned non-JSON in ${elapsed}ms: ${text.slice(0, 200)}`)
return { answer: null, timeMs: elapsed, timedOut: false, error: true }
}
const data = webhookResponseSchema.safeParse(parsed)
if (!data.success) {
console.log(`[webhook] ${url} invalid response shape in ${elapsed}ms: ${data.error.message}`)
logger.warn('webhook', `${url} invalid response shape in ${elapsed}ms: ${data.error.message}`)
return { answer: null, timeMs: elapsed, timedOut: false, error: true }
}
@@ -209,7 +210,7 @@ async function callWebhook(
const answer = data.data.answer ? data.data.answer.slice(0, MAX_ANSWER_LENGTH) : null
const trashTalk = data.data.trash_talk ? data.data.trash_talk.slice(0, MAX_TRASH_TALK_LENGTH) : undefined
console.log(`[webhook] ${url} OK in ${elapsed}ms answer=${(answer || '').slice(0, 80)}`)
logger.info('webhook', `${url} OK in ${elapsed}ms answer=${(answer || '').slice(0, 80)}`)
return {
answer,
trashTalk,
@@ -220,7 +221,7 @@ async function callWebhook(
} catch (err: unknown) {
const elapsed = Date.now() - start
const isAbort = err instanceof Error && err.name === 'AbortError'
console.log(`[webhook] ${url} ${isAbort ? "TIMEOUT" : "ERROR"} in ${elapsed}ms: ${toError(err).message}`)
logger.warn('webhook', `${url} ${isAbort ? "TIMEOUT" : "ERROR"} in ${elapsed}ms: ${toError(err).message}`)
return {
answer: null,
timeMs: elapsed,
@@ -243,7 +244,7 @@ async function getBotResponse(
arena: Arena,
): Promise<WebhookResponse> {
if (isHumanPlayer(bot.webhookUrl)) {
console.log(`[fight] ${bot.name} is human player, waiting for browser response`)
logger.info('fight', `${bot.name} is human player, waiting for browser response`)
emit(fightId, 'human_challenge', {
botId: bot.id,
round: roundNumber,
@@ -260,7 +261,7 @@ async function getBotResponse(
}
if (isClassicBot(bot.webhookUrl)) {
console.log(`[fight] ${bot.name} is classic bot, generating response`)
logger.info('fight', `${bot.name} is classic bot, generating response`)
const classic = generateClassicBotResponse(challenge, bot.name)
return {
answer: classic.answer || null,
@@ -272,7 +273,7 @@ async function getBotResponse(
}
if (isMockBot(bot.webhookUrl)) {
console.log(`[fight] ${bot.name} is mock bot, generating response`)
logger.info('fight', `${bot.name} is mock bot, generating response`)
const mock = generateMockBotResponse(challenge, bot.name)
return {
answer: mock.answer || null,
@@ -282,7 +283,7 @@ async function getBotResponse(
error: mock.error,
}
}
console.log(`[fight] ${bot.name} has real webhook: ${bot.webhookUrl}`)
logger.info('fight', `${bot.name} has real webhook: ${bot.webhookUrl}`)
return callWebhook(bot.webhookUrl, challenge, roundNumber, fightId, opponent, arena)
}
@@ -339,7 +340,7 @@ async function trackWebhookResult(botId: string, webhookUrl: string, succeeded:
.from(schema.bots).where(eq(schema.bots.id, botId)).limit(1)
if (bot[0] && bot[0].consecutiveErrors >= 5) {
await db.update(schema.bots).set({ isActive: false }).where(eq(schema.bots.id, botId))
console.log(`[fight] bot ${botId} auto-deactivated after 5 consecutive webhook errors`)
logger.warn('fight', `bot ${botId} auto-deactivated after 5 consecutive webhook errors`)
}
}
}
@@ -568,7 +569,7 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
winnerEloChange, loserEloChange,
isPerfect: !!isPerfect, isKO, isUpset,
totalRounds: lastRound, arena: arena.name,
}).catch(err => console.warn('[nostr] publish failed:', err))
}).catch(err => logger.warn('nostr', `publish failed: ${err}`))
}
// Settle bets
@@ -583,7 +584,7 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
}).where(eq(schema.bets.id, s.betId)).run()
}
} catch (err) {
console.error(`[betting] settlement failed for fight ${fightId}:`, err)
logger.error('betting', `settlement failed for fight ${fightId}: ${err}`)
}
// Ranked fight payout
@@ -596,7 +597,7 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
if (humanBotId) {
payWinner(fightId, humanBotId).catch(err => {
console.error(`[payments] payout failed for fight ${fightId}:`, err)
logger.error('payments', `payout failed for fight ${fightId}: ${err}`)
})
} else if (!winnerId) {
// Draw — refund both entry fees
@@ -604,7 +605,7 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
.where(sql`${schema.payments.fightId} = ${fightId} AND ${schema.payments.direction} = 'in' AND ${schema.payments.status} = 'confirmed'`)
for (const payment of entryPayments) {
refundEntry(payment.id).catch(err => {
console.error(`[payments] draw refund failed for ${payment.id}:`, err)
logger.error('payments', `draw refund failed for ${payment.id}: ${err}`)
})
}
}
@@ -651,7 +652,7 @@ export async function runFightAsync(botAId: string, botBId: string, mode: 'free'
executeFightRounds(fightId, botA, botB, arena, mode)
.catch(err => {
console.error(`[botfights] fight ${fightId} error:`, err)
logger.error('fight', `fight ${fightId} error: ${err}`)
// Mark fight as cancelled so it doesn't stay 'live' forever
db.update(schema.fights).set({
status: 'cancelled',