diff --git a/src/routes/api/v8/channels/#channel_id/invites.ts b/src/routes/api/v8/channels/#channel_id/invites.ts
deleted file mode 100644
index e3f2b77e..00000000
--- a/src/routes/api/v8/channels/#channel_id/invites.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Router, Request, Response } from "express";
-import { HTTPError } from "lambert-server";
-
-import { check } from "@util/instanceOf";
-import { random } from "@util/RandomInviteID";
-import { emitEvent } from "@util/Event";
-
-import { InviteCreateSchema } from "@schema/Invite";
-
-import { getPermission, ChannelModel, InviteModel, InviteCreateEvent } from "fosscord-server-util";
-
-const router: Router = Router();
-
-router.post("/", check(InviteCreateSchema), async (req: Request, res: Response) => {
- const usID = req.user_id;
- const chID = BigInt(req.params.channel_id);
- const channel = await ChannelModel.findOne({ id: chID }).exec();
-
- if (!channel || !channel.guild_id) {
- throw new HTTPError("This channel doesn't exist", 404);
- }
- const { guild_id: guID } = channel;
-
- const permission = await getPermission(usID, guID);
-
- if (!permission.has("CREATE_INSTANT_INVITE")) {
- throw new HTTPError("You aren't authorised to access this endpoint", 401);
- }
-
- const invite = {
- code: random(),
- temporary: req.body.temporary,
- uses: 0,
- max_uses: req.body.max_uses,
- max_age: req.body.max_age,
- created_at: new Date(),
- guild_id: guID,
- channel_id: chID,
- inviter_id: usID,
- };
-
- await new InviteModel(invite).save();
-
- await emitEvent({ event: "INVITE_CREATE", data: invite } as InviteCreateEvent);
- res.status(201).send(invite);
-});
-
-export default router;
diff --git a/src/routes/api/v8/invites/index.ts b/src/routes/api/v8/invites/index.ts
deleted file mode 100644
index 9a4e81fa..00000000
--- a/src/routes/api/v8/invites/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { Router } from "express";
-const router: Router = Router();
-
-export default router;
diff --git a/src/routes/assets/index.ts b/src/routes/assets/index.ts
deleted file mode 100644
index df30d13e..00000000
--- a/src/routes/assets/index.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * * patch to redirect requests from cloned client
- * (../../client/index.html)
- */
-import { Router } from "express";
-import fetch, { Response } from "node-fetch";
-
-const router: Router = Router();
-const cache = new Map<string, Response>();
-const assetEndpoint = "https://discord.com/assets/";
-
-export async function getCache(key: string): Promise<Response> {
- let cachedRessource = cache.get(key);
-
- if (!cachedRessource) {
- const res = await fetch(assetEndpoint + key);
- // @ts-ignore
- res.bufferResponse = await res.buffer();
- cache.set(key, res);
- cachedRessource = res;
- }
-
- return cachedRessource;
-}
-
-router.get("/:hash", async (req, res) => {
- res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
- const cache = await getCache(req.params.hash);
- res.set("content-type", <string>cache.headers.get("content-type"));
- // @ts-ignore
- res.send(cache.bufferResponse);
-});
-
-export default router;
diff --git a/src/routes/api/v8/auth/login.ts b/src/routes/auth/login.ts
index 3f924e7c..cc2b6202 100644
--- a/src/routes/api/v8/auth/login.ts
+++ b/src/routes/auth/login.ts
@@ -1,9 +1,9 @@
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 jwt from "jsonwebtoken";
import { User, UserModel } from "fosscord-server-util";
-import Config from "../../../../util/Config";
+import Config from "../../util/Config";
import { adjustEmail } from "./register";
const router: Router = Router();
@@ -25,12 +25,11 @@ router.post(
const query: any[] = [{ phone: login }];
if (email) query.push({ email });
- // * MongoDB Specific query for user with same email or phone number
const user = await UserModel.findOne(
{
$or: query,
},
- `hash id user_settings.locale user_settings.theme`
+ `user_data.hash id user_settings.locale user_settings.theme`
).exec();
if (!user) {
@@ -40,7 +39,7 @@ router.post(
}
// the salt is saved in the password refer to bcrypt docs
- const same_password = await bcrypt.compare(password, user.hash);
+ const same_password = await bcrypt.compare(password, user.user_data.hash);
if (!same_password) {
throw FieldErrors({
password: { message: req.t("auth:login.INVALID_PASSWORD"), code: "INVALID_PASSWORD" },
@@ -57,13 +56,13 @@ router.post(
}
);
-export async function generateToken(id: bigint) {
+export async function generateToken(id: string) {
const iat = Math.floor(Date.now() / 1000);
const algorithm = "HS256";
return new Promise((res, rej) => {
jwt.sign(
- { id: `${id}`, iat },
+ { id: id, iat },
Config.get().security.jwtSecret,
{
algorithm,
@@ -80,7 +79,6 @@ export async function generateToken(id: bigint) {
* POST /auth/login
* @argument { login: "email@gmail.com", password: "cleartextpassword", undelete: false, captcha_key: null, login_source: null, gift_code_sku_id: null, }
-
* MFA required:
* @returns {"token": null, "mfa": true, "sms": true, "ticket": "SOME TICKET JWT TOKEN"}
diff --git a/src/routes/api/v8/auth/register.ts b/src/routes/auth/register.ts
index 99df82f1..5501203d 100644
--- a/src/routes/api/v8/auth/register.ts
+++ b/src/routes/auth/register.ts
@@ -1,8 +1,8 @@
import { Request, Response, Router } from "express";
-import Config from "../../../../util/Config";
+import Config from "../../util/Config";
import { trimSpecial, User, Snowflake, UserModel } from "fosscord-server-util";
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 { generateToken } from "./login";
@@ -52,7 +52,7 @@ router.post(
let discriminator = "";
// get register Config
- const { register } = Config.get();
+ const { register, security } = Config.get();
// check if registration is allowed
if (!register.allowNewRegistration) {
@@ -129,9 +129,9 @@ router.post(
}
}
- if (register.requireCaptcha) {
+ if (register.requireCaptcha && security.captcha.enabled) {
if (!captcha_key) {
- const { sitekey, service } = Config.get().security.captcha;
+ const { sitekey, service } = security.captcha;
return res.status(400).json({
captcha_key: ["captcha-required"],
captcha_sitekey: sitekey,
@@ -165,9 +165,10 @@ router.post(
});
}
- // constructing final user object
- // TODO fix:
- // @ts-ignore
+ // TODO: save date_of_birth
+ // appearently discord doesn't save the date of birth and just calculate if nsfw is allowed
+ // if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false
+
const user: User = {
id: Snowflake.generate(),
created_at: new Date(),
@@ -176,13 +177,34 @@ router.post(
avatar: null,
bot: false,
system: false,
+ desktop: false,
+ mobile: false,
+ premium: false,
+ premium_type: 0,
+ phone: undefined,
mfa_enabled: false,
verified: false,
+ presence: {
+ activities: [],
+ client_status: {
+ desktop: undefined,
+ mobile: undefined,
+ web: undefined,
+ },
+ status: "offline",
+ },
email: adjusted_email,
+ nsfw_allowed: true, // TODO: depending on age
+ public_flags: 0n,
flags: 0n, // TODO: generate default flags
- hash: adjusted_password,
guilds: [],
- valid_tokens_since: new Date(),
+ user_data: {
+ hash: adjusted_password,
+ valid_tokens_since: new Date(),
+ relationships: [],
+ connected_accounts: [],
+ fingerprints: [],
+ },
user_settings: {
afk_timeout: 300,
allow_accessibility_detection: true,
@@ -225,7 +247,7 @@ router.post(
};
// insert user into database
- await new UserModel(user).save({});
+ await new UserModel(user).save();
return res.json({ token: await generateToken(user.id) });
}
diff --git a/src/routes/api/v8/channels/#channel_id/followers.ts b/src/routes/channels/#channel_id/followers.ts
index 9a4e81fa..9a4e81fa 100644
--- a/src/routes/api/v8/channels/#channel_id/followers.ts
+++ b/src/routes/channels/#channel_id/followers.ts
diff --git a/src/routes/api/v8/channels/#channel_id/index.ts b/src/routes/channels/#channel_id/index.ts
index 9a4e81fa..9a4e81fa 100644
--- a/src/routes/api/v8/channels/#channel_id/index.ts
+++ b/src/routes/channels/#channel_id/index.ts
diff --git a/src/routes/channels/#channel_id/invites.ts b/src/routes/channels/#channel_id/invites.ts
new file mode 100644
index 00000000..da802800
--- /dev/null
+++ b/src/routes/channels/#channel_id/invites.ts
@@ -0,0 +1,68 @@
+import { Router, Request, Response } from "express";
+import { HTTPError } from "lambert-server";
+
+import { check } from "../../../util/instanceOf";
+import { random } from "../../../util/RandomInviteID";
+import { emitEvent } from "../../../util/Event";
+
+import { InviteCreateSchema } from "../../../schema/Invite";
+
+import { getPermission, ChannelModel, InviteModel, InviteCreateEvent, toObject } from "fosscord-server-util";
+
+const router: Router = Router();
+
+router.post("/", check(InviteCreateSchema), async (req: Request, res: Response) => {
+ const { user_id } = req;
+ const { channel_id } = req.params;
+ const channel = await ChannelModel.findOne({ id: channel_id }).exec();
+
+ if (!channel || !channel.guild_id) {
+ throw new HTTPError("This channel doesn't exist", 404);
+ }
+ const { guild_id } = channel;
+
+ const permission = await getPermission(user_id, guild_id);
+
+ if (!permission.has("CREATE_INSTANT_INVITE")) {
+ throw new HTTPError("You aren't authorised to access this endpoint", 401);
+ }
+
+ const invite = {
+ code: random(),
+ temporary: req.body.temporary,
+ uses: 0,
+ max_uses: req.body.max_uses,
+ max_age: req.body.max_age,
+ created_at: new Date(),
+ guild_id,
+ channel_id: channel_id,
+ inviter_id: user_id,
+ };
+
+ await new InviteModel(invite).save();
+
+ await emitEvent({ event: "INVITE_CREATE", data: invite, guild_id } as InviteCreateEvent);
+ res.status(201).send(invite);
+});
+
+router.get("/", async (req: Request, res: Response) => {
+ const { user_id } = req;
+ const { channel_id } = req.params;
+ const channel = await ChannelModel.findOne({ id: channel_id }).exec();
+
+ if (!channel || !channel.guild_id) {
+ throw new HTTPError("This channel doesn't exist", 404);
+ }
+ const { guild_id } = channel;
+ const permission = await getPermission(user_id, guild_id);
+
+ if (!permission.has("MANAGE_CHANNELS")) {
+ throw new HTTPError("You aren't authorised to access this endpoint", 401);
+ }
+
+ const invites = await InviteModel.find({ guild_id }).exec();
+
+ res.status(200).send(toObject(invites));
+});
+
+export default router;
diff --git a/src/routes/api/v8/channels/#channel_id/messages/bulk-delete.ts b/src/routes/channels/#channel_id/messages/bulk-delete.ts
index dc7d5f66..89e9d720 100644
--- a/src/routes/api/v8/channels/#channel_id/messages/bulk-delete.ts
+++ b/src/routes/channels/#channel_id/messages/bulk-delete.ts
@@ -1,9 +1,9 @@
import { Router } from "express";
import { ChannelModel, getPermission, MessageDeleteBulkEvent, MessageModel } from "fosscord-server-util";
import { HTTPError } from "lambert-server";
-import Config from "@util/Config";
-import { emitEvent } from "@util/Event";
-import { check } from "@util/instanceOf";
+import Config from "../../../../util/Config";
+import { emitEvent } from "../../../../util/Event";
+import { check } from "../../../../util/instanceOf";
const router: Router = Router();
@@ -12,8 +12,8 @@ export default router;
// TODO: should users be able to bulk delete messages or only bots?
// TODO: should this request fail, if you provide messages older than 14 days/invalid ids?
// https://discord.com/developers/docs/resources/channel#bulk-delete-messages
-router.post("/", check({ messages: [BigInt] }), async (req, res) => {
- const channel_id = BigInt(req.params.channel_id);
+router.post("/", check({ messages: [String] }), async (req, res) => {
+ const channel_id = req.params.channel_id
const channel = await ChannelModel.findOne({ id: channel_id }, { permission_overwrites: true, guild_id: true }).exec();
if (!channel?.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400);
@@ -22,7 +22,7 @@ router.post("/", check({ messages: [BigInt] }), async (req, res) => {
const { maxBulkDelete } = Config.get().limits.message;
- const { messages } = req.body as { messages: bigint[] };
+ const { messages } = req.body as { messages: string[] };
if (messages.length < 2) throw new HTTPError("You must at least specify 2 messages to bulk delete");
if (messages.length > maxBulkDelete) throw new HTTPError(`You cannot delete more than ${maxBulkDelete} messages`);
diff --git a/src/routes/api/v8/channels/#channel_id/messages/index.ts b/src/routes/channels/#channel_id/messages/index.ts
index b2eb8ef4..4978b98a 100644
--- a/src/routes/api/v8/channels/#channel_id/messages/index.ts
+++ b/src/routes/channels/#channel_id/messages/index.ts
@@ -1,11 +1,23 @@
import { Router } from "express";
-import { ChannelModel, ChannelType, getPermission, Message, MessageCreateEvent, MessageModel, Snowflake } from "fosscord-server-util";
+import {
+ ChannelModel,
+ ChannelType,
+ getPermission,
+ Message,
+ MessageCreateEvent,
+ MessageDocument,
+ MessageModel,
+ Snowflake,
+ toObject,
+} from "fosscord-server-util";
import { HTTPError } from "lambert-server";
-import { MessageCreateSchema } from "@schema/Message";
-import { check, instanceOf, Length } from "@util/instanceOf";
-import { PublicUserProjection } from "@util/User";
+import { MessageCreateSchema } from "../../../../schema/Message";
+import { check, instanceOf, Length } from "../../../../util/instanceOf";
+import { PublicUserProjection } from "../../../../util/User";
import multer from "multer";
-import { emitEvent } from "@util/Event";
+import { emitEvent } from "../../../../util/Event";
+import { Query } from "mongoose";
+import { PublicMemberProjection } from "../../../../util/Member";
const router: Router = Router();
export default router;
@@ -27,23 +39,23 @@ function isTextChannel(type: ChannelType): boolean {
// https://discord.com/developers/docs/resources/channel#create-message
// get messages
router.get("/", async (req, res) => {
- const channel_id = BigInt(req.params.channel_id);
+ const channel_id = req.params.channel_id;
const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, type: true, permission_overwrites: true }).exec();
if (!channel) throw new HTTPError("Channel not found", 404);
isTextChannel(channel.type);
try {
- instanceOf({ $around: BigInt, $after: BigInt, $before: BigInt, $limit: new Length(Number, 1, 100) }, req.query, {
+ instanceOf({ $around: String, $after: String, $before: String, $limit: new Length(Number, 1, 100) }, req.query, {
path: "query",
req,
});
} catch (error) {
return res.status(400).json({ code: 50035, message: "Invalid Query", success: false, errors: error });
}
- var { around, after, before, limit }: { around?: bigint; after?: bigint; before?: bigint; limit?: number } = req.query;
+ var { around, after, before, limit }: { around?: string; after?: string; before?: string; limit?: number } = req.query;
if (!limit) limit = 50;
- var halfLimit = BigInt(Math.floor(limit / 2));
+ var halfLimit = Math.floor(limit / 2);
if ([ChannelType.GUILD_VOICE, ChannelType.GUILD_CATEGORY, ChannelType.GUILD_STORE].includes(channel.type))
throw new HTTPError("Not a text channel");
@@ -57,24 +69,21 @@ router.get("/", async (req, res) => {
if (!channel.recipients.includes(req.user_id)) throw new HTTPError("You don't have permission to view this channel", 401);
}
- var query: any;
+ var query: Query<MessageDocument[], MessageDocument, {}>;
if (after) query = MessageModel.find({ channel_id, id: { $gt: after } });
else if (before) query = MessageModel.find({ channel_id, id: { $lt: before } });
- else if (around) query = MessageModel.find({ channel_id, id: { $gt: around - halfLimit, $lt: around + halfLimit } });
+ else if (around)
+ query = MessageModel.find({
+ channel_id,
+ id: { $gt: (BigInt(around) - BigInt(halfLimit)).toString(), $lt: (BigInt(around) + BigInt(halfLimit)).toString() },
+ });
else {
query = MessageModel.find({ channel_id }).sort({ id: -1 });
}
- const messages = await query
- .limit(limit)
- .populate({ path: "author", select: PublicUserProjection })
- .populate({ path: "mentions", select: PublicUserProjection })
- .populate({ path: "mention_channels", select: { id: true, guild_id: true, type: true, name: true } })
- .populate("mention_roles")
- // .populate({ path: "member", select: PublicMemberProjection })
- .exec();
+ const messages = await query.limit(limit).exec();
- return res.json(messages);
+ return res.json(toObject(messages));
});
// TODO: config max upload size
@@ -88,11 +97,12 @@ const messageUpload = multer({ limits: { fieldSize: 1024 * 1024 * 1024 * 50 } })
// TODO: trim and replace message content and every embed field
// Send message
router.post("/", check(MessageCreateSchema), async (req, res) => {
- const channel_id = BigInt(req.params.channel_id);
+ const channel_id = req.params.channel_id;
const body = req.body as MessageCreateSchema;
const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, type: true, permission_overwrites: true }).exec();
if (!channel) throw new HTTPError("Channel not found", 404);
+ // TODO: are tts messages allowed in dm channels? should permission be checked?
if (channel.guild_id) {
const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel });
@@ -108,24 +118,36 @@ router.post("/", check(MessageCreateSchema), async (req, res) => {
if (body.message_reference) {
if (body.message_reference.channel_id !== channel_id) throw new HTTPError("You can only reference messages from this channel");
- // TODO: should it be checked if the message exists?
+ // TODO: should be checked if the referenced message exists?
}
const embeds = [];
if (body.embed) embeds.push(body.embed);
+ // TODO: check and put all in body in it
const message: Message = {
id: Snowflake.generate(),
channel_id,
guild_id: channel.guild_id,
author_id: req.user_id,
- content: req.body,
+ content: body.content,
timestamp: new Date(),
- embeds,
+ mention_channels_ids: [],
+ mention_role_ids: [],
+ mention_user_ids: [],
attachments: [],
+ embeds: [],
+ reactions: [],
+ type: 0,
+ tts: body.tts,
+ nonce: body.nonce,
+ pinned: false,
};
- await new MessageModel(message).save();
+ const doc = await new MessageModel(message).populate({ path: "member", select: PublicMemberProjection }).save();
+ const data = toObject(doc);
+
+ await emitEvent({ event: "MESSAGE_CREATE", channel_id, data, guild_id: channel.guild_id } as MessageCreateEvent);
- await emitEvent({ event: "MESSAGE_CREATE", channel_id, data: {} } as MessageCreateEvent);
+ return res.send(data);
});
diff --git a/src/routes/api/v8/channels/#channel_id/permissions.ts b/src/routes/channels/#channel_id/permissions.ts
index 9a4e81fa..9a4e81fa 100644
--- a/src/routes/api/v8/channels/#channel_id/permissions.ts
+++ b/src/routes/channels/#channel_id/permissions.ts
diff --git a/src/routes/api/v8/channels/#channel_id/pins.ts b/src/routes/channels/#channel_id/pins.ts
index 9a4e81fa..9a4e81fa 100644
--- a/src/routes/api/v8/channels/#channel_id/pins.ts
+++ b/src/routes/channels/#channel_id/pins.ts
diff --git a/src/routes/api/v8/channels/#channel_id/recipients.ts b/src/routes/channels/#channel_id/recipients.ts
index 9a4e81fa..9a4e81fa 100644
--- a/src/routes/api/v8/channels/#channel_id/recipients.ts
+++ b/src/routes/channels/#channel_id/recipients.ts
diff --git a/src/routes/api/v8/channels/#channel_id/typing.ts b/src/routes/channels/#channel_id/typing.ts
index 9a4e81fa..9a4e81fa 100644
--- a/src/routes/api/v8/channels/#channel_id/typing.ts
+++ b/src/routes/channels/#channel_id/typing.ts
diff --git a/src/routes/api/v8/channels/#channel_id/webhooks.ts b/src/routes/channels/#channel_id/webhooks.ts
index 9a4e81fa..9a4e81fa 100644
--- a/src/routes/api/v8/channels/#channel_id/webhooks.ts
+++ b/src/routes/channels/#channel_id/webhooks.ts
diff --git a/src/routes/experiments.ts b/src/routes/experiments.ts
new file mode 100644
index 00000000..6bca49c5
--- /dev/null
+++ b/src/routes/experiments.ts
@@ -0,0 +1,10 @@
+import { Router } from "express";
+
+const router = Router();
+
+router.get("/", (req, res) => {
+ // TODO:
+ res.send({ fingerprint: "", assignments: [] });
+});
+
+export default router;
diff --git a/src/routes/gateway.ts b/src/routes/gateway.ts
new file mode 100644
index 00000000..53302f12
--- /dev/null
+++ b/src/routes/gateway.ts
@@ -0,0 +1,9 @@
+import { Router } from "express";
+
+const router = Router();
+
+router.get("/", (req, res) => {
+ res.send({ url: "ws://localhost:8080" });
+});
+
+export default router;
diff --git a/src/routes/api/v8/guilds/#id/bans.ts b/src/routes/guilds/#guild_id/bans.ts
index 9fbcf01c..3de80a32 100644
--- a/src/routes/api/v8/guilds/#id/bans.ts
+++ b/src/routes/guilds/#guild_id/bans.ts
@@ -1,28 +1,28 @@
import { Request, Response, Router } from "express";
-import { BanModel, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel } from "fosscord-server-util";
+import { BanModel, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel, toObject } from "fosscord-server-util";
import { HTTPError } from "lambert-server";
-import { getIpAdress } from "@middlewares/GlobalRateLimit";
-import { BanCreateSchema } from "@schema/Ban";
-import { emitEvent } from "@util/Event";
-import { check } from "@util/instanceOf";
-import { removeMember } from "@util/Member";
-import { getPublicUser } from "@util/User";
+import { getIpAdress } from "../../../middlewares/GlobalRateLimit";
+import { BanCreateSchema } from "../../../schema/Ban";
+import { emitEvent } from "../../../util/Event";
+import { check } from "../../../util/instanceOf";
+import { removeMember } from "../../../util/Member";
+import { getPublicUser } from "../../../util/User";
const router: Router = Router();
router.get("/", async (req: Request, res: Response) => {
- const guild_id = BigInt(req.params.id);
+ const guild_id = req.params.id;
- const guild = await GuildModel.findOne({ id: guild_id }).exec();
+ const guild = await GuildModel.exists({ id: guild_id });
if (!guild) throw new HTTPError("Guild not found", 404);
var bans = await BanModel.find({ guild_id: guild_id }).exec();
- return res.json(bans);
+ return res.json(toObject(bans));
});
router.get("/:user", async (req: Request, res: Response) => {
- const guild_id = BigInt(req.params.id);
- const user_id = BigInt(req.params.ban);
+ const guild_id = req.params.id;
+ const user_id = req.params.ban;
var ban = await BanModel.findOne({ guild_id: guild_id, user_id: user_id }).exec();
if (!ban) throw new HTTPError("Ban not found", 404);
@@ -30,8 +30,8 @@ router.get("/:user", async (req: Request, res: Response) => {
});
router.post("/:user_id", check(BanCreateSchema), async (req: Request, res: Response) => {
- const guild_id = BigInt(req.params.id);
- const banned_user_id = BigInt(req.params.user_id);
+ const guild_id = req.params.id;
+ const banned_user_id = req.params.user_id;
const banned_user = await getPublicUser(banned_user_id);
const perms = await getPermission(req.user_id, guild_id);
@@ -61,30 +61,30 @@ router.post("/:user_id", check(BanCreateSchema), async (req: Request, res: Respo
});
router.delete("/:user_id", async (req: Request, res: Response) => {
- var guild_id = BigInt(req.params.id);
- var banned_user_id = BigInt(req.params.user_id);
+ var { guild_id } = req.params;
+ var banned_user_id = req.params.user_id;
const banned_user = await getPublicUser(banned_user_id);
- const guild = await GuildModel.findOne({ id: guild_id }, { id: true }).exec();
+ const guild = await GuildModel.exists({ id: guild_id });
if (!guild) throw new HTTPError("Guild not found", 404);
- const perms = await getPermission(req.user_id, guild.id);
+ const perms = await getPermission(req.user_id, guild_id);
if (!perms.has("BAN_MEMBERS")) {
throw new HTTPError("No permissions", 403);
}
await BanModel.deleteOne({
user_id: banned_user_id,
- guild_id: guild.id,
+ guild_id,
}).exec();
await emitEvent({
event: "GUILD_BAN_REMOVE",
data: {
- guild_id: guild.id,
+ guild_id,
user: banned_user,
},
- guild_id: guild.id,
+ guild_id,
} as GuildBanRemoveEvent);
return res.status(204).send();
diff --git a/src/routes/api/v8/guilds/#id/channels.ts b/src/routes/guilds/#guild_id/channels.ts
index 1316a2ca..e0d8f3ac 100644
--- a/src/routes/api/v8/guilds/#id/channels.ts
+++ b/src/routes/guilds/#guild_id/channels.ts
@@ -1,19 +1,20 @@
import { Router } from "express";
-import { ChannelModel, ChannelType, GuildModel, Snowflake } from "fosscord-server-util";
+import { ChannelCreateEvent, ChannelModel, ChannelType, GuildModel, Snowflake, toObject } from "fosscord-server-util";
import { HTTPError } from "lambert-server";
-import { ChannelModifySchema } from "../../../../../schema/Channel";
-import { check } from "../../../../../util/instanceOf";
+import { ChannelModifySchema } from "../../../schema/Channel";
+import { emitEvent } from "../../../util/Event";
+import { check } from "../../../util/instanceOf";
const router = Router();
router.get("/", async (req, res) => {
- const guild_id = BigInt(req.params.id);
+ const { guild_id } = req.params;
const channels = await ChannelModel.find({ guild_id }).exec();
- res.json(channels);
+ res.json(toObject(channels));
});
router.post("/", check(ChannelModifySchema), async (req, res) => {
- const guild_id = BigInt(req.params.id);
+ const { guild_id } = req.params;
const body = req.body as ChannelModifySchema;
if (!body.permission_overwrites) body.permission_overwrites = [];
if (!body.topic) body.topic = "";
@@ -45,6 +46,8 @@ router.post("/", check(ChannelModifySchema), async (req, res) => {
};
await new ChannelModel(channel).save();
+ await emitEvent({ event: "CHANNEL_CREATE", data: channel, guild_id } as ChannelCreateEvent);
+
res.json(channel);
});
diff --git a/src/routes/api/v8/guilds/#id/index.ts b/src/routes/guilds/#guild_id/index.ts
index e86d9416..7e5f49d3 100644
--- a/src/routes/api/v8/guilds/#id/index.ts
+++ b/src/routes/guilds/#guild_id/index.ts
@@ -5,26 +5,31 @@ import {
getPermission,
GuildDeleteEvent,
GuildModel,
+ GuildUpdateEvent,
InviteModel,
MemberModel,
MessageModel,
RoleModel,
+ toObject,
UserModel,
} from "fosscord-server-util";
import { HTTPError } from "lambert-server";
-import { GuildUpdateSchema } from "../../../../../schema/Guild";
-import { emitEvent } from "../../../../../util/Event";
-import { check } from "../../../../../util/instanceOf";
+import { GuildUpdateSchema } from "../../../schema/Guild";
+import { emitEvent } from "../../../util/Event";
+import { check } from "../../../util/instanceOf";
+import "missing-native-js-functions";
const router = Router();
router.get("/", async (req: Request, res: Response) => {
- const guild_id = BigInt(req.params.id);
+ const { guild_id } = req.params;
- const guild = await GuildModel.findOne({ id: guild_id }).exec();
+ const guild = await GuildModel.findOne({ id: guild_id })
+ .populate({ path: "joined_at", match: { id: req.user_id } })
+ .exec();
if (!guild) throw new HTTPError("Guild does not exist", 404);
- const member = await MemberModel.findOne({ guild_id: guild_id, id: req.user_id }, "id").exec();
+ const member = await MemberModel.exists({ guild_id: guild_id, id: req.user_id });
if (!member) throw new HTTPError("You are not a member of the guild you are trying to access", 401);
return res.json(guild);
@@ -32,20 +37,27 @@ router.get("/", async (req: Request, res: Response) => {
router.patch("/", check(GuildUpdateSchema), async (req: Request, res: Response) => {
const body = req.body as GuildUpdateSchema;
- const guild_id = BigInt(req.params.id);
-
- const guild = await GuildModel.findOne({ id: guild_id }).exec();
- if (!guild) throw new HTTPError("This guild does not exist", 404);
+ const { guild_id } = req.params;
+ // TODO: guild update check image
const perms = await getPermission(req.user_id, guild_id);
if (!perms.has("MANAGE_GUILD")) throw new HTTPError("You do not have the MANAGE_GUILD permission", 401);
- await GuildModel.updateOne({ id: guild_id }, body).exec();
- return res.status(204);
+ const guild = await GuildModel.findOneAndUpdate({ id: guild_id }, body)
+ .populate({ path: "joined_at", match: { id: req.user_id } })
+ .exec();
+
+ const data = toObject(guild);
+
+ emitEvent({ event: "GUILD_UPDATE", data: data, guild_id } as GuildUpdateEvent);
+
+ return res.send(data);
});
-router.delete("/", async (req: Request, res: Response) => {
- var guild_id = BigInt(req.params.id);
+// discord prefixes this route with /delete instead of using the delete method
+// docs are wrong https://discord.com/developers/docs/resources/guild#delete-guild
+router.post("/delete", async (req: Request, res: Response) => {
+ var { guild_id } = req.params;
const guild = await GuildModel.findOne({ id: guild_id }, "owner_id").exec();
if (!guild) throw new HTTPError("This guild does not exist", 404);
@@ -67,7 +79,7 @@ router.delete("/", async (req: Request, res: Response) => {
await InviteModel.deleteMany({ guild_id }).exec();
await MessageModel.deleteMany({ guild_id }).exec();
- return res.status(204).send();
+ return res.sendStatus(204);
});
export default router;
diff --git a/src/routes/api/v8/guilds/#id/members.ts b/src/routes/guilds/#guild_id/members.ts
index 0aed61ae..f4e6d4e8 100644
--- a/src/routes/api/v8/guilds/#id/members.ts
+++ b/src/routes/guilds/#guild_id/members.ts
@@ -1,21 +1,21 @@
import { Request, Response, Router } from "express";
-import { GuildModel, MemberModel } from "fosscord-server-util";
+import { GuildModel, MemberModel, toObject } from "fosscord-server-util";
import { HTTPError } from "lambert-server";
-import { instanceOf, Length } from "../../../../../util/instanceOf";
-import { PublicMemberProjection } from "../../../../../util/Member";
-import { PublicUserProjection } from "../../../../../util/User";
+import { instanceOf, Length } from "../../../util/instanceOf";
+import { PublicMemberProjection } from "../../../util/Member";
+import { PublicUserProjection } from "../../../util/User";
const router = Router();
// TODO: not allowed for user -> only allowed for bots with privileged intents
// TODO: send over websocket
router.get("/", async (req: Request, res: Response) => {
- const guild_id = BigInt(req.params.id);
+ const { guild_id } = req.params;
const guild = await GuildModel.findOne({ id: guild_id }).exec();
if (!guild) throw new HTTPError("Guild not found", 404);
try {
- instanceOf({ $limit: new Length(Number, 1, 1000), $after: BigInt }, req.query, {
+ instanceOf({ $limit: new Length(Number, 1, 1000), $after: String }, req.query, {
path: "query",
req,
ref: { obj: null, key: "" },
@@ -26,22 +26,21 @@ router.get("/", async (req: Request, res: Response) => {
// @ts-ignore
if (!req.query.limit) req.query.limit = 1;
- const { limit, after } = (<unknown>req.query) as { limit: number; after: bigint };
+ const { limit, after } = (<unknown>req.query) as { limit: number; after: string };
const query = after ? { id: { $gt: after } } : {};
var members = await MemberModel.find({ guild_id, ...query }, PublicMemberProjection)
.limit(limit)
- .populate({ path: "user", select: PublicUserProjection })
.exec();
- return res.json(members);
+ return res.json(toObject(members));
});
router.get("/:member", async (req: Request, res: Response) => {
- const guild_id = BigInt(req.params.id);
- const user_id = BigInt(req.params.member);
+ const { guild_id } = req.params;
+ const user_id = req.params.member;
- const member = await MemberModel.findOne({ id: user_id, guild_id }).populate({ path: "user", select: PublicUserProjection }).exec();
+ const member = await MemberModel.findOne({ id: user_id, guild_id }).exec();
if (!member) throw new HTTPError("Member not found", 404);
return res.json(member);
diff --git a/src/routes/api/v8/guilds/index.ts b/src/routes/guilds/index.ts
index 319184ad..57d7ddc4 100644
--- a/src/routes/api/v8/guilds/index.ts
+++ b/src/routes/guilds/index.ts
@@ -1,11 +1,11 @@
import { Router, Request, Response } from "express";
import { RoleModel, GuildModel, Snowflake, Guild } from "fosscord-server-util";
import { HTTPError } from "lambert-server";
-import { check } from "./../../../../util/instanceOf";
-import { GuildCreateSchema } from "../../../../schema/Guild";
-import Config from "../../../../util/Config";
-import { getPublicUser } from "../../../../util/User";
-import { addMember } from "../../../../util/Member";
+import { check } from "./../../util/instanceOf";
+import { GuildCreateSchema } from "../../schema/Guild";
+import Config from "../../util/Config";
+import { getPublicUser } from "../../util/User";
+import { addMember } from "../../util/Member";
const router: Router = Router();
@@ -29,11 +29,11 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) =
afk_timeout: 300,
application_id: undefined,
banner: undefined,
- default_message_notifications: undefined,
+ default_message_notifications: 0,
description: undefined,
splash: undefined,
discovery_splash: undefined,
- explicit_content_filter: undefined,
+ explicit_content_filter: 0,
features: [],
id: guild_id,
large: undefined,
@@ -48,11 +48,11 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) =
premium_tier: 0,
public_updates_channel_id: undefined,
rules_channel_id: undefined,
- system_channel_flags: undefined,
+ system_channel_flags: 0,
system_channel_id: undefined,
unavailable: false,
vanity_url_code: undefined,
- verification_level: undefined,
+ verification_level: 0,
welcome_screen: [],
widget_channel_id: undefined,
widget_enabled: false,
diff --git a/src/routes/api/v8/guilds/templates/index.ts b/src/routes/guilds/templates/index.ts
index 9a4e81fa..9a4e81fa 100644
--- a/src/routes/api/v8/guilds/templates/index.ts
+++ b/src/routes/guilds/templates/index.ts
diff --git a/src/routes/invites/index.ts b/src/routes/invites/index.ts
new file mode 100644
index 00000000..c40fb0af
--- /dev/null
+++ b/src/routes/invites/index.ts
@@ -0,0 +1,28 @@
+import { Router, Request, Response } from "express";
+import { getPermission, InviteModel, toObject } from "fosscord-server-util";
+import { HTTPError } from "lambert-server";
+const router: Router = Router();
+
+router.get("/:invite_code", async (req: Request, res: Response) => {
+ const { invite_code: code } = req.params;
+
+ const invite = await InviteModel.findOne({ code }).exec();
+
+ if (!invite) throw new HTTPError("Unknown Invite", 404);
+ res.status(200).send({ invite: toObject(invite) });
+});
+
+router.delete("/:invite_code", async (req: Request, res: Response) => {
+ const { invite_code: code } = req.params;
+ const invite = await InviteModel.findOne({ code }).exec();
+
+ if (!invite) throw new HTTPError("Unknown Invite", 404);
+
+ const { guild_id, channel_id } = invite;
+ const perms = await getPermission(req.user_id, guild_id, channel_id);
+
+ if (!perms.has("MANAGE_GUILD") || !perms.has("MANAGE_CHANNELS")) throw new HTTPError("You aren't allow", 401);
+ res.status(200).send({ invite: toObject(invite) });
+});
+
+export default router;
diff --git a/src/routes/science.ts b/src/routes/science.ts
new file mode 100644
index 00000000..ab3ce58c
--- /dev/null
+++ b/src/routes/science.ts
@@ -0,0 +1,10 @@
+import { Router } from "express";
+
+const router = Router();
+
+router.post("/", (req, res) => {
+ // TODO:
+ res.sendStatus(204);
+});
+
+export default router;
diff --git a/src/routes/test.ts b/src/routes/test.ts
deleted file mode 100644
index b488d6e4..00000000
--- a/src/routes/test.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Router } from "express";
-import { getPermission, MemberModel, db } from "fosscord-server-util";
-import { Types } from "mongoose";
-const router: Router = Router();
-
-router.get("/", async (req, res) => {
- // @ts-ignore
- const perm = await getPermission(813185668657184768n, 813189959920910336n);
- console.log(perm);
- if (perm.has("ADD_REACTIONS")) console.log("add");
- res.send("OK");
-});
-
-export default router;
diff --git a/src/routes/users/@me/affinities/guilds.ts b/src/routes/users/@me/affinities/guilds.ts
new file mode 100644
index 00000000..ea0fe59d
--- /dev/null
+++ b/src/routes/users/@me/affinities/guilds.ts
@@ -0,0 +1,10 @@
+import { Router } from "express";
+
+const router = Router();
+
+router.get("/", (req, res) => {
+ // TODO:
+ res.status(200).send({ guild_affinities: [] });
+});
+
+export default router;
diff --git a/src/routes/api/v8/users/@me/guilds.ts b/src/routes/users/@me/guilds.ts
index a2a64ce6..d0fbaa3e 100644
--- a/src/routes/api/v8/users/@me/guilds.ts
+++ b/src/routes/users/@me/guilds.ts
@@ -1,8 +1,8 @@
import { Router, Request, Response } from "express";
-import { GuildModel, MemberModel, UserModel, GuildDeleteEvent, GuildMemberRemoveEvent } from "fosscord-server-util";
+import { GuildModel, MemberModel, UserModel, GuildDeleteEvent, GuildMemberRemoveEvent, toObject } from "fosscord-server-util";
import { HTTPError } from "lambert-server";
-import { emitEvent } from "../../../../../util/Event";
-import { getPublicUser } from "../../../../../util/User";
+import { emitEvent } from "../../../util/Event";
+import { getPublicUser } from "../../../util/User";
const router: Router = Router();
@@ -11,37 +11,42 @@ router.get("/", async (req: Request, res: Response) => {
if (!user) throw new HTTPError("User not found", 404);
var guildIDs = user.guilds || [];
- var guild = await GuildModel.find({ id: { $in: guildIDs } }).exec();
- res.json(guild);
+ var guild = await GuildModel.find({ id: { $in: guildIDs } })
+ .populate({ path: "joined_at", match: { id: req.user_id } })
+ .exec();
+
+ res.json(toObject(guild));
});
// user send to leave a certain guild
router.delete("/:id", async (req: Request, res: Response) => {
- const guildID = BigInt(req.params.id);
- const guild = await GuildModel.findOne({ id: guildID }).exec();
+ const guild_id = req.params.id;
+ const guild = await GuildModel.findOne({ id: guild_id }, { guild_id: true }).exec();
if (!guild) throw new HTTPError("Guild doesn't exist", 404);
if (guild.owner_id === req.user_id) throw new HTTPError("You can't leave your own guild", 400);
- await MemberModel.deleteOne({ id: req.user_id, guild_id: guildID }).exec();
- await UserModel.updateOne({ id: req.user_id }, { $pull: { guilds: guildID } }).exec();
- const user = await getPublicUser(req.user_id);
+ await Promise.all([
+ MemberModel.deleteOne({ id: req.user_id, guild_id: guild_id }).exec(),
+ UserModel.updateOne({ id: req.user_id }, { $pull: { guilds: guild_id } }).exec(),
+ emitEvent({
+ event: "GUILD_DELETE",
+ data: {
+ id: guild_id,
+ },
+ user_id: req.user_id,
+ } as GuildDeleteEvent),
+ ]);
- await emitEvent({
- event: "GUILD_DELETE",
- data: {
- id: guildID,
- },
- user_id: req.user_id,
- } as GuildDeleteEvent);
+ const user = await getPublicUser(req.user_id);
await emitEvent({
event: "GUILD_MEMBER_REMOVE",
data: {
- guild_id: guildID,
+ guild_id: guild_id,
user: user,
},
- guild_id: guildID,
+ guild_id: guild_id,
} as GuildMemberRemoveEvent);
return res.status(204).send();
diff --git a/src/routes/api/v8/users/@me/index.ts b/src/routes/users/@me/index.ts
index 32877dcc..32877dcc 100644
--- a/src/routes/api/v8/users/@me/index.ts
+++ b/src/routes/users/@me/index.ts
diff --git a/src/routes/users/@me/library.ts b/src/routes/users/@me/library.ts
new file mode 100644
index 00000000..2ffff851
--- /dev/null
+++ b/src/routes/users/@me/library.ts
@@ -0,0 +1,10 @@
+import { Router } from "express";
+
+const router = Router();
+
+router.get("/", (req, res) => {
+ // TODO:
+ res.status(200).send([]);
+});
+
+export default router;
diff --git a/src/routes/users/@me/settings.ts b/src/routes/users/@me/settings.ts
new file mode 100644
index 00000000..f1d95caf
--- /dev/null
+++ b/src/routes/users/@me/settings.ts
@@ -0,0 +1,10 @@
+import { Router } from "express";
+
+const router = Router();
+
+router.patch("/", (req, res) => {
+ // TODO:
+ res.sendStatus(204);
+});
+
+export default router;
|