107 lines
3.5 KiB
JavaScript
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; |