import next from "next" import { createServer } from "http" import { Server } from "socket.io" import { runMigrations } from "@/lib/db/migrate" import { mcProcessManager } from "@/lib/minecraft/process" import { setupSocketServer } from "@/lib/socket/server" import { auth } from "@/lib/auth/index" const dev = process.env.NODE_ENV !== "production" const port = Number(process.env.PORT ?? 3000) const hostname = process.env.HOSTNAME ?? "0.0.0.0" async function main(): Promise { // ---- 1. Run DB migrations ------------------------------------------------- console.log("[Server] Running database migrations…") await runMigrations() console.log("[Server] Migrations complete") // ---- 2. Prepare Next.js app ----------------------------------------------- const app = next({ dev, hostname, port }) const handle = app.getRequestHandler() await app.prepare() console.log(`[Server] Next.js ready (${dev ? "development" : "production"})`) // ---- 3. Create Node.js HTTP server ---------------------------------------- const httpServer = createServer((req, res) => { handle(req, res).catch((err: unknown) => { console.error("[Server] Next.js handler error:", err) res.statusCode = 500 res.end("Internal Server Error") }) }) // ---- 4. Attach Socket.io -------------------------------------------------- const io = new Server(httpServer, { cors: { // Restrict to same origin in production; allow all in dev for convenience origin: dev ? "*" : (process.env.NEXTAUTH_URL ?? `http://${hostname}:${port}`), credentials: true, }, // Use websocket transport first, fall back to polling transports: ["websocket", "polling"], }) // Delegate namespace setup (auth middleware + event handlers) to the module setupSocketServer(io, auth) // ---- 5. Start HTTP server ------------------------------------------------- await new Promise((resolve, reject) => { httpServer.on("error", reject) httpServer.listen(port, hostname, () => { resolve() }) }) console.log(`[Server] Listening on http://${hostname}:${port}`) // ---- 6. Initialize Minecraft process manager ------------------------------ // (Does NOT auto-start the MC server; that is controlled through the UI) console.log("[Server] McProcessManager initialized") // ---- 7. Graceful shutdown ------------------------------------------------- const shutdown = async (signal: string): Promise => { console.log(`\n[Server] Received ${signal}, shutting down…`) // Stop accepting new connections httpServer.close() // Disconnect all Socket.io clients await io.close() // Stop the Minecraft server gracefully if it is running const status = mcProcessManager.getStatus() if (status.running) { console.log("[Server] Stopping Minecraft server…") try { await mcProcessManager.stop(false) } catch { // If graceful stop fails, force kill try { await mcProcessManager.stop(true) } catch (err) { console.error("[Server] Force stop failed:", err) } } } console.log("[Server] Goodbye.") process.exit(0) } process.on("SIGTERM", () => void shutdown("SIGTERM")) process.on("SIGINT", () => void shutdown("SIGINT")) } main().catch((err: unknown) => { console.error("[Server] Fatal startup error:", err) process.exit(1) })