/* ! /server/index.js ? Main entry point initializing Express, Socket.IO, and dynamic autoloaders for routes and actions. */ // % imports import http from 'http'; import express from 'express'; import { Server as SocketServer } from 'socket.io'; import path from 'path'; import { fileURLToPath } from 'url'; import dbWorker from '../database/index.js'; // import the database worker & factory const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); /* = The Socket.IO Action-Autoloader: ^ How it works: ? Recursively scans the "/actions" directory. ? Maps all JavaScript files into a nested object structure for dot-notation access. ? Ignores "index.js" and files starting with a dot (e.g., ".templateAction.js"). ? Example mapping: "/actions/chat/message.js" becomes "chat.message(socket, data)". ^ Usage: ?? Import the actions: "import actions from './actions.js';" ?? Trigger an action inside a connection: "socket.on('chat.message', (data) => chat.message(socket, data));" ? The action template expects the active "socket" and incoming "data" as arguments. */ import actions from './actions.js'; /* = The Express Router-Autoloader: ^ How it works: ? Recursively scans the "/routers" directory. ? Automatically generates a nested Express Router tree based on folder and file names. ? Ignores "index.js" and files starting with a dot (e.g., ".templateRouter.js"). ? Files named "root.js" are mounted directly to their current directory path (/). ? Example mapping: "/routers/api/v1/users.js" becomes the endpoint "/api/v1/users". ^ Usage: ?? Import the master router: "import routers from './routers.js';" ?? Mount to Express app: "app.use('/', routers);" ? Each router template receives an Express sub-router instance to define its methods (GET, POST, etc.). */ import routers, { registeredEndpoints } from './routers.js'; // $ debug-output console.info('[Autoloader] Loaded Actions:', Object.keys(actions)); console.info('[Autoloader] Loaded Routers:', registeredEndpoints); // % server initialization const app = express(); const server = http.createServer(app); // % socket.io initialization const io = new SocketServer(server, { cors: { origin: '*', methods: ['GET', 'POST'] } }); // % view engine & static resources app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, '..', 'frontend', 'views')); app.use(express.static(path.join(__dirname, '..', 'frontend', 'resources'))); // % middleware app.use(express.json()); app.use(express.urlencoded({ extended: true })); // $ Inject database worker into every request app.use((req, res, next) => { req.db = dbWorker; next(); }); // % mount routers app.use('/', routers); // % socket.io connection handling io.on('connection', (socket) => { console.log(`[Socket.IO] Client connected: ${socket.id}`); // $ Automatically map all loaded actions to this socket for (const [eventName, actionHandler] of Object.entries(actions)) { if (typeof actionHandler === 'function') { socket.on(eventName, (data) => actionHandler(socket, dbWorker, data)); } } socket.on('disconnect', () => { console.log(`[Socket.IO] Client disconnected: ${socket.id}`); }); }); // % start server const PORT = process.env.PORT; server.listen(PORT, () => { console.log(`[Server] Express and Socket.IO are running on port ${PORT}`); }); // $ export the server instance export default server;