diff options
Diffstat (limited to '')
43 files changed, 208 insertions, 224 deletions
diff --git a/src/util/config/types/GifConfiguration.ts b/src/util/config/types/GifConfiguration.ts index a623448c..0e5583fa 100644 --- a/src/util/config/types/GifConfiguration.ts +++ b/src/util/config/types/GifConfiguration.ts @@ -18,6 +18,6 @@ export class GifConfiguration { enabled: boolean = true; - provider: "tenor" = "tenor"; // more coming soon + provider = "tenor" as const; // more coming soon apiKey?: string = "LIVDSRZULELA"; } diff --git a/src/util/dtos/DmChannelDTO.ts b/src/util/dtos/DmChannelDTO.ts index 9d4d8dc3..02f7e8f3 100644 --- a/src/util/dtos/DmChannelDTO.ts +++ b/src/util/dtos/DmChannelDTO.ts @@ -44,16 +44,14 @@ export class DmChannelDTO { obj.type = channel.type; obj.recipients = ( await Promise.all( - channel - .recipients!.filter( - (r) => !excluded_recipients.includes(r.user_id), - ) + channel.recipients + ?.filter((r) => !excluded_recipients.includes(r.user_id)) .map(async (r) => { return await User.findOneOrFail({ where: { id: r.user_id }, select: PublicUserProjection, }); - }), + }) || [], ) ).map((u) => new MinimalPublicUserDTO(u)); return obj; diff --git a/src/util/dtos/ReadyGuildDTO.ts b/src/util/dtos/ReadyGuildDTO.ts index 38303aed..97e6931f 100644 --- a/src/util/dtos/ReadyGuildDTO.ts +++ b/src/util/dtos/ReadyGuildDTO.ts @@ -23,7 +23,7 @@ export interface IReadyGuildDTO { channels: Channel[]; data_mode: string; // what is this emojis: Emoji[]; - guild_scheduled_events: any[]; + guild_scheduled_events: unknown[]; // TODO id: string; large: boolean | undefined; lazy: boolean; @@ -57,12 +57,12 @@ export interface IReadyGuildDTO { max_video_channel_users: number | undefined; max_members: number | undefined; nsfw_level: number | undefined; - hub_type?: any | null; // ???? + hub_type?: unknown | null; // ???? }; roles: Role[]; - stage_instances: any[]; + stage_instances: unknown[]; stickers: Sticker[]; - threads: any[]; + threads: unknown[]; version: string; } @@ -71,7 +71,7 @@ export class ReadyGuildDTO implements IReadyGuildDTO { channels: Channel[]; data_mode: string; // what is this emojis: Emoji[]; - guild_scheduled_events: any[]; + guild_scheduled_events: unknown[]; id: string; large: boolean | undefined; lazy: boolean; @@ -105,12 +105,12 @@ export class ReadyGuildDTO implements IReadyGuildDTO { max_video_channel_users: number | undefined; max_members: number | undefined; nsfw_level: number | undefined; - hub_type?: any | null; // ???? + hub_type?: unknown | null; // ???? }; roles: Role[]; - stage_instances: any[]; + stage_instances: unknown[]; stickers: Sticker[]; - threads: any[]; + threads: unknown[]; version: string; constructor(guild: Guild) { diff --git a/src/util/entities/Application.ts b/src/util/entities/Application.ts index 94a015e9..94709320 100644 --- a/src/util/entities/Application.ts +++ b/src/util/entities/Application.ts @@ -16,16 +16,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - OneToOne, - RelationId, -} from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, OneToOne } from "typeorm"; import { BaseClass } from "./BaseClass"; -import { Guild } from "./Guild"; import { Team } from "./Team"; import { User } from "./User"; @@ -44,7 +36,7 @@ export class Application extends BaseClass { summary: string = ""; @Column({ type: "simple-json", nullable: true }) - type?: any; + type?: object; // TODO: this type is bad @Column() hook: boolean = true; @@ -176,6 +168,6 @@ export interface ApplicationCommandInteractionData { export interface ApplicationCommandInteractionDataOption { name: string; - value?: any; + value?: unknown; options?: ApplicationCommandInteractionDataOption[]; } diff --git a/src/util/entities/AuditLog.ts b/src/util/entities/AuditLog.ts index 68893ea8..0cc2fc04 100644 --- a/src/util/entities/AuditLog.ts +++ b/src/util/entities/AuditLog.ts @@ -173,8 +173,8 @@ export interface AuditLogChangeValue { explicit_content_filter?: number; default_message_notifications?: number; vanity_url_code?: string; - $add?: {}[]; - $remove?: {}[]; + $add?: object[]; // TODO: These types are bad. + $remove?: object[]; prune_delete_days?: number; widget_enabled?: boolean; widget_channel_id?: string; diff --git a/src/util/entities/BackupCodes.ts b/src/util/entities/BackupCodes.ts index 1245ecd1..467e1fe3 100644 --- a/src/util/entities/BackupCodes.ts +++ b/src/util/entities/BackupCodes.ts @@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; import crypto from "crypto"; @@ -38,7 +38,7 @@ export class BackupCode extends BaseClass { } export function generateMfaBackupCodes(user_id: string) { - let backup_codes: BackupCode[] = []; + const backup_codes: BackupCode[] = []; for (let i = 0; i < 10; i++) { const code = BackupCode.create({ user: { id: user_id }, diff --git a/src/util/entities/BaseClass.ts b/src/util/entities/BaseClass.ts index e3df5ad4..445b3fc9 100644 --- a/src/util/entities/BaseClass.ts +++ b/src/util/entities/BaseClass.ts @@ -29,7 +29,7 @@ import { getDatabase } from "../util/Database"; import { OrmUtils } from "../imports/OrmUtils"; export class BaseClassWithoutId extends BaseEntity { - private get construct(): any { + private get construct() { return this.constructor; } @@ -37,19 +37,24 @@ export class BaseClassWithoutId extends BaseEntity { return getDatabase()?.getMetadata(this.construct); } - assign(props: any) { + assign(props: object) { OrmUtils.mergeDeep(this, props); return this; } + // TODO: fix eslint + // eslint-disable-next-line @typescript-eslint/no-explicit-any toJSON(): any { return Object.fromEntries( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/no-non-null-assertion this.metadata!.columns // @ts-ignore .map((x) => [x.propertyName, this[x.propertyName]]) .concat( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore this.metadata.relations.map((x) => [ x.propertyName, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore this[x.propertyName], ]), diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts index 2e5f030c..1f128713 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts @@ -35,7 +35,6 @@ import { Snowflake, trimSpecial, InvisibleCharacters, - ChannelTypes, } from "../util"; import { ChannelCreateEvent, ChannelRecipientRemoveEvent } from "../interfaces"; import { Recipient } from "./Recipient"; @@ -219,7 +218,7 @@ export class Channel extends BaseClass { !guild.features.includes("ALLOW_INVALID_CHANNEL_NAMES") && channel.name ) { - for (var character of InvisibleCharacters) + for (const character of InvisibleCharacters) if (channel.name.includes(character)) throw new HTTPError( "Channel name cannot include invalid characters", @@ -237,7 +236,7 @@ export class Channel extends BaseClass { 403, ); - if (channel.name.match(/\-\-+/g)) + if (channel.name.match(/--+/g)) throw new HTTPError( "Channel name cannot include multiple adjacent dashes.", 403, @@ -344,8 +343,9 @@ export class Channel extends BaseClass { relations: ["channel", "channel.recipients"], }); - for (let ur of userRecipients) { - let re = ur.channel.recipients!.map((r) => r.user_id); + for (const ur of userRecipients) { + if (!ur.channel.recipients) continue; + const re = ur.channel.recipients.map((r) => r.user_id); if (re.length === channelRecipients.length) { if (containsAll(re, channelRecipients)) { if (channel == null) { @@ -380,8 +380,8 @@ export class Channel extends BaseClass { const channel_dto = await DmChannelDTO.from(channel); - if (type === ChannelType.GROUP_DM) { - for (let recipient of channel.recipients!) { + if (type === ChannelType.GROUP_DM && channel.recipients) { + for (const recipient of channel.recipients) { await emitEvent({ event: "CHANNEL_CREATE", data: channel_dto.excludedRecipients([recipient.user_id]), diff --git a/src/util/entities/ConnectedAccount.ts b/src/util/entities/ConnectedAccount.ts index 1d0d0586..9f0ce35e 100644 --- a/src/util/entities/ConnectedAccount.ts +++ b/src/util/entities/ConnectedAccount.ts @@ -20,8 +20,10 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; -export interface PublicConnectedAccount - extends Pick<ConnectedAccount, "name" | "type" | "verified"> {} +export type PublicConnectedAccount = Pick< + ConnectedAccount, + "name" | "type" | "verified" +>; @Entity("connected_accounts") export class ConnectedAccount extends BaseClass { diff --git a/src/util/entities/Emoji.ts b/src/util/entities/Emoji.ts index 95a641f1..94ce3d54 100644 --- a/src/util/entities/Emoji.ts +++ b/src/util/entities/Emoji.ts @@ -20,7 +20,6 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { User } from "."; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; -import { Role } from "./Role"; @Entity("emojis") export class Emoji extends BaseClass { diff --git a/src/util/entities/Encryption.ts b/src/util/entities/Encryption.ts index db9d0983..016b4331 100644 --- a/src/util/entities/Encryption.ts +++ b/src/util/entities/Encryption.ts @@ -16,32 +16,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - RelationId, -} from "typeorm"; +import { Column, Entity } from "typeorm"; import { BaseClass } from "./BaseClass"; -import { Guild } from "./Guild"; -import { PublicUserProjection, User } from "./User"; -import { HTTPError } from "lambert-server"; -import { - containsAll, - emitEvent, - getPermission, - Snowflake, - trimSpecial, - InvisibleCharacters, -} from "../util"; -import { BitField, BitFieldResolvable, BitFlag } from "../util/BitField"; -import { Recipient } from "./Recipient"; -import { Message } from "./Message"; -import { ReadState } from "./ReadState"; -import { Invite } from "./Invite"; -import { DmChannelDTO } from "../dtos"; @Entity("security_settings") export class SecuritySettings extends BaseClass { diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts index 9f300334..c835f5fc 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts @@ -20,10 +20,8 @@ import { Column, Entity, JoinColumn, - ManyToMany, ManyToOne, OneToMany, - OneToOne, RelationId, } from "typeorm"; import { Config, handleFile, Snowflake } from ".."; @@ -370,12 +368,12 @@ export class Guild extends BaseClass { } }); - for (const channel of body.channels?.sort((a, b) => + for (const channel of body.channels.sort((a) => a.parent_id ? 1 : -1, )) { - var id = ids.get(channel.id) || Snowflake.generate(); + const id = ids.get(channel.id) || Snowflake.generate(); - var parent_id = ids.get(channel.parent_id); + const parent_id = ids.get(channel.parent_id); await Channel.createChannel( { ...channel, guild_id, id, parent_id }, diff --git a/src/util/entities/Member.ts b/src/util/entities/Member.ts index 801b5738..c68fe215 100644 --- a/src/util/entities/Member.ts +++ b/src/util/entities/Member.ts @@ -33,7 +33,7 @@ import { RelationId, } from "typeorm"; import { Guild } from "./Guild"; -import { Config, emitEvent, FieldErrors } from "../util"; +import { Config, emitEvent } from "../util"; import { GuildCreateEvent, GuildDeleteEvent, @@ -212,12 +212,16 @@ export class Member extends BaseClassWithoutId { } static async addRole(user_id: string, guild_id: string, role_id: string) { - const [member, role] = await Promise.all([ + const [member] = await Promise.all([ Member.findOneOrFail({ where: { id: user_id, guild_id }, relations: ["user", "roles"], // we don't want to load the role objects just the ids - //@ts-ignore - select: ["index", "roles.id"], // TODO fix type + select: { + index: true, + roles: { + id: true, + }, + }, }), Role.findOneOrFail({ where: { id: role_id, guild_id }, @@ -249,8 +253,12 @@ export class Member extends BaseClassWithoutId { Member.findOneOrFail({ where: { id: user_id, guild_id }, relations: ["user", "roles"], // we don't want to load the role objects just the ids - //@ts-ignore - select: ["roles.id", "index"], // TODO: fix type + select: { + index: true, + roles: { + id: true, + }, + }, }), await Role.findOneOrFail({ where: { id: role_id, guild_id } }), ]); @@ -327,7 +335,7 @@ export class Member extends BaseClassWithoutId { guild_id, user: { sessions: { - status: Not("invisible" as "invisible"), // lol typescript? + status: Not("invisible" as const), // lol typescript? }, }, }, @@ -506,8 +514,7 @@ export const PublicMemberProjection: PublicMemberKeys[] = [ "premium_since", ]; -// @ts-ignore -export type PublicMember = Pick<Member, Omit<PublicMemberKeys, "roles">> & { +export type PublicMember = Omit<Pick<Member, PublicMemberKeys>, "roles"> & { user: PublicUser; roles: string[]; // only role ids not objects }; diff --git a/src/util/entities/ReadState.ts b/src/util/entities/ReadState.ts index a907b701..825beb03 100644 --- a/src/util/entities/ReadState.ts +++ b/src/util/entities/ReadState.ts @@ -26,7 +26,6 @@ import { } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; -import { Message } from "./Message"; import { User } from "./User"; // for read receipts diff --git a/src/util/entities/StickerPack.ts b/src/util/entities/StickerPack.ts index 911d8d05..ce8d5e87 100644 --- a/src/util/entities/StickerPack.ts +++ b/src/util/entities/StickerPack.ts @@ -22,7 +22,6 @@ import { JoinColumn, ManyToOne, OneToMany, - OneToOne, RelationId, } from "typeorm"; import { Sticker } from "."; diff --git a/src/util/entities/Team.ts b/src/util/entities/Team.ts index 730ff75f..82859409 100644 --- a/src/util/entities/Team.ts +++ b/src/util/entities/Team.ts @@ -20,7 +20,6 @@ import { Column, Entity, JoinColumn, - ManyToMany, ManyToOne, OneToMany, RelationId, diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index ed9e3884..658584c3 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -17,8 +17,6 @@ */ import { - BeforeInsert, - BeforeUpdate, Column, Entity, FindOneOptions, @@ -34,6 +32,7 @@ import { Member } from "./Member"; import { UserSettings } from "./UserSettings"; import { Session } from "./Session"; import { Config, FieldErrors, Snowflake, trimSpecial, adjustEmail } from ".."; +import { Request } from "express"; export enum PublicUserEnum { username, @@ -80,7 +79,7 @@ export const PrivateUserProjection = [ // Private user data that should never get sent to the client export type PublicUser = Pick<User, PublicUserKeys>; -export interface UserPublic extends Pick<User, PublicUserKeys> {} +export type UserPublic = Pick<User, PublicUserKeys>; export interface UserPrivate extends Pick<User, PrivateUserKeys> { locale: string; @@ -266,6 +265,7 @@ export class User extends BaseClass { } toPublicUser() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const user: any = {}; PublicUserProjection.forEach((x) => { user[x] = this[x]; @@ -277,6 +277,7 @@ export class User extends BaseClass { return await User.findOneOrFail({ where: { id: user_id }, ...opts, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore select: [...PublicUserProjection, ...(opts?.select || [])], // TODO: fix }); @@ -328,7 +329,6 @@ export class User extends BaseClass { email, username, password, - date_of_birth, id, req, }: { @@ -337,7 +337,7 @@ export class User extends BaseClass { email?: string; date_of_birth?: Date; // "2000-04-03" id?: string; - req?: any; + req?: Request; }) { // trim special uf8 control characters -> Backspace, Newline, ... username = trimSpecial(username); @@ -348,7 +348,8 @@ export class User extends BaseClass { throw FieldErrors({ username: { code: "USERNAME_TOO_MANY_USERS", - message: req.t("auth:register.USERNAME_TOO_MANY_USERS"), + message: + req?.t("auth:register.USERNAME_TOO_MANY_USERS") || "", }, }); } @@ -357,7 +358,7 @@ export class User extends BaseClass { // appearently discord doesn't save the date of birth and just calculate if nsfw is allowed // if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false const language = - req.language === "en" ? "en-US" : req.language || "en-US"; + req?.language === "en" ? "en-US" : req?.language || "en-US"; const settings = UserSettings.create({ locale: language, @@ -386,7 +387,9 @@ export class User extends BaseClass { setImmediate(async () => { if (Config.get().guild.autoJoin.enabled) { for (const guild of Config.get().guild.autoJoin.guilds || []) { - await Member.addToGuild(user.id, guild).catch((e) => {}); + await Member.addToGuild(user.id, guild).catch((e) => + console.error("[Autojoin]", e), + ); } } }); diff --git a/src/util/imports/OrmUtils.ts b/src/util/imports/OrmUtils.ts index e6551471..039c81fe 100644 --- a/src/util/imports/OrmUtils.ts +++ b/src/util/imports/OrmUtils.ts @@ -1,25 +1,10 @@ -/* - Fosscord: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Fosscord and Fosscord Contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. -*/ - -//source: https://github.com/typeorm/typeorm/blob/master/src/util/OrmUtils.ts +// source: https://github.com/typeorm/typeorm/blob/master/src/util/OrmUtils.ts +// Copyright (c) 2015-2022 TypeORM. http://typeorm.github.io +/* eslint-disable @typescript-eslint/no-explicit-any */ +// @fc-license-skip export class OrmUtils { // Checks if it's an object made by Object.create(null), {} or new Object() - private static isPlainObject(item: any) { + private static isPlainObject(item: unknown) { if (item === null || item === undefined) { return false; } diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts index e902cdf3..c3bfbf9b 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts @@ -39,6 +39,7 @@ import { Presence, UserSettings, IReadyGuildDTO, + ReadState, } from "@fosscord/util"; export interface Event { @@ -47,6 +48,7 @@ export interface Event { channel_id?: string; created_at?: Date; event: EVENT; + // eslint-disable-next-line @typescript-eslint/no-explicit-any data?: any; } @@ -103,12 +105,12 @@ export interface ReadyEventData { [number, [[number, [number, number]]]], { b: number; k: bigint[] }[], ][]; - guild_join_requests?: any[]; // ? what is this? this is new + guild_join_requests?: unknown[]; // ? what is this? this is new shard?: [number, number]; user_settings?: UserSettings; relationships?: PublicRelationship[]; // TODO read_state: { - entries: any[]; // TODO + entries: ReadState[]; // TODO partial: boolean; version: number; }; @@ -124,7 +126,7 @@ export interface ReadyEventData { merged_members?: PublicMember[][]; // probably all users who the user is in contact with users?: PublicUser[]; - sessions: any[]; + sessions: unknown[]; } export interface ReadyEvent extends Event { @@ -178,7 +180,7 @@ export interface GuildCreateEvent extends Event { joined_at: Date; // TODO: add them to guild guild_scheduled_events: never[]; - guild_hashes: {}; + guild_hashes: unknown; presences: never[]; stage_instances: never[]; threads: never[]; @@ -408,7 +410,7 @@ export interface TypingStartEvent extends Event { export interface UserUpdateEvent extends Event { event: "USER_UPDATE"; - data: User; + data: Omit<User, "data">; } export interface UserDeleteEvent extends Event { diff --git a/src/util/interfaces/Interaction.ts b/src/util/interfaces/Interaction.ts index 8695fca9..4158eda1 100644 --- a/src/util/interfaces/Interaction.ts +++ b/src/util/interfaces/Interaction.ts @@ -21,7 +21,7 @@ import { AllowedMentions, Embed } from "../entities/Message"; export interface Interaction { id: string; type: InteractionType; - data?: {}; + data?: object; // TODO typing guild_id: string; channel_id: string; member_id: string; diff --git a/src/util/schemas/ChannelPermissionOverwriteSchema.ts b/src/util/schemas/ChannelPermissionOverwriteSchema.ts index 97fe9dee..62d0ad14 100644 --- a/src/util/schemas/ChannelPermissionOverwriteSchema.ts +++ b/src/util/schemas/ChannelPermissionOverwriteSchema.ts @@ -18,5 +18,4 @@ import { ChannelPermissionOverwrite } from "@fosscord/util"; -export interface ChannelPermissionOverwriteSchema - extends ChannelPermissionOverwrite {} +export type ChannelPermissionOverwriteSchema = ChannelPermissionOverwrite; diff --git a/src/util/schemas/IdentifySchema.ts b/src/util/schemas/IdentifySchema.ts index 18ab2b49..9bb14ca3 100644 --- a/src/util/schemas/IdentifySchema.ts +++ b/src/util/schemas/IdentifySchema.ts @@ -18,6 +18,8 @@ import { ActivitySchema } from "@fosscord/util"; +// TODO: Need a way to allow camalCase and pascal_case without just duplicating the schema + export const IdentifySchema = { token: String, $intents: BigInt, // discord uses a Integer for bitfields we use bigints tho. | instanceOf will automatically convert the Number to a BigInt @@ -98,7 +100,7 @@ export interface IdentifySchema { referring_domain_current?: string; release_channel?: "stable" | "dev" | "ptb" | "canary"; client_build_number?: number; - client_event_source?: any; + client_event_source?: string; client_version?: string; system_locale?: string; }; @@ -111,23 +113,23 @@ export interface IdentifySchema { guild_subscriptions?: boolean; capabilities?: number; client_state?: { - guild_hashes?: any; + guild_hashes?: unknown; highest_last_message_id?: string | number; read_state_version?: number; user_guild_settings_version?: number; user_settings_version?: number; useruser_guild_settings_version?: number; private_channels_version?: number; - guild_versions?: any; + guild_versions?: unknown; api_code_version?: number; }; clientState?: { - guildHashes?: any; + guildHashes?: unknown; highestLastMessageId?: string | number; readStateVersion?: number; userGuildSettingsVersion?: number; useruserGuildSettingsVersion?: number; - guildVersions?: any; + guildVersions?: unknown; apiCodeVersion?: number; }; v?: number; diff --git a/src/util/schemas/LazyRequestSchema.ts b/src/util/schemas/LazyRequestSchema.ts index 02fe0d8b..7cf3fd36 100644 --- a/src/util/schemas/LazyRequestSchema.ts +++ b/src/util/schemas/LazyRequestSchema.ts @@ -22,8 +22,8 @@ export interface LazyRequestSchema { activities?: boolean; threads?: boolean; typing?: true; - members?: any[]; - thread_member_lists?: any[]; + members?: unknown[]; + thread_member_lists?: unknown[]; } export const LazyRequestSchema = { @@ -32,6 +32,6 @@ export const LazyRequestSchema = { $channels: Object, $typing: Boolean, $threads: Boolean, - $members: [] as any[], - $thread_member_lists: [] as any[], + $members: [] as unknown[], + $thread_member_lists: [] as unknown[], }; diff --git a/src/util/schemas/MessageCreateSchema.ts b/src/util/schemas/MessageCreateSchema.ts index 1dd5d32c..4ee6e738 100644 --- a/src/util/schemas/MessageCreateSchema.ts +++ b/src/util/schemas/MessageCreateSchema.ts @@ -18,6 +18,11 @@ import { Embed } from "@fosscord/util"; +type Attachment = { + id: string; + filename: string; +}; + export interface MessageCreateSchema { type?: number; content?: string; @@ -41,11 +46,11 @@ export interface MessageCreateSchema { fail_if_not_exists?: boolean; }; payload_json?: string; - file?: any; + file?: { filename: string }; /** TODO: we should create an interface for attachments TODO: OpenWAAO<-->attachment-style metadata conversion **/ - attachments?: any[]; + attachments?: Attachment[]; sticker_ids?: string[]; } diff --git a/src/util/schemas/UserSettingsSchema.ts b/src/util/schemas/UserSettingsSchema.ts index e25588c4..5a590b02 100644 --- a/src/util/schemas/UserSettingsSchema.ts +++ b/src/util/schemas/UserSettingsSchema.ts @@ -18,4 +18,4 @@ import { UserSettings } from "@fosscord/util"; -export interface UserSettingsSchema extends Partial<UserSettings> {} +export type UserSettingsSchema = Partial<UserSettings>; diff --git a/src/util/schemas/Validator.ts b/src/util/schemas/Validator.ts index 26a88ef9..3190dd05 100644 --- a/src/util/schemas/Validator.ts +++ b/src/util/schemas/Validator.ts @@ -45,7 +45,7 @@ export const ajv = new Ajv({ addFormats(ajv); -export function validateSchema<G>(schema: string, data: G): G { +export function validateSchema<G extends object>(schema: string, data: G): G { const valid = ajv.validate(schema, normalizeBody(data)); if (!valid) throw ajv.errors; return data; @@ -55,13 +55,13 @@ export function validateSchema<G>(schema: string, data: G): G { // this removes null values as ajv doesn't treat them as undefined // normalizeBody allows to handle circular structures without issues // taken from https://github.com/serverless/serverless/blob/master/lib/classes/ConfigSchemaHandler/index.js#L30 (MIT license) -export const normalizeBody = (body: any = {}) => { +export const normalizeBody = (body: object = {}) => { const normalizedObjectsSet = new WeakSet(); - const normalizeObject = (object: any) => { + const normalizeObject = (object: object) => { if (normalizedObjectsSet.has(object)) return; normalizedObjectsSet.add(object); if (Array.isArray(object)) { - for (const [index, value] of object.entries()) { + for (const [, value] of object.entries()) { if (typeof value === "object") normalizeObject(value); } } else { @@ -75,6 +75,8 @@ export const normalizeBody = (body: any = {}) => { key === "discovery_splash" ) continue; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore delete object[key]; } else if (typeof value === "object") { normalizeObject(value); diff --git a/src/util/util/Array.ts b/src/util/util/Array.ts index 1935fa7a..dbc75b85 100644 --- a/src/util/util/Array.ts +++ b/src/util/util/Array.ts @@ -16,6 +16,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -export function containsAll(arr: any[], target: any[]) { +// TODO: remove this function. + +export function containsAll(arr: unknown[], target: unknown[]) { return target.every((v) => arr.includes(v)); } diff --git a/src/util/util/AutoUpdate.ts b/src/util/util/AutoUpdate.ts index 08836ea2..a4a97f3f 100644 --- a/src/util/util/AutoUpdate.ts +++ b/src/util/util/AutoUpdate.ts @@ -36,7 +36,7 @@ export function enableAutoUpdate(opts: { downloadType?: "zip"; }) { if (!opts.checkInterval) return; - var interval = 1000 * 60 * 60 * 24; + const interval = 1000 * 60 * 60 * 24; if (typeof opts.checkInterval === "number") opts.checkInterval = 1000 * interval; @@ -70,6 +70,7 @@ export function enableAutoUpdate(opts: { }); } +// eslint-disable-next-line @typescript-eslint/no-unused-vars async function download(url: string, dir: string) { try { // TODO: use file stream instead of buffer (to prevent crash because of high memory usage for big files) @@ -99,7 +100,7 @@ async function getLatestVersion(url: string) { try { const agent = new ProxyAgent(); const response = await fetch(url, { agent }); - const content = (await response.json()) as any; // TODO: types + const content = await response.json(); return content.version; } catch (error) { throw new Error("[Auto update] check failed for " + url); diff --git a/src/util/util/BitField.ts b/src/util/util/BitField.ts index 62bc3c46..d8758327 100644 --- a/src/util/util/BitField.ts +++ b/src/util/util/BitField.ts @@ -6,7 +6,7 @@ export type BitFieldResolvable = | number - | BigInt + | bigint | BitField | string | BitFieldResolvable[]; @@ -135,6 +135,7 @@ export class BitField { * @returns {number} */ static resolve(bit: BitFieldResolvable = BigInt(0)): bigint { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const FLAGS = this.FLAGS || this.constructor?.FLAGS; @@ -152,6 +153,7 @@ export class BitField { if (bit instanceof BitField) return bit.bitfield; if (Array.isArray(bit)) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const resolve = this.constructor?.resolve || this.resolve; return bit diff --git a/src/util/util/Config.ts b/src/util/util/Config.ts index 4b2dbb0d..b83f1d7b 100644 --- a/src/util/util/Config.ts +++ b/src/util/util/Config.ts @@ -24,8 +24,8 @@ import { ConfigValue } from "../config"; // TODO: yaml instead of json const overridePath = process.env.CONFIG_PATH ?? ""; -var config: ConfigValue; -var pairs: ConfigEntity[]; +let config: ConfigValue; +let pairs: ConfigEntity[]; // TODO: use events to inform about config updates // Config keys are separated with _ @@ -84,6 +84,8 @@ export const Config = { }; function applyConfig(val: ConfigValue) { + // TODO: typings + // eslint-disable-next-line @typescript-eslint/no-explicit-any async function apply(obj: any, key = ""): Promise<any> { if (typeof obj === "object" && obj !== null) return Promise.all( @@ -107,7 +109,9 @@ function applyConfig(val: ConfigValue) { } function pairsToConfig(pairs: ConfigEntity[]) { - var value: any = {}; + // TODO: typings + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const value: any = {}; pairs.forEach((p) => { const keys = p.key.split("_"); diff --git a/src/util/util/Constants.ts b/src/util/util/Constants.ts index d65fda58..1afdce49 100644 --- a/src/util/util/Constants.ts +++ b/src/util/util/Constants.ts @@ -1125,7 +1125,7 @@ export const MembershipStates = ["INSERTED", "INVITED", "ACCEPTED"]; export const WebhookTypes = ["Custom", "Incoming", "Channel Follower"]; function keyMirror(arr: string[]) { - let tmp = Object.create(null); + const tmp = Object.create(null); for (const value of arr) tmp[value] = value; return tmp; } diff --git a/src/util/util/Database.ts b/src/util/util/Database.ts index 38011b9c..e1a4003f 100644 --- a/src/util/util/Database.ts +++ b/src/util/util/Database.ts @@ -25,14 +25,14 @@ import path from "path"; // UUID extension option is only supported with postgres // We want to generate all id's with Snowflakes that's why we have our own BaseEntity class -var dbConnection: DataSource | undefined; +let dbConnection: DataSource | undefined; // For typeorm cli if (!process.env) { config(); } -let dbConnectionString = +const dbConnectionString = process.env.DATABASE || path.join(process.cwd(), "database.db"); const DatabaseType = dbConnectionString.includes("://") @@ -41,6 +41,7 @@ const DatabaseType = dbConnectionString.includes("://") const isSqlite = DatabaseType.includes("sqlite"); const DataSourceOptions = new DataSource({ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore type 'string' is not 'mysql' | 'sqlite' | 'mariadb' | etc etc type: DatabaseType, charset: "utf8mb4", diff --git a/src/util/util/Email.ts b/src/util/util/Email.ts index f45728fc..48d8cae1 100644 --- a/src/util/util/Email.ts +++ b/src/util/util/Email.ts @@ -17,27 +17,29 @@ */ export const EMAIL_REGEX = - /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; export function adjustEmail(email?: string): string | undefined { if (!email) return email; // body parser already checked if it is a valid email const parts = <RegExpMatchArray>email.match(EMAIL_REGEX); - // @ts-ignore if (!parts || parts.length < 5) return undefined; - const domain = parts[5]; - const user = parts[1]; - // TODO: check accounts with uncommon email domains - if (domain === "gmail.com" || domain === "googlemail.com") { - // replace .dots and +alternatives -> Gmail Dot Trick https://support.google.com/mail/answer/7436150 and https://generator.email/blog/gmail-generator - let v = user.replace(/[.]|(\+.*)/g, "") + "@gmail.com"; - } + return email; + // // TODO: The below code doesn't actually do anything. + // const domain = parts[5]; + // const user = parts[1]; - if (domain === "google.com") { - // replace .dots and +alternatives -> Google Staff GMail Dot Trick - let v = user.replace(/[.]|(\+.*)/g, "") + "@google.com"; - } + // // TODO: check accounts with uncommon email domains + // if (domain === "gmail.com" || domain === "googlemail.com") { + // // replace .dots and +alternatives -> Gmail Dot Trick https://support.google.com/mail/answer/7436150 and https://generator.email/blog/gmail-generator + // const v = user.replace(/[.]|(\+.*)/g, "") + "@gmail.com"; + // } - return email; + // if (domain === "google.com") { + // // replace .dots and +alternatives -> Google Staff GMail Dot Trick + // const v = user.replace(/[.]|(\+.*)/g, "") + "@google.com"; + // } + + // return email; } diff --git a/src/util/util/Event.ts b/src/util/util/Event.ts index 5e01e644..79be1a10 100644 --- a/src/util/util/Event.ts +++ b/src/util/util/Event.ts @@ -55,6 +55,7 @@ export async function emitEvent(payload: Omit<Event, "created_at">) { export async function initEvent() { await RabbitMQ.init(); // does nothing if rabbitmq is not setup if (RabbitMQ.connection) { + // empty on purpose? } else { // use event emitter // use process messages @@ -62,9 +63,9 @@ export async function initEvent() { } export interface EventOpts extends Event { - acknowledge?: Function; + acknowledge?: () => unknown; channel?: Channel; - cancel: Function; + cancel: (id?: string) => unknown; } export interface ListenEventOpts { @@ -80,17 +81,18 @@ export interface ProcessEvent { export async function listenEvent( event: string, - callback: (event: EventOpts) => any, + callback: (event: EventOpts) => unknown, opts?: ListenEventOpts, ) { if (RabbitMQ.connection) { - return await rabbitListen( - // @ts-ignore - opts?.channel || RabbitMQ.channel, - event, - callback, - { acknowledge: opts?.acknowledge }, - ); + const channel = opts?.channel || RabbitMQ.channel; + if (!channel) + throw new Error( + "[Events] An event was sent without an associated channel", + ); + return await rabbitListen(channel, event, callback, { + acknowledge: opts?.acknowledge, + }); } else if (process.env.EVENT_TRANSMISSION === "process") { const cancel = async () => { process.removeListener("message", listener); @@ -103,13 +105,13 @@ export async function listenEvent( callback({ ...msg.event, cancel }); }; - //@ts-ignore apparently theres no function addListener with this signature - process.addListener("message", listener); + // TODO: assert the type is correct? + process.addListener("message", (msg) => listener(msg as ProcessEvent)); process.setMaxListeners(process.getMaxListeners() + 1); return cancel; } else { - const listener = (opts: any) => callback({ ...opts, cancel }); + const listener = (opts: EventOpts) => callback({ ...opts, cancel }); const cancel = async () => { events.removeListener(event, listener); events.setMaxListeners(events.getMaxListeners() - 1); @@ -124,7 +126,7 @@ export async function listenEvent( async function rabbitListen( channel: Channel, id: string, - callback: (event: EventOpts) => any, + callback: (event: EventOpts) => unknown, opts?: { acknowledge?: boolean }, ) { await channel.assertExchange(id, "fanout", { durable: false }); diff --git a/src/util/util/FieldError.ts b/src/util/util/FieldError.ts index 69df6f8e..eff793a8 100644 --- a/src/util/util/FieldError.ts +++ b/src/util/util/FieldError.ts @@ -42,7 +42,7 @@ export class FieldError extends Error { constructor( public code: string | number, public message: string, - public errors?: any, + public errors?: object, // TODO: I don't like this typing. ) { super(message); } diff --git a/src/util/util/Permissions.ts b/src/util/util/Permissions.ts index 19614640..996c72ea 100644 --- a/src/util/util/Permissions.ts +++ b/src/util/util/Permissions.ts @@ -12,14 +12,7 @@ import { import { BitField } from "./BitField"; import "missing-native-js-functions"; import { BitFieldResolvable, BitFlag } from "./BitField"; - -var HTTPError: any; - -try { - HTTPError = require("lambert-server").HTTPError; -} catch (e) { - HTTPError = Error; -} +import { HTTPError } from "lambert-server"; export type PermissionResolvable = | bigint @@ -31,7 +24,7 @@ export type PermissionResolvable = type PermissionString = keyof typeof Permissions.FLAGS; // BigInt doesn't have a bit limit (https://stackoverflow.com/questions/53335545/whats-the-biggest-bigint-value-in-js-as-per-spec) -const CUSTOM_PERMISSION_OFFSET = BigInt(1) << BigInt(64); // 27 permission bits left for discord to add new ones +// const CUSTOM_PERMISSION_OFFSET = BigInt(1) << BigInt(64); // 27 permission bits left for discord to add new ones export class Permissions extends BitField { cache: PermissionCache = {}; @@ -114,7 +107,6 @@ export class Permissions extends BitField { */ hasThrow(permission: PermissionResolvable) { if (this.has(permission) && this.has("VIEW_CHANNEL")) return true; - // @ts-ignore throw new HTTPError( `You are missing the following permissions ${permission}`, 403, @@ -177,11 +169,11 @@ export class Permissions extends BitField { }) { if (user.id === "0") return new Permissions("ADMINISTRATOR"); // system user id - let roles = guild.roles.filter((x) => user.roles.includes(x.id)); + const roles = guild.roles.filter((x) => user.roles.includes(x.id)); let permission = Permissions.rolePermission(roles); if (channel?.overwrites) { - let overwrites = channel.overwrites.filter((x) => { + const overwrites = channel.overwrites.filter((x) => { if (x.type === 0 && user.roles.includes(x.id)) return true; if (x.type === 1 && x.id == user.id) return true; return false; @@ -244,9 +236,9 @@ export async function getPermission( } = {}, ) { if (!user_id) throw new HTTPError("User not found"); - var channel: Channel | undefined; - var member: Member | undefined; - var guild: Guild | undefined; + let channel: Channel | undefined; + let member: Member | undefined; + let guild: Guild | undefined; if (channel_id) { channel = await Channel.findOneOrFail({ @@ -258,7 +250,6 @@ export async function getPermission( "permission_overwrites", "owner_id", "guild_id", - // @ts-ignore ...(opts.channel_select || []), ], }); @@ -268,12 +259,7 @@ export async function getPermission( if (guild_id) { guild = await Guild.findOneOrFail({ where: { id: guild_id }, - select: [ - "id", - "owner_id", - // @ts-ignore - ...(opts.guild_select || []), - ], + select: ["id", "owner_id", ...(opts.guild_select || [])], relations: opts.guild_relations, }); if (guild.owner_id === user_id) @@ -285,17 +271,16 @@ export async function getPermission( // select: [ // "id", // TODO: Bug in typeorm? adding these selects breaks the query. // "roles", - // @ts-ignore // ...(opts.member_select || []), // ], }); } - let recipient_ids: any = channel?.recipients?.map((x) => x.user_id); - if (!recipient_ids?.length) recipient_ids = null; + let recipient_ids = channel?.recipients?.map((x) => x.user_id); + if (!recipient_ids?.length) recipient_ids = undefined; // TODO: remove guild.roles and convert recipient_ids to recipients - var permission = Permissions.finalPermission({ + const permission = Permissions.finalPermission({ user: { id: user_id, roles: member?.roles.map((x) => x.id) || [], diff --git a/src/util/util/Rights.ts b/src/util/util/Rights.ts index 4d437956..b48477ed 100644 --- a/src/util/util/Rights.ts +++ b/src/util/util/Rights.ts @@ -20,14 +20,7 @@ import { BitField } from "./BitField"; import "missing-native-js-functions"; import { BitFieldResolvable, BitFlag } from "./BitField"; import { User } from "../entities"; - -var HTTPError: any; - -try { - HTTPError = require("lambert-server").HTTPError; -} catch (e) { - HTTPError = Error; -} +import { HTTPError } from "lambert-server"; export type RightResolvable = | bigint @@ -118,7 +111,6 @@ export class Rights extends BitField { hasThrow(permission: RightResolvable) { if (this.has(permission)) return true; - // @ts-ignore throw new HTTPError( `You are missing the following rights ${permission}`, 403, @@ -137,6 +129,6 @@ export async function getRights( in_behalf?: (keyof User)[]; } = {} **/ ) { - let user = await User.findOneOrFail({ where: { id: user_id } }); + const user = await User.findOneOrFail({ where: { id: user_id } }); return new Rights(user.rights); } diff --git a/src/util/util/Sentry.ts b/src/util/util/Sentry.ts index b0eee169..e1248353 100644 --- a/src/util/util/Sentry.ts +++ b/src/util/util/Sentry.ts @@ -34,7 +34,7 @@ export const Sentry = { Config.get().sentry; if (!enabled) return; - if (!!SentryNode.getCurrentHub().getClient()) return; // we've already initialised sentry + if (SentryNode.getCurrentHub().getClient()) return; // we've already initialised sentry console.log("[Sentry] Enabling sentry..."); @@ -60,7 +60,7 @@ export const Sentry = { environment, }); - SentryNode.addGlobalEventProcessor((event, hint) => { + SentryNode.addGlobalEventProcessor((event) => { if (event.transaction) { // Rewrite things that look like IDs to `:id` for sentry event.transaction = event.transaction @@ -112,6 +112,8 @@ export const Sentry = { errorHandlersUsed = true; app.use(SentryNode.Handlers.errorHandler()); + // The typings for this are broken? + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars app.use(function onError(err: any, req: any, res: any, next: any) { res.statusCode = 500; res.end(res.sentry + "\n"); diff --git a/src/util/util/Snowflake.ts b/src/util/util/Snowflake.ts index 65546958..93898fbb 100644 --- a/src/util/util/Snowflake.ts +++ b/src/util/util/Snowflake.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck import * as cluster from "cluster"; @@ -87,10 +88,10 @@ export class Snowflake { static generateWorkerProcess() { // worker process - returns a number - var time = BigInt(Date.now() - Snowflake.EPOCH) << BigInt(22); - var worker = Snowflake.workerId << 17n; - var process = Snowflake.processId << 12n; - var increment = Snowflake.INCREMENT++; + const time = BigInt(Date.now() - Snowflake.EPOCH) << BigInt(22); + const worker = Snowflake.workerId << 17n; + const process = Snowflake.processId << 12n; + const increment = Snowflake.INCREMENT++; return BigInt(time | worker | process | increment); } diff --git a/src/util/util/String.ts b/src/util/util/String.ts index 7addd49a..74fd0295 100644 --- a/src/util/util/String.ts +++ b/src/util/util/String.ts @@ -19,7 +19,6 @@ import { SPECIAL_CHAR } from "./Regex"; export function trimSpecial(str?: string): string { - // @ts-ignore - if (!str) return; + if (!str) return ""; return str.replace(SPECIAL_CHAR, "").trim(); } diff --git a/src/util/util/Token.ts b/src/util/util/Token.ts index c00142bb..ca81eaaa 100644 --- a/src/util/util/Token.ts +++ b/src/util/util/Token.ts @@ -22,7 +22,15 @@ import { User } from "../entities"; export const JWTOptions: VerifyOptions = { algorithms: ["HS256"] }; -export function checkToken(token: string, jwtSecret: string): Promise<any> { +export type UserTokenData = { + user: User; + decoded: { id: string; iat: number }; +}; + +export function checkToken( + token: string, + jwtSecret: string, +): Promise<UserTokenData> { return new Promise((res, rej) => { token = token.replace("Bot ", ""); token = token.replace("Bearer ", ""); @@ -31,24 +39,35 @@ export function checkToken(token: string, jwtSecret: string): Promise<any> { as we don't really have separate pathways for bots **/ - jwt.verify(token, jwtSecret, JWTOptions, async (err, decoded: any) => { + jwt.verify(token, jwtSecret, JWTOptions, async (err, decoded) => { if (err || !decoded) return rej("Invalid Token"); + if ( + typeof decoded == "string" || + !("id" in decoded) || + !decoded.iat + ) + return rej("Invalid Token"); // will never happen, just for typings. const user = await User.findOne({ where: { id: decoded.id }, select: ["data", "bot", "disabled", "deleted", "rights"], }); + if (!user) return rej("Invalid Token"); + // we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds if ( decoded.iat * 1000 < new Date(user.data.valid_tokens_since).setSeconds(0, 0) ) return rej("Invalid Token"); + if (user.disabled) return rej("User disabled"); if (user.deleted) return rej("User not found"); - return res({ decoded, user }); + // Using as here because we assert `id` and `iat` are in decoded. + // TS just doesn't want to assume its there, though. + return res({ decoded, user } as UserTokenData); }); }); } diff --git a/src/util/util/TraverseDirectory.ts b/src/util/util/TraverseDirectory.ts index 0cc48d4a..223e3ee0 100644 --- a/src/util/util/TraverseDirectory.ts +++ b/src/util/util/TraverseDirectory.ts @@ -22,7 +22,7 @@ import { Server, traverseDirectory } from "lambert-server"; const extension = Symbol.for("ts-node.register.instance") in process ? "ts" : "js"; -const DEFAULT_FILTER = new RegExp("^([^.].*)(?<!\.d).(" + extension + ")$"); +const DEFAULT_FILTER = new RegExp("^([^.].*)(?<!\\.d).(" + extension + ")$"); export function registerRoutes(server: Server, root: string) { return traverseDirectory( diff --git a/src/util/util/cdn.ts b/src/util/util/cdn.ts index 6d2fd0f1..7f447c53 100644 --- a/src/util/util/cdn.ts +++ b/src/util/util/cdn.ts @@ -24,7 +24,8 @@ import { Config } from "./Config"; export async function uploadFile( path: string, - file?: Express.Multer.File, + // These are the only props we use, don't need to enforce the full type. + file?: Pick<Express.Multer.File, "mimetype" | "originalname" | "buffer">, ): Promise<Attachment> { if (!file?.buffer) throw new HTTPError("Missing file in body"); @@ -60,7 +61,6 @@ export async function handleFile( const mimetype = body.split(":")[1].split(";")[0]; const buffer = Buffer.from(body.split(",")[1], "base64"); - // @ts-ignore const { id } = await uploadFile(path, { buffer, mimetype, |