diff --git a/src/util/Database.ts b/src/util/Database.ts
index 2304378c..339ac65b 100644
--- a/src/util/Database.ts
+++ b/src/util/Database.ts
@@ -1,13 +1,31 @@
import "./MongoBigInt";
-import mongoose, { Collection, Connection } from "mongoose";
+import mongoose, { Collection, Connection, LeanDocument } from "mongoose";
import { ChangeStream, ChangeEvent, Long } from "mongodb";
import EventEmitter from "events";
+import { Document } from "mongoose";
const uri = process.env.MONGO_URL || "mongodb://localhost:27017/fosscord?readPreference=secondaryPreferred";
-const connection = mongoose.createConnection(uri, { autoIndex: true });
+console.log(`[DB] connect: ${uri}`);
+
+const connection = mongoose.createConnection(uri, {
+ autoIndex: true,
+ useNewUrlParser: true,
+ useUnifiedTopology: true,
+ useFindAndModify: false,
+});
export default <Connection>connection;
+function transform<T>(document: T) {
+ // @ts-ignore
+ return document.toObject({ virtuals: true });
+}
+
+export function toObject<T>(document: T): LeanDocument<T> {
+ // @ts-ignore
+ return Array.isArray(document) ? document.map((x) => transform<T>(x)) : transform(document);
+}
+
export interface MongooseCache {
on(event: "delete", listener: (id: string) => void): this;
on(event: "change", listener: (data: any) => void): this;
@@ -29,7 +47,8 @@ export class MongooseCache extends EventEmitter {
super();
}
- async init() {
+ init = async () => {
+ // @ts-ignore
this.stream = this.collection.watch(this.pipeline, { fullDocument: "updateLookup" });
this.stream.on("change", this.change);
@@ -40,9 +59,15 @@ export class MongooseCache extends EventEmitter {
const arr = await this.collection.aggregate(this.pipeline).toArray();
this.data = arr.length ? arr[0] : arr;
}
- }
+ };
- convertResult(obj: any) {
+ 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) => {
@@ -51,40 +76,44 @@ export class MongooseCache extends EventEmitter {
}
return obj;
- }
+ };
change = (doc: ChangeEvent) => {
- // @ts-ignore
- if (doc.fullDocument) {
+ try {
// @ts-ignore
- if (!this.opts.onlyEvents) this.data = doc.fullDocument;
- }
+ 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;
+ 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;
+ }
+ } catch (error) {
+ this.emit("error", error);
}
};
- destroy() {
- this.stream.off("change", this.change);
+ 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
index c4e5f623..fc451925 100644
--- a/src/util/MongoBigInt.ts
+++ b/src/util/MongoBigInt.ts
@@ -44,7 +44,7 @@ class LongSchema extends mongoose.SchemaType {
if (val instanceof Number || "number" == typeof val) return BigInt(val);
if (!Array.isArray(val) && val.toString) return BigInt(val.toString());
- // @ts-ignore
+ //@ts-ignore
throw new SchemaType.CastError("Long", val);
}
diff --git a/src/util/Permissions.ts b/src/util/Permissions.ts
index ae21e138..c7a41594 100644
--- a/src/util/Permissions.ts
+++ b/src/util/Permissions.ts
@@ -97,6 +97,16 @@ export class Permissions extends BitField {
return (checkAdmin && super.has(Permissions.FLAGS.ADMINISTRATOR)) || super.has(permission);
}
+ /**
+ * Checks whether the bitfield has a permission, or multiple permissions, but throws an Error if user fails to match auth criteria.
+ */
+ hasThrow(permission: PermissionResolvable, checkAdmin = true) {
+ if ((checkAdmin && super.has(Permissions.FLAGS.ADMINISTRATOR)) || super.has(permission)) {
+ return true;
+ }
+ throw new Error(`User doesn't fulfill the following permission criteria: ${permission}`);
+ }
+
static channelPermission(overwrites: ChannelPermissionOverwrite[], init?: bigint) {
// TODO: do not deny any permissions if admin
return overwrites.reduce((permission, overwrite) => {
@@ -121,7 +131,7 @@ export class Permissions extends BitField {
guild,
channel,
}: {
- user: { id: bigint; roles: bigint[] };
+ user: { id: string; roles: string[] };
guild: { roles: Role[] };
channel?: {
overwrites?: ChannelPermissionOverwrite[];
@@ -144,22 +154,24 @@ export class Permissions extends BitField {
}
export async function getPermission(
- user_id: bigint,
- guild_id: bigint,
- channel_id?: bigint,
- cache?: { channel?: ChannelDocument | null; member?: MemberDocument | null }
+ user_id: string,
+ guild_id: string,
+ channel_id?: string,
+ cache?: { channel?: ChannelDocument | null; member?: MemberDocument | null; guild?: GuildDocument | null }
) {
- var { channel, member } = cache || {};
+ var { channel, member, guild } = cache || {};
- const guild = await GuildModel.findOne({ id: guild_id }, { owner_id: true }).exec();
+ if (!guild) guild = await GuildModel.findOne({ id: guild_id }, { owner_id: true }).exec();
if (!guild) throw new Error("Guild not found");
if (guild.owner_id === user_id) return new Permissions(Permissions.FLAGS.ADMINISTRATOR);
- member = await MemberModel.findOne({ guild_id, id: user_id }, "roles").exec();
+ if (!member) member = await MemberModel.findOne({ guild_id, id: user_id }, "roles").exec();
if (!member) throw new Error("Member not found");
- var roles = await RoleModel.find({ guild_id, id: { $in: member.roles } }).exec();
- if (channel_id) {
+ var roles = await RoleModel.find({ guild_id, id: { $in: member.roles } })
+ .lean()
+ .exec();
+ if (channel_id && !channel) {
channel = await ChannelModel.findOne({ id: channel_id }, "permission_overwrites").exec();
}
diff --git a/src/util/Snowflake.ts b/src/util/Snowflake.ts
index 9e94bbd9..1ccae43c 100644
--- a/src/util/Snowflake.ts
+++ b/src/util/Snowflake.ts
@@ -87,7 +87,7 @@ export class Snowflake {
var worker = Snowflake.workerId << 17n;
var process = Snowflake.processId << 12n;
var increment = Snowflake.INCREMENT++;
- return time | worker | process | increment;
+ return (time | worker | process | increment).toString();
}
/**
diff --git a/src/util/checkToken.ts b/src/util/checkToken.ts
index b4635126..d5a128b4 100644
--- a/src/util/checkToken.ts
+++ b/src/util/checkToken.ts
@@ -4,7 +4,7 @@ import Config from "./Config";
export function checkToken(token: string): Promise<any> {
return new Promise((res, rej) => {
- jwt.verify(token, Config.getAll().api.security.jwtSecret, JWTOptions, (err, decoded: any) => {
+ jwt.verify(token, Config.getAll()?.api?.security?.jwtSecret, JWTOptions, (err, decoded: any) => {
if (err || !decoded) return rej("Invalid Token");
return res(decoded);
diff --git a/src/util/index.ts b/src/util/index.ts
new file mode 100644
index 00000000..7e8bca20
--- /dev/null
+++ b/src/util/index.ts
@@ -0,0 +1,8 @@
+export * from "./String";
+export * from "./BitField";
+export * from "./Intents";
+export * from "./MessageFlags";
+export * from "./Permissions";
+export * from "./Snowflake";
+export * from "./UserFlags";
+export * from "./toBigInt"
\ No newline at end of file
diff --git a/src/util/toBigInt.ts b/src/util/toBigInt.ts
new file mode 100644
index 00000000..d57c4568
--- /dev/null
+++ b/src/util/toBigInt.ts
@@ -0,0 +1,3 @@
+export default function toBigInt(string: String): BigInt {
+ return BigInt(string);
+}
|