100 lines
3.4 KiB
TypeScript
100 lines
3.4 KiB
TypeScript
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<void> {
|
|
// ---- 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<void>((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<never> => {
|
|
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)
|
|
})
|