mirror of
https://github.com/spacebarchat/server.git
synced 2026-05-25 12:04:39 +00:00
Add ESLint (#941)
* Add eslint, switch to lint-staged for precommit * Fix all ESLint errors * Update GH workflow to check prettier and eslint
This commit is contained in:
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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[];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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],
|
||||
]),
|
||||
|
||||
@@ -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]),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
OneToOne,
|
||||
RelationId,
|
||||
} from "typeorm";
|
||||
import { Sticker } from ".";
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
Column,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
RelationId,
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -18,5 +18,4 @@
|
||||
|
||||
import { ChannelPermissionOverwrite } from "@fosscord/util";
|
||||
|
||||
export interface ChannelPermissionOverwriteSchema
|
||||
extends ChannelPermissionOverwrite {}
|
||||
export type ChannelPermissionOverwriteSchema = ChannelPermissionOverwrite;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[],
|
||||
};
|
||||
|
||||
@@ -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[];
|
||||
}
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
|
||||
import { UserSettings } from "@fosscord/util";
|
||||
|
||||
export interface UserSettingsSchema extends Partial<UserSettings> {}
|
||||
export type UserSettingsSchema = Partial<UserSettings>;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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("_");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
+17
-15
@@ -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";
|
||||
}
|
||||
|
||||
if (domain === "google.com") {
|
||||
// replace .dots and +alternatives -> Google Staff GMail Dot Trick
|
||||
let v = user.replace(/[.]|(\+.*)/g, "") + "@google.com";
|
||||
}
|
||||
|
||||
return email;
|
||||
// // TODO: The below code doesn't actually do anything.
|
||||
// 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
|
||||
// const v = user.replace(/[.]|(\+.*)/g, "") + "@gmail.com";
|
||||
// }
|
||||
|
||||
// if (domain === "google.com") {
|
||||
// // replace .dots and +alternatives -> Google Staff GMail Dot Trick
|
||||
// const v = user.replace(/[.]|(\+.*)/g, "") + "@google.com";
|
||||
// }
|
||||
|
||||
// return email;
|
||||
}
|
||||
|
||||
+16
-14
@@ -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 });
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) || [],
|
||||
|
||||
+2
-10
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
+22
-3
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user