diff --git a/api/package-lock.json b/api/package-lock.json
index bff4e940..1b673d4a 100644
--- a/api/package-lock.json
+++ b/api/package-lock.json
@@ -28,8 +28,8 @@
"i18next-http-middleware": "^3.1.3",
"i18next-node-fs-backend": "^2.1.3",
"jsonwebtoken": "^8.5.1",
- "lambert-server": "^1.2.8",
- "missing-native-js-functions": "^1.2.10",
+ "lambert-server": "^1.2.10",
+ "missing-native-js-functions": "^1.2.11",
"mongoose": "^5.12.3",
"mongoose-autopopulate": "^0.12.3",
"mongoose-long": "^0.3.2",
@@ -74,9 +74,11 @@
"dot-prop": "^6.0.1",
"env-paths": "^2.2.1",
"jsonwebtoken": "^8.5.1",
- "missing-native-js-functions": "^1.2.10",
+ "lambert-server": "^1.2.10",
+ "missing-native-js-functions": "^1.2.11",
"node-fetch": "^2.6.1",
"patch-package": "^6.4.7",
+ "pg": "^8.7.1",
"reflect-metadata": "^0.1.13",
"sqlite3": "^5.0.2",
"typeorm": "^0.2.37",
@@ -6927,16 +6929,16 @@
}
},
"node_modules/lambert-server": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.2.8.tgz",
- "integrity": "sha512-vi/Ku/QudY+WIdGO9bc0qLfVhfuJFWXk1+etesPW1vW29sPbmevLL6IwfvCtw+/MyzRAJLOyCBfQ310a68+2QQ==",
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.2.10.tgz",
+ "integrity": "sha512-BHGPmpUrRklFJHPu0vAA8NBewtEd4IX80FRpV4nX9z8kHTUYHqnYHoBeUEWoUmxAeFQvQae1Axk5RQXRQk4VNw==",
"dependencies": {
"body-parser": "^1.19.0",
"chalk": "^4.1.1",
"express": "^4.17.1",
"express-async-errors": "^3.1.1",
"helmet": "^4.4.1",
- "missing-native-js-functions": "^1.1.8"
+ "missing-native-js-functions": "^1.2.11"
}
},
"node_modules/lazystream": {
@@ -7356,9 +7358,9 @@
}
},
"node_modules/missing-native-js-functions": {
- "version": "1.2.10",
- "resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.2.10.tgz",
- "integrity": "sha512-sq+oAw/C3OtUyKopLNOf/+U85YNx7db6fy5nVfGVKlGdcV8tX24GjOSkcZeCAnAIjMEnlQBWTr17JXa3OJj22g=="
+ "version": "1.2.11",
+ "resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.2.11.tgz",
+ "integrity": "sha512-U97IscNBL4Wg9adYjEBT46Hb0Ld5dPT8vbdwFX+TNzGrFQCc4WqoGAZouaLNFwUqxzzHZ9DVg59unwnQyeIIQg=="
},
"node_modules/mixin-deep": {
"version": "1.3.2",
@@ -12476,9 +12478,11 @@
"env-paths": "^2.2.1",
"jest": "^27.0.6",
"jsonwebtoken": "^8.5.1",
- "missing-native-js-functions": "^1.2.10",
+ "lambert-server": "^1.2.10",
+ "missing-native-js-functions": "^1.2.11",
"node-fetch": "^2.6.1",
"patch-package": "^6.4.7",
+ "pg": "^8.7.1",
"reflect-metadata": "^0.1.13",
"sqlite3": "^5.0.2",
"typeorm": "^0.2.37",
@@ -17639,16 +17643,16 @@
}
},
"lambert-server": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.2.8.tgz",
- "integrity": "sha512-vi/Ku/QudY+WIdGO9bc0qLfVhfuJFWXk1+etesPW1vW29sPbmevLL6IwfvCtw+/MyzRAJLOyCBfQ310a68+2QQ==",
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.2.10.tgz",
+ "integrity": "sha512-BHGPmpUrRklFJHPu0vAA8NBewtEd4IX80FRpV4nX9z8kHTUYHqnYHoBeUEWoUmxAeFQvQae1Axk5RQXRQk4VNw==",
"requires": {
"body-parser": "^1.19.0",
"chalk": "^4.1.1",
"express": "^4.17.1",
"express-async-errors": "^3.1.1",
"helmet": "^4.4.1",
- "missing-native-js-functions": "^1.1.8"
+ "missing-native-js-functions": "^1.2.11"
}
},
"lazystream": {
@@ -18009,9 +18013,9 @@
}
},
"missing-native-js-functions": {
- "version": "1.2.10",
- "resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.2.10.tgz",
- "integrity": "sha512-sq+oAw/C3OtUyKopLNOf/+U85YNx7db6fy5nVfGVKlGdcV8tX24GjOSkcZeCAnAIjMEnlQBWTr17JXa3OJj22g=="
+ "version": "1.2.11",
+ "resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.2.11.tgz",
+ "integrity": "sha512-U97IscNBL4Wg9adYjEBT46Hb0Ld5dPT8vbdwFX+TNzGrFQCc4WqoGAZouaLNFwUqxzzHZ9DVg59unwnQyeIIQg=="
},
"mixin-deep": {
"version": "1.3.2",
diff --git a/api/package.json b/api/package.json
index 1310d577..eef2d069 100644
--- a/api/package.json
+++ b/api/package.json
@@ -74,8 +74,8 @@
"i18next-http-middleware": "^3.1.3",
"i18next-node-fs-backend": "^2.1.3",
"jsonwebtoken": "^8.5.1",
- "lambert-server": "^1.2.8",
- "missing-native-js-functions": "^1.2.10",
+ "lambert-server": "^1.2.10",
+ "missing-native-js-functions": "^1.2.11",
"mongoose": "^5.12.3",
"mongoose-autopopulate": "^0.12.3",
"mongoose-long": "^0.3.2",
diff --git a/api/src/middlewares/ErrorHandler.ts b/api/src/middlewares/ErrorHandler.ts
index 8e2cd923..0ed37bb4 100644
--- a/api/src/middlewares/ErrorHandler.ts
+++ b/api/src/middlewares/ErrorHandler.ts
@@ -4,7 +4,7 @@ import { FieldError } from "../util/instanceOf";
// TODO: update with new body/typorm validation
export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) {
- if (!error) next();
+ if (!error) return next();
try {
let code = 400;
@@ -18,7 +18,6 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne
message = error.message;
errors = error.errors;
} else {
- console.error(error);
if (req.server?.options?.production) {
message = "Internal Server Error";
}
@@ -27,7 +26,7 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne
if (httpcode > 511) httpcode = 400;
- console.error(`[Error] ${code} ${req.url} ${message}`, errors || error);
+ console.error(`[Error] ${code} ${req.url}`, errors || error, "body:", req.body);
res.status(httpcode).json({ code: code, message, errors });
} catch (error) {
diff --git a/api/src/middlewares/RateLimit.ts b/api/src/middlewares/RateLimit.ts
index 9601dad3..e0cf103a 100644
--- a/api/src/middlewares/RateLimit.ts
+++ b/api/src/middlewares/RateLimit.ts
@@ -1,6 +1,6 @@
-// @ts-nocheck
-import { db, Bucket, Config, listenEvent, emitEvent } from "@fosscord/util";
+import { Config, listenEvent, emitEvent, RateLimit } from "@fosscord/util";
import { NextFunction, Request, Response, Router } from "express";
+import { LessThan } from "typeorm";
import { getIpAdress } from "../util/ipAddress";
import { API_PREFIX_TRAILING_SLASH } from "./Authentication";
@@ -18,10 +18,10 @@ TODO: different for methods (GET/POST)
*/
-var Cache = new Map<string, Bucket>();
-const EventRateLimit = "ratelimit";
+var Cache = new Map<string, RateLimit>();
+const EventRateLimit = "RATELIMIT";
-export default function RateLimit(opts: {
+export default function rateLimit(opts: {
bucket?: string;
window: number;
count: number;
@@ -36,15 +36,15 @@ export default function RateLimit(opts: {
}): any {
return async (req: Request, res: Response, next: NextFunction): Promise<any> => {
const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, "");
- var user_id = getIpAdress(req);
- if (!opts.onlyIp && req.user_id) user_id = req.user_id;
+ var executor_id = getIpAdress(req);
+ if (!opts.onlyIp && req.user_id) executor_id = req.user_id;
var max_hits = opts.count;
if (opts.bot && req.user_bot) max_hits = opts.bot;
if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) max_hits = opts.GET;
else if (opts.MODIFY && ["POST", "DELETE", "PATCH", "PUT"].includes(req.method)) max_hits = opts.MODIFY;
- const offender = Cache.get(user_id + bucket_id) as Bucket | null;
+ const offender = Cache.get(executor_id + bucket_id);
if (offender && offender.blocked) {
const reset = offender.expires_at.getTime();
@@ -72,12 +72,12 @@ export default function RateLimit(opts: {
offender.expires_at = new Date(Date.now() + opts.window * 1000);
offender.blocked = false;
// mongodb ttl didn't update yet -> manually update/delete
- db.collection("ratelimits").update({ id: bucket_id, user_id }, { $set: offender });
- Cache.delete(user_id + bucket_id);
+ RateLimit.delete({ id: bucket_id, executor_id });
+ Cache.delete(executor_id + bucket_id);
}
}
next();
- const hitRouteOpts = { bucket_id, user_id, max_hits, window: opts.window };
+ const hitRouteOpts = { bucket_id, executor_id, max_hits, window: opts.window };
if (opts.error || opts.success) {
res.once("finish", () => {
@@ -97,69 +97,74 @@ export default function RateLimit(opts: {
export async function initRateLimits(app: Router) {
const { routes, global, ip, error } = Config.get().limits.rate;
await listenEvent(EventRateLimit, (event) => {
- Cache.set(event.channel_id, event.data);
+ Cache.set(event.channel_id as string, event.data);
event.acknowledge?.();
});
+ await RateLimit.delete({ expires_at: LessThan(new Date()) }); // clean up if not already deleted
+ const limits = await RateLimit.find({ blocked: true });
+ limits.forEach((limit) => {
+ Cache.set(limit.executor_id, limit);
+ });
setInterval(() => {
Cache.forEach((x, key) => {
- if (Date.now() > x.expires_at) Cache.delete(key);
+ if (new Date() > x.expires_at) {
+ Cache.delete(key);
+ RateLimit.delete({ executor_id: key });
+ }
});
}, 1000 * 60 * 10);
app.use(
- RateLimit({
+ rateLimit({
bucket: "global",
onlyIp: true,
...ip
})
);
- app.use(RateLimit({ bucket: "global", ...global }));
+ app.use(rateLimit({ bucket: "global", ...global }));
app.use(
- RateLimit({
+ rateLimit({
bucket: "error",
error: true,
onlyIp: true,
...error
})
);
- app.use("/guilds/:id", RateLimit(routes.guild));
- app.use("/webhooks/:id", RateLimit(routes.webhook));
- app.use("/channels/:id", RateLimit(routes.channel));
- app.use("/auth/login", RateLimit(routes.auth.login));
- app.use("/auth/register", RateLimit({ onlyIp: true, success: true, ...routes.auth.register }));
+ app.use("/guilds/:id", rateLimit(routes.guild));
+ app.use("/webhooks/:id", rateLimit(routes.webhook));
+ app.use("/channels/:id", rateLimit(routes.channel));
+ app.use("/auth/login", rateLimit(routes.auth.login));
+ app.use("/auth/register", rateLimit({ onlyIp: true, success: true, ...routes.auth.register }));
}
-async function hitRoute(opts: { user_id: string; bucket_id: string; max_hits: number; window: number }) {
- const filter = { id: opts.bucket_id, user_id: opts.user_id };
- const { value } = await db.collection("ratelimits").findOneOrFailAndUpdate(
- filter,
- {
- $setOnInsert: {
- id: opts.bucket_id,
- user_id: opts.user_id,
- expires_at: new Date(Date.now() + opts.window * 1000)
- },
- $inc: {
- hits: 1
- }
- // Conditionally update blocked doesn't work
- },
- { upsert: true, returnDocument: "before" }
- );
- if (!value) return;
- const updateBlock = !value.blocked && value.hits >= opts.max_hits;
+async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number }) {
+ var ratelimit = await RateLimit.findOne({ id: opts.bucket_id, executor_id: opts.executor_id });
+ if (!ratelimit) {
+ ratelimit = new RateLimit({
+ id: opts.bucket_id,
+ executor_id: opts.executor_id,
+ expires_at: new Date(Date.now() + opts.window * 1000),
+ hits: 0,
+ blocked: false
+ });
+ }
+
+ ratelimit.hits++;
+
+ const updateBlock = !ratelimit.blocked && ratelimit.hits >= opts.max_hits;
if (updateBlock) {
- value.blocked = true;
- Cache.set(opts.user_id + opts.bucket_id, value);
+ ratelimit.blocked = true;
+ Cache.set(opts.executor_id + opts.bucket_id, ratelimit);
await emitEvent({
channel_id: EventRateLimit,
event: EventRateLimit,
- data: value
+ data: ratelimit
});
- await db.collection("ratelimits").update(filter, { $set: { blocked: true } });
} else {
- Cache.delete(opts.user_id);
+ Cache.delete(opts.executor_id);
}
+
+ await ratelimit.save();
}
diff --git a/api/src/routes/auth/login.ts b/api/src/routes/auth/login.ts
index c0acad4e..7fd0f870 100644
--- a/api/src/routes/auth/login.ts
+++ b/api/src/routes/auth/login.ts
@@ -21,7 +21,7 @@ router.post(
async (req: Request, res: Response) => {
const { login, password, captcha_key, undelete } = req.body;
const email = adjustEmail(login);
- console.log(req.body, email);
+ console.log("login", email);
const config = Config.get();
diff --git a/api/src/routes/auth/register.ts b/api/src/routes/auth/register.ts
index 5ad6d6a4..b0d8c9bd 100644
--- a/api/src/routes/auth/register.ts
+++ b/api/src/routes/auth/register.ts
@@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
-import { trimSpecial, User, Snowflake, Config } from "@fosscord/util";
+import { trimSpecial, User, Snowflake, Config, defaultSettings } from "@fosscord/util";
import bcrypt from "bcrypt";
import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../util/instanceOf";
import "missing-native-js-functions";
@@ -182,17 +182,29 @@ router.post(
// if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false
const user = await new User({
+ created_at: new Date(),
username: adjusted_username,
discriminator,
+ bot: false,
+ system: false,
+ desktop: false,
+ mobile: false,
premium: true,
premium_type: 2,
+ bio: "",
+ mfa_enabled: false,
+ verified: false,
+ disabled: false,
+ deleted: false,
email: adjusted_email,
nsfw_allowed: true, // TODO: depending on age
- guilds: [],
+ public_flags: "0",
+ flags: "0", // TODO: generate
data: {
hash: adjusted_password,
valid_tokens_since: new Date()
- }
+ },
+ settings: defaultSettings
}).save();
return res.json({ token: await generateToken(user.id) });
diff --git a/api/src/schema/Message.ts b/api/src/schema/Message.ts
index f9bfcc67..bf10c037 100644
--- a/api/src/schema/Message.ts
+++ b/api/src/schema/Message.ts
@@ -11,7 +11,7 @@ export const MessageCreateSchema = {
$content: new Length(String, 0, 2000),
$nonce: String,
$tts: Boolean,
- $flags: BigInt,
+ $flags: String,
$embed: {
$title: new Length(String, 0, 256), //title of embed
$type: String, // type of embed (always "rich" for webhook embeds)
@@ -69,7 +69,7 @@ export interface MessageCreateSchema {
content?: string;
nonce?: string;
tts?: boolean;
- flags?: bigint;
+ flags?: string;
embed?: Embed & { timestamp?: string };
allowed_mentions?: {
parse?: string[];
diff --git a/bundle/database.db b/bundle/database.db
new file mode 100644
index 00000000..9572c45e
--- /dev/null
+++ b/bundle/database.db
Binary files differdiff --git a/bundle/package-lock.json b/bundle/package-lock.json
index 379b1c18..9fcb5490 100644
--- a/bundle/package-lock.json
+++ b/bundle/package-lock.json
@@ -63,19 +63,21 @@
"i18next-http-middleware": "^3.1.3",
"i18next-node-fs-backend": "^2.1.3",
"jsonwebtoken": "^8.5.1",
- "lambert-server": "^1.2.8",
- "missing-native-js-functions": "^1.2.10",
+ "lambert-server": "^1.2.10",
+ "missing-native-js-functions": "^1.2.11",
"mongoose": "^5.12.3",
"mongoose-autopopulate": "^0.12.3",
"mongoose-long": "^0.3.2",
"multer": "^1.4.2",
- "node-fetch": "^2.6.1"
+ "node-fetch": "^2.6.1",
+ "typeorm": "^0.2.37"
},
"devDependencies": {
"@types/amqplib": "^0.8.1",
"@types/bcrypt": "^5.0.0",
"@types/express": "^4.17.9",
"@types/i18next-node-fs-backend": "^2.1.0",
+ "@types/jest": "^27.0.1",
"@types/jsonwebtoken": "^8.5.0",
"@types/mongodb": "^3.6.9",
"@types/mongoose": "^5.10.5",
@@ -111,11 +113,13 @@
"file-type": "^16.5.0",
"fs-extra": "^10.0.0",
"image-size": "^1.0.0",
+ "jest": "^27.0.6",
"lambert-db": "^1.2.3",
"lambert-server": "^1.2.8",
"missing-native-js-functions": "^1.2.10",
"multer": "^1.4.2",
"node-fetch": "^2.6.1",
+ "supertest": "^6.1.6",
"typescript": "^4.1.2",
"uuid": "^8.3.2"
},
@@ -173,26 +177,30 @@
"hasInstallScript": true,
"license": "GPLV3",
"dependencies": {
- "ajv": "^8.5.0",
+ "ajv": "^8.6.2",
"amqplib": "^0.8.0",
+ "class-validator": "^0.13.1",
"dot-prop": "^6.0.1",
"env-paths": "^2.2.1",
"jsonwebtoken": "^8.5.1",
- "missing-native-js-functions": "^1.2.10",
- "mongodb": "^3.6.9",
- "mongoose": "^5.13.7",
- "mongoose-autopopulate": "^0.12.3",
+ "lambert-server": "^1.2.10",
+ "missing-native-js-functions": "^1.2.11",
"node-fetch": "^2.6.1",
- "typescript": "^4.1.3"
+ "patch-package": "^6.4.7",
+ "pg": "^8.7.1",
+ "reflect-metadata": "^0.1.13",
+ "sqlite3": "^5.0.2",
+ "typeorm": "^0.2.37",
+ "typescript": "^4.3.5",
+ "typescript-json-schema": "^0.50.1"
},
"devDependencies": {
"@types/amqplib": "^0.8.1",
"@types/jsonwebtoken": "^8.5.0",
- "@types/mongodb": "^3.6.9",
"@types/mongoose-autopopulate": "^0.10.1",
- "@types/mongoose-lean-virtuals": "^0.5.1",
"@types/node": "^14.17.9",
- "@types/node-fetch": "^2.5.12"
+ "@types/node-fetch": "^2.5.12",
+ "jest": "^27.0.6"
}
},
"node_modules/@babel/runtime": {
@@ -1868,6 +1876,7 @@
"@types/bcrypt": "^5.0.0",
"@types/express": "^4.17.9",
"@types/i18next-node-fs-backend": "^2.1.0",
+ "@types/jest": "^27.0.1",
"@types/jsonwebtoken": "^8.5.0",
"@types/mongodb": "^3.6.9",
"@types/mongoose": "^5.10.5",
@@ -1899,8 +1908,8 @@
"image-size": "^1.0.0",
"jest": "^26.6.3",
"jsonwebtoken": "^8.5.1",
- "lambert-server": "^1.2.8",
- "missing-native-js-functions": "^1.2.10",
+ "lambert-server": "^1.2.10",
+ "missing-native-js-functions": "^1.2.11",
"mongoose": "^5.12.3",
"mongoose-autopopulate": "^0.12.3",
"mongoose-long": "^0.3.2",
@@ -1909,6 +1918,7 @@
"saslprep": "^1.0.3",
"ts-node": "^9.1.1",
"ts-node-dev": "^1.1.6",
+ "typeorm": "^0.2.37",
"typescript": "^4.1.2"
}
},
@@ -1940,11 +1950,13 @@
"file-type": "^16.5.0",
"fs-extra": "^10.0.0",
"image-size": "^1.0.0",
+ "jest": "^27.0.6",
"lambert-db": "^1.2.3",
"lambert-server": "^1.2.8",
"missing-native-js-functions": "^1.2.10",
"multer": "^1.4.2",
"node-fetch": "^2.6.1",
+ "supertest": "^6.1.6",
"typescript": "^4.1.2",
"uuid": "^8.3.2"
}
@@ -1981,22 +1993,26 @@
"requires": {
"@types/amqplib": "^0.8.1",
"@types/jsonwebtoken": "^8.5.0",
- "@types/mongodb": "^3.6.9",
"@types/mongoose-autopopulate": "^0.10.1",
- "@types/mongoose-lean-virtuals": "^0.5.1",
"@types/node": "^14.17.9",
"@types/node-fetch": "^2.5.12",
- "ajv": "^8.5.0",
+ "ajv": "^8.6.2",
"amqplib": "^0.8.0",
+ "class-validator": "^0.13.1",
"dot-prop": "^6.0.1",
"env-paths": "^2.2.1",
+ "jest": "^27.0.6",
"jsonwebtoken": "^8.5.1",
- "missing-native-js-functions": "^1.2.10",
- "mongodb": "^3.6.9",
- "mongoose": "^5.13.7",
- "mongoose-autopopulate": "^0.12.3",
+ "lambert-server": "^1.2.10",
+ "missing-native-js-functions": "^1.2.11",
"node-fetch": "^2.6.1",
- "typescript": "^4.1.3"
+ "patch-package": "^6.4.7",
+ "pg": "^8.7.1",
+ "reflect-metadata": "^0.1.13",
+ "sqlite3": "^5.0.2",
+ "typeorm": "^0.2.37",
+ "typescript": "^4.3.5",
+ "typescript-json-schema": "^0.50.1"
}
},
"@types/amqplib": {
diff --git a/bundle/package.json b/bundle/package.json
index 38377d15..71d7efea 100644
--- a/bundle/package.json
+++ b/bundle/package.json
@@ -5,8 +5,9 @@
"main": "src/start.js",
"scripts": {
"preinstall": "cd ../util && npm i && cd ../api && npm i && cd ../cdn && npm i && cd ../gateway && npm i",
- "build": "npm run build:api && npm run build:cdn && npm run build:gateway && npm run build:bundle",
+ "build": "npm run build:util && npm run build:api && npm run build:cdn && npm run build:gateway && npm run build:bundle",
"build:bundle": "npx tsc -b .",
+ "build:util": "cd ../util/ && npm run build",
"build:api": "cd ../api/ && npm run build",
"build:cdn": "cd ../cdn/ && npm run build",
"build:gateway": "cd ../gateway/ && npm run build",
diff --git a/bundle/src/Database.ts b/bundle/src/Database.ts
deleted file mode 100644
index 12febc1c..00000000
--- a/bundle/src/Database.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import fs from "fs";
-import { MongoMemoryServer } from "mongodb-memory-server";
-import path from "path";
-import exitHook from "async-exit-hook";
-
-if (process.arch == "ia32") {
- Object.defineProperty(process, "arch", {
- value: "x64",
- });
-}
-
-export async function setupDatabase() {
- if (process.env.MONGO_URL) return; // exit because the user provides his own mongodb
- const dbPath = path.join(__dirname, "..", "..", "db");
- const dbName = "fosscord";
- const storageEngine = "wiredTiger";
- const port = 27020;
- const ip = "127.0.0.1";
- var mongod: MongoMemoryServer;
- fs.mkdirSync(dbPath, { recursive: true });
-
- exitHook((callback: any) => {
- (async () => {
- console.log(`Stopping MongoDB ...`);
- await mongod.stop();
- console.log(`Stopped MongoDB`);
- callback();
- })();
- });
-
- console.log(`[Database] starting ...`);
- mongod = new MongoMemoryServer({
- instance: {
- port,
- ip,
- dbName,
- dbPath,
- storageEngine,
- auth: false, // by default `mongod` is started with '--noauth', start `mongod` with '--auth'
- },
- });
- await mongod.start();
- process.env.MONGO_URL = mongod.getUri(dbName);
-}
diff --git a/bundle/src/Server.ts b/bundle/src/Server.ts
index e0586601..56c82cd1 100644
--- a/bundle/src/Server.ts
+++ b/bundle/src/Server.ts
@@ -6,7 +6,7 @@ import { FosscordServer as APIServer } from "@fosscord/api";
import { Server as GatewayServer } from "@fosscord/gateway";
import { CDNServer } from "@fosscord/cdn/";
import express from "express";
-import { Config } from "@fosscord/util";
+import { Config, initDatabase } from "@fosscord/util";
const app = express();
const server = http.createServer();
@@ -22,6 +22,8 @@ const cdn = new CDNServer({ server, port, production, app });
const gateway = new GatewayServer({ server, port, production });
async function main() {
+ await initDatabase();
+ await Config.init();
await Config.set({
cdn: {
endpointClient: "${location.host}",
diff --git a/bundle/src/start.ts b/bundle/src/start.ts
index 323995ae..843e3812 100644
--- a/bundle/src/start.ts
+++ b/bundle/src/start.ts
@@ -1,7 +1,6 @@
// process.env.MONGOMS_DEBUG = "true";
import cluster from "cluster";
import os from "os";
-import { setupDatabase } from "./Database";
import { initStats } from "./stats";
// TODO: add tcp socket event transmission
@@ -12,7 +11,6 @@ if (cluster.isMaster && !process.env.masterStarted) {
(async () => {
initStats();
- await setupDatabase();
if (cores === 1) {
require("./Server.js");
diff --git a/cdn/src/Server.ts b/cdn/src/Server.ts
index 522e11c2..f4a6b576 100644
--- a/cdn/src/Server.ts
+++ b/cdn/src/Server.ts
@@ -1,5 +1,5 @@
import { Server, ServerOptions } from "lambert-server";
-import { Config, db } from "@fosscord/util";
+import { Config, initDatabase } from "@fosscord/util";
import path from "path";
import avatarsRoute from "./routes/avatars";
@@ -13,8 +13,7 @@ export class CDNServer extends Server {
}
async start() {
- // @ts-ignore
- await (db as Promise<Connection>);
+ await initDatabase();
await Config.init();
this.app.use((req, res, next) => {
res.set("Access-Control-Allow-Origin", "*");
diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts
index 9ca4b1e4..e6a93824 100644
--- a/util/src/entities/Guild.ts
+++ b/util/src/entities/Guild.ts
@@ -158,7 +158,7 @@ export class Guild extends BaseClass {
vanity_url_code?: string;
@JoinColumn({ name: "vanity_url_code" })
- @OneToOne(() => Invite, (invite: Invite) => invite.code)
+ @ManyToOne(() => Invite)
vanity_url?: Invite;
@Column({ nullable: true })
diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts
index 0c41a2eb..43d0f9d0 100644
--- a/util/src/entities/Message.ts
+++ b/util/src/entities/Message.ts
@@ -148,8 +148,8 @@ export class Message extends BaseClass {
party_id: string;
};
- @Column({ type: "bigint", nullable: true })
- flags?: bigint;
+ @Column({ nullable: true })
+ flags?: string;
@RelationId((message: Message) => message.stickers)
sticker_ids: string[];
diff --git a/util/src/entities/RateLimit.ts b/util/src/entities/RateLimit.ts
index 3ac35df3..49af0416 100644
--- a/util/src/entities/RateLimit.ts
+++ b/util/src/entities/RateLimit.ts
@@ -7,12 +7,8 @@ export class RateLimit extends BaseClass {
@Column()
id: "global" | "error" | string; // channel_239842397 | guild_238927349823 | webhook_238923423498
- @RelationId((rate_limit: RateLimit) => rate_limit.user)
- user_id: string;
-
- @JoinColumn({ name: "user_id" })
- @ManyToOne(() => User, (user) => user.id)
- user: User;
+ @Column() // no relation as it also
+ executor_id: string;
@Column()
hits: number;
diff --git a/util/src/entities/Role.ts b/util/src/entities/Role.ts
index 7c6ce64e..ddae7e40 100644
--- a/util/src/entities/Role.ts
+++ b/util/src/entities/Role.ts
@@ -1,4 +1,5 @@
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
+
import { BaseClass } from "./BaseClass";
import { Guild } from "./Guild";
diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts
index c5f870fa..73afba67 100644
--- a/util/src/entities/User.ts
+++ b/util/src/entities/User.ts
@@ -49,7 +49,7 @@ export class User extends BaseClass {
avatar?: string; // hash of the user avatar
@Column({ nullable: true })
- accent_color?: number = 0; // banner color of user
+ accent_color?: number; // banner color of user
@Column({ nullable: true })
banner?: string; // hash of the user banner
@@ -58,52 +58,52 @@ export class User extends BaseClass {
phone?: string; // phone number of the user
@Column()
- desktop: boolean = false; // if the user has desktop app installed
+ desktop: boolean; // if the user has desktop app installed
@Column()
- mobile: boolean = false; // if the user has mobile app installed
+ mobile: boolean; // if the user has mobile app installed
@Column()
- premium: boolean = false; // if user bought nitro
+ premium: boolean; // if user bought nitro
@Column()
- premium_type: number = 0; // nitro level
+ premium_type: number; // nitro level
@Column()
- bot: boolean = false; // if user is bot
+ bot: boolean; // if user is bot
@Column()
- bio: string = ""; // short description of the user (max 190 chars -> should be configurable)
+ bio: string; // short description of the user (max 190 chars -> should be configurable)
@Column()
- system: boolean = false; // shouldn't be used, the api sents this field type true, if the generated message comes from a system generated author
+ 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 = false; // if the user is older than 18 (resp. Config)
+ nsfw_allowed: boolean; // if the user is older than 18 (resp. Config)
@Column()
- mfa_enabled: boolean = false; // if multi factor authentication is enabled
+ mfa_enabled: boolean; // if multi factor authentication is enabled
@Column()
created_at: Date = new Date(); // registration date
@Column()
- verified: boolean = false; // if the user is offically verified
+ verified: boolean; // if the user is offically verified
@Column()
- disabled: boolean = false; // if the account is disabled
+ disabled: boolean; // if the account is disabled
@Column()
- deleted: boolean = false; // if the user was deleted
+ deleted: boolean; // if the user was deleted
@Column({ nullable: true })
email?: string; // email of the user
- @Column({ type: "bigint" })
- flags: bigint = BigInt(0); // UserFlags
+ @Column()
+ flags: string; // UserFlags
- @Column({ type: "bigint" })
- public_flags: bigint = BigInt(0);
+ @Column()
+ public_flags: string;
@RelationId((user: User) => user.relationships)
relationship_ids: string[]; // array of guild ids the user is part of
@@ -123,13 +123,13 @@ export class User extends BaseClass {
data: {
valid_tokens_since: Date; // all tokens with a previous issue date are invalid
hash?: string; // hash of the password, salt is saved in password (bcrypt)
- } = { valid_tokens_since: new Date() };
+ };
@Column({ type: "simple-array" })
fingerprints: string[] = []; // array of fingerprints -> used to prevent multiple accounts
@Column({ type: "simple-json" })
- settings: UserSettings = defaultSettings;
+ settings: UserSettings;
static async getPublicUser(user_id: string, opts?: FindOneOptions<User>) {
const user = await User.findOne(user_id, {
diff --git a/util/src/entities/index.ts b/util/src/entities/index.ts
index b9e361c1..e0246a10 100644
--- a/util/src/entities/index.ts
+++ b/util/src/entities/index.ts
@@ -14,6 +14,7 @@ export * from "./RateLimit";
export * from "./ReadState";
export * from "./Relationship";
export * from "./Role";
+export * from "./Sticker";
export * from "./Team";
export * from "./TeamMember";
export * from "./Template";
diff --git a/util/src/interfaces/Event.ts b/util/src/interfaces/Event.ts
index c3516c4c..0de55f71 100644
--- a/util/src/interfaces/Event.ts
+++ b/util/src/interfaces/Event.ts
@@ -515,4 +515,4 @@ export type EVENT =
| "RELATIONSHIP_REMOVE"
| CUSTOMEVENTS;
-export type CUSTOMEVENTS = "INVALIDATED";
+export type CUSTOMEVENTS = "INVALIDATED" | "RATELIMIT";
diff --git a/util/src/util/Config.ts b/util/src/util/Config.ts
index f8574f38..f16921bd 100644
--- a/util/src/util/Config.ts
+++ b/util/src/util/Config.ts
@@ -6,6 +6,7 @@ var config: ConfigEntity;
export const Config = {
init: async function init() {
+ if (config) return config;
config = new ConfigEntity({}, { id: "0" });
return this.set((config.value || {}).merge(DefaultConfigOptions));
},
@@ -13,7 +14,8 @@ export const Config = {
return config.value as ConfigValue;
},
set: function set(val: any) {
- config.value = val.merge(config.value);
+ if (!config) return;
+ config.value = val.merge(config?.value || {});
return config.save();
},
};
diff --git a/util/src/util/Database.ts b/util/src/util/Database.ts
index f49fb04c..c22d8abd 100644
--- a/util/src/util/Database.ts
+++ b/util/src/util/Database.ts
@@ -1,5 +1,5 @@
import "reflect-metadata";
-import { Connection, createConnection } from "typeorm";
+import { Connection, createConnection, ValueTransformer } from "typeorm";
import * as Models from "../entities";
// UUID extension option is only supported with postgres
@@ -14,10 +14,10 @@ export function initDatabase() {
console.log("[Database] connecting ...");
// @ts-ignore
promise = createConnection({
- // type: "sqlite",
- // database: "database.db",
- type: "postgres",
- url: "postgres://fosscord:wb94SmuURM2Syv&@localhost/fosscord",
+ type: "sqlite",
+ database: "database.db",
+ // type: "postgres",
+ // url: "postgres://fosscord:wb94SmuURM2Syv&@localhost/fosscord",
//
entities: Object.values(Models).filter((x) => x.constructor.name !== "Object"),
synchronize: true,
@@ -25,6 +25,8 @@ export function initDatabase() {
cache: {
duration: 1000 * 3, // cache all find queries for 3 seconds
},
+ bigNumberStrings: false,
+ supportBigNumbers: true,
});
promise.then((connection) => {
diff --git a/util/src/util/checkToken.ts b/util/src/util/checkToken.ts
index 1e203006..8415e8c0 100644
--- a/util/src/util/checkToken.ts
+++ b/util/src/util/checkToken.ts
@@ -12,7 +12,8 @@ export function checkToken(token: string, jwtSecret: string): Promise<any> {
const user = await User.findOne({ id: decoded.id }, { select: ["data", "bot", "disabled", "deleted"] });
if (!user) 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.data.valid_tokens_since.setSeconds(0, 0)) return rej("Invalid Token");
+ if (decoded.iat * 1000 < new Date(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");
|