Files
CubeAdmin/server.ts
2026-03-08 15:49:34 +01:00

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