Files
botfights/server/src/index.ts
T
DorianandClaude Opus 4.6 5ee6b6c88c fix: fight restart bug, mobile TTS, character sizing, bitcoin symbols
- Fix fight replay showing empty scene: validate rounds before playing,
  retry loading fight data up to 5 times when rounds are missing
- Add COOP/COEP headers to production server for SharedArrayBuffer
  (required by Kokoro TTS WASM threading on mobile)
- Add blob: to scriptSrc/workerSrc CSP for Web Worker support
- Fix mobile character cutoff: raise ground line to 0.82, add
  MOBILE_SCALE_CAP (0.55) so fighters never shrink too small
- Add ₿ bitcoin chest badge to all fighter sprites (scales with tier)
- Add sats coin stack on bot profile page proportional to satsWon
- Seed dev tournament with 8 mock bots on startup (dev mode only)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 08:21:16 +00:00

56 lines
2.1 KiB
TypeScript

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'))