import { serve } from '@hono/node-server' import { logger } from './lib/logger.js' import { app } from './app.js' import { runMigrations } from './db/startup.js' import { seedMockBots, seedClassicBots } from './engine/mock.js' import { startBackgroundFights } from './engine/background.js' import { getActiveFighterCount } from './engine/orchestrator.js' import { clearAllPending } from './engine/human-responses.js' import { seedDevTournament } from './engine/dev-seed.js' // Production env validation — warn but don't crash (wallet features degrade gracefully) if (process.env.NODE_ENV === 'production') { const walletVars = ['BOTFIGHTS_NWC_URL', 'BOTFIGHTS_WALLET_ENCRYPTION_KEY'] const missing = walletVars.filter(k => !process.env[k]) if (missing.length > 0) { logger.warn('env', `Missing env vars: ${missing.join(', ')} — wallet/payment features disabled`) } if (!process.env.CORS_ORIGIN || process.env.CORS_ORIGIN === '*') { logger.warn('env', 'CORS_ORIGIN not set — allowing all origins') } } // Run migrations and seed mock bots before starting the server runMigrations() await seedMockBots() await seedClassicBots() // Dev mode: seed tournament + betting data for testing if (process.env.NODE_ENV !== 'production') { await seedDevTournament() } const port = Number(process.env.PORT) || 9100 serve({ fetch: app.fetch, port }, () => { logger.info('server', `listening on http://localhost:${port}`) // Start background fight loop so the site always has fresh activity startBackgroundFights() }) // Graceful shutdown — drain in-flight fights before exiting const shutdown = async (signal: string) => { logger.info('server', `${signal} received, shutting down...`) const active = getActiveFighterCount() if (active > 0) { logger.info('server', `${active} fighters still active, waiting 10s...`) } await new Promise(r => setTimeout(r, active > 0 ? 10_000 : 500)) clearAllPending() logger.info('server', 'shutdown complete') process.exit(0) } process.on('SIGTERM', () => shutdown('SIGTERM')) process.on('SIGINT', () => shutdown('SIGINT'))