diff --git a/src/util/Config.ts b/src/util/Config.ts
index f125bd18..dfa942e7 100644
--- a/src/util/Config.ts
+++ b/src/util/Config.ts
@@ -4,12 +4,12 @@ import db, { MongooseCache } from "./Database";
import { Snowflake } from "./Snowflake";
import crypto from "crypto";
-var Config = new MongooseCache(db.collection("config"), [], { onlyEvents: false });
+var Config = new MongooseCache(db.collection("config"), [], { onlyEvents: false, array: false });
export default {
init: async function init(defaultOpts: any = DefaultOptions) {
await Config.init();
- return this.set(Config.data.merge(defaultOpts));
+ return this.set((Config.data || {}).merge(defaultOpts));
},
get: function get() {
return <DefaultOptions>Config.data;
@@ -88,6 +88,7 @@ export interface DefaultOptions {
sitekey: string | null;
secret: string | null;
};
+ ipdataApiKey: string | null;
};
login: {
requireCaptcha: boolean;
@@ -107,6 +108,7 @@ export interface DefaultOptions {
requireInvite: boolean;
allowNewRegistration: boolean;
allowMultipleAccounts: boolean;
+ blockProxies: boolean;
password: {
minLength: number;
minNumbers: number;
@@ -176,6 +178,7 @@ export const DefaultOptions: DefaultOptions = {
sitekey: null,
secret: null,
},
+ ipdataApiKey: "eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9",
},
login: {
requireCaptcha: false,
@@ -196,6 +199,7 @@ export const DefaultOptions: DefaultOptions = {
requireCaptcha: true,
allowNewRegistration: true,
allowMultipleAccounts: true,
+ blockProxies: true,
password: {
minLength: 8,
minNumbers: 2,
diff --git a/src/util/Database.ts b/src/util/Database.ts
index e5323ed6..8c6847a8 100644
--- a/src/util/Database.ts
+++ b/src/util/Database.ts
@@ -2,11 +2,10 @@ import "./MongoBigInt";
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";
+import { URL } from "url";
-// TODO: auto throw error if findOne doesn't find anything
-console.log(`[DB] connect: ${uri}`);
+const url = new URL(uri.replace("mongodb://", "http://"));
const connection = mongoose.createConnection(uri, {
autoIndex: true,
@@ -14,6 +13,7 @@ const connection = mongoose.createConnection(uri, {
useUnifiedTopology: true,
useFindAndModify: false,
});
+console.log(`[Database] connect: mongodb://${url.username}@${url.host}${url.pathname}${url.search}`);
export default <Connection>connection;
@@ -47,29 +47,38 @@ export interface MongooseCache {
export class MongooseCache extends EventEmitter {
public stream: ChangeStream;
public data: any;
+ public initalizing?: Promise<void>;
constructor(
public collection: Collection,
public pipeline: Array<Record<string, unknown>>,
public opts: {
onlyEvents: boolean;
+ array?: boolean;
}
) {
super();
+ if (this.opts.array == null) this.opts.array = true;
}
- init = async () => {
- // @ts-ignore
- this.stream = this.collection.watch(this.pipeline, { fullDocument: "updateLookup" });
+ 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);
+ 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();
- this.data = arr.length ? arr[0] : arr;
- }
+ 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) => {
@@ -91,23 +100,34 @@ export class MongooseCache extends EventEmitter {
change = (doc: ChangeEvent) => {
try {
- // @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":
+ 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();
@@ -120,6 +140,7 @@ export class MongooseCache extends EventEmitter {
};
destroy = () => {
+ this.data = null;
this.stream?.off("change", this.change);
this.emit("close");
diff --git a/src/util/checkToken.ts b/src/util/checkToken.ts
index 73ffb670..e021a406 100644
--- a/src/util/checkToken.ts
+++ b/src/util/checkToken.ts
@@ -4,16 +4,21 @@ import { UserModel } from "../models";
export function checkToken(token: string, jwtSecret: string): Promise<any> {
return new Promise((res, rej) => {
+ token = token.replace("Bot ", ""); // TODO: proper bot support
jwt.verify(token, jwtSecret, JWTOptions, async (err, decoded: any) => {
if (err || !decoded) return rej("Invalid Token");
- const user = await UserModel.findOne({ id: decoded.id }, { "user_data.valid_tokens_since": true }).exec();
+ const user = await UserModel.findOne(
+ { id: decoded.id },
+ { "user_data.valid_tokens_since": true, bot: true }
+ ).exec();
if (!user) return rej("Invalid Token");
- if (decoded.iat * 1000 < user.user_data.valid_tokens_since.getTime()) return rej("Invalid Token");
+ // we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds
+ if (decoded.iat * 1000 < user.user_data.valid_tokens_since.setSeconds(0, 0)) return rej("Invalid Token");
if (user.disabled) return rej("User disabled");
if (user.deleted) return rej("User not found");
- return res(decoded);
+ return res({ decoded, user });
});
});
}
|