From a94f7c76172d83b95f43b059b9a5af4299eb2f8d Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 14 Feb 2021 19:01:41 +0100 Subject: [PATCH] switch to mongoose --- .DS_Store | Bin 6148 -> 8196 bytes package-lock.json | 26 +++++++-- package.json | 2 + src/Server.ts | 15 ++++- src/middlewares/GlobalRateLimit.ts | 2 + src/middlewares/RateLimit.ts | 3 + src/routes/api/v8/auth/login.ts | 12 ++-- src/routes/api/v8/auth/register.ts | 14 +++-- src/routes/api/v8/guilds/index.ts | 88 ++++++++++++----------------- src/test/mongo_test.ts | 34 ++++++----- src/util/Event.ts | 24 ++------ 11 files changed, 121 insertions(+), 99 deletions(-) diff --git a/.DS_Store b/.DS_Store index 7daf908b1893dfa12298e2692abd1bf221dd3a8c..07eb6a586f26ba11e6e20f6a40480390881df879 100644 GIT binary patch literal 8196 zcmeHMzl#$=82#pMPs2s@3c*xyK?SuC()gng)?@936G0I4k}P)z8~3}e&!egiXaneWZa+c%S4z6}wn^~tbD)FGl4D&txk z&4R*lu0dI{J?DW6<0;?jZ48RhZorfdaX=gp2gCt!KpgmA9Kbu9ORMI+Z=%wQ1L8m< z9pL9fjLMi;EDh>M2O6CM03&qkg6lQD0LB&*i={zCP?Q-8G((k5F_amO{@C&oi={y` zoRm#IlwDcb3`ObHF@LP-q!NSDiUZ<6=m6*L5nZ7z+M#_ffA2nj=f*}wn)dUekG|IG z=9@3cxA(XF`0&X1?zZ!7pm%_wbo_8#+NKHhz13;ye#me&T)XfwJM&d*n44!*R7&bu zo*whi;d!3Wm2)nlvD2#ePpl}v*HJyA`ytru2k`u2~EQ)*Up2Ez0o^za>0d_5ChrAu5_J@$+ zYS_Cw^JQ)x_dM6LJU#k#O((El`Xpn|M*3@r zx9h+EH_;loXK|o04yY*INY`<@Hn$FLoOA6M^${u;=9LEZ5Hw8Tew~g3XMY&tI<~RI YVrdW&lz;vqz|BMC{jc{xn&vt18?iz-WdHyG delta 434 zcmZp1XfcprU|?W$DortDU=RQ@Ie-{MGjUE#6q~50D9Qwq2aDw~lq8iG7bNB6Cv8kz z&N$gXhNYg3A(7)Bi`h9i1eqb~1-OB< jD=4@(CVpq0%rD~ziVskLvw&z|;DaIrEVenGXAUy}{N`z? diff --git a/package-lock.json b/package-lock.json index 147b1d76..b35ee829 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,8 @@ "lambert-db": "^1.1.8", "lambert-server": "^1.0.10", "missing-native-js-functions": "^1.2.2", + "mongodb": "^3.6.4", + "mongoose-long": "^0.3.2", "node-fetch": "^2.6.1" }, "devDependencies": { @@ -778,12 +780,13 @@ }, "node_modules/fosscord-server-util": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/fosscord/fosscord-server-util.git#3d54ed476240702812caf0b6e1ce94bfb0329cb5", + "resolved": "git+ssh://git@github.com/fosscord/fosscord-server-util.git#4125b7810d6da218fef6e26ec7cfbd1032c4cbb3", "license": "ISC", "dependencies": { "jsonwebtoken": "^8.5.1", "lambert-db": "^1.1.7", - "missing-native-js-functions": "^1.2.2" + "missing-native-js-functions": "^1.2.2", + "mongodb": "^3.6.4" } }, "node_modules/fresh": { @@ -1461,6 +1464,14 @@ "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" }, + "node_modules/mongoose-long": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/mongoose-long/-/mongoose-long-0.3.2.tgz", + "integrity": "sha512-5gTjPH6HUmtNhamv8MPwExWo01Z4d9CT5njZlupqqbmxzMXTbDOgCuP/jnK+9SV0Fs7nuyYlXv7pJ/nA2pAAuA==", + "peerDependencies": { + "mongoose": "4.x || 5.x" + } + }, "node_modules/mongoose/node_modules/mongodb": { "version": "3.6.3", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", @@ -2987,12 +2998,13 @@ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, "fosscord-server-util": { - "version": "git+ssh://git@github.com/fosscord/fosscord-server-util.git#3d54ed476240702812caf0b6e1ce94bfb0329cb5", + "version": "git+ssh://git@github.com/fosscord/fosscord-server-util.git#4125b7810d6da218fef6e26ec7cfbd1032c4cbb3", "from": "fosscord-server-util@github:fosscord/fosscord-server-util", "requires": { "jsonwebtoken": "^8.5.1", "lambert-db": "^1.1.7", - "missing-native-js-functions": "^1.2.2" + "missing-native-js-functions": "^1.2.2", + "mongodb": "^3.6.4" } }, "fresh": { @@ -3586,6 +3598,12 @@ "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" }, + "mongoose-long": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/mongoose-long/-/mongoose-long-0.3.2.tgz", + "integrity": "sha512-5gTjPH6HUmtNhamv8MPwExWo01Z4d9CT5njZlupqqbmxzMXTbDOgCuP/jnK+9SV0Fs7nuyYlXv7pJ/nA2pAAuA==", + "requires": {} + }, "mpath": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.3.tgz", diff --git a/package.json b/package.json index d88232ba..6520901a 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,8 @@ "lambert-db": "^1.1.8", "lambert-server": "^1.0.10", "missing-native-js-functions": "^1.2.2", + "mongodb": "^3.6.4", + "mongoose-long": "^0.3.2", "node-fetch": "^2.6.1" }, "devDependencies": { diff --git a/src/Server.ts b/src/Server.ts index b561f94e..fa4111db 100644 --- a/src/Server.ts +++ b/src/Server.ts @@ -1,5 +1,6 @@ import "missing-native-js-functions"; import fs from "fs/promises"; +import { Connection } from "mongoose"; import { Server, ServerOptions } from "lambert-server"; import { Authentication, GlobalRateLimit } from "./middlewares/"; import Config from "./util/Config"; @@ -29,8 +30,20 @@ export class DiscordServer extends Server { super({ ...opts, errorHandler: false, jsonBody: false }); } + async setupSchema() { + await db.collection("users").createIndex({ id: 1 }, { unique: true }); + await db.collection("messages").createIndex({ id: 1 }, { unique: true }); + await db.collection("channels").createIndex({ id: 1 }, { unique: true }); + await db.collection("guilds").createIndex({ id: 1 }, { unique: true }); + await db.collection("members").createIndex({ id: 1 }, { unique: true }); + await db.collection("roles").createIndex({ id: 1 }, { unique: true }); + await db.collection("emojis").createIndex({ id: 1 }, { unique: true }); + } + async start() { - await db.init(); + // @ts-ignore + await (db as Promise); + await this.setupSchema(); console.log("[DB] connected"); await Promise.all([Config.init()]); diff --git a/src/middlewares/GlobalRateLimit.ts b/src/middlewares/GlobalRateLimit.ts index c729987a..33dc5e0f 100644 --- a/src/middlewares/GlobalRateLimit.ts +++ b/src/middlewares/GlobalRateLimit.ts @@ -6,6 +6,8 @@ import { db } from "fosscord-server-util"; // TODO: increment count on serverside export async function GlobalRateLimit(req: Request, res: Response, next: NextFunction) { + return next(); + // TODO: use new db mongoose models if (!Config.get().limits.rate.ip.enabled) return next(); const ip = getIpAdress(req); diff --git a/src/middlewares/RateLimit.ts b/src/middlewares/RateLimit.ts index c42d773c..b015c7e0 100644 --- a/src/middlewares/RateLimit.ts +++ b/src/middlewares/RateLimit.ts @@ -5,6 +5,9 @@ import { getIpAdress } from "./GlobalRateLimit"; export function RateLimit({ count = 10, timespan = 1000 * 5, name = "/" }) { return async (req: Request, res: Response, next: NextFunction) => { + return next(); + // TODO: use new db mongoose models + let id = req.userid || getIpAdress(req); const limit: { count: number; start: number } = (await db.data.ratelimit.routes[name][id].get()) || { diff --git a/src/routes/api/v8/auth/login.ts b/src/routes/api/v8/auth/login.ts index ac9775df..acdac1ce 100644 --- a/src/routes/api/v8/auth/login.ts +++ b/src/routes/api/v8/auth/login.ts @@ -2,7 +2,7 @@ import { Request, Response, Router } from "express"; import { check, FieldErrors, Length } from "../../../../util/instanceOf"; import bcrypt from "bcrypt"; import jwt from "jsonwebtoken"; -import { db, User } from "fosscord-server-util"; +import { User, UserModel } from "fosscord-server-util"; import Config from "../../../../util/Config"; import { adjustEmail } from "./register"; @@ -23,10 +23,12 @@ router.post( const { login, password } = req.body; // * MongoDB Specific query for user with same email or phone number - const userquery = { $or: [{ email: adjustEmail(login) }, { phone: login }] }; - const user: User = await db.data - .users(userquery) - .get({ hash: true, id: true, user_settings: { locale: true, theme: true } }); + const user = await UserModel.findOne( + { + $or: [{ email: adjustEmail(login) }, { phone: login }], + }, + `hash id user_settings.locale user_settings.theme` + ).exec(); if (!user) { throw FieldErrors({ diff --git a/src/routes/api/v8/auth/register.ts b/src/routes/api/v8/auth/register.ts index 1250b689..1205c462 100644 --- a/src/routes/api/v8/auth/register.ts +++ b/src/routes/api/v8/auth/register.ts @@ -1,6 +1,6 @@ import { Request, Response, Router } from "express"; import Config from "../../../../util/Config"; -import { db, trimSpecial, User, Snowflake } from "fosscord-server-util"; +import { trimSpecial, User, Snowflake, UserModel } from "fosscord-server-util"; import bcrypt from "bcrypt"; import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../../../util/instanceOf"; import "missing-native-js-functions"; @@ -80,7 +80,8 @@ router.post( adjusted_email = adjustEmail(email); // check if there is already an account with this email - const exists = await db.data.users({ email: adjusted_email }).get(); + const exists = await UserModel.findOne({ email: adjusted_email }).exec(); + if (exists) { throw FieldErrors({ email: { @@ -116,7 +117,8 @@ router.post( if (!register.allowMultipleAccounts) { // TODO: check if fingerprint was eligible generated - const exists = await db.data.users({ fingerprint }).get(); + const exists = await UserModel.findOne({ fingerprints: fingerprint }).exec(); + if (exists) { throw FieldErrors({ email: { @@ -150,7 +152,7 @@ router.post( // TODO: is there any better way to generate a random discriminator only once, without checking if it already exists in the mongodb database? for (let tries = 0; tries < 5; tries++) { discriminator = Math.randomIntBetween(1, 9999).toString().padStart(4, "0"); - exists = await db.data.users({ discriminator, username: adjusted_username }).get({ id: true }); + exists = await UserModel.findOne({ discriminator, username: adjusted_username }, "id").exec(); if (!exists) break; } @@ -164,6 +166,8 @@ router.post( } // constructing final user object + // TODO fix: + // @ts-ignore const user: User = { id: Snowflake.generate(), created_at: Date.now(), @@ -220,7 +224,7 @@ router.post( }; // insert user into database - await db.data.users.push(user); + await new UserModel(user).save({}); return res.json({ token: await generateToken(user.id) }); } diff --git a/src/routes/api/v8/guilds/index.ts b/src/routes/api/v8/guilds/index.ts index 2d7e081a..46372a68 100644 --- a/src/routes/api/v8/guilds/index.ts +++ b/src/routes/api/v8/guilds/index.ts @@ -1,19 +1,20 @@ import { Router, Request, Response } from "express"; -import { db, Guild, Snowflake } from "fosscord-server-util"; +import { GuildDeleteEvent, GuildModel, MemberModel, Snowflake } from "fosscord-server-util"; import { HTTPError } from "lambert-server"; import { check } from "./../../../../util/instanceOf"; -import { GuildCreateSchema, GuildGetSchema, GuildUpdateSchema } from "../../../../schema/Guild"; +import { GuildCreateSchema, GuildUpdateSchema } from "../../../../schema/Guild"; +import { emitEvent } from "../../../../util/Event"; const router: Router = Router(); router.get("/:id", async (req: Request, res: Response) => { - const member = await db.data.guilds({ id: req.params.id }).members({ id: req.userid }).get({ id: true }); + const guild = await GuildModel.findOne({ id: BigInt(req.params.id) }).exec(); + if (!guild) throw new HTTPError("Guild doesn't exist"); - if (!member) { - throw new HTTPError("you arent a member of the guild you are trying to access", 401); - } + const member = await MemberModel.findOne({ guild_id: req.params.id, id: req.userid }, "id").exec(); + + if (!member) throw new HTTPError("you arent a member of the guild you are trying to access", 401); - const guild = await db.data.guilds({ id: req.params.id }).get(GuildGetSchema); return res.json(guild); }); @@ -21,7 +22,7 @@ router.patch("/:id", check(GuildUpdateSchema), async (req: Request, res: Respons // TODO: check permission of member const body = req.body as GuildUpdateSchema; - const guild = await db.data.guilds({ id: req.params.id }).get({ id: true }); + const guild = await GuildModel.findOne({ id: BigInt(req.params.id) }).exec(); if (!guild) throw new HTTPError("This guild doesnt exist", 404); throw "not finished"; @@ -30,70 +31,40 @@ router.patch("/:id", check(GuildUpdateSchema), async (req: Request, res: Respons // // TODO: finish POST route router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) => { const body = req.body as GuildCreateSchema; + // TODO: check if user is in more than 100 (config max guilds) const guildID = Snowflake.generate(); - const guild: Guild = { - // name: undefined, - // owner: undefined, - ...body, // ! contains name & icon values + const guild = { + name: body.name, + region: body.region || "en-US", owner_id: req.userid, + icon: undefined, afk_channel_id: undefined, afk_timeout: 300, application_id: undefined, banner: undefined, - channels: [], default_message_notifications: undefined, description: undefined, splash: undefined, discovery_splash: undefined, - emojis: [], explicit_content_filter: undefined, features: [], - // icon: undefined, id: guildID, - // joined_at: undefined, large: undefined, max_members: 250000, - max_presences: undefined, + max_presences: 250000, max_video_channel_users: 25, - member_count: 0, presence_count: 0, - members: [ - { - id: req.userid, - roles: [], // @everyone role is not explicitly set, the client and server automatically assumes it - joined_at: Date.now(), - nick: undefined, - premium_since: undefined, - deaf: false, - mute: false, - pending: false, - permissions: 8n, // value will be computed if a role is changed - }, - ], + member_count: 0, mfa_level: 0, preferred_locale: "en-US", premium_subscription_count: 0, premium_tier: 0, - presences: [], public_updates_channel_id: undefined, - region: undefined, - roles: [ - { - color: 0, - hoist: false, - name: "@everyone", - permissions: 0n, - id: guildID, - managed: true, // ? discord set this to true, - mentionable: false, - position: 0, - }, - ], rules_channel_id: undefined, system_channel_flags: undefined, system_channel_id: undefined, - unavailable: undefined, + unavailable: false, vanity_url_code: undefined, verification_level: undefined, voice_states: [], @@ -103,7 +74,9 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) = }; try { - await db.data.guilds.push(guild); + await new GuildModel(guild).save(); + // TODO: insert default everyone role + // TODO: automatically add user to guild } catch (error) { throw new HTTPError("Couldnt create Guild", 500); } @@ -111,16 +84,29 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) = }); router.delete("/:id", async (req: Request, res: Response) => { - const { id: guildID } = req.params; - - const guild = await db.data.guilds({ id: guildID }).get({ owner_id: true }); + try { + var guildID = BigInt(req.params.id); + } catch (error) { + throw new HTTPError("Invalid id format", 400); + } + const guild = await GuildModel.findOne({ id: BigInt(req.params.id) }, "owner_id").exec(); if (!guild) throw new HTTPError("This guild doesnt exist", 404); if (guild.owner_id !== req.userid) throw new HTTPError("You arent the owner of this guild", 401); - await db.data.guilds({ id: guildID }).delete(); + await emitEvent({ + event: "GUILD_DELETE", + data: { + id: guildID, + }, + guild_id: guildID, + } as GuildDeleteEvent); + + await GuildModel.deleteOne({ id: guildID }).exec(); return res.status(204); }); export default router; + +export async function addMember(guild: bigint, user: bigint) {} diff --git a/src/test/mongo_test.ts b/src/test/mongo_test.ts index b2cc2bce..5c973429 100644 --- a/src/test/mongo_test.ts +++ b/src/test/mongo_test.ts @@ -1,20 +1,28 @@ -import mongoose from "mongoose"; -import { Long } from "mongodb"; -import { Snowflake } from "fosscord-server-util"; +import mongoose, { Schema, Types } from "mongoose"; +import { Long as MongoTypeLong } from "mongodb"; +require("mongoose-long")(mongoose); + +const partSchema = new Schema({ + long: { + type: mongoose.Types.Long, + }, +}); + +const Part = mongoose.model("Part", partSchema, "test"); async function main() { - const conn = await mongoose.createConnection( - "mongodb://localhost:27017/lambert?readPreference=secondaryPreferred", - { - useNewUrlParser: true, - useUnifiedTopology: false, - } - ); + const conn = await mongoose.connect("mongodb://localhost:27017/lambert?readPreference=secondaryPreferred", { + useNewUrlParser: true, + useUnifiedTopology: false, + }); console.log("connected"); - const result = await conn.collection("users").insertOne({ test: Long.fromString(Snowflake.generate().toString()) }); - // .project(undefined) - console.log(result); + const part = new Part({ long: 390810485244821505n }); + + // await part.save(); + console.log("saved"); + const test = await Part.find({}); + console.log(test); } main(); diff --git a/src/util/Event.ts b/src/util/Event.ts index 53c08e2b..c75c909f 100644 --- a/src/util/Event.ts +++ b/src/util/Event.ts @@ -1,26 +1,10 @@ -import { db } from "fosscord-server-util"; +import { Event, EventModel } from "fosscord-server-util"; -export async function emitEvent({ - guild, - user, - channel, - event, - data, -}: { - guild?: bigint; - channel?: bigint; - user?: bigint; - event: string; - data: any; -}) { +export async function emitEvent(payload: Omit) { const emitEvent = { created_at: Math.floor(Date.now() / 1000), // in seconds - guild_id: guild, - user_id: user, - channel_id: channel, - data, - event, + ...payload, }; - return await db.data.events.push(emitEvent); + return await new EventModel(emitEvent).save(); }