import { Reflect } from "https://deno.land/x/reflect_metadata@v0.1.12/mod.ts"; import serialize from "./serialize.ts"; import HttpError from "./HttpError.ts"; import RestResponse from "./RestResponse.ts"; import RestRequest from "./RestRequest.ts"; export type HandlerRes = Promise | RestResponse | undefined; export interface Handler { handle(req: RestRequest): HandlerRes; } export interface RestOptions { route?: string; } // deno-lint-ignore no-explicit-any export type ProcessFunc = (this: T, req: RestRequest, arg: any, name: string) => Promise | unknown; export interface RouterHandler { path: string; handler: Handler; } export function addMetaQuery(target: T, ...handlers: ((r: T) => RouterHandler)[]) { let props = Reflect.getOwnMetadata('router:queries', target); if (props === undefined) Reflect.defineMetadata('router:queries', props = [], target); props.push(...handlers); } export function makeParameterModifier(func: ProcessFunc) { return (target: T, key: string, index: number) => { let res = Reflect.getOwnMetadata('router:params', target, key); if (res === undefined) Reflect.defineMetadata('router:params', res = [], target, key); (res[index] ??= []).push(func); return res; } } export default class Router { private _handlers?: RouterHandler[]; public defaultHandler?: Handler; private _init() { if (this._handlers === undefined) { this._handlers = []; // why the actual fuck not // deno-lint-ignore no-this-alias for (let proto = this; proto instanceof Router; proto = Object.getPrototypeOf(proto)) { const props = Reflect.getOwnMetadata('router:queries', proto); for (const handler of props ?? []) { this._handlers.push(handler(this)); } } } return this._handlers; } public async handle(req: RestRequest) { for (const hnd of this._init()) { const _req = req.match(hnd.path); if (_req) { try { const res = await hnd.handler.handle(_req); if (res) { // damn you cors res.header('Access-Control-Allow-Origin', '*') res.header('Access-Control-Allow-Headers', '*') return res; } } catch (e) { const res = await this.onError(_req, e); if (res instanceof HttpError) return new RestResponse() .body(await serialize(res.body)) .status(res.status); else return res; } } } return this.defaultHandler?.handle(req); } public addHandler(path: string, handler: Handler) { this._init().push({ path, handler }); return this; } public onError(_req: RestRequest, error: unknown): Promise | RestResponse | HttpError { if (error instanceof HttpError) return error; else { console.error(error); try { return new HttpError(`Internal error: ${error}\nSee logs for details`, 500); } catch { return new HttpError('Internal error.\nSee logs for details', 500); } } } public async attach(server: Deno.Listener) { while (true) { for await (const conn of server) { (async () => { for await (const req of Deno.serveHttp(conn)) { const r = await this.handle(RestRequest.fromMessage(req)); if (r) req.respondWith(r.toFetchResponse()); } })().catch(console.error); } } } }