feat(project) cleanup, removed dependencies, updated structure

This commit is contained in:
Benjamin Wegener
2023-12-08 02:05:23 +00:00
parent 8aeea132a5
commit 07fa57f178
18 changed files with 853 additions and 173 deletions

251
src/Routing/Router.js Normal file
View File

@@ -0,0 +1,251 @@
import LayRequest from '../IO/LayRequest';
import LayResponse from '../IO/LayResponse';
import RequestParser from '../Utils/RequestParser';
export default class Router {
server = undefined;
routes = new Map();
static allMethodKey = '$all';
static methods = ['all', 'get', 'post', 'put', 'patch', 'head', 'options'];
headerList = new Map();
constructor(server) {
this.server = server;
}
async handle(req) {
if (!(req instanceof Request)) {
throw new Error('`req` is not a proper Request');
}
let method = (req.method || '').toString().toLowerCase();
const methodKey = `$${method.toLowerCase()}`;
let path;
let url = req.url;
if (!(url instanceof URL) && typeof url == 'string') {
url = new URL(url);
path = url.pathname;
}
if (path.startsWith('/')) path = path.substring(1);
if (path.endsWith('/')) path = path.substring(0, path.length - 1);
let currentNode = this.routes;
let paramsList = [];
if (path != '') {
let segments = path.split('/');
for (let index = 0; index < segments.length; index++) {
const segment = segments[index];
if (currentNode.has(segment)) {
currentNode = currentNode.get(segment);
continue;
}
if (currentNode.has(':') || currentNode.has('?')) {
paramsList.push(segment);
currentNode = currentNode.get(':') ?? currentNode.get('?');
continue;
}
currentNode = null;
break;
}
}
if (
!(currentNode instanceof Map) ||
(['get', 'head'].indexOf(method) < 0 &&
!currentNode.has(methodKey) &&
!currentNode.has(Router.allMethodKey))
) {
return new Response(`[${method}] Method Not Allowed`, {
status: 405,
});
}
if (
!currentNode.has(methodKey) &&
!currentNode.get(Router.allMethodKey)
) {
return new Response(`Not Implemented: [${method}, ${req.url}]`, {
status: 501,
});
}
const reqContext = {
request: null,
response: null,
};
try {
reqContext.request = new LayRequest(req);
reqContext.response = new LayResponse(this);
} catch (err) {
console.log(err);
}
let handle =
currentNode.get(methodKey) || currentNode.get(Router.allMethodKey);
if (typeof handle == 'object' && typeof handle.callback == 'function') {
if (Array.isArray(handle.params)) {
for (const name of handle.params) {
let value = paramsList[reqContext.request.params.size];
if (reqContext.request.params.has(name)) {
let yet = reqContext.request.params.get(name);
if (!Array.isArray(yet)) {
yet = [yet];
}
yet.push(value);
value = yet;
}
reqContext.request.params.set(name, value);
}
}
}
try {
const retVal = await handle.callback(
reqContext.request,
reqContext.response
);
if (typeof retVal != 'undefined') {
if (retVal instanceof Response) {
return retVal;
} else if (typeof retVal == 'string') {
reqContext.response.send(retVal);
} else {
reqContext.response.json(retVal);
}
}
} catch (err) {
console.log(err);
return new Response('Error', { status: 500 });
}
if (reqContext.response.redirect()) {
return Response.redirect(
reqContext.response.redirect,
reqContext.response.status || 302
);
}
return new Response(reqContext.response.body, {
status: reqContext.response.status() || 200,
headers: reqContext.response.headers() || {},
});
}
add(route, callback, method = 'get', middleware = null) {
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 (Router.methods.indexOf(method) < 0)
throw new Error(
`invalid \`method\` (${method}) given`,
Router.methods
);
if (!Array.isArray(middleware)) {
middleware = typeof middleware == 'function' ? [middleware] : null;
}
const methodKey = `$${method.toLowerCase()}`;
if (route.startsWith('/')) route = route.substring(1);
if (route.endsWith('/')) route = route.substring(0, route.length - 1);
let params = [];
let segments = route.split('/');
let currentNode = this.routes;
if (route == '') {
if (currentNode.get(methodKey)) {
if (!this.server.option('allowOverwrite')) {
console.log(
`overwriting of routes is prohibited: [${method}] \`${route}\``
);
return;
}
console.log(`overwriting route for \`${route}\``);
}
currentNode.set(methodKey, { callback, middleware, params: [] });
return;
}
// add method to the segments to store the callback with
// and to allow different methods for the same route
for (let index = 0; index < segments.length; index++) {
let segment = segments[index];
let varName = null;
if (segment.startsWith(':')) {
let newSegment = ':';
varName = segment.substring(1);
if (segment.endsWith('?')) {
// optional param
newSegment = '?';
varName = varName.substring(0, -1);
}
segment = newSegment;
params.push(varName);
}
if (!currentNode.has(segment)) currentNode.set(segment, new Map());
currentNode = currentNode.get(segment);
}
if (currentNode.has(methodKey)) {
if (!this.server.option('allowOverwrite')) {
console.log(
`overwriting of routes is prohibited: [${method}] \`${route}\``
);
return;
}
console.log(`overwriting route for \`${route}\``);
}
currentNode.set(methodKey, { callback, middleware, params });
}
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;
if (route != '') {
let segments = route.toString().split('/');
for (let index = 0; index < segments.length; index++) {
const segment = segments[index];
if (currentNode.has(segment)) {
currentNode = currentNode.get(segment);
continue;
}
if (currentNode.has(':') || currentNode.has('?')) {
currentNode = currentNode.get(':') ?? currentNode.get('?');
continue;
}
currentNode = null;
break;
}
}
if (
!(currentNode instanceof Map) ||
!currentNode.has(methodKey) ||
(typeof cb == 'function' && currentNode.get(methodKey) != cb)
) {
return false;
}
return currentNode.delete(methodKey);
}
}