diff --git a/src/Server.ts b/src/Server.ts
index fcc5374b..69222636 100644
--- a/src/Server.ts
+++ b/src/Server.ts
@@ -86,7 +86,7 @@ export class FosscordServer extends Server {
// @ts-ignore
this.app = api;
api.use(RateLimit({ bucket: "global", count: 10, window: 5, bot: 250 }));
- api.use(RateLimit({ bucket: "error", count: 5, error: true, window: 5, bot: 15, onylIp: true }));
+ api.use(RateLimit({ bucket: "error", count: 5, error: true, window: 5, bot: 15, onlyIp: true }));
api.use("/guilds/:id", RateLimit({ count: 5, window: 5 }));
api.use("/webhooks/:id", RateLimit({ count: 5, window: 5 }));
api.use("/channels/:id", RateLimit({ count: 5, window: 5 }));
diff --git a/src/middlewares/RateLimit.ts b/src/middlewares/RateLimit.ts
index 088c3161..0858744a 100644
--- a/src/middlewares/RateLimit.ts
+++ b/src/middlewares/RateLimit.ts
@@ -42,14 +42,14 @@ export default function RateLimit(opts: {
MODIFY?: number;
error?: boolean;
success?: boolean;
- onylIp?: boolean;
+ onlyIp?: boolean;
}): any {
Cache.init(); // will only initalize it once
return async (req: Request, res: Response, next: NextFunction): Promise<any> => {
const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, "");
var user_id = getIpAdress(req);
- if (!opts.onylIp && req.user_id) user_id = req.user_id;
+ if (!opts.onlyIp && req.user_id) user_id = req.user_id;
var max_hits = opts.count;
if (opts.bot && req.user_bot) max_hits = opts.bot;
diff --git a/src/routes/auth/login.ts b/src/routes/auth/login.ts
index c92ddccc..8d1a8df3 100644
--- a/src/routes/auth/login.ts
+++ b/src/routes/auth/login.ts
@@ -13,7 +13,7 @@ export default router;
router.post(
"/",
- RateLimit({ count: 5, window: 60, onylIp: true }),
+ RateLimit({ count: 5, window: 60, onlyIp: true }),
check({
login: new Length(String, 2, 100), // email or telephone
password: new Length(String, 8, 72),
diff --git a/src/routes/auth/register.ts b/src/routes/auth/register.ts
index eb5cd97d..0dd92e16 100644
--- a/src/routes/auth/register.ts
+++ b/src/routes/auth/register.ts
@@ -12,7 +12,7 @@ const router: Router = Router();
router.post(
"/",
- RateLimit({ count: 2, window: 60 * 60 * 12, onylIp: true, success: true }),
+ RateLimit({ count: 2, window: 60 * 60 * 12, onlyIp: true, success: true }),
check({
username: new Length(String, 2, 32),
// TODO: check min password length in config
diff --git a/src/routes/channels/#channel_id/index.ts b/src/routes/channels/#channel_id/index.ts
index 434f61a5..81e5054e 100644
--- a/src/routes/channels/#channel_id/index.ts
+++ b/src/routes/channels/#channel_id/index.ts
@@ -30,7 +30,7 @@ router.delete("/", async (req: Request, res: Response) => {
// TODO: Dm channel "close" not delete
const data = toObject(channel);
- await emitEvent({ event: "CHANNEL_DELETE", data, guild_id: channel?.guild_id, channel_id } as ChannelDeleteEvent);
+ await emitEvent({ event: "CHANNEL_DELETE", data, channel_id } as ChannelDeleteEvent);
await ChannelModel.deleteOne({ id: channel_id });
@@ -51,7 +51,6 @@ router.patch("/", check(ChannelModifySchema), async (req: Request, res: Response
await emitEvent({
event: "CHANNEL_UPDATE",
data,
- guild_id: channel.guild_id,
channel_id
} as ChannelUpdateEvent);
diff --git a/src/routes/channels/#channel_id/messages/#message_id/index.ts b/src/routes/channels/#channel_id/messages/#message_id/index.ts
index 9cd63d26..a7c23d2f 100644
--- a/src/routes/channels/#channel_id/messages/#message_id/index.ts
+++ b/src/routes/channels/#channel_id/messages/#message_id/index.ts
@@ -35,7 +35,6 @@ router.patch("/", check(MessageCreateSchema), async (req: Request, res: Response
await emitEvent({
event: "MESSAGE_UPDATE",
channel_id,
- guild_id: message.guild_id,
data: { ...toObject(message), nonce: undefined }
} as MessageUpdateEvent);
@@ -60,7 +59,6 @@ router.delete("/", async (req: Request, res: Response) => {
await emitEvent({
event: "MESSAGE_DELETE",
channel_id,
- guild_id: channel.guild_id,
data: {
id: message_id,
channel_id,
diff --git a/src/routes/channels/#channel_id/messages/#message_id/reactions.ts b/src/routes/channels/#channel_id/messages/#message_id/reactions.ts
index 9f68b5cd..168a870f 100644
--- a/src/routes/channels/#channel_id/messages/#message_id/reactions.ts
+++ b/src/routes/channels/#channel_id/messages/#message_id/reactions.ts
@@ -48,7 +48,6 @@ router.delete("/", async (req: Request, res: Response) => {
await emitEvent({
event: "MESSAGE_REACTION_REMOVE_ALL",
channel_id,
- guild_id: channel.guild_id,
data: {
channel_id,
message_id,
@@ -79,7 +78,6 @@ router.delete("/:emoji", async (req: Request, res: Response) => {
await emitEvent({
event: "MESSAGE_REACTION_REMOVE_EMOJI",
channel_id,
- guild_id: channel.guild_id,
data: {
channel_id,
message_id,
@@ -140,7 +138,6 @@ router.put("/:emoji/:user_id", async (req: Request, res: Response) => {
await emitEvent({
event: "MESSAGE_REACTION_ADD",
channel_id,
- guild_id: channel.guild_id,
data: {
user_id: req.user_id,
channel_id,
@@ -179,7 +176,6 @@ router.delete("/:emoji/:user_id", async (req: Request, res: Response) => {
await emitEvent({
event: "MESSAGE_REACTION_REMOVE",
channel_id,
- guild_id: channel.guild_id,
data: {
user_id: req.user_id,
channel_id,
diff --git a/src/routes/channels/#channel_id/permissions.ts b/src/routes/channels/#channel_id/permissions.ts
index f3cef53e..12364293 100644
--- a/src/routes/channels/#channel_id/permissions.ts
+++ b/src/routes/channels/#channel_id/permissions.ts
@@ -44,7 +44,6 @@ router.put("/:overwrite_id", check({ allow: String, deny: String, type: Number,
await emitEvent({
event: "CHANNEL_UPDATE",
channel_id,
- guild_id: channel.guild_id,
data: channel
} as ChannelUpdateEvent);
@@ -64,7 +63,6 @@ router.delete("/:overwrite_id", async (req: Request, res: Response) => {
await emitEvent({
event: "CHANNEL_UPDATE",
channel_id,
- guild_id: channel.guild_id,
data: channel
} as ChannelUpdateEvent);
diff --git a/src/routes/channels/#channel_id/pins.ts b/src/routes/channels/#channel_id/pins.ts
index f5bd2ef7..65d6b975 100644
--- a/src/routes/channels/#channel_id/pins.ts
+++ b/src/routes/channels/#channel_id/pins.ts
@@ -32,14 +32,12 @@ router.put("/:message_id", async (req: Request, res: Response) => {
await emitEvent({
event: "MESSAGE_UPDATE",
channel_id,
- guild_id: channel.guild_id,
data: message
} as MessageUpdateEvent);
await emitEvent({
event: "CHANNEL_PINS_UPDATE",
channel_id,
- guild_id: channel.guild_id,
data: {
channel_id,
guild_id: channel.guild_id,
@@ -64,14 +62,12 @@ router.delete("/:message_id", async (req: Request, res: Response) => {
await emitEvent({
event: "MESSAGE_UPDATE",
channel_id,
- guild_id: channel.guild_id,
data: message
} as MessageUpdateEvent);
await emitEvent({
event: "CHANNEL_PINS_UPDATE",
channel_id,
- guild_id: channel.guild_id,
data: {
channel_id,
guild_id: channel.guild_id,
diff --git a/src/routes/channels/#channel_id/typing.ts b/src/routes/channels/#channel_id/typing.ts
index 2c2b9bc9..de549883 100644
--- a/src/routes/channels/#channel_id/typing.ts
+++ b/src/routes/channels/#channel_id/typing.ts
@@ -16,7 +16,6 @@ router.post("/", async (req: Request, res: Response) => {
await emitEvent({
event: "TYPING_START",
channel_id: channel_id,
- guild_id: channel.guild_id,
data: {
// this is the paylod
member: toObject(member),
diff --git a/src/routes/guilds/#guild_id/channels.ts b/src/routes/guilds/#guild_id/channels.ts
index 15cc7394..52361f5e 100644
--- a/src/routes/guilds/#guild_id/channels.ts
+++ b/src/routes/guilds/#guild_id/channels.ts
@@ -7,7 +7,8 @@ import {
Snowflake,
toObject,
ChannelUpdateEvent,
- AnyChannel
+ AnyChannel,
+ getPermission
} from "@fosscord/server-util";
import { HTTPError } from "lambert-server";
import { ChannelModifySchema } from "../../../schema/Channel";
@@ -25,7 +26,9 @@ router.get("/", async (req: Request, res: Response) => {
// 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;
@@ -35,16 +38,36 @@ router.post("/", check(ChannelModifySchema), async (req: Request, res: Response)
});
// TODO: check if parent_id exists
-router.patch("/", check(ChannelModifySchema), async (req: Request, res: Response) => {
- const { guild_id } = req.params;
- const body = req.body as ChannelModifySchema;
+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 guild = await GuildModel.findOne({ id: guild_id }, { id: true }).exec();
- const channel = await ChannelModel.findOneAndUpdate({ guild_id }, body).exec();
+ const permission = await getPermission(req.user_id, guild_id);
+ permission.hasThrow("MANAGE_CHANNELS");
- await emitEvent({ event: "CHANNEL_UPDATE", data: channel } as ChannelUpdateEvent);
+ const opts: any = {};
+ if (body.position) opts.position = body.position;
- res.json(toObject(channel));
-});
+ 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/users/@me/index.ts b/src/routes/users/@me/index.ts
index f6b29958..7bd4a486 100644
--- a/src/routes/users/@me/index.ts
+++ b/src/routes/users/@me/index.ts
@@ -11,16 +11,38 @@ 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.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: PublicUserProjection }).exec();
+ 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/schema/User.ts b/src/schema/User.ts
index 77ee08b4..c7478f8d 100644
--- a/src/schema/User.ts
+++ b/src/schema/User.ts
@@ -5,7 +5,10 @@ export const UserModifySchema = {
$avatar: String,
$bio: new Length(String, 0, 190),
$accent_color: Number,
- $banner: String
+ $banner: String,
+ $password: String,
+ $new_password: String,
+ $code: String // 2fa code
};
export interface UserModifySchema {
@@ -14,4 +17,7 @@ export interface UserModifySchema {
bio?: string;
accent_color?: number | null;
banner?: string | null;
+ password?: string;
+ new_password?: string;
+ code?: string;
}
diff --git a/src/util/Event.ts b/src/util/Event.ts
index 5ff027e5..d0b78a53 100644
--- a/src/util/Event.ts
+++ b/src/util/Event.ts
@@ -1,13 +1,25 @@
-import { Event, EventModel } from "@fosscord/server-util";
+import { Config, Event, EventModel, rabbitCon, rabbitCh } from "@fosscord/server-util";
export async function emitEvent(payload: Omit<Event, "created_at">) {
- const obj = {
- created_at: new Date(), // in seconds
- ...payload,
- };
- // TODO: bigint isn't working
+ if (rabbitCon) {
+ const id = (payload.channel_id || payload.user_id || payload.guild_id) as string;
+ if (!id) console.error("event doesn't contain any id", payload);
+ const data = typeof payload.data === "object" ? JSON.stringify(payload.data) : payload.data; // use rabbitmq for event transmission
- return await new EventModel(obj).save();
+ // assertQueue isn't needed, because a queue will automatically created if it doesn't exist
+ const successful = rabbitCh.sendToQueue(id, Buffer.from(`${data}`), { type: payload.event });
+ if (!successful) throw new Error("failed to send event");
+ } else {
+ // use mongodb for event transmission
+ // TODO: use event emitter for local server bundle
+ const obj = {
+ created_at: new Date(), // in seconds
+ ...payload
+ };
+ // TODO: bigint isn't working
+
+ return await new EventModel(obj).save();
+ }
}
export async function emitAuditLog(payload: any) {}
|