import express, {Router as ExpressRouter} from "express"; import {IMiddlewareCollection} from "../middleware/IMiddleware"; import {AppServiceProvider} from "./AppServiceProvider"; import {HttpContext} from "../contexts/HttpContext"; import path from "path"; export class RouteGroup { public routes: (Route | RouteGroup)[] = []; public name; public middlewares: string[] = [] public parentGroup: RouteGroup constructor(name: string) { this.name = name; } public populate(route: (Route | RouteGroup), params: string) { if (route instanceof RouteGroup) { route.routes.forEach(child => { child.middlewares?.concat(route.middlewares); this.populate(child, params) }); return; } route.prefix(params); } public middleware(middleware: string[]) { this.middlewares = middleware } } export class Route { public prefixes: string[] = []; public path: string; public method: string; public handler: Function | string; public middlewares: Array = []; public parentGroup: RouteGroup; constructor(path: string, method: string, handler: Function | string) { this.path = path; this.method = method; this.handler = handler; this.prefixes.push(this.path) } public prefix(path: string) { this.prefixes.unshift(path); } public getPath() { return "/" + this.prefixes.join("/") } public middleware(middlewares: Array) { this.middlewares = middlewares } } export class Router { public routes: (Route | RouteGroup)[] = []; public openedGroups: Array = []; private router: ExpressRouter = ExpressRouter(); private middlewareCollection: IMiddlewareCollection; public static instance: Router; public setMiddlewares(middlewareCollection: IMiddlewareCollection) { this.middlewareCollection = middlewareCollection; } private getRecentGroup() { return this.openedGroups[this.openedGroups.length - 1]; } private route(path: string, method: string, handler: Function | string) { const route = new Route(path, method, handler); const openedGroup = this.getRecentGroup(); if (openedGroup) { openedGroup.routes.push(route) route.parentGroup = openedGroup } else { this.routes.push(route) } return route; } private _get(path: string, handler: Function | string) { return this.route(path, "get", handler); } private _static(path: string, dir: string) { return this.route(path, "use", dir); } private _group(name: string, cb: Function) { const group = new RouteGroup(name); const openedGroup = this.getRecentGroup(); if (openedGroup) { openedGroup.routes.push(group) group.parentGroup = openedGroup } else { this.routes.push(group); } this.openedGroups.push(group); cb(); this.openedGroups.pop(); group.populate(group, group.name); return group; } public static static(path: string, dir: string) { return this.getInstance()._static(path, dir); } public static group(name: string, cb: Function) { return this.getInstance()._group(name, cb); } public static get(path: string, handler: Function | string) { console.log(handler) return this.getInstance()._get(path, handler); } private getMiddleware(parentGroup: RouteGroup, route: Route) { route.middlewares = parentGroup?.middlewares.concat(route.middlewares) if (!parentGroup?.parentGroup) return; return this.getMiddleware(parentGroup.parentGroup, route); } private async parseRoute(route: (Route | RouteGroup)) { if (route instanceof RouteGroup) { route.routes.forEach(child => { this.parseRoute(child) }) } else { if (route.method === "use") { this.router.use(route.getPath(), express.static(route.handler as string)); return; } this.getMiddleware(route.parentGroup, route); const middlewares: Function[] = []; if (route.middlewares?.length) { route.middlewares.forEach(x => { if (this.middlewareCollection[x]) { middlewares.push(this.middlewareCollection[x].handle); } }) } if (typeof route.handler === "string") { const parts = route.handler.split("@"); const controller = parts[0]; const method = parts[1]; let usedController; for (let ctrl of AppServiceProvider.getControllers()) { if (ctrl.constructor.name === controller) { usedController = ctrl; break; } } if (!usedController) { console.log("Controller " + controller + " could not be found"); process.exit(0) } this.router[route.method](route.getPath(), ...middlewares, async (req, res) => { const httpContext: HttpContext = { request: req, status: 200, } const data = await usedController[method](httpContext); if (httpContext.redirect) { return res.redirect(httpContext.redirect) } else if (httpContext.view) { return res.sendFile(path.resolve(AppServiceProvider.getInstance().getBaseDir(), "..", "views", httpContext.view)) } else if (httpContext.static) { return express.static(httpContext.static); } res.status(httpContext.status).send(data); }); } else { this.router[route.method](route.getPath(), ...middlewares, route.handler); } } } public run() { this.routes.forEach(async route => { await this.parseRoute(route) }); } public getRouter() { return this.router } public static create() { this.instance = new this(); return this.instance } public static getInstance() { return this.instance } }