Spaces:
Sleeping
Sleeping
| import express from "express"; | |
| import { createServer } from "http"; | |
| import { Server } from "socket.io"; | |
| import cors from "cors"; | |
| import dotenv from "dotenv"; | |
| import path from "path"; | |
| import fs from "fs"; | |
| import { fileURLToPath } from "url"; | |
| import { GameManager } from "./services/gameManager"; | |
| import { setupSocketHandlers } from "./sockets/gameSocket"; | |
| // Get __dirname equivalent in ES modules | |
| const __filename = fileURLToPath(import.meta.url); | |
| const __dirname = path.dirname(__filename); | |
| // Load environment variables | |
| // Priority: .env.local > .env (load .env first, then .env.local overwrites) | |
| // When running via ts-node: __dirname = backend/src/, so go up 1 level | |
| // When running compiled: __dirname = dist/backend/src/, so go up 3 levels | |
| const isDev = __dirname.includes("/src") && !__dirname.includes("/dist"); | |
| const levelsUp = isDev ? "../" : "../../../"; | |
| const envPath = path.join(__dirname, levelsUp, ".env"); | |
| const envLocalPath = path.join(__dirname, levelsUp, ".env.local"); | |
| // Load .env first (base configuration) | |
| if (fs.existsSync(envPath)) { | |
| dotenv.config({ path: envPath }); | |
| console.log("[Config] Loaded .env"); | |
| } else { | |
| console.log(`[Config] .env not found at: ${envPath}`); | |
| } | |
| // Load .env.local second (overrides .env for local development) | |
| if (fs.existsSync(envLocalPath)) { | |
| dotenv.config({ path: envLocalPath, override: true }); | |
| console.log("[Config] Loaded .env.local (overriding .env)"); | |
| } else { | |
| console.log(`[Config] .env.local not found at: ${envLocalPath}`); | |
| } | |
| const app = express(); | |
| const httpServer = createServer(app); | |
| const io = new Server(httpServer, { | |
| cors: { | |
| origin: | |
| process.env.NODE_ENV === "production" | |
| ? process.env.CLIENT_URL || "http://localhost:5173" | |
| : true, // Allow all origins in development | |
| methods: ["GET", "POST"], | |
| credentials: true | |
| } | |
| }); | |
| // Create GameManager instance | |
| const gameManager = new GameManager(); | |
| const PORT = parseInt(process.env.PORT || "3000", 10); | |
| const HOST = process.env.HOST || "0.0.0.0"; | |
| console.log(`[Config] Server Configuration:`); | |
| console.log(`[Config] PORT: ${PORT}`); | |
| console.log(`[Config] HOST: ${HOST}`); | |
| console.log(`[Config] NODE_ENV: ${process.env.NODE_ENV || "development"}`); | |
| console.log(`[Config] CLIENT_URL: ${process.env.CLIENT_URL || "not set"}`); | |
| // Middleware | |
| app.use(cors()); | |
| app.use(express.json()); | |
| // Serve static files from frontend build (for production) | |
| if (process.env.NODE_ENV === "production") { | |
| const frontendPath = path.join(__dirname, "../../app/dist"); | |
| app.use(express.static(frontendPath)); | |
| // Serve index.html for SPA routes (paths without file extensions) | |
| app.get("*", (req: any, res: any, next: any) => { | |
| // Skip API and socket.io routes | |
| if (req.path.startsWith("/health") || req.path.startsWith("/socket.io")) { | |
| return next(); | |
| } | |
| // Skip requests for files with extensions (let static middleware handle them or return 404) | |
| if (path.extname(req.path)) { | |
| return next(); | |
| } | |
| // Serve index.html for SPA navigation routes | |
| res.sendFile(path.join(frontendPath, "index.html")); | |
| }); | |
| } | |
| // Health check endpoint | |
| app.get("/health", (_req: any, res: any) => { | |
| res.json({ status: "ok", timestamp: new Date().toISOString() }); | |
| }); | |
| // Socket.io connection handling | |
| io.on("connection", (socket: any) => { | |
| console.log(`New client connected: ${socket.id}`); | |
| // Setup game-related socket handlers | |
| setupSocketHandlers(io, socket, gameManager); | |
| // Echo test handler (for testing) | |
| socket.on("echo", (data: any, callback: any) => { | |
| const timestamp = new Date().toISOString(); | |
| const responseMessage = `Hello from server! Received: "${data.message}" at ${timestamp}`; | |
| console.log(`[Echo] Client ${socket.id}: ${data.message}`); | |
| // Send response via callback | |
| if (callback && typeof callback === "function") { | |
| callback({ | |
| message: responseMessage, | |
| serverTime: timestamp, | |
| clientTime: data.timestamp | |
| }); | |
| } | |
| }); | |
| socket.on("disconnect", () => { | |
| console.log(`Client disconnected: ${socket.id}`); | |
| }); | |
| }); | |
| // Start server | |
| httpServer.listen(PORT, HOST, () => { | |
| console.log(`Server running on ${HOST}:${PORT}`); | |
| console.log(`Health check: http://${HOST}:${PORT}/health`); | |
| console.log(`Environment: ${process.env.NODE_ENV || "development"}`); | |
| }); | |