switch to mongoose

This commit is contained in:
Flam3rboy 2021-02-14 19:01:41 +01:00
parent 52ef9f7a3e
commit a94f7c7617
11 changed files with 121 additions and 99 deletions

BIN
.DS_Store vendored

Binary file not shown.

26
package-lock.json generated
View File

@ -21,6 +21,8 @@
"lambert-db": "^1.1.8", "lambert-db": "^1.1.8",
"lambert-server": "^1.0.10", "lambert-server": "^1.0.10",
"missing-native-js-functions": "^1.2.2", "missing-native-js-functions": "^1.2.2",
"mongodb": "^3.6.4",
"mongoose-long": "^0.3.2",
"node-fetch": "^2.6.1" "node-fetch": "^2.6.1"
}, },
"devDependencies": { "devDependencies": {
@ -778,12 +780,13 @@
}, },
"node_modules/fosscord-server-util": { "node_modules/fosscord-server-util": {
"version": "1.0.0", "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", "license": "ISC",
"dependencies": { "dependencies": {
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"lambert-db": "^1.1.7", "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": { "node_modules/fresh": {
@ -1461,6 +1464,14 @@
"resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
"integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" "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": { "node_modules/mongoose/node_modules/mongodb": {
"version": "3.6.3", "version": "3.6.3",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz",
@ -2987,12 +2998,13 @@
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
}, },
"fosscord-server-util": { "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", "from": "fosscord-server-util@github:fosscord/fosscord-server-util",
"requires": { "requires": {
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"lambert-db": "^1.1.7", "lambert-db": "^1.1.7",
"missing-native-js-functions": "^1.2.2" "missing-native-js-functions": "^1.2.2",
"mongodb": "^3.6.4"
} }
}, },
"fresh": { "fresh": {
@ -3586,6 +3598,12 @@
"resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
"integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" "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": { "mpath": {
"version": "0.8.3", "version": "0.8.3",
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.3.tgz", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.3.tgz",

View File

@ -32,6 +32,8 @@
"lambert-db": "^1.1.8", "lambert-db": "^1.1.8",
"lambert-server": "^1.0.10", "lambert-server": "^1.0.10",
"missing-native-js-functions": "^1.2.2", "missing-native-js-functions": "^1.2.2",
"mongodb": "^3.6.4",
"mongoose-long": "^0.3.2",
"node-fetch": "^2.6.1" "node-fetch": "^2.6.1"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,5 +1,6 @@
import "missing-native-js-functions"; import "missing-native-js-functions";
import fs from "fs/promises"; import fs from "fs/promises";
import { Connection } from "mongoose";
import { Server, ServerOptions } from "lambert-server"; import { Server, ServerOptions } from "lambert-server";
import { Authentication, GlobalRateLimit } from "./middlewares/"; import { Authentication, GlobalRateLimit } from "./middlewares/";
import Config from "./util/Config"; import Config from "./util/Config";
@ -29,8 +30,20 @@ export class DiscordServer extends Server {
super({ ...opts, errorHandler: false, jsonBody: false }); 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() { async start() {
await db.init(); // @ts-ignore
await (db as Promise<Connection>);
await this.setupSchema();
console.log("[DB] connected"); console.log("[DB] connected");
await Promise.all([Config.init()]); await Promise.all([Config.init()]);

View File

@ -6,6 +6,8 @@ import { db } from "fosscord-server-util";
// TODO: increment count on serverside // TODO: increment count on serverside
export async function GlobalRateLimit(req: Request, res: Response, next: NextFunction) { 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(); if (!Config.get().limits.rate.ip.enabled) return next();
const ip = getIpAdress(req); const ip = getIpAdress(req);

View File

@ -5,6 +5,9 @@ import { getIpAdress } from "./GlobalRateLimit";
export function RateLimit({ count = 10, timespan = 1000 * 5, name = "/" }) { export function RateLimit({ count = 10, timespan = 1000 * 5, name = "/" }) {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
return next();
// TODO: use new db mongoose models
let id = req.userid || getIpAdress(req); let id = req.userid || getIpAdress(req);
const limit: { count: number; start: number } = (await db.data.ratelimit.routes[name][id].get()) || { const limit: { count: number; start: number } = (await db.data.ratelimit.routes[name][id].get()) || {

View File

@ -2,7 +2,7 @@ import { Request, Response, Router } from "express";
import { check, FieldErrors, Length } from "../../../../util/instanceOf"; import { check, FieldErrors, Length } from "../../../../util/instanceOf";
import bcrypt from "bcrypt"; import bcrypt from "bcrypt";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { db, User } from "fosscord-server-util"; import { User, UserModel } from "fosscord-server-util";
import Config from "../../../../util/Config"; import Config from "../../../../util/Config";
import { adjustEmail } from "./register"; import { adjustEmail } from "./register";
@ -23,10 +23,12 @@ router.post(
const { login, password } = req.body; const { login, password } = req.body;
// * MongoDB Specific query for user with same email or phone number // * MongoDB Specific query for user with same email or phone number
const userquery = { $or: [{ email: adjustEmail(login) }, { phone: login }] }; const user = await UserModel.findOne(
const user: User = await db.data {
.users(userquery) $or: [{ email: adjustEmail(login) }, { phone: login }],
.get({ hash: true, id: true, user_settings: { locale: true, theme: true } }); },
`hash id user_settings.locale user_settings.theme`
).exec();
if (!user) { if (!user) {
throw FieldErrors({ throw FieldErrors({

View File

@ -1,6 +1,6 @@
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
import Config from "../../../../util/Config"; 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 bcrypt from "bcrypt";
import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../../../util/instanceOf"; import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../../../util/instanceOf";
import "missing-native-js-functions"; import "missing-native-js-functions";
@ -80,7 +80,8 @@ router.post(
adjusted_email = adjustEmail(email); adjusted_email = adjustEmail(email);
// check if there is already an account with this 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) { if (exists) {
throw FieldErrors({ throw FieldErrors({
email: { email: {
@ -116,7 +117,8 @@ router.post(
if (!register.allowMultipleAccounts) { if (!register.allowMultipleAccounts) {
// TODO: check if fingerprint was eligible generated // 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) { if (exists) {
throw FieldErrors({ throw FieldErrors({
email: { 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? // 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++) { for (let tries = 0; tries < 5; tries++) {
discriminator = Math.randomIntBetween(1, 9999).toString().padStart(4, "0"); 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; if (!exists) break;
} }
@ -164,6 +166,8 @@ router.post(
} }
// constructing final user object // constructing final user object
// TODO fix:
// @ts-ignore
const user: User = { const user: User = {
id: Snowflake.generate(), id: Snowflake.generate(),
created_at: Date.now(), created_at: Date.now(),
@ -220,7 +224,7 @@ router.post(
}; };
// insert user into database // insert user into database
await db.data.users.push(user); await new UserModel(user).save({});
return res.json({ token: await generateToken(user.id) }); return res.json({ token: await generateToken(user.id) });
} }

View File

@ -1,19 +1,20 @@
import { Router, Request, Response } from "express"; 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 { HTTPError } from "lambert-server";
import { check } from "./../../../../util/instanceOf"; 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(); const router: Router = Router();
router.get("/:id", async (req: Request, res: Response) => { 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) { const member = await MemberModel.findOne({ guild_id: req.params.id, id: req.userid }, "id").exec();
throw new HTTPError("you arent a member of the guild you are trying to access", 401);
} 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); return res.json(guild);
}); });
@ -21,7 +22,7 @@ router.patch("/:id", check(GuildUpdateSchema), async (req: Request, res: Respons
// TODO: check permission of member // TODO: check permission of member
const body = req.body as GuildUpdateSchema; 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); if (!guild) throw new HTTPError("This guild doesnt exist", 404);
throw "not finished"; throw "not finished";
@ -30,70 +31,40 @@ router.patch("/:id", check(GuildUpdateSchema), async (req: Request, res: Respons
// // TODO: finish POST route // // TODO: finish POST route
router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) => { router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) => {
const body = req.body as GuildCreateSchema; const body = req.body as GuildCreateSchema;
// TODO: check if user is in more than 100 (config max guilds)
const guildID = Snowflake.generate(); const guildID = Snowflake.generate();
const guild: Guild = { const guild = {
// name: undefined, name: body.name,
// owner: undefined, region: body.region || "en-US",
...body, // ! contains name & icon values
owner_id: req.userid, owner_id: req.userid,
icon: undefined,
afk_channel_id: undefined, afk_channel_id: undefined,
afk_timeout: 300, afk_timeout: 300,
application_id: undefined, application_id: undefined,
banner: undefined, banner: undefined,
channels: [],
default_message_notifications: undefined, default_message_notifications: undefined,
description: undefined, description: undefined,
splash: undefined, splash: undefined,
discovery_splash: undefined, discovery_splash: undefined,
emojis: [],
explicit_content_filter: undefined, explicit_content_filter: undefined,
features: [], features: [],
// icon: undefined,
id: guildID, id: guildID,
// joined_at: undefined,
large: undefined, large: undefined,
max_members: 250000, max_members: 250000,
max_presences: undefined, max_presences: 250000,
max_video_channel_users: 25, max_video_channel_users: 25,
member_count: 0,
presence_count: 0, presence_count: 0,
members: [ member_count: 0,
{
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
},
],
mfa_level: 0, mfa_level: 0,
preferred_locale: "en-US", preferred_locale: "en-US",
premium_subscription_count: 0, premium_subscription_count: 0,
premium_tier: 0, premium_tier: 0,
presences: [],
public_updates_channel_id: undefined, 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, rules_channel_id: undefined,
system_channel_flags: undefined, system_channel_flags: undefined,
system_channel_id: undefined, system_channel_id: undefined,
unavailable: undefined, unavailable: false,
vanity_url_code: undefined, vanity_url_code: undefined,
verification_level: undefined, verification_level: undefined,
voice_states: [], voice_states: [],
@ -103,7 +74,9 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) =
}; };
try { 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) { } catch (error) {
throw new HTTPError("Couldnt create Guild", 500); 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) => { router.delete("/:id", async (req: Request, res: Response) => {
const { id: guildID } = req.params; try {
var guildID = BigInt(req.params.id);
const guild = await db.data.guilds({ id: guildID }).get({ owner_id: true }); } 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) 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); 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); return res.status(204);
}); });
export default router; export default router;
export async function addMember(guild: bigint, user: bigint) {}

View File

@ -1,20 +1,28 @@
import mongoose from "mongoose"; import mongoose, { Schema, Types } from "mongoose";
import { Long } from "mongodb"; import { Long as MongoTypeLong } from "mongodb";
import { Snowflake } from "fosscord-server-util"; require("mongoose-long")(mongoose);
const partSchema = new Schema({
long: {
type: mongoose.Types.Long,
},
});
const Part = mongoose.model("Part", partSchema, "test");
async function main() { async function main() {
const conn = await mongoose.createConnection( const conn = await mongoose.connect("mongodb://localhost:27017/lambert?readPreference=secondaryPreferred", {
"mongodb://localhost:27017/lambert?readPreference=secondaryPreferred", useNewUrlParser: true,
{ useUnifiedTopology: false,
useNewUrlParser: true, });
useUnifiedTopology: false,
}
);
console.log("connected"); 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(); main();

View File

@ -1,26 +1,10 @@
import { db } from "fosscord-server-util"; import { Event, EventModel } from "fosscord-server-util";
export async function emitEvent({ export async function emitEvent(payload: Omit<Event, "created_at">) {
guild,
user,
channel,
event,
data,
}: {
guild?: bigint;
channel?: bigint;
user?: bigint;
event: string;
data: any;
}) {
const emitEvent = { const emitEvent = {
created_at: Math.floor(Date.now() / 1000), // in seconds created_at: Math.floor(Date.now() / 1000), // in seconds
guild_id: guild, ...payload,
user_id: user,
channel_id: channel,
data,
event,
}; };
return await db.data.events.push(emitEvent); return await new EventModel(emitEvent).save();
} }