From c2ce88dee726bb6c1993bf4615aa866d089ca494 Mon Sep 17 00:00:00 2001 From: Puyodead1 Date: Fri, 24 Mar 2023 21:21:21 -0400 Subject: [PATCH] guilds --- assets/openapi.json | Bin 332848 -> 393887 bytes assets/schemas.json | Bin 9267985 -> 12158116 bytes src/api/routes/guilds/#guild_id/bans.ts | 71 ++++- src/api/routes/guilds/#guild_id/channels.ts | 40 ++- src/api/routes/guilds/#guild_id/delete.ts | 62 ++-- .../#guild_id/discovery-requirements.ts | 70 +++-- src/api/routes/guilds/#guild_id/emojis.ts | 94 ++++-- src/api/routes/guilds/#guild_id/index.ts | 67 ++-- src/api/routes/guilds/#guild_id/invites.ts | 11 +- .../guilds/#guild_id/member-verification.ts | 26 +- .../#guild_id/members/#member_id/index.ts | 157 +++++++--- .../#guild_id/members/#member_id/nick.ts | 13 +- .../#member_id/roles/#role_id/index.ts | 20 +- .../routes/guilds/#guild_id/members/index.ts | 61 ++-- .../guilds/#guild_id/messages/search.ts | 261 ++++++++-------- .../routes/guilds/#guild_id/profile/index.ts | 15 +- src/api/routes/guilds/#guild_id/prune.ts | 53 +++- src/api/routes/guilds/#guild_id/regions.ts | 37 ++- .../guilds/#guild_id/roles/#role_id/index.ts | 65 +++- .../routes/guilds/#guild_id/roles/index.ts | 36 ++- src/api/routes/guilds/#guild_id/stickers.ts | 84 ++++- src/api/routes/guilds/#guild_id/templates.ts | 68 ++++- src/api/routes/guilds/#guild_id/vanity-url.ts | 31 +- .../#guild_id/voice-states/#user_id/index.ts | 16 +- .../routes/guilds/#guild_id/welcome-screen.ts | 34 ++- .../routes/guilds/#guild_id/widget.json.ts | 147 +++++---- src/api/routes/guilds/#guild_id/widget.png.ts | 286 ++++++++++-------- src/api/routes/guilds/#guild_id/widget.ts | 45 ++- src/api/routes/guilds/index.ts | 16 +- src/api/routes/guilds/templates/index.ts | 105 ++++--- src/util/entities/Guild.ts | 13 +- src/util/interfaces/GuildWelcomeScreen.ts | 10 + src/util/interfaces/index.ts | 1 + .../schemas/responses/GuildBansResponse.ts | 10 + .../responses/GuildChannelsResponse.ts | 3 + .../schemas/responses/GuildCreateResponse.ts | 3 + .../responses/GuildDiscoveryRequirements.ts | 23 ++ .../schemas/responses/GuildEmojisResponse.ts | 3 + .../schemas/responses/GuildInvitesResponse.ts | 3 + .../schemas/responses/GuildMembersResponse.ts | 3 + .../responses/GuildMessagesSearchResponse.ts | 32 ++ .../schemas/responses/GuildPruneResponse.ts | 7 + src/util/schemas/responses/GuildResponse.ts | 3 + .../schemas/responses/GuildRolesResponse.ts | 3 + .../responses/GuildStickersResponse.ts | 3 + .../responses/GuildTemplatesResponse.ts | 3 + src/util/schemas/responses/GuildVanityUrl.ts | 17 ++ .../responses/GuildVoiceRegionsResponse.ts | 9 + .../responses/GuildWidgetJsonResponse.ts | 21 ++ .../responses/GuildWidgetSettingsResponse.ts | 6 + .../responses/MemberJoinGuildResponse.ts | 8 + src/util/schemas/responses/index.ts | 18 ++ 52 files changed, 1564 insertions(+), 629 deletions(-) create mode 100644 src/util/interfaces/GuildWelcomeScreen.ts create mode 100644 src/util/schemas/responses/GuildBansResponse.ts create mode 100644 src/util/schemas/responses/GuildChannelsResponse.ts create mode 100644 src/util/schemas/responses/GuildCreateResponse.ts create mode 100644 src/util/schemas/responses/GuildDiscoveryRequirements.ts create mode 100644 src/util/schemas/responses/GuildEmojisResponse.ts create mode 100644 src/util/schemas/responses/GuildInvitesResponse.ts create mode 100644 src/util/schemas/responses/GuildMembersResponse.ts create mode 100644 src/util/schemas/responses/GuildMessagesSearchResponse.ts create mode 100644 src/util/schemas/responses/GuildPruneResponse.ts create mode 100644 src/util/schemas/responses/GuildResponse.ts create mode 100644 src/util/schemas/responses/GuildRolesResponse.ts create mode 100644 src/util/schemas/responses/GuildStickersResponse.ts create mode 100644 src/util/schemas/responses/GuildTemplatesResponse.ts create mode 100644 src/util/schemas/responses/GuildVanityUrl.ts create mode 100644 src/util/schemas/responses/GuildVoiceRegionsResponse.ts create mode 100644 src/util/schemas/responses/GuildWidgetJsonResponse.ts create mode 100644 src/util/schemas/responses/GuildWidgetSettingsResponse.ts create mode 100644 src/util/schemas/responses/MemberJoinGuildResponse.ts diff --git a/assets/openapi.json b/assets/openapi.json index bc379d16268d7c409ae553bb01d5425a46c76bdc..8cee67f13a7d7e45e689fbc6cd2fefbacd93173a 100644 GIT binary patch delta 8143 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 delta 3726 zcma)8dsJ0b8ejW-dtdGaE?!VUH}%Dclqoj-tgWjUgw(5(;J<_f0N`B-Y|FwFMA{ry?@g(@p>~EjZL>mF?y$vHPX_} zuZ6|v)+nth=H4Ye`1RQdxb#0HhL_bj@v*5S22Zw;2+X-IMV>HLQAmbkzhV~6Q+GZX zvT)|}GpEd)#SiW7&I{rbc++}{m3t(?VMl3P?T<6E9krQFtpl+tL7RXLaatelALPXP z!&(xyx~L16A0)jhWyJ1 z?9Z!4x@|}F4x!!AzlNk>Yl`NlR`mUN|E8y$w; z^$@H34@IBpo4`Hb=8H?C6-J!q;{QCS4zJXaDC~ELM4C8hI3k9Gh;k2^GpW`y^;;_L z%^?%U;K_mD6wQrfudCfq@Vk68&w3`87yM}`X8oW=;K3dc%`5lCa(|^0M_eQh4EvOz zsQ;FHNn~eZc-l@Eo}R8n>fNl(n6nD7tVMJ3jKnC>e3kqV^6nz--b+HxAoYmGc$m$^ z+GO}wS5de^b4(S*!(mZ~7?1<84(zsB>o0txU=uMvX9E!4tJ*2D$AD8agcVcZ9#42P zTrAClC?(qa0Wc!`ciBjS>6;-N{Y!x2sPXWk;nBIe=qXrj1B$d?LBC+ImnT9u^E^dz z5PzKvk@h(P0**tcLrx`;+;jXq8)rmF)R!AAG-_|2cM&U+x~o}1Qyx) zfds2pfX_bnA$V)OX>TO@`*KK7d5b0vZ<`I1pZy7*uds4b%(rg@ZXl^n%>08@TeA?i zUxzR}{RR};;bh^k(sa^|dJ&vk0}Kl{!&1Wt8y=GvBYhXU$$jrrk-r6Qm~3R3WxssY zus~I#L-%uW(@xkC#Ks<*O!1>1E-4h0L)eIy_Q1*eEYl9d2=p9;Xe`quaphxp*d!=@&SA<)IA!=4 zElF&EuweSjPD8#}@pmX^hK*1I_Uh+wM4gv^0ozP;`FRcpoQDH88m6CRVIuwlgepie z?jm$kwDe%;%_mHhe*-%@$ne>#u%%)lyoH`-nuk@lx=?e#gAh}?(pLgWW&DJM z%BpE@Z0$xXbiXeM7CcLbiQG6kJ_!3rFZz}VdwdWL#jRs$SM*PVo>;$CV+TnG`npP*Za z9zGTQn_*-imBKvby+Fgq3+TH6MeixJT16EyvplnXK_wY5pI)|uS(RVCh^`D^oL@p6 znE4{DG3gs6^48Ed34UB+8tU_;yh}Tvb-`VwbYW*g2Ucy9uiv@X=zd#mrYj`8hv<~g z5ZqBpoyv6aAE{akM#~Le37f10RqLeVc7hGB(FokJjXvKQaMJx%=;x<@woR=n9N9wy z+t?CD?4#=T2Bz-Pe!JfX4pVh+4ehq0bi3J8sR+TAWAvyU#me-Z2AX_7p%4E-&HZZ^ z){T7|sWAL?^bI#cu*-S6)eJDXnKH~d#!_+a*K~;;Mbb|HnnvNYX1dE3TA>eqNAm-r zEvX!S`Q(ncZ4qd?O}817D{oaCV*%`*>b9%(=#3=|zjCn;j41X-ZWCHKJEejI2~$0+ z#q_pzcNr;fPqeqUFpEa9zx*HOr#d8mQ|~6WXl{}4B>S8O5ZXtPtiF1h{Zy5+S~iA= zM<=moRK9w7cXv*3c?k;zY?;bJ@z>8WzYQOFG%?~Kh_ik&f73aNEZszN8$G#}#9>wq zZ4hG@uwR<*#{Z7rspp9zRuUKtgL#?kjNK_o`cTI*;ke(Cc!Or^hirjZp2fOI zyST;mJ9M4AYG>K%KuJwM^#V6CNkjiJ4&8Oy+M_m1nlaOQ-wWm1y~UV@yRI>{{%oV0My%>}txOe8O`!Omr35B$ Tm`Fds`T}N6gva&2@aynjRclXu diff --git a/assets/schemas.json b/assets/schemas.json index ca4bbd0dd7ec45f8a1af9b695d9f81c7d753cd31..854aea78028560857ba7d0288f57666b8b41f4a2 100644 GIT binary patch delta 84887 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 delta 27916 zcmeI)Z&1@^902fR!1f!PFlaC)!p1OS;9wkD0=XmOKvZyWMma)$8rb4|u@i{EOV;JJ z)T7p|<4dMC52;fbhdFvWd(kX9yDoJvLh`~6?ch#NGP0!XJbJdDFY2y$eP4dxvF*2K z+lTM>`Tu)P9GHuL=gN4TI~b$VgMVswBaG>L<>1OvW`X~=!PK_Mwotgrv%;6-{}=M6@-e0%J#jRsi)=)ug+zGc0)hSvLJ%mpU&>4ffgPpjIRDz4n=oX z%Gu5IoP0`tWLm~)Qz&5ZRN1o=xe6K=;DLHN2lJqF(EJK@q|iE29$9Zb$a)Kk$P~?# zRX44o8La3|28^{8z7*gsD%4WbgjbWYb8*&cYMKy1bytha2^10387QG?D9k$a>i#tj zicrJ|t=aZODS;w_4q8eXwepCdZL#I!6rqTq*6*JbAJ9k%ZKMSA2P*_N`iadnT%v9hLbu7|SJpGXvLPTNP6SR)S<;)uVrDR85-B)#Vec0d48UTp@^Ut7Mm+5LJ>hTzirt}popM-{vBHg6b}?`n_d*)*u|7o zaF@QJjNVRW4Gfrk;`&q&&(`R`{#aZpxD2Yyfwwj#!<{b;%OiK0DM5TQuNcC5O`7!3 zH6&z4&fv*|%M%83-hvutwX@}*1sLzaB7zMowJqWX})T-`71rT#F+ztU4-Yhx9* zT?2IZ4+dxC@E^Xfl4r&=^)3)Wf4t|k5hxxga8MOn_f7j6QpHyF9o=mqRb*AT;T~*; zP=zj)ZPf2spveV1U*@QEiPSO10_A7_O{MQ?r5XSO)Z?A*VBYD@d|IEu;Cy&C<<#e0 zyC^=fK>6(V#r7TrbNITOY2N^zD&wlxC`=JS`6A5*YUmL`|CArArj$nnJrX?LN)vmC zplOGO0<=324-|NW3ha98u#;LQJXO}R_M>O0Wx@jmzUNZKCe { const { guild_id } = req.params; @@ -73,7 +83,20 @@ router.get( router.get( "/:user", - route({ permission: "BAN_MEMBERS" }), + route({ + permission: "BAN_MEMBERS", + responses: { + 200: { + body: "BanModeratorSchema", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id } = req.params; const user_id = req.params.ban; @@ -97,7 +120,21 @@ router.get( router.put( "/:user_id", - route({ requestBody: "BanCreateSchema", permission: "BAN_MEMBERS" }), + route({ + requestBody: "BanCreateSchema", + permission: "BAN_MEMBERS", + responses: { + 200: { + body: "Ban", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id } = req.params; const banned_user_id = req.params.user_id; @@ -143,7 +180,20 @@ router.put( router.put( "/@me", - route({ requestBody: "BanCreateSchema" }), + route({ + requestBody: "BanCreateSchema", + responses: { + 200: { + body: "Ban", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id } = req.params; @@ -182,7 +232,18 @@ router.put( router.delete( "/:user_id", - route({ permission: "BAN_MEMBERS" }), + route({ + permission: "BAN_MEMBERS", + responses: { + 204: {}, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id, user_id } = req.params; diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index ff167b02..0cbfca00 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -28,18 +28,39 @@ import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; const router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; - const channels = await Channel.find({ where: { guild_id } }); +router.get( + "/", + route({ + responses: { + 201: { + body: "GuildChannelsResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; + const channels = await Channel.find({ where: { guild_id } }); - res.json(channels); -}); + res.json(channels); + }, +); router.post( "/", route({ requestBody: "ChannelModifySchema", permission: "MANAGE_CHANNELS", + responses: { + 201: { + body: "Channel", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, }), async (req: Request, res: Response) => { // creates a new guild channel https://discord.com/developers/docs/resources/guild#create-guild-channel @@ -60,6 +81,15 @@ router.patch( route({ requestBody: "ChannelReorderSchema", permission: "MANAGE_CHANNELS", + responses: { + 204: {}, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, }), async (req: Request, res: Response) => { // changes guild channel position diff --git a/src/api/routes/guilds/#guild_id/delete.ts b/src/api/routes/guilds/#guild_id/delete.ts index ec72a4ae..dee52c81 100644 --- a/src/api/routes/guilds/#guild_id/delete.ts +++ b/src/api/routes/guilds/#guild_id/delete.ts @@ -16,37 +16,51 @@ along with this program. If not, see . */ -import { emitEvent, GuildDeleteEvent, Guild } from "@spacebar/util"; -import { Router, Request, Response } from "express"; -import { HTTPError } from "lambert-server"; import { route } from "@spacebar/api"; +import { Guild, GuildDeleteEvent, emitEvent } from "@spacebar/util"; +import { Request, Response, Router } from "express"; +import { HTTPError } from "lambert-server"; 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("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; - - const guild = await Guild.findOneOrFail({ - where: { id: guild_id }, - select: ["owner_id"], - }); - if (guild.owner_id !== req.user_id) - throw new HTTPError("You are not the owner of this guild", 401); - - await Promise.all([ - Guild.delete({ id: guild_id }), // this will also delete all guild related data - emitEvent({ - event: "GUILD_DELETE", - data: { - id: guild_id, +router.post( + "/", + route({ + responses: { + 204: {}, + 401: { + body: "APIErrorResponse", }, - guild_id: guild_id, - } as GuildDeleteEvent), - ]); + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; - return res.sendStatus(204); -}); + const guild = await Guild.findOneOrFail({ + where: { id: guild_id }, + select: ["owner_id"], + }); + if (guild.owner_id !== req.user_id) + throw new HTTPError("You are not the owner of this guild", 401); + + await Promise.all([ + Guild.delete({ id: guild_id }), // this will also delete all guild related data + emitEvent({ + event: "GUILD_DELETE", + data: { + id: guild_id, + }, + guild_id: guild_id, + } as GuildDeleteEvent), + ]); + + return res.sendStatus(204); + }, +); export default router; diff --git a/src/api/routes/guilds/#guild_id/discovery-requirements.ts b/src/api/routes/guilds/#guild_id/discovery-requirements.ts index 5e15676a..76b6d895 100644 --- a/src/api/routes/guilds/#guild_id/discovery-requirements.ts +++ b/src/api/routes/guilds/#guild_id/discovery-requirements.ts @@ -16,40 +16,50 @@ along with this program. If not, see . */ -import { Router, Request, Response } from "express"; import { route } from "@spacebar/api"; +import { Request, Response, Router } from "express"; const router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; - // TODO: - // Load from database - // Admin control, but for now it allows anyone to be discoverable - - res.send({ - guild_id: guild_id, - safe_environment: true, - healthy: true, - health_score_pending: false, - size: true, - nsfw_properties: {}, - protected: true, - sufficient: true, - sufficient_without_grace_period: true, - valid_rules_channel: true, - retention_healthy: true, - engagement_healthy: true, - age: true, - minimum_age: 0, - health_score: { - avg_nonnew_participators: 0, - avg_nonnew_communicators: 0, - num_intentful_joiners: 0, - perc_ret_w1_intentful: 0, +router.get( + "/", + route({ + responses: { + 200: { + body: "GuildDiscoveryRequirements", + }, }, - minimum_size: 0, - }); -}); + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; + // TODO: + // Load from database + // Admin control, but for now it allows anyone to be discoverable + + res.send({ + guild_id: guild_id, + safe_environment: true, + healthy: true, + health_score_pending: false, + size: true, + nsfw_properties: {}, + protected: true, + sufficient: true, + sufficient_without_grace_period: true, + valid_rules_channel: true, + retention_healthy: true, + engagement_healthy: true, + age: true, + minimum_age: 0, + health_score: { + avg_nonnew_participators: 0, + avg_nonnew_communicators: 0, + num_intentful_joiners: 0, + perc_ret_w1_intentful: 0, + }, + minimum_size: 0, + }); + }, +); export default router; diff --git a/src/api/routes/guilds/#guild_id/emojis.ts b/src/api/routes/guilds/#guild_id/emojis.ts index f8707b24..b1f3c7bf 100644 --- a/src/api/routes/guilds/#guild_id/emojis.ts +++ b/src/api/routes/guilds/#guild_id/emojis.ts @@ -34,37 +34,77 @@ import { Request, Response, Router } from "express"; const router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; +router.get( + "/", + route({ + responses: { + 200: { + body: "GuildEmojisResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; - await Member.IsInGuildOrFail(req.user_id, guild_id); + await Member.IsInGuildOrFail(req.user_id, guild_id); - const emojis = await Emoji.find({ - where: { guild_id: guild_id }, - relations: ["user"], - }); + const emojis = await Emoji.find({ + where: { guild_id: guild_id }, + relations: ["user"], + }); - return res.json(emojis); -}); + return res.json(emojis); + }, +); -router.get("/:emoji_id", route({}), async (req: Request, res: Response) => { - const { guild_id, emoji_id } = req.params; +router.get( + "/:emoji_id", + route({ + responses: { + 200: { + body: "Emoji", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id, emoji_id } = req.params; - await Member.IsInGuildOrFail(req.user_id, guild_id); + await Member.IsInGuildOrFail(req.user_id, guild_id); - const emoji = await Emoji.findOneOrFail({ - where: { guild_id: guild_id, id: emoji_id }, - relations: ["user"], - }); + const emoji = await Emoji.findOneOrFail({ + where: { guild_id: guild_id, id: emoji_id }, + relations: ["user"], + }); - return res.json(emoji); -}); + return res.json(emoji); + }, +); router.post( "/", route({ requestBody: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_AND_STICKERS", + responses: { + 201: { + body: "Emoji", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, }), async (req: Request, res: Response) => { const { guild_id } = req.params; @@ -115,6 +155,14 @@ router.patch( route({ requestBody: "EmojiModifySchema", permission: "MANAGE_EMOJIS_AND_STICKERS", + responses: { + 200: { + body: "Emoji", + }, + 403: { + body: "APIErrorResponse", + }, + }, }), async (req: Request, res: Response) => { const { emoji_id, guild_id } = req.params; @@ -141,7 +189,15 @@ router.patch( router.delete( "/:emoji_id", - route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), + route({ + permission: "MANAGE_EMOJIS_AND_STICKERS", + responses: { + 204: {}, + 403: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { emoji_id, guild_id } = req.params; diff --git a/src/api/routes/guilds/#guild_id/index.ts b/src/api/routes/guilds/#guild_id/index.ts index 46346008..7c8f583e 100644 --- a/src/api/routes/guilds/#guild_id/index.ts +++ b/src/api/routes/guilds/#guild_id/index.ts @@ -34,28 +34,61 @@ import { HTTPError } from "lambert-server"; const router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; +router.get( + "/", + route({ + responses: { + "200": { + body: "GuildResponse", + }, + 401: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; - const [guild, member] = await Promise.all([ - Guild.findOneOrFail({ where: { id: guild_id } }), - Member.findOne({ where: { 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, - ); + const [guild, member] = await Promise.all([ + Guild.findOneOrFail({ where: { id: guild_id } }), + Member.findOne({ where: { 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.send({ - ...guild, - joined_at: member?.joined_at, - }); -}); + return res.send({ + ...guild, + joined_at: member?.joined_at, + }); + }, +); router.patch( "/", - route({ requestBody: "GuildUpdateSchema", permission: "MANAGE_GUILD" }), + route({ + requestBody: "GuildUpdateSchema", + permission: "MANAGE_GUILD", + responses: { + "200": { + body: "GuildUpdateSchema", + }, + 401: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const body = req.body as GuildUpdateSchema; const { guild_id } = req.params; diff --git a/src/api/routes/guilds/#guild_id/invites.ts b/src/api/routes/guilds/#guild_id/invites.ts index 9c446928..219b6a4f 100644 --- a/src/api/routes/guilds/#guild_id/invites.ts +++ b/src/api/routes/guilds/#guild_id/invites.ts @@ -16,15 +16,22 @@ along with this program. If not, see . */ -import { Invite, PublicInviteRelation } from "@spacebar/util"; import { route } from "@spacebar/api"; +import { Invite, PublicInviteRelation } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); router.get( "/", - route({ permission: "MANAGE_GUILD" }), + route({ + permission: "MANAGE_GUILD", + responses: { + 200: { + body: "GuildInvitesResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id } = req.params; diff --git a/src/api/routes/guilds/#guild_id/member-verification.ts b/src/api/routes/guilds/#guild_id/member-verification.ts index 242f3684..2c39093e 100644 --- a/src/api/routes/guilds/#guild_id/member-verification.ts +++ b/src/api/routes/guilds/#guild_id/member-verification.ts @@ -16,17 +16,27 @@ along with this program. If not, see . */ -import { Router, Request, Response } from "express"; import { route } from "@spacebar/api"; +import { Request, Response, Router } from "express"; const router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - // TODO: member verification +router.get( + "/", + route({ + responses: { + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + // TODO: member verification - res.status(404).json({ - message: "Unknown Guild Member Verification Form", - code: 10068, - }); -}); + res.status(404).json({ + message: "Unknown Guild Member Verification Form", + code: 10068, + }); + }, +); export default router; diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts index 814c8f8b..5f1f6fa7 100644 --- a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts +++ b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts @@ -34,20 +34,52 @@ import { Request, Response, Router } from "express"; const router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id, member_id } = req.params; - await Member.IsInGuildOrFail(req.user_id, guild_id); +router.get( + "/", + route({ + responses: { + 200: { + body: "Member", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id, member_id } = req.params; + await Member.IsInGuildOrFail(req.user_id, guild_id); - const member = await Member.findOneOrFail({ - where: { id: member_id, guild_id }, - }); + const member = await Member.findOneOrFail({ + where: { id: member_id, guild_id }, + }); - return res.json(member); -}); + return res.json(member); + }, +); router.patch( "/", - route({ requestBody: "MemberChangeSchema" }), + route({ + requestBody: "MemberChangeSchema", + responses: { + 200: { + body: "Member", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id } = req.params; const member_id = @@ -119,54 +151,81 @@ router.patch( }, ); -router.put("/", route({}), async (req: Request, res: Response) => { - // TODO: Lurker mode +router.put( + "/", + route({ + responses: { + 200: { + body: "MemberJoinGuildResponse", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + // TODO: Lurker mode - const rights = await getRights(req.user_id); + const rights = await getRights(req.user_id); - const { guild_id } = req.params; - let { member_id } = req.params; - if (member_id === "@me") { - member_id = req.user_id; - rights.hasThrow("JOIN_GUILDS"); - } else { - // TODO: join others by controller - } + const { guild_id } = req.params; + let { member_id } = req.params; + if (member_id === "@me") { + member_id = req.user_id; + rights.hasThrow("JOIN_GUILDS"); + } else { + // TODO: join others by controller + } - const guild = await Guild.findOneOrFail({ - where: { id: guild_id }, - }); + const guild = await Guild.findOneOrFail({ + where: { id: guild_id }, + }); - const emoji = await Emoji.find({ - where: { guild_id: guild_id }, - }); + const emoji = await Emoji.find({ + where: { guild_id: guild_id }, + }); - const roles = await Role.find({ - where: { guild_id: guild_id }, - }); + const roles = await Role.find({ + where: { guild_id: guild_id }, + }); - const stickers = await Sticker.find({ - where: { guild_id: guild_id }, - }); + const stickers = await Sticker.find({ + where: { guild_id: guild_id }, + }); - await Member.addToGuild(member_id, guild_id); - res.send({ ...guild, emojis: emoji, roles: roles, stickers: stickers }); -}); + await Member.addToGuild(member_id, guild_id); + res.send({ ...guild, emojis: emoji, roles: roles, stickers: stickers }); + }, +); -router.delete("/", route({}), async (req: Request, res: Response) => { - const { guild_id, member_id } = req.params; - const permission = await getPermission(req.user_id, guild_id); - const rights = await getRights(req.user_id); - if (member_id === "@me" || member_id === req.user_id) { - // TODO: unless force-joined - rights.hasThrow("SELF_LEAVE_GROUPS"); - } else { - rights.hasThrow("KICK_BAN_MEMBERS"); - permission.hasThrow("KICK_MEMBERS"); - } +router.delete( + "/", + route({ + responses: { + 204: {}, + 403: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id, member_id } = req.params; + const permission = await getPermission(req.user_id, guild_id); + const rights = await getRights(req.user_id); + if (member_id === "@me" || member_id === req.user_id) { + // TODO: unless force-joined + rights.hasThrow("SELF_LEAVE_GROUPS"); + } else { + rights.hasThrow("KICK_BAN_MEMBERS"); + permission.hasThrow("KICK_MEMBERS"); + } - await Member.removeFromGuild(member_id, guild_id); - res.sendStatus(204); -}); + await Member.removeFromGuild(member_id, guild_id); + res.sendStatus(204); + }, +); export default router; diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts b/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts index 41aaa84b..7b8e44d3 100644 --- a/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts +++ b/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts @@ -24,7 +24,18 @@ const router = Router(); router.patch( "/", - route({ requestBody: "MemberNickChangeSchema" }), + route({ + requestBody: "MemberNickChangeSchema", + responses: { + 200: {}, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id } = req.params; let permissionString: PermissionResolvable = "MANAGE_NICKNAMES"; diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts b/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts index 698df88f..46dd70bb 100644 --- a/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts +++ b/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts @@ -16,15 +16,23 @@ along with this program. If not, see . */ -import { Member } from "@spacebar/util"; import { route } from "@spacebar/api"; +import { Member } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); router.delete( "/", - route({ permission: "MANAGE_ROLES" }), + route({ + permission: "MANAGE_ROLES", + responses: { + 204: {}, + 403: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id, role_id, member_id } = req.params; @@ -35,7 +43,13 @@ router.delete( router.put( "/", - route({ permission: "MANAGE_ROLES" }), + route({ + permission: "MANAGE_ROLES", + responses: { + 204: {}, + 403: {}, + }, + }), async (req: Request, res: Response) => { const { guild_id, role_id, member_id } = req.params; diff --git a/src/api/routes/guilds/#guild_id/members/index.ts b/src/api/routes/guilds/#guild_id/members/index.ts index f7a55cf1..1ab9dd28 100644 --- a/src/api/routes/guilds/#guild_id/members/index.ts +++ b/src/api/routes/guilds/#guild_id/members/index.ts @@ -16,35 +16,58 @@ along with this program. If not, see . */ -import { Request, Response, Router } from "express"; -import { Member, PublicMemberProjection } from "@spacebar/util"; import { route } from "@spacebar/api"; -import { MoreThan } from "typeorm"; +import { Member, PublicMemberProjection } from "@spacebar/util"; +import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; +import { MoreThan } from "typeorm"; const router = Router(); // TODO: send over websocket // TODO: check for GUILD_MEMBERS intent -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; - const limit = Number(req.query.limit) || 1; - if (limit > 1000 || limit < 1) - throw new HTTPError("Limit must be between 1 and 1000"); - const after = `${req.query.after}`; - const query = after ? { id: MoreThan(after) } : {}; +router.get( + "/", + route({ + query: { + limit: { + type: "number", + description: + "max number of members to return (1-1000). default 1", + }, + after: { + type: "string", + }, + }, + responses: { + 200: { + body: "GuildMembersResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; + const limit = Number(req.query.limit) || 1; + if (limit > 1000 || limit < 1) + throw new HTTPError("Limit must be between 1 and 1000"); + const after = `${req.query.after}`; + const query = after ? { id: MoreThan(after) } : {}; - await Member.IsInGuildOrFail(req.user_id, guild_id); + await Member.IsInGuildOrFail(req.user_id, guild_id); - const members = await Member.find({ - where: { guild_id, ...query }, - select: PublicMemberProjection, - take: limit, - order: { id: "ASC" }, - }); + const members = await Member.find({ + where: { guild_id, ...query }, + select: PublicMemberProjection, + take: limit, + order: { id: "ASC" }, + }); - return res.json(members); -}); + return res.json(members); + }, +); export default router; diff --git a/src/api/routes/guilds/#guild_id/messages/search.ts b/src/api/routes/guilds/#guild_id/messages/search.ts index bc5f1b6e..637d1e43 100644 --- a/src/api/routes/guilds/#guild_id/messages/search.ts +++ b/src/api/routes/guilds/#guild_id/messages/search.ts @@ -18,140 +18,159 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { Request, Response, Router } from "express"; import { route } from "@spacebar/api"; -import { getPermission, FieldErrors, Message, Channel } from "@spacebar/util"; +import { Channel, FieldErrors, Message, getPermission } from "@spacebar/util"; +import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; import { FindManyOptions, In, Like } from "typeorm"; const router: Router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - const { - channel_id, - content, - // include_nsfw, // TODO - offset, - sort_order, - // sort_by, // TODO: Handle 'relevance' - limit, - author_id, - } = req.query; - - const parsedLimit = Number(limit) || 50; - if (parsedLimit < 1 || parsedLimit > 100) - throw new HTTPError("limit must be between 1 and 100", 422); - - if (sort_order) { - if ( - typeof sort_order != "string" || - ["desc", "asc"].indexOf(sort_order) == -1 - ) - throw FieldErrors({ - sort_order: { - message: "Value must be one of ('desc', 'asc').", - code: "BASE_TYPE_CHOICES", - }, - }); // todo this is wrong - } - - const permissions = await getPermission( - req.user_id, - req.params.guild_id, - channel_id as string | undefined, - ); - permissions.hasThrow("VIEW_CHANNEL"); - if (!permissions.has("READ_MESSAGE_HISTORY")) - return res.json({ messages: [], total_results: 0 }); - - const query: FindManyOptions = { - order: { - timestamp: sort_order - ? (sort_order.toUpperCase() as "ASC" | "DESC") - : "DESC", - }, - take: parsedLimit || 0, - where: { - guild: { - id: req.params.guild_id, +router.get( + "/", + route({ + responses: { + 200: { + body: "GuildMessagesSearchResponse", + }, + 403: { + body: "APIErrorResponse", + }, + 422: { + body: "APIErrorResponse", }, }, - relations: [ - "author", - "webhook", - "application", - "mentions", - "mention_roles", - "mention_channels", - "sticker_items", - "attachments", - ], - skip: offset ? Number(offset) : 0, - }; - //@ts-ignore - if (channel_id) query.where.channel = { id: channel_id }; - else { - // get all channel IDs that this user can access - const channels = await Channel.find({ - where: { guild_id: req.params.guild_id }, - select: ["id"], - }); - const ids = []; + }), + async (req: Request, res: Response) => { + const { + channel_id, + content, + // include_nsfw, // TODO + offset, + sort_order, + // sort_by, // TODO: Handle 'relevance' + limit, + author_id, + } = req.query; - for (const channel of channels) { - const perm = await getPermission( - req.user_id, - req.params.guild_id, - channel.id, - ); - if (!perm.has("VIEW_CHANNEL") || !perm.has("READ_MESSAGE_HISTORY")) - continue; - ids.push(channel.id); + const parsedLimit = Number(limit) || 50; + if (parsedLimit < 1 || parsedLimit > 100) + throw new HTTPError("limit must be between 1 and 100", 422); + + if (sort_order) { + if ( + typeof sort_order != "string" || + ["desc", "asc"].indexOf(sort_order) == -1 + ) + throw FieldErrors({ + sort_order: { + message: "Value must be one of ('desc', 'asc').", + code: "BASE_TYPE_CHOICES", + }, + }); // todo this is wrong } - //@ts-ignore - query.where.channel = { id: In(ids) }; - } - //@ts-ignore - if (author_id) query.where.author = { id: author_id }; - //@ts-ignore - if (content) query.where.content = Like(`%${content}%`); + const permissions = await getPermission( + req.user_id, + req.params.guild_id, + channel_id as string | undefined, + ); + permissions.hasThrow("VIEW_CHANNEL"); + if (!permissions.has("READ_MESSAGE_HISTORY")) + return res.json({ messages: [], total_results: 0 }); - const messages: Message[] = await Message.find(query); - - const messagesDto = messages.map((x) => [ - { - id: x.id, - type: x.type, - content: x.content, - channel_id: x.channel_id, - author: { - id: x.author?.id, - username: x.author?.username, - avatar: x.author?.avatar, - avatar_decoration: null, - discriminator: x.author?.discriminator, - public_flags: x.author?.public_flags, + const query: FindManyOptions = { + order: { + timestamp: sort_order + ? (sort_order.toUpperCase() as "ASC" | "DESC") + : "DESC", }, - attachments: x.attachments, - embeds: x.embeds, - mentions: x.mentions, - mention_roles: x.mention_roles, - pinned: x.pinned, - mention_everyone: x.mention_everyone, - tts: x.tts, - timestamp: x.timestamp, - edited_timestamp: x.edited_timestamp, - flags: x.flags, - components: x.components, - hit: true, - }, - ]); + take: parsedLimit || 0, + where: { + guild: { + id: req.params.guild_id, + }, + }, + relations: [ + "author", + "webhook", + "application", + "mentions", + "mention_roles", + "mention_channels", + "sticker_items", + "attachments", + ], + skip: offset ? Number(offset) : 0, + }; + //@ts-ignore + if (channel_id) query.where.channel = { id: channel_id }; + else { + // get all channel IDs that this user can access + const channels = await Channel.find({ + where: { guild_id: req.params.guild_id }, + select: ["id"], + }); + const ids = []; - return res.json({ - messages: messagesDto, - total_results: messages.length, - }); -}); + for (const channel of channels) { + const perm = await getPermission( + req.user_id, + req.params.guild_id, + channel.id, + ); + if ( + !perm.has("VIEW_CHANNEL") || + !perm.has("READ_MESSAGE_HISTORY") + ) + continue; + ids.push(channel.id); + } + + //@ts-ignore + query.where.channel = { id: In(ids) }; + } + //@ts-ignore + if (author_id) query.where.author = { id: author_id }; + //@ts-ignore + if (content) query.where.content = Like(`%${content}%`); + + const messages: Message[] = await Message.find(query); + + const messagesDto = messages.map((x) => [ + { + id: x.id, + type: x.type, + content: x.content, + channel_id: x.channel_id, + author: { + id: x.author?.id, + username: x.author?.username, + avatar: x.author?.avatar, + avatar_decoration: null, + discriminator: x.author?.discriminator, + public_flags: x.author?.public_flags, + }, + attachments: x.attachments, + embeds: x.embeds, + mentions: x.mentions, + mention_roles: x.mention_roles, + pinned: x.pinned, + mention_everyone: x.mention_everyone, + tts: x.tts, + timestamp: x.timestamp, + edited_timestamp: x.edited_timestamp, + flags: x.flags, + components: x.components, + hit: true, + }, + ]); + + return res.json({ + messages: messagesDto, + total_results: messages.length, + }); + }, +); export default router; diff --git a/src/api/routes/guilds/#guild_id/profile/index.ts b/src/api/routes/guilds/#guild_id/profile/index.ts index 32de8653..60526259 100644 --- a/src/api/routes/guilds/#guild_id/profile/index.ts +++ b/src/api/routes/guilds/#guild_id/profile/index.ts @@ -31,7 +31,20 @@ const router = Router(); router.patch( "/:member_id", - route({ requestBody: "MemberChangeProfileSchema" }), + route({ + requestBody: "MemberChangeProfileSchema", + responses: { + 200: { + body: "Member", + }, + 400: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id } = req.params; // const member_id = diff --git a/src/api/routes/guilds/#guild_id/prune.ts b/src/api/routes/guilds/#guild_id/prune.ts index dbed546b..92ea91fc 100644 --- a/src/api/routes/guilds/#guild_id/prune.ts +++ b/src/api/routes/guilds/#guild_id/prune.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { Router, Request, Response } from "express"; -import { Guild, Member, Snowflake } from "@spacebar/util"; -import { LessThan, IsNull } from "typeorm"; import { route } from "@spacebar/api"; +import { Guild, Member, Snowflake } from "@spacebar/util"; +import { Request, Response, Router } from "express"; +import { IsNull, LessThan } from "typeorm"; const router = Router(); //Returns all inactive members, respecting role hierarchy @@ -80,25 +80,46 @@ export const inactiveMembers = async ( return members; }; -router.get("/", route({}), async (req: Request, res: Response) => { - const days = parseInt(req.query.days as string); +router.get( + "/", + route({ + responses: { + "200": { + body: "GuildPruneResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const days = parseInt(req.query.days as string); - let roles = req.query.include_roles; - if (typeof roles === "string") roles = [roles]; //express will return array otherwise + let roles = req.query.include_roles; + if (typeof roles === "string") roles = [roles]; //express will return array otherwise - const members = await inactiveMembers( - req.params.guild_id, - req.user_id, - days, - roles as string[], - ); + const members = await inactiveMembers( + req.params.guild_id, + req.user_id, + days, + roles as string[], + ); - res.send({ pruned: members.length }); -}); + res.send({ pruned: members.length }); + }, +); router.post( "/", - route({ permission: "KICK_MEMBERS", right: "KICK_BAN_MEMBERS" }), + route({ + permission: "KICK_MEMBERS", + right: "KICK_BAN_MEMBERS", + responses: { + 200: { + body: "GuildPurgeResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const days = parseInt(req.body.days); diff --git a/src/api/routes/guilds/#guild_id/regions.ts b/src/api/routes/guilds/#guild_id/regions.ts index de1e8769..166c9625 100644 --- a/src/api/routes/guilds/#guild_id/regions.ts +++ b/src/api/routes/guilds/#guild_id/regions.ts @@ -16,22 +16,35 @@ along with this program. If not, see . */ +import { getIpAdress, getVoiceRegions, route } from "@spacebar/api"; import { Guild } from "@spacebar/util"; import { Request, Response, Router } from "express"; -import { getVoiceRegions, route, getIpAdress } from "@spacebar/api"; const router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; - const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); - //TODO we should use an enum for guild's features and not hardcoded strings - return res.json( - await getVoiceRegions( - getIpAdress(req), - guild.features.includes("VIP_REGIONS"), - ), - ); -}); +router.get( + "/", + route({ + responses: { + 200: { + body: "GuildVoiceRegionsResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; + const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); + //TODO we should use an enum for guild's features and not hardcoded strings + return res.json( + await getVoiceRegions( + getIpAdress(req), + guild.features.includes("VIP_REGIONS"), + ), + ); + }, +); export default router; diff --git a/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts b/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts index c7f1a8e8..ea1a782a 100644 --- a/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts +++ b/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts @@ -31,16 +31,48 @@ import { HTTPError } from "lambert-server"; const router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id, role_id } = req.params; - await Member.IsInGuildOrFail(req.user_id, guild_id); - const role = await Role.findOneOrFail({ where: { guild_id, id: role_id } }); - return res.json(role); -}); +router.get( + "/", + route({ + responses: { + 200: { + body: "Role", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id, role_id } = req.params; + await Member.IsInGuildOrFail(req.user_id, guild_id); + const role = await Role.findOneOrFail({ + where: { guild_id, id: role_id }, + }); + return res.json(role); + }, +); router.delete( "/", - route({ permission: "MANAGE_ROLES" }), + route({ + permission: "MANAGE_ROLES", + responses: { + 204: {}, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id, role_id } = req.params; if (role_id === guild_id) @@ -69,7 +101,24 @@ router.delete( router.patch( "/", - route({ requestBody: "RoleModifySchema", permission: "MANAGE_ROLES" }), + route({ + requestBody: "RoleModifySchema", + permission: "MANAGE_ROLES", + responses: { + 200: { + body: "Role", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { role_id, guild_id } = req.params; const body = req.body as RoleModifySchema; diff --git a/src/api/routes/guilds/#guild_id/roles/index.ts b/src/api/routes/guilds/#guild_id/roles/index.ts index 0efafab7..77d84347 100644 --- a/src/api/routes/guilds/#guild_id/roles/index.ts +++ b/src/api/routes/guilds/#guild_id/roles/index.ts @@ -21,7 +21,6 @@ import { Config, DiscordApiErrors, emitEvent, - getPermission, GuildRoleCreateEvent, GuildRoleUpdateEvent, Member, @@ -47,7 +46,21 @@ router.get("/", route({}), async (req: Request, res: Response) => { router.post( "/", - route({ requestBody: "RoleModifySchema", permission: "MANAGE_ROLES" }), + route({ + requestBody: "RoleModifySchema", + permission: "MANAGE_ROLES", + responses: { + 200: { + body: "Role", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const guild_id = req.params.guild_id; const body = req.body as RoleModifySchema; @@ -104,14 +117,25 @@ router.post( router.patch( "/", - route({ requestBody: "RolePositionUpdateSchema" }), + route({ + requestBody: "RolePositionUpdateSchema", + permission: "MANAGE_ROLES", + responses: { + 200: { + body: "GuildRolesResponse", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id } = req.params; const body = req.body as RolePositionUpdateSchema; - const perms = await getPermission(req.user_id, guild_id); - perms.hasThrow("MANAGE_ROLES"); - await Promise.all( body.map(async (x) => Role.update({ guild_id, id: x.id }, { position: x.position }), diff --git a/src/api/routes/guilds/#guild_id/stickers.ts b/src/api/routes/guilds/#guild_id/stickers.ts index 2e9470ec..38b10e7d 100644 --- a/src/api/routes/guilds/#guild_id/stickers.ts +++ b/src/api/routes/guilds/#guild_id/stickers.ts @@ -33,12 +33,25 @@ import { HTTPError } from "lambert-server"; import multer from "multer"; const router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; - await Member.IsInGuildOrFail(req.user_id, guild_id); +router.get( + "/", + route({ + responses: { + 200: { + body: "GuildStickersResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; + await Member.IsInGuildOrFail(req.user_id, guild_id); - res.json(await Sticker.find({ where: { guild_id } })); -}); + res.json(await Sticker.find({ where: { guild_id } })); + }, +); const bodyParser = multer({ limits: { @@ -55,6 +68,17 @@ router.post( route({ permission: "MANAGE_EMOJIS_AND_STICKERS", requestBody: "ModifyGuildStickerSchema", + responses: { + 200: { + body: "Sticker", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, }), async (req: Request, res: Response) => { if (!req.file) throw new HTTPError("missing file"); @@ -98,20 +122,46 @@ export function getStickerFormat(mime_type: string) { } } -router.get("/:sticker_id", route({}), async (req: Request, res: Response) => { - const { guild_id, sticker_id } = req.params; - await Member.IsInGuildOrFail(req.user_id, guild_id); +router.get( + "/:sticker_id", + route({ + responses: { + 200: { + body: "Sticker", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id, sticker_id } = req.params; + await Member.IsInGuildOrFail(req.user_id, guild_id); - res.json( - await Sticker.findOneOrFail({ where: { guild_id, id: sticker_id } }), - ); -}); + res.json( + await Sticker.findOneOrFail({ + where: { guild_id, id: sticker_id }, + }), + ); + }, +); router.patch( "/:sticker_id", route({ requestBody: "ModifyGuildStickerSchema", permission: "MANAGE_EMOJIS_AND_STICKERS", + responses: { + 200: { + body: "Sticker", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, }), async (req: Request, res: Response) => { const { guild_id, sticker_id } = req.params; @@ -141,7 +191,15 @@ async function sendStickerUpdateEvent(guild_id: string) { router.delete( "/:sticker_id", - route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), + route({ + permission: "MANAGE_EMOJIS_AND_STICKERS", + responses: { + 204: {}, + 403: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id, sticker_id } = req.params; diff --git a/src/api/routes/guilds/#guild_id/templates.ts b/src/api/routes/guilds/#guild_id/templates.ts index cb517083..12b235c7 100644 --- a/src/api/routes/guilds/#guild_id/templates.ts +++ b/src/api/routes/guilds/#guild_id/templates.ts @@ -40,19 +40,46 @@ const TemplateGuildProjection: (keyof Guild)[] = [ "icon", ]; -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; +router.get( + "/", + route({ + responses: { + 200: { + body: "GuildTemplatesResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; - const templates = await Template.find({ - where: { source_guild_id: guild_id }, - }); + const templates = await Template.find({ + where: { source_guild_id: guild_id }, + }); - return res.json(templates); -}); + return res.json(templates); + }, +); router.post( "/", - route({ requestBody: "TemplateCreateSchema", permission: "MANAGE_GUILD" }), + route({ + requestBody: "TemplateCreateSchema", + permission: "MANAGE_GUILD", + responses: { + 200: { + body: "Template", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id } = req.params; const guild = await Guild.findOneOrFail({ @@ -80,7 +107,13 @@ router.post( router.delete( "/:code", - route({ permission: "MANAGE_GUILD" }), + route({ + permission: "MANAGE_GUILD", + responses: { + 200: { body: "Template" }, + 403: { body: "APIErrorResponse" }, + }, + }), async (req: Request, res: Response) => { const { code, guild_id } = req.params; @@ -95,7 +128,13 @@ router.delete( router.put( "/:code", - route({ permission: "MANAGE_GUILD" }), + route({ + permission: "MANAGE_GUILD", + responses: { + 200: { body: "Template" }, + 403: { body: "APIErrorResponse" }, + }, + }), async (req: Request, res: Response) => { const { code, guild_id } = req.params; const guild = await Guild.findOneOrFail({ @@ -114,7 +153,14 @@ router.put( router.patch( "/:code", - route({ requestBody: "TemplateModifySchema", permission: "MANAGE_GUILD" }), + route({ + requestBody: "TemplateModifySchema", + permission: "MANAGE_GUILD", + responses: { + 200: { body: "Template" }, + 403: { body: "APIErrorResponse" }, + }, + }), async (req: Request, res: Response) => { const { code, guild_id } = req.params; const { name, description } = req.body; diff --git a/src/api/routes/guilds/#guild_id/vanity-url.ts b/src/api/routes/guilds/#guild_id/vanity-url.ts index 73620f8b..d271c976 100644 --- a/src/api/routes/guilds/#guild_id/vanity-url.ts +++ b/src/api/routes/guilds/#guild_id/vanity-url.ts @@ -33,7 +33,20 @@ const InviteRegex = /\W/g; router.get( "/", - route({ permission: "MANAGE_GUILD" }), + route({ + permission: "MANAGE_GUILD", + responses: { + 200: { + body: "GuildVanityUrlResponse", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id } = req.params; const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); @@ -60,7 +73,21 @@ router.get( router.patch( "/", - route({ requestBody: "VanityUrlSchema", permission: "MANAGE_GUILD" }), + route({ + requestBody: "VanityUrlSchema", + permission: "MANAGE_GUILD", + responses: { + 200: { + body: "GuildVanityUrlCreateResponse", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const { guild_id } = req.params; const body = req.body as VanityUrlSchema; diff --git a/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts b/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts index ff1bc487..60c69075 100644 --- a/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts +++ b/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts @@ -34,7 +34,21 @@ const router = Router(); router.patch( "/", - route({ requestBody: "VoiceStateUpdateSchema" }), + route({ + requestBody: "VoiceStateUpdateSchema", + responses: { + 204: {}, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const body = req.body as VoiceStateUpdateSchema; const { guild_id } = req.params; diff --git a/src/api/routes/guilds/#guild_id/welcome-screen.ts b/src/api/routes/guilds/#guild_id/welcome-screen.ts index 35320e0c..2a739683 100644 --- a/src/api/routes/guilds/#guild_id/welcome-screen.ts +++ b/src/api/routes/guilds/#guild_id/welcome-screen.ts @@ -23,20 +23,42 @@ import { HTTPError } from "lambert-server"; const router: Router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - const guild_id = req.params.guild_id; +router.get( + "/", + route({ + responses: { + 200: { + body: "GuildWelcomeScreen", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const guild_id = req.params.guild_id; - const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); - await Member.IsInGuildOrFail(req.user_id, guild_id); + const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); + await Member.IsInGuildOrFail(req.user_id, guild_id); - res.json(guild.welcome_screen); -}); + res.json(guild.welcome_screen); + }, +); router.patch( "/", route({ requestBody: "GuildUpdateWelcomeScreenSchema", permission: "MANAGE_GUILD", + responses: { + 204: {}, + 400: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, }), async (req: Request, res: Response) => { const guild_id = req.params.guild_id; diff --git a/src/api/routes/guilds/#guild_id/widget.json.ts b/src/api/routes/guilds/#guild_id/widget.json.ts index 1799f0be..69b5d48c 100644 --- a/src/api/routes/guilds/#guild_id/widget.json.ts +++ b/src/api/routes/guilds/#guild_id/widget.json.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { Request, Response, Router } from "express"; -import { Permissions, Guild, Invite, Channel, Member } from "@spacebar/util"; -import { HTTPError } from "lambert-server"; import { random, route } from "@spacebar/api"; +import { Channel, Guild, Invite, Member, Permissions } from "@spacebar/util"; +import { Request, Response, Router } from "express"; +import { HTTPError } from "lambert-server"; const router: Router = Router(); @@ -32,77 +32,90 @@ const router: Router = Router(); // 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("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; +router.get( + "/", + route({ + responses: { + 200: { + body: "GuildWidgetJsonResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; - const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); - if (!guild.widget_enabled) throw new HTTPError("Widget Disabled", 404); + const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); + if (!guild.widget_enabled) throw new HTTPError("Widget Disabled", 404); - // Fetch existing widget invite for widget channel - let invite = await Invite.findOne({ - where: { channel_id: guild.widget_channel_id }, - }); + // Fetch existing widget invite for widget channel + let invite = await Invite.findOne({ + where: { channel_id: guild.widget_channel_id }, + }); - 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()); + 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()); - invite = await Invite.create({ - 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, - }).save(); - } - - // Fetch voice channels, and the @everyone permissions object - const channels: { id: string; name: string; position: number }[] = []; - - ( - await Channel.find({ - where: { guild_id: guild_id, type: 2 }, - order: { position: "ASC" }, - }) - ).filter((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 ?? "Unknown channel", - position: doc.position ?? 0, - }); + invite = await Invite.create({ + 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, + }).save(); } - }); - // Fetch members - // TODO: Understand how Discord's max 100 random member sample works, and apply to here (see top of this file) - const members = await Member.find({ where: { guild_id: guild_id } }); + // Fetch voice channels, and the @everyone permissions object + const channels: { id: string; name: string; position: number }[] = []; - // 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, - }; + ( + await Channel.find({ + where: { guild_id: guild_id, type: 2 }, + order: { position: "ASC" }, + }) + ).filter((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 ?? "Unknown channel", + position: doc.position ?? 0, + }); + } + }); - res.set("Cache-Control", "public, max-age=300"); - return res.json(data); -}); + // Fetch members + // TODO: Understand how Discord's max 100 random member sample works, and apply to here (see top of this file) + const members = await Member.find({ where: { guild_id: guild_id } }); + + // 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/api/routes/guilds/#guild_id/widget.png.ts b/src/api/routes/guilds/#guild_id/widget.png.ts index 4e975603..c9ba8afc 100644 --- a/src/api/routes/guilds/#guild_id/widget.png.ts +++ b/src/api/routes/guilds/#guild_id/widget.png.ts @@ -18,11 +18,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Request, Response, Router } from "express"; -import { Guild } from "@spacebar/util"; -import { HTTPError } from "lambert-server"; import { route } from "@spacebar/api"; +import { Guild } from "@spacebar/util"; +import { Request, Response, Router } from "express"; import fs from "fs"; +import { HTTPError } from "lambert-server"; import path from "path"; const router: Router = Router(); @@ -31,130 +31,178 @@ const router: Router = Router(); // https://discord.com/developers/docs/resources/guild#get-guild-widget-image // TODO: Cache the response -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; +router.get( + "/", + route({ + responses: { + 200: {}, + 400: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; - const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); - if (!guild.widget_enabled) throw new HTTPError("Unknown Guild", 404); + const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); + 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 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 Spacebar 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: + // 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, ); - } + } - // 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); -}); + // Setup canvas + const { createCanvas } = require("canvas"); + const { loadImage } = require("canvas"); + const sizeOf = require("image-size"); + + // TODO: Widget style templates need Spacebar 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, diff --git a/src/api/routes/guilds/#guild_id/widget.ts b/src/api/routes/guilds/#guild_id/widget.ts index 2cacd8d3..cae0d6be 100644 --- a/src/api/routes/guilds/#guild_id/widget.ts +++ b/src/api/routes/guilds/#guild_id/widget.ts @@ -23,21 +23,48 @@ import { Request, Response, Router } from "express"; const router: Router = Router(); // https://discord.com/developers/docs/resources/guild#get-guild-widget-settings -router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; +router.get( + "/", + route({ + responses: { + 200: { + body: "GuildWidgetSettingsResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; - const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); + const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); - return res.json({ - enabled: guild.widget_enabled || false, - channel_id: guild.widget_channel_id || null, - }); -}); + 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( "/", - route({ requestBody: "WidgetModifySchema", permission: "MANAGE_GUILD" }), + route({ + requestBody: "WidgetModifySchema", + permission: "MANAGE_GUILD", + responses: { + 200: { + body: "WidgetModifySchema", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const body = req.body as WidgetModifySchema; const { guild_id } = req.params; diff --git a/src/api/routes/guilds/index.ts b/src/api/routes/guilds/index.ts index 55fe088e..26173ed5 100644 --- a/src/api/routes/guilds/index.ts +++ b/src/api/routes/guilds/index.ts @@ -33,7 +33,21 @@ const router: Router = Router(); router.post( "/", - route({ requestBody: "GuildCreateSchema", right: "CREATE_GUILDS" }), + route({ + requestBody: "GuildCreateSchema", + right: "CREATE_GUILDS", + responses: { + 201: { + body: "GuildCreateResponse", + }, + 400: { + body: "APIErrorResponse", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), async (req: Request, res: Response) => { const body = req.body as GuildCreateSchema; diff --git a/src/api/routes/guilds/templates/index.ts b/src/api/routes/guilds/templates/index.ts index 8eff5563..32129cca 100644 --- a/src/api/routes/guilds/templates/index.ts +++ b/src/api/routes/guilds/templates/index.ts @@ -31,53 +31,72 @@ import { Request, Response, Router } from "express"; import fetch from "node-fetch"; const router: Router = Router(); -router.get("/:code", route({}), async (req: Request, res: Response) => { - const { allowDiscordTemplates, allowRaws, enabled } = - Config.get().templates; - if (!enabled) - res.json({ - code: 403, - message: "Template creation & usage is disabled on this instance.", - }).sendStatus(403); - - const { code } = req.params; - - if (code.startsWith("discord:")) { - if (!allowDiscordTemplates) - return res - .json({ - code: 403, - message: - "Discord templates cannot be used on this instance.", - }) - .sendStatus(403); - const discordTemplateID = code.split("discord:", 2)[1]; - - const discordTemplateData = await fetch( - `https://discord.com/api/v9/guilds/templates/${discordTemplateID}`, - { - method: "get", - headers: { "Content-Type": "application/json" }, +router.get( + "/:code", + route({ + responses: { + 200: { + body: "GuildTemplate", }, - ); - return res.json(await discordTemplateData.json()); - } + 403: { + body: "APIErrorResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { allowDiscordTemplates, allowRaws, enabled } = + Config.get().templates; + if (!enabled) + res.json({ + code: 403, + message: + "Template creation & usage is disabled on this instance.", + }).sendStatus(403); - if (code.startsWith("external:")) { - if (!allowRaws) - return res - .json({ - code: 403, - message: "Importing raws is disabled on this instance.", - }) - .sendStatus(403); + const { code } = req.params; - return res.json(code.split("external:", 2)[1]); - } + if (code.startsWith("discord:")) { + if (!allowDiscordTemplates) + return res + .json({ + code: 403, + message: + "Discord templates cannot be used on this instance.", + }) + .sendStatus(403); + const discordTemplateID = code.split("discord:", 2)[1]; - const template = await Template.findOneOrFail({ where: { code: code } }); - res.json(template); -}); + const discordTemplateData = await fetch( + `https://discord.com/api/v9/guilds/templates/${discordTemplateID}`, + { + method: "get", + headers: { "Content-Type": "application/json" }, + }, + ); + return res.json(await discordTemplateData.json()); + } + + if (code.startsWith("external:")) { + if (!allowRaws) + return res + .json({ + code: 403, + message: "Importing raws is disabled on this instance.", + }) + .sendStatus(403); + + return res.json(code.split("external:", 2)[1]); + } + + const template = await Template.findOneOrFail({ + where: { code: code }, + }); + res.json(template); + }, +); router.post( "/:code", diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts index e8454986..7d7ae1ea 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts @@ -24,7 +24,7 @@ import { OneToMany, RelationId, } from "typeorm"; -import { Config, handleFile, Snowflake } from ".."; +import { Config, GuildWelcomeScreen, handleFile, Snowflake } from ".."; import { Ban } from "./Ban"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; @@ -270,16 +270,7 @@ export class Guild extends BaseClass { verification_level?: number; @Column({ type: "simple-json" }) - welcome_screen: { - enabled: boolean; - description: string; - welcome_channels: { - description: string; - emoji_id?: string; - emoji_name?: string; - channel_id: string; - }[]; - }; + welcome_screen: GuildWelcomeScreen; @Column({ nullable: true }) @RelationId((guild: Guild) => guild.widget_channel) diff --git a/src/util/interfaces/GuildWelcomeScreen.ts b/src/util/interfaces/GuildWelcomeScreen.ts new file mode 100644 index 00000000..38b6061b --- /dev/null +++ b/src/util/interfaces/GuildWelcomeScreen.ts @@ -0,0 +1,10 @@ +export interface GuildWelcomeScreen { + enabled: boolean; + description: string; + welcome_channels: { + description: string; + emoji_id?: string; + emoji_name?: string; + channel_id: string; + }[]; +} diff --git a/src/util/interfaces/index.ts b/src/util/interfaces/index.ts index c6a00458..6620ba32 100644 --- a/src/util/interfaces/index.ts +++ b/src/util/interfaces/index.ts @@ -19,6 +19,7 @@ export * from "./Activity"; export * from "./ConnectedAccount"; export * from "./Event"; +export * from "./GuildWelcomeScreen"; export * from "./Interaction"; export * from "./Presence"; export * from "./Status"; diff --git a/src/util/schemas/responses/GuildBansResponse.ts b/src/util/schemas/responses/GuildBansResponse.ts new file mode 100644 index 00000000..876a4bc4 --- /dev/null +++ b/src/util/schemas/responses/GuildBansResponse.ts @@ -0,0 +1,10 @@ +export interface GuildBansResponse { + reason: string; + user: { + username: string; + discriminator: string; + id: string; + avatar: string | null; + public_flags: number; + }; +} diff --git a/src/util/schemas/responses/GuildChannelsResponse.ts b/src/util/schemas/responses/GuildChannelsResponse.ts new file mode 100644 index 00000000..3321455d --- /dev/null +++ b/src/util/schemas/responses/GuildChannelsResponse.ts @@ -0,0 +1,3 @@ +import { Channel } from "../../entities"; + +export type GuildChannelsResponse = Channel[]; diff --git a/src/util/schemas/responses/GuildCreateResponse.ts b/src/util/schemas/responses/GuildCreateResponse.ts new file mode 100644 index 00000000..8185cb86 --- /dev/null +++ b/src/util/schemas/responses/GuildCreateResponse.ts @@ -0,0 +1,3 @@ +export interface GuildCreateResponse { + id: string; +} diff --git a/src/util/schemas/responses/GuildDiscoveryRequirements.ts b/src/util/schemas/responses/GuildDiscoveryRequirements.ts new file mode 100644 index 00000000..2d303133 --- /dev/null +++ b/src/util/schemas/responses/GuildDiscoveryRequirements.ts @@ -0,0 +1,23 @@ +export interface GuildDiscoveryRequirements { + uild_id: string; + safe_environment: boolean; + healthy: boolean; + health_score_pending: boolean; + size: boolean; + nsfw_properties: unknown; + protected: boolean; + sufficient: boolean; + sufficient_without_grace_period: boolean; + valid_rules_channel: boolean; + retention_healthy: boolean; + engagement_healthy: boolean; + age: boolean; + minimum_age: number; + health_score: { + avg_nonnew_participators: number; + avg_nonnew_communicators: number; + num_intentful_joiners: number; + perc_ret_w1_intentful: number; + }; + minimum_size: number; +} diff --git a/src/util/schemas/responses/GuildEmojisResponse.ts b/src/util/schemas/responses/GuildEmojisResponse.ts new file mode 100644 index 00000000..cea6fd55 --- /dev/null +++ b/src/util/schemas/responses/GuildEmojisResponse.ts @@ -0,0 +1,3 @@ +import { Emoji } from "../../entities"; + +export type GuildEmojisResponse = Emoji[]; diff --git a/src/util/schemas/responses/GuildInvitesResponse.ts b/src/util/schemas/responses/GuildInvitesResponse.ts new file mode 100644 index 00000000..cf9ed9cc --- /dev/null +++ b/src/util/schemas/responses/GuildInvitesResponse.ts @@ -0,0 +1,3 @@ +import { Invite } from "../../entities"; + +export type GuildInvitesResponse = Invite[]; diff --git a/src/util/schemas/responses/GuildMembersResponse.ts b/src/util/schemas/responses/GuildMembersResponse.ts new file mode 100644 index 00000000..8d14fd9e --- /dev/null +++ b/src/util/schemas/responses/GuildMembersResponse.ts @@ -0,0 +1,3 @@ +import { Member } from "../../entities"; + +export type GuildMembersResponse = Member[]; diff --git a/src/util/schemas/responses/GuildMessagesSearchResponse.ts b/src/util/schemas/responses/GuildMessagesSearchResponse.ts new file mode 100644 index 00000000..0b6248b7 --- /dev/null +++ b/src/util/schemas/responses/GuildMessagesSearchResponse.ts @@ -0,0 +1,32 @@ +import { + Attachment, + Embed, + MessageType, + PublicUser, + Role, +} from "../../entities"; + +export interface GuildMessagesSearchMessage { + id: string; + type: MessageType; + content?: string; + channel_id: string; + author: PublicUser; + attachments: Attachment[]; + embeds: Embed[]; + mentions: PublicUser[]; + mention_roles: Role[]; + pinned: boolean; + mention_everyone?: boolean; + tts: boolean; + timestamp: string; + edited_timestamp: string | null; + flags: number; + components: unknown[]; + hit: true; +} + +export interface GuildMessagesSearchResponse { + messages: GuildMessagesSearchMessage[]; + total_results: number; +} diff --git a/src/util/schemas/responses/GuildPruneResponse.ts b/src/util/schemas/responses/GuildPruneResponse.ts new file mode 100644 index 00000000..fb1abb89 --- /dev/null +++ b/src/util/schemas/responses/GuildPruneResponse.ts @@ -0,0 +1,7 @@ +export interface GuildPruneResponse { + pruned: number; +} + +export interface GuildPurgeResponse { + purged: number; +} diff --git a/src/util/schemas/responses/GuildResponse.ts b/src/util/schemas/responses/GuildResponse.ts new file mode 100644 index 00000000..00035243 --- /dev/null +++ b/src/util/schemas/responses/GuildResponse.ts @@ -0,0 +1,3 @@ +import { Guild } from "../../entities"; + +export type GuildResponse = Guild & { joined_at: string }; diff --git a/src/util/schemas/responses/GuildRolesResponse.ts b/src/util/schemas/responses/GuildRolesResponse.ts new file mode 100644 index 00000000..a064cddb --- /dev/null +++ b/src/util/schemas/responses/GuildRolesResponse.ts @@ -0,0 +1,3 @@ +import { Role } from "../../entities"; + +export type GuildRolesResponse = Role[]; diff --git a/src/util/schemas/responses/GuildStickersResponse.ts b/src/util/schemas/responses/GuildStickersResponse.ts new file mode 100644 index 00000000..a02f3e55 --- /dev/null +++ b/src/util/schemas/responses/GuildStickersResponse.ts @@ -0,0 +1,3 @@ +import { Sticker } from "../../entities"; + +export type GuildStickersResponse = Sticker[]; diff --git a/src/util/schemas/responses/GuildTemplatesResponse.ts b/src/util/schemas/responses/GuildTemplatesResponse.ts new file mode 100644 index 00000000..e975fe43 --- /dev/null +++ b/src/util/schemas/responses/GuildTemplatesResponse.ts @@ -0,0 +1,3 @@ +import { Template } from "../../entities"; + +export type GuildTemplatesResponse = Template[]; diff --git a/src/util/schemas/responses/GuildVanityUrl.ts b/src/util/schemas/responses/GuildVanityUrl.ts new file mode 100644 index 00000000..ff37bf4e --- /dev/null +++ b/src/util/schemas/responses/GuildVanityUrl.ts @@ -0,0 +1,17 @@ +export interface GuildVanityUrl { + code: string; + uses: number; +} + +export interface GuildVanityUrlNoInvite { + code: null; +} + +export type GuildVanityUrlResponse = + | GuildVanityUrl + | GuildVanityUrl[] + | GuildVanityUrlNoInvite; + +export interface GuildVanityUrlCreateResponse { + code: string; +} diff --git a/src/util/schemas/responses/GuildVoiceRegionsResponse.ts b/src/util/schemas/responses/GuildVoiceRegionsResponse.ts new file mode 100644 index 00000000..c17e2f5d --- /dev/null +++ b/src/util/schemas/responses/GuildVoiceRegionsResponse.ts @@ -0,0 +1,9 @@ +export interface GuildVoiceRegion { + id: string; + name: string; + custom: boolean; + deprecated: boolean; + optimal: boolean; +} + +export type GuildVoiceRegionsResponse = GuildVoiceRegion[]; diff --git a/src/util/schemas/responses/GuildWidgetJsonResponse.ts b/src/util/schemas/responses/GuildWidgetJsonResponse.ts new file mode 100644 index 00000000..ef85dd08 --- /dev/null +++ b/src/util/schemas/responses/GuildWidgetJsonResponse.ts @@ -0,0 +1,21 @@ +import { ClientStatus } from "../../interfaces"; + +export interface GuildWidgetJsonResponse { + id: string; + name: string; + instant_invite: string; + channels: { + id: string; + name: string; + position: number; + }[]; + members: { + id: string; + username: string; + discriminator: string; + avatar: string | null; + status: ClientStatus; + avatar_url: string; + }[]; + presence_count: number; +} diff --git a/src/util/schemas/responses/GuildWidgetSettingsResponse.ts b/src/util/schemas/responses/GuildWidgetSettingsResponse.ts new file mode 100644 index 00000000..3c6b45ce --- /dev/null +++ b/src/util/schemas/responses/GuildWidgetSettingsResponse.ts @@ -0,0 +1,6 @@ +import { Snowflake } from "../../util"; + +export interface GuildWidgetSettingsResponse { + enabled: boolean; + channel_id: Snowflake | null; +} diff --git a/src/util/schemas/responses/MemberJoinGuildResponse.ts b/src/util/schemas/responses/MemberJoinGuildResponse.ts new file mode 100644 index 00000000..d7b39d10 --- /dev/null +++ b/src/util/schemas/responses/MemberJoinGuildResponse.ts @@ -0,0 +1,8 @@ +import { Emoji, Guild, Role, Sticker } from "../../entities"; + +export interface MemberJoinGuildResponse { + guild: Guild; + emojis: Emoji[]; + roles: Role[]; + stickers: Sticker[]; +} diff --git a/src/util/schemas/responses/index.ts b/src/util/schemas/responses/index.ts index 30949f7f..91c889db 100644 --- a/src/util/schemas/responses/index.ts +++ b/src/util/schemas/responses/index.ts @@ -12,7 +12,25 @@ export * from "./ChannelWebhooksResponse"; export * from "./GatewayBotResponse"; export * from "./GatewayResponse"; export * from "./GenerateRegistrationTokensResponse"; +export * from "./GuildBansResponse"; +export * from "./GuildChannelsResponse"; +export * from "./GuildCreateResponse"; +export * from "./GuildDiscoveryRequirements"; +export * from "./GuildEmojisResponse"; +export * from "./GuildInvitesResponse"; +export * from "./GuildMembersResponse"; +export * from "./GuildMessagesSearchResponse"; +export * from "./GuildPruneResponse"; +export * from "./GuildResponse"; +export * from "./GuildRolesResponse"; +export * from "./GuildStickersResponse"; +export * from "./GuildTemplatesResponse"; +export * from "./GuildVanityUrl"; +export * from "./GuildVoiceRegionsResponse"; +export * from "./GuildWidgetJsonResponse"; +export * from "./GuildWidgetSettingsResponse"; export * from "./LocationMetadataResponse"; +export * from "./MemberJoinGuildResponse"; export * from "./Tenor"; export * from "./TokenResponse"; export * from "./UserProfileResponse";