From 8595646b72d42953814ffa2493630d15cfeeb857 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sat, 13 Feb 2021 09:30:21 +0100 Subject: :sparkles: mongoose Schemas --- src/util/Config.ts | 1 + src/util/Database.ts | 94 +++++++++++++++++++++++++++++++++++++++++++++---- src/util/MongoBigInt.ts | 76 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 src/util/MongoBigInt.ts (limited to 'src/util') diff --git a/src/util/Config.ts b/src/util/Config.ts index 5886b268..91ffda01 100644 --- a/src/util/Config.ts +++ b/src/util/Config.ts @@ -5,6 +5,7 @@ var Config: ProviderCache; export default { init: async function init(opts: DefaultOptions = DefaultOptions) { + await db.collection("config").findOne({}); Config = await db.data.config({}).cache(); await Config.init(); await Config.set(opts.merge(Config.cache || {})); diff --git a/src/util/Database.ts b/src/util/Database.ts index ed45a9ad..56f53f9a 100644 --- a/src/util/Database.ts +++ b/src/util/Database.ts @@ -1,9 +1,89 @@ -import { MongoDatabase } from "lambert-db"; +import "./MongoBigInt"; +import mongoose, { Collection } from "mongoose"; +import { ChangeStream, ChangeEvent, Long } from "mongodb"; +import EventEmitter from "events"; +const uri = process.env.MONGO_URL || "mongodb://localhost:27017/fosscord?readPreference=secondaryPreferred"; -// TODO: load url from config -const db = new MongoDatabase("mongodb://127.0.0.1:27017/lambert?readPreference=secondaryPreferred", { - useNewUrlParser: true, - useUnifiedTopology: false, -}); +const connection = mongoose.createConnection(uri, { autoIndex: true }); -export default db; +export default connection; + +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; + + constructor( + public collection: Collection, + public pipeline: Array>, + public opts: { + onlyEvents: boolean; + } + ) { + super(); + } + + async init() { + 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) { + this.data = await this.collection.aggregate(this.pipeline).toArray(); + } + } + + 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) => { + // @ts-ignore + if (doc.fullDocument) { + // @ts-ignore + if (!this.opts.onlyEvents) this.data = doc.fullDocument; + } + + switch (doc.operationType) { + case "dropDatabase": + return this.destroy(); + case "drop": + return this.destroy(); + case "delete": + return this.emit("delete", doc.documentKey._id.toHexString()); + case "insert": + return this.emit("insert", doc.fullDocument); + case "update": + case "replace": + return this.emit("change", doc.fullDocument); + case "invalidate": + return this.destroy(); + default: + return; + } + }; + + destroy() { + this.stream.off("change", this.change); + this.emit("close"); + + if (this.stream.isClosed()) return; + + return this.stream.close(); + } +} diff --git a/src/util/MongoBigInt.ts b/src/util/MongoBigInt.ts new file mode 100644 index 00000000..cc185bed --- /dev/null +++ b/src/util/MongoBigInt.ts @@ -0,0 +1,76 @@ +import mongoose from "mongoose"; + +class LongSchema extends mongoose.SchemaType { + public $conditionalHandlers = { + $lt: this.handleSingle, + $lte: this.handleSingle, + $gt: this.handleSingle, + $gte: this.handleSingle, + $ne: this.handleSingle, + $in: this.handleArray, + $nin: this.handleArray, + $mod: this.handleArray, + $all: this.handleArray, + $bitsAnySet: this.handleArray, + $bitsAllSet: this.handleArray, + }; + + handleSingle(val: any) { + return this.cast(val); + } + + handleArray(val: any) { + var self = this; + return val.map(function (m: any) { + return self.cast(m); + }); + } + + checkRequired(val: any) { + return null != val; + } + + cast(val: any, scope?: any, init?: any) { + if (null === val) return val; + if ("" === val) return null; + + if (val instanceof mongoose.mongo.Long) return BigInt(val.toString()); + if (val instanceof Number || "number" == typeof val) return BigInt(val); + if (!Array.isArray(val) && val.toString) return BigInt(val.toString()); + + // @ts-ignore + throw new SchemaType.CastError("Long", val); + } + + castForQuery($conditional: string, value: any) { + var handler; + if (2 === arguments.length) { + // @ts-ignore + handler = this.$conditionalHandlers[$conditional]; + if (!handler) { + throw new Error("Can't use " + $conditional + " with Long."); + } + return handler.call(this, value); + } else { + return this.cast($conditional); + } + } +} + +LongSchema.cast = mongoose.SchemaType.cast; +LongSchema.set = mongoose.SchemaType.set; +LongSchema.get = mongoose.SchemaType.get; + +declare module "mongoose" { + namespace Types { + class Long extends mongoose.mongo.Long {} + } + namespace Schema { + namespace Types { + class Long extends LongSchema {} + } + } +} + +mongoose.Schema.Types.Long = LongSchema; +mongoose.Types.Long = mongoose.mongo.Long; -- cgit 1.4.1