clonegur/backend/server/Router.ts

118 lines
3.9 KiB
TypeScript
Raw Normal View History

2023-06-29 14:31:25 +00:00
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> | RestResponse | undefined;
export interface Handler {
handle(req: RestRequest): HandlerRes;
}
export interface RestOptions {
route?: string;
}
// deno-lint-ignore no-explicit-any
export type ProcessFunc<T extends Router> = (this: T, req: RestRequest, arg: any, name: string) => Promise<unknown> | unknown;
export interface RouterHandler {
path: string;
handler: Handler;
}
export function addMetaQuery<T extends Router>(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<T extends Router>(func: ProcessFunc<T>) {
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);
2023-06-30 10:19:21 +00:00
if (res) {
// damn you cors
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', '*')
return res;
}
2023-06-29 14:31:25 +00:00
}
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> | 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);
}
}
}
2023-06-30 00:37:07 +00:00
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());
}
2023-06-30 22:20:20 +00:00
})().catch(console.error);
2023-06-29 14:31:25 +00:00
}
}
}
}