summary refs log tree commit diff
path: root/src/api/middlewares
diff options
context:
space:
mode:
authorMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2022-09-26 22:29:30 +1000
committerMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2022-09-26 22:41:21 +1000
commit99ee7e9400f06e8718612d8b52d15215dc620774 (patch)
tree08de8c5d3985b9c2eaa419f5198f891ecd82d012 /src/api/middlewares
parentRemove the cdn storage location log (diff)
downloadserver-99ee7e9400f06e8718612d8b52d15215dc620774.tar.xz
Prettier
Diffstat (limited to 'src/api/middlewares')
-rw-r--r--src/api/middlewares/Authentication.ts18
-rw-r--r--src/api/middlewares/BodyParser.ts3
-rw-r--r--src/api/middlewares/CORS.ts12
-rw-r--r--src/api/middlewares/ErrorHandler.ts25
-rw-r--r--src/api/middlewares/RateLimit.ts70
-rw-r--r--src/api/middlewares/Translation.ts14
6 files changed, 107 insertions, 35 deletions
diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts
index 1df7911b..50048b12 100644
--- a/src/api/middlewares/Authentication.ts
+++ b/src/api/middlewares/Authentication.ts
@@ -10,7 +10,7 @@ export const NO_AUTHORIZATION_ROUTES = [
 	"/auth/mfa/totp",
 	// Routes with a seperate auth system
 	"/webhooks/",
-	// Public information endpoints 
+	// Public information endpoints
 	"/ping",
 	"/gateway",
 	"/experiments",
@@ -26,7 +26,7 @@ export const NO_AUTHORIZATION_ROUTES = [
 	// Public policy pages
 	"/policies/instance",
 	// Asset delivery
-	/\/guilds\/\d+\/widget\.(json|png)/
+	/\/guilds\/\d+\/widget\.(json|png)/,
 ];
 
 export const API_PREFIX = /^\/api(\/v\d+)?/;
@@ -43,7 +43,11 @@ declare global {
 	}
 }
 
-export async function Authentication(req: Request, res: Response, next: NextFunction) {
+export async function Authentication(
+	req: Request,
+	res: Response,
+	next: NextFunction,
+) {
 	if (req.method === "OPTIONS") return res.sendStatus(204);
 	const url = req.url.replace(API_PREFIX, "");
 	if (url.startsWith("/invites") && req.method === "GET") return next();
@@ -54,12 +58,16 @@ export async function Authentication(req: Request, res: Response, next: NextFunc
 		})
 	)
 		return next();
-	if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401));
+	if (!req.headers.authorization)
+		return next(new HTTPError("Missing Authorization Header", 401));
 
 	try {
 		const { jwtSecret } = Config.get().security;
 
-		const { decoded, user }: any = await checkToken(req.headers.authorization, jwtSecret);
+		const { decoded, user }: any = await checkToken(
+			req.headers.authorization,
+			jwtSecret,
+		);
 
 		req.token = decoded;
 		req.user_id = decoded.id;
diff --git a/src/api/middlewares/BodyParser.ts b/src/api/middlewares/BodyParser.ts
index 4cb376bc..7741f1fd 100644
--- a/src/api/middlewares/BodyParser.ts
+++ b/src/api/middlewares/BodyParser.ts
@@ -6,7 +6,8 @@ export function BodyParser(opts?: OptionsJson) {
 	const jsonParser = bodyParser.json(opts);
 
 	return (req: Request, res: Response, next: NextFunction) => {
-		if (!req.headers["content-type"]) req.headers["content-type"] = "application/json";
+		if (!req.headers["content-type"])
+			req.headers["content-type"] = "application/json";
 
 		jsonParser(req, res, (err) => {
 			if (err) {
diff --git a/src/api/middlewares/CORS.ts b/src/api/middlewares/CORS.ts
index 20260cf9..2dce51c6 100644
--- a/src/api/middlewares/CORS.ts
+++ b/src/api/middlewares/CORS.ts
@@ -7,10 +7,16 @@ export function CORS(req: Request, res: Response, next: NextFunction) {
 	// TODO: use better CSP
 	res.set(
 		"Content-security-policy",
-		"default-src *  data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';"
+		"default-src *  data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';",
+	);
+	res.set(
+		"Access-Control-Allow-Headers",
+		req.header("Access-Control-Request-Headers") || "*",
+	);
+	res.set(
+		"Access-Control-Allow-Methods",
+		req.header("Access-Control-Request-Methods") || "*",
 	);
-	res.set("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers") || "*");
-	res.set("Access-Control-Allow-Methods", req.header("Access-Control-Request-Methods") || "*");
 
 	next();
 }
diff --git a/src/api/middlewares/ErrorHandler.ts b/src/api/middlewares/ErrorHandler.ts
index 2012b91c..bf3b011a 100644
--- a/src/api/middlewares/ErrorHandler.ts
+++ b/src/api/middlewares/ErrorHandler.ts
@@ -3,7 +3,12 @@ import { HTTPError } from "lambert-server";
 import { ApiError, FieldError } from "@fosscord/util";
 const EntityNotFoundErrorRegex = /"(\w+)"/;
 
-export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) {
+export function ErrorHandler(
+	error: Error,
+	req: Request,
+	res: Response,
+	next: NextFunction,
+) {
 	if (!error) return next();
 
 	try {
@@ -12,20 +17,28 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne
 		let message = error?.toString();
 		let errors = undefined;
 
-		if (error instanceof HTTPError && error.code) code = httpcode = error.code;
+		if (error instanceof HTTPError && error.code)
+			code = httpcode = error.code;
 		else if (error instanceof ApiError) {
 			code = error.code;
 			message = error.message;
 			httpcode = error.httpStatus;
 		} else if (error.name === "EntityNotFoundError") {
-			message = `${error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item"} could not be found`;
+			message = `${
+				error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item"
+			} could not be found`;
 			code = httpcode = 404;
 		} else if (error instanceof FieldError) {
 			code = Number(error.code);
 			message = error.message;
 			errors = error.errors;
 		} else {
-			console.error(`[Error] ${code} ${req.url}\n`, errors || error, "\nbody:", req.body);
+			console.error(
+				`[Error] ${code} ${req.url}\n`,
+				errors || error,
+				"\nbody:",
+				req.body,
+			);
 
 			if (req.server?.options?.production) {
 				// don't expose internal errors to the user, instead human errors should be thrown as HTTPError
@@ -39,6 +52,8 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne
 		res.status(httpcode).json({ code: code, message, errors });
 	} catch (error) {
 		console.error(`[Internal Server Error] 500`, error);
-		return res.status(500).json({ code: 500, message: "Internal Server Error" });
+		return res
+			.status(500)
+			.json({ code: 500, message: "Internal Server Error" });
 	}
 }
diff --git a/src/api/middlewares/RateLimit.ts b/src/api/middlewares/RateLimit.ts
index 57645c0b..b3976a16 100644
--- a/src/api/middlewares/RateLimit.ts
+++ b/src/api/middlewares/RateLimit.ts
@@ -40,21 +40,32 @@ export default function rateLimit(opts: {
 	success?: boolean;
 	onlyIp?: boolean;
 }): any {
-	return async (req: Request, res: Response, next: NextFunction): Promise<any> => {
+	return async (
+		req: Request,
+		res: Response,
+		next: NextFunction,
+	): Promise<any> => {
 		// exempt user? if so, immediately short circuit
 		if (req.user_id) {
 			const rights = await getRights(req.user_id);
 			if (rights.has("BYPASS_RATE_LIMITS")) return next();
 		}
 
-		const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, "");
+		const bucket_id =
+			opts.bucket ||
+			req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, "");
 		let executor_id = getIpAdress(req);
 		if (!opts.onlyIp && req.user_id) executor_id = req.user_id;
 
 		let max_hits = opts.count;
 		if (opts.bot && req.user_bot) max_hits = opts.bot;
-		if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) max_hits = opts.GET;
-		else if (opts.MODIFY && ["POST", "DELETE", "PATCH", "PUT"].includes(req.method)) max_hits = opts.MODIFY;
+		if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method))
+			max_hits = opts.GET;
+		else if (
+			opts.MODIFY &&
+			["POST", "DELETE", "PATCH", "PUT"].includes(req.method)
+		)
+			max_hits = opts.MODIFY;
 
 		let offender = Cache.get(executor_id + bucket_id);
 
@@ -75,11 +86,15 @@ export default function rateLimit(opts: {
 				const global = bucket_id === "global";
 				// each block violation pushes the expiry one full window further
 				reset += opts.window * 1000;
-				offender.expires_at = new Date(offender.expires_at.getTime() + opts.window * 1000);
+				offender.expires_at = new Date(
+					offender.expires_at.getTime() + opts.window * 1000,
+				);
 				resetAfterMs = reset - Date.now();
 				resetAfterSec = Math.ceil(resetAfterMs / 1000);
 
-				console.log(`blocked bucket: ${bucket_id} ${executor_id}`, { resetAfterMs });
+				console.log(`blocked bucket: ${bucket_id} ${executor_id}`, {
+					resetAfterMs,
+				});
 				return (
 					res
 						.status(429)
@@ -91,20 +106,33 @@ export default function rateLimit(opts: {
 						.set("Retry-After", `${Math.ceil(resetAfterSec)}`)
 						.set("X-RateLimit-Bucket", `${bucket_id}`)
 						// TODO: error rate limit message translation
-						.send({ message: "You are being rate limited.", retry_after: resetAfterSec, global })
+						.send({
+							message: "You are being rate limited.",
+							retry_after: resetAfterSec,
+							global,
+						})
 				);
 			}
 		}
 
 		next();
-		const hitRouteOpts = { bucket_id, executor_id, max_hits, window: opts.window };
+		const hitRouteOpts = {
+			bucket_id,
+			executor_id,
+			max_hits,
+			window: opts.window,
+		};
 
 		if (opts.error || opts.success) {
 			res.once("finish", () => {
 				// check if error and increment error rate limit
 				if (res.statusCode >= 400 && opts.error) {
 					return hitRoute(hitRouteOpts);
-				} else if (res.statusCode >= 200 && res.statusCode < 300 && opts.success) {
+				} else if (
+					res.statusCode >= 200 &&
+					res.statusCode < 300 &&
+					opts.success
+				) {
 					return hitRoute(hitRouteOpts);
 				}
 			});
@@ -141,8 +169,8 @@ export async function initRateLimits(app: Router) {
 		rateLimit({
 			bucket: "global",
 			onlyIp: true,
-			...ip
-		})
+			...ip,
+		}),
 	);
 	app.use(rateLimit({ bucket: "global", ...global }));
 	app.use(
@@ -150,17 +178,25 @@ export async function initRateLimits(app: Router) {
 			bucket: "error",
 			error: true,
 			onlyIp: true,
-			...error
-		})
+			...error,
+		}),
 	);
 	app.use("/guilds/:id", rateLimit(routes.guild));
 	app.use("/webhooks/:id", rateLimit(routes.webhook));
 	app.use("/channels/:id", rateLimit(routes.channel));
 	app.use("/auth/login", rateLimit(routes.auth.login));
-	app.use("/auth/register", rateLimit({ onlyIp: true, success: true, ...routes.auth.register }));
+	app.use(
+		"/auth/register",
+		rateLimit({ onlyIp: true, success: true, ...routes.auth.register }),
+	);
 }
 
-async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number; }) {
+async function hitRoute(opts: {
+	executor_id: string;
+	bucket_id: string;
+	max_hits: number;
+	window: number;
+}) {
 	const id = opts.executor_id + opts.bucket_id;
 	let limit = Cache.get(id);
 	if (!limit) {
@@ -169,7 +205,7 @@ async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits
 			executor_id: opts.executor_id,
 			expires_at: new Date(Date.now() + opts.window * 1000),
 			hits: 0,
-			blocked: false
+			blocked: false,
 		};
 		Cache.set(id, limit);
 	}
@@ -205,4 +241,4 @@ async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits
 	}
 	await ratelimit.save();
 	*/
-}
\ No newline at end of file
+}
diff --git a/src/api/middlewares/Translation.ts b/src/api/middlewares/Translation.ts
index c0b7a4b8..05038040 100644
--- a/src/api/middlewares/Translation.ts
+++ b/src/api/middlewares/Translation.ts
@@ -9,8 +9,12 @@ const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "assets");
 
 export async function initTranslation(router: Router) {
 	const languages = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "locales"));
-	const namespaces = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "locales", "en"));
-	const ns = namespaces.filter((x) => x.endsWith(".json")).map((x) => x.slice(0, x.length - 5));
+	const namespaces = fs.readdirSync(
+		path.join(ASSET_FOLDER_PATH, "locales", "en"),
+	);
+	const ns = namespaces
+		.filter((x) => x.endsWith(".json"))
+		.map((x) => x.slice(0, x.length - 5));
 
 	await i18next
 		.use(i18nextBackend)
@@ -21,9 +25,11 @@ export async function initTranslation(router: Router) {
 			fallbackLng: "en",
 			ns,
 			backend: {
-				loadPath:  path.join(ASSET_FOLDER_PATH, "locales") + "/{{lng}}/{{ns}}.json",
+				loadPath:
+					path.join(ASSET_FOLDER_PATH, "locales") +
+					"/{{lng}}/{{ns}}.json",
 			},
-			load: "all"
+			load: "all",
 		});
 
 	router.use(i18nextMiddleware.handle(i18next, {}));