import type { Request, Response, NextFunction } from 'express'; import express from 'express'; import bodyParser from 'body-parser'; import config from 'config'; import basePath from '../basepath'; import cookieParser from 'cookie-parser'; import http from 'http'; import expressWs from 'express-ws'; process.env.TZ = "Etc/GMT"; console.log("Loading ketr.ketran"); const app = express(); const server = http.createServer(app); app.use(cookieParser()); expressWs(app, server); require("../console-line"); /* Monkey-patch console.log with line numbers */ // Temporary debug routes (dev-only). Mount before static so we can // inspect what the server receives for base-prefixed requests. try { // import the debug router using ESM style; fallback to require at runtime if needed // (some dev environments may still emit JS commonjs files) // eslint-disable-next-line @typescript-eslint/no-var-requires const debugRouter = require("../routes/debug").default || require("../routes/debug"); app.use(basePath, debugRouter); } catch (e: any) { console.error('Failed to mount debug routes (src):', e && e.stack || e); } const frontendPath = (config.get("frontendPath") as string).replace(/\/$/, "") + "/", serverConfig = config.get("server") as { port: number }; console.log("Hosting server from: " + basePath); // DB handles are initialized by the modules below; we don't need file-scoped vars here. app.use((req: Request, _res: Response, next: NextFunction) => { console.log(`${req.method} ${req.url}`); next(); }); app.use(bodyParser.json()); /* App is behind an nginx proxy which we trust, so use the remote address * set in the headers */ app.set("trust proxy", true); app.set("basePath", basePath); // basepath is a simple exported string // eslint-disable-next-line @typescript-eslint/no-var-requires const basepathRouter = require("../routes/basepath").default || require("../routes/basepath"); app.use(basePath, basepathRouter); /* Handle static files first so excessive logging doesn't occur */ app.use(basePath, express.static(frontendPath, { index: false })); // index route (may be ESM default export) // eslint-disable-next-line @typescript-eslint/no-var-requires const index = require("../routes/index").default || require("../routes/index"); if (config.has("admin")) { const admin = config.get("admin"); app.set("admin", admin); } // games router // eslint-disable-next-line @typescript-eslint/no-var-requires const gamesRouter = require("../routes/games").default || require("../routes/games"); app.use(`${basePath}api/v1/games`, gamesRouter); /* Allow loading of the app w/out being logged in */ app.use(basePath, index); /* /games loads the default index */ app.use(basePath + "games", index); /* Declare the "catch all" index route last; the final route is a 404 dynamic router */ app.use(basePath, index); /** * Create HTTP server and listen for new connections */ app.set("port", serverConfig.port); process.on('SIGINT', () => { console.log("Gracefully shutting down from SIGINT (Ctrl-C) in 2 seconds"); setTimeout(() => process.exit(-1), 2000); server.close(() => process.exit(1)); }); // database initializers // eslint-disable-next-line @typescript-eslint/no-var-requires import { initGameDB } from '../routes/games/store'; initGameDB().then(function(_db: any) { // games DB initialized via store facade }).then(function() { // eslint-disable-next-line @typescript-eslint/no-var-requires return Promise.resolve((require("../db/users") as any).default || require("../db/users")).then(function(_db: any) { // users DB initialized }); }).then(function() { console.log("DB connected. Opening server."); server.listen(serverConfig.port, () => { console.log(`http/ws server listening on ${serverConfig.port}`); }); }).catch(function(error: any) { console.error(error); process.exit(-1); }); server.on("error", function(error: any) { if (error.syscall !== "listen") { throw error; } // handle specific listen errors with friendly messages switch (error.code) { case "EACCES": console.error(serverConfig.port + " requires elevated privileges"); process.exit(1); break; case "EADDRINUSE": console.error(serverConfig.port + " is already in use"); process.exit(1); break; default: throw error; } }); export { app, server };