require('dotenv').config() const http = require('http') const app = require('./app') const botScore = require('./middleware/botScore') const { ensureSchema, close } = require('./utils/db') const { seedDefaults, createInitialAdminFromEnv } = require('../db/seed') const settings = require('./model/settings/settings.model') const mailer = require('./utils/mailer') const createLogger = require('./utils/logger') const pkg = require('../package.json') const log = createLogger('server') const PORT = Number(process.env.PORT) || 3000 const HOST = '0.0.0.0' // bind all interfaces so Pangolin / the LAN can reach it async function start() { log.info(`starting UOMysticmoon server v${pkg.version}`, { node: process.version, env: process.env.NODE_ENV || 'development', logLevel: process.env.LOG_LEVEL || 'info', logFile: createLogger.logFilePath || 'disabled (console only)', db: `${process.env.DB_HOST || '127.0.0.1'}:${process.env.DB_PORT || 3306}/${process.env.DB_NAME || 'uomysticmoon'}`, cookieSecure: process.env.COOKIE_SECURE || 'auto', smtp: mailer.isConfigured() ? 'configured' : 'not configured (mailto fallback)', }) log.info('ensuring database schema...') await ensureSchema() log.info('seeding defaults...') await seedDefaults() await createInitialAdminFromEnv() const mode = await settings.get('site_mode') log.info(`site mode: ${String(mode || 'live').toUpperCase()}`) const server = http.createServer(app) server.listen(PORT, HOST, () => { log.info(`listening on http://${HOST}:${PORT} (API at /api/v1, health at /api/health)`) }) setupShutdown(server) } function setupShutdown(server) { let closing = false const shutdown = async (signal) => { if (closing) return closing = true log.warn(`${signal} received — shutting down gracefully`) botScore.stopSweeper() // stop the bot-store cleanup interval server.close(() => log.info('http server closed')) try { await close() log.info('database pool closed') } catch (err) { log.error('error closing database pool', err) } await createLogger.close() // flush the log file process.exit(0) } process.on('SIGINT', () => shutdown('SIGINT')) process.on('SIGTERM', () => shutdown('SIGTERM')) process.on('unhandledRejection', (reason) => log.error('unhandledRejection', { reason: String(reason) })) process.on('uncaughtException', (err) => { log.error('uncaughtException', err) process.exit(1) }) } start().catch((err) => { log.error('failed to start server', err) process.exit(1) })