summary refs log tree commit diff
path: root/src/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/api')
-rw-r--r--src/api/Server.ts17
-rw-r--r--src/api/middlewares/Authentication.ts10
-rw-r--r--src/api/middlewares/RateLimit.ts12
-rw-r--r--src/api/routes/applications/#id/bot/index.ts6
-rw-r--r--src/api/routes/applications/#id/index.ts2
-rw-r--r--src/api/routes/applications/#id/skus.ts1
-rw-r--r--src/api/routes/applications/index.ts2
-rw-r--r--src/api/routes/auth/generate-registration-tokens.ts2
-rw-r--r--src/api/routes/auth/login.ts2
-rw-r--r--src/api/routes/auth/mfa/totp.ts6
-rw-r--r--src/api/routes/auth/register.ts10
-rw-r--r--src/api/routes/channels/#channel_id/followers.ts2
-rw-r--r--src/api/routes/channels/#channel_id/index.ts2
-rw-r--r--src/api/routes/channels/#channel_id/invites.ts1
-rw-r--r--src/api/routes/channels/#channel_id/messages/#message_id/index.ts13
-rw-r--r--src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts7
-rw-r--r--src/api/routes/channels/#channel_id/messages/bulk-delete.ts2
-rw-r--r--src/api/routes/channels/#channel_id/messages/index.ts65
-rw-r--r--src/api/routes/channels/#channel_id/permissions.ts18
-rw-r--r--src/api/routes/channels/#channel_id/pins.ts8
-rw-r--r--src/api/routes/channels/#channel_id/purge.ts8
-rw-r--r--src/api/routes/channels/#channel_id/recipients.ts8
-rw-r--r--src/api/routes/channels/#channel_id/webhooks.ts4
-rw-r--r--src/api/routes/discoverable-guilds.ts4
-rw-r--r--src/api/routes/discovery.ts3
-rw-r--r--src/api/routes/download.ts (renamed from src/api/routes/download/index.ts)5
-rw-r--r--src/api/routes/gifs/search.ts2
-rw-r--r--src/api/routes/gifs/trending-gifs.ts2
-rw-r--r--src/api/routes/gifs/trending.ts61
-rw-r--r--src/api/routes/guild-recommendations.ts7
-rw-r--r--src/api/routes/guilds/#guild_id/bans.ts12
-rw-r--r--src/api/routes/guilds/#guild_id/channels.ts2
-rw-r--r--src/api/routes/guilds/#guild_id/delete.ts14
-rw-r--r--src/api/routes/guilds/#guild_id/discovery-requirements.ts2
-rw-r--r--src/api/routes/guilds/#guild_id/index.ts12
-rw-r--r--src/api/routes/guilds/#guild_id/invites.ts2
-rw-r--r--src/api/routes/guilds/#guild_id/members/#member_id/index.ts18
-rw-r--r--src/api/routes/guilds/#guild_id/members/#member_id/nick.ts12
-rw-r--r--src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts2
-rw-r--r--src/api/routes/guilds/#guild_id/members/index.ts2
-rw-r--r--src/api/routes/guilds/#guild_id/messages/search.ts18
-rw-r--r--src/api/routes/guilds/#guild_id/profile/index.ts5
-rw-r--r--src/api/routes/guilds/#guild_id/prune.ts10
-rw-r--r--src/api/routes/guilds/#guild_id/regions.ts5
-rw-r--r--src/api/routes/guilds/#guild_id/roles/#role_id/index.ts3
-rw-r--r--src/api/routes/guilds/#guild_id/roles/index.ts3
-rw-r--r--src/api/routes/guilds/#guild_id/templates.ts6
-rw-r--r--src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts5
-rw-r--r--src/api/routes/guilds/#guild_id/widget.json.ts19
-rw-r--r--src/api/routes/guilds/#guild_id/widget.png.ts5
-rw-r--r--src/api/routes/guilds/index.ts2
-rw-r--r--src/api/routes/guilds/templates/index.ts6
-rw-r--r--src/api/routes/oauth2/authorize.ts12
-rw-r--r--src/api/routes/partners/#guild_id/requirements.ts2
-rw-r--r--src/api/routes/policies/instance/domains.ts1
-rw-r--r--src/api/routes/store/published-listings/applications.ts2
-rw-r--r--src/api/routes/store/published-listings/skus.ts2
-rw-r--r--src/api/routes/updates.ts3
-rw-r--r--src/api/routes/users/#id/delete.ts3
-rw-r--r--src/api/routes/users/#id/profile.ts9
-rw-r--r--src/api/routes/users/#id/relationships.ts4
-rw-r--r--src/api/routes/users/@me/delete.ts2
-rw-r--r--src/api/routes/users/@me/guilds/#guild_id/settings.ts2
-rw-r--r--src/api/routes/users/@me/index.ts7
-rw-r--r--src/api/routes/users/@me/mfa/codes-verification.ts10
-rw-r--r--src/api/routes/users/@me/mfa/codes.ts2
-rw-r--r--src/api/routes/users/@me/mfa/totp/disable.ts2
-rw-r--r--src/api/routes/users/@me/mfa/totp/enable.ts2
-rw-r--r--src/api/routes/users/@me/relationships.ts6
-rw-r--r--src/api/routes/users/@me/settings.ts2
-rw-r--r--src/api/start.ts7
-rw-r--r--src/api/util/handlers/Instance.ts17
-rw-r--r--src/api/util/handlers/Message.ts34
-rw-r--r--src/api/util/handlers/Voice.ts2
-rw-r--r--src/api/util/handlers/route.ts6
-rw-r--r--src/api/util/utility/EmbedHandlers.ts38
-rw-r--r--src/api/util/utility/RandomInviteID.ts8
-rw-r--r--src/api/util/utility/captcha.ts11
-rw-r--r--src/api/util/utility/ipAddress.ts11
-rw-r--r--src/api/util/utility/passwordStrength.ts8
80 files changed, 360 insertions, 312 deletions
diff --git a/src/api/Server.ts b/src/api/Server.ts
index 4660e6b1..0177be40 100644
--- a/src/api/Server.ts
+++ b/src/api/Server.ts
@@ -22,7 +22,7 @@ import { Authentication, CORS } from "./middlewares/";
 import { Config, initDatabase, initEvent, Sentry } from "@fosscord/util";
 import { ErrorHandler } from "./middlewares/ErrorHandler";
 import { BodyParser } from "./middlewares/BodyParser";
-import { Router, Request, Response, NextFunction } from "express";
+import { Router, Request, Response } from "express";
 import path from "path";
 import { initRateLimits } from "./middlewares/RateLimit";
 import TestClient from "./middlewares/TestClient";
@@ -32,12 +32,12 @@ import { initInstance } from "./util/handlers/Instance";
 import { registerRoutes } from "@fosscord/util";
 import { red } from "picocolors";
 
-export interface FosscordServerOptions extends ServerOptions {}
+export type FosscordServerOptions = ServerOptions;
 
 declare global {
+	// eslint-disable-next-line @typescript-eslint/no-namespace
 	namespace Express {
 		interface Request {
-			// @ts-ignore
 			server: FosscordServer;
 		}
 	}
@@ -47,6 +47,7 @@ export class FosscordServer extends Server {
 	public declare options: FosscordServerOptions;
 
 	constructor(opts?: Partial<FosscordServerOptions>) {
+		// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 		// @ts-ignore
 		super({ ...opts, errorHandler: false, jsonBody: false });
 	}
@@ -58,12 +59,12 @@ export class FosscordServer extends Server {
 		await initInstance();
 		await Sentry.init(this.app);
 
-		let logRequests = process.env["LOG_REQUESTS"] != undefined;
+		const logRequests = process.env["LOG_REQUESTS"] != undefined;
 		if (logRequests) {
 			this.app.use(
 				morgan("combined", {
 					skip: (req, res) => {
-						var skip = !(
+						let skip = !(
 							process.env["LOG_REQUESTS"]?.includes(
 								res.statusCode.toString(),
 							) ?? false
@@ -80,7 +81,9 @@ export class FosscordServer extends Server {
 		this.app.use(BodyParser({ inflate: true, limit: "10mb" }));
 
 		const app = this.app;
-		const api = Router(); // @ts-ignore
+		const api = Router();
+		// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+		// @ts-ignore
 		this.app = api;
 
 		api.use(Authentication);
@@ -95,7 +98,7 @@ export class FosscordServer extends Server {
 		// 404 is not an error in express, so this should not be an error middleware
 		// this is a fine place to put the 404 handler because its after we register the routes
 		// and since its not an error middleware, our error handler below still works.
-		api.use("*", (req: Request, res: Response, next: NextFunction) => {
+		api.use("*", (req: Request, res: Response) => {
 			res.status(404).json({
 				message: "404 endpoint not found",
 				code: 0,
diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts
index 208c54d6..8e0dcc7c 100644
--- a/src/api/middlewares/Authentication.ts
+++ b/src/api/middlewares/Authentication.ts
@@ -54,11 +54,12 @@ export const API_PREFIX = /^\/api(\/v\d+)?/;
 export const API_PREFIX_TRAILING_SLASH = /^\/api(\/v\d+)?\//;
 
 declare global {
+	// eslint-disable-next-line @typescript-eslint/no-namespace
 	namespace Express {
 		interface Request {
 			user_id: string;
 			user_bot: boolean;
-			token: string;
+			token: { id: string; iat: number };
 			rights: Rights;
 		}
 	}
@@ -87,7 +88,7 @@ export async function Authentication(
 	try {
 		const { jwtSecret } = Config.get().security;
 
-		const { decoded, user }: any = await checkToken(
+		const { decoded, user } = await checkToken(
 			req.headers.authorization,
 			jwtSecret,
 		);
@@ -97,7 +98,8 @@ export async function Authentication(
 		req.user_bot = user.bot;
 		req.rights = new Rights(Number(user.rights));
 		return next();
-	} catch (error: any) {
-		return next(new HTTPError(error?.toString(), 400));
+	} catch (error) {
+		// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+		return next(new HTTPError(error!.toString(), 400));
 	}
 }
diff --git a/src/api/middlewares/RateLimit.ts b/src/api/middlewares/RateLimit.ts
index ae102c94..1a28f356 100644
--- a/src/api/middlewares/RateLimit.ts
+++ b/src/api/middlewares/RateLimit.ts
@@ -42,7 +42,7 @@ type RateLimit = {
 	expires_at: Date;
 };
 
-let Cache = new Map<string, RateLimit>();
+const Cache = new Map<string, RateLimit>();
 const EventRateLimit = "RATELIMIT";
 
 export default function rateLimit(opts: {
@@ -57,12 +57,8 @@ export default function rateLimit(opts: {
 	error?: boolean;
 	success?: boolean;
 	onlyIp?: boolean;
-}): any {
-	return async (
-		req: Request,
-		res: Response,
-		next: NextFunction,
-	): Promise<any> => {
+}) {
+	return async (req: Request, res: Response, next: NextFunction) => {
 		// exempt user? if so, immediately short circuit
 		if (req.user_id) {
 			const rights = await getRights(req.user_id);
@@ -85,7 +81,7 @@ export default function rateLimit(opts: {
 		)
 			max_hits = opts.MODIFY;
 
-		let offender = Cache.get(executor_id + bucket_id);
+		const offender = Cache.get(executor_id + bucket_id);
 
 		if (offender) {
 			let reset = offender.expires_at.getTime();
diff --git a/src/api/routes/applications/#id/bot/index.ts b/src/api/routes/applications/#id/bot/index.ts
index 7d0a637e..9bc3c571 100644
--- a/src/api/routes/applications/#id/bot/index.ts
+++ b/src/api/routes/applications/#id/bot/index.ts
@@ -64,8 +64,8 @@ router.post("/", route({}), async (req: Request, res: Response) => {
 });
 
 router.post("/reset", route({}), async (req: Request, res: Response) => {
-	let bot = await User.findOneOrFail({ where: { id: req.params.id } });
-	let owner = await User.findOneOrFail({ where: { id: req.user_id } });
+	const bot = await User.findOneOrFail({ where: { id: req.params.id } });
+	const owner = await User.findOneOrFail({ where: { id: req.user_id } });
 
 	if (owner.id != req.user_id)
 		throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
@@ -80,7 +80,7 @@ router.post("/reset", route({}), async (req: Request, res: Response) => {
 
 	await bot.save();
 
-	let token = await generateToken(bot.id);
+	const token = await generateToken(bot.id);
 
 	res.json({ token }).status(200);
 });
diff --git a/src/api/routes/applications/#id/index.ts b/src/api/routes/applications/#id/index.ts
index 2b283880..59e90168 100644
--- a/src/api/routes/applications/#id/index.ts
+++ b/src/api/routes/applications/#id/index.ts
@@ -20,10 +20,8 @@ import { Request, Response, Router } from "express";
 import { route } from "@fosscord/api";
 import {
 	Application,
-	OrmUtils,
 	DiscordApiErrors,
 	ApplicationModifySchema,
-	User,
 } from "@fosscord/util";
 import { verifyToken } from "node-2fa";
 import { HTTPError } from "lambert-server";
diff --git a/src/api/routes/applications/#id/skus.ts b/src/api/routes/applications/#id/skus.ts
index 23e6eb6b..5a3a479f 100644
--- a/src/api/routes/applications/#id/skus.ts
+++ b/src/api/routes/applications/#id/skus.ts
@@ -18,7 +18,6 @@
 
 import { Request, Response, Router } from "express";
 import { route } from "@fosscord/api";
-import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util";
 
 const router: Router = Router();
 
diff --git a/src/api/routes/applications/index.ts b/src/api/routes/applications/index.ts
index 6ea24870..859ee145 100644
--- a/src/api/routes/applications/index.ts
+++ b/src/api/routes/applications/index.ts
@@ -28,7 +28,7 @@ import {
 const router: Router = Router();
 
 router.get("/", route({}), async (req: Request, res: Response) => {
-	let results = await Application.find({
+	const results = await Application.find({
 		where: { owner: { id: req.user_id } },
 		relations: ["owner", "bot"],
 	});
diff --git a/src/api/routes/auth/generate-registration-tokens.ts b/src/api/routes/auth/generate-registration-tokens.ts
index 64e3b0a6..c79d2a59 100644
--- a/src/api/routes/auth/generate-registration-tokens.ts
+++ b/src/api/routes/auth/generate-registration-tokens.ts
@@ -32,7 +32,7 @@ router.get(
 			? parseInt(req.query.length as string)
 			: 255;
 
-		let tokens: ValidRegistrationToken[] = [];
+		const tokens: ValidRegistrationToken[] = [];
 
 		for (let i = 0; i < count; i++) {
 			const token = ValidRegistrationToken.create({
diff --git a/src/api/routes/auth/login.ts b/src/api/routes/auth/login.ts
index 5f1b7a14..4d367546 100644
--- a/src/api/routes/auth/login.ts
+++ b/src/api/routes/auth/login.ts
@@ -74,7 +74,7 @@ router.post(
 				"totp_secret",
 				"mfa_enabled",
 			],
-		}).catch((e) => {
+		}).catch(() => {
 			throw FieldErrors({
 				login: {
 					message: req.t("auth:login.INVALID_LOGIN"),
diff --git a/src/api/routes/auth/mfa/totp.ts b/src/api/routes/auth/mfa/totp.ts
index 42485535..65cdd397 100644
--- a/src/api/routes/auth/mfa/totp.ts
+++ b/src/api/routes/auth/mfa/totp.ts
@@ -27,8 +27,8 @@ router.post(
 	"/",
 	route({ body: "TotpSchema" }),
 	async (req: Request, res: Response) => {
-		const { code, ticket, gift_code_sku_id, login_source } =
-			req.body as TotpSchema;
+		// const { code, ticket, gift_code_sku_id, login_source } =
+		const { code, ticket } = req.body as TotpSchema;
 
 		const user = await User.findOneOrFail({
 			where: {
@@ -47,7 +47,7 @@ router.post(
 		});
 
 		if (!backup) {
-			const ret = verifyToken(user.totp_secret!, code);
+			const ret = verifyToken(user.totp_secret || "", code);
 			if (!ret || ret.delta != 0)
 				throw new HTTPError(
 					req.t("auth:login.INVALID_TOTP_CODE"),
diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts
index b98f17c5..0bf8efae 100644
--- a/src/api/routes/auth/register.ts
+++ b/src/api/routes/auth/register.ts
@@ -36,7 +36,7 @@ import {
 } from "@fosscord/api";
 import bcrypt from "bcrypt";
 import { HTTPError } from "lambert-server";
-import { LessThan, MoreThan } from "typeorm";
+import { MoreThan } from "typeorm";
 
 const router: Router = Router();
 
@@ -53,12 +53,12 @@ router.post(
 		let regTokenUsed = false;
 		if (req.get("Referrer") && req.get("Referrer")?.includes("token=")) {
 			// eg theyre on https://staging.fosscord.com/register?token=whatever
-			const token = req.get("Referrer")!.split("token=")[1].split("&")[0];
+			const token = req.get("Referrer")?.split("token=")[1].split("&")[0];
 			if (token) {
-				const regToken = await ValidRegistrationToken.findOne({
+				const regToken = await ValidRegistrationToken.findOneOrFail({
 					where: { token, expires_at: MoreThan(new Date()) },
 				});
-				await ValidRegistrationToken.delete({ token });
+				await regToken.remove();
 				regTokenUsed = true;
 				console.log(
 					`[REGISTER] Registration token ${token} used for registration!`,
@@ -71,7 +71,7 @@ router.post(
 		}
 
 		// email will be slightly modified version of the user supplied email -> e.g. protection against GMail Trick
-		let email = adjustEmail(body.email);
+		const email = adjustEmail(body.email);
 
 		// check if registration is allowed
 		if (!regTokenUsed && !register.allowNewRegistration) {
diff --git a/src/api/routes/channels/#channel_id/followers.ts b/src/api/routes/channels/#channel_id/followers.ts
index 0ff784df..a9d5d4ee 100644
--- a/src/api/routes/channels/#channel_id/followers.ts
+++ b/src/api/routes/channels/#channel_id/followers.ts
@@ -16,7 +16,7 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Response, Request } from "express";
+import { Router } from "express";
 const router: Router = Router();
 // TODO:
 
diff --git a/src/api/routes/channels/#channel_id/index.ts b/src/api/routes/channels/#channel_id/index.ts
index 5bcd3a84..4a2023d2 100644
--- a/src/api/routes/channels/#channel_id/index.ts
+++ b/src/api/routes/channels/#channel_id/index.ts
@@ -92,7 +92,7 @@ router.patch(
 	"/",
 	route({ body: "ChannelModifySchema", permission: "MANAGE_CHANNELS" }),
 	async (req: Request, res: Response) => {
-		var payload = req.body as ChannelModifySchema;
+		const payload = req.body as ChannelModifySchema;
 		const { channel_id } = req.params;
 		if (payload.icon)
 			payload.icon = await handleFile(
diff --git a/src/api/routes/channels/#channel_id/invites.ts b/src/api/routes/channels/#channel_id/invites.ts
index b9105bea..49620aaf 100644
--- a/src/api/routes/channels/#channel_id/invites.ts
+++ b/src/api/routes/channels/#channel_id/invites.ts
@@ -86,7 +86,6 @@ router.get(
 	"/",
 	route({ permission: "MANAGE_CHANNELS" }),
 	async (req: Request, res: Response) => {
-		const { user_id } = req;
 		const { channel_id } = req.params;
 		const channel = await Channel.findOneOrFail({
 			where: { id: channel_id },
diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts b/src/api/routes/channels/#channel_id/messages/#message_id/index.ts
index 3d9a69be..9ea33340 100644
--- a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts
+++ b/src/api/routes/channels/#channel_id/messages/#message_id/index.ts
@@ -30,7 +30,6 @@ import {
 	Snowflake,
 	uploadFile,
 	MessageCreateSchema,
-	DiscordApiErrors,
 } from "@fosscord/util";
 import { Router, Response, Request } from "express";
 import multer from "multer";
@@ -59,7 +58,7 @@ router.patch(
 	}),
 	async (req: Request, res: Response) => {
 		const { message_id, channel_id } = req.params;
-		var body = req.body as MessageCreateSchema;
+		let body = req.body as MessageCreateSchema;
 
 		const message = await Message.findOneOrFail({
 			where: { id: message_id, channel_id },
@@ -85,6 +84,7 @@ router.patch(
 		const new_message = await handleMessage({
 			...message,
 			// TODO: should message_reference be overridable?
+			// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 			// @ts-ignore
 			message_reference: message.message_reference,
 			...body,
@@ -127,7 +127,7 @@ router.put(
 	}),
 	async (req: Request, res: Response) => {
 		const { channel_id, message_id } = req.params;
-		var body = req.body as MessageCreateSchema;
+		const body = req.body as MessageCreateSchema;
 		const attachments: Attachment[] = [];
 
 		const rights = await getRights(req.user_id);
@@ -171,7 +171,7 @@ router.put(
 
 		const embeds = body.embeds || [];
 		if (body.embed) embeds.push(body.embed);
-		let message = await handleMessage({
+		const message = await handleMessage({
 			...body,
 			type: 0,
 			pinned: false,
@@ -197,7 +197,10 @@ router.put(
 			channel.save(),
 		]);
 
-		postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error
+		// no await as it shouldnt block the message send function and silently catch error
+		postHandleMessage(message).catch((e) =>
+			console.error("[Message] post-message handler failed", e),
+		);
 
 		return res.json(message);
 	},
diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts
index bf6d43e5..c3598b24 100644
--- a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts
+++ b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts
@@ -165,13 +165,13 @@ router.put(
 				x.emoji.name === emoji.name,
 		);
 
-		if (!already_added) req.permission!.hasThrow("ADD_REACTIONS");
+		if (!already_added) req.permission?.hasThrow("ADD_REACTIONS");
 
 		if (emoji.id) {
 			const external_emoji = await Emoji.findOneOrFail({
 				where: { id: emoji.id },
 			});
-			if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS");
+			if (!already_added) req.permission?.hasThrow("USE_EXTERNAL_EMOJIS");
 			emoji.animated = external_emoji.animated;
 			emoji.name = external_emoji.name;
 		}
@@ -214,7 +214,8 @@ router.delete(
 	"/:emoji/:user_id",
 	route({}),
 	async (req: Request, res: Response) => {
-		var { message_id, channel_id, user_id } = req.params;
+		let { user_id } = req.params;
+		const { message_id, channel_id } = req.params;
 
 		const emoji = getEmoji(req.params.emoji);
 
diff --git a/src/api/routes/channels/#channel_id/messages/bulk-delete.ts b/src/api/routes/channels/#channel_id/messages/bulk-delete.ts
index ad5d24c8..ee039d3e 100644
--- a/src/api/routes/channels/#channel_id/messages/bulk-delete.ts
+++ b/src/api/routes/channels/#channel_id/messages/bulk-delete.ts
@@ -50,7 +50,7 @@ router.post(
 		const rights = await getRights(req.user_id);
 		rights.hasThrow("SELF_DELETE_MESSAGES");
 
-		let superuser = rights.has("MANAGE_MESSAGES");
+		const superuser = rights.has("MANAGE_MESSAGES");
 		const permission = await getPermission(
 			req.user_id,
 			channel?.guild_id,
diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts
index 6e4f06a2..76f6a0dc 100644
--- a/src/api/routes/channels/#channel_id/messages/index.ts
+++ b/src/api/routes/channels/#channel_id/messages/index.ts
@@ -31,23 +31,16 @@ import {
 	Snowflake,
 	uploadFile,
 	Member,
-	Role,
 	MessageCreateSchema,
 	ReadState,
-	DiscordApiErrors,
-	getRights,
 	Rights,
+	Reaction,
+	User,
 } from "@fosscord/util";
 import { HTTPError } from "lambert-server";
-import {
-	handleMessage,
-	postHandleMessage,
-	route,
-	getIpAdress,
-} from "@fosscord/api";
+import { handleMessage, postHandleMessage, route } from "@fosscord/api";
 import multer from "multer";
-import { yellow } from "picocolors";
-import { FindManyOptions, LessThan, MoreThan } from "typeorm";
+import { FindManyOptions, FindOperator, LessThan, MoreThan } from "typeorm";
 import { URL } from "url";
 
 const router: Router = Router();
@@ -93,7 +86,7 @@ router.get("/", async (req: Request, res: Response) => {
 	if (limit < 1 || limit > 100)
 		throw new HTTPError("limit must be between 1 and 100", 422);
 
-	var halfLimit = Math.floor(limit / 2);
+	const halfLimit = Math.floor(limit / 2);
 
 	const permissions = await getPermission(
 		req.user_id,
@@ -103,7 +96,9 @@ router.get("/", async (req: Request, res: Response) => {
 	permissions.hasThrow("VIEW_CHANNEL");
 	if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
 
-	var query: FindManyOptions<Message> & { where: { id?: any } } = {
+	const query: FindManyOptions<Message> & {
+		where: { id?: FindOperator<string> | FindOperator<string>[] };
+	} = {
 		order: { timestamp: "DESC" },
 		take: limit,
 		where: { channel_id },
@@ -140,23 +135,21 @@ router.get("/", async (req: Request, res: Response) => {
 	const endpoint = Config.get().cdn.endpointPublic;
 
 	return res.json(
-		messages.map((x: any) => {
-			(x.reactions || []).forEach((x: any) => {
-				// @ts-ignore
-				if ((x.user_ids || []).includes(req.user_id)) x.me = true;
-				// @ts-ignore
-				delete x.user_ids;
+		messages.map((x: Partial<Message>) => {
+			(x.reactions || []).forEach((y: Partial<Reaction>) => {
+				// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+				//@ts-ignore
+				if ((y.user_ids || []).includes(req.user_id)) y.me = true;
+				delete y.user_ids;
 			});
-			// @ts-ignore
 			if (!x.author)
-				x.author = {
+				x.author = User.create({
 					id: "4",
 					discriminator: "0000",
 					username: "Fosscord Ghost",
-					public_flags: "0",
-					avatar: null,
-				};
-			x.attachments?.forEach((y: any) => {
+					public_flags: 0,
+				});
+			x.attachments?.forEach((y: Attachment) => {
 				// dynamically set attachment proxy_url in case the endpoint changed
 				const uri = y.proxy_url.startsWith("http")
 					? y.proxy_url
@@ -168,7 +161,7 @@ router.get("/", async (req: Request, res: Response) => {
 
 			/**
 			Some clients ( discord.js ) only check if a property exists within the response,
-			which causes erorrs when, say, the `application` property is `null`.
+			which causes errors when, say, the `application` property is `null`.
 			**/
 
 			// for (var curr in x) {
@@ -216,7 +209,7 @@ router.post(
 	}),
 	async (req: Request, res: Response) => {
 		const { channel_id } = req.params;
-		var body = req.body as MessageCreateSchema;
+		const body = req.body as MessageCreateSchema;
 		const attachments: Attachment[] = [];
 
 		const channel = await Channel.findOneOrFail({
@@ -244,7 +237,7 @@ router.post(
 		}
 
 		if (!req.rights.has(Rights.FLAGS.BYPASS_RATE_LIMITS)) {
-			var limits = Config.get().limits;
+			const limits = Config.get().limits;
 			if (limits.absoluteRate.register.enabled) {
 				const count = await Message.count({
 					where: {
@@ -269,7 +262,7 @@ router.post(
 		}
 
 		const files = (req.files as Express.Multer.File[]) ?? [];
-		for (var currFile of files) {
+		for (const currFile of files) {
 			try {
 				const file = await uploadFile(
 					`/attachments/${channel.id}`,
@@ -279,13 +272,13 @@ router.post(
 					Attachment.create({ ...file, proxy_url: file.url }),
 				);
 			} catch (error) {
-				return res.status(400).json({ message: error!.toString() });
+				return res.status(400).json({ message: error?.toString() });
 			}
 		}
 
 		const embeds = body.embeds || [];
 		if (body.embed) embeds.push(body.embed);
-		let message = await handleMessage({
+		const message = await handleMessage({
 			...body,
 			type: 0,
 			pinned: false,
@@ -304,7 +297,7 @@ router.post(
 
 			// Only one recipients should be closed here, since in group DMs the recipient is deleted not closed
 			await Promise.all(
-				channel.recipients!.map((recipient) => {
+				channel.recipients?.map((recipient) => {
 					if (recipient.closed) {
 						recipient.closed = false;
 						return Promise.all([
@@ -318,7 +311,7 @@ router.post(
 							}),
 						]);
 					}
-				}),
+				}) || [],
 			);
 		}
 
@@ -332,6 +325,7 @@ router.post(
 				});
 			}
 
+			// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 			//@ts-ignore
 			message.member.roles = message.member.roles
 				.filter((x) => x.id != x.guild_id)
@@ -362,7 +356,10 @@ router.post(
 			channel.save(),
 		]);
 
-		postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error
+		// no await as it shouldnt block the message send function and silently catch error
+		postHandleMessage(message).catch((e) =>
+			console.error("[Message] post-message handler failed", e),
+		);
 
 		return res.json(message);
 	},
diff --git a/src/api/routes/channels/#channel_id/permissions.ts b/src/api/routes/channels/#channel_id/permissions.ts
index 7aa29700..da448678 100644
--- a/src/api/routes/channels/#channel_id/permissions.ts
+++ b/src/api/routes/channels/#channel_id/permissions.ts
@@ -43,7 +43,7 @@ router.put(
 		const { channel_id, overwrite_id } = req.params;
 		const body = req.body as ChannelPermissionOverwriteSchema;
 
-		var channel = await Channel.findOneOrFail({
+		const channel = await Channel.findOneOrFail({
 			where: { id: channel_id },
 		});
 		if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
@@ -56,22 +56,24 @@ router.put(
 				throw new HTTPError("user not found", 404);
 		} else throw new HTTPError("type not supported", 501);
 
-		//@ts-ignore
-		var overwrite: ChannelPermissionOverwrite =
+		let overwrite: ChannelPermissionOverwrite | undefined =
 			channel.permission_overwrites?.find((x) => x.id === overwrite_id);
 		if (!overwrite) {
-			// @ts-ignore
 			overwrite = {
 				id: overwrite_id,
 				type: body.type,
+				allow: "0",
+				deny: "0",
 			};
-			channel.permission_overwrites!.push(overwrite);
+			channel.permission_overwrites?.push(overwrite);
 		}
 		overwrite.allow = String(
-			req.permission!.bitfield & (BigInt(body.allow) || BigInt("0")),
+			(req.permission?.bitfield || 0n) &
+				(BigInt(body.allow) || BigInt("0")),
 		);
 		overwrite.deny = String(
-			req.permission!.bitfield & (BigInt(body.deny) || BigInt("0")),
+			(req.permission?.bitfield || 0n) &
+				(BigInt(body.deny) || BigInt("0")),
 		);
 
 		await Promise.all([
@@ -99,7 +101,7 @@ router.delete(
 		});
 		if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
 
-		channel.permission_overwrites = channel.permission_overwrites!.filter(
+		channel.permission_overwrites = channel.permission_overwrites?.filter(
 			(x) => x.id === overwrite_id,
 		);
 
diff --git a/src/api/routes/channels/#channel_id/pins.ts b/src/api/routes/channels/#channel_id/pins.ts
index f48e0ff5..28419383 100644
--- a/src/api/routes/channels/#channel_id/pins.ts
+++ b/src/api/routes/channels/#channel_id/pins.ts
@@ -21,13 +21,11 @@ import {
 	ChannelPinsUpdateEvent,
 	Config,
 	emitEvent,
-	getPermission,
 	Message,
 	MessageUpdateEvent,
 	DiscordApiErrors,
 } from "@fosscord/util";
 import { Router, Request, Response } from "express";
-import { HTTPError } from "lambert-server";
 import { route } from "@fosscord/api";
 
 const router: Router = Router();
@@ -43,7 +41,7 @@ router.put(
 		});
 
 		// * in dm channels anyone can pin messages -> only check for guilds
-		if (message.guild_id) req.permission!.hasThrow("MANAGE_MESSAGES");
+		if (message.guild_id) req.permission?.hasThrow("MANAGE_MESSAGES");
 
 		const pinned_count = await Message.count({
 			where: { channel: { id: channel_id }, pinned: true },
@@ -83,7 +81,7 @@ router.delete(
 		const channel = await Channel.findOneOrFail({
 			where: { id: channel_id },
 		});
-		if (channel.guild_id) req.permission!.hasThrow("MANAGE_MESSAGES");
+		if (channel.guild_id) req.permission?.hasThrow("MANAGE_MESSAGES");
 
 		const message = await Message.findOneOrFail({
 			where: { id: message_id },
@@ -120,7 +118,7 @@ router.get(
 	async (req: Request, res: Response) => {
 		const { channel_id } = req.params;
 
-		let pins = await Message.find({
+		const pins = await Message.find({
 			where: { channel_id: channel_id, pinned: true },
 		});
 
diff --git a/src/api/routes/channels/#channel_id/purge.ts b/src/api/routes/channels/#channel_id/purge.ts
index 05660acf..04d8cfa2 100644
--- a/src/api/routes/channels/#channel_id/purge.ts
+++ b/src/api/routes/channels/#channel_id/purge.ts
@@ -19,10 +19,9 @@
 import { HTTPError } from "lambert-server";
 import { route } from "@fosscord/api";
 import { isTextChannel } from "./messages";
-import { FindManyOptions, Between, Not } from "typeorm";
+import { FindManyOptions, Between, Not, FindOperator } from "typeorm";
 import {
 	Channel,
-	Config,
 	emitEvent,
 	getPermission,
 	getRights,
@@ -69,7 +68,9 @@ router.post(
 
 		// TODO: send the deletion event bite-by-bite to prevent client stress
 
-		var query: FindManyOptions<Message> & { where: { id?: any } } = {
+		const query: FindManyOptions<Message> & {
+			where: { id?: FindOperator<string> };
+		} = {
 			order: { id: "ASC" },
 			// take: limit,
 			where: {
@@ -93,7 +94,6 @@ router.post(
 		};
 
 		const messages = await Message.find(query);
-		const endpoint = Config.get().cdn.endpointPublic;
 
 		if (messages.length == 0) {
 			res.sendStatus(304);
diff --git a/src/api/routes/channels/#channel_id/recipients.ts b/src/api/routes/channels/#channel_id/recipients.ts
index 6928dd34..252a8ef0 100644
--- a/src/api/routes/channels/#channel_id/recipients.ts
+++ b/src/api/routes/channels/#channel_id/recipients.ts
@@ -41,7 +41,7 @@ router.put("/:user_id", route({}), async (req: Request, res: Response) => {
 
 	if (channel.type !== ChannelType.GROUP_DM) {
 		const recipients = [
-			...channel.recipients!.map((r) => r.user_id),
+			...(channel.recipients?.map((r) => r.user_id) || []),
 			user_id,
 		].unique();
 
@@ -51,11 +51,11 @@ router.put("/:user_id", route({}), async (req: Request, res: Response) => {
 		);
 		return res.status(201).json(new_channel);
 	} else {
-		if (channel.recipients!.map((r) => r.user_id).includes(user_id)) {
+		if (channel.recipients?.map((r) => r.user_id).includes(user_id)) {
 			throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
 		}
 
-		channel.recipients!.push(
+		channel.recipients?.push(
 			Recipient.create({ channel_id: channel_id, user_id: user_id }),
 		);
 		await channel.save();
@@ -95,7 +95,7 @@ router.delete("/:user_id", route({}), async (req: Request, res: Response) => {
 	)
 		throw DiscordApiErrors.MISSING_PERMISSIONS;
 
-	if (!channel.recipients!.map((r) => r.user_id).includes(user_id)) {
+	if (!channel.recipients?.map((r) => r.user_id).includes(user_id)) {
 		throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
 	}
 
diff --git a/src/api/routes/channels/#channel_id/webhooks.ts b/src/api/routes/channels/#channel_id/webhooks.ts
index 511933c3..31cae747 100644
--- a/src/api/routes/channels/#channel_id/webhooks.ts
+++ b/src/api/routes/channels/#channel_id/webhooks.ts
@@ -55,10 +55,10 @@ router.post(
 
 		const webhook_count = await Webhook.count({ where: { channel_id } });
 		const { maxWebhooks } = Config.get().limits.channel;
-		if (webhook_count > maxWebhooks)
+		if (maxWebhooks && webhook_count > maxWebhooks)
 			throw DiscordApiErrors.MAXIMUM_WEBHOOKS.withParams(maxWebhooks);
 
-		var { avatar, name } = req.body as WebhookCreateSchema;
+		let { avatar, name } = req.body as WebhookCreateSchema;
 		name = trimSpecial(name);
 
 		// TODO: move this
diff --git a/src/api/routes/discoverable-guilds.ts b/src/api/routes/discoverable-guilds.ts
index 522861eb..8f90d73c 100644
--- a/src/api/routes/discoverable-guilds.ts
+++ b/src/api/routes/discoverable-guilds.ts
@@ -26,8 +26,8 @@ const router = Router();
 
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const { offset, limit, categories } = req.query;
-	var showAllGuilds = Config.get().guild.discovery.showAllGuilds;
-	var configLimit = Config.get().guild.discovery.limit;
+	const showAllGuilds = Config.get().guild.discovery.showAllGuilds;
+	const configLimit = Config.get().guild.discovery.limit;
 	let guilds;
 	if (categories == undefined) {
 		guilds = showAllGuilds
diff --git a/src/api/routes/discovery.ts b/src/api/routes/discovery.ts
index 411c3bc5..1414a617 100644
--- a/src/api/routes/discovery.ts
+++ b/src/api/routes/discovery.ts
@@ -26,7 +26,8 @@ router.get("/categories", route({}), async (req: Request, res: Response) => {
 	// TODO:
 	// Get locale instead
 
-	const { locale, primary_only } = req.query;
+	// const { locale, primary_only } = req.query;
+	const { primary_only } = req.query;
 
 	const out = primary_only
 		? await Categories.find()
diff --git a/src/api/routes/download/index.ts b/src/api/routes/download.ts
index 246c8834..b6c03a48 100644
--- a/src/api/routes/download/index.ts
+++ b/src/api/routes/download.ts
@@ -22,11 +22,6 @@ import { FieldErrors, Release } from "@fosscord/util";
 
 const router = Router();
 
-/*
-	TODO: Putting the download route in /routes/download.ts doesn't register the route, for some reason
-	But putting it here *does*
-*/
-
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const { platform } = req.query;
 
diff --git a/src/api/routes/gifs/search.ts b/src/api/routes/gifs/search.ts
index 02022004..ae63d643 100644
--- a/src/api/routes/gifs/search.ts
+++ b/src/api/routes/gifs/search.ts
@@ -41,7 +41,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		},
 	);
 
-	const { results } = (await response.json()) as any; // TODO: types
+	const { results } = await response.json();
 
 	res.json(results.map(parseGifResult)).status(200);
 });
diff --git a/src/api/routes/gifs/trending-gifs.ts b/src/api/routes/gifs/trending-gifs.ts
index d3fdb00a..d0698fa0 100644
--- a/src/api/routes/gifs/trending-gifs.ts
+++ b/src/api/routes/gifs/trending-gifs.ts
@@ -41,7 +41,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		},
 	);
 
-	const { results } = (await response.json()) as any; // TODO: types
+	const { results } = await response.json();
 
 	res.json(results.map(parseGifResult)).status(200);
 });
diff --git a/src/api/routes/gifs/trending.ts b/src/api/routes/gifs/trending.ts
index 5dc43e85..5c872df8 100644
--- a/src/api/routes/gifs/trending.ts
+++ b/src/api/routes/gifs/trending.ts
@@ -25,7 +25,57 @@ import { HTTPError } from "lambert-server";
 
 const router = Router();
 
-export function parseGifResult(result: any) {
+// TODO: Move somewhere else
+enum TENOR_GIF_TYPES {
+	gif,
+	mediumgif,
+	tinygif,
+	nanogif,
+	mp4,
+	loopedmp4,
+	tinymp4,
+	nanomp4,
+	webm,
+	tinywebm,
+	nanowebm,
+}
+
+type TENOR_MEDIA = {
+	preview: string;
+	url: string;
+	dims: number[];
+	size: number;
+};
+
+type TENOR_GIF = {
+	created: number;
+	hasaudio: boolean;
+	id: string;
+	media: { [type in keyof typeof TENOR_GIF_TYPES]: TENOR_MEDIA }[];
+	tags: string[];
+	title: string;
+	itemurl: string;
+	hascaption: boolean;
+	url: string;
+};
+
+type TENOR_CATEGORY = {
+	searchterm: string;
+	path: string;
+	image: string;
+	name: string;
+};
+
+type TENOR_CATEGORIES_RESULTS = {
+	tags: TENOR_CATEGORY[];
+};
+
+type TENOR_TRENDING_RESULTS = {
+	next: string;
+	results: TENOR_GIF[];
+};
+
+export function parseGifResult(result: TENOR_GIF) {
 	return {
 		id: result.id,
 		title: result.title,
@@ -50,7 +100,8 @@ export function getGifApiKey() {
 router.get("/", route({}), async (req: Request, res: Response) => {
 	// TODO: Custom providers
 	// TODO: return gifs as mp4
-	const { media_format, locale } = req.query;
+	// const { media_format, locale } = req.query;
+	const { locale } = req.query;
 
 	const apiKey = getGifApiKey();
 
@@ -75,11 +126,11 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		),
 	]);
 
-	const { tags } = (await responseSource.json()) as any; // TODO: types
-	const { results } = (await trendGifSource.json()) as any; //TODO: types;
+	const { tags } = (await responseSource.json()) as TENOR_CATEGORIES_RESULTS;
+	const { results } = (await trendGifSource.json()) as TENOR_TRENDING_RESULTS;
 
 	res.json({
-		categories: tags.map((x: any) => ({
+		categories: tags.map((x) => ({
 			name: x.searchterm,
 			src: x.image,
 		})),
diff --git a/src/api/routes/guild-recommendations.ts b/src/api/routes/guild-recommendations.ts
index 13c08734..ac2ad9e7 100644
--- a/src/api/routes/guild-recommendations.ts
+++ b/src/api/routes/guild-recommendations.ts
@@ -25,10 +25,11 @@ import { Like } from "typeorm";
 const router = Router();
 
 router.get("/", route({}), async (req: Request, res: Response) => {
-	const { limit, personalization_disabled } = req.query;
-	var showAllGuilds = Config.get().guild.discovery.showAllGuilds;
+	// const { limit, personalization_disabled } = req.query;
+	const { limit } = req.query;
+	const showAllGuilds = Config.get().guild.discovery.showAllGuilds;
 
-	const genLoadId = (size: Number) =>
+	const genLoadId = (size: number) =>
 		[...Array(size)]
 			.map(() => Math.floor(Math.random() * 16).toString(16))
 			.join("");
diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts
index efb06fa0..b044689f 100644
--- a/src/api/routes/guilds/#guild_id/bans.ts
+++ b/src/api/routes/guilds/#guild_id/bans.ts
@@ -41,8 +41,8 @@ router.get(
 	async (req: Request, res: Response) => {
 		const { guild_id } = req.params;
 
-		let bans = await Ban.find({ where: { guild_id: guild_id } });
-		let promisesToAwait: object[] = [];
+		const bans = await Ban.find({ where: { guild_id: guild_id } });
+		const promisesToAwait: object[] = [];
 		const bansObj: object[] = [];
 
 		bans.filter((ban) => ban.user_id !== ban.executor_id); // pretend self-bans don't exist to prevent victim chasing
@@ -104,14 +104,14 @@ router.put(
 
 		if (
 			req.user_id === banned_user_id &&
-			banned_user_id === req.permission!.cache.guild?.owner_id
+			banned_user_id === req.permission?.cache.guild?.owner_id
 		)
 			throw new HTTPError(
 				"You are the guild owner, hence can't ban yourself",
 				403,
 			);
 
-		if (req.permission!.cache.guild?.owner_id === banned_user_id)
+		if (req.permission?.cache.guild?.owner_id === banned_user_id)
 			throw new HTTPError("You can't ban the owner", 400);
 
 		const banned_user = await User.getPublicUser(banned_user_id);
@@ -149,7 +149,7 @@ router.put(
 
 		const banned_user = await User.getPublicUser(req.params.user_id);
 
-		if (req.permission!.cache.guild?.owner_id === req.params.user_id)
+		if (req.permission?.cache.guild?.owner_id === req.params.user_id)
 			throw new HTTPError(
 				"You are the guild owner, hence can't ban yourself",
 				403,
@@ -186,7 +186,7 @@ router.delete(
 	async (req: Request, res: Response) => {
 		const { guild_id, user_id } = req.params;
 
-		let ban = await Ban.findOneOrFail({
+		const ban = await Ban.findOneOrFail({
 			where: { guild_id: guild_id, user_id: user_id },
 		});
 
diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts
index b72f5ddb..acdb5f19 100644
--- a/src/api/routes/guilds/#guild_id/channels.ts
+++ b/src/api/routes/guilds/#guild_id/channels.ts
@@ -68,7 +68,7 @@ router.patch(
 						400,
 					);
 
-				const opts: any = {};
+				const opts: Partial<Channel> = {};
 				if (x.position != null) opts.position = x.position;
 
 				if (x.parent_id) {
diff --git a/src/api/routes/guilds/#guild_id/delete.ts b/src/api/routes/guilds/#guild_id/delete.ts
index 551c6829..9a13c9b4 100644
--- a/src/api/routes/guilds/#guild_id/delete.ts
+++ b/src/api/routes/guilds/#guild_id/delete.ts
@@ -16,17 +16,7 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import {
-	Channel,
-	emitEvent,
-	GuildDeleteEvent,
-	Guild,
-	Member,
-	Message,
-	Role,
-	Invite,
-	Emoji,
-} from "@fosscord/util";
+import { emitEvent, GuildDeleteEvent, Guild } from "@fosscord/util";
 import { Router, Request, Response } from "express";
 import { HTTPError } from "lambert-server";
 import { route } from "@fosscord/api";
@@ -36,7 +26,7 @@ const router = Router();
 // discord prefixes this route with /delete instead of using the delete method
 // docs are wrong https://discord.com/developers/docs/resources/guild#delete-guild
 router.post("/", route({}), async (req: Request, res: Response) => {
-	var { guild_id } = req.params;
+	const { guild_id } = req.params;
 
 	const guild = await Guild.findOneOrFail({
 		where: { id: guild_id },
diff --git a/src/api/routes/guilds/#guild_id/discovery-requirements.ts b/src/api/routes/guilds/#guild_id/discovery-requirements.ts
index 11dcc33e..de2da6ee 100644
--- a/src/api/routes/guilds/#guild_id/discovery-requirements.ts
+++ b/src/api/routes/guilds/#guild_id/discovery-requirements.ts
@@ -16,8 +16,6 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Guild, Config } from "@fosscord/util";
-
 import { Router, Request, Response } from "express";
 import { route } from "@fosscord/api";
 
diff --git a/src/api/routes/guilds/#guild_id/index.ts b/src/api/routes/guilds/#guild_id/index.ts
index 0df90f56..c262a088 100644
--- a/src/api/routes/guilds/#guild_id/index.ts
+++ b/src/api/routes/guilds/#guild_id/index.ts
@@ -47,10 +47,10 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 			401,
 		);
 
-	// @ts-ignore
-	guild.joined_at = member?.joined_at;
-
-	return res.send(guild);
+	return res.send({
+		...guild,
+		joined_at: member?.joined_at,
+	});
 });
 
 router.patch(
@@ -68,7 +68,7 @@ router.patch(
 				"MANAGE_GUILDS",
 			);
 
-		var guild = await Guild.findOneOrFail({
+		const guild = await Guild.findOneOrFail({
 			where: { id: guild_id },
 			relations: ["emojis", "roles", "stickers"],
 		});
@@ -110,7 +110,7 @@ router.patch(
 				"DISCOVERABLE",
 			];
 
-			for (var feature of diff) {
+			for (const feature of diff) {
 				if (MUTABLE_FEATURES.includes(feature)) continue;
 
 				throw FosscordApiErrors.FEATURE_IS_IMMUTABLE.withParams(
diff --git a/src/api/routes/guilds/#guild_id/invites.ts b/src/api/routes/guilds/#guild_id/invites.ts
index 6e9cc3e6..dd099992 100644
--- a/src/api/routes/guilds/#guild_id/invites.ts
+++ b/src/api/routes/guilds/#guild_id/invites.ts
@@ -16,7 +16,7 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { getPermission, Invite, PublicInviteRelation } from "@fosscord/util";
+import { Invite, PublicInviteRelation } from "@fosscord/util";
 import { route } from "@fosscord/api";
 import { Request, Response, Router } from "express";
 
diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts
index 2cf7c08b..2daa7d9b 100644
--- a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts
+++ b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts
@@ -49,11 +49,12 @@ router.patch(
 	"/",
 	route({ body: "MemberChangeSchema" }),
 	async (req: Request, res: Response) => {
-		let { guild_id, member_id } = req.params;
-		if (member_id === "@me") member_id = req.user_id;
+		const { guild_id } = req.params;
+		const member_id =
+			req.params.member_id === "@me" ? req.user_id : req.params.member_id;
 		const body = req.body as MemberChangeSchema;
 
-		let member = await Member.findOneOrFail({
+		const member = await Member.findOneOrFail({
 			where: { id: member_id, guild_id },
 			relations: ["roles", "user"],
 		});
@@ -101,7 +102,8 @@ router.put("/", route({}), async (req: Request, res: Response) => {
 
 	const rights = await getRights(req.user_id);
 
-	let { guild_id, member_id } = req.params;
+	const { guild_id } = req.params;
+	let { member_id } = req.params;
 	if (member_id === "@me") {
 		member_id = req.user_id;
 		rights.hasThrow("JOIN_GUILDS");
@@ -109,19 +111,19 @@ router.put("/", route({}), async (req: Request, res: Response) => {
 		// TODO: join others by controller
 	}
 
-	var guild = await Guild.findOneOrFail({
+	const guild = await Guild.findOneOrFail({
 		where: { id: guild_id },
 	});
 
-	var emoji = await Emoji.find({
+	const emoji = await Emoji.find({
 		where: { guild_id: guild_id },
 	});
 
-	var roles = await Role.find({
+	const roles = await Role.find({
 		where: { guild_id: guild_id },
 	});
 
-	var stickers = await Sticker.find({
+	const stickers = await Sticker.find({
 		where: { guild_id: guild_id },
 	});
 
diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts b/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts
index 619b66f7..c93eab08 100644
--- a/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts
+++ b/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts
@@ -26,12 +26,12 @@ router.patch(
 	"/",
 	route({ body: "MemberNickChangeSchema" }),
 	async (req: Request, res: Response) => {
-		var { guild_id, member_id } = req.params;
-		var permissionString: PermissionResolvable = "MANAGE_NICKNAMES";
-		if (member_id === "@me") {
-			member_id = req.user_id;
-			permissionString = "CHANGE_NICKNAME";
-		}
+		const { guild_id } = req.params;
+		let permissionString: PermissionResolvable = "MANAGE_NICKNAMES";
+		const member_id =
+			req.params.member_id === "@me"
+				? ((permissionString = "CHANGE_NICKNAME"), req.user_id)
+				: req.params.member_id;
 
 		const perms = await getPermission(req.user_id, guild_id);
 		perms.hasThrow(permissionString);
diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts b/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts
index e64893b7..16c5e789 100644
--- a/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts
+++ b/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts
@@ -16,7 +16,7 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { getPermission, Member } from "@fosscord/util";
+import { Member } from "@fosscord/util";
 import { route } from "@fosscord/api";
 import { Request, Response, Router } from "express";
 
diff --git a/src/api/routes/guilds/#guild_id/members/index.ts b/src/api/routes/guilds/#guild_id/members/index.ts
index b96210f3..51e9eb1f 100644
--- a/src/api/routes/guilds/#guild_id/members/index.ts
+++ b/src/api/routes/guilds/#guild_id/members/index.ts
@@ -17,7 +17,7 @@
 */
 
 import { Request, Response, Router } from "express";
-import { Guild, Member, PublicMemberProjection } from "@fosscord/util";
+import { Member, PublicMemberProjection } from "@fosscord/util";
 import { route } from "@fosscord/api";
 import { MoreThan } from "typeorm";
 import { HTTPError } from "lambert-server";
diff --git a/src/api/routes/guilds/#guild_id/messages/search.ts b/src/api/routes/guilds/#guild_id/messages/search.ts
index 7061b5f0..601167ee 100644
--- a/src/api/routes/guilds/#guild_id/messages/search.ts
+++ b/src/api/routes/guilds/#guild_id/messages/search.ts
@@ -16,6 +16,8 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
+/* eslint-disable @typescript-eslint/ban-ts-comment */
+
 import { Request, Response, Router } from "express";
 import { route } from "@fosscord/api";
 import { getPermission, FieldErrors, Message, Channel } from "@fosscord/util";
@@ -28,10 +30,10 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	const {
 		channel_id,
 		content,
-		include_nsfw, // TODO
+		// include_nsfw, // TODO
 		offset,
 		sort_order,
-		sort_by, // TODO: Handle 'relevance'
+		// sort_by, // TODO: Handle 'relevance'
 		limit,
 		author_id,
 	} = req.query;
@@ -62,7 +64,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	if (!permissions.has("READ_MESSAGE_HISTORY"))
 		return res.json({ messages: [], total_results: 0 });
 
-	var query: FindManyOptions<Message> = {
+	const query: FindManyOptions<Message> = {
 		order: {
 			timestamp: sort_order
 				? (sort_order.toUpperCase() as "ASC" | "DESC")
@@ -87,7 +89,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		skip: offset ? Number(offset) : 0,
 	};
 	//@ts-ignore
-	if (channel_id) query.where!.channel = { id: channel_id };
+	if (channel_id) query.where.channel = { id: channel_id };
 	else {
 		// get all channel IDs that this user can access
 		const channels = await Channel.find({
@@ -96,7 +98,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		});
 		const ids = [];
 
-		for (var channel of channels) {
+		for (const channel of channels) {
 			const perm = await getPermission(
 				req.user_id,
 				req.params.guild_id,
@@ -108,12 +110,12 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		}
 
 		//@ts-ignore
-		query.where!.channel = { id: In(ids) };
+		query.where.channel = { id: In(ids) };
 	}
 	//@ts-ignore
-	if (author_id) query.where!.author = { id: author_id };
+	if (author_id) query.where.author = { id: author_id };
 	//@ts-ignore
-	if (content) query.where!.content = Like(`%${content}%`);
+	if (content) query.where.content = Like(`%${content}%`);
 
 	const messages: Message[] = await Message.find(query);
 
diff --git a/src/api/routes/guilds/#guild_id/profile/index.ts b/src/api/routes/guilds/#guild_id/profile/index.ts
index 5771fbf1..cbf0ff6a 100644
--- a/src/api/routes/guilds/#guild_id/profile/index.ts
+++ b/src/api/routes/guilds/#guild_id/profile/index.ts
@@ -33,8 +33,9 @@ router.patch(
 	"/:member_id",
 	route({ body: "MemberChangeProfileSchema" }),
 	async (req: Request, res: Response) => {
-		let { guild_id, member_id } = req.params;
-		if (member_id === "@me") member_id = req.user_id;
+		const { guild_id } = req.params;
+		// const member_id =
+		// 	req.params.member_id === "@me" ? req.user_id : req.params.member_id;
 		const body = req.body as MemberChangeProfileSchema;
 
 		let member = await Member.findOneOrFail({
diff --git a/src/api/routes/guilds/#guild_id/prune.ts b/src/api/routes/guilds/#guild_id/prune.ts
index 1199df54..37b70f63 100644
--- a/src/api/routes/guilds/#guild_id/prune.ts
+++ b/src/api/routes/guilds/#guild_id/prune.ts
@@ -29,16 +29,16 @@ export const inactiveMembers = async (
 	days: number,
 	roles: string[] = [],
 ) => {
-	var date = new Date();
+	const date = new Date();
 	date.setDate(date.getDate() - days);
 	//Snowflake should have `generateFromTime` method? Or similar?
-	var minId = BigInt(date.valueOf() - Snowflake.EPOCH) << BigInt(22);
+	const minId = BigInt(date.valueOf() - Snowflake.EPOCH) << BigInt(22);
 
 	/**
 	idea: ability to customise the cutoff variable
 	possible candidates: public read receipt, last presence, last VC leave
 	**/
-	var members = await Member.find({
+	let members = await Member.find({
 		where: [
 			{
 				guild_id,
@@ -83,7 +83,7 @@ export const inactiveMembers = async (
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const days = parseInt(req.query.days as string);
 
-	var roles = req.query.include_roles;
+	let roles = req.query.include_roles;
 	if (typeof roles === "string") roles = [roles]; //express will return array otherwise
 
 	const members = await inactiveMembers(
@@ -102,7 +102,7 @@ router.post(
 	async (req: Request, res: Response) => {
 		const days = parseInt(req.body.days);
 
-		var roles = req.query.include_roles;
+		let roles = req.query.include_roles;
 		if (typeof roles === "string") roles = [roles];
 
 		const { guild_id } = req.params;
diff --git a/src/api/routes/guilds/#guild_id/regions.ts b/src/api/routes/guilds/#guild_id/regions.ts
index a9c04a39..61ba00bf 100644
--- a/src/api/routes/guilds/#guild_id/regions.ts
+++ b/src/api/routes/guilds/#guild_id/regions.ts
@@ -16,10 +16,9 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Config, Guild, Member } from "@fosscord/util";
+import { Guild } from "@fosscord/util";
 import { Request, Response, Router } from "express";
-import { getVoiceRegions, route } from "@fosscord/api";
-import { getIpAdress } from "@fosscord/api";
+import { getVoiceRegions, route, getIpAdress } from "@fosscord/api";
 
 const router = Router();
 
diff --git a/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts b/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts
index 22eb439a..48e77897 100644
--- a/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts
+++ b/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts
@@ -87,7 +87,8 @@ router.patch(
 		role.assign({
 			...body,
 			permissions: String(
-				req.permission!.bitfield & BigInt(body.permissions || "0"),
+				(req.permission?.bitfield || 0n) &
+					BigInt(body.permissions || "0"),
 			),
 		});
 
diff --git a/src/api/routes/guilds/#guild_id/roles/index.ts b/src/api/routes/guilds/#guild_id/roles/index.ts
index feab84ef..54d4b12c 100644
--- a/src/api/routes/guilds/#guild_id/roles/index.ts
+++ b/src/api/routes/guilds/#guild_id/roles/index.ts
@@ -68,7 +68,8 @@ router.post(
 			guild_id: guild_id,
 			managed: false,
 			permissions: String(
-				req.permission!.bitfield & BigInt(body.permissions || "0"),
+				(req.permission?.bitfield || 0n) &
+					BigInt(body.permissions || "0"),
 			),
 			tags: undefined,
 			icon: undefined,
diff --git a/src/api/routes/guilds/#guild_id/templates.ts b/src/api/routes/guilds/#guild_id/templates.ts
index f5244313..284bbccf 100644
--- a/src/api/routes/guilds/#guild_id/templates.ts
+++ b/src/api/routes/guilds/#guild_id/templates.ts
@@ -44,7 +44,7 @@ const TemplateGuildProjection: (keyof Guild)[] = [
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const { guild_id } = req.params;
 
-	var templates = await Template.find({
+	const templates = await Template.find({
 		where: { source_guild_id: guild_id },
 	});
 
@@ -60,9 +60,9 @@ router.post(
 			where: { id: guild_id },
 			select: TemplateGuildProjection,
 		});
-		const exists = await Template.findOneOrFail({
+		const exists = await Template.findOne({
 			where: { id: guild_id },
-		}).catch((e) => {});
+		});
 		if (exists) throw new HTTPError("Template already exists", 400);
 
 		const template = await Template.create({
diff --git a/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts b/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts
index 9883ef0d..3577df17 100644
--- a/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts
+++ b/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts
@@ -37,8 +37,9 @@ router.patch(
 	route({ body: "VoiceStateUpdateSchema" }),
 	async (req: Request, res: Response) => {
 		const body = req.body as VoiceStateUpdateSchema;
-		var { guild_id, user_id } = req.params;
-		if (user_id === "@me") user_id = req.user_id;
+		const { guild_id } = req.params;
+		const user_id =
+			req.params.user_id === "@me" ? req.user_id : req.params.user_id;
 
 		const perms = await getPermission(
 			req.user_id,
diff --git a/src/api/routes/guilds/#guild_id/widget.json.ts b/src/api/routes/guilds/#guild_id/widget.json.ts
index 46b8aa8b..9319d058 100644
--- a/src/api/routes/guilds/#guild_id/widget.json.ts
+++ b/src/api/routes/guilds/#guild_id/widget.json.ts
@@ -17,14 +17,7 @@
 */
 
 import { Request, Response, Router } from "express";
-import {
-	Config,
-	Permissions,
-	Guild,
-	Invite,
-	Channel,
-	Member,
-} from "@fosscord/util";
+import { Permissions, Guild, Invite, Channel, Member } from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 import { random, route } from "@fosscord/api";
 
@@ -46,7 +39,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	if (!guild.widget_enabled) throw new HTTPError("Widget Disabled", 404);
 
 	// Fetch existing widget invite for widget channel
-	var invite = await Invite.findOne({
+	let invite = await Invite.findOne({
 		where: { channel_id: guild.widget_channel_id },
 	});
 
@@ -70,7 +63,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	}
 
 	// Fetch voice channels, and the @everyone permissions object
-	const channels = [] as any[];
+	const channels: { id: string; name: string; position: number }[] = [];
 
 	(
 		await Channel.find({
@@ -88,15 +81,15 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		) {
 			channels.push({
 				id: doc.id,
-				name: doc.name,
-				position: doc.position,
+				name: doc.name ?? "Unknown channel",
+				position: doc.position ?? 0,
 			});
 		}
 	});
 
 	// Fetch members
 	// TODO: Understand how Discord's max 100 random member sample works, and apply to here (see top of this file)
-	let members = await Member.find({ where: { guild_id: guild_id } });
+	const members = await Member.find({ where: { guild_id: guild_id } });
 
 	// Construct object to respond with
 	const data = {
diff --git a/src/api/routes/guilds/#guild_id/widget.png.ts b/src/api/routes/guilds/#guild_id/widget.png.ts
index 1c6a62ac..65de8978 100644
--- a/src/api/routes/guilds/#guild_id/widget.png.ts
+++ b/src/api/routes/guilds/#guild_id/widget.png.ts
@@ -16,6 +16,8 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
 import { Request, Response, Router } from "express";
 import { Guild } from "@fosscord/util";
 import { HTTPError } from "lambert-server";
@@ -161,8 +163,7 @@ async function drawIcon(
 	scale: number,
 	icon: string,
 ) {
-	// @ts-ignore
-	const img = new require("canvas").Image();
+	const img = new (require("canvas").Image)();
 	img.src = icon;
 
 	// Do some canvas clipping magic!
diff --git a/src/api/routes/guilds/index.ts b/src/api/routes/guilds/index.ts
index 125d25b0..64af4bd1 100644
--- a/src/api/routes/guilds/index.ts
+++ b/src/api/routes/guilds/index.ts
@@ -18,7 +18,6 @@
 
 import { Router, Request, Response } from "express";
 import {
-	Role,
 	Guild,
 	Config,
 	getRights,
@@ -52,6 +51,7 @@ router.post(
 
 		const { autoJoin } = Config.get().guild;
 		if (autoJoin.enabled && !autoJoin.guilds?.length) {
+			// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 			// @ts-ignore
 			await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } });
 		}
diff --git a/src/api/routes/guilds/templates/index.ts b/src/api/routes/guilds/templates/index.ts
index f3bb3ef1..a43337d8 100644
--- a/src/api/routes/guilds/templates/index.ts
+++ b/src/api/routes/guilds/templates/index.ts
@@ -86,8 +86,8 @@ router.post(
 		const {
 			enabled,
 			allowTemplateCreation,
-			allowDiscordTemplates,
-			allowRaws,
+			// allowDiscordTemplates,
+			// allowRaws,
 		} = Config.get().templates;
 		if (!enabled)
 			return res
@@ -121,7 +121,7 @@ router.post(
 
 		const guild_id = Snowflake.generate();
 
-		const [guild, role] = await Promise.all([
+		const [guild] = await Promise.all([
 			Guild.create({
 				...body,
 				...template.serialized_source_guild,
diff --git a/src/api/routes/oauth2/authorize.ts b/src/api/routes/oauth2/authorize.ts
index be9b39b4..e238b72f 100644
--- a/src/api/routes/oauth2/authorize.ts
+++ b/src/api/routes/oauth2/authorize.ts
@@ -27,16 +27,14 @@ import {
 	Member,
 	Permissions,
 	User,
-	getRights,
-	Rights,
-	MemberPrivateProjection,
 } from "@fosscord/util";
 const router = Router();
 
 // TODO: scopes, other oauth types
 
 router.get("/", route({}), async (req: Request, res: Response) => {
-	const { client_id, scope, response_type, redirect_url } = req.query;
+	// const { client_id, scope, response_type, redirect_url } = req.query;
+	const { client_id } = req.query;
 
 	const app = await Application.findOne({
 		where: {
@@ -68,6 +66,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 			},
 		},
 		relations: ["guild", "roles"],
+		// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 		//@ts-ignore
 		// prettier-ignore
 		select: ["guild.id", "guild.name", "guild.icon", "guild.mfa_level", "guild.owner_id", "roles.id"],
@@ -139,7 +138,8 @@ router.post(
 	route({ body: "ApplicationAuthorizeSchema" }),
 	async (req: Request, res: Response) => {
 		const body = req.body as ApplicationAuthorizeSchema;
-		const { client_id, scope, response_type, redirect_url } = req.query;
+		// const { client_id, scope, response_type, redirect_url } = req.query;
+		const { client_id } = req.query;
 
 		// TODO: captcha verification
 		// TODO: MFA verification
@@ -153,7 +153,7 @@ router.post(
 		// getPermission cache won't exist if we're owner
 		if (
 			Object.keys(perms.cache || {}).length > 0 &&
-			perms.cache.member!.user.bot
+			perms.cache.member?.user.bot
 		)
 			throw DiscordApiErrors.UNAUTHORIZED;
 		perms.hasThrow("MANAGE_GUILD");
diff --git a/src/api/routes/partners/#guild_id/requirements.ts b/src/api/routes/partners/#guild_id/requirements.ts
index 11dcc33e..de2da6ee 100644
--- a/src/api/routes/partners/#guild_id/requirements.ts
+++ b/src/api/routes/partners/#guild_id/requirements.ts
@@ -16,8 +16,6 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Guild, Config } from "@fosscord/util";
-
 import { Router, Request, Response } from "express";
 import { route } from "@fosscord/api";
 
diff --git a/src/api/routes/policies/instance/domains.ts b/src/api/routes/policies/instance/domains.ts
index 929cf65c..33c06765 100644
--- a/src/api/routes/policies/instance/domains.ts
+++ b/src/api/routes/policies/instance/domains.ts
@@ -19,7 +19,6 @@
 import { Router, Request, Response } from "express";
 import { route } from "@fosscord/api";
 import { Config } from "@fosscord/util";
-import { config } from "dotenv";
 const router = Router();
 
 router.get("/", route({}), async (req: Request, res: Response) => {
diff --git a/src/api/routes/store/published-listings/applications.ts b/src/api/routes/store/published-listings/applications.ts
index 16604960..ec9168b1 100644
--- a/src/api/routes/store/published-listings/applications.ts
+++ b/src/api/routes/store/published-listings/applications.ts
@@ -23,7 +23,7 @@ const router: Router = Router();
 
 router.get("/:id", route({}), async (req: Request, res: Response) => {
 	//TODO
-	const id = req.params.id;
+	// const id = req.params.id;
 	res.json({
 		id: "",
 		summary: "",
diff --git a/src/api/routes/store/published-listings/skus.ts b/src/api/routes/store/published-listings/skus.ts
index 16604960..ec9168b1 100644
--- a/src/api/routes/store/published-listings/skus.ts
+++ b/src/api/routes/store/published-listings/skus.ts
@@ -23,7 +23,7 @@ const router: Router = Router();
 
 router.get("/:id", route({}), async (req: Request, res: Response) => {
 	//TODO
-	const id = req.params.id;
+	// const id = req.params.id;
 	res.json({
 		id: "",
 		summary: "",
diff --git a/src/api/routes/updates.ts b/src/api/routes/updates.ts
index 5555fcda..5c237465 100644
--- a/src/api/routes/updates.ts
+++ b/src/api/routes/updates.ts
@@ -18,12 +18,11 @@
 
 import { Router, Response, Request } from "express";
 import { route } from "@fosscord/api";
-import { Config, FieldErrors, Release } from "@fosscord/util";
+import { FieldErrors, Release } from "@fosscord/util";
 
 const router = Router();
 
 router.get("/", route({}), async (req: Request, res: Response) => {
-	const { client } = Config.get();
 	const platform = req.query.platform;
 
 	if (!platform)
diff --git a/src/api/routes/users/#id/delete.ts b/src/api/routes/users/#id/delete.ts
index e7caeb05..9bc3f9f8 100644
--- a/src/api/routes/users/#id/delete.ts
+++ b/src/api/routes/users/#id/delete.ts
@@ -23,7 +23,6 @@ import {
 	PrivateUserProjection,
 	User,
 	UserDeleteEvent,
-	UserDeleteSchema,
 } from "@fosscord/util";
 import { Request, Response, Router } from "express";
 
@@ -33,7 +32,7 @@ router.post(
 	"/",
 	route({ right: "MANAGE_USERS" }),
 	async (req: Request, res: Response) => {
-		let user = await User.findOneOrFail({
+		await User.findOneOrFail({
 			where: { id: req.params.id },
 			select: [...PrivateUserProjection, "data"],
 		});
diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts
index 1103bb48..dbf95a52 100644
--- a/src/api/routes/users/#id/profile.ts
+++ b/src/api/routes/users/#id/profile.ts
@@ -19,11 +19,9 @@
 import { Router, Request, Response } from "express";
 import {
 	PublicConnectedAccount,
-	PublicUser,
 	User,
 	UserPublic,
 	Member,
-	Guild,
 	UserProfileModifySchema,
 	handleFile,
 	PrivateUserProjection,
@@ -53,8 +51,8 @@ router.get(
 			relations: ["connected_accounts"],
 		});
 
-		var mutual_guilds: object[] = [];
-		var premium_guild_since;
+		const mutual_guilds: object[] = [];
+		let premium_guild_since;
 
 		if (with_mutual_guilds == "true") {
 			const requested_member = await Member.find({
@@ -169,7 +167,7 @@ router.patch(
 				`/banners/${req.user_id}`,
 				body.banner as string,
 			);
-		let user = await User.findOneOrFail({
+		const user = await User.findOneOrFail({
 			where: { id: req.user_id },
 			select: [...PrivateUserProjection, "data"],
 		});
@@ -177,6 +175,7 @@ router.patch(
 		user.assign(body);
 		await user.save();
 
+		// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 		// @ts-ignore
 		delete user.data;
 
diff --git a/src/api/routes/users/#id/relationships.ts b/src/api/routes/users/#id/relationships.ts
index aa36967a..e915e3ff 100644
--- a/src/api/routes/users/#id/relationships.ts
+++ b/src/api/routes/users/#id/relationships.ts
@@ -36,7 +36,7 @@ router.get(
 	"/",
 	route({ test: { response: { body: "UserRelationsResponse" } } }),
 	async (req: Request, res: Response) => {
-		var mutual_relations: object[] = [];
+		const mutual_relations: object[] = [];
 		const requested_relations = await User.findOneOrFail({
 			where: { id: req.params.id },
 			relations: ["relationships"],
@@ -53,7 +53,7 @@ router.get(
 					rmem.type === 1 &&
 					rmem.to_id !== req.user_id
 				) {
-					var relation_user = await User.getPublicUser(rmem.to_id);
+					const relation_user = await User.getPublicUser(rmem.to_id);
 
 					mutual_relations.push({
 						id: relation_user.id,
diff --git a/src/api/routes/users/@me/delete.ts b/src/api/routes/users/@me/delete.ts
index a6ae2d13..8043eae3 100644
--- a/src/api/routes/users/@me/delete.ts
+++ b/src/api/routes/users/@me/delete.ts
@@ -17,7 +17,7 @@
 */
 
 import { Router, Request, Response } from "express";
-import { Guild, Member, User } from "@fosscord/util";
+import { Member, User } from "@fosscord/util";
 import { route } from "@fosscord/api";
 import bcrypt from "bcrypt";
 import { HTTPError } from "lambert-server";
diff --git a/src/api/routes/users/@me/guilds/#guild_id/settings.ts b/src/api/routes/users/@me/guilds/#guild_id/settings.ts
index 0525bea2..72c95d6b 100644
--- a/src/api/routes/users/@me/guilds/#guild_id/settings.ts
+++ b/src/api/routes/users/@me/guilds/#guild_id/settings.ts
@@ -43,7 +43,7 @@ router.patch(
 		const body = req.body as UserGuildSettingsSchema;
 
 		if (body.channel_overrides) {
-			for (var channel in body.channel_overrides) {
+			for (const channel in body.channel_overrides) {
 				Channel.findOneOrFail({ where: { id: channel } });
 			}
 		}
diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts
index 596e2575..0d3c3135 100644
--- a/src/api/routes/users/@me/index.ts
+++ b/src/api/routes/users/@me/index.ts
@@ -55,7 +55,7 @@ router.patch(
 		});
 
 		// Populated on password change
-		var newToken: string | undefined;
+		let newToken: string | undefined;
 
 		if (body.avatar)
 			body.avatar = await handleFile(
@@ -120,7 +120,7 @@ router.patch(
 		}
 
 		if (body.username) {
-			var check_username = body?.username?.replace(/\s/g, "");
+			const check_username = body?.username?.replace(/\s/g, "");
 			if (!check_username) {
 				throw FieldErrors({
 					username: {
@@ -153,7 +153,8 @@ router.patch(
 		user.validate();
 		await user.save();
 
-		// @ts-ignore
+		// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+		//@ts-ignore
 		delete user.data;
 
 		// TODO: send update member list event in gateway
diff --git a/src/api/routes/users/@me/mfa/codes-verification.ts b/src/api/routes/users/@me/mfa/codes-verification.ts
index ac16f7e7..24f018c9 100644
--- a/src/api/routes/users/@me/mfa/codes-verification.ts
+++ b/src/api/routes/users/@me/mfa/codes-verification.ts
@@ -23,6 +23,7 @@ import {
 	generateMfaBackupCodes,
 	User,
 	CodesVerificationSchema,
+	DiscordApiErrors,
 } from "@fosscord/util";
 
 const router = Router();
@@ -31,14 +32,17 @@ router.post(
 	"/",
 	route({ body: "CodesVerificationSchema" }),
 	async (req: Request, res: Response) => {
-		const { key, nonce, regenerate } = req.body as CodesVerificationSchema;
+		// const { key, nonce, regenerate } = req.body as CodesVerificationSchema;
+		const { regenerate } = req.body as CodesVerificationSchema;
 
 		// TODO: We don't have email/etc etc, so can't send a verification code.
 		// Once that's done, this route can verify `key`
 
-		const user = await User.findOneOrFail({ where: { id: req.user_id } });
+		// const user = await User.findOneOrFail({ where: { id: req.user_id } });
+		if ((await User.count({ where: { id: req.user_id } })) === 0)
+			throw DiscordApiErrors.UNKNOWN_USER;
 
-		var codes: BackupCode[];
+		let codes: BackupCode[];
 		if (regenerate) {
 			await BackupCode.update(
 				{ user: { id: req.user_id } },
diff --git a/src/api/routes/users/@me/mfa/codes.ts b/src/api/routes/users/@me/mfa/codes.ts
index 09b9b329..e2600400 100644
--- a/src/api/routes/users/@me/mfa/codes.ts
+++ b/src/api/routes/users/@me/mfa/codes.ts
@@ -51,7 +51,7 @@ router.post(
 			});
 		}
 
-		var codes: BackupCode[];
+		let codes: BackupCode[];
 		if (regenerate) {
 			await BackupCode.update(
 				{ user: { id: req.user_id } },
diff --git a/src/api/routes/users/@me/mfa/totp/disable.ts b/src/api/routes/users/@me/mfa/totp/disable.ts
index c399ba33..e35691ae 100644
--- a/src/api/routes/users/@me/mfa/totp/disable.ts
+++ b/src/api/routes/users/@me/mfa/totp/disable.ts
@@ -42,7 +42,7 @@ router.post(
 
 		const backup = await BackupCode.findOne({ where: { code: body.code } });
 		if (!backup) {
-			const ret = verifyToken(user.totp_secret!, body.code);
+			const ret = verifyToken(user.totp_secret || "", body.code);
 			if (!ret || ret.delta != 0)
 				throw new HTTPError(
 					req.t("auth:login.INVALID_TOTP_CODE"),
diff --git a/src/api/routes/users/@me/mfa/totp/enable.ts b/src/api/routes/users/@me/mfa/totp/enable.ts
index a59983ac..f6519ad0 100644
--- a/src/api/routes/users/@me/mfa/totp/enable.ts
+++ b/src/api/routes/users/@me/mfa/totp/enable.ts
@@ -57,7 +57,7 @@ router.post(
 		if (verifyToken(body.secret, body.code)?.delta != 0)
 			throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
 
-		let backup_codes = generateMfaBackupCodes(req.user_id);
+		const backup_codes = generateMfaBackupCodes(req.user_id);
 		await Promise.all(backup_codes.map((x) => x.save()));
 		await User.update(
 			{ id: req.user_id },
diff --git a/src/api/routes/users/@me/relationships.ts b/src/api/routes/users/@me/relationships.ts
index de684a34..4dfb4c33 100644
--- a/src/api/routes/users/@me/relationships.ts
+++ b/src/api/routes/users/@me/relationships.ts
@@ -175,7 +175,7 @@ async function updateRelationship(
 		select: userProjection,
 	});
 
-	var relationship = user.relationships.find((x) => x.to_id === id);
+	let relationship = user.relationships.find((x) => x.to_id === id);
 	const friendRequest = friend.relationships.find(
 		(x) => x.to_id === req.user_id,
 	);
@@ -219,13 +219,13 @@ async function updateRelationship(
 	if (user.relationships.length >= maxFriends)
 		throw DiscordApiErrors.MAXIMUM_FRIENDS.withParams(maxFriends);
 
-	var incoming_relationship = Relationship.create({
+	let incoming_relationship = Relationship.create({
 		nickname: undefined,
 		type: RelationshipType.incoming,
 		to: user,
 		from: friend,
 	});
-	var outgoing_relationship = Relationship.create({
+	let outgoing_relationship = Relationship.create({
 		nickname: undefined,
 		type: RelationshipType.outgoing,
 		to: friend,
diff --git a/src/api/routes/users/@me/settings.ts b/src/api/routes/users/@me/settings.ts
index ad922084..c883bb30 100644
--- a/src/api/routes/users/@me/settings.ts
+++ b/src/api/routes/users/@me/settings.ts
@@ -17,7 +17,7 @@
 */
 
 import { Router, Response, Request } from "express";
-import { OrmUtils, User, UserSettingsSchema } from "@fosscord/util";
+import { User, UserSettingsSchema } from "@fosscord/util";
 import { route } from "@fosscord/api";
 
 const router = Router();
diff --git a/src/api/start.ts b/src/api/start.ts
index e80a7d4a..7975d085 100644
--- a/src/api/start.ts
+++ b/src/api/start.ts
@@ -26,7 +26,7 @@ config();
 import { FosscordServer } from "./Server";
 import cluster from "cluster";
 import os from "os";
-var cores = 1;
+let cores = 1;
 try {
 	cores = Number(process.env.THREADS) || os.cpus().length;
 } catch {
@@ -41,16 +41,17 @@ if (cluster.isPrimary && process.env.NODE_ENV == "production") {
 		cluster.fork();
 	}
 
-	cluster.on("exit", (worker, code, signal) => {
+	cluster.on("exit", (worker) => {
 		console.log(`worker ${worker.process.pid} died, restart worker`);
 		cluster.fork();
 	});
 } else {
-	var port = Number(process.env.PORT) || 3001;
+	const port = Number(process.env.PORT) || 3001;
 
 	const server = new FosscordServer({ port });
 	server.start().catch(console.error);
 
+	// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 	// @ts-ignore
 	global.server = server;
 }
diff --git a/src/api/util/handlers/Instance.ts b/src/api/util/handlers/Instance.ts
index acac1fb8..08157208 100644
--- a/src/api/util/handlers/Instance.ts
+++ b/src/api/util/handlers/Instance.ts
@@ -16,7 +16,7 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Config, Guild, Session } from "@fosscord/util";
+import { Session } from "@fosscord/util";
 
 export async function initInstance() {
 	// TODO: clean up database and delete tombstone data
@@ -24,15 +24,14 @@ export async function initInstance() {
 
 	// create default guild and add it to auto join
 	// TODO: check if any current user is not part of autoJoinGuilds
-	const { autoJoin } = Config.get().guild;
+	// const { autoJoin } = Config.get().guild;
 
-	if (autoJoin.enabled && !autoJoin.guilds?.length) {
-		let guild = await Guild.findOne({ where: {}, select: ["id"] });
-		if (guild) {
-			// @ts-ignore
-			await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } });
-		}
-	}
+	// if (autoJoin.enabled && !autoJoin.guilds?.length) {
+	// 	const guild = await Guild.findOne({ where: {}, select: ["id"] });
+	// 	if (guild) {
+	// 		await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } });
+	// 	}
+	// }
 
 	// TODO: do no clear sessions for instance cluster
 	await Session.delete({});
diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts
index 2371358f..42325681 100644
--- a/src/api/util/handlers/Message.ts
+++ b/src/api/util/handlers/Message.ts
@@ -51,7 +51,7 @@ const allow_empty = false;
 // TODO: embed gifs/videos/images
 
 const LINK_REGEX =
-	/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
+	/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g;
 
 export async function handleMessage(opts: MessageOptions): Promise<Message> {
 	const channel = await Channel.findOneOrFail({
@@ -129,7 +129,6 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
 		}
 		/** Q: should be checked if the referenced message exists? ANSWER: NO
 		 otherwise backfilling won't work **/
-		// @ts-ignore
 		message.type = MessageType.REPLY;
 	}
 
@@ -144,29 +143,29 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
 		throw new HTTPError("Empty messages are not allowed", 50006);
 	}
 
-	var content = opts.content;
-	var mention_channel_ids = [] as string[];
-	var mention_role_ids = [] as string[];
-	var mention_user_ids = [] as string[];
-	var mention_everyone = false;
+	let content = opts.content;
+	const mention_channel_ids = [] as string[];
+	const mention_role_ids = [] as string[];
+	const mention_user_ids = [] as string[];
+	let mention_everyone = false;
 
 	if (content) {
 		// TODO: explicit-only mentions
 		message.content = content.trim();
-		content = content.replace(/ *\`[^)]*\` */g, ""); // remove codeblocks
-		for (const [_, mention] of content.matchAll(CHANNEL_MENTION)) {
+		content = content.replace(/ *`[^)]*` */g, ""); // remove codeblocks
+		for (const [, mention] of content.matchAll(CHANNEL_MENTION)) {
 			if (!mention_channel_ids.includes(mention))
 				mention_channel_ids.push(mention);
 		}
 
-		for (const [_, mention] of content.matchAll(USER_MENTION)) {
+		for (const [, mention] of content.matchAll(USER_MENTION)) {
 			if (!mention_user_ids.includes(mention))
 				mention_user_ids.push(mention);
 		}
 
 		await Promise.all(
 			Array.from(content.matchAll(ROLE_MENTION)).map(
-				async ([_, mention]) => {
+				async ([, mention]) => {
 					const role = await Role.findOneOrFail({
 						where: { id: mention, guild_id: channel.guild_id },
 					});
@@ -198,8 +197,8 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
 
 // TODO: cache link result in db
 export async function postHandleMessage(message: Message) {
-	const content = message.content?.replace(/ *\`[^)]*\` */g, ""); // remove markdown
-	var links = content?.match(LINK_REGEX);
+	const content = message.content?.replace(/ *`[^)]*` */g, ""); // remove markdown
+	let links = content?.match(LINK_REGEX);
 	if (!links) return;
 
 	const data = { ...message };
@@ -232,8 +231,8 @@ export async function postHandleMessage(message: Message) {
 			// tried to use shorthand but types didn't like me L
 			if (!Array.isArray(res)) res = [res];
 
-			for (var embed of res) {
-				var cache = EmbedCache.create({
+			for (const embed of res) {
+				const cache = EmbedCache.create({
 					url: link,
 					embed: embed,
 				});
@@ -279,7 +278,10 @@ export async function sendMessage(opts: MessageOptions) {
 		} as MessageCreateEvent),
 	]);
 
-	postHandleMessage(message).catch((e) => {}); // no await as it should catch error non-blockingly
+	// no await as it should catch error non-blockingly
+	postHandleMessage(message).catch((e) =>
+		console.error("[Message] post-message handler failed", e),
+	);
 
 	return message;
 }
diff --git a/src/api/util/handlers/Voice.ts b/src/api/util/handlers/Voice.ts
index d8d5c279..24bfa7b3 100644
--- a/src/api/util/handlers/Voice.ts
+++ b/src/api/util/handlers/Voice.ts
@@ -31,7 +31,7 @@ export async function getVoiceRegions(ipAddress: string, vip: boolean) {
 
 		let min = Number.POSITIVE_INFINITY;
 
-		for (let ar of availableRegions) {
+		for (const ar of availableRegions) {
 			//TODO the endpoint location should be saved in the database if not already present to prevent IPAnalysis call
 			const dist = distanceBetweenLocations(
 				clientIpAnalysis,
diff --git a/src/api/util/handlers/route.ts b/src/api/util/handlers/route.ts
index 6fcc6c73..cb160637 100644
--- a/src/api/util/handlers/route.ts
+++ b/src/api/util/handlers/route.ts
@@ -34,6 +34,8 @@ import { NextFunction, Request, Response } from "express";
 import { AnyValidateFunction } from "ajv/dist/core";
 
 declare global {
+	// TODO: fix this
+	// eslint-disable-next-line @typescript-eslint/no-namespace
 	namespace Express {
 		interface Request {
 			permission?: Permissions;
@@ -53,7 +55,7 @@ export interface RouteOptions {
 	body?: `${string}Schema`; // typescript interface name
 	test?: {
 		response?: RouteResponse;
-		body?: any;
+		body?: unknown;
 		path?: string;
 		event?: EVENT | EVENT[];
 		headers?: Record<string, string>;
@@ -61,7 +63,7 @@ export interface RouteOptions {
 }
 
 export function route(opts: RouteOptions) {
-	var validate: AnyValidateFunction<any> | undefined;
+	let validate: AnyValidateFunction | undefined;
 	if (opts.body) {
 		validate = ajv.getSchema(opts.body);
 		if (!validate) throw new Error(`Body schema ${opts.body} not found`);
diff --git a/src/api/util/utility/EmbedHandlers.ts b/src/api/util/utility/EmbedHandlers.ts
index 522ff82b..8466a374 100644
--- a/src/api/util/utility/EmbedHandlers.ts
+++ b/src/api/util/utility/EmbedHandlers.ts
@@ -17,13 +17,13 @@
 */
 
 import { Config, Embed, EmbedType } from "@fosscord/util";
-import fetch, { Response } from "node-fetch";
+import fetch, { RequestInit } from "node-fetch";
 import * as cheerio from "cheerio";
 import probe from "probe-image-size";
 import crypto from "crypto";
 import { yellow } from "picocolors";
 
-export const DEFAULT_FETCH_OPTIONS: any = {
+export const DEFAULT_FETCH_OPTIONS: RequestInit = {
 	redirect: "follow",
 	follow: 1,
 	headers: {
@@ -50,7 +50,7 @@ export const getProxyUrl = (
 
 	// Imagor
 	if (imagorServerUrl) {
-		let path = `${width}x${height}/${url.host}${url.pathname}`;
+		const path = `${width}x${height}/${url.host}${url.pathname}`;
 
 		const hash = crypto
 			.createHmac("sha1", secret)
@@ -92,8 +92,8 @@ export const getMetaDescriptions = (text: string) => {
 		image: getMeta($, "og:image") || getMeta($, "twitter:image"),
 		image_fallback: $(`image`).attr("src"),
 		video_fallback: $(`video`).attr("src"),
-		width: parseInt(getMeta($, "og:image:width")!) || 0,
-		height: parseInt(getMeta($, "og:image:height")!) || 0,
+		width: parseInt(getMeta($, "og:image:width") || "0"),
+		height: parseInt(getMeta($, "og:image:height") || "0"),
 		url: getMeta($, "og:url"),
 		youtube_embed: getMeta($, "og:video:secure_url"),
 	};
@@ -192,8 +192,8 @@ export const EmbedHandlers: {
 				proxy_url: metas.image
 					? getProxyUrl(
 							new URL(metas.image),
-							metas.width!,
-							metas.height!,
+							metas.width,
+							metas.height,
 					  )
 					: undefined,
 			},
@@ -239,9 +239,9 @@ export const EmbedHandlers: {
 		const text = json.data.text;
 		const created_at = new Date(json.data.created_at);
 		const metrics = json.data.public_metrics;
-		let media = json.includes.media?.filter(
-			(x: any) => x.type == "photo",
-		) as any[]; // TODO: video
+		const media = json.includes.media?.filter(
+			(x: { type: string }) => x.type == "photo",
+		);
 
 		const embed: Embed = {
 			type: EmbedType.rich,
@@ -334,7 +334,7 @@ export const EmbedHandlers: {
 				width: 640,
 				height: 640,
 				proxy_url: metas.image
-					? getProxyUrl(new URL(metas.image!), 640, 640)
+					? getProxyUrl(new URL(metas.image), 640, 640)
 					: undefined,
 				url: metas.image,
 			},
@@ -365,9 +365,9 @@ export const EmbedHandlers: {
 				url: url.href,
 				proxy_url: metas.image
 					? getProxyUrl(
-							new URL(metas.image!),
-							metas.width!,
-							metas.height!,
+							new URL(metas.image),
+							metas.width,
+							metas.height,
 					  )
 					: undefined,
 			},
@@ -395,7 +395,7 @@ export const EmbedHandlers: {
 				height: 215,
 				url: metas.image,
 				proxy_url: metas.image
-					? getProxyUrl(new URL(metas.image!), 460, 215)
+					? getProxyUrl(new URL(metas.image), 460, 215)
 					: undefined,
 			},
 			provider: {
@@ -436,7 +436,7 @@ export const EmbedHandlers: {
 				// TODO: does this adjust with aspect ratio?
 				width: metas.width,
 				height: metas.height,
-				url: metas.youtube_embed!,
+				url: metas.youtube_embed,
 			},
 			url: url.href,
 			type: EmbedType.video,
@@ -447,9 +447,9 @@ export const EmbedHandlers: {
 				url: metas.image,
 				proxy_url: metas.image
 					? getProxyUrl(
-							new URL(metas.image!),
-							metas.width!,
-							metas.height!,
+							new URL(metas.image),
+							metas.width,
+							metas.height,
 					  )
 					: undefined,
 			},
diff --git a/src/api/util/utility/RandomInviteID.ts b/src/api/util/utility/RandomInviteID.ts
index e95b4d1d..7ce54ad2 100644
--- a/src/api/util/utility/RandomInviteID.ts
+++ b/src/api/util/utility/RandomInviteID.ts
@@ -24,7 +24,7 @@ import crypto from "crypto";
 
 export function random(length = 6) {
 	// Declare all characters
-	let chars =
+	const chars =
 		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 
 	// Pick characers randomly
@@ -38,14 +38,14 @@ export function random(length = 6) {
 
 export function snowflakeBasedInvite() {
 	// Declare all characters
-	let chars =
+	const chars =
 		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-	let base = BigInt(chars.length);
+	const base = BigInt(chars.length);
 	let snowflake = Snowflake.generateWorkerProcess();
 
 	// snowflakes hold ~10.75 characters worth of entropy;
 	// safe to generate a 8-char invite out of them
-	let str = "";
+	const str = "";
 	for (let i = 0; i < 10; i++) {
 		str.concat(chars.charAt(Number(snowflake % base)));
 		snowflake = snowflake / base;
diff --git a/src/api/util/utility/captcha.ts b/src/api/util/utility/captcha.ts
index 2d31f891..bd05582f 100644
--- a/src/api/util/utility/captcha.ts
+++ b/src/api/util/utility/captcha.ts
@@ -47,7 +47,10 @@ export async function verifyCaptcha(response: string, ip?: string) {
 	const { security } = Config.get();
 	const { service, secret, sitekey } = security.captcha;
 
-	if (!service) throw new Error("Cannot verify captcha without service");
+	if (!service || !secret || !sitekey)
+		throw new Error(
+			"CAPTCHA is not configured correctly. https://docs.fosscord.com/setup/server/security/captcha/",
+		);
 
 	const res = await fetch(verifyEndpoints[service], {
 		method: "POST",
@@ -56,9 +59,9 @@ export async function verifyCaptcha(response: string, ip?: string) {
 		},
 		body:
 			`response=${encodeURIComponent(response)}` +
-			`&secret=${encodeURIComponent(secret!)}` +
-			`&sitekey=${encodeURIComponent(sitekey!)}` +
-			(ip ? `&remoteip=${encodeURIComponent(ip!)}` : ""),
+			`&secret=${encodeURIComponent(secret)}` +
+			`&sitekey=${encodeURIComponent(sitekey)}` +
+			(ip ? `&remoteip=${encodeURIComponent(ip)}` : ""),
 	});
 
 	return (await res.json()) as hcaptchaResponse | recaptchaResponse;
diff --git a/src/api/util/utility/ipAddress.ts b/src/api/util/utility/ipAddress.ts
index 785844ce..71a48682 100644
--- a/src/api/util/utility/ipAddress.ts
+++ b/src/api/util/utility/ipAddress.ts
@@ -85,7 +85,7 @@ export async function IPAnalysis(ip: string): Promise<typeof exampleData> {
 
 	return (
 		await fetch(`https://api.ipdata.co/${ip}?api-key=${ipdataApiKey}`)
-	).json() as any; // TODO: types
+	).json();
 }
 
 export function isProxy(data: typeof exampleData) {
@@ -97,14 +97,21 @@ export function isProxy(data: typeof exampleData) {
 }
 
 export function getIpAdress(req: Request): string {
+	// TODO: express can do this (trustProxies: true)?
+
 	return (
+		// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 		// @ts-ignore
 		req.headers[Config.get().security.forwadedFor] ||
 		req.socket.remoteAddress
 	);
 }
 
-export function distanceBetweenLocations(loc1: any, loc2: any): number {
+type Location = { latitude: number; longitude: number };
+export function distanceBetweenLocations(
+	loc1: Location,
+	loc2: Location,
+): number {
 	return distanceBetweenCoords(
 		loc1.latitude,
 		loc1.longitude,
diff --git a/src/api/util/utility/passwordStrength.ts b/src/api/util/utility/passwordStrength.ts
index c4dcd509..b293b856 100644
--- a/src/api/util/utility/passwordStrength.ts
+++ b/src/api/util/utility/passwordStrength.ts
@@ -23,7 +23,7 @@ const reNUMBER = /[0-9]/g;
 const reUPPERCASELETTER = /[A-Z]/g;
 const reSYMBOLS = /[A-Z,a-z,0-9]/g;
 
-const blocklist: string[] = []; // TODO: update ones passwordblocklist is stored in db
+// const blocklist: string[] = []; // TODO: update ones passwordblocklist is stored in db
 /*
  * https://en.wikipedia.org/wiki/Password_policy
  * password must meet following criteria, to be perfect:
@@ -38,7 +38,7 @@ const blocklist: string[] = []; // TODO: update ones passwordblocklist is stored
 export function checkPassword(password: string): number {
 	const { minLength, minNumbers, minUpperCase, minSymbols } =
 		Config.get().register.password;
-	var strength = 0;
+	let strength = 0;
 
 	// checks for total password len
 	if (password.length >= minLength - 1) {
@@ -68,13 +68,13 @@ export function checkPassword(password: string): number {
 		strength = 0;
 	}
 
-	let entropyMap: { [key: string]: number } = {};
+	const entropyMap: { [key: string]: number } = {};
 	for (let i = 0; i < password.length; i++) {
 		if (entropyMap[password[i]]) entropyMap[password[i]]++;
 		else entropyMap[password[i]] = 1;
 	}
 
-	let entropies = Object.values(entropyMap);
+	const entropies = Object.values(entropyMap);
 
 	entropies.map((x) => x / entropyMap.length);
 	strength +=