trigo / trigo-web /backend /src /server.ts
k-l-lambda's picture
fix: exclude static files from SPA catch-all route
001aa74
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"}`);
});