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<Record<string, unknown>>,
+ 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;
|