From 698ad90d3e82a15b85ceb021ad5667109ac0bcdb Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Fri, 31 Mar 2023 15:26:15 +1100 Subject: [PATCH] Revert "Merge pull request #1008 from spacebarchat/dev/samuel" This reverts commit 69ea71aa9e0bd2e5a98904a66fba0ad3745707cb, reversing changes made to 8b2faf0b18336e5dff1eeff4e849bcfd96b09e88. --- .gitignore | 4 - package-lock.json | Bin 541638 -> 524405 bytes package.json | 22 +-- src/api/Server.ts | 4 +- src/api/middlewares/Authentication.ts | 17 +- src/api/middlewares/Translation.ts | 59 +++---- src/api/routes/guilds/#guild_id/templates.ts | 1 - src/api/routes/users/@me/notes.ts | 6 +- src/api/util/handlers/Message.ts | 2 +- src/util/cache/Cache.ts | 85 ---------- src/util/cache/EntityCache.ts | 162 ------------------- src/util/cache/LocalCache.ts | 94 ----------- src/util/cache/index.ts | 3 - src/util/config/Config.ts | 2 - src/util/config/types/CacheConfiguration.ts | 22 --- src/util/config/types/index.ts | 1 - src/util/entities/Application.ts | 4 +- src/util/entities/Attachment.ts | 4 +- src/util/entities/AuditLog.ts | 4 +- src/util/entities/BackupCodes.ts | 4 +- src/util/entities/Ban.ts | 4 +- src/util/entities/BaseClass.ts | 44 ++--- src/util/entities/Channel.ts | 4 +- src/util/entities/ClientRelease.ts | 4 +- src/util/entities/ConnectedAccount.ts | 4 +- src/util/entities/EmbedCache.ts | 4 +- src/util/entities/Emoji.ts | 4 +- src/util/entities/Encryption.ts | 4 +- src/util/entities/Guild.ts | 4 +- src/util/entities/Message.ts | 4 +- src/util/entities/Note.ts | 4 +- src/util/entities/RateLimit.ts | 4 +- src/util/entities/ReadState.ts | 4 +- src/util/entities/Recipient.ts | 4 +- src/util/entities/Relationship.ts | 4 +- src/util/entities/Role.ts | 4 +- src/util/entities/SecurityKey.ts | 4 +- src/util/entities/Session.ts | 4 +- src/util/entities/Sticker.ts | 4 +- src/util/entities/StickerPack.ts | 4 +- src/util/entities/Team.ts | 4 +- src/util/entities/TeamMember.ts | 4 +- src/util/entities/Template.ts | 4 +- src/util/entities/User.ts | 4 +- src/util/entities/VoiceState.ts | 4 +- src/util/entities/Webhook.ts | 4 +- src/util/schemas/Validator.ts | 2 +- src/util/util/Database.ts | 17 +- src/util/util/Permissions.ts | 33 ++-- src/util/util/Token.ts | 5 +- src/util/util/index.ts | 2 +- 51 files changed, 147 insertions(+), 556 deletions(-) delete mode 100644 src/util/cache/Cache.ts delete mode 100644 src/util/cache/EntityCache.ts delete mode 100644 src/util/cache/LocalCache.ts delete mode 100644 src/util/cache/index.ts delete mode 100644 src/util/config/types/CacheConfiguration.ts diff --git a/.gitignore b/.gitignore index 9ac5d944..bc780d64 100644 --- a/.gitignore +++ b/.gitignore @@ -9,10 +9,6 @@ assets/cache .env config.json assets/cacheMisses -assets/client_test -scripts/client.js -src/api/middlewares/TestClient.ts -yarn.lock .vscode/settings.json diff --git a/package-lock.json b/package-lock.json index 3713fc53f85f20cb78b29efd2a61d1beb8c4ab64..8927001f2df6072364d354786fe4a8d69ffa7112 100644 GIT binary patch delta 58282 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 delta 65442 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%0D9#8z|DU72FEUtv<1WlI{hf{ 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 R%CSzX>tCKj<=1zd{}+1n7R>+v diff --git a/package.json b/package.json index 85edb0e5..99a0f2d3 100644 --- a/package.json +++ b/package.json @@ -38,13 +38,14 @@ }, "homepage": "https://fosscord.com", "devDependencies": { - "@types/amqplib": "^0.10.1", + "@types/amqplib": "^0.8.2", "@types/bcrypt": "^5.0.0", "@types/body-parser": "^1.19.2", "@types/cookie-parser": "^1.4.3", "@types/express": "^4.17.15", + "@types/i18next-node-fs-backend": "^2.1.1", "@types/json-bigint": "^1.0.1", - "@types/jsonwebtoken": "^9.0.1", + "@types/jsonwebtoken": "^8.5.9", "@types/morgan": "^1.9.3", "@types/multer": "^1.4.7", "@types/node": "^18.7.20", @@ -61,7 +62,7 @@ "husky": "^8.0.0", "prettier": "^2.7.1", "pretty-quick": "^3.1.3", - "typescript": "^5.0.2" + "typescript": "^4.9.4" }, "dependencies": { "@aws-sdk/client-s3": "^3.178.0", @@ -79,20 +80,21 @@ "exif-be-gone": "^1.3.1", "fast-zlib": "^2.0.1", "fido2-lib": "^3.3.5", - "file-type": "16.5.4", + "file-type": "16.5", "form-data": "^4.0.0", - "i18next": "^22.4.12", - "i18next-fs-backend": "^2.1.1", + "i18next": "^21.9.2", + "i18next-http-middleware": "^3.2.1", + "i18next-node-fs-backend": "^2.1.3", "image-size": "^1.0.2", "json-bigint": "^1.0.0", - "jsonwebtoken": "^9.0.0", + "jsonwebtoken": "^8.5.1", "lambert-server": "^1.2.12", "missing-native-js-functions": "^1.2.18", "module-alias": "^2.2.2", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "node-2fa": "^2.0.3", - "node-fetch": "^2.6.9", + "node-fetch": "^2.6.7", "node-os-utils": "^1.3.7", "nodemailer": "^6.9.0", "picocolors": "^1.0.0", @@ -112,10 +114,10 @@ "@fosscord/util": "dist/util" }, "optionalDependencies": { - "better-sqlite3": "^8.2.0", "erlpack": "^0.1.4", "nodemailer-mailgun-transport": "^2.1.5", "nodemailer-mailjet-transport": "github:n0script22/nodemailer-mailjet-transport", - "nodemailer-sendgrid-transport": "github:Maria-Golomb/nodemailer-sendgrid-transport" + "nodemailer-sendgrid-transport": "github:Maria-Golomb/nodemailer-sendgrid-transport", + "sqlite3": "^5.1.5" } } diff --git a/src/api/Server.ts b/src/api/Server.ts index f88f94c0..032e923e 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -32,7 +32,7 @@ import "missing-native-js-functions"; import morgan from "morgan"; import path from "path"; import { red } from "picocolors"; -import { CORS, initAuthentication } from "./middlewares/"; +import { Authentication, CORS } from "./middlewares/"; import { BodyParser } from "./middlewares/BodyParser"; import { ErrorHandler } from "./middlewares/ErrorHandler"; import { initRateLimits } from "./middlewares/RateLimit"; @@ -106,7 +106,7 @@ export class FosscordServer extends Server { // @ts-ignore this.app = api; - initAuthentication(api); + api.use(Authentication); await initRateLimits(api); await initTranslation(api); diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts index 0aa585e5..400a16f4 100644 --- a/src/api/middlewares/Authentication.ts +++ b/src/api/middlewares/Authentication.ts @@ -18,9 +18,8 @@ import { checkToken, Config, Rights } from "@fosscord/util"; import * as Sentry from "@sentry/node"; -import { NextFunction, Request, Response, Router } from "express"; +import { NextFunction, Request, Response } from "express"; import { HTTPError } from "lambert-server"; -import { createSecretKey, KeyObject } from "crypto"; export const NO_AUTHORIZATION_ROUTES = [ // Authentication routes @@ -70,16 +69,6 @@ declare global { } } -let jwtPublicKey: KeyObject; - -// Initialize the jwt secret as a key object so it does not need to be regenerated for each request. -export function initAuthentication(api: Router) { - jwtPublicKey = createSecretKey( - Buffer.from(Config.get().security.jwtSecret), - ); - api.use(Authentication); -} - export async function Authentication( req: Request, res: Response, @@ -101,9 +90,11 @@ export async function Authentication( Sentry.setUser({ id: req.user_id }); try { + const { jwtSecret } = Config.get().security; + const { decoded, user } = await checkToken( req.headers.authorization, - jwtPublicKey, + jwtSecret, ); req.token = decoded; diff --git a/src/api/middlewares/Translation.ts b/src/api/middlewares/Translation.ts index 0ddc56bb..60ff4ad7 100644 --- a/src/api/middlewares/Translation.ts +++ b/src/api/middlewares/Translation.ts @@ -18,20 +18,11 @@ import fs from "fs"; import path from "path"; -import i18next, { TFunction } from "i18next"; -import i18nextBackend from "i18next-fs-backend"; +import i18next from "i18next"; +import i18nextMiddleware from "i18next-http-middleware"; +import i18nextBackend from "i18next-node-fs-backend"; import { Router } from "express"; -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Express { - interface Request { - t: TFunction; - language?: string; - } - } -} - const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "assets"); export async function initTranslation(router: Router) { @@ -43,33 +34,21 @@ export async function initTranslation(router: Router) { .filter((x) => x.endsWith(".json")) .map((x) => x.slice(0, x.length - 5)); - await i18next.use(i18nextBackend).init({ - preload: languages, - // debug: true, - fallbackLng: "en", - ns, - backend: { - loadPath: - path.join(ASSET_FOLDER_PATH, "locales") + - "/{{lng}}/{{ns}}.json", - }, - load: "all", - }); + await i18next + .use(i18nextBackend) + .use(i18nextMiddleware.LanguageDetector) + .init({ + preload: languages, + // debug: true, + fallbackLng: "en", + ns, + backend: { + loadPath: + path.join(ASSET_FOLDER_PATH, "locales") + + "/{{lng}}/{{ns}}.json", + }, + load: "all", + }); - router.use((req, res, next) => { - let lng = "en"; - if (req.headers["accept-language"]) { - lng = req.headers["accept-language"].split(",")[0]; - } - req.language = lng; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - req.t = (key: string | string[], options?: any) => { - return i18next.t(key, { - ...options, - lng, - }); - }; - next(); - }); + router.use(i18nextMiddleware.handle(i18next, {})); } diff --git a/src/api/routes/guilds/#guild_id/templates.ts b/src/api/routes/guilds/#guild_id/templates.ts index 707675e5..8a8c53fe 100644 --- a/src/api/routes/guilds/#guild_id/templates.ts +++ b/src/api/routes/guilds/#guild_id/templates.ts @@ -39,7 +39,6 @@ const TemplateGuildProjection: (keyof Guild)[] = [ "system_channel_id", "system_channel_flags", "icon", - "id", ]; router.get("/", route({}), async (req: Request, res: Response) => { diff --git a/src/api/routes/users/@me/notes.ts b/src/api/routes/users/@me/notes.ts index 07c07d72..87d45277 100644 --- a/src/api/routes/users/@me/notes.ts +++ b/src/api/routes/users/@me/notes.ts @@ -52,17 +52,17 @@ router.put("/:id", route({}), async (req: Request, res: Response) => { where: { owner: { id: owner.id }, target: { id: target.id } }, }) ) { - await Note.update( + Note.update( { owner: { id: owner.id }, target: { id: target.id } }, { owner, target, content: note }, ); } else { - await Note.create({ + Note.insert({ id: Snowflake.generate(), owner, target, content: note, - }).save(); + }); } } else { await Note.delete({ diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index 8818904e..65dbcdfe 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -274,7 +274,7 @@ export async function sendMessage(opts: MessageOptions) { const message = await handleMessage({ ...opts, timestamp: new Date() }); await Promise.all([ - message.save(), + Message.insert(message), emitEvent({ event: "MESSAGE_CREATE", channel_id: opts.channel_id, diff --git a/src/util/cache/Cache.ts b/src/util/cache/Cache.ts deleted file mode 100644 index fb66c2e3..00000000 --- a/src/util/cache/Cache.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* - 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 . -*/ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import { EntityMetadata, FindOptionsWhere } from "typeorm"; -import { LocalCache } from "./LocalCache"; - -declare module "typeorm" { - interface BaseEntity { - metadata?: EntityMetadata; - cache: CacheManager; - } -} - -export type BaseEntityWithId = { id: string; [name: string]: any }; -export type Criteria = - | string - | string[] - | number - | number[] - | FindOptionsWhere; - -export interface Cache { - get(id: string): BaseEntityWithId | undefined; - set(id: string, entity: BaseEntityWithId): this; - find(options: Record): BaseEntityWithId | undefined; - filter(options: Record): BaseEntityWithId[]; - delete(id: string): boolean; -} - -export class CacheManager { - // last access time to automatically remove old entities from cache after 5 minutes of inactivity (to prevent memory leaks) - cache: Cache; - - constructor() { - this.cache = new LocalCache(); - // TODO: Config.get().cache.redis; - } - - delete(id: string) { - return this.cache.delete(id); - } - - insert(entity: BaseEntityWithId) { - if (!entity.id) return; - - return this.cache.set(entity.id, entity); - } - - find(options?: Record, select?: string[] | undefined) { - if (!options) return null; - const entity = this.cache.find(options); - if (!entity) return null; - if (!select) return entity; - - const result = {}; - for (const prop of select) { - // @ts-ignore - result[prop] = entity[prop]; - } - - // @ts-ignore - return entity.constructor.create(result); - } - - filter(options: Record) { - return this.cache.filter(options); - } -} diff --git a/src/util/cache/EntityCache.ts b/src/util/cache/EntityCache.ts deleted file mode 100644 index ba1e5bd8..00000000 --- a/src/util/cache/EntityCache.ts +++ /dev/null @@ -1,162 +0,0 @@ -/* - 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 . -*/ -/* eslint-disable */ -import { - DataSource, - FindOneOptions, - EntityNotFoundError, - FindOptionsWhere, -} from "typeorm"; -import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; -import { BaseClassWithId } from "../entities/BaseClass"; -import { Config, getDatabase, RabbitMQ } from "../util"; -import { CacheManager } from "./Cache"; - -function getObjectKeysAsArray(obj?: Record) { - if (!obj) return []; - if (Array.isArray(obj)) return obj; - return Object.keys(obj); -} - -export type ThisType = { - new (): T; -} & typeof BaseEntityCache; - -interface BaseEntityCache { - constructor: typeof BaseEntityCache; -} - -// @ts-ignore -class BaseEntityCache extends BaseClassWithId { - static cache: CacheManager; - static cacheEnabled: boolean; - - public get metadata() { - return getDatabase()?.getMetadata(this.constructor)!; - } - - static useDataSource(dataSource: DataSource | null) { - super.useDataSource(dataSource); - const isMultiThreaded = - process.env.EVENT_TRANSMISSION === "process" || RabbitMQ.connection; - this.cacheEnabled = Config.get().cache.enabled ?? !isMultiThreaded; - if (Config.get().cache.redis) return; // TODO: Redis cache - if (!this.cacheEnabled) return; - this.cache = new CacheManager(); - } - - static async findOne( - this: ThisType, - options: FindOneOptions, - ) { - // @ts-ignore - if (!this.cacheEnabled) return super.findOne(options); - let select = getObjectKeysAsArray(options.select); - - if (!select.length) { - // get all columns that are marked as select - getDatabase() - ?.getMetadata(this) - .columns.forEach((x) => { - if (!x.isSelect) return; - select.push(x.propertyName); - }); - } - if (options.relations) { - select.push(...getObjectKeysAsArray(options.relations)); - } - - const cacheResult = this.cache.find(options.where as never, select); - if (cacheResult) { - const hasAllProps = select.every((key) => { - if (key.includes(".")) return true; // @ts-ignore - return cacheResult[key] !== undefined; - }); - // console.log(`[Cache] get ${cacheResult.id} from ${cacheResult.constructor.name}`,); - if (hasAllProps) return cacheResult; - } - - // @ts-ignore - const result = await super.findOne(options); - if (!result) return null; - - this.cache.insert(result as any); - - return result; - } - - static async findOneOrFail( - this: ThisType, - options: FindOneOptions, - ) { - const result = await this.findOne(options); - if (!result) throw new EntityNotFoundError(this, options); - return result; - } - - save() { - if (this.constructor.cacheEnabled) this.constructor.cache.insert(this); - return super.save(); - } - - remove() { - if (this.constructor.cacheEnabled) - this.constructor.cache.delete(this.id); - return super.remove(); - } - - static async update( - this: ThisType, - criteria: FindOptionsWhere, - partialEntity: QueryDeepPartialEntity, - ) { - // @ts-ignore - const result = super.update(criteria, partialEntity); - if (!this.cacheEnabled) return result; - - const entities = this.cache.filter(criteria as never); - for (const entity of entities) { - // @ts-ignore - partialEntity.id = entity.id; - this.cache.insert(partialEntity as never); - } - - return result; - } - - static async delete( - this: ThisType, - criteria: FindOptionsWhere, - ) { - // @ts-ignore - const result = super.delete(criteria); - if (!this.cacheEnabled) return result; - - const entities = this.cache.filter(criteria as never); - for (const entity of entities) { - this.cache.delete(entity.id); - } - - return result; - } -} - -// needed, because typescript can't infer the type of the static methods with generics -const EntityCache = BaseEntityCache as unknown as typeof BaseClassWithId; - -export { EntityCache }; diff --git a/src/util/cache/LocalCache.ts b/src/util/cache/LocalCache.ts deleted file mode 100644 index 9c4f23b3..00000000 --- a/src/util/cache/LocalCache.ts +++ /dev/null @@ -1,94 +0,0 @@ -/* - 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 . -*/ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -import { BaseEntityWithId, Cache } from "./Cache"; - -export const cacheTimeout = 1000 * 60 * 5; - -export class LocalCache extends Map implements Cache { - last_access = new Map(); - - constructor() { - super(); - - setInterval(() => { - const now = Date.now(); - for (const [key, value] of this.last_access) { - if (now - value > cacheTimeout) { - this.delete(key); - this.last_access.delete(key); - } - } - }, cacheTimeout); - } - - set(key: string, value: BaseEntityWithId): this { - if (this.has(key)) { - this.update(key, value); - return this; - } - this.last_access.set(key, Date.now()); - return super.set(key, value as never); - } - - get(key: string) { - const value = super.get(key); - if (value) this.last_access.set(key, Date.now()); - return value; - } - - update(id: string, entity: BaseEntityWithId) { - const oldEntity = this.get(id); - if (!oldEntity) return; - for (const key in entity) { - // @ts-ignore - if (entity[key] === undefined) continue; // @ts-ignore - oldEntity[key] = entity[key]; - } - } - - find(options: Record): BaseEntityWithId | undefined { - if (options.id && Object.keys(options).length === 1) { - return this.get(options.id); - } - for (const entity of this.values()) { - if (objectFulfillsQuery(entity, options)) return entity; - } - } - - filter(options: Record): BaseEntityWithId[] { - const result = []; - for (const entity of this.values()) { - if (objectFulfillsQuery(entity, options)) { - result.push(entity); - } - } - return result; - } -} - -function objectFulfillsQuery( - entity: BaseEntityWithId, - options: Record, -) { - for (const key in options) { - // @ts-ignore - if (entity[key] !== options[key]) return false; - } - return true; -} diff --git a/src/util/cache/index.ts b/src/util/cache/index.ts deleted file mode 100644 index 43f0aa96..00000000 --- a/src/util/cache/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./EntityCache"; -export * from "./Cache"; -export * from "./LocalCache"; diff --git a/src/util/config/Config.ts b/src/util/config/Config.ts index 7de01b66..90b98b7a 100644 --- a/src/util/config/Config.ts +++ b/src/util/config/Config.ts @@ -37,7 +37,6 @@ import { SecurityConfiguration, SentryConfiguration, TemplateConfiguration, - CacheConfiguration, } from "../config"; export class ConfigValue { @@ -62,5 +61,4 @@ export class ConfigValue { email: EmailConfiguration = new EmailConfiguration(); passwordReset: PasswordResetConfiguration = new PasswordResetConfiguration(); - cache: CacheConfiguration = new CacheConfiguration(); } diff --git a/src/util/config/types/CacheConfiguration.ts b/src/util/config/types/CacheConfiguration.ts deleted file mode 100644 index 4178090e..00000000 --- a/src/util/config/types/CacheConfiguration.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - 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 . -*/ - -export class CacheConfiguration { - enabled: boolean | null = true; - redis: string | null = null; -} diff --git a/src/util/config/types/index.ts b/src/util/config/types/index.ts index a52ca627..782ebfc3 100644 --- a/src/util/config/types/index.ts +++ b/src/util/config/types/index.ts @@ -17,7 +17,6 @@ */ export * from "./ApiConfiguration"; -export * from "./CacheConfiguration"; export * from "./CdnConfiguration"; export * from "./DefaultsConfiguration"; export * from "./EmailConfiguration"; diff --git a/src/util/entities/Application.ts b/src/util/entities/Application.ts index e0cdc481..962b2a8e 100644 --- a/src/util/entities/Application.ts +++ b/src/util/entities/Application.ts @@ -17,12 +17,12 @@ */ import { Column, Entity, JoinColumn, ManyToOne, OneToOne } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Team } from "./Team"; import { User } from "./User"; @Entity("applications") -export class Application extends EntityCache { +export class Application extends BaseClass { @Column() name: string; diff --git a/src/util/entities/Attachment.ts b/src/util/entities/Attachment.ts index 521be583..d60ac41c 100644 --- a/src/util/entities/Attachment.ts +++ b/src/util/entities/Attachment.ts @@ -25,11 +25,11 @@ import { RelationId, } from "typeorm"; import { URL } from "url"; -import { EntityCache } from "../cache"; import { deleteFile } from "../util/cdn"; +import { BaseClass } from "./BaseClass"; @Entity("attachments") -export class Attachment extends EntityCache { +export class Attachment extends BaseClass { @Column() filename: string; // name of file attached diff --git a/src/util/entities/AuditLog.ts b/src/util/entities/AuditLog.ts index 9768ef04..e47f92fb 100644 --- a/src/util/entities/AuditLog.ts +++ b/src/util/entities/AuditLog.ts @@ -17,7 +17,7 @@ */ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { ChannelPermissionOverwrite } from "./Channel"; import { User } from "./User"; @@ -112,7 +112,7 @@ export enum AuditLogEvents { } @Entity("audit_logs") -export class AuditLog extends EntityCache { +export class AuditLog extends BaseClass { @JoinColumn({ name: "target_id" }) @ManyToOne(() => User) target?: User; diff --git a/src/util/entities/BackupCodes.ts b/src/util/entities/BackupCodes.ts index 3d40338e..61e8f12a 100644 --- a/src/util/entities/BackupCodes.ts +++ b/src/util/entities/BackupCodes.ts @@ -17,12 +17,12 @@ */ import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { User } from "./User"; import crypto from "crypto"; @Entity("backup_codes") -export class BackupCode extends EntityCache { +export class BackupCode extends BaseClass { @JoinColumn({ name: "user_id" }) @ManyToOne(() => User, { onDelete: "CASCADE" }) user: User; diff --git a/src/util/entities/Ban.ts b/src/util/entities/Ban.ts index 2b3b688b..1693cd40 100644 --- a/src/util/entities/Ban.ts +++ b/src/util/entities/Ban.ts @@ -17,12 +17,12 @@ */ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { User } from "./User"; @Entity("bans") -export class Ban extends EntityCache { +export class Ban extends BaseClass { @Column({ nullable: true }) @RelationId((ban: Ban) => ban.user) user_id: string; diff --git a/src/util/entities/BaseClass.ts b/src/util/entities/BaseClass.ts index d2770bfd..f4b3cf59 100644 --- a/src/util/entities/BaseClass.ts +++ b/src/util/entities/BaseClass.ts @@ -25,12 +25,16 @@ import { PrimaryColumn, } from "typeorm"; import { Snowflake } from "../util/Snowflake"; +import { getDatabase } from "../util/Database"; import { OrmUtils } from "../imports/OrmUtils"; -import { getDatabase } from "../util"; export class BaseClassWithoutId extends BaseEntity { - public get metadata() { - return getDatabase()?.getMetadata(this.constructor); + private get construct() { + return this.constructor; + } + + private get metadata() { + return getDatabase()?.getMetadata(this.construct); } assign(props: object) { @@ -57,23 +61,8 @@ export class BaseClassWithoutId extends BaseEntity { ), ); } -} -export const PrimaryIdColumn = process.env.DATABASE?.startsWith("mongodb") - ? ObjectIdColumn - : PrimaryColumn; - -export class BaseClassWithId extends BaseClassWithoutId { - @PrimaryIdColumn() - id: string = Snowflake.generate(); - - @BeforeUpdate() - @BeforeInsert() - _do_validate() { - if (!this.id) this.id = Snowflake.generate(); - } - - static increment( + static increment( conditions: FindOptionsWhere, propertyPath: string, value: number | string, @@ -82,7 +71,7 @@ export class BaseClassWithId extends BaseClassWithoutId { return repository.increment(conditions, propertyPath, value); } - static decrement( + static decrement( conditions: FindOptionsWhere, propertyPath: string, value: number | string, @@ -91,3 +80,18 @@ export class BaseClassWithId extends BaseClassWithoutId { return repository.decrement(conditions, propertyPath, value); } } + +export const PrimaryIdColumn = process.env.DATABASE?.startsWith("mongodb") + ? ObjectIdColumn + : PrimaryColumn; + +export class BaseClass extends BaseClassWithoutId { + @PrimaryIdColumn() + id: string = Snowflake.generate(); + + @BeforeUpdate() + @BeforeInsert() + _do_validate() { + if (!this.id) this.id = Snowflake.generate(); + } +} diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts index 218907ed..9ce04848 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts @@ -24,7 +24,7 @@ import { OneToMany, RelationId, } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { PublicUserProjection, User } from "./User"; import { HTTPError } from "lambert-server"; @@ -70,7 +70,7 @@ export enum ChannelType { } @Entity("channels") -export class Channel extends EntityCache { +export class Channel extends BaseClass { @Column() created_at: Date; diff --git a/src/util/entities/ClientRelease.ts b/src/util/entities/ClientRelease.ts index a26bcd8a..cfbc3a9b 100644 --- a/src/util/entities/ClientRelease.ts +++ b/src/util/entities/ClientRelease.ts @@ -17,10 +17,10 @@ */ import { Column, Entity } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; @Entity("client_release") -export class Release extends EntityCache { +export class Release extends BaseClass { @Column() name: string; diff --git a/src/util/entities/ConnectedAccount.ts b/src/util/entities/ConnectedAccount.ts index 8ef03fea..33550197 100644 --- a/src/util/entities/ConnectedAccount.ts +++ b/src/util/entities/ConnectedAccount.ts @@ -17,7 +17,7 @@ */ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { User } from "./User"; export type PublicConnectedAccount = Pick< @@ -26,7 +26,7 @@ export type PublicConnectedAccount = Pick< >; @Entity("connected_accounts") -export class ConnectedAccount extends EntityCache { +export class ConnectedAccount extends BaseClass { @Column({ nullable: true }) @RelationId((account: ConnectedAccount) => account.user) user_id: string; diff --git a/src/util/entities/EmbedCache.ts b/src/util/entities/EmbedCache.ts index 49361032..8ff2a457 100644 --- a/src/util/entities/EmbedCache.ts +++ b/src/util/entities/EmbedCache.ts @@ -16,12 +16,12 @@ along with this program. If not, see . */ -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Entity, Column } from "typeorm"; import { Embed } from "./Message"; @Entity("embed_cache") -export class EmbedCache extends EntityCache { +export class EmbedCache extends BaseClass { @Column() url: string; diff --git a/src/util/entities/Emoji.ts b/src/util/entities/Emoji.ts index bf15f4ec..f1bf2d59 100644 --- a/src/util/entities/Emoji.ts +++ b/src/util/entities/Emoji.ts @@ -18,11 +18,11 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { User } from "."; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; @Entity("emojis") -export class Emoji extends EntityCache { +export class Emoji extends BaseClass { @Column() animated: boolean; diff --git a/src/util/entities/Encryption.ts b/src/util/entities/Encryption.ts index 24fbaa6c..8325bdee 100644 --- a/src/util/entities/Encryption.ts +++ b/src/util/entities/Encryption.ts @@ -17,10 +17,10 @@ */ import { Column, Entity } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; @Entity("security_settings") -export class SecuritySettings extends EntityCache { +export class SecuritySettings extends BaseClass { @Column({ nullable: true }) guild_id: string; diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts index 7253f118..b1693838 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts @@ -26,7 +26,7 @@ import { } from "typeorm"; import { Config, handleFile, Snowflake } from ".."; import { Ban } from "./Ban"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Emoji } from "./Emoji"; import { Invite } from "./Invite"; @@ -67,7 +67,7 @@ export const PublicGuildRelations = [ ]; @Entity("guilds") -export class Guild extends EntityCache { +export class Guild extends BaseClass { @Column({ nullable: true }) @RelationId((guild: Guild) => guild.afk_channel) afk_channel_id?: string; diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index db98a6f2..519c431e 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -34,7 +34,7 @@ import { OneToMany, RelationId, } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Webhook } from "./Webhook"; import { Sticker } from "./Sticker"; @@ -70,7 +70,7 @@ export enum MessageType { @Entity("messages") @Index(["channel_id", "id"], { unique: true }) -export class Message extends EntityCache { +export class Message extends BaseClass { @Column({ nullable: true }) @RelationId((message: Message) => message.channel) @Index() diff --git a/src/util/entities/Note.ts b/src/util/entities/Note.ts index 2005ab0f..196f6861 100644 --- a/src/util/entities/Note.ts +++ b/src/util/entities/Note.ts @@ -17,12 +17,12 @@ */ import { Column, Entity, JoinColumn, ManyToOne, Unique } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { User } from "./User"; @Entity("notes") @Unique(["owner", "target"]) -export class Note extends EntityCache { +export class Note extends BaseClass { @JoinColumn({ name: "owner_id" }) @ManyToOne(() => User, { onDelete: "CASCADE" }) owner: User; diff --git a/src/util/entities/RateLimit.ts b/src/util/entities/RateLimit.ts index 5402ad06..8d00f59a 100644 --- a/src/util/entities/RateLimit.ts +++ b/src/util/entities/RateLimit.ts @@ -17,10 +17,10 @@ */ import { Column, Entity } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; @Entity("rate_limits") -export class RateLimit extends EntityCache { +export class RateLimit extends BaseClass { @Column() // no relation as it also executor_id: string; diff --git a/src/util/entities/ReadState.ts b/src/util/entities/ReadState.ts index 56856a1e..1b280d12 100644 --- a/src/util/entities/ReadState.ts +++ b/src/util/entities/ReadState.ts @@ -24,7 +24,7 @@ import { ManyToOne, RelationId, } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { User } from "./User"; @@ -34,7 +34,7 @@ import { User } from "./User"; @Entity("read_states") @Index(["channel_id", "user_id"], { unique: true }) -export class ReadState extends EntityCache { +export class ReadState extends BaseClass { @Column() @RelationId((read_state: ReadState) => read_state.channel) channel_id: string; diff --git a/src/util/entities/Recipient.ts b/src/util/entities/Recipient.ts index 32ae7936..797349e5 100644 --- a/src/util/entities/Recipient.ts +++ b/src/util/entities/Recipient.ts @@ -17,10 +17,10 @@ */ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; @Entity("recipients") -export class Recipient extends EntityCache { +export class Recipient extends BaseClass { @Column() @RelationId((recipient: Recipient) => recipient.channel) channel_id: string; diff --git a/src/util/entities/Relationship.ts b/src/util/entities/Relationship.ts index e67557aa..740095c2 100644 --- a/src/util/entities/Relationship.ts +++ b/src/util/entities/Relationship.ts @@ -24,7 +24,7 @@ import { ManyToOne, RelationId, } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { User } from "./User"; export enum RelationshipType { @@ -36,7 +36,7 @@ export enum RelationshipType { @Entity("relationships") @Index(["from_id", "to_id"], { unique: true }) -export class Relationship extends EntityCache { +export class Relationship extends BaseClass { @Column({}) @RelationId((relationship: Relationship) => relationship.from) from_id: string; diff --git a/src/util/entities/Role.ts b/src/util/entities/Role.ts index 2fb63e93..85877c12 100644 --- a/src/util/entities/Role.ts +++ b/src/util/entities/Role.ts @@ -18,11 +18,11 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; @Entity("roles") -export class Role extends EntityCache { +export class Role extends BaseClass { @Column({ nullable: true }) @RelationId((role: Role) => role.guild) guild_id: string; diff --git a/src/util/entities/SecurityKey.ts b/src/util/entities/SecurityKey.ts index cdcc814b..fd7a4c5e 100644 --- a/src/util/entities/SecurityKey.ts +++ b/src/util/entities/SecurityKey.ts @@ -17,11 +17,11 @@ */ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { User } from "./User"; @Entity("security_keys") -export class SecurityKey extends EntityCache { +export class SecurityKey extends BaseClass { @Column({ nullable: true }) @RelationId((key: SecurityKey) => key.user) user_id: string; diff --git a/src/util/entities/Session.ts b/src/util/entities/Session.ts index a03ff114..6c6f7caa 100644 --- a/src/util/entities/Session.ts +++ b/src/util/entities/Session.ts @@ -17,7 +17,7 @@ */ import { User } from "./User"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { Status } from "../interfaces/Status"; import { Activity } from "../interfaces/Activity"; @@ -25,7 +25,7 @@ import { Activity } from "../interfaces/Activity"; //TODO we need to remove all sessions on server start because if the server crashes without closing websockets it won't delete them @Entity("sessions") -export class Session extends EntityCache { +export class Session extends BaseClass { @Column({ nullable: true }) @RelationId((session: Session) => session.user) user_id: string; diff --git a/src/util/entities/Sticker.ts b/src/util/entities/Sticker.ts index 9139a5b1..cd07e65a 100644 --- a/src/util/entities/Sticker.ts +++ b/src/util/entities/Sticker.ts @@ -18,7 +18,7 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { User } from "./User"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; export enum StickerType { @@ -34,7 +34,7 @@ export enum StickerFormatType { } @Entity("stickers") -export class Sticker extends EntityCache { +export class Sticker extends BaseClass { @Column() name: string; diff --git a/src/util/entities/StickerPack.ts b/src/util/entities/StickerPack.ts index cc5c392b..61ab1287 100644 --- a/src/util/entities/StickerPack.ts +++ b/src/util/entities/StickerPack.ts @@ -25,10 +25,10 @@ import { RelationId, } from "typeorm"; import { Sticker } from "."; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; @Entity("sticker_packs") -export class StickerPack extends EntityCache { +export class StickerPack extends BaseClass { @Column() name: string; diff --git a/src/util/entities/Team.ts b/src/util/entities/Team.ts index 04d34d30..7bedc4af 100644 --- a/src/util/entities/Team.ts +++ b/src/util/entities/Team.ts @@ -24,12 +24,12 @@ import { OneToMany, RelationId, } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { TeamMember } from "./TeamMember"; import { User } from "./User"; @Entity("teams") -export class Team extends EntityCache { +export class Team extends BaseClass { @Column({ nullable: true }) icon?: string; diff --git a/src/util/entities/TeamMember.ts b/src/util/entities/TeamMember.ts index d2053bb7..539da957 100644 --- a/src/util/entities/TeamMember.ts +++ b/src/util/entities/TeamMember.ts @@ -17,7 +17,7 @@ */ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { User } from "./User"; export enum TeamMemberState { @@ -26,7 +26,7 @@ export enum TeamMemberState { } @Entity("team_members") -export class TeamMember extends EntityCache { +export class TeamMember extends BaseClass { @Column({ type: "int" }) membership_state: TeamMemberState; diff --git a/src/util/entities/Template.ts b/src/util/entities/Template.ts index 101329d1..c417f1f0 100644 --- a/src/util/entities/Template.ts +++ b/src/util/entities/Template.ts @@ -17,12 +17,12 @@ */ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { User } from "./User"; @Entity("templates") -export class Template extends EntityCache { +export class Template extends BaseClass { @Column({ unique: true }) code: string; diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index ec7a11d6..df9af328 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -34,7 +34,7 @@ import { trimSpecial, } from ".."; import { BitField } from "../util/BitField"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { ConnectedAccount } from "./ConnectedAccount"; import { Member } from "./Member"; import { Relationship } from "./Relationship"; @@ -94,7 +94,7 @@ export interface UserPrivate extends Pick { } @Entity("users") -export class User extends EntityCache { +export class User extends BaseClass { @Column() username: string; // username max length 32, min 2 (should be configurable) diff --git a/src/util/entities/VoiceState.ts b/src/util/entities/VoiceState.ts index 826ae0e3..b291c4d3 100644 --- a/src/util/entities/VoiceState.ts +++ b/src/util/entities/VoiceState.ts @@ -17,7 +17,7 @@ */ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Guild } from "./Guild"; import { User } from "./User"; @@ -25,7 +25,7 @@ import { Member } from "./Member"; //https://gist.github.com/vassjozsef/e482c65df6ee1facaace8b3c9ff66145#file-voice_state-ex @Entity("voice_states") -export class VoiceState extends EntityCache { +export class VoiceState extends BaseClass { @Column({ nullable: true }) @RelationId((voice_state: VoiceState) => voice_state.guild) guild_id: string; diff --git a/src/util/entities/Webhook.ts b/src/util/entities/Webhook.ts index eeabfea5..91498a22 100644 --- a/src/util/entities/Webhook.ts +++ b/src/util/entities/Webhook.ts @@ -18,7 +18,7 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { Application } from "./Application"; -import { EntityCache } from "../cache"; +import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Guild } from "./Guild"; import { User } from "./User"; @@ -30,7 +30,7 @@ export enum WebhookType { } @Entity("webhooks") -export class Webhook extends EntityCache { +export class Webhook extends BaseClass { @Column({ type: "int" }) type: WebhookType; diff --git a/src/util/schemas/Validator.ts b/src/util/schemas/Validator.ts index 84677e25..1de511d3 100644 --- a/src/util/schemas/Validator.ts +++ b/src/util/schemas/Validator.ts @@ -43,7 +43,7 @@ export const ajv = new Ajv({ allowUnionTypes: true, }); -addFormats(ajv as never); +addFormats(ajv); export function validateSchema(schema: string, data: G): G { const valid = ajv.validate(schema, normalizeBody(data)); diff --git a/src/util/util/Database.ts b/src/util/util/Database.ts index b0d9d24e..e23bb895 100644 --- a/src/util/util/Database.ts +++ b/src/util/util/Database.ts @@ -38,8 +38,7 @@ const dbConnectionString = const DatabaseType = dbConnectionString.includes("://") ? dbConnectionString.split(":")[0]?.replace("+srv", "") - : "better-sqlite3"; - + : "sqlite"; const isSqlite = DatabaseType.includes("sqlite"); const DataSourceOptions = new DataSource({ @@ -51,7 +50,7 @@ const DataSourceOptions = new DataSource({ database: isSqlite ? dbConnectionString : undefined, entities: [path.join(__dirname, "..", "entities", "*.js")], synchronize: !!process.env.DB_SYNC, - logging: process.env["DB_LOGGING"] === "true", + logging: false, bigNumberStrings: false, supportBigNumbers: true, name: "default", @@ -78,13 +77,7 @@ export async function initDatabase(): Promise { } if (!process.env.DB_SYNC) { - const supported = [ - "mysql", - "mariadb", - "postgres", - "sqlite", - "better-sqlite3", - ]; + const supported = ["mysql", "mariadb", "postgres", "sqlite"]; if (!supported.includes(DatabaseType)) { console.log( "[Database]" + @@ -120,10 +113,10 @@ export async function initDatabase(): Promise { // Manually insert every current migration to prevent this: await Promise.all( dbConnection.migrations.map((migration) => - Migration.create({ + Migration.insert({ name: migration.name, timestamp: Date.now(), - }).save(), + }), ), ); } else { diff --git a/src/util/util/Permissions.ts b/src/util/util/Permissions.ts index 4aeb9268..996c72ea 100644 --- a/src/util/util/Permissions.ts +++ b/src/util/util/Permissions.ts @@ -257,26 +257,23 @@ export async function getPermission( } if (guild_id) { - const result = await Promise.all([ - Guild.findOneOrFail({ - where: { id: guild_id }, - select: ["id", "owner_id", ...(opts.guild_select || [])], - relations: opts.guild_relations, - }), - Member.findOneOrFail({ - where: { guild_id, id: user_id }, - relations: ["roles", ...(opts.member_relations || [])], - // select: [ - // "id", // TODO: Bug in typeorm? adding these selects breaks the query. - // "roles", - // ...(opts.member_select || []), - // ], - }), - ]); - guild = result[0]; - member = result[1]; + guild = await Guild.findOneOrFail({ + where: { id: guild_id }, + select: ["id", "owner_id", ...(opts.guild_select || [])], + relations: opts.guild_relations, + }); if (guild.owner_id === user_id) return new Permissions(Permissions.FLAGS.ADMINISTRATOR); + + member = await Member.findOneOrFail({ + where: { guild_id, id: user_id }, + relations: ["roles", ...(opts.member_relations || [])], + // select: [ + // "id", // TODO: Bug in typeorm? adding these selects breaks the query. + // "roles", + // ...(opts.member_select || []), + // ], + }); } let recipient_ids = channel?.recipients?.map((x) => x.user_id); diff --git a/src/util/util/Token.ts b/src/util/util/Token.ts index b839a519..50e63c5f 100644 --- a/src/util/util/Token.ts +++ b/src/util/util/Token.ts @@ -19,7 +19,6 @@ import jwt, { VerifyOptions } from "jsonwebtoken"; import { Config } from "./Config"; import { User } from "../entities"; -import { KeyObject } from "crypto"; export const JWTOptions: VerifyOptions = { algorithms: ["HS256"] }; @@ -63,7 +62,7 @@ async function checkEmailToken( export function checkToken( token: string, - jwtSecret: string | KeyObject, + jwtSecret: string, isEmailVerification = false, ): Promise { return new Promise((res, rej) => { @@ -87,7 +86,7 @@ export function checkToken( const user = await User.findOne({ where: { id: decoded.id }, - select: ["id", "data", "bot", "disabled", "deleted", "rights"], + select: ["data", "bot", "disabled", "deleted", "rights"], }); if (!user) return rej("Invalid Token"); diff --git a/src/util/util/index.ts b/src/util/util/index.ts index 10ae7152..838239b7 100644 --- a/src/util/util/index.ts +++ b/src/util/util/index.ts @@ -24,7 +24,7 @@ export * from "./cdn"; export * from "./Config"; export * from "./Constants"; export * from "./Database"; -export * from "./email/index"; +export * from "./email"; export * from "./Event"; export * from "./FieldError"; export * from "./Intents";