feature: serverus modularly serves up a directory as an HTTP server
This commit is contained in:
commit
58139b078d
20 changed files with 1449 additions and 0 deletions
111
handlers/typescript.ts
Normal file
111
handlers/typescript.ts
Normal file
|
@ -0,0 +1,111 @@
|
|||
import { walk } from '@std/fs';
|
||||
import { delay } from '@std/async/delay';
|
||||
import * as path from '@std/path';
|
||||
import { getCookies } from '@std/http/cookie';
|
||||
|
||||
export type PRECHECK = (
|
||||
request: Request,
|
||||
meta: Record<string, any>
|
||||
) => undefined | Response | Promise<undefined | Response>;
|
||||
export type PRECHECKS_TABLE = Record<'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH', PRECHECK>;
|
||||
export type ROUTE_HANDLER_METHOD = (request: Request, meta: Record<string, any>) => Promise<Response> | Response;
|
||||
|
||||
export interface ROUTE_HANDLER {
|
||||
PRECHECKS?: PRECHECKS_TABLE;
|
||||
GET?: ROUTE_HANDLER_METHOD;
|
||||
POST?: ROUTE_HANDLER_METHOD;
|
||||
PUT?: ROUTE_HANDLER_METHOD;
|
||||
DELETE?: ROUTE_HANDLER_METHOD;
|
||||
PATCH?: ROUTE_HANDLER_METHOD;
|
||||
default?: ROUTE_HANDLER_METHOD;
|
||||
}
|
||||
|
||||
const routes: Map<URLPattern, ROUTE_HANDLER> = new Map<URLPattern, ROUTE_HANDLER>();
|
||||
let loading: boolean = false;
|
||||
let all_routes_loaded: boolean = false;
|
||||
|
||||
/**
|
||||
* Handles requests for which there are typescript files in the root tree.
|
||||
*
|
||||
* NOTE: On initial request the tree will be scanned and handlers loaded,
|
||||
* concurrent requests should wait for the load, but until the tree has been
|
||||
* scanned, requests may take longer while the load completes.
|
||||
*
|
||||
* @param request The incoming HTTP request
|
||||
* @returns Either a response (a handler for the request path and method was found) or undefined if unhandled.
|
||||
*/
|
||||
export default async function handle_typescript(request: Request): Promise<Response | undefined> {
|
||||
if (!all_routes_loaded) {
|
||||
if (!loading) {
|
||||
loading = true;
|
||||
|
||||
const root_directory = path.resolve(Deno.cwd());
|
||||
|
||||
for await (
|
||||
const entry of walk(root_directory, {
|
||||
exts: ['.ts'],
|
||||
skip: [/\.test\.ts$/]
|
||||
})
|
||||
) {
|
||||
if (entry.isFile) {
|
||||
const relative_path = entry.path.substring(root_directory.length);
|
||||
const route_path = relative_path
|
||||
.replace(/\.ts$/, '')
|
||||
//.replace(/\[(\w+)\]/g, ':$1')
|
||||
.replace(/\/index$/, '')
|
||||
.replace(/___/g, ':') || // required for windows, uncivilized OS that it is
|
||||
'/';
|
||||
|
||||
const import_path = new URL('file://' + entry.path, import.meta.url).toString();
|
||||
const module: ROUTE_HANDLER = await import(import_path) as ROUTE_HANDLER;
|
||||
|
||||
const pattern = new URLPattern({ pathname: route_path });
|
||||
routes.set(pattern, module);
|
||||
}
|
||||
}
|
||||
|
||||
all_routes_loaded = true;
|
||||
loading = false;
|
||||
}
|
||||
|
||||
do {
|
||||
await delay(10);
|
||||
} while (!all_routes_loaded);
|
||||
}
|
||||
|
||||
for (const [pattern, handler_module] of routes) {
|
||||
const match = pattern.exec(request.url);
|
||||
if (match) {
|
||||
const method = request.method as keyof ROUTE_HANDLER;
|
||||
const method_handler: ROUTE_HANDLER_METHOD = (handler_module[method] ?? handler_module.default) as ROUTE_HANDLER_METHOD;
|
||||
if (!method_handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cookies: Record<string, string> = getCookies(request.headers);
|
||||
const query = Object.fromEntries(new URL(request.url).searchParams.entries());
|
||||
|
||||
const metadata = {
|
||||
cookies,
|
||||
params: match.pathname.groups,
|
||||
query
|
||||
};
|
||||
|
||||
const precheck: PRECHECK | undefined = handler_module.PRECHECKS?.[request.method as keyof PRECHECKS_TABLE];
|
||||
if (precheck) {
|
||||
const result = await precheck(request, metadata);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return await method_handler(request, metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function unload(): void {
|
||||
loading = false;
|
||||
routes.clear();
|
||||
all_routes_loaded = false;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue