This repository has no description
Something went wrong. Try again.
5.2 kB · 150 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151// SPDX-License-Identifier: AGPL-3.0-or-later
import {Permissions} from '@fluxer/constants/src/ChannelConstants';import {MissingPermissionsError} from '@fluxer/errors/src/domains/core/MissingPermissionsError';import type {SearchableGuildMember} from '@fluxer/schema/src/contracts/search/SearchDocumentTypes';import {GuildIdParam} from '@fluxer/schema/src/domains/common/CommonParamSchemas';import { GuildMemberSearchRequest, GuildMemberSearchResponse, type GuildMemberSearchResponse as GuildMemberSearchResponseBody, type GuildMemberSearchResult,} from '@fluxer/schema/src/domains/guild/GuildMemberSearchSchemas';import {createGuildID} from '../../BrandedTypes';import {LoginRequired} from '../../middleware/AuthMiddleware';import {RateLimitMiddleware} from '../../middleware/RateLimitMiddleware';import {OpenAPI} from '../../middleware/ResponseTypeMiddleware';import {RateLimitConfigs} from '../../RateLimitConfig';import {getGuildMemberSearchService} from '../../SearchFactory';import {guildMembersNeedReindexing} from '../../search/GuildMemberIndexingUtils';import type {HonoApp} from '../../types/HonoEnv';import {Validator} from '../../Validator';import {GuildRepository} from '../repositories/GuildRepository';
const MEMBERS_PAGE_PERMISSIONS = Permissions.MANAGE_GUILD | Permissions.MANAGE_ROLES | Permissions.MANAGE_NICKNAMES | Permissions.BAN_MEMBERS | Permissions.MODERATE_MEMBERS | Permissions.KICK_MEMBERS;const guildRepository = new GuildRepository();
function createEmptySearchResponse(guildId: string, indexing: boolean): GuildMemberSearchResponseBody { return { guild_id: guildId, members: [], page_result_count: 0, total_result_count: 0, indexing, };}
function mapSearchableMember(hit: SearchableGuildMember): GuildMemberSearchResult { return { id: hit.id, guild_id: hit.guildId, user_id: hit.userId, username: hit.username, discriminator: hit.discriminator, global_name: hit.globalName, nickname: hit.nickname, role_ids: hit.roleIds, joined_at: hit.joinedAt, supplemental: { join_source_type: hit.joinSourceType, source_invite_code: hit.sourceInviteCode, inviter_id: hit.inviterId, }, is_bot: hit.isBot, };}
export function GuildMemberSearchController(app: HonoApp) { app.post( '/guilds/:guild_id/members-search', RateLimitMiddleware(RateLimitConfigs.GUILD_MEMBERS), LoginRequired, Validator('param', GuildIdParam), Validator('json', GuildMemberSearchRequest), OpenAPI({ operationId: 'search_guild_members', summary: 'Search guild members', responseSchema: GuildMemberSearchResponse, statusCode: 200, security: ['botToken', 'bearerToken', 'sessionToken'], tags: ['Guilds'], description: 'Search and filter guild members with pagination support.', }), async (ctx) => { const userId = ctx.get('user').id; const guildId = createGuildID(ctx.req.valid('param').guild_id); const guildIdString = guildId.toString(); const body = ctx.req.valid('json'); const {getMyPermissions} = await ctx.get('guildService').getGuildAuthenticated({userId, guildId}); const permissions = await getMyPermissions(); if ((permissions & MEMBERS_PAGE_PERMISSIONS) === 0n) { throw new MissingPermissionsError(); } const canViewInvites = (permissions & Permissions.MANAGE_GUILD) !== 0n; const guild = await guildRepository.findUnique(guildId); if (!guild) { return ctx.json(createEmptySearchResponse(guildIdString, false)); } const searchService = getGuildMemberSearchService(); if (!searchService || !searchService.isAvailable()) { return ctx.json(createEmptySearchResponse(guildIdString, false)); } const needsIndexing = guildMembersNeedReindexing(guild.membersIndexedAt); if (needsIndexing) { const workerService = ctx.get('workerService'); await workerService.addJob( 'indexGuildMembers', { guildId: guildIdString, }, {jobKey: `index-guild-members-${guildId}-lazy`, maxAttempts: 3}, ); return ctx.json(createEmptySearchResponse(guildIdString, true)); } const query = body.query?.trim() ?? ''; const limit = body.limit ?? 25; const offset = body.offset ?? 0; const results = await searchService.searchMembers( query, { guildId: guildIdString, roleIds: body.role_ids, joinedAtGte: body.joined_at_gte, joinedAtLte: body.joined_at_lte, joinSourceType: canViewInvites ? body.join_source_type : undefined, sourceInviteCode: canViewInvites ? body.source_invite_code : undefined, userCreatedAtGte: body.user_created_at_gte, userCreatedAtLte: body.user_created_at_lte, isBot: body.is_bot, sortBy: body.sort_by, sortOrder: body.sort_order, }, {limit, offset}, ); const members = results.hits.map((hit) => { const member = mapSearchableMember(hit); if (!canViewInvites) { member.supplemental = { join_source_type: null, source_invite_code: null, inviter_id: null, }; } return member; }); return ctx.json({ guild_id: guildIdString, members, page_result_count: members.length, total_result_count: results.total, indexing: false, }); }, );}