diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts index 5ae9bdb9..78951954 100644 --- a/src/api/routes/guilds/#guild_id/bans.ts +++ b/src/api/routes/guilds/#guild_id/bans.ts @@ -18,11 +18,13 @@ import { getIpAdress, route } from "@spacebar/api"; import { + APIBansArray, Ban, BanRegistrySchema, DiscordApiErrors, GuildBanAddEvent, GuildBanRemoveEvent, + GuildBansResponse, Member, User, emitEvent, @@ -40,7 +42,7 @@ router.get( permission: "BAN_MEMBERS", responses: { 200: { - body: "GuildBansResponse", + body: "APIBansArray", }, 403: { body: "APIErrorResponse", @@ -50,27 +52,27 @@ router.get( async (req: Request, res: Response) => { const { guild_id } = req.params; - const bans = await Ban.find({ where: { guild_id: guild_id } }); - const promisesToAwait: object[] = []; - const bansObj: object[] = []; + let bans = await Ban.find({ where: { guild_id: guild_id } }); + const promisesToAwait: Promise[] = []; + const bansObj: APIBansArray = []; - bans.filter((ban) => ban.user_id !== ban.executor_id); // pretend self-bans don't exist to prevent victim chasing + bans = bans.filter((ban) => ban.user_id !== ban.executor_id); // pretend self-bans don't exist to prevent victim chasing bans.forEach((ban) => { promisesToAwait.push(User.getPublicUser(ban.user_id)); }); - const bannedUsers: object[] = await Promise.all(promisesToAwait); + const bannedUsers = await Promise.all(promisesToAwait); bans.forEach((ban, index) => { - const user = bannedUsers[index] as User; + const user = bannedUsers[index]; bansObj.push({ - reason: ban.reason, + reason: ban.reason ?? null, user: { username: user.username, discriminator: user.discriminator, id: user.id, - avatar: user.avatar, + avatar: user.avatar ?? null, public_flags: user.public_flags, }, }); @@ -80,13 +82,83 @@ router.get( }, ); +router.get( + "/search", + route({ + permission: "BAN_MEMBERS", + query: { + query: { + type: "string", + description: + "Query to match username(s) and display name(s) against (1-32 characters)", + required: true, + }, + limit: { + type: "number", + description: + "Max number of members to return (1-10, default 10)", + required: false, + }, + }, + responses: { + 200: { + body: "APIBansArray", + }, + 403: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; + + const limit = Number(req.query.limit) || 10; + if (limit > 10 || limit < 1) + throw new HTTPError("Limit must be between 1 and 10"); + + const query = String(req.query.query); + if (!query || query.trim().length === 0 || query.length > 32) { + throw new HTTPError( + "The query must be between 1 and 32 characters in length", + ); + } + + let bans = await Ban.createQueryBuilder("ban") + .leftJoinAndSelect("ban.user", "user") + .where("ban.guild_id = :guildId", { guildId: guild_id }) + .andWhere("user.username LIKE :userName", { + userName: `%${query}%`, + }) + .limit(limit) + .getMany(); + + bans = bans.filter((ban) => ban.user_id !== ban.executor_id); // pretend self-bans don't exist to prevent victim chasing + + const bansObj: APIBansArray = bans.map((ban) => { + const user = ban.user; + return { + reason: ban.reason ?? null, + user: { + username: user.username, + discriminator: user.discriminator, + id: user.id, + avatar: user.avatar ?? null, + public_flags: user.public_flags, + }, + }; + }); + + return res.json(bansObj); + }, +); + router.get( "/:user_id", route({ permission: "BAN_MEMBERS", responses: { 200: { - body: "BanModeratorSchema", + body: "GuildBansResponse", }, 403: { body: "APIErrorResponse", @@ -106,9 +178,17 @@ router.get( if (ban.user_id === ban.executor_id) throw DiscordApiErrors.UNKNOWN_BAN; // pretend self-bans don't exist to prevent victim chasing - const banInfo = { - user: await User.getPublicUser(ban.user_id), - reason: ban.reason, + const user = await User.getPublicUser(ban.user_id); + + const banInfo: GuildBansResponse = { + user: { + username: user.username, + discriminator: user.discriminator, + id: user.id, + avatar: user.avatar ?? null, + public_flags: user.public_flags, + }, + reason: ban.reason ?? null, }; return res.json(banInfo); @@ -121,9 +201,7 @@ router.put( requestBody: "BanCreateSchema", permission: "BAN_MEMBERS", responses: { - 200: { - body: "Ban", - }, + 204: {}, 400: { body: "APIErrorResponse", }, diff --git a/src/util/schemas/responses/TypedResponses.ts b/src/util/schemas/responses/TypedResponses.ts index 3b864781..bc204502 100644 --- a/src/util/schemas/responses/TypedResponses.ts +++ b/src/util/schemas/responses/TypedResponses.ts @@ -16,7 +16,7 @@ along with this program. If not, see . */ -import { GuildCreateResponse } from "@spacebar/util"; +import { GuildBansResponse, GuildCreateResponse } from "@spacebar/util"; import { GeneralConfiguration, LimitsConfiguration } from "../../config"; import { DmChannelDTO } from "../../dtos"; import { @@ -73,6 +73,8 @@ export type ApplicationSkusResponse = unknown[]; export type APIApplicationArray = Application[]; +export type APIBansArray = GuildBansResponse[]; + export type APIInviteArray = Invite[]; export type APIMessageArray = Message[];