diff --git a/src/routes/auth/login.ts b/src/routes/auth/login.ts
deleted file mode 100644
index c3661608..00000000
--- a/src/routes/auth/login.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-import { Request, Response, Router } from "express";
-import { check, FieldErrors, Length } from "../../util/instanceOf";
-import bcrypt from "bcrypt";
-import jwt from "jsonwebtoken";
-import { Config, UserModel } from "@fosscord/server-util";
-import { adjustEmail } from "./register";
-import RateLimit from "../../middlewares/RateLimit";
-
-const router: Router = Router();
-export default router;
-
-// TODO: check if user is deleted --> prohibit login
-
-router.post(
- "/",
- check({
- login: new Length(String, 2, 100), // email or telephone
- password: new Length(String, 8, 72),
- $undelete: Boolean,
- $captcha_key: String,
- $login_source: String,
- $gift_code_sku_id: String
- }),
- async (req: Request, res: Response) => {
- const { login, password, captcha_key, undelete } = req.body;
- const email = adjustEmail(login);
- const query: any[] = [{ phone: login }];
- if (email) query.push({ email });
-
- // TODO: Rewrite this to have the proper config syntax on the new method
-
- const config = Config.get();
-
- if (config.login.requireCaptcha && config.security.captcha.enabled) {
- if (!captcha_key) {
- const { sitekey, service } = config.security.captcha;
- return res.status(400).json({
- captcha_key: ["captcha-required"],
- captcha_sitekey: sitekey,
- captcha_service: service
- });
- }
-
- // TODO: check captcha
- }
-
- const user = await UserModel.findOne(
- { $or: query },
- { user_data: { hash: true }, id: true, disabled: true, deleted: true, user_settings: { locale: true, theme: true } }
- )
- .exec()
- .catch((e) => {
- throw FieldErrors({ login: { message: req.t("auth:login.INVALID_LOGIN"), code: "INVALID_LOGIN" } });
- });
-
- if (undelete) {
- // undelete refers to un'disable' here
- if (user.disabled) await UserModel.updateOne({ id: user.id }, { disabled: false }).exec();
- if (user.deleted) await UserModel.updateOne({ id: user.id }, { deleted: false }).exec();
- } else {
- if (user.deleted) return res.status(400).json({ message: "This account is scheduled for deletion.", code: 20011 });
- if (user.disabled) return res.status(400).json({ message: req.t("auth:login.ACCOUNT_DISABLED"), code: 20013 });
- }
-
- // the salt is saved in the password refer to bcrypt docs
- 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" } });
- }
-
- const token = await generateToken(user.id);
-
- // Notice this will have a different token structure, than discord
- // Discord header is just the user id as string, which is not possible with npm-jsonwebtoken package
- // https://user-images.githubusercontent.com/6506416/81051916-dd8c9900-8ec2-11ea-8794-daf12d6f31f0.png
-
- res.json({ token, user_settings: user.user_settings });
- }
-);
-
-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 },
- Config.get().security.jwtSecret,
- {
- algorithm
- },
- (err, token) => {
- if (err) return rej(err);
- return res(token);
- }
- );
- });
-}
-
-/**
- * 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"}
-
- * Captcha required:
- * @returns {"captcha_key": ["captcha-required"], "captcha_sitekey": null, "captcha_service": "recaptcha"}
-
- * Sucess:
- * @returns {"token": "USERTOKEN", "user_settings": {"locale": "en", "theme": "dark"}}
-
- */
diff --git a/src/routes/auth/register.ts b/src/routes/auth/register.ts
deleted file mode 100644
index 66a1fc8d..00000000
--- a/src/routes/auth/register.ts
+++ /dev/null
@@ -1,309 +0,0 @@
-import { Request, Response, Router } from "express";
-import { trimSpecial, User, Snowflake, UserModel, Config } from "@fosscord/server-util";
-import bcrypt from "bcrypt";
-import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../util/instanceOf";
-import "missing-native-js-functions";
-import { generateToken } from "./login";
-import { getIpAdress, IPAnalysis, isProxy } from "../../util/ipAddress";
-import { HTTPError } from "lambert-server";
-import RateLimit from "../../middlewares/RateLimit";
-
-const router: Router = Router();
-
-router.post(
- "/",
- check({
- username: new Length(String, 2, 32),
- // TODO: check min password length in config
- // prevent Denial of Service with max length of 72 chars
- password: new Length(String, 8, 72),
- consent: Boolean,
- $email: new Length(Email, 5, 100),
- $fingerprint: String,
- $invite: String,
- $date_of_birth: Date, // "2000-04-03"
- $gift_code_sku_id: String,
- $captcha_key: String
- }),
- async (req: Request, res: Response) => {
- const {
- email,
- username,
- password,
- consent,
- fingerprint,
- invite,
- date_of_birth,
- gift_code_sku_id, // ? what is this
- captcha_key
- } = req.body;
-
- // get register Config
- const { register, security } = Config.get();
- const ip = getIpAdress(req);
-
- if (register.blockProxies) {
- if (isProxy(await IPAnalysis(ip))) {
- console.log(`proxy ${ip} blocked from registration`);
- throw new HTTPError("Your IP is blocked from registration");
- }
- }
-
- console.log("register", req.body.email, req.body.username, ip);
- // TODO: automatically join invite
- // TODO: gift_code_sku_id?
- // TODO: check password strength
-
- // adjusted_email will be slightly modified version of the user supplied email -> e.g. protection against GMail Trick
- let adjusted_email: string | null = adjustEmail(email);
-
- // adjusted_password will be the hash of the password
- let adjusted_password: string = "";
-
- // trim special uf8 control characters -> Backspace, Newline, ...
- let adjusted_username: string = trimSpecial(username);
-
- // discriminator will be randomly generated
- let discriminator = "";
-
- // check if registration is allowed
- if (!register.allowNewRegistration) {
- throw FieldErrors({
- email: { code: "REGISTRATION_DISABLED", message: req.t("auth:register.REGISTRATION_DISABLED") }
- });
- }
-
- // check if the user agreed to the Terms of Service
- if (!consent) {
- throw FieldErrors({
- consent: { code: "CONSENT_REQUIRED", message: req.t("auth:register.CONSENT_REQUIRED") }
- });
- }
-
- // require invite to register -> e.g. for organizations to send invites to their employees
- if (register.requireInvite && !invite) {
- throw FieldErrors({
- email: { code: "INVITE_ONLY", message: req.t("auth:register.INVITE_ONLY") }
- });
- }
-
- if (email) {
- // replace all dots and chars after +, if its a gmail.com email
- if (!adjusted_email) throw FieldErrors({ email: { code: "INVALID_EMAIL", message: req.t("auth:register.INVALID_EMAIL") } });
-
- // check if there is already an account with this email
- const exists = await UserModel.findOne({ email: adjusted_email })
- .exec()
- .catch((e) => {});
-
- if (exists) {
- throw FieldErrors({
- email: {
- code: "EMAIL_ALREADY_REGISTERED",
- message: req.t("auth:register.EMAIL_ALREADY_REGISTERED")
- }
- });
- }
- } else if (register.email.necessary) {
- throw FieldErrors({
- email: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
- });
- }
-
- if (register.dateOfBirth.necessary && !date_of_birth) {
- throw FieldErrors({
- date_of_birth: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
- });
- } else if (register.dateOfBirth.minimum) {
- const minimum = new Date();
- minimum.setFullYear(minimum.getFullYear() - register.dateOfBirth.minimum);
-
- // higher is younger
- if (date_of_birth > minimum) {
- throw FieldErrors({
- date_of_birth: {
- code: "DATE_OF_BIRTH_UNDERAGE",
- message: req.t("auth:register.DATE_OF_BIRTH_UNDERAGE", { years: register.dateOfBirth.minimum })
- }
- });
- }
- }
-
- if (!register.allowMultipleAccounts) {
- // TODO: check if fingerprint was eligible generated
- const exists = await UserModel.findOne({ fingerprints: fingerprint })
- .exec()
- .catch((e) => {});
-
- if (exists) {
- throw FieldErrors({
- email: {
- code: "EMAIL_ALREADY_REGISTERED",
- message: req.t("auth:register.EMAIL_ALREADY_REGISTERED")
- }
- });
- }
- }
-
- if (register.requireCaptcha && security.captcha.enabled) {
- if (!captcha_key) {
- const { sitekey, service } = security.captcha;
- return res.status(400).json({
- captcha_key: ["captcha-required"],
- captcha_sitekey: sitekey,
- captcha_service: service
- });
- }
-
- // TODO: check captcha
- }
-
- // the salt is saved in the password refer to bcrypt docs
- adjusted_password = await bcrypt.hash(password, 12);
-
- let exists;
- // randomly generates a discriminator between 1 and 9999 and checks max five times if it already exists
- // if it all five times already exists, abort with USERNAME_TOO_MANY_USERS error
- // else just continue
- // 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");
- try {
- exists = await UserModel.findOne({ discriminator, username: adjusted_username }, "id").exec();
- } catch (error) {
- // doesn't exist -> break
- break;
- }
- }
-
- if (exists) {
- throw FieldErrors({
- username: {
- code: "USERNAME_TOO_MANY_USERS",
- message: req.t("auth:register.USERNAME_TOO_MANY_USERS")
- }
- });
- }
-
- // 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(),
- username: adjusted_username,
- discriminator,
- avatar: null,
- accent_color: null,
- banner: null,
- bot: false,
- system: false,
- desktop: false,
- mobile: false,
- premium: true,
- premium_type: 2,
- phone: null,
- bio: "",
- mfa_enabled: false,
- verified: false,
- disabled: false,
- deleted: 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
- guilds: [],
- user_data: {
- hash: adjusted_password,
- valid_tokens_since: new Date(),
- relationships: [],
- connected_accounts: [],
- fingerprints: []
- },
- user_settings: {
- afk_timeout: 300,
- allow_accessibility_detection: true,
- animate_emoji: true,
- animate_stickers: 0,
- contact_sync_enabled: false,
- convert_emoticons: false,
- custom_status: {
- emoji_id: null,
- emoji_name: null,
- expires_at: null,
- text: null
- },
- default_guilds_restricted: false,
- detect_platform_accounts: true,
- developer_mode: false,
- disable_games_tab: false,
- enable_tts_command: true,
- explicit_content_filter: 0,
- friend_source_flags: { all: true },
- gateway_connected: false,
- gif_auto_play: true,
- guild_folders: [],
- guild_positions: [],
- inline_attachment_media: true,
- inline_embed_media: true,
- locale: req.language,
- message_display_compact: false,
- native_phone_integration_enabled: true,
- render_embeds: true,
- render_reactions: true,
- restricted_guilds: [],
- show_current_game: true,
- status: "offline",
- stream_notifications_enabled: true,
- theme: "dark",
- timezone_offset: 0
- // timezone_offset: // TODO: timezone from request
- }
- };
-
- // insert user into database
- await new UserModel(user).save();
-
- return res.json({ token: await generateToken(user.id) });
- }
-);
-
-export function adjustEmail(email: string): string | null {
- // body parser already checked if it is a valid email
- const parts = <RegExpMatchArray>email.match(EMAIL_REGEX);
- // @ts-ignore
- if (!parts || parts.length < 5) return undefined;
- const domain = parts[5];
- const user = parts[1];
-
- // TODO: check accounts with uncommon email domains
- if (domain === "gmail.com" || domain === "googlemail.com") {
- // replace .dots and +alternatives -> Gmail Dot Trick https://support.google.com/mail/answer/7436150 and https://generator.email/blog/gmail-generator
- return user.replace(/[.]|(\+.*)/g, "") + "@gmail.com";
- }
-
- return email;
-}
-
-export default router;
-
-/**
- * POST /auth/register
- * @argument { "fingerprint":"805826570869932034.wR8vi8lGlFBJerErO9LG5NViJFw", "email":"qo8etzvaf@gmail.com", "username":"qp39gr98", "password":"wtp9gep9gw", "invite":null, "consent":true, "date_of_birth":"2000-04-04", "gift_code_sku_id":null, "captcha_key":null}
- *
- * Field Error
- * @returns { "code": 50035, "errors": { "consent": { "_errors": [{ "code": "CONSENT_REQUIRED", "message": "You must agree to Discord's Terms of Service and Privacy Policy." }]}}, "message": "Invalid Form Body"}
- *
- * Success 201:
- * @returns {token: "OMITTED"}
- */
diff --git a/src/routes/channels/#channel_id/followers.ts b/src/routes/channels/#channel_id/followers.ts
deleted file mode 100644
index 641af4f8..00000000
--- a/src/routes/channels/#channel_id/followers.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Router, Response, Request } from "express";
-const router: Router = Router();
-// TODO:
-
-export default router;
-
-/**
- *
- * @param {"webhook_channel_id":"754001514330062952"}
- *
- * Creates a WebHook in the channel and returns the id of it
- *
- * @returns {"channel_id": "816382962056560690", "webhook_id": "834910735095037962"}
- */
diff --git a/src/routes/channels/#channel_id/index.ts b/src/routes/channels/#channel_id/index.ts
deleted file mode 100644
index 81e5054e..00000000
--- a/src/routes/channels/#channel_id/index.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import { ChannelDeleteEvent, ChannelModel, ChannelUpdateEvent, getPermission, GuildUpdateEvent, toObject } from "@fosscord/server-util";
-import { Router, Response, Request } from "express";
-import { HTTPError } from "lambert-server";
-import { ChannelModifySchema } from "../../../schema/Channel";
-import { emitEvent } from "../../../util/Event";
-import { check } from "../../../util/instanceOf";
-const router: Router = Router();
-// TODO: delete channel
-// TODO: Get channel
-
-router.get("/", async (req: Request, res: Response) => {
- const { channel_id } = req.params;
-
- const channel = await ChannelModel.findOne({ id: channel_id }).exec();
-
- const permission = await getPermission(req.user_id, channel.guild_id, channel_id);
- permission.hasThrow("VIEW_CHANNEL");
-
- return res.send(toObject(channel));
-});
-
-router.delete("/", async (req: Request, res: Response) => {
- const { channel_id } = req.params;
-
- const channel = await ChannelModel.findOne({ id: channel_id }).exec();
-
- const permission = await getPermission(req.user_id, channel?.guild_id, channel_id, { channel });
- permission.hasThrow("MANAGE_CHANNELS");
-
- // TODO: Dm channel "close" not delete
- const data = toObject(channel);
-
- await emitEvent({ event: "CHANNEL_DELETE", data, channel_id } as ChannelDeleteEvent);
-
- await ChannelModel.deleteOne({ id: channel_id });
-
- res.send(data);
-});
-
-router.patch("/", check(ChannelModifySchema), async (req: Request, res: Response) => {
- var payload = req.body as ChannelModifySchema;
- const { channel_id } = req.params;
-
- const permission = await getPermission(req.user_id, undefined, channel_id);
- permission.hasThrow("MANAGE_CHANNELS");
-
- const channel = await ChannelModel.findOneAndUpdate({ id: channel_id }, payload).exec();
-
- const data = toObject(channel);
-
- await emitEvent({
- event: "CHANNEL_UPDATE",
- data,
- channel_id
- } as ChannelUpdateEvent);
-
- res.send(data);
-});
-
-export default router;
diff --git a/src/routes/channels/#channel_id/invites.ts b/src/routes/channels/#channel_id/invites.ts
deleted file mode 100644
index c9db4dd2..00000000
--- a/src/routes/channels/#channel_id/invites.ts
+++ /dev/null
@@ -1,65 +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, 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.guild_id) {
- throw new HTTPError("This channel doesn't exist", 404);
- }
- const { guild_id } = channel;
-
- const permission = await getPermission(user_id, guild_id);
- permission.hasThrow("CREATE_INSTANT_INVITE");
-
- const expires_at = new Date(req.body.max_age * 1000 + Date.now());
-
- const invite = {
- code: random(),
- temporary: req.body.temporary,
- uses: 0,
- max_uses: req.body.max_uses,
- max_age: req.body.max_age,
- expires_at,
- 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.guild_id) {
- throw new HTTPError("This channel doesn't exist", 404);
- }
- const { guild_id } = channel;
- const permission = await getPermission(user_id, guild_id);
- permission.hasThrow("MANAGE_CHANNELS");
-
- const invites = await InviteModel.find({ guild_id }).exec();
-
- res.status(200).send(toObject(invites));
-});
-
-export default router;
diff --git a/src/routes/channels/#channel_id/messages/#message_id/ack.ts b/src/routes/channels/#channel_id/messages/#message_id/ack.ts
deleted file mode 100644
index f4d9e696..00000000
--- a/src/routes/channels/#channel_id/messages/#message_id/ack.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { getPermission, MessageAckEvent, ReadStateModel } from "@fosscord/server-util";
-import { Request, Response, Router } from "express";
-import { emitEvent } from "../../../../../util/Event";
-import { check } from "../../../../../util/instanceOf";
-
-const router = Router();
-
-// TODO: check if message exists
-// TODO: send read state event to all channel members
-
-router.post("/", check({ $manual: Boolean, $mention_count: Number }), async (req: Request, res: Response) => {
- const { channel_id, message_id } = req.params;
-
- const permission = await getPermission(req.user_id, undefined, channel_id);
- permission.hasThrow("VIEW_CHANNEL");
-
- await ReadStateModel.updateOne(
- { user_id: req.user_id, channel_id, message_id },
- { user_id: req.user_id, channel_id, message_id }
- ).exec();
-
- await emitEvent({
- event: "MESSAGE_ACK",
- user_id: req.user_id,
- data: {
- channel_id,
- message_id,
- version: 496
- }
- } as MessageAckEvent);
-
- res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/channels/#channel_id/messages/#message_id/crosspost.ts b/src/routes/channels/#channel_id/messages/#message_id/crosspost.ts
deleted file mode 100644
index 6753e832..00000000
--- a/src/routes/channels/#channel_id/messages/#message_id/crosspost.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { Router, Response, Request } from "express";
-
-const router = Router();
-
-// TODO:
-// router.post("/", (req: Request, res: Response) => {});
-
-export default router;
diff --git a/src/routes/channels/#channel_id/messages/#message_id/index.ts b/src/routes/channels/#channel_id/messages/#message_id/index.ts
deleted file mode 100644
index a7c23d2f..00000000
--- a/src/routes/channels/#channel_id/messages/#message_id/index.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { ChannelModel, getPermission, MessageDeleteEvent, MessageModel, MessageUpdateEvent, toObject } from "@fosscord/server-util";
-import { Router, Response, Request } from "express";
-import { HTTPError } from "lambert-server";
-import { MessageCreateSchema } from "../../../../../schema/Message";
-import { emitEvent } from "../../../../../util/Event";
-import { check } from "../../../../../util/instanceOf";
-import { handleMessage, postHandleMessage } from "../../../../../util/Message";
-
-const router = Router();
-
-router.patch("/", check(MessageCreateSchema), async (req: Request, res: Response) => {
- const { message_id, channel_id } = req.params;
- var body = req.body as MessageCreateSchema;
-
- var message = await MessageModel.findOne({ id: message_id, channel_id }, { author_id: true }).exec();
-
- const permissions = await getPermission(req.user_id, undefined, channel_id);
-
- if (req.user_id !== message.author_id) {
- permissions.hasThrow("MANAGE_MESSAGES");
- body = { flags: body.flags };
- }
-
- const opts = await handleMessage({
- ...body,
- author_id: message.author_id,
- channel_id,
- id: message_id,
- edited_timestamp: new Date()
- });
-
- // @ts-ignore
- message = await MessageModel.findOneAndUpdate({ id: message_id }, opts).populate("author").exec();
-
- await emitEvent({
- event: "MESSAGE_UPDATE",
- channel_id,
- data: { ...toObject(message), nonce: undefined }
- } as MessageUpdateEvent);
-
- postHandleMessage(message);
-
- return res.json(toObject(message));
-});
-
-// TODO: delete attachments in message
-
-router.delete("/", async (req: Request, res: Response) => {
- const { message_id, channel_id } = req.params;
-
- const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true });
- const message = await MessageModel.findOne({ id: message_id }, { author_id: true }).exec();
-
- const permission = await getPermission(req.user_id, channel.guild_id, channel_id);
- if (message.author_id !== req.user_id) permission.hasThrow("MANAGE_MESSAGES");
-
- await MessageModel.deleteOne({ id: message_id }).exec();
-
- await emitEvent({
- event: "MESSAGE_DELETE",
- channel_id,
- data: {
- id: message_id,
- channel_id,
- guild_id: channel.guild_id
- }
- } as MessageDeleteEvent);
-
- res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/channels/#channel_id/messages/#message_id/reactions.ts b/src/routes/channels/#channel_id/messages/#message_id/reactions.ts
deleted file mode 100644
index 168a870f..00000000
--- a/src/routes/channels/#channel_id/messages/#message_id/reactions.ts
+++ /dev/null
@@ -1,191 +0,0 @@
-import {
- ChannelModel,
- EmojiModel,
- getPermission,
- MemberModel,
- MessageModel,
- MessageReactionAddEvent,
- MessageReactionRemoveAllEvent,
- MessageReactionRemoveEmojiEvent,
- MessageReactionRemoveEvent,
- PartialEmoji,
- PublicUserProjection,
- toObject,
- UserModel
-} from "@fosscord/server-util";
-import { Router, Response, Request } from "express";
-import { HTTPError } from "lambert-server";
-import { emitEvent } from "../../../../../util/Event";
-
-const router = Router();
-// TODO: check if emoji is really an unicode emoji or a prperly encoded external emoji
-
-function getEmoji(emoji: string): PartialEmoji {
- emoji = decodeURIComponent(emoji);
- const parts = emoji.includes(":") && emoji.split(":");
- if (parts)
- return {
- name: parts[0],
- id: parts[1]
- };
-
- return {
- id: undefined,
- name: emoji
- };
-}
-
-router.delete("/", async (req: Request, res: Response) => {
- const { message_id, channel_id } = req.params;
-
- const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true }).exec();
-
- const permissions = await getPermission(req.user_id, undefined, channel_id);
- permissions.hasThrow("MANAGE_MESSAGES");
-
- await MessageModel.findOneAndUpdate({ id: message_id, channel_id }, { reactions: [] }).exec();
-
- await emitEvent({
- event: "MESSAGE_REACTION_REMOVE_ALL",
- channel_id,
- data: {
- channel_id,
- message_id,
- guild_id: channel.guild_id
- }
- } as MessageReactionRemoveAllEvent);
-
- res.sendStatus(204);
-});
-
-router.delete("/:emoji", async (req: Request, res: Response) => {
- const { message_id, channel_id } = req.params;
- const emoji = getEmoji(req.params.emoji);
-
- const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true }).exec();
-
- const permissions = await getPermission(req.user_id, undefined, channel_id);
- permissions.hasThrow("MANAGE_MESSAGES");
-
- const message = await MessageModel.findOne({ id: message_id, channel_id }).exec();
-
- const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
- if (!already_added) throw new HTTPError("Reaction not found", 404);
- message.reactions.remove(already_added);
-
- await MessageModel.updateOne({ id: message_id, channel_id }, message).exec();
-
- await emitEvent({
- event: "MESSAGE_REACTION_REMOVE_EMOJI",
- channel_id,
- data: {
- channel_id,
- message_id,
- guild_id: channel.guild_id,
- emoji
- }
- } as MessageReactionRemoveEmojiEvent);
-
- res.sendStatus(204);
-});
-
-router.get("/:emoji", async (req: Request, res: Response) => {
- const { message_id, channel_id } = req.params;
- const emoji = getEmoji(req.params.emoji);
-
- const message = await MessageModel.findOne({ id: message_id, channel_id }).exec();
- if (!message) throw new HTTPError("Message not found", 404);
- const reaction = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
- if (!reaction) throw new HTTPError("Reaction not found", 404);
-
- const permissions = await getPermission(req.user_id, undefined, channel_id);
- permissions.hasThrow("VIEW_CHANNEL");
-
- const users = await UserModel.find({ id: { $in: reaction.user_ids } }, PublicUserProjection).exec();
-
- res.json(toObject(users));
-});
-
-router.put("/:emoji/:user_id", async (req: Request, res: Response) => {
- const { message_id, channel_id, user_id } = req.params;
- if (user_id !== "@me") throw new HTTPError("Invalid user");
- const emoji = getEmoji(req.params.emoji);
-
- const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true }).exec();
- const message = await MessageModel.findOne({ id: message_id, channel_id }).exec();
- const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
-
- const permissions = await getPermission(req.user_id, undefined, channel_id);
- permissions.hasThrow("READ_MESSAGE_HISTORY");
- if (!already_added) permissions.hasThrow("ADD_REACTIONS");
-
- if (emoji.id) {
- const external_emoji = await EmojiModel.findOne({ id: emoji.id }).exec();
- if (!already_added) permissions.hasThrow("USE_EXTERNAL_EMOJIS");
- emoji.animated = external_emoji.animated;
- emoji.name = external_emoji.name;
- }
-
- if (already_added) {
- if (already_added.user_ids.includes(req.user_id)) return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error
- already_added.count++;
- } else message.reactions.push({ count: 1, emoji, user_ids: [req.user_id] });
-
- await MessageModel.updateOne({ id: message_id, channel_id }, message).exec();
-
- const member = channel.guild_id && (await MemberModel.findOne({ id: req.user_id }).exec());
-
- await emitEvent({
- event: "MESSAGE_REACTION_ADD",
- channel_id,
- data: {
- user_id: req.user_id,
- channel_id,
- message_id,
- guild_id: channel.guild_id,
- emoji,
- member
- }
- } as MessageReactionAddEvent);
-
- res.sendStatus(204);
-});
-
-router.delete("/:emoji/:user_id", async (req: Request, res: Response) => {
- var { message_id, channel_id, user_id } = req.params;
-
- const emoji = getEmoji(req.params.emoji);
-
- const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true }).exec();
- const message = await MessageModel.findOne({ id: message_id, channel_id }).exec();
-
- const permissions = await getPermission(req.user_id, undefined, channel_id);
-
- if (user_id === "@me") user_id = req.user_id;
- else permissions.hasThrow("MANAGE_MESSAGES");
-
- const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
- if (!already_added || !already_added.user_ids.includes(user_id)) throw new HTTPError("Reaction not found", 404);
-
- already_added.count--;
-
- if (already_added.count <= 0) message.reactions.remove(already_added);
-
- await MessageModel.updateOne({ id: message_id, channel_id }, message).exec();
-
- await emitEvent({
- event: "MESSAGE_REACTION_REMOVE",
- channel_id,
- data: {
- user_id: req.user_id,
- channel_id,
- message_id,
- guild_id: channel.guild_id,
- emoji
- }
- } as MessageReactionRemoveEvent);
-
- res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/channels/#channel_id/messages/bulk-delete.ts b/src/routes/channels/#channel_id/messages/bulk-delete.ts
deleted file mode 100644
index e53cd597..00000000
--- a/src/routes/channels/#channel_id/messages/bulk-delete.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Router, Response, Request } from "express";
-import { ChannelModel, Config, getPermission, MessageDeleteBulkEvent, MessageModel } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { emitEvent } from "../../../../util/Event";
-import { check } from "../../../../util/instanceOf";
-
-const router: Router = Router();
-
-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: [String] }), async (req: Request, res: Response) => {
- const { channel_id } = req.params;
- 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);
-
- const permission = await getPermission(req.user_id, channel?.guild_id, channel_id, { channel });
- permission.hasThrow("MANAGE_MESSAGES");
-
- const { maxBulkDelete } = Config.get().limits.message;
-
- 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`);
-
- await MessageModel.deleteMany({ id: { $in: messages } }).exec();
-
- await emitEvent({
- event: "MESSAGE_DELETE_BULK",
- channel_id,
- data: { ids: messages, channel_id, guild_id: channel.guild_id }
- } as MessageDeleteBulkEvent);
-
- res.sendStatus(204);
-});
diff --git a/src/routes/channels/#channel_id/messages/index.ts b/src/routes/channels/#channel_id/messages/index.ts
deleted file mode 100644
index fea4d6a4..00000000
--- a/src/routes/channels/#channel_id/messages/index.ts
+++ /dev/null
@@ -1,146 +0,0 @@
-import { Router, Response, Request } from "express";
-import { Attachment, ChannelModel, ChannelType, getPermission, MessageDocument, MessageModel, toObject } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { MessageCreateSchema } from "../../../../schema/Message";
-import { check, instanceOf, Length } from "../../../../util/instanceOf";
-import multer from "multer";
-import { Query } from "mongoose";
-import { sendMessage } from "../../../../util/Message";
-import { uploadFile } from "../../../../util/cdn";
-
-const router: Router = Router();
-
-export default router;
-
-export function isTextChannel(type: ChannelType): boolean {
- switch (type) {
- case ChannelType.GUILD_VOICE:
- case ChannelType.GUILD_CATEGORY:
- throw new HTTPError("not a text channel", 400);
- case ChannelType.DM:
- case ChannelType.GROUP_DM:
- case ChannelType.GUILD_NEWS:
- case ChannelType.GUILD_STORE:
- case ChannelType.GUILD_TEXT:
- return true;
- }
-}
-
-// https://discord.com/developers/docs/resources/channel#create-message
-// get messages
-router.get("/", async (req: Request, res: Response) => {
- const channel_id = req.params.channel_id;
- const channel = await ChannelModel.findOne(
- { id: channel_id },
- { guild_id: true, type: true, permission_overwrites: true, recipient_ids: true, owner_id: true }
- )
- .lean() // lean is needed, because we don't want to populate .recipients that also auto deletes .recipient_ids
- .exec();
- if (!channel) throw new HTTPError("Channel not found", 404);
-
- isTextChannel(channel.type);
-
- try {
- 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?: string; after?: string; before?: string; limit?: number } = req.query;
- if (!limit) limit = 50;
- var halfLimit = Math.floor(limit / 2);
-
- // @ts-ignore
- const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel });
- permissions.hasThrow("VIEW_CHANNEL");
- if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
-
- 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: (BigInt(around) - BigInt(halfLimit)).toString(), $lt: (BigInt(around) + BigInt(halfLimit)).toString() }
- });
- else {
- query = MessageModel.find({ channel_id });
- }
-
- query = query.sort({ id: -1 });
-
- const messages = await query.limit(limit).exec();
-
- return res.json(
- toObject(messages).map((x) => {
- (x.reactions || []).forEach((x) => {
- // @ts-ignore
- if ((x.user_ids || []).includes(req.user_id)) x.me = true;
- // @ts-ignore
- delete x.user_ids;
- });
- // @ts-ignore
- if (!x.author) x.author = { discriminator: "0000", username: "Deleted User", public_flags: 0n, avatar: null };
-
- return x;
- })
- );
-});
-
-// TODO: config max upload size
-const messageUpload = multer({
- limits: {
- fileSize: 1024 * 1024 * 100,
- fields: 10,
- files: 1
- },
- storage: multer.memoryStorage()
-}); // max upload 50 mb
-
-// TODO: dynamically change limit of MessageCreateSchema with config
-// TODO: check: sum of all characters in an embed structure must not exceed 6000 characters
-
-// https://discord.com/developers/docs/resources/channel#create-message
-// TODO: text channel slowdown
-// TODO: trim and replace message content and every embed field
-// TODO: check allowed_mentions
-
-// Send message
-router.post("/", messageUpload.single("file"), async (req: Request, res: Response) => {
- const { channel_id } = req.params;
- var body = req.body as MessageCreateSchema;
- const attachments: Attachment[] = [];
-
- if (req.file) {
- try {
- const file = await uploadFile(`/attachments/${channel_id}`, req.file);
- attachments.push({ ...file, proxy_url: file.url });
- } catch (error) {
- return res.status(400).json(error);
- }
- }
-
- if (body.payload_json) {
- body = JSON.parse(body.payload_json);
- }
-
- const errors = instanceOf(MessageCreateSchema, body, { req });
- if (errors !== true) throw errors;
-
- const embeds = [];
- if (body.embed) embeds.push(body.embed);
- const data = await sendMessage({
- ...body,
- type: 0,
- pinned: false,
- author_id: req.user_id,
- embeds,
- channel_id,
- attachments,
- edited_timestamp: null
- });
-
- return res.send(data);
-});
diff --git a/src/routes/channels/#channel_id/permissions.ts b/src/routes/channels/#channel_id/permissions.ts
deleted file mode 100644
index 12364293..00000000
--- a/src/routes/channels/#channel_id/permissions.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { ChannelModel, ChannelPermissionOverwrite, ChannelUpdateEvent, getPermission, MemberModel, RoleModel } from "@fosscord/server-util";
-import { Router, Response, Request } from "express";
-import { HTTPError } from "lambert-server";
-import { emitEvent } from "../../../util/Event";
-import { check } from "../../../util/instanceOf";
-const router: Router = Router();
-
-// TODO: Only permissions your bot has in the guild or channel can be allowed/denied (unless your bot has a MANAGE_ROLES overwrite in the channel)
-
-router.put("/:overwrite_id", check({ allow: String, deny: String, type: Number, id: String }), async (req: Request, res: Response) => {
- const { channel_id, overwrite_id } = req.params;
- const body = req.body as { allow: bigint; deny: bigint; type: number; id: string };
-
- var channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, permission_overwrites: true }).exec();
- if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
-
- const permissions = await getPermission(req.user_id, channel.guild_id, channel_id);
- permissions.hasThrow("MANAGE_ROLES");
-
- if (body.type === 0) {
- if (!(await RoleModel.exists({ id: overwrite_id }))) throw new HTTPError("role not found", 404);
- } else if (body.type === 1) {
- if (!(await MemberModel.exists({ id: overwrite_id }))) throw new HTTPError("user not found", 404);
- } else throw new HTTPError("type not supported", 501);
-
- // @ts-ignore
- var overwrite: ChannelPermissionOverwrite = channel.permission_overwrites.find((x) => x.id === overwrite_id);
- if (!overwrite) {
- // @ts-ignore
- overwrite = {
- id: overwrite_id,
- type: body.type,
- allow: body.allow,
- deny: body.deny
- };
- channel.permission_overwrites.push(overwrite);
- }
- overwrite.allow = body.allow;
- overwrite.deny = body.deny;
-
- // @ts-ignore
- channel = await ChannelModel.findOneAndUpdate({ id: channel_id }, channel).exec();
-
- await emitEvent({
- event: "CHANNEL_UPDATE",
- channel_id,
- data: channel
- } as ChannelUpdateEvent);
-
- return res.sendStatus(204);
-});
-
-// TODO: check permission hierarchy
-router.delete("/:overwrite_id", async (req: Request, res: Response) => {
- const { channel_id, overwrite_id } = req.params;
-
- const permissions = await getPermission(req.user_id, undefined, channel_id);
- permissions.hasThrow("MANAGE_ROLES");
-
- const channel = await ChannelModel.findOneAndUpdate({ id: channel_id }, { $pull: { permission_overwrites: { id: overwrite_id } } });
- if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
-
- await emitEvent({
- event: "CHANNEL_UPDATE",
- channel_id,
- data: channel
- } as ChannelUpdateEvent);
-
- return res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/channels/#channel_id/pins.ts b/src/routes/channels/#channel_id/pins.ts
deleted file mode 100644
index 65d6b975..00000000
--- a/src/routes/channels/#channel_id/pins.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import {
- ChannelModel,
- ChannelPinsUpdateEvent,
- Config,
- getPermission,
- MessageModel,
- MessageUpdateEvent,
- toObject
-} from "@fosscord/server-util";
-import { Router, Request, Response } from "express";
-import { HTTPError } from "lambert-server";
-import { emitEvent } from "../../../util/Event";
-
-const router: Router = Router();
-
-router.put("/:message_id", async (req: Request, res: Response) => {
- const { channel_id, message_id } = req.params;
- const channel = await ChannelModel.findOne({ id: channel_id }).exec();
- const permission = await getPermission(req.user_id, channel.guild_id, channel_id);
- permission.hasThrow("VIEW_CHANNEL");
-
- // * in dm channels anyone can pin messages -> only check for guilds
- if (channel.guild_id) permission.hasThrow("MANAGE_MESSAGES");
-
- const pinned_count = await MessageModel.count({ channel_id, pinned: true }).exec();
- const { maxPins } = Config.get().limits.channel;
- if (pinned_count >= maxPins) throw new HTTPError("Max pin count reached: " + maxPins);
-
- await MessageModel.updateOne({ id: message_id }, { pinned: true }).exec();
- const message = toObject(await MessageModel.findOne({ id: message_id }).exec());
-
- await emitEvent({
- event: "MESSAGE_UPDATE",
- channel_id,
- data: message
- } as MessageUpdateEvent);
-
- await emitEvent({
- event: "CHANNEL_PINS_UPDATE",
- channel_id,
- data: {
- channel_id,
- guild_id: channel.guild_id,
- last_pin_timestamp: undefined
- }
- } as ChannelPinsUpdateEvent);
-
- res.sendStatus(204);
-});
-
-router.delete("/:message_id", async (req: Request, res: Response) => {
- const { channel_id, message_id } = req.params;
-
- const channel = await ChannelModel.findOne({ id: channel_id }).exec();
-
- const permission = await getPermission(req.user_id, channel.guild_id, channel_id);
- permission.hasThrow("VIEW_CHANNEL");
- if (channel.guild_id) permission.hasThrow("MANAGE_MESSAGES");
-
- const message = toObject(await MessageModel.findOneAndUpdate({ id: message_id }, { pinned: false }).exec());
-
- await emitEvent({
- event: "MESSAGE_UPDATE",
- channel_id,
- data: message
- } as MessageUpdateEvent);
-
- await emitEvent({
- event: "CHANNEL_PINS_UPDATE",
- channel_id,
- data: {
- channel_id,
- guild_id: channel.guild_id,
- last_pin_timestamp: undefined
- }
- } as ChannelPinsUpdateEvent);
-
- res.sendStatus(204);
-});
-
-router.get("/", async (req: Request, res: Response) => {
- const { channel_id } = req.params;
-
- const channel = await ChannelModel.findOne({ id: channel_id }).exec();
- const permission = await getPermission(req.user_id, channel.guild_id, channel_id);
- permission.hasThrow("VIEW_CHANNEL");
-
- let pins = await MessageModel.find({ channel_id: channel_id, pinned: true }).exec();
-
- res.send(toObject(pins));
-});
-
-export default router;
diff --git a/src/routes/channels/#channel_id/recipients.ts b/src/routes/channels/#channel_id/recipients.ts
deleted file mode 100644
index ea6bc563..00000000
--- a/src/routes/channels/#channel_id/recipients.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { Router, Response, Request } from "express";
-const router: Router = Router();
-// TODO:
-
-export default router;
diff --git a/src/routes/channels/#channel_id/typing.ts b/src/routes/channels/#channel_id/typing.ts
deleted file mode 100644
index de549883..00000000
--- a/src/routes/channels/#channel_id/typing.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { ChannelModel, MemberModel, toObject, TypingStartEvent } from "@fosscord/server-util";
-import { Router, Request, Response } from "express";
-
-import { HTTPError } from "lambert-server";
-import { emitEvent } from "../../../util/Event";
-
-const router: Router = Router();
-
-router.post("/", async (req: Request, res: Response) => {
- const { channel_id } = req.params;
- const user_id = req.user_id;
- const timestamp = Date.now();
- const channel = await ChannelModel.findOne({ id: channel_id });
- const member = await MemberModel.findOne({ id: user_id }).exec();
-
- await emitEvent({
- event: "TYPING_START",
- channel_id: channel_id,
- data: {
- // this is the paylod
- member: toObject(member),
- channel_id,
- timestamp,
- user_id,
- guild_id: channel.guild_id
- }
- } as TypingStartEvent);
- res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/channels/#channel_id/webhooks.ts b/src/routes/channels/#channel_id/webhooks.ts
deleted file mode 100644
index 6c1aea2a..00000000
--- a/src/routes/channels/#channel_id/webhooks.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Router, Response, Request } from "express";
-import { check, Length } from "../../../util/instanceOf";
-import { ChannelModel, getPermission, trimSpecial } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { isTextChannel } from "./messages/index";
-
-const router: Router = Router();
-// TODO:
-
-// TODO: use Image Data Type for avatar instead of String
-router.post("/", check({ name: new Length(String, 1, 80), $avatar: String }), async (req: Request, res: Response) => {
- const channel_id = req.params.channel_id;
- const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, type: true }).exec();
-
- isTextChannel(channel.type);
- if (!channel.guild_id) throw new HTTPError("Not a guild channel", 400);
-
- const permission = await getPermission(req.user_id, channel.guild_id);
- permission.hasThrow("MANAGE_WEBHOOKS");
-
- var { avatar, name } = req.body as { name: string; avatar?: string };
- name = trimSpecial(name);
- if (name === "clyde") throw new HTTPError("Invalid name", 400);
-});
-
-export default router;
diff --git a/src/routes/experiments.ts b/src/routes/experiments.ts
deleted file mode 100644
index 3bdbed62..00000000
--- a/src/routes/experiments.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Router, Response, Request } from "express";
-
-const router = Router();
-
-router.get("/", (req: Request, res: Response) => {
- // TODO:
- res.send({ fingerprint: "", assignments: [] });
-});
-
-export default router;
diff --git a/src/routes/gateway.ts b/src/routes/gateway.ts
deleted file mode 100644
index f2bc5b34..00000000
--- a/src/routes/gateway.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Config } from "@fosscord/server-util";
-import { Router, Response, Request } from "express";
-
-const router = Router();
-
-router.get("/", (req: Request, res: Response) => {
- const { endpoint } = Config.get().gateway;
- res.json({ url: endpoint || process.env.GATEWAY || "ws://localhost:3002" });
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/bans.ts b/src/routes/guilds/#guild_id/bans.ts
deleted file mode 100644
index d9752f61..00000000
--- a/src/routes/guilds/#guild_id/bans.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-import { Request, Response, Router } from "express";
-import { BanModel, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel, toObject } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { getIpAdress } from "../../../util/ipAddress";
-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 } = req.params;
-
- 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 }, { user_id: true, reason: true }).exec();
- return res.json(toObject(bans));
-});
-
-router.get("/:user", async (req: Request, res: Response) => {
- const { guild_id } = req.params;
- const user_id = req.params.ban;
-
- var ban = await BanModel.findOne({ guild_id: guild_id, user_id: user_id }).exec();
- return res.json(ban);
-});
-
-router.put("/:user_id", check(BanCreateSchema), async (req: Request, res: Response) => {
- const { guild_id } = req.params;
- 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);
- perms.hasThrow("BAN_MEMBERS");
- if (req.user_id === banned_user_id) throw new HTTPError("You can't ban yourself", 400);
-
- await removeMember(banned_user_id, guild_id);
-
- const ban = await new BanModel({
- user_id: banned_user_id,
- guild_id: guild_id,
- ip: getIpAdress(req),
- executor_id: req.user_id,
- reason: req.body.reason // || otherwise empty
- }).save();
-
- await emitEvent({
- event: "GUILD_BAN_ADD",
- data: {
- guild_id: guild_id,
- user: banned_user
- },
- guild_id: guild_id
- } as GuildBanAddEvent);
-
- return res.json(toObject(ban));
-});
-
-router.delete("/:user_id", async (req: Request, res: Response) => {
- 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.exists({ id: guild_id });
- if (!guild) throw new HTTPError("Guild not found", 404);
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("BAN_MEMBERS");
-
- await BanModel.deleteOne({
- user_id: banned_user_id,
- guild_id
- }).exec();
-
- await emitEvent({
- event: "GUILD_BAN_REMOVE",
- data: {
- guild_id,
- user: banned_user
- },
- guild_id
- } as GuildBanRemoveEvent);
-
- return res.status(204).send();
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/channels.ts b/src/routes/guilds/#guild_id/channels.ts
deleted file mode 100644
index 52361f5e..00000000
--- a/src/routes/guilds/#guild_id/channels.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Router, Response, Request } from "express";
-import {
- ChannelCreateEvent,
- ChannelModel,
- ChannelType,
- GuildModel,
- Snowflake,
- toObject,
- ChannelUpdateEvent,
- AnyChannel,
- getPermission
-} from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { ChannelModifySchema } from "../../../schema/Channel";
-import { emitEvent } from "../../../util/Event";
-import { check } from "../../../util/instanceOf";
-import { createChannel } from "../../../util/Channel";
-const router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- const { guild_id } = req.params;
- const channels = await ChannelModel.find({ guild_id }).exec();
-
- res.json(toObject(channels));
-});
-
-// TODO: check if channel type is permitted
-// TODO: check if parent_id exists
-
-router.post("/", check(ChannelModifySchema), async (req: Request, res: Response) => {
- // creates a new guild channel https://discord.com/developers/docs/resources/guild#create-guild-channel
- const { guild_id } = req.params;
- const body = req.body as ChannelModifySchema;
-
- const channel = await createChannel({ ...body, guild_id }, req.user_id);
-
- res.json(toObject(channel));
-});
-
-// TODO: check if parent_id exists
-router.patch(
- "/",
- check([{ id: String, $position: Number, $lock_permissions: Boolean, $parent_id: String }]),
- async (req: Request, res: Response) => {
- // changes guild channel position
- const { guild_id } = req.params;
- const body = req.body as { id: string; position?: number; lock_permissions?: boolean; parent_id?: string };
- body.position = Math.floor(body.position || 0);
- if (!body.position && !body.parent_id) throw new HTTPError(`You need to at least specify position or parent_id`, 400);
-
- const permission = await getPermission(req.user_id, guild_id);
- permission.hasThrow("MANAGE_CHANNELS");
-
- const opts: any = {};
- if (body.position) opts.position = body.position;
-
- if (body.parent_id) {
- opts.parent_id = body.parent_id;
- const parent_channel = await ChannelModel.findOne({ id: body.parent_id, guild_id }, { permission_overwrites: true }).exec();
- if (body.lock_permissions) {
- opts.permission_overwrites = parent_channel.permission_overwrites;
- }
- }
-
- const channel = await ChannelModel.findOneAndUpdate({ id: req.body, guild_id }, opts).exec();
-
- await emitEvent({ event: "CHANNEL_UPDATE", data: channel, channel_id: body.id, guild_id } as ChannelUpdateEvent);
-
- res.json(toObject(channel));
- }
-);
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/delete.ts b/src/routes/guilds/#guild_id/delete.ts
deleted file mode 100644
index 6cca289e..00000000
--- a/src/routes/guilds/#guild_id/delete.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import {
- ChannelModel,
- EmojiModel,
- GuildDeleteEvent,
- GuildModel,
- InviteModel,
- MemberModel,
- MessageModel,
- RoleModel,
- UserModel
-} from "@fosscord/server-util";
-import { Router, Request, Response } from "express";
-import { HTTPError } from "lambert-server";
-import { emitEvent } from "../../../util/Event";
-
-const router = Router();
-
-// 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("/", async (req: Request, res: Response) => {
- var { guild_id } = req.params;
-
- const guild = await GuildModel.findOne({ id: guild_id }, "owner_id").exec();
- if (guild.owner_id !== req.user_id) throw new HTTPError("You are not the owner of this guild", 401);
-
- await emitEvent({
- event: "GUILD_DELETE",
- data: {
- id: guild_id
- },
- guild_id: guild_id
- } as GuildDeleteEvent);
-
- await Promise.all([
- GuildModel.deleteOne({ id: guild_id }).exec(),
- UserModel.updateMany({ guilds: guild_id }, { $pull: { guilds: guild_id } }).exec(),
- RoleModel.deleteMany({ guild_id }).exec(),
- ChannelModel.deleteMany({ guild_id }).exec(),
- EmojiModel.deleteMany({ guild_id }).exec(),
- InviteModel.deleteMany({ guild_id }).exec(),
- MessageModel.deleteMany({ guild_id }).exec(),
- MemberModel.deleteMany({ guild_id }).exec()
- ]);
-
- return res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/index.ts b/src/routes/guilds/#guild_id/index.ts
deleted file mode 100644
index dc4ddb39..00000000
--- a/src/routes/guilds/#guild_id/index.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { Request, Response, Router } from "express";
-import {
- ChannelModel,
- EmojiModel,
- 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 { handleFile } from "../../../util/cdn";
-import "missing-native-js-functions";
-
-const router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- const { guild_id } = req.params;
-
- const guild = await GuildModel.findOne({ id: guild_id })
- .populate({ path: "joined_at", match: { id: req.user_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);
-});
-
-router.patch("/", check(GuildUpdateSchema), async (req: Request, res: Response) => {
- const body = req.body as GuildUpdateSchema;
- const { guild_id } = req.params;
- // TODO: guild update check image
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_GUILD");
-
- if (body.icon) body.icon = await handleFile(`/icons/${guild_id}`, body.icon);
- if (body.banner) body.banner = await handleFile(`/banners/${guild_id}`, body.banner);
- if (body.splash) body.splash = await handleFile(`/splashes/${guild_id}`, body.splash);
-
- 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.json(data);
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/invites.ts b/src/routes/guilds/#guild_id/invites.ts
deleted file mode 100644
index 1894ec96..00000000
--- a/src/routes/guilds/#guild_id/invites.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { getPermission, InviteModel, toObject } from "@fosscord/server-util";
-import { Request, Response, Router } from "express";
-
-const router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- const { guild_id } = req.params;
-
- const permissions = await getPermission(req.user_id, guild_id);
- permissions.hasThrow("MANAGE_GUILD");
-
- const invites = await InviteModel.find({ guild_id }).exec();
-
- return res.json(toObject(invites));
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/members/#member_id/index.ts b/src/routes/guilds/#guild_id/members/#member_id/index.ts
deleted file mode 100644
index 9a1676e6..00000000
--- a/src/routes/guilds/#guild_id/members/#member_id/index.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import { Request, Response, Router } from "express";
-import {
- GuildModel,
- MemberModel,
- UserModel,
- toObject,
- GuildMemberAddEvent,
- getPermission,
- PermissionResolvable,
- RoleModel,
- GuildMemberUpdateEvent
-} from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { addMember, isMember, removeMember } from "../../../../../util/Member";
-import { check } from "../../../../../util/instanceOf";
-import { MemberChangeSchema } from "../../../../../schema/Member";
-import { emitEvent } from "../../../../../util/Event";
-
-const router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- const { guild_id, member_id } = req.params;
- await isMember(req.user_id, guild_id);
-
- const member = await MemberModel.findOne({ id: member_id, guild_id }).exec();
-
- return res.json(toObject(member));
-});
-
-router.patch("/", check(MemberChangeSchema), async (req: Request, res: Response) => {
- const { guild_id, member_id } = req.params;
- const body = req.body as MemberChangeSchema;
- if (body.roles) {
- const roles = await RoleModel.find({ id: { $in: body.roles } }).exec();
- if (body.roles.length !== roles.length) throw new HTTPError("Roles not found", 404);
- // TODO: check if user has permission to add role
- }
-
- const member = await MemberModel.findOneAndUpdate({ id: member_id, guild_id }, body).exec();
-
- await emitEvent({
- event: "GUILD_MEMBER_UPDATE",
- guild_id,
- data: toObject(member)
- } as GuildMemberUpdateEvent);
-
- res.json(toObject(member));
-});
-
-router.put("/", async (req: Request, res: Response) => {
- const { guild_id, member_id } = req.params;
-
- throw new HTTPError("Maintenance: Currently you can't add a member", 403);
- // TODO: only for oauth2 applications
- await addMember(member_id, guild_id);
- res.sendStatus(204);
-});
-
-router.delete("/", async (req: Request, res: Response) => {
- const { guild_id, member_id } = req.params;
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("KICK_MEMBERS");
-
- await removeMember(member_id, guild_id);
- res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/members/#member_id/nick.ts b/src/routes/guilds/#guild_id/members/#member_id/nick.ts
deleted file mode 100644
index 9078409d..00000000
--- a/src/routes/guilds/#guild_id/members/#member_id/nick.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { getPermission, PermissionResolvable } from "@fosscord/server-util";
-import { Request, Response, Router } from "express";
-import { check } from "lambert-server";
-import { MemberNickChangeSchema } from "../../../../../schema/Member";
-import { changeNickname } from "../../../../../util/Member";
-
-const router = Router();
-
-router.patch("/", check(MemberNickChangeSchema), async (req: Request, res: Response) => {
- var { guild_id, member_id } = req.params;
- var permissionString: PermissionResolvable = "MANAGE_NICKNAMES";
- if (member_id === "@me") {
- member_id = req.user_id;
- permissionString = "CHANGE_NICKNAME";
- }
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow(permissionString);
-
- await changeNickname(member_id, guild_id, req.body.nickname);
- res.status(204);
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts b/src/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts
deleted file mode 100644
index b7a43c74..00000000
--- a/src/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { getPermission } from "@fosscord/server-util";
-import { Request, Response, Router } from "express";
-import { addRole, removeRole } from "../../../../../../../util/Member";
-
-const router = Router();
-
-router.delete("/:member_id/roles/:role_id", async (req: Request, res: Response) => {
- const { guild_id, role_id, member_id } = req.params;
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_ROLES");
-
- await removeRole(member_id, guild_id, role_id);
- res.sendStatus(204);
-});
-
-router.put("/:member_id/roles/:role_id", async (req: Request, res: Response) => {
- const { guild_id, role_id, member_id } = req.params;
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_ROLES");
-
- await addRole(member_id, guild_id, role_id);
- res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/members/index.ts b/src/routes/guilds/#guild_id/members/index.ts
deleted file mode 100644
index a157d8f5..00000000
--- a/src/routes/guilds/#guild_id/members/index.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Request, Response, Router } from "express";
-import { GuildModel, MemberModel, toObject } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { instanceOf, Length } from "../../../../util/instanceOf";
-import { PublicMemberProjection, isMember } from "../../../../util/Member";
-
-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 } = req.params;
- const guild = await GuildModel.findOne({ id: guild_id }).exec();
- await isMember(req.user_id, guild_id);
-
- try {
- instanceOf({ $limit: new Length(Number, 1, 1000), $after: String }, req.query, {
- path: "query",
- req,
- ref: { obj: null, key: "" }
- });
- } catch (error) {
- return res.status(400).json({ code: 50035, message: "Invalid Query", success: false, errors: error });
- }
-
- // @ts-ignore
- if (!req.query.limit) req.query.limit = 1;
- 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)
- .exec();
-
- return res.json(toObject(members));
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/regions.ts b/src/routes/guilds/#guild_id/regions.ts
deleted file mode 100644
index 3a46d766..00000000
--- a/src/routes/guilds/#guild_id/regions.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Config } from "@fosscord/server-util";
-import { Request, Response, Router } from "express";
-
-const router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- return res.json(Config.get().regions.available);
-});
-
-export default router;
\ No newline at end of file
diff --git a/src/routes/guilds/#guild_id/roles.ts b/src/routes/guilds/#guild_id/roles.ts
deleted file mode 100644
index 77206a0f..00000000
--- a/src/routes/guilds/#guild_id/roles.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import { Request, Response, Router } from "express";
-import {
- RoleModel,
- GuildModel,
- getPermission,
- toObject,
- UserModel,
- Snowflake,
- MemberModel,
- GuildRoleCreateEvent,
- GuildRoleUpdateEvent,
- GuildRoleDeleteEvent
-} from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { emitEvent } from "../../../util/Event";
-import { check } from "../../../util/instanceOf";
-import { RoleModifySchema } from "../../../schema/Roles";
-import { getPublicUser } from "../../../util/User";
-import { isMember } from "../../../util/Member";
-
-const router: Router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- const guild_id = req.params.guild_id;
-
- await isMember(req.user_id, guild_id);
-
- const roles = await RoleModel.find({ guild_id: guild_id }).exec();
-
- return res.json(toObject(roles));
-});
-
-router.post("/", check(RoleModifySchema), async (req: Request, res: Response) => {
- const guild_id = req.params.guild_id;
- const body = req.body as RoleModifySchema;
-
- const guild = await GuildModel.findOne({ id: guild_id }, { id: true }).exec();
- const user = await UserModel.findOne({ id: req.user_id }).exec();
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_ROLES");
- if (!body.name) throw new HTTPError("You need to specify a name");
-
- const role = await new RoleModel({
- ...body,
- id: Snowflake.generate(),
- guild_id: guild_id,
- managed: false,
- position: 0,
- tags: null,
- permissions: body.permissions || 0n
- }).save();
-
- await emitEvent({
- event: "GUILD_ROLE_CREATE",
- guild_id,
- data: {
- guild_id,
- role: toObject(role)
- }
- } as GuildRoleCreateEvent);
-
- res.json(toObject(role));
-});
-
-router.delete("/:role_id", async (req: Request, res: Response) => {
- const guild_id = req.params.guild_id;
- const { role_id } = req.params;
-
- const guild = await GuildModel.findOne({ id: guild_id }, { id: true }).exec();
- const user = await UserModel.findOne({ id: req.user_id }).exec();
-
- const perms = await getPermission(req.user_id, guild_id);
-
- if (!perms.has("MANAGE_ROLES")) throw new HTTPError("You missing the MANAGE_ROLES permission", 401);
-
- await RoleModel.findOneAndDelete({
- id: role_id,
- guild_id: guild_id
- }).exec();
-
- await emitEvent({
- event: "GUILD_ROLE_DELETE",
- guild_id,
- data: {
- guild_id,
- role_id
- }
- } as GuildRoleDeleteEvent);
-
- res.sendStatus(204);
-});
-
-// TODO: check role hierarchy
-
-router.patch("/:role_id", check(RoleModifySchema), async (req: Request, res: Response) => {
- const guild_id = req.params.guild_id;
- const { role_id } = req.params;
- const body = req.body as RoleModifySchema;
-
- const guild = await GuildModel.findOne({ id: guild_id }, { id: true }).exec();
- const user = await UserModel.findOne({ id: req.user_id }).exec();
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_ROLES");
-
- const role = await RoleModel.findOneAndUpdate(
- {
- id: role_id,
- guild_id: guild_id
- },
- // @ts-ignore
- body
- ).exec();
-
- await emitEvent({
- event: "GUILD_ROLE_UPDATE",
- guild_id,
- data: {
- guild_id,
- role
- }
- } as GuildRoleUpdateEvent);
-
- res.json(toObject(role));
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/templates.ts b/src/routes/guilds/#guild_id/templates.ts
deleted file mode 100644
index 8306ac37..00000000
--- a/src/routes/guilds/#guild_id/templates.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-import { Request, Response, Router } from "express";
-import { TemplateModel, GuildModel, getPermission, toObject, UserModel, Snowflake } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { TemplateCreateSchema, TemplateModifySchema } from "../../../schema/Template";
-import { check } from "../../../util/instanceOf";
-import { generateCode } from "../../../util/String";
-
-const router: Router = Router();
-
-const TemplateGuildProjection = {
- name: true,
- description: true,
- region: true,
- verification_level: true,
- default_message_notifications: true,
- explicit_content_filter: true,
- preferred_locale: true,
- afk_timeout: true,
- roles: true,
- channels: true,
- afk_channel_id: true,
- system_channel_id: true,
- system_channel_flags: true,
- icon_hash: true
-};
-
-router.get("/", async (req: Request, res: Response) => {
- const { guild_id } = req.params;
-
- var templates = await TemplateModel.find({ source_guild_id: guild_id }).exec();
-
- return res.json(toObject(templates));
-});
-
-router.post("/", check(TemplateCreateSchema), async (req: Request, res: Response) => {
- const { guild_id } = req.params;
- const guild = await GuildModel.findOne({ id: guild_id }, TemplateGuildProjection).exec();
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_GUILD");
-
- const exists = await TemplateModel.findOne({ id: guild_id })
- .exec()
- .catch((e) => {});
- if (exists) throw new HTTPError("Template already exists", 400);
-
- const template = await new TemplateModel({
- ...req.body,
- code: generateCode(),
- creator_id: req.user_id,
- created_at: new Date(),
- updated_at: new Date(),
- source_guild_id: guild_id,
- serialized_source_guild: guild
- }).save();
-
- res.json(toObject(template)).send();
-});
-
-router.delete("/:code", async (req: Request, res: Response) => {
- const guild_id = req.params.guild_id;
- const { code } = req.params;
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_GUILD");
-
- const template = await TemplateModel.findOneAndDelete({
- code
- }).exec();
-
- res.send(toObject(template));
-});
-
-router.put("/:code", async (req: Request, res: Response) => {
- const guild_id = req.params.guild_id;
- const { code } = req.params;
-
- const guild = await GuildModel.findOne({ id: guild_id }, TemplateGuildProjection).exec();
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_GUILD");
-
- const template = await TemplateModel.findOneAndUpdate({ code }, { serialized_source_guild: guild }).exec();
-
- res.json(toObject(template)).send();
-});
-
-router.patch("/:code", check(TemplateModifySchema), async (req: Request, res: Response) => {
- const { guild_id } = req.params;
- const { code } = req.params;
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_GUILD");
-
- const template = await TemplateModel.findOneAndUpdate({ code }, { name: req.body.name, description: req.body.description }).exec();
-
- res.json(toObject(template)).send();
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/vanity-url.ts b/src/routes/guilds/#guild_id/vanity-url.ts
deleted file mode 100644
index 323b2647..00000000
--- a/src/routes/guilds/#guild_id/vanity-url.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { getPermission, GuildModel, InviteModel, trimSpecial } from "@fosscord/server-util";
-import { Router, Request, Response } from "express";
-import { HTTPError } from "lambert-server";
-import { check, Length } from "../../../util/instanceOf";
-import { isMember } from "../../../util/Member";
-
-const router = Router();
-
-const InviteRegex = /\W/g;
-
-router.get("/", async (req: Request, res: Response) => {
- const { guild_id } = req.params;
-
- await isMember(req.user_id, guild_id);
- const guild = await GuildModel.findOne({ id: guild_id }).exec();
- if (!guild.vanity_url) throw new HTTPError("This guild has no vanity url", 204);
-
- return res.json({ code: guild.vanity_url.code });
-});
-
-// TODO: check if guild is elgible for vanity url
-router.patch("/", check({ code: new Length(String, 0, 20) }), async (req: Request, res: Response) => {
- const { guild_id } = req.params;
- var code = req.body.code.replace(InviteRegex);
- if (!code) code = null;
-
- const permission = await getPermission(req.user_id, guild_id);
- permission.hasThrow("MANAGE_GUILD");
-
- const alreadyExists = await Promise.all([
- GuildModel.findOne({ "vanity_url.code": code })
- .exec()
- .catch(() => null),
- InviteModel.findOne({ code: code })
- .exec()
- .catch(() => null)
- ]);
- if (alreadyExists.some((x) => x)) throw new HTTPError("Vanity url already exists", 400);
-
- await GuildModel.updateOne({ id: guild_id }, { "vanity_url.code": code }).exec();
-
- return res.json({ code: code });
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/welcome_screen.ts b/src/routes/guilds/#guild_id/welcome_screen.ts
deleted file mode 100644
index 656a0ee0..00000000
--- a/src/routes/guilds/#guild_id/welcome_screen.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { Request, Response, Router } from "express";
-import { GuildModel, getPermission, toObject, Snowflake } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { emitEvent } from "../../../util/Event";
-import { check } from "../../../util/instanceOf";
-import { isMember } from "../../../util/Member";
-import { GuildAddChannelToWelcomeScreenSchema } from "../../../schema/Guild";
-import { getPublicUser } from "../../../util/User";
-
-const router: Router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- const guild_id = req.params.guild_id;
-
- const guild = await GuildModel.findOne({ id: guild_id });
-
- await isMember(req.user_id, guild_id);
-
- res.json(toObject(guild.welcome_screen));
-});
-
-router.post("/", check(GuildAddChannelToWelcomeScreenSchema), async (req: Request, res: Response) => {
- const guild_id = req.params.guild_id;
- const body = req.body as GuildAddChannelToWelcomeScreenSchema;
-
- const guild = await GuildModel.findOne({ id: guild_id }).exec();
-
- var channelObject = {
- ...body
- };
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_GUILD");
-
- if (!guild.welcome_screen.enabled) throw new HTTPError("Welcome screen disabled", 400);
- if (guild.welcome_screen.welcome_channels.some((channel) => channel.channel_id === body.channel_id))
- throw new Error("Welcome Channel exists");
-
- await GuildModel.findOneAndUpdate(
- {
- id: guild_id
- },
- { $push: { "welcome_screen.welcome_channels": channelObject } }
- ).exec();
-
- res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/widget.json.ts b/src/routes/guilds/#guild_id/widget.json.ts
deleted file mode 100644
index 6f777ab4..00000000
--- a/src/routes/guilds/#guild_id/widget.json.ts
+++ /dev/null
@@ -1,139 +0,0 @@
-import { Request, Response, Router } from "express";
-import { Config, Permissions, GuildModel, InviteModel, ChannelModel, MemberModel } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { random } from "../../../util/RandomInviteID";
-
-const router: Router = Router();
-
-// Undocumented API notes:
-// An invite is created for the widget_channel_id on request (only if an existing one created by the widget doesn't already exist)
-// This invite created doesn't include an inviter object like user created ones and has a default expiry of 24 hours
-// Missing user object information is intentional (https://github.com/discord/discord-api-docs/issues/1287)
-// channels returns voice channel objects where @everyone has the CONNECT permission
-// members (max 100 returned) is a sample of all members, and bots par invisible status, there exists some alphabetical distribution pattern between the members returned
-
-// https://discord.com/developers/docs/resources/guild#get-guild-widget
-// TODO: Cache the response for a guild for 5 minutes regardless of response
-router.get("/", async (req: Request, res: Response) => {
- const { guild_id } = req.params;
-
- const guild = await GuildModel.findOne({ id: guild_id }).exec();
- if (!guild.widget_enabled) throw new HTTPError("Widget Disabled", 404);
-
- // Fetch existing widget invite for widget channel
- var invite = await InviteModel.findOne({ channel_id: guild.widget_channel_id, inviter_id: { $type: 10 } }).exec();
- if (guild.widget_channel_id && !invite) {
- // Create invite for channel if none exists
- // TODO: Refactor invite create code to a shared function
- const max_age = 86400; // 24 hours
- const expires_at = new Date(max_age * 1000 + Date.now());
- const body = {
- code: random(),
- temporary: false,
- uses: 0,
- max_uses: 0,
- max_age: max_age,
- expires_at,
- created_at: new Date(),
- guild_id,
- channel_id: guild.widget_channel_id,
- inviter_id: null
- };
-
- invite = await new InviteModel(body).save();
- }
-
- // Fetch voice channels, and the @everyone permissions object
- let channels: any[] = [];
- await ChannelModel.find({ guild_id: guild_id, type: 2 }, { permission_overwrites: { $elemMatch: { id: guild_id } } })
- .lean()
- .select("id name position permission_overwrites")
- .sort({ position: 1 })
- .cursor()
- .eachAsync((doc) => {
- // Only return channels where @everyone has the CONNECT permission
- if (
- doc.permission_overwrites === undefined ||
- Permissions.channelPermission(doc.permission_overwrites, Permissions.FLAGS.CONNECT) === Permissions.FLAGS.CONNECT
- ) {
- channels.push({
- id: doc.id,
- name: doc.name,
- position: doc.position
- });
- }
- });
-
- // Fetch members
- // TODO: Understand how Discord's max 100 random member sample works, and apply to here (see top of this file)
- let members: any[] = [];
- await MemberModel.find({ guild_id: guild_id })
- .lean()
- .populate({ path: "user", select: { _id: 0, username: 1, avatar: 1, presence: 1 } })
- .select("id user nick deaf mute")
- .cursor()
- .eachAsync((doc) => {
- const status = doc.user?.presence?.status || "offline";
- if (status == "offline") return;
-
- let item = {};
-
- item = {
- ...item,
- id: null, // this is updated during the sort outside of the query
- username: doc.nick || doc.user?.username,
- discriminator: "0000", // intended (https://github.com/discord/discord-api-docs/issues/1287)
- avatar: null, // intended, avatar_url below will return a unique guild + user url to the avatar
- status: status
- };
-
- const activity = doc.user?.presence?.activities?.[0];
- if (activity) {
- item = {
- ...item,
- game: { name: activity.name }
- };
- }
-
- // TODO: If the member is in a voice channel, return extra widget details
- // Extra fields returned include deaf, mute, self_deaf, self_mute, supress, and channel_id (voice channel connected to)
- // Get this from VoiceState
-
- // TODO: Implement a widget-avatar endpoint on the CDN, and implement logic here to request it
- // Get unique avatar url for guild user, cdn to serve the actual avatar image on this url
- /*
- const avatar = doc.user?.avatar;
- if (avatar) {
- const CDN_HOST = Config.get().cdn.endpoint || "http://localhost:3003";
- const avatar_url = "/widget-avatars/" + ;
- item = {
- ...item,
- avatar_url: avatar_url
- }
- }
- */
-
- members.push(item);
- });
-
- // Sort members, and update ids (Unable to do under the mongoose query due to https://mongoosejs.com/docs/faq.html#populate_sort_order)
- members = members.sort((first, second) => 0 - (first.username > second.username ? -1 : 1));
- members.forEach((x, i) => {
- x.id = i;
- });
-
- // Construct object to respond with
- const data = {
- id: guild_id,
- name: guild.name,
- instant_invite: invite?.code,
- channels: channels,
- members: members,
- presence_count: guild.presence_count
- };
-
- res.set("Cache-Control", "public, max-age=300");
- return res.json(data);
-});
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/widget.png.ts b/src/routes/guilds/#guild_id/widget.png.ts
deleted file mode 100644
index a0a8c938..00000000
--- a/src/routes/guilds/#guild_id/widget.png.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import { Request, Response, Router } from "express";
-import { GuildModel } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import fs from "fs";
-import path from "path";
-
-const router: Router = Router();
-
-// TODO: use svg templates instead of node-canvas for improved performance and to change it easily
-
-// https://discord.com/developers/docs/resources/guild#get-guild-widget-image
-// TODO: Cache the response
-router.get("/", async (req: Request, res: Response) => {
- const { guild_id } = req.params;
-
- const guild = await GuildModel.findOne({ id: guild_id }).exec();
- if (!guild.widget_enabled) throw new HTTPError("Unknown Guild", 404);
-
- // Fetch guild information
- const icon = guild.icon;
- const name = guild.name;
- const presence = guild.presence_count + " ONLINE";
-
- // Fetch parameter
- const style = req.query.style?.toString() || "shield";
- if (!["shield", "banner1", "banner2", "banner3", "banner4"].includes(style)) {
- throw new HTTPError("Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').", 400);
- }
-
- // Setup canvas
- const { createCanvas } = require("canvas");
- const { loadImage } = require("canvas");
- const sizeOf = require("image-size");
-
- // TODO: Widget style templates need Fosscord branding
- const source = path.join(__dirname, "..", "..", "..", "..", "assets", "widget", `${style}.png`);
- if (!fs.existsSync(source)) {
- throw new HTTPError("Widget template does not exist.", 400);
- }
-
- // Create base template image for parameter
- const { width, height } = await sizeOf(source);
- const canvas = createCanvas(width, height);
- const ctx = canvas.getContext("2d");
- const template = await loadImage(source);
- ctx.drawImage(template, 0, 0);
-
- // Add the guild specific information to the template asset image
- switch (style) {
- case "shield":
- ctx.textAlign = "center";
- await drawText(ctx, 73, 13, "#FFFFFF", "thin 10px Verdana", presence);
- break;
- case "banner1":
- if (icon) await drawIcon(ctx, 20, 27, 50, icon);
- await drawText(ctx, 83, 51, "#FFFFFF", "12px Verdana", name, 22);
- await drawText(ctx, 83, 66, "#C9D2F0FF", "thin 11px Verdana", presence);
- break;
- case "banner2":
- if (icon) await drawIcon(ctx, 13, 19, 36, icon);
- await drawText(ctx, 62, 34, "#FFFFFF", "12px Verdana", name, 15);
- await drawText(ctx, 62, 49, "#C9D2F0FF", "thin 11px Verdana", presence);
- break;
- case "banner3":
- if (icon) await drawIcon(ctx, 20, 20, 50, icon);
- await drawText(ctx, 83, 44, "#FFFFFF", "12px Verdana", name, 27);
- await drawText(ctx, 83, 58, "#C9D2F0FF", "thin 11px Verdana", presence);
- break;
- case "banner4":
- if (icon) await drawIcon(ctx, 21, 136, 50, icon);
- await drawText(ctx, 84, 156, "#FFFFFF", "13px Verdana", name, 27);
- await drawText(ctx, 84, 171, "#C9D2F0FF", "thin 12px Verdana", presence);
- break;
- default:
- throw new HTTPError("Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').", 400);
- }
-
- // Return final image
- const buffer = canvas.toBuffer("image/png");
- res.set("Content-Type", "image/png");
- res.set("Cache-Control", "public, max-age=3600");
- return res.send(buffer);
-});
-
-async function drawIcon(canvas: any, x: number, y: number, scale: number, icon: string) {
- // @ts-ignore
- const img = new require("canvas").Image();
- img.src = icon;
-
- // Do some canvas clipping magic!
- canvas.save();
- canvas.beginPath();
-
- const r = scale / 2; // use scale to determine radius
- canvas.arc(x + r, y + r, r, 0, 2 * Math.PI, false); // start circle at x, and y coords + radius to find center
-
- canvas.clip();
- canvas.drawImage(img, x, y, scale, scale);
-
- canvas.restore();
-}
-
-async function drawText(canvas: any, x: number, y: number, color: string, font: string, text: string, maxcharacters?: number) {
- canvas.fillStyle = color;
- canvas.font = font;
- if (text.length > (maxcharacters || 0) && maxcharacters) text = text.slice(0, maxcharacters) + "...";
- canvas.fillText(text, x, y);
-}
-
-export default router;
diff --git a/src/routes/guilds/#guild_id/widget.ts b/src/routes/guilds/#guild_id/widget.ts
deleted file mode 100644
index 0e6df186..00000000
--- a/src/routes/guilds/#guild_id/widget.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Request, Response, Router } from "express";
-import { getPermission, GuildModel } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { check } from "../../../util/instanceOf";
-import { WidgetModifySchema } from "../../../schema/Widget";
-
-const router: Router = Router();
-
-// https://discord.com/developers/docs/resources/guild#get-guild-widget-settings
-router.get("/", async (req: Request, res: Response) => {
- const { guild_id } = req.params;
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_GUILD");
-
- const guild = await GuildModel.findOne({ id: guild_id }).exec();
-
- return res.json({ enabled: guild.widget_enabled || false, channel_id: guild.widget_channel_id || null });
-});
-
-// https://discord.com/developers/docs/resources/guild#modify-guild-widget
-router.patch("/", check(WidgetModifySchema), async (req: Request, res: Response) => {
- const body = req.body as WidgetModifySchema;
- const { guild_id } = req.params;
-
- const perms = await getPermission(req.user_id, guild_id);
- perms.hasThrow("MANAGE_GUILD");
-
- await GuildModel.updateOne({ id: guild_id }, { widget_enabled: body.enabled, widget_channel_id: body.channel_id }).exec();
- // Widget invite for the widget_channel_id gets created as part of the /guilds/{guild.id}/widget.json request
-
- return res.json(body);
-});
-
-export default router;
diff --git a/src/routes/guilds/index.ts b/src/routes/guilds/index.ts
deleted file mode 100644
index 25b55896..00000000
--- a/src/routes/guilds/index.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import { Router, Request, Response } from "express";
-import { RoleModel, GuildModel, Snowflake, Guild, RoleDocument, Config } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { check } from "./../../util/instanceOf";
-import { GuildCreateSchema } from "../../schema/Guild";
-import { getPublicUser } from "../../util/User";
-import { addMember } from "../../util/Member";
-import { createChannel } from "../../util/Channel";
-
-const router: Router = Router();
-
-//TODO: create default channel
-
-router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) => {
- const body = req.body as GuildCreateSchema;
-
- const { maxGuilds } = Config.get().limits.user;
- const user = await getPublicUser(req.user_id, { guilds: true });
-
- if (user.guilds.length >= maxGuilds) {
- throw new HTTPError(`Maximum number of guilds reached ${maxGuilds}`, 403);
- }
-
- const guild_id = Snowflake.generate();
- const guild: Guild = {
- name: body.name,
- region: Config.get().regions.default,
- owner_id: req.user_id,
- icon: undefined,
- afk_channel_id: undefined,
- afk_timeout: 300,
- application_id: undefined,
- banner: undefined,
- default_message_notifications: 0,
- description: undefined,
- splash: undefined,
- discovery_splash: undefined,
- explicit_content_filter: 0,
- features: [],
- id: guild_id,
- large: undefined,
- max_members: 250000,
- max_presences: 250000,
- max_video_channel_users: 25,
- presence_count: 0,
- member_count: 0, // will automatically be increased by addMember()
- mfa_level: 0,
- preferred_locale: "en-US",
- premium_subscription_count: 0,
- premium_tier: 0,
- public_updates_channel_id: undefined,
- rules_channel_id: undefined,
- system_channel_flags: 0,
- system_channel_id: undefined,
- unavailable: false,
- vanity_url: undefined,
- verification_level: 0,
- welcome_screen: {
- enabled: false,
- description: "No description",
- welcome_channels: []
- },
- widget_channel_id: undefined,
- widget_enabled: false
- };
-
- const [guild_doc, role] = await Promise.all([
- new GuildModel(guild).save(),
- new RoleModel({
- id: guild_id,
- guild_id: guild_id,
- color: 0,
- hoist: false,
- managed: false,
- mentionable: false,
- name: "@everyone",
- permissions: 2251804225n,
- position: 0,
- tags: null
- }).save()
- ]);
-
- await createChannel({ name: "general", type: 0, guild_id, position: 0, permission_overwrites: [] }, req.user_id);
- await addMember(req.user_id, guild_id);
-
- res.status(201).json({ id: guild.id });
-});
-
-export default router;
diff --git a/src/routes/guilds/templates/index.ts b/src/routes/guilds/templates/index.ts
deleted file mode 100644
index 0f332de0..00000000
--- a/src/routes/guilds/templates/index.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { Request, Response, Router } from "express";
-const router: Router = Router();
-import { TemplateModel, GuildModel, toObject, UserModel, RoleModel, Snowflake, Guild, Config } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { GuildTemplateCreateSchema } from "../../../schema/Guild";
-import { getPublicUser } from "../../../util/User";
-import { check } from "../../../util/instanceOf";
-import { addMember } from "../../../util/Member";
-
-router.get("/:code", async (req: Request, res: Response) => {
- const { code } = req.params;
-
- const template = await TemplateModel.findOne({ code: code }).exec();
-
- res.json(toObject(template)).send();
-});
-
-router.post("/:code", check(GuildTemplateCreateSchema), async (req: Request, res: Response) => {
- const { code } = req.params;
- const body = req.body as GuildTemplateCreateSchema;
-
- const { maxGuilds } = Config.get().limits.user;
- const user = await getPublicUser(req.user_id, { guilds: true });
-
- if (user.guilds.length >= maxGuilds) {
- throw new HTTPError(`Maximum number of guilds reached ${maxGuilds}`, 403);
- }
-
- const template = await TemplateModel.findOne({ code: code }).exec();
-
- const guild_id = Snowflake.generate();
-
- const guild: Guild = {
- ...body,
- ...template.serialized_source_guild,
- id: guild_id,
- owner_id: req.user_id
- };
-
- const [guild_doc, role] = await Promise.all([
- new GuildModel(guild).save(),
- new RoleModel({
- id: guild_id,
- guild_id: guild_id,
- color: 0,
- hoist: false,
- managed: true,
- mentionable: true,
- name: "@everyone",
- permissions: 2251804225n,
- position: 0,
- tags: null
- }).save()
- ]);
-
- await addMember(req.user_id, guild_id, { guild: guild_doc });
-
- res.status(201).json({ id: guild.id });
-});
-
-export default router;
diff --git a/src/routes/invites/index.ts b/src/routes/invites/index.ts
deleted file mode 100644
index 8c04713c..00000000
--- a/src/routes/invites/index.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { Router, Request, Response } from "express";
-import { getPermission, InviteModel, toObject } from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { addMember } from "../../util/Member";
-const router: Router = Router();
-
-router.get("/:code", async (req: Request, res: Response) => {
- const { code } = req.params;
-
- const invite = await InviteModel.findOne({ code }).exec();
- if (!invite) throw new HTTPError("Unknown Invite", 404);
-
- res.status(200).send(toObject(invite));
-});
-
-router.post("/:code", async (req: Request, res: Response) => {
- const { code } = req.params;
-
- const invite = await InviteModel.findOneAndUpdate({ code }, { $inc: { uses: 1 } }).exec();
- if (!invite) throw new HTTPError("Unknown Invite", 404);
-
- await addMember(req.user_id, invite.guild_id);
-
- res.status(200).send(toObject(invite));
-});
-
-router.delete("/:code", async (req: Request, res: Response) => {
- const { 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 missing the MANAGE_GUILD or MANAGE_CHANNELS permission", 401);
-
- await InviteModel.deleteOne({ code }).exec();
-
- res.status(200).send({ invite: toObject(invite) });
-});
-
-export default router;
diff --git a/src/routes/ping.ts b/src/routes/ping.ts
deleted file mode 100644
index 38daf81e..00000000
--- a/src/routes/ping.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Router, Response, Request } from "express";
-
-const router = Router();
-
-router.get("/", (req: Request, res: Response) => {
- res.send("pong");
-});
-
-export default router;
diff --git a/src/routes/science.ts b/src/routes/science.ts
deleted file mode 100644
index b16ef783..00000000
--- a/src/routes/science.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Router, Response, Request } from "express";
-
-const router = Router();
-
-router.post("/", (req: Request, res: Response) => {
- // TODO:
- res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/users/#id/index.ts b/src/routes/users/#id/index.ts
deleted file mode 100644
index a2ad3ae6..00000000
--- a/src/routes/users/#id/index.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Router, Request, Response } from "express";
-import { getPublicUser } from "../../../util/User";
-import { HTTPError } from "lambert-server";
-
-const router: Router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- const { id } = req.params;
-
- res.json(await getPublicUser(id));
-});
-
-export default router;
diff --git a/src/routes/users/#id/profile.ts b/src/routes/users/#id/profile.ts
deleted file mode 100644
index 4b4b9439..00000000
--- a/src/routes/users/#id/profile.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Router, Request, Response } from "express";
-import { getPublicUser } from "../../../util/User";
-
-const router: Router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- const user = await getPublicUser(req.params.id, { user_data: true })
-
- res.json({
- connected_accounts: user.user_data.connected_accounts,
- premium_guild_since: null, // TODO
- premium_since: null, // TODO
- user: {
- username: user.username,
- discriminator: user.discriminator,
- id: user.id,
- public_flags: user.public_flags,
- avatar: user.avatar,
- accent_color: user.accent_color,
- banner: user.banner,
- bio: req.user_bot ? null : user.bio,
- bot: user.bot,
- }
- });
-});
-
-export default router;
diff --git a/src/routes/users/@me/affinities/guilds.ts b/src/routes/users/@me/affinities/guilds.ts
deleted file mode 100644
index fa6be0e7..00000000
--- a/src/routes/users/@me/affinities/guilds.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Router, Response, Request } from "express";
-
-const router = Router();
-
-router.get("/", (req: Request, res: Response) => {
- // TODO:
- res.status(200).send({ guild_affinities: [] });
-});
-
-export default router;
diff --git a/src/routes/users/@me/affinities/user.ts b/src/routes/users/@me/affinities/user.ts
deleted file mode 100644
index 0790a8a4..00000000
--- a/src/routes/users/@me/affinities/user.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Router, Response, Request } from "express";
-
-const router = Router();
-
-router.get("/", (req: Request, res: Response) => {
- // TODO:
- res.status(200).send({ user_affinities: [], inverse_user_affinities: [] });
-});
-
-export default router;
diff --git a/src/routes/users/@me/channels.ts b/src/routes/users/@me/channels.ts
deleted file mode 100644
index a425a25f..00000000
--- a/src/routes/users/@me/channels.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import { Router, Request, Response } from "express";
-import {
- ChannelModel,
- ChannelCreateEvent,
- toObject,
- ChannelType,
- Snowflake,
- trimSpecial,
- Channel,
- DMChannel,
- UserModel
-} from "@fosscord/server-util";
-import { HTTPError } from "lambert-server";
-import { emitEvent } from "../../../util/Event";
-import { DmChannelCreateSchema } from "../../../schema/Channel";
-import { check } from "../../../util/instanceOf";
-
-const router: Router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- var channels = await ChannelModel.find({ recipient_ids: req.user_id }).exec();
-
- res.json(toObject(channels));
-});
-
-router.post("/", check(DmChannelCreateSchema), async (req: Request, res: Response) => {
- const body = req.body as DmChannelCreateSchema;
-
- body.recipients = body.recipients.filter((x) => x !== req.user_id).unique();
-
- if (!(await Promise.all(body.recipients.map((x) => UserModel.exists({ id: x })))).every((x) => x)) {
- throw new HTTPError("Recipient not found");
- }
-
- const type = body.recipients.length === 1 ? ChannelType.DM : ChannelType.GROUP_DM;
- const name = trimSpecial(body.name);
-
- const channel = await new ChannelModel({
- name,
- type,
- owner_id: req.user_id,
- id: Snowflake.generate(),
- created_at: new Date(),
- last_message_id: null,
- recipient_ids: [...body.recipients, req.user_id]
- }).save();
-
- await emitEvent({ event: "CHANNEL_CREATE", data: toObject(channel), user_id: req.user_id } as ChannelCreateEvent);
-
- res.json(toObject(channel));
-});
-
-export default router;
diff --git a/src/routes/users/@me/delete.ts b/src/routes/users/@me/delete.ts
deleted file mode 100644
index edda8e2d..00000000
--- a/src/routes/users/@me/delete.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Router, Request, Response } from "express";
-import { GuildModel, MemberModel, UserModel } from "@fosscord/server-util";
-import bcrypt from "bcrypt";
-const router = Router();
-
-router.post("/", async (req: Request, res: Response) => {
- const user = await UserModel.findOne({ id: req.user_id }).exec(); //User object
-
- let correctpass = await bcrypt.compare(req.body.password, user!.user_data.hash); //Not sure if user typed right password :/
- if (correctpass) {
- await Promise.all([
- UserModel.deleteOne({ id: req.user_id }).exec(), //Yeetus user deletus
- MemberModel.deleteMany({ id: req.user_id }).exec()
- ]);
-
- res.sendStatus(204);
- } else {
- res.sendStatus(401);
- }
-});
-
-export default router;
diff --git a/src/routes/users/@me/disable.ts b/src/routes/users/@me/disable.ts
deleted file mode 100644
index 0e5b734e..00000000
--- a/src/routes/users/@me/disable.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { UserModel } from "@fosscord/server-util";
-import { Router, Response, Request } from "express";
-import bcrypt from "bcrypt";
-
-const router = Router();
-
-router.post("/", async (req: Request, res: Response) => {
- const user = await UserModel.findOne({ id: req.user_id }).exec(); //User object
-
- let correctpass = await bcrypt.compare(req.body.password, user!.user_data.hash); //Not sure if user typed right password :/
- if (correctpass) {
- await UserModel.updateOne({ id: req.user_id }, { disabled: true }).exec();
-
- res.sendStatus(204);
- } else {
- res.status(400).json({ message: "Password does not match", code: 50018 });
- }
-});
-
-export default router;
diff --git a/src/routes/users/@me/guilds.ts b/src/routes/users/@me/guilds.ts
deleted file mode 100644
index 6528552b..00000000
--- a/src/routes/users/@me/guilds.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Router, Request, Response } from "express";
-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";
-
-const router: Router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- const user = await UserModel.findOne({ id: req.user_id }, { guilds: true }).exec();
- if (!user) throw new HTTPError("User not found", 404);
-
- var guildIDs = user.guilds || [];
- 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 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 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),
- ]);
-
- const user = await getPublicUser(req.user_id);
-
- await emitEvent({
- event: "GUILD_MEMBER_REMOVE",
- data: {
- guild_id: guild_id,
- user: user,
- },
- guild_id: guild_id,
- } as GuildMemberRemoveEvent);
-
- return res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/users/@me/index.ts b/src/routes/users/@me/index.ts
deleted file mode 100644
index 7bd4a486..00000000
--- a/src/routes/users/@me/index.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Router, Request, Response } from "express";
-import { UserModel, toObject, PublicUserProjection } from "@fosscord/server-util";
-import { getPublicUser } from "../../../util/User";
-import { UserModifySchema } from "../../../schema/User";
-import { check } from "../../../util/instanceOf";
-import { handleFile } from "../../../util/cdn";
-
-const router: Router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- res.json(await getPublicUser(req.user_id));
-});
-
-const UserUpdateProjection = {
- accent_color: true,
- avatar: true,
- banner: true,
- bio: true,
- bot: true,
- discriminator: true,
- email: true,
- flags: true,
- id: true,
- locale: true,
- mfa_enabled: true,
- nsfw_alllowed: true,
- phone: true,
- public_flags: true,
- purchased_flags: true,
- // token: true, // this isn't saved in the db and needs to be set manually
- username: true,
- verified: true
-};
-
-router.patch("/", check(UserModifySchema), async (req: Request, res: Response) => {
- const body = req.body as UserModifySchema;
-
- if (body.avatar) body.avatar = await handleFile(`/avatars/${req.user_id}`, body.avatar as string);
- if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string);
-
- const user = await UserModel.findOneAndUpdate({ id: req.user_id }, body, { projection: UserUpdateProjection }).exec();
- // TODO: dispatch user update event
-
- res.json(toObject(user));
-});
-
-export default router;
-// {"message": "Invalid two-factor code", "code": 60008}
diff --git a/src/routes/users/@me/library.ts b/src/routes/users/@me/library.ts
deleted file mode 100644
index d771cb5e..00000000
--- a/src/routes/users/@me/library.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Router, Response, Request } from "express";
-
-const router = Router();
-
-router.get("/", (req: Request, res: Response) => {
- // TODO:
- res.status(200).send([]);
-});
-
-export default router;
diff --git a/src/routes/users/@me/profile.ts b/src/routes/users/@me/profile.ts
deleted file mode 100644
index b67d1964..00000000
--- a/src/routes/users/@me/profile.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Router, Request, Response } from "express";
-import { getPublicUser } from "../../../util/User";
-
-const router: Router = Router();
-
-router.get("/", async (req: Request, res: Response) => {
- const user = await getPublicUser(req.user_id, { user_data: true })
-
- res.json({
- connected_accounts: user.user_data.connected_accounts,
- premium_guild_since: null, // TODO
- premium_since: null, // TODO
- user: {
- username: user.username,
- discriminator: user.discriminator,
- id: user.id,
- public_flags: user.public_flags,
- avatar: user.avatar,
- accent_color: user.accent_color,
- banner: user.banner,
- bio: user.bio,
- bot: user.bot,
- }
- });
-});
-
-export default router;
diff --git a/src/routes/users/@me/relationships.ts b/src/routes/users/@me/relationships.ts
deleted file mode 100644
index a8f03143..00000000
--- a/src/routes/users/@me/relationships.ts
+++ /dev/null
@@ -1,176 +0,0 @@
-import {
- RelationshipAddEvent,
- UserModel,
- PublicUserProjection,
- toObject,
- RelationshipType,
- RelationshipRemoveEvent,
- UserDocument
-} from "@fosscord/server-util";
-import { Router, Response, Request } from "express";
-import { HTTPError } from "lambert-server";
-import { emitEvent } from "../../../util/Event";
-import { check, Length } from "../../../util/instanceOf";
-
-const router = Router();
-
-const userProjection = { "user_data.relationships": true, ...PublicUserProjection };
-
-router.get("/", async (req: Request, res: Response) => {
- const user = await UserModel.findOne({ id: req.user_id }, { user_data: { relationships: true } })
- .populate({ path: "user_data.relationships.id", model: UserModel })
- .exec();
-
- return res.json(toObject(user.user_data.relationships));
-});
-
-async function addRelationship(req: Request, res: Response, friend: UserDocument, type: RelationshipType) {
- const id = friend.id;
- if (id === req.user_id) throw new HTTPError("You can't add yourself as a friend");
-
- const user = await UserModel.findOne({ id: req.user_id }, userProjection).exec();
- const newUserRelationships = [...user.user_data.relationships];
- const newFriendRelationships = [...friend.user_data.relationships];
-
- var relationship = newUserRelationships.find((x) => x.id === id);
- const friendRequest = newFriendRelationships.find((x) => x.id === req.user_id);
-
- if (type === RelationshipType.blocked) {
- if (relationship) {
- if (relationship.type === RelationshipType.blocked) throw new HTTPError("You already blocked the user");
- relationship.type = RelationshipType.blocked;
- } else {
- relationship = { id, type: RelationshipType.blocked };
- newUserRelationships.push(relationship);
- }
-
- if (friendRequest && friendRequest.type !== RelationshipType.blocked) {
- newFriendRelationships.remove(friendRequest);
- await Promise.all([
- UserModel.updateOne({ id: friend.id }, { "user_data.relationships": newFriendRelationships }).exec(),
- emitEvent({
- event: "RELATIONSHIP_REMOVE",
- data: friendRequest,
- user_id: id
- } as RelationshipRemoveEvent)
- ]);
- }
-
- await Promise.all([
- UserModel.updateOne({ id: req.user_id }, { "user_data.relationships": newUserRelationships }).exec(),
- emitEvent({
- event: "RELATIONSHIP_ADD",
- data: {
- ...toObject(relationship),
- user: { ...toObject(friend), user_data: undefined }
- },
- user_id: req.user_id
- } as RelationshipAddEvent)
- ]);
-
- return res.sendStatus(204);
- }
-
- var incoming_relationship = { id: req.user_id, nickname: undefined, type: RelationshipType.incoming };
- var outgoing_relationship = { id, nickname: undefined, type: RelationshipType.outgoing };
-
- if (friendRequest) {
- if (friendRequest.type === RelationshipType.blocked) throw new HTTPError("The user blocked you");
- // accept friend request
- // @ts-ignore
- incoming_relationship = friendRequest;
- incoming_relationship.type = RelationshipType.friends;
- outgoing_relationship.type = RelationshipType.friends;
- } else newFriendRelationships.push(incoming_relationship);
-
- if (relationship) {
- if (relationship.type === RelationshipType.outgoing) throw new HTTPError("You already sent a friend request");
- if (relationship.type === RelationshipType.blocked) throw new HTTPError("Unblock the user before sending a friend request");
- if (relationship.type === RelationshipType.friends) throw new HTTPError("You are already friends with the user");
- } else newUserRelationships.push(outgoing_relationship);
-
- await Promise.all([
- UserModel.updateOne({ id: req.user_id }, { "user_data.relationships": newUserRelationships }).exec(),
- UserModel.updateOne({ id: friend.id }, { "user_data.relationships": newFriendRelationships }).exec(),
- emitEvent({
- event: "RELATIONSHIP_ADD",
- data: {
- ...outgoing_relationship,
- user: { ...toObject(friend), user_data: undefined }
- },
- user_id: req.user_id
- } as RelationshipAddEvent),
- emitEvent({
- event: "RELATIONSHIP_ADD",
- data: {
- ...toObject(incoming_relationship),
- should_notify: true,
- user: { ...toObject(user), user_data: undefined }
- },
- user_id: id
- } as RelationshipAddEvent)
- ]);
-
- return res.sendStatus(204);
-}
-
-router.put("/:id", check({ $type: new Length(Number, 1, 4) }), async (req: Request, res: Response) => {
- return await addRelationship(req, res, await UserModel.findOne({ id: req.params.id }), req.body.type);
-});
-
-router.post("/", check({ discriminator: String, username: String }), async (req: Request, res: Response) => {
- return await addRelationship(
- req,
- res,
- await UserModel.findOne(req.body as { discriminator: string; username: string }).exec(),
- req.body.type
- );
-});
-
-router.delete("/:id", async (req: Request, res: Response) => {
- const { id } = req.params;
- if (id === req.user_id) throw new HTTPError("You can't remove yourself as a friend");
-
- const user = await UserModel.findOne({ id: req.user_id }).exec();
- if (!user) throw new HTTPError("Invalid token", 400);
-
- const friend = await UserModel.findOne({ id }, userProjection).exec();
- if (!friend) throw new HTTPError("User not found", 404);
-
- const relationship = user.user_data.relationships.find((x) => x.id === id);
- const friendRequest = friend.user_data.relationships.find((x) => x.id === req.user_id);
- if (relationship?.type === RelationshipType.blocked) {
- // unblock user
- user.user_data.relationships.remove(relationship);
-
- await Promise.all([
- user.save(),
- emitEvent({ event: "RELATIONSHIP_REMOVE", user_id: req.user_id, data: relationship } as RelationshipRemoveEvent)
- ]);
- return res.sendStatus(204);
- }
- if (!relationship || !friendRequest) throw new HTTPError("You are not friends with the user", 404);
- if (friendRequest.type === RelationshipType.blocked) throw new HTTPError("The user blocked you");
-
- user.user_data.relationships.remove(relationship);
- friend.user_data.relationships.remove(friendRequest);
-
- await Promise.all([
- user.save(),
- friend.save(),
- emitEvent({
- event: "RELATIONSHIP_REMOVE",
- data: relationship,
- user_id: req.user_id
- } as RelationshipRemoveEvent),
- emitEvent({
- event: "RELATIONSHIP_REMOVE",
- data: friendRequest,
- user_id: id
- } as RelationshipRemoveEvent)
- ]);
-
- return res.sendStatus(204);
-});
-
-export default router;
diff --git a/src/routes/users/@me/settings.ts b/src/routes/users/@me/settings.ts
deleted file mode 100644
index cca9b3ab..00000000
--- a/src/routes/users/@me/settings.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Router, Response, Request } from "express";
-
-const router = Router();
-
-router.patch("/", (req: Request, res: Response) => {
- // TODO:
- res.sendStatus(204);
-});
-
-export default router;
|