enh(Routing) Multidomain support!
This commit is contained in:
@@ -1,24 +1,52 @@
|
||||
import RequestParser from '../Utils/RequestParser';
|
||||
import BaseRouter from './BaseRouter';
|
||||
import Logger from '../Utils/Logger';
|
||||
import LayResponse from '../IO/LayResponse';
|
||||
|
||||
/**
|
||||
* Router class that extends BaseRouter.
|
||||
* @extends BaseRouter
|
||||
*/
|
||||
export default class Router extends BaseRouter {
|
||||
domain = undefined;
|
||||
server = undefined;
|
||||
routes = new Map();
|
||||
static allMethodKey = '$all';
|
||||
static methods = ['all', 'get', 'post', 'put', 'patch', 'head', 'options'];
|
||||
headerList = new Map();
|
||||
|
||||
constructor(server) {
|
||||
#domain = undefined;
|
||||
#server = undefined;
|
||||
#routes = new Map();
|
||||
#headerList = new Map();
|
||||
|
||||
/**
|
||||
* Constructor for the Router class.
|
||||
* @param {Server} server - The server instance.
|
||||
*/
|
||||
constructor(server, options = {}) {
|
||||
super();
|
||||
this.server = server;
|
||||
this.#server = server;
|
||||
this.#domain = options.domain || undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server instance.
|
||||
* @returns {Server} The server instance.
|
||||
*/
|
||||
getServer() {
|
||||
return this.server;
|
||||
return this.#server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the domain of the router
|
||||
* @returns {String} The base domain for the router.
|
||||
*/
|
||||
getDomain() {
|
||||
return this.#domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the incoming request.
|
||||
* @param {RequestContext} reqContext - The request context.
|
||||
* @returns {Promise<Response>} The response promise.
|
||||
*/
|
||||
async handle(reqContext) {
|
||||
let method = (reqContext.request.method || '').toString().toLowerCase();
|
||||
const methodKey = `$${method.toLowerCase()}`;
|
||||
@@ -32,7 +60,7 @@ export default class Router extends BaseRouter {
|
||||
if (path.startsWith('/')) path = path.substring(1);
|
||||
if (path.endsWith('/')) path = path.substring(0, path.length - 1);
|
||||
|
||||
let currentNode = this.routes;
|
||||
let currentNode = this.#routes;
|
||||
let fallbackNode = currentNode.has('*') ? currentNode.get('*') : undefined;
|
||||
let paramsList = [];
|
||||
|
||||
@@ -65,7 +93,7 @@ export default class Router extends BaseRouter {
|
||||
(['get', 'head'].indexOf(method) < 0 &&
|
||||
!currentNode.has(methodKey) &&
|
||||
!currentNode.has(Router.allMethodKey))
|
||||
) {
|
||||
) {LayResponse
|
||||
return new Response(`[${method}] Method Not Allowed`, {
|
||||
status: 405,
|
||||
});
|
||||
@@ -102,13 +130,26 @@ export default class Router extends BaseRouter {
|
||||
|
||||
try {
|
||||
await reqContext.request.parseBody();
|
||||
let retVal = await handle.callback(
|
||||
let retVal = await this.executeMiddleware(
|
||||
handle.middleware,
|
||||
reqContext.request,
|
||||
reqContext.response,
|
||||
async () => {
|
||||
return await handle.callback(reqContext.request, reqContext.response);
|
||||
},
|
||||
5000 // Add a timeout of 5 seconds for middleware execution
|
||||
);
|
||||
|
||||
await reqContext.request.parseBody();
|
||||
retVal = await handle.callback(
|
||||
reqContext.request,
|
||||
reqContext.response
|
||||
);
|
||||
if (typeof retVal != 'undefined') {
|
||||
if (retVal instanceof Promise) {
|
||||
retVal = await retVal;
|
||||
} else if (retVal instanceof LayResponse) {
|
||||
return await retVal.build();
|
||||
} else if (retVal instanceof Response) {
|
||||
return retVal;
|
||||
} else if (typeof retVal == 'string') {
|
||||
@@ -118,9 +159,9 @@ export default class Router extends BaseRouter {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
Logger(err);
|
||||
console.log(err);
|
||||
return new Response('Error', { status: 500 });
|
||||
console.error(err)
|
||||
Logger.error(err);
|
||||
return new Response('Something went wrong!', { status: 500 });
|
||||
}
|
||||
|
||||
if (reqContext.response.redirect()) {
|
||||
@@ -133,18 +174,27 @@ export default class Router extends BaseRouter {
|
||||
return await reqContext.response.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a route to the router.
|
||||
* @param {string} route - The route path.
|
||||
* @param {function} callback - The route callback function.
|
||||
* @param {Object} options - The route options.
|
||||
* @param {function[]} [middleware] - The middleware functions.
|
||||
*/
|
||||
add(route, callback) {
|
||||
let options = arguments[2];
|
||||
let middleware = arguments[3];
|
||||
if (typeof callback != 'function')
|
||||
throw new Error('`callback` is not a function');
|
||||
if (typeof route != 'string')
|
||||
throw new Error('`route` is not a string');
|
||||
|
||||
if (typeof callback !== 'function')
|
||||
throw new TypeError('`callback` must be a function');
|
||||
if (typeof route !== 'string')
|
||||
throw new TypeError('`route` must be a string');
|
||||
if (Router.methods.indexOf(options.method) < 0)
|
||||
throw new Error(
|
||||
`invalid \`method\` (${options.method}) given`,
|
||||
Router.methods
|
||||
);
|
||||
|
||||
if (!Array.isArray(middleware)) {
|
||||
middleware = typeof middleware == 'function' ? [middleware] : null;
|
||||
}
|
||||
@@ -155,11 +205,11 @@ export default class Router extends BaseRouter {
|
||||
|
||||
let params = [];
|
||||
let segments = route.split('/');
|
||||
let currentNode = this.routes;
|
||||
let currentNode = this.#routes;
|
||||
|
||||
if (route == '') {
|
||||
if (currentNode.get(methodKey)) {
|
||||
if (!this.server.option('allowRouteOverwrite')) {
|
||||
if (!this.#server.option('allowRouteOverwrite')) {
|
||||
Logger(
|
||||
`overwriting of routes is prohibited: [${options.method}] \`${route}\``
|
||||
);
|
||||
@@ -196,7 +246,7 @@ export default class Router extends BaseRouter {
|
||||
}
|
||||
|
||||
if (currentNode.has(methodKey)) {
|
||||
if (!this.server.option('allowRouteOverwrite')) {
|
||||
if (!this.#server.option('allowRouteOverwrite')) {
|
||||
Logger(
|
||||
`overwriting of routes is prohibited: [${method}] \`${route}\``
|
||||
);
|
||||
@@ -209,12 +259,19 @@ export default class Router extends BaseRouter {
|
||||
currentNode.set(methodKey, { callback, middleware, params });
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a route from the router.
|
||||
* @param {string} route - The route path.
|
||||
* @param {string} method - The HTTP method.
|
||||
* @param {function} [cb=null] - The callback function to remove.
|
||||
* @returns {boolean} True if the route was removed, false otherwise.
|
||||
*/
|
||||
remove(route, method, cb = null) {
|
||||
const methodKey = `$${method.toLowerCase()}`;
|
||||
if (route.startsWith('/')) route = route.substring(1);
|
||||
if (route.endsWith('/')) route = route.substring(0, route.length - 1);
|
||||
|
||||
let currentNode = this.routes;
|
||||
let currentNode = this.#routes;
|
||||
|
||||
if (route != '') {
|
||||
let segments = route.toString().split('/');
|
||||
@@ -247,4 +304,45 @@ export default class Router extends BaseRouter {
|
||||
|
||||
return currentNode.delete(methodKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the middleware chain.
|
||||
* @param {function[]} middleware - The middleware functions.
|
||||
* @param {Request} req - The request object.
|
||||
* @param {Response} res - The response object.
|
||||
* @param {function} next - The next function to call.
|
||||
* @returns {Promise<void>} A promise that resolves when the middleware chain is completed.
|
||||
*/
|
||||
async executeMiddleware(middleware, req, res, next) {
|
||||
if (!Array.isArray(middleware) || middleware.length === 0) {
|
||||
return await next();
|
||||
}
|
||||
|
||||
const executeNext = async (index) => {
|
||||
if (index >= middleware.length) {
|
||||
return await next();
|
||||
}
|
||||
|
||||
const currentMiddleware = middleware[index];
|
||||
|
||||
const timeoutPromise = new Promise((_, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(new Error('Middleware execution timed out'));
|
||||
}, timeout);
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.race([
|
||||
currentMiddleware(req, res, async () => {
|
||||
return await executeNext(index + 1);
|
||||
}),
|
||||
timeoutPromise,
|
||||
]);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
return await executeNext(0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user