summary refs log tree commit diff
path: root/src/api/util
diff options
context:
space:
mode:
authorMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2023-01-20 18:10:47 +1100
committerGitHub <noreply@github.com>2023-01-20 18:10:47 +1100
commit084dc0be08555891cad4c2bb984822a62ec5ec9f (patch)
treeed2ca0fafefa2224ae32761f955f63935422a97d /src/api/util
parentfix: route file regex (#956) (diff)
downloadserver-084dc0be08555891cad4c2bb984822a62ec5ec9f.tar.xz
Add ESLint (#941)
* Add eslint, switch to lint-staged for precommit

* Fix all ESLint errors

* Update GH workflow to check prettier and eslint
Diffstat (limited to 'src/api/util')
-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
9 files changed, 74 insertions, 61 deletions
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 +=