import Router from "@koa/router"; import Koa from "koa"; type Constructor = new (...args: any[]) => T; type Route = { method: string; path: string; fn: any; }; const routeSymbol = Symbol("routeSymbol"); export function get(path: string) { return function getDecorator (originalMethod: any, context: ClassMethodDecoratorContext) { if (context.metadata) { if (!context.metadata[routeSymbol]) { context.metadata[routeSymbol] = [] as Route[]; } context.metadata[routeSymbol].push({method: 'get', path, fn: originalMethod}); } return originalMethod; } } export function post(path: string) { return function getDecorator (originalMethod: any, context: ClassMethodDecoratorContext) { if (context.metadata) { if (!context.metadata[routeSymbol]) { context.metadata[routeSymbol] = [] as Route; } context.metadata[routeSymbol].push({method: 'post', path, fn: originalMethod}); } return originalMethod; } } export function middleware(fn: any, path: string = '') { return function middlewareDecorator (constructor: any, context: ClassDecoratorContext) { if (context.metadata) { if (!context.metadata[routeSymbol]) { context.metadata[routeSymbol] = []; } context.metadata[routeSymbol].unshift({method: 'use', path, fn}); } return constructor; } } export function router(prefix: string) { return function routerDecorator(constructor: T, context: ClassDecoratorContext) { return class KoaRouter extends constructor { public router: Router; constructor(...args: any[]) { super(...args); this.router = new Router({prefix}); for (const route of context.metadata?.[routeSymbol] || [] satisfies Route[]) switch (route.method) { case "get": this.router.get(route.path, route.fn); break; case "post": this.router.post(route.path, route.fn); break; case 'use': this.router.use(route.fn); break; } } public injectInto(app: Koa) { app.use(this.router.routes()).use(this.router.allowedMethods()); } } } }