Files
2026-08-30 15:25:31 +02:00

107 lines
3.5 KiB
JavaScript

/*
! /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;