middleware works as it should! With recursion, which I hate :)

This commit is contained in:
2025-01-27 19:03:35 +03:00
parent 4eb62f688a
commit 2b2779b0e6
7 changed files with 5005 additions and 36 deletions

4928
client/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,12 +7,14 @@ import { ok, ResultFromJSON } from "@shared/utils/result.ts";
import { ResultResponseFromJSON } from "@src/lib/context.ts";
import admin from "@src/lib/admin.ts";
import UsbipManager from "@shared/utils/usbip.ts";
import loggerMiddleware from "@src/middleware/logger.ts";
const router = new HttpRouter();
const views = Deno.cwd() + "/views/";
const eta = new Eta({ views });
router.use(loggerMiddleware);
router.use(rateLimitMiddleware);
router.use(authMiddleware);

View File

@ -8,7 +8,7 @@ type RequestHandler<S extends string> = (
export type Middleware = (
c: Context<string>,
next: () => Promise<void>,
) => Promise<Response | undefined> | Response | undefined;
) => Promise<Response | void> | Response | void;
type MethodHandlers<S extends string> = Partial<
Record<string, RequestHandler<S>>
@ -19,7 +19,7 @@ const DEFAULT_NOT_FOUND_HANDLER = () => new Response("404 Not found");
class HttpRouter {
routerTree = new RouterTree<MethodHandlers<any>>();
pathPreprocessor?: (path: string) => string;
middlewareChain: Middleware[] = [];
middlewares: Middleware[] = [];
defaultNotFoundHandler: RequestHandler<string> = DEFAULT_NOT_FOUND_HANDLER;
setPathProcessor(processor: (path: string) => string) {
@ -27,7 +27,7 @@ class HttpRouter {
}
use(mw: Middleware): HttpRouter {
this.middlewareChain.push(mw);
this.middlewares.push(mw);
return this;
}
@ -99,23 +99,58 @@ class HttpRouter {
): Promise<Response> {
const c = new Context(req, connInfo, {});
let i = 0;
const mw = this.middlewareChain[i++];
const path = this.pathPreprocessor
? this.pathPreprocessor(c.path)
: c.path;
return await this.routerTree
let params: Params<string> = {};
const handler = this.routerTree
.find(path)
.andThen((routeMatch) => {
const { value, params } = routeMatch;
const handler = value[req.method];
return handler
? some(handler(Context.setParams(c, params)))
: none;
const { value: handlers, params: paramsMatched } = routeMatch;
params = paramsMatched;
const handler = handlers[req.method];
return handler ? some(handler) : none;
})
.unwrapOrElse(() => this.defaultNotFoundHandler(c));
.unwrapOrElse(() => this.defaultNotFoundHandler);
const cf = await this.executeMiddlewareChain(
this.middlewares,
handler,
Context.setParams(c, params),
);
return cf.res;
}
private async executeMiddlewareChain<S extends string>(
middlewares: Middleware[],
handler: RequestHandler<S>,
c: Context<S>,
) {
let currentIndex = -1;
const dispatch = async (index: number): Promise<void> => {
currentIndex = index;
if (index < middlewares.length) {
const middleware = middlewares[index];
const result = await middleware(c, () => dispatch(index + 1));
if (result !== undefined) {
c.res = await Promise.resolve(result);
}
} else {
const res = await handler(c);
c.res = res;
}
};
await dispatch(0);
return c;
}
}

View File

@ -3,7 +3,7 @@ import admin from "@lib/admin.ts";
const LOGIN_PATH = "/login";
const authMiddleware: Middleware = (c) => {
const authMiddleware: Middleware = async (c, next) => {
const token = c.cookies.get("token");
const isValid = token
.map((token) => admin.sessions.verifyToken(token))
@ -11,9 +11,8 @@ const authMiddleware: Middleware = (c) => {
const path = c.path;
if (path.startsWith("/public")) {
return;
}
await next();
} else {
if (path !== LOGIN_PATH && !isValid) {
return c.redirect("/login");
}
@ -21,6 +20,9 @@ const authMiddleware: Middleware = (c) => {
if (path === LOGIN_PATH && isValid) {
return c.redirect("");
}
await next();
}
};
export default authMiddleware;

View File

@ -0,0 +1,9 @@
import { Middleware } from "@lib/router.ts";
const loggerMiddleware: Middleware = async (c, next) => {
console.log(c.req.method, c.path);
await next();
console.log(c.res.status);
};
export default loggerMiddleware;

View File

@ -8,7 +8,7 @@ const requestCounts: Partial<
const MAX_REQUESTS_PER_WINDOW = 300;
const RATE_LIMIT_WINDOW = 60000;
const rateLimitMiddleware: Middleware = (c) => {
const rateLimitMiddleware: Middleware = async (c, next) => {
const hostnameOpt = c.hostname;
if (hostnameOpt.isSome()) {
@ -19,15 +19,9 @@ const rateLimitMiddleware: Middleware = (c) => {
if (!clientCount || now - clientCount.lastReset > RATE_LIMIT_WINDOW) {
requestCounts[hostname] = { count: 1, lastReset: now };
return;
}
if (clientCount.count < MAX_REQUESTS_PER_WINDOW) {
} else if (clientCount.count < MAX_REQUESTS_PER_WINDOW) {
clientCount.count++;
return;
}
if (c.preferredType.isSome()) {
} else if (c.preferredType.isSome()) {
log.info(`client ${hostname} is rate limeted`);
switch (c.preferredType.value) {
case "html": {
@ -46,11 +40,13 @@ const rateLimitMiddleware: Middleware = (c) => {
);
}
}
}
} else {
return new Response("429 Too Many Request", {
status: 429,
});
}
}
await next();
};
export default rateLimitMiddleware;

View File

@ -1,3 +0,0 @@
quantile
quntile