From f711a0411cbe48319ea3497c4f26f872c18cd2a2 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sat, 21 Aug 2021 16:47:22 +0200 Subject: [PATCH] :construction: typeorm --- api/src/routes/users/@me/settings.ts | 3 +- util/{src/models => oldModels}/Application.ts | 0 util/{src/models => oldModels}/AuditLog.ts | 0 util/{src/models => oldModels}/Ban.ts | 0 util/{src/models => oldModels}/Channel.ts | 0 util/{src/models => oldModels}/Emoji.ts | 0 util/{src/models => oldModels}/Event.ts | 2 +- util/{src/models => oldModels}/Guild.ts | 0 util/{src/models => oldModels}/Interaction.ts | 0 util/{src/models => oldModels}/Invite.ts | 0 util/{src/models => oldModels}/Member.ts | 0 util/{src/models => oldModels}/Message.ts | 0 util/{src/models => oldModels}/RateLimit.ts | 0 util/{src/models => oldModels}/ReadState.ts | 0 util/{src/models => oldModels}/Role.ts | 0 util/{src/models => oldModels}/Team.ts | 0 util/{src/models => oldModels}/Template.ts | 0 util/{src/models => oldModels}/VoiceState.ts | 0 util/{src/models => oldModels}/Webhook.ts | 0 util/oldModels/index.ts | 93 +++++++++ util/package-lock.json | Bin 52078 -> 158687 bytes util/package.json | 3 + util/src/index.ts | 12 +- util/src/models/Activity.ts | 80 +------ util/src/models/BaseClass.ts | 28 +++ util/src/models/Status.ts | 6 - util/src/models/User.ts | 196 +++++++----------- util/src/models/index.ts | 91 +------- util/src/util/Database.ts | 159 -------------- util/tsconfig.json | 4 +- util/{src => }/util/AutoUpdate.ts | 0 util/{src => }/util/BitField.ts | 0 util/{src => }/util/Config.ts | 0 util/{src => }/util/Constants.ts | 0 util/util/Database.ts | 7 + util/{src => }/util/Event.ts | 0 util/{src => }/util/Intents.ts | 0 util/{src => }/util/MessageFlags.ts | 0 util/{src => }/util/MongoBigInt.ts | 0 util/{src => }/util/Permissions.ts | 0 util/{src => }/util/RabbitMQ.ts | 0 util/{src => }/util/Regex.ts | 0 util/{src => }/util/Snowflake.ts | 0 util/{src => }/util/String.ts | 0 util/{src => }/util/UserFlags.ts | 0 util/{src => }/util/checkToken.ts | 0 util/{src => }/util/index.ts | 1 + util/{src => }/util/toBigInt.ts | 0 48 files changed, 218 insertions(+), 467 deletions(-) rename util/{src/models => oldModels}/Application.ts (100%) rename util/{src/models => oldModels}/AuditLog.ts (100%) rename util/{src/models => oldModels}/Ban.ts (100%) rename util/{src/models => oldModels}/Channel.ts (100%) rename util/{src/models => oldModels}/Emoji.ts (100%) rename util/{src/models => oldModels}/Event.ts (99%) rename util/{src/models => oldModels}/Guild.ts (100%) rename util/{src/models => oldModels}/Interaction.ts (100%) rename util/{src/models => oldModels}/Invite.ts (100%) rename util/{src/models => oldModels}/Member.ts (100%) rename util/{src/models => oldModels}/Message.ts (100%) rename util/{src/models => oldModels}/RateLimit.ts (100%) rename util/{src/models => oldModels}/ReadState.ts (100%) rename util/{src/models => oldModels}/Role.ts (100%) rename util/{src/models => oldModels}/Team.ts (100%) rename util/{src/models => oldModels}/Template.ts (100%) rename util/{src/models => oldModels}/VoiceState.ts (100%) rename util/{src/models => oldModels}/Webhook.ts (100%) create mode 100644 util/oldModels/index.ts create mode 100644 util/src/models/BaseClass.ts delete mode 100644 util/src/util/Database.ts rename util/{src => }/util/AutoUpdate.ts (100%) rename util/{src => }/util/BitField.ts (100%) rename util/{src => }/util/Config.ts (100%) rename util/{src => }/util/Constants.ts (100%) create mode 100644 util/util/Database.ts rename util/{src => }/util/Event.ts (100%) rename util/{src => }/util/Intents.ts (100%) rename util/{src => }/util/MessageFlags.ts (100%) rename util/{src => }/util/MongoBigInt.ts (100%) rename util/{src => }/util/Permissions.ts (100%) rename util/{src => }/util/RabbitMQ.ts (100%) rename util/{src => }/util/Regex.ts (100%) rename util/{src => }/util/Snowflake.ts (100%) rename util/{src => }/util/String.ts (100%) rename util/{src => }/util/UserFlags.ts (100%) rename util/{src => }/util/checkToken.ts (100%) rename util/{src => }/util/index.ts (91%) rename util/{src => }/util/toBigInt.ts (100%) diff --git a/api/src/routes/users/@me/settings.ts b/api/src/routes/users/@me/settings.ts index 5664fc2f..f045a010 100644 --- a/api/src/routes/users/@me/settings.ts +++ b/api/src/routes/users/@me/settings.ts @@ -8,7 +8,8 @@ const router = Router(); router.patch("/", check(UserSettingsSchema), async (req: Request, res: Response) => { const body = req.body as UserSettings; - await UserModel.updateOne({ id: req.user_id }, body).exec(); + // only users can update user settings + await UserModel.updateOne({ id: req.user_id, bot: false }, body).exec(); res.sendStatus(204); }); diff --git a/util/src/models/Application.ts b/util/oldModels/Application.ts similarity index 100% rename from util/src/models/Application.ts rename to util/oldModels/Application.ts diff --git a/util/src/models/AuditLog.ts b/util/oldModels/AuditLog.ts similarity index 100% rename from util/src/models/AuditLog.ts rename to util/oldModels/AuditLog.ts diff --git a/util/src/models/Ban.ts b/util/oldModels/Ban.ts similarity index 100% rename from util/src/models/Ban.ts rename to util/oldModels/Ban.ts diff --git a/util/src/models/Channel.ts b/util/oldModels/Channel.ts similarity index 100% rename from util/src/models/Channel.ts rename to util/oldModels/Channel.ts diff --git a/util/src/models/Emoji.ts b/util/oldModels/Emoji.ts similarity index 100% rename from util/src/models/Emoji.ts rename to util/oldModels/Emoji.ts diff --git a/util/src/models/Event.ts b/util/oldModels/Event.ts similarity index 99% rename from util/src/models/Event.ts rename to util/oldModels/Event.ts index 86d0fd00..904522a8 100644 --- a/util/src/models/Event.ts +++ b/util/oldModels/Event.ts @@ -3,7 +3,7 @@ import { DMChannel, Channel } from "./Channel"; import { Guild } from "./Guild"; import { Member, PublicMember, UserGuildSettings } from "./Member"; import { Emoji } from "./Emoji"; -import { Presence } from "./Activity"; +import { Presence } from "../models/Activity"; import { Role } from "./Role"; import { Invite } from "./Invite"; import { Message, PartialEmoji } from "./Message"; diff --git a/util/src/models/Guild.ts b/util/oldModels/Guild.ts similarity index 100% rename from util/src/models/Guild.ts rename to util/oldModels/Guild.ts diff --git a/util/src/models/Interaction.ts b/util/oldModels/Interaction.ts similarity index 100% rename from util/src/models/Interaction.ts rename to util/oldModels/Interaction.ts diff --git a/util/src/models/Invite.ts b/util/oldModels/Invite.ts similarity index 100% rename from util/src/models/Invite.ts rename to util/oldModels/Invite.ts diff --git a/util/src/models/Member.ts b/util/oldModels/Member.ts similarity index 100% rename from util/src/models/Member.ts rename to util/oldModels/Member.ts diff --git a/util/src/models/Message.ts b/util/oldModels/Message.ts similarity index 100% rename from util/src/models/Message.ts rename to util/oldModels/Message.ts diff --git a/util/src/models/RateLimit.ts b/util/oldModels/RateLimit.ts similarity index 100% rename from util/src/models/RateLimit.ts rename to util/oldModels/RateLimit.ts diff --git a/util/src/models/ReadState.ts b/util/oldModels/ReadState.ts similarity index 100% rename from util/src/models/ReadState.ts rename to util/oldModels/ReadState.ts diff --git a/util/src/models/Role.ts b/util/oldModels/Role.ts similarity index 100% rename from util/src/models/Role.ts rename to util/oldModels/Role.ts diff --git a/util/src/models/Team.ts b/util/oldModels/Team.ts similarity index 100% rename from util/src/models/Team.ts rename to util/oldModels/Team.ts diff --git a/util/src/models/Template.ts b/util/oldModels/Template.ts similarity index 100% rename from util/src/models/Template.ts rename to util/oldModels/Template.ts diff --git a/util/src/models/VoiceState.ts b/util/oldModels/VoiceState.ts similarity index 100% rename from util/src/models/VoiceState.ts rename to util/oldModels/VoiceState.ts diff --git a/util/src/models/Webhook.ts b/util/oldModels/Webhook.ts similarity index 100% rename from util/src/models/Webhook.ts rename to util/oldModels/Webhook.ts diff --git a/util/oldModels/index.ts b/util/oldModels/index.ts new file mode 100644 index 00000000..63578a13 --- /dev/null +++ b/util/oldModels/index.ts @@ -0,0 +1,93 @@ +// @ts-nocheck +import mongoose, { Schema, Document } from "mongoose"; +import mongooseAutoPopulate from "mongoose-autopopulate"; + +type UpdateWithAggregationPipeline = UpdateAggregationStage[]; +type UpdateAggregationStage = + | { $addFields: any } + | { $set: any } + | { $project: any } + | { $unset: any } + | { $replaceRoot: any } + | { $replaceWith: any }; +type EnforceDocument = T extends Document ? T : T & Document & TMethods; + +declare module "mongoose" { + interface SchemaOptions { + removeResponse?: string[]; + } + interface Model { + // removed null -> always return document -> throw error if it doesn't exist + findOne( + filter?: FilterQuery, + projection?: any | null, + options?: QueryOptions | null, + callback?: (err: CallbackError, doc: EnforceDocument) => void + ): QueryWithHelpers, EnforceDocument, TQueryHelpers>; + findOneAndUpdate( + filter?: FilterQuery, + update?: UpdateQuery | UpdateWithAggregationPipeline, + options?: QueryOptions | null, + callback?: (err: any, doc: EnforceDocument | null, res: any) => void + ): QueryWithHelpers, EnforceDocument, TQueryHelpers>; + } +} + +var HTTPError: any; + +try { + HTTPError = require("lambert-server").HTTPError; +} catch (e) { + HTTPError = Error; +} + +mongoose.plugin(mongooseAutoPopulate); + +mongoose.plugin((schema: Schema, opts: any) => { + schema.set("toObject", { + virtuals: true, + versionKey: false, + transform(doc: any, ret: any) { + delete ret._id; + delete ret.__v; + const props = schema.get("removeResponse") || []; + props.forEach((prop: string) => { + delete ret[prop]; + }); + }, + }); + schema.post("findOne", function (doc, next) { + try { + // @ts-ignore + const isExistsQuery = JSON.stringify(this._userProvidedFields) === JSON.stringify({ _id: 1 }); + if (!doc && !isExistsQuery) { + // @ts-ignore + return next(new HTTPError(`${this?.mongooseCollection?.name}.${this?._conditions?.id} not found`, 400)); + } + // @ts-ignore + return next(); + } catch (error) { + // @ts-ignore + next(); + } + }); +}); + +export * from "../models/Activity"; +export * from "./Application"; +export * from "./Ban"; +export * from "./Channel"; +export * from "./Emoji"; +export * from "./Event"; +export * from "./Template"; +export * from "./Guild"; +export * from "./Invite"; +export * from "./Interaction"; +export * from "./Member"; +export * from "./Message"; +export * from "../models/Status"; +export * from "./Role"; +export * from "./User"; +export * from "./VoiceState"; +export * from "./ReadState"; +export * from "./RateLimit"; diff --git a/util/package-lock.json b/util/package-lock.json index befb6563dec7b84ed7585bd12f74dbbe45291228..d0be1490499e2e6106ca46a2a911f1dd650d8013 100644 GIT binary patch literal 158687 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 delta 658 zcmX}oT}TsP7zJR?x9QxRD>u~4%}v9yMVqBW8Hqu4A*GlT%AkxcL`pA=pe{6oC=879 zMc<5-MJS|J;#Yl$6oy{-lVvFsB`N+inWBQFp(we6?#|UYhiBxoezr!_VimGPcpwM0 zi$?ju@<~Hk+ZxHKv&r2qi=J$6tvp$dK6;;IlEbN1P(xjm%u5>uwI5yOQV+uDVOS)$ z5n^fo$vBG7j3-}Kr~H;312)&9os1j8=zC&xl}}5i+&Jma-3;kG;e}j2l?|D3+5vgz zoDp)h;4vt_a!FR)js}%gNlN!>X|zTsU$X`BO>MbGzIc!v`fqf-ksO&pbgRiu`x@u| zKcSqaJXzF|1ah=#rLQ&IES-IB$jR{wkgc;ZkUjIUkcNd2&~tk{4-_Lv_Ww$O4F9c# z>{v_0%PD9Crw3zw-EHGKx9C>3AS6H?(xC|It{yhr6wraSy1Od*CD)BPvL%63LQ!K7wEZ|6aXZG*o7$A-#ov)Z?pac!EOk({A|rMjs|P zwI2=2{}JI(!-H^X)Zs7i8UkE8o*%<>_((g`Z0tVny}Eiwy9l1hK@+PO(6t5vJ-zOmp*2(aCfL7MgRL0(gU}V3$Y% iUrH9^d~lB#QRcnk8Q63{bf}A&Vnq{d(3cuY4SxVo9|@NL diff --git a/util/package.json b/util/package.json index d84897dc..5825ce87 100644 --- a/util/package.json +++ b/util/package.json @@ -45,6 +45,9 @@ "mongoose": "^5.13.7", "mongoose-autopopulate": "^0.12.3", "node-fetch": "^2.6.1", + "reflect-metadata": "^0.1.13", + "sqlite3": "^5.0.2", + "typeorm": "^0.2.37", "typescript": "^4.1.3" } } diff --git a/util/src/index.ts b/util/src/index.ts index 3565fb6b..c6bbfd57 100644 --- a/util/src/index.ts +++ b/util/src/index.ts @@ -1,10 +1,10 @@ -export * from "./util/checkToken"; +import "reflect-metadata"; -export * as Constants from "./util/Constants"; +// export * as Constants from "../util/Constants"; export * from "./models/index"; -export * from "./util/index"; +// export * from "../util/index"; -import Config from "./util/Config"; -import db, { MongooseCache, toObject } from "./util/Database"; +// import Config from "../util/Config"; +// import db, { MongooseCache, toObject } from "./util/Database"; -export { Config, db, MongooseCache, toObject }; +// export { Config }; diff --git a/util/src/models/Activity.ts b/util/src/models/Activity.ts index 17abd1ca..6b13477f 100644 --- a/util/src/models/Activity.ts +++ b/util/src/models/Activity.ts @@ -1,7 +1,5 @@ -import { User } from ".."; +import { User } from "./User"; import { ClientStatus, Status } from "./Status"; -import { Schema, model, Types, Document } from "mongoose"; -import toBigInt from "../util/toBigInt"; export interface Presence { user: User; @@ -47,82 +45,6 @@ export interface Activity { flags?: bigint; } -export const ActivitySchema = { - name: { type: String, required: true }, - type: { type: Number, required: true }, - url: String, - created_at: Date, - timestamps: [ - { - start: Number, - end: Number, - }, - ], - application_id: String, - details: String, - state: String, - emoji: { - name: String, - id: String, - amimated: Boolean, - }, - party: { - id: String, - size: [Number, Number], - }, - assets: { - large_image: String, - large_text: String, - small_image: String, - small_text: String, - }, - secrets: { - join: String, - spectate: String, - match: String, - }, - instance: Boolean, - flags: { type: String, get: toBigInt }, -}; - -export const ActivityBodySchema = { - name: String, - type: Number, - $url: String, - $created_at: Date, - $timestamps: [ - { - $start: Number, - $end: Number, - }, - ], - $application_id: String, - $details: String, - $state: String, - $emoji: { - $name: String, - $id: String, - $amimated: Boolean, - }, - $party: { - $id: String, - $size: [Number, Number], - }, - $assets: { - $large_image: String, - $large_text: String, - $small_image: String, - $small_text: String, - }, - $secrets: { - $join: String, - $spectate: String, - $match: String, - }, - $instance: Boolean, - $flags: BigInt, -}; - export enum ActivityType { GAME = 0, STREAMING = 1, diff --git a/util/src/models/BaseClass.ts b/util/src/models/BaseClass.ts new file mode 100644 index 00000000..78cd329c --- /dev/null +++ b/util/src/models/BaseClass.ts @@ -0,0 +1,28 @@ +import "reflect-metadata"; +import { BaseEntity, Column } from "typeorm"; + +export class BaseClass extends BaseEntity { + @Column() + id?: string; + + constructor(props?: any) { + super(); + BaseClass.assign(props, this, "body."); + } + + private static assign(props: any, object: any, path?: string): any { + const expectedType = Reflect.getMetadata("design:type", object, props); + console.log(expectedType, object, props, path, typeof object); + + if (typeof object !== typeof props) throw new Error(`Property at ${path} must be`); + if (typeof object === "object") + return Object.keys(object).map((key) => BaseClass.assign(props[key], object[key], `${path}.${key}`)); + } +} + +// @ts-ignore +global.BaseClass = BaseClass; + +var test = new BaseClass({}); + +setTimeout(() => {}, 10000 * 1000); diff --git a/util/src/models/Status.ts b/util/src/models/Status.ts index 5a9bf2ca..c4dab586 100644 --- a/util/src/models/Status.ts +++ b/util/src/models/Status.ts @@ -5,9 +5,3 @@ export interface ClientStatus { mobile?: string; // e.g. iOS/Android web?: string; // e.g. browser, bot account } - -export const ClientStatus = { - desktop: String, - mobile: String, - web: String, -}; diff --git a/util/src/models/User.ts b/util/src/models/User.ts index c667e954..38045738 100644 --- a/util/src/models/User.ts +++ b/util/src/models/User.ts @@ -1,8 +1,7 @@ -import { Activity, ActivitySchema } from "./Activity"; +import { Column, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm"; +import { Activity } from "./Activity"; +import { BaseClass } from "./BaseClass"; import { ClientStatus, Status } from "./Status"; -import { Schema, Types, Document } from "mongoose"; -import db from "../util/Database"; -import toBigInt from "../util/toBigInt"; export const PublicUserProjection = { username: true, @@ -16,53 +15,109 @@ export const PublicUserProjection = { bot: true, }; -export interface User { +export class User extends BaseClass { + @PrimaryGeneratedColumn() id: string; - username: string; // username max length 32, min 2 + + @Column() + username: string; // username max length 32, min 2 (should be configurable) + + @Column() discriminator: string; // #0001 4 digit long string from #0001 - #9999 + + @Column() avatar: string | null; // hash of the user avatar + + @Column() accent_color: number | null; // banner color of user - banner: string | null; + + @Column() + banner: string | null; // hash of the user banner + + @Column() phone: string | null; // phone number of the user + + @Column() desktop: boolean; // if the user has desktop app installed + + @Column() mobile: boolean; // if the user has mobile app installed + + @Column() premium: boolean; // if user bought nitro + + @Column() premium_type: number; // nitro level + + @Column() bot: boolean; // if user is bot - bio: string; // short description of the user (max 190 chars) - system: boolean; // shouldn't be used, the api sents this field type true, if the genetaed message comes from a system generated author + + @Column() + bio: string; // short description of the user (max 190 chars -> should be configurable) + + @Column() + system: boolean; // shouldn't be used, the api sents this field type true, if the generated message comes from a system generated author + + @Column() nsfw_allowed: boolean; // if the user is older than 18 (resp. Config) + + @Column() mfa_enabled: boolean; // if multi factor authentication is enabled + + @Column() created_at: Date; // registration date + + @Column() verified: boolean; // if the user is offically verified + + @Column() disabled: boolean; // if the account is disabled + + @Column() deleted: boolean; // if the user was deleted + + @Column() email: string | null; // email of the user + + @Column() flags: bigint; // UserFlags + + @Column() public_flags: bigint; - user_settings: UserSettings; + + @Column("simple-array") // string in simple-array must not contain commas guilds: string[]; // array of guild ids the user is part of + + @Column("simple-json") + user_settings: UserSettings; + + @Column("simple-json") user_data: UserData; + + @Column("simple-json") presence: { status: Status; activities: Activity[]; client_status: ClientStatus; }; + + @Column("simple-json") + relationships: Relationship[]; + + @Column("simple-json") + connected_accounts: ConnectedAccount[]; } -// Private user data: +// @ts-ignore +global.User = User; + +// Private user data that should never get sent to the client export interface UserData { valid_tokens_since: Date; // all tokens with a previous issue date are invalid - relationships: Relationship[]; - connected_accounts: ConnectedAccount[]; hash: string; // hash of the password, salt is saved in password (bcrypt) fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts } -export interface UserDocument extends User, Document { - id: string; -} - export interface PublicUser { id: string; discriminator: string; @@ -143,110 +198,3 @@ export interface UserSettings { theme: "dark" | "white"; // dark timezone_offset: number; // e.g -60 } - -export const UserSchema = new Schema({ - id: String, - username: String, - discriminator: String, - avatar: String, - accent_color: Number, - banner: String, - phone: String, - desktop: Boolean, - mobile: Boolean, - premium: Boolean, - premium_type: Number, - bot: Boolean, - bio: String, - system: Boolean, - nsfw_allowed: Boolean, - mfa_enabled: Boolean, - created_at: Date, - verified: Boolean, - disabled: Boolean, - deleted: Boolean, - email: String, - flags: { type: String, get: toBigInt }, // TODO: automatically convert Types.Long to BitField of UserFlags - public_flags: { type: String, get: toBigInt }, - guilds: [String], // array of guild ids the user is part of - user_data: { - fingerprints: [String], - hash: String, // hash of the password, salt is saved in password (bcrypt) - valid_tokens_since: Date, // all tokens with a previous issue date are invalid - relationships: [ - { - id: { type: String, required: true }, - nickname: String, - type: { type: Number }, - }, - ], - connected_accounts: [ - { - access_token: String, - friend_sync: Boolean, - id: String, - name: String, - revoked: Boolean, - show_activity: Boolean, - type: { type: String }, - verifie: Boolean, - visibility: Number, - }, - ], - }, - user_settings: { - afk_timeout: Number, - allow_accessibility_detection: Boolean, - animate_emoji: Boolean, - animate_stickers: Number, - contact_sync_enabled: Boolean, - convert_emoticons: Boolean, - custom_status: { - emoji_id: String, - emoji_name: String, - expires_at: Number, - text: String, - }, - default_guilds_restricted: Boolean, - detect_platform_accounts: Boolean, - developer_mode: Boolean, - disable_games_tab: Boolean, - enable_tts_command: Boolean, - explicit_content_filter: Number, - friend_source_flags: { all: Boolean }, - gateway_connected: Boolean, - gif_auto_play: Boolean, - // every top guild is displayed as a "folder" - guild_folders: [ - { - color: Number, - guild_ids: [String], - id: Number, - name: String, - }, - ], - guild_positions: [String], // guild ids ordered by position - inline_attachment_media: Boolean, - inline_embed_media: Boolean, - locale: String, // en_US - message_display_compact: Boolean, - native_phone_integration_enabled: Boolean, - render_embeds: Boolean, - render_reactions: Boolean, - restricted_guilds: [String], - show_current_game: Boolean, - status: String, - stream_notifications_enabled: Boolean, - theme: String, // dark - timezone_offset: Number, // e.g -60, - }, - - presence: { - status: String, - activities: [ActivitySchema], - client_status: ClientStatus, - }, -}); - -// @ts-ignore -export const UserModel = db.model("User", UserSchema, "users"); diff --git a/util/src/models/index.ts b/util/src/models/index.ts index b6100f86..94882a0a 100644 --- a/util/src/models/index.ts +++ b/util/src/models/index.ts @@ -1,93 +1,4 @@ -// @ts-nocheck -import mongoose, { Schema, Document } from "mongoose"; -import mongooseAutoPopulate from "mongoose-autopopulate"; - -type UpdateWithAggregationPipeline = UpdateAggregationStage[]; -type UpdateAggregationStage = - | { $addFields: any } - | { $set: any } - | { $project: any } - | { $unset: any } - | { $replaceRoot: any } - | { $replaceWith: any }; -type EnforceDocument = T extends Document ? T : T & Document & TMethods; - -declare module "mongoose" { - interface SchemaOptions { - removeResponse?: string[]; - } - interface Model { - // removed null -> always return document -> throw error if it doesn't exist - findOne( - filter?: FilterQuery, - projection?: any | null, - options?: QueryOptions | null, - callback?: (err: CallbackError, doc: EnforceDocument) => void - ): QueryWithHelpers, EnforceDocument, TQueryHelpers>; - findOneAndUpdate( - filter?: FilterQuery, - update?: UpdateQuery | UpdateWithAggregationPipeline, - options?: QueryOptions | null, - callback?: (err: any, doc: EnforceDocument | null, res: any) => void - ): QueryWithHelpers, EnforceDocument, TQueryHelpers>; - } -} - -var HTTPError: any; - -try { - HTTPError = require("lambert-server").HTTPError; -} catch (e) { - HTTPError = Error; -} - -mongoose.plugin(mongooseAutoPopulate); - -mongoose.plugin((schema: Schema, opts: any) => { - schema.set("toObject", { - virtuals: true, - versionKey: false, - transform(doc: any, ret: any) { - delete ret._id; - delete ret.__v; - const props = schema.get("removeResponse") || []; - props.forEach((prop: string) => { - delete ret[prop]; - }); - }, - }); - schema.post("findOne", function (doc, next) { - try { - // @ts-ignore - const isExistsQuery = JSON.stringify(this._userProvidedFields) === JSON.stringify({ _id: 1 }); - if (!doc && !isExistsQuery) { - // @ts-ignore - return next(new HTTPError(`${this?.mongooseCollection?.name}.${this?._conditions?.id} not found`, 400)); - } - // @ts-ignore - return next(); - } catch (error) { - // @ts-ignore - next(); - } - }); -}); - export * from "./Activity"; -export * from "./Application"; -export * from "./Ban"; -export * from "./Channel"; -export * from "./Emoji"; -export * from "./Event"; -export * from "./Template"; -export * from "./Guild"; -export * from "./Invite"; -export * from "./Interaction"; -export * from "./Member"; -export * from "./Message"; +export * from "./BaseClass"; export * from "./Status"; -export * from "./Role"; export * from "./User"; -export * from "./VoiceState"; -export * from "./ReadState"; -export * from "./RateLimit"; diff --git a/util/src/util/Database.ts b/util/src/util/Database.ts deleted file mode 100644 index ea517234..00000000 --- a/util/src/util/Database.ts +++ /dev/null @@ -1,159 +0,0 @@ -// @ts-nocheck -import "./MongoBigInt"; -import mongoose, { Collection, Connection, LeanDocument } from "mongoose"; -import { ChangeStream, ChangeEvent, Long } from "mongodb"; -import EventEmitter from "events"; -const uri = process.env.MONGO_URL || "mongodb://localhost:27017/fosscord?readPreference=secondaryPreferred"; -import { URL } from "url"; - -const url = new URL(uri.replace("mongodb://", "http://")); - -const connection = mongoose.createConnection(uri, { - autoIndex: true, - useNewUrlParser: true, - useUnifiedTopology: true, - useFindAndModify: true, -}); - -// this will return the new updated document for findOneAndUpdate -mongoose.set("returnOriginal", false); // https://mongoosejs.com/docs/api/model.html#model_Model.findOneAndUpdate - -console.log(`[Database] connect: mongodb://${url.username}@${url.host}${url.pathname}${url.search}`); -connection.once("open", () => { - console.log("[Database] connected"); -}); - -export default connection; - -function transform(document: T) { - // @ts-ignore - if (!document || !document.toObject) { - try { - // @ts-ignore - delete document._id; - // @ts-ignore - delete document.__v; - } catch (error) {} - return document; - } - // @ts-ignore - return document.toObject({ virtuals: true }); -} - -export function toObject(document: T): LeanDocument { - // @ts-ignore - return Array.isArray(document) ? document.map((x) => transform(x)) : transform(document); -} - -export interface MongooseCache { - on(event: "delete", listener: (id: string) => void): this; - on(event: "change", listener: (data: any) => void): this; - on(event: "insert", listener: (data: any) => void): this; - on(event: "close", listener: () => void): this; -} - -export class MongooseCache extends EventEmitter { - public stream: ChangeStream; - public data: any; - public initalizing?: Promise; - - constructor( - public collection: Collection, - public pipeline: Array>, - public opts: { - onlyEvents: boolean; - array?: boolean; - } - ) { - super(); - if (this.opts.array == null) this.opts.array = true; - } - - init = () => { - if (this.initalizing) return this.initalizing; - this.initalizing = new Promise(async (resolve, reject) => { - // @ts-ignore - this.stream = this.collection.watch(this.pipeline, { fullDocument: "updateLookup" }); - - this.stream.on("change", this.change); - this.stream.on("close", this.destroy); - this.stream.on("error", console.error); - - if (!this.opts.onlyEvents) { - const arr = await this.collection.aggregate(this.pipeline).toArray(); - if (this.opts.array) this.data = arr || []; - else this.data = arr?.[0]; - } - resolve(); - }); - return this.initalizing; - }; - - changeStream = (pipeline: any) => { - this.pipeline = pipeline; - this.destroy(); - this.init(); - }; - - convertResult = (obj: any) => { - if (obj instanceof Long) return BigInt(obj.toString()); - if (typeof obj === "object") { - Object.keys(obj).forEach((key) => { - obj[key] = this.convertResult(obj[key]); - }); - } - - return obj; - }; - - change = (doc: ChangeEvent) => { - try { - switch (doc.operationType) { - case "dropDatabase": - return this.destroy(); - case "drop": - return this.destroy(); - case "delete": - if (!this.opts.onlyEvents) { - if (this.opts.array) { - this.data = this.data.filter((x: any) => doc.documentKey?._id?.equals(x._id)); - } else this.data = null; - } - return this.emit("delete", doc.documentKey._id.toHexString()); - case "insert": - if (!this.opts.onlyEvents) { - if (this.opts.array) this.data.push(doc.fullDocument); - else this.data = doc.fullDocument; - } - return this.emit("insert", doc.fullDocument); - case "update": - case "replace": - if (!this.opts.onlyEvents) { - if (this.opts.array) { - const i = this.data.findIndex((x: any) => doc.fullDocument?._id?.equals(x._id)); - if (i == -1) this.data.push(doc.fullDocument); - else this.data[i] = doc.fullDocument; - } else this.data = doc.fullDocument; - } - - return this.emit("change", doc.fullDocument); - case "invalidate": - return this.destroy(); - default: - return; - } - } catch (error) { - this.emit("error", error); - } - }; - - destroy = () => { - this.data = null; - this.stream?.off("change", this.change); - this.emit("close"); - - if (this.stream.isClosed()) return; - - return this.stream.close(); - }; -} diff --git a/util/tsconfig.json b/util/tsconfig.json index 520774d3..fa3bc6cb 100644 --- a/util/tsconfig.json +++ b/util/tsconfig.json @@ -65,6 +65,8 @@ /* Advanced Options */ "skipLibCheck": true /* Skip type checking of declaration files. */, - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, + "emitDecoratorMetadata": true, + "experimentalDecorators": true } } diff --git a/util/src/util/AutoUpdate.ts b/util/util/AutoUpdate.ts similarity index 100% rename from util/src/util/AutoUpdate.ts rename to util/util/AutoUpdate.ts diff --git a/util/src/util/BitField.ts b/util/util/BitField.ts similarity index 100% rename from util/src/util/BitField.ts rename to util/util/BitField.ts diff --git a/util/src/util/Config.ts b/util/util/Config.ts similarity index 100% rename from util/src/util/Config.ts rename to util/util/Config.ts diff --git a/util/src/util/Constants.ts b/util/util/Constants.ts similarity index 100% rename from util/src/util/Constants.ts rename to util/util/Constants.ts diff --git a/util/util/Database.ts b/util/util/Database.ts new file mode 100644 index 00000000..e0e43547 --- /dev/null +++ b/util/util/Database.ts @@ -0,0 +1,7 @@ +import "reflect-metadata"; +import { createConnection } from "typeorm"; + +// 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 + +createConnection({ type: "sqlite", database: "database.db", entities: [], synchronize: true, logging: true }); diff --git a/util/src/util/Event.ts b/util/util/Event.ts similarity index 100% rename from util/src/util/Event.ts rename to util/util/Event.ts diff --git a/util/src/util/Intents.ts b/util/util/Intents.ts similarity index 100% rename from util/src/util/Intents.ts rename to util/util/Intents.ts diff --git a/util/src/util/MessageFlags.ts b/util/util/MessageFlags.ts similarity index 100% rename from util/src/util/MessageFlags.ts rename to util/util/MessageFlags.ts diff --git a/util/src/util/MongoBigInt.ts b/util/util/MongoBigInt.ts similarity index 100% rename from util/src/util/MongoBigInt.ts rename to util/util/MongoBigInt.ts diff --git a/util/src/util/Permissions.ts b/util/util/Permissions.ts similarity index 100% rename from util/src/util/Permissions.ts rename to util/util/Permissions.ts diff --git a/util/src/util/RabbitMQ.ts b/util/util/RabbitMQ.ts similarity index 100% rename from util/src/util/RabbitMQ.ts rename to util/util/RabbitMQ.ts diff --git a/util/src/util/Regex.ts b/util/util/Regex.ts similarity index 100% rename from util/src/util/Regex.ts rename to util/util/Regex.ts diff --git a/util/src/util/Snowflake.ts b/util/util/Snowflake.ts similarity index 100% rename from util/src/util/Snowflake.ts rename to util/util/Snowflake.ts diff --git a/util/src/util/String.ts b/util/util/String.ts similarity index 100% rename from util/src/util/String.ts rename to util/util/String.ts diff --git a/util/src/util/UserFlags.ts b/util/util/UserFlags.ts similarity index 100% rename from util/src/util/UserFlags.ts rename to util/util/UserFlags.ts diff --git a/util/src/util/checkToken.ts b/util/util/checkToken.ts similarity index 100% rename from util/src/util/checkToken.ts rename to util/util/checkToken.ts diff --git a/util/src/util/index.ts b/util/util/index.ts similarity index 91% rename from util/src/util/index.ts rename to util/util/index.ts index e52a23b7..67c45b59 100644 --- a/util/src/util/index.ts +++ b/util/util/index.ts @@ -9,3 +9,4 @@ export * from "./UserFlags"; export * from "./toBigInt"; export * from "./RabbitMQ"; export * from "./Event"; +export * from "./checkToken"; diff --git a/util/src/util/toBigInt.ts b/util/util/toBigInt.ts similarity index 100% rename from util/src/util/toBigInt.ts rename to util/util/toBigInt.ts