Spaces:
Running
Running
| import "dotenv/config"; | |
| import { createServer } from "http"; | |
| import app from "./app.js"; | |
| import { setupWS } from "./chat/ws.js"; | |
| import { logger } from "./lib/logger.js"; | |
| import { initDb, getUser, saveUser } from "./db/index.js"; | |
| import { connectRedis } from "./lib/redis.js"; | |
| import bcrypt from "bcryptjs"; | |
| const port = Number(process.env.PORT ?? 3000); | |
| if (Number.isNaN(port) || port <= 0) throw new Error(`Invalid PORT: "${process.env.PORT}"`); | |
| const httpServer = createServer(app); | |
| function startListening(retriesLeft) { | |
| httpServer.listen(port, async () => { | |
| logger.info({ port }, "Nexus Chat server listening"); | |
| await seedAdmin(); | |
| }); | |
| httpServer.once("error", (err) => { | |
| if (err.code === "EADDRINUSE" && retriesLeft > 0) { | |
| logger.warn({ port, retriesLeft }, "Port in use, retrying in 3s…"); | |
| setTimeout(() => { | |
| httpServer.removeAllListeners("error"); | |
| httpServer.close(() => startListening(retriesLeft - 1)); | |
| // Force-close if close hangs | |
| setTimeout(() => startListening(retriesLeft - 1), 500); | |
| }, 3000); | |
| } else { | |
| logger.error({ err }, "Server error"); | |
| process.exit(1); | |
| } | |
| }); | |
| } | |
| async function main() { | |
| await initDb(); | |
| logger.info("PostgreSQL ready"); | |
| await connectRedis(); | |
| setupWS(httpServer); | |
| startListening(10); | |
| } | |
| main().catch((err) => { | |
| logger.error({ err }, "Fatal startup error"); | |
| process.exit(1); | |
| }); | |
| async function seedAdmin() { | |
| const admins = [ | |
| { email: "admin@nexus.vn", displayName: "quan5s" }, | |
| { email: "quantri@nexus.vn", displayName: "quan5s" }, | |
| ]; | |
| for (const { email, displayName } of admins) { | |
| const existing = await getUser(email); | |
| if (!existing) { | |
| const passwordHash = await bcrypt.hash(process.env.SEED_ADMIN_PASSWORD ?? "11082012", 10); | |
| await saveUser({ | |
| email, displayName, passwordHash, role: "admin", | |
| avatar: null, nameTagColor: "red", createdAt: Date.now() | |
| }); | |
| logger.info({ email }, "Admin seeded"); | |
| } | |
| } | |
| } | |