summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Server.ts2
-rw-r--r--src/middlewares/RateLimit.ts4
-rw-r--r--src/routes/auth/login.ts2
-rw-r--r--src/routes/auth/register.ts2
-rw-r--r--src/routes/channels/#channel_id/index.ts3
-rw-r--r--src/routes/channels/#channel_id/messages/#message_id/index.ts2
-rw-r--r--src/routes/channels/#channel_id/messages/#message_id/reactions.ts4
-rw-r--r--src/routes/channels/#channel_id/permissions.ts2
-rw-r--r--src/routes/channels/#channel_id/pins.ts4
-rw-r--r--src/routes/channels/#channel_id/typing.ts1
-rw-r--r--src/routes/guilds/#guild_id/channels.ts41
-rw-r--r--src/routes/users/@me/index.ts26
-rw-r--r--src/schema/User.ts8
-rw-r--r--src/util/Event.ts26
14 files changed, 88 insertions, 39 deletions
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) {}