diff options
author | Madeline <46743919+MaddyUnderStars@users.noreply.github.com> | 2023-01-20 18:10:47 +1100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-20 18:10:47 +1100 |
commit | 084dc0be08555891cad4c2bb984822a62ec5ec9f (patch) | |
tree | ed2ca0fafefa2224ae32761f955f63935422a97d /src/api/util | |
parent | fix: route file regex (#956) (diff) | |
download | server-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.ts | 17 | ||||
-rw-r--r-- | src/api/util/handlers/Message.ts | 34 | ||||
-rw-r--r-- | src/api/util/handlers/Voice.ts | 2 | ||||
-rw-r--r-- | src/api/util/handlers/route.ts | 6 | ||||
-rw-r--r-- | src/api/util/utility/EmbedHandlers.ts | 38 | ||||
-rw-r--r-- | src/api/util/utility/RandomInviteID.ts | 8 | ||||
-rw-r--r-- | src/api/util/utility/captcha.ts | 11 | ||||
-rw-r--r-- | src/api/util/utility/ipAddress.ts | 11 | ||||
-rw-r--r-- | src/api/util/utility/passwordStrength.ts | 8 |
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 += |