2024-09-19 00:41:40 +01:00
|
|
|
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
|
|
|
import { Console } from "hsconsole";
|
|
|
|
import Session from "../objects/Session";
|
|
|
|
import SessionUser from "../objects/SessionUser";
|
|
|
|
import RequestCtx from "../objects/RequestCtx";
|
2024-09-26 00:47:08 +01:00
|
|
|
import { UserLevel } from "../enums/UserLevel";
|
2024-09-19 00:41:40 +01:00
|
|
|
|
|
|
|
// prepare for ts-ignore :3
|
|
|
|
// TODO: figure out some runtime field / type checking so
|
|
|
|
// can auto badRequest on missing stuff.
|
|
|
|
export default abstract class Controller {
|
|
|
|
public static FastifyInstance:FastifyInstance;
|
|
|
|
|
|
|
|
public constructor() {
|
|
|
|
const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(this));
|
2024-09-26 00:47:08 +01:00
|
|
|
const rawControllerParts = this.constructor.name.split("_");
|
|
|
|
const controllerName = rawControllerParts.splice(0, 1)[0].replace("Controller", "").toLowerCase();
|
|
|
|
let controllerAuthLevel: UserLevel | undefined;
|
|
|
|
|
|
|
|
for (const prop of rawControllerParts) {
|
|
|
|
if (prop.startsWith("Auth")) {
|
|
|
|
const userLevel = prop.split("$")[1];
|
|
|
|
// @ts-ignore
|
|
|
|
controllerAuthLevel = UserLevel[userLevel];
|
|
|
|
Console.printInfo(`Set Auth level requirement for ${this.constructor.name} to ${userLevel}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-19 00:41:40 +01:00
|
|
|
for (const method of methods) {
|
|
|
|
if (method === "constructor" || method[0] !== method[0].toUpperCase()) { // * Anything that starts with lowercase we'll consider "private"
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const params = method.split("_");
|
|
|
|
const methodNameRaw = params.splice(0, 1)[0]
|
|
|
|
const methodName = methodNameRaw.toLowerCase();
|
|
|
|
const doAuth = !params.includes("AllowAnonymous");
|
|
|
|
|
2024-09-26 00:47:08 +01:00
|
|
|
|
2024-09-19 00:41:40 +01:00
|
|
|
// @ts-ignore
|
|
|
|
const controllerRequestHandler = this[method];
|
|
|
|
const requestHandler = (req:FastifyRequest, res:FastifyReply) => {
|
|
|
|
let session = Session.CheckValiditiy(req.cookies);
|
|
|
|
if (doAuth && session === undefined) {
|
|
|
|
return res.redirect(`/account/login?returnTo=${encodeURIComponent(req.url)}`);
|
|
|
|
}
|
2024-09-26 00:47:08 +01:00
|
|
|
if (session !== undefined && controllerAuthLevel !== undefined && controllerAuthLevel !== session.userLevel) {
|
|
|
|
return res.status(403).send("Forbidden");
|
|
|
|
}
|
2024-09-19 00:41:40 +01:00
|
|
|
|
|
|
|
const requestCtx = new RequestCtx(req, res, controllerName, methodName, session);
|
|
|
|
controllerRequestHandler.bind(requestCtx)(req.method === "GET" ? req.query : req.body);
|
|
|
|
}
|
|
|
|
|
|
|
|
let funcMethods:Array<string> = [];
|
|
|
|
for (const param of params) {
|
|
|
|
if (param === "Get" || param === "Post" || param === "Put") {
|
|
|
|
funcMethods.push(param);
|
|
|
|
// @ts-ignore
|
|
|
|
Controller.FastifyInstance[param.toLowerCase()](`/${controllerName}/${methodName === "index" ? "" : methodName}`, requestHandler);
|
|
|
|
Console.printInfo(`Registered ${this.constructor.name}.${method} to "/${controllerName}/${methodName === "index" ? "" : methodName}" as ${param}`);
|
|
|
|
if (methodName === "index") {
|
|
|
|
// @ts-ignore
|
|
|
|
Controller.FastifyInstance[param.toLowerCase()](`/${controllerName}/${methodName}`, requestHandler);
|
|
|
|
Console.printInfo(`Registered ${this.constructor.name}.${method} to "/${controllerName}/${methodName}" as ${param}`);
|
|
|
|
// @ts-ignore
|
|
|
|
Controller.FastifyInstance[param.toLowerCase()](`/${controllerName}`, requestHandler);
|
|
|
|
Console.printInfo(`Registered ${this.constructor.name}.${method} to "/${controllerName}" as ${param}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (controllerName === "home" && methodName === "index") {
|
|
|
|
for (const httpMethod of funcMethods) {
|
|
|
|
Console.printInfo(`Registered ${this.constructor.name}.${method} to "/" as ${httpMethod}`);
|
|
|
|
// @ts-ignore
|
|
|
|
Controller.FastifyInstance[httpMethod.toLowerCase()](`/`, requestHandler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// not real, these RequestCtx so they autocomplete :)
|
|
|
|
// yeah, i know. this is terrible.
|
|
|
|
|
|
|
|
// Fields
|
|
|
|
// @ts-ignore
|
|
|
|
public session:SessionUser;
|
|
|
|
// @ts-ignore
|
|
|
|
public req: FastifyRequest;
|
|
|
|
// @ts-ignore
|
|
|
|
public res: FastifyReply;
|
|
|
|
|
|
|
|
// Methods
|
|
|
|
view(view?:string | Object, model?: Object) {}
|
|
|
|
redirectToAction(action:string, controller?:string) {}
|
|
|
|
ok(message?:string) {}
|
|
|
|
badRequest(message?:string) {}
|
|
|
|
unauthorised(message?:string) {}
|
2024-09-26 00:47:08 +01:00
|
|
|
forbidden(message?:string) {}
|
2024-09-19 00:41:40 +01:00
|
|
|
}
|