This repository has no description
Something went wrong. Try again.
25 kB · 681 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682// SPDX-License-Identifier: AGPL-3.0-or-later
import {AdminACLs} from '@fluxer/constants/src/AdminACLs';import { BanAvatarHashRequest, BanCheckResponseSchema, BanEmailRequest, BanFileShaRequest, BanIpRequest, BanPhraseRequest, BanProfileSubstringRequest, BanUrlDomainRequest, BanUrlRequest, BanUserAvatarRequest, BanUserAvatarResponseSchema, BulkBanFileShasRequest, BulkJobResponse, CheckAvatarHashRequest, CheckFileShaRequest, CheckUrlBlocklistRequest, SuspiciousEmailDomainRequest, UnbanFileShaRequest, UnbanUrlDomainRequest, UnbanUrlRequest,} from '@fluxer/schema/src/domains/admin/AdminSchemas';import {requireAdminACL} from '../../middleware/AdminMiddleware';import {RateLimitMiddleware} from '../../middleware/RateLimitMiddleware';import {OpenAPI} from '../../middleware/ResponseTypeMiddleware';import {getWorkerService} from '../../middleware/ServiceRegistry';import {RateLimitConfigs} from '../../RateLimitConfig';import type {HonoApp} from '../../types/HonoEnv';import {Validator} from '../../Validator';
export function BanAdminController(app: HonoApp) { app.post( '/admin/bans/ip/add', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_IP_ADD), Validator('json', BanIpRequest), OpenAPI({ operationId: 'add_ip_ban', summary: 'Add IP ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Ban one or more IP addresses from accessing the platform. Users connecting from banned IPs will be denied service. Can be applied retroactively.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.banIp(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/ip/remove', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_IP_REMOVE), Validator('json', BanIpRequest), OpenAPI({ operationId: 'remove_ip_ban', summary: 'Remove IP ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Lift a previously applied IP ban, allowing traffic from those addresses again. Used for appeals or when bans were applied in error.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.unbanIp(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/ip/check', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_IP_CHECK), Validator('json', BanIpRequest), OpenAPI({ operationId: 'check_ip_ban_status', summary: 'Check IP ban status', responseSchema: BanCheckResponseSchema, statusCode: 200, security: ['adminApiKey'], tags: ['Admin'], description: 'Query whether one or more IP addresses are currently banned. Returns the ban status and any associated metadata like reason or expiration.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.banManagementService.checkIpBan(ctx.req.valid('json'))); }, ); app.post( '/admin/bans/email/add', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_EMAIL_ADD), Validator('json', BanEmailRequest), OpenAPI({ operationId: 'add_email_ban', summary: 'Add email ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Ban one or more email addresses from registering or creating accounts. Users attempting to use banned emails will be blocked.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.banEmail(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/email/remove', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_EMAIL_REMOVE), Validator('json', BanEmailRequest), OpenAPI({ operationId: 'remove_email_ban', summary: 'Remove email ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Lift a previously applied email ban, allowing the address to be used for new registrations. Used for appeals or error correction.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.unbanEmail(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/email/check', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_EMAIL_CHECK), Validator('json', BanEmailRequest), OpenAPI({ operationId: 'check_email_ban_status', summary: 'Check email ban status', responseSchema: BanCheckResponseSchema, statusCode: 200, security: ['adminApiKey'], tags: ['Admin'], description: 'Query whether one or more email addresses are currently banned from registration. Returns the ban status and metadata.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.banManagementService.checkEmailBan(ctx.req.valid('json'))); }, ); app.post( '/admin/suspicious-email-domains/add', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.SUSPICIOUS_EMAIL_DOMAIN_ADD), Validator('json', SuspiciousEmailDomainRequest), OpenAPI({ operationId: 'add_suspicious_email_domain', summary: 'Add suspicious email domain', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Flag an email domain as suspicious. Registration is not blocked, but new accounts using this domain are required to verify a phone number before they can act on the platform. The list itself is not exposed to users — they only see the verified-phone gate.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.addSuspiciousEmailDomain( ctx.req.valid('json'), adminUserId, auditLogReason, ); return ctx.body(null, 204); }, ); app.post( '/admin/suspicious-email-domains/remove', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.SUSPICIOUS_EMAIL_DOMAIN_REMOVE), Validator('json', SuspiciousEmailDomainRequest), OpenAPI({ operationId: 'remove_suspicious_email_domain', summary: 'Remove suspicious email domain flag', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Remove a domain from the suspicious list. New registrations from this domain will no longer be auto-required to verify a phone number on signup.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.removeSuspiciousEmailDomain( ctx.req.valid('json'), adminUserId, auditLogReason, ); return ctx.body(null, 204); }, ); app.post( '/admin/suspicious-email-domains/check', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.SUSPICIOUS_EMAIL_DOMAIN_CHECK), Validator('json', SuspiciousEmailDomainRequest), OpenAPI({ operationId: 'check_suspicious_email_domain', summary: 'Check suspicious email domain status', responseSchema: BanCheckResponseSchema, statusCode: 200, security: ['adminApiKey'], tags: ['Admin'], description: 'Query whether an email domain is currently flagged as suspicious.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.banManagementService.checkSuspiciousEmailDomain(ctx.req.valid('json'))); }, ); app.post( '/admin/bans/phrase/add', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_PHRASE_ADD), Validator('json', BanPhraseRequest), OpenAPI({ operationId: 'add_phrase_ban', summary: 'Add phrase ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Ban a phrase. Matching is case-insensitive and also normalizes common bypass tricks such as inserted whitespace, punctuation, invisible characters, and compatibility glyphs.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.banPhrase(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/phrase/remove', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_PHRASE_REMOVE), Validator('json', BanPhraseRequest), OpenAPI({ operationId: 'remove_phrase_ban', summary: 'Remove phrase ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Lift a previously applied phrase ban, allowing messages containing that phrase again.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.unbanPhrase(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/phrase/check', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_PHRASE_CHECK), Validator('json', BanPhraseRequest), OpenAPI({ operationId: 'check_phrase_ban_status', summary: 'Check phrase ban status', responseSchema: BanCheckResponseSchema, statusCode: 200, security: ['adminApiKey'], tags: ['Admin'], description: 'Query whether a phrase is currently banned.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.banManagementService.checkPhraseBan(ctx.req.valid('json'))); }, ); app.post( '/admin/bans/url/add', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_URL_ADD), Validator('json', BanUrlRequest), OpenAPI({ operationId: 'add_url_ban', summary: 'Add URL ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Ban one or more URLs from being posted on the platform. Messages containing banned URLs will be blocked.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.banUrl(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/url/remove', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_URL_REMOVE), Validator('json', UnbanUrlRequest), OpenAPI({ operationId: 'remove_url_ban', summary: 'Remove URL ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Lift a previously applied URL ban, allowing the URL to be posted again.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.unbanUrl(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/url/check', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_URL_CHECK), Validator('json', CheckUrlBlocklistRequest), OpenAPI({ operationId: 'check_url_ban_status', summary: 'Check URL ban status', responseSchema: BanCheckResponseSchema, statusCode: 200, security: ['adminApiKey'], tags: ['Admin'], description: 'Query whether one or more URLs are currently banned from being posted.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.banManagementService.checkUrlBan(ctx.req.valid('json'))); }, ); app.post( '/admin/bans/url-domain/add', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_URL_DOMAIN_ADD), Validator('json', BanUrlDomainRequest), OpenAPI({ operationId: 'add_url_domain_ban', summary: 'Add URL domain ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Ban an entire URL domain from being linked on the platform. All URLs under the banned domain will be blocked.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.banUrlDomain(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/url-domain/remove', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_URL_DOMAIN_REMOVE), Validator('json', UnbanUrlDomainRequest), OpenAPI({ operationId: 'remove_url_domain_ban', summary: 'Remove URL domain ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Lift a previously applied URL domain ban, allowing links to that domain again.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.unbanUrlDomain(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/url-domain/check', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_URL_DOMAIN_CHECK), Validator('json', BanUrlDomainRequest), OpenAPI({ operationId: 'check_url_domain_ban_status', summary: 'Check URL domain ban status', responseSchema: BanCheckResponseSchema, statusCode: 200, security: ['adminApiKey'], tags: ['Admin'], description: 'Query whether a URL domain is currently banned from being linked.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.banManagementService.checkUrlDomainBan(ctx.req.valid('json'))); }, ); app.post( '/admin/bans/file-sha/add', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_FILE_SHA_ADD), Validator('json', BanFileShaRequest), OpenAPI({ operationId: 'add_file_sha_ban', summary: 'Add file SHA ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Ban one or more files by SHA hash. Uploads matching the banned hash will be rejected.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.banFileSha(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/file-sha/remove', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_FILE_SHA_REMOVE), Validator('json', UnbanFileShaRequest), OpenAPI({ operationId: 'remove_file_sha_ban', summary: 'Remove file SHA ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Lift a previously applied file SHA ban, allowing uploads of that file again.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.unbanFileSha(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/file-sha/check', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_FILE_SHA_CHECK), Validator('json', CheckFileShaRequest), OpenAPI({ operationId: 'check_file_sha_ban_status', summary: 'Check file SHA ban status', responseSchema: BanCheckResponseSchema, statusCode: 200, security: ['adminApiKey'], tags: ['Admin'], description: 'Query whether one or more file SHA hashes are currently banned.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.banManagementService.checkFileShaBan(ctx.req.valid('json'))); }, ); app.post( '/admin/bans/avatar-hash/add', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_AVATAR_HASH_ADD), Validator('json', BanAvatarHashRequest), OpenAPI({ operationId: 'add_avatar_hash_ban', summary: 'Add avatar hash ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Ban one or more 8-char MD5-prefix avatar hashes. Avatars matching the banned hash will be rejected on upload.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.banAvatarHash(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/avatar-hash/remove', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_AVATAR_HASH_REMOVE), Validator('json', CheckAvatarHashRequest), OpenAPI({ operationId: 'remove_avatar_hash_ban', summary: 'Remove avatar hash ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Lift a previously applied avatar-hash ban.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.unbanAvatarHash(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/avatar-hash/check', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_AVATAR_HASH_CHECK), Validator('json', CheckAvatarHashRequest), OpenAPI({ operationId: 'check_avatar_hash_ban_status', summary: 'Check avatar hash ban status', responseSchema: BanCheckResponseSchema, statusCode: 200, security: ['adminApiKey'], tags: ['Admin'], description: 'Query whether any of the provided avatar hashes are banned.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.banManagementService.checkAvatarHashBan(ctx.req.valid('json'))); }, ); app.post( '/admin/users/:user_id/ban-avatar', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_AVATAR_HASH_ADD), Validator('json', BanUserAvatarRequest), OpenAPI({ operationId: 'ban_user_avatar', summary: "Ban this user's current avatar", responseSchema: BanUserAvatarResponseSchema, statusCode: 200, security: ['adminApiKey'], tags: ['Admin'], description: "Reads the user's current avatar_hash, strips any animation prefix, and adds the 8-char hash to the avatar blocklist. Returns the banned hash.", }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const userId = ctx.req.param('user_id'); const body = ctx.req.valid('json'); return ctx.json( await adminService.banManagementService.banUserAvatar({user_id: userId, ...body}, adminUserId, auditLogReason), ); }, ); app.post( '/admin/bans/profile-substring/add', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_PROFILE_SUBSTRING_ADD), Validator('json', BanProfileSubstringRequest), OpenAPI({ operationId: 'add_profile_substring_ban', summary: 'Add profile-substring ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Ban a substring within a specific profile field (username, global_name, nickname, bio, or pronouns). Matching reuses the phrase blocklist normalization (whitespace, punctuation, zero-width, lookalikes).', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.banProfileSubstring(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/profile-substring/remove', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_PROFILE_SUBSTRING_REMOVE), Validator('json', BanProfileSubstringRequest), OpenAPI({ operationId: 'remove_profile_substring_ban', summary: 'Remove profile-substring ban', responseSchema: null, statusCode: 204, security: ['adminApiKey'], tags: ['Admin'], description: 'Lift a previously applied profile-substring ban.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.banManagementService.unbanProfileSubstring(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/bans/profile-substring/check', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_PROFILE_SUBSTRING_CHECK), Validator('json', BanProfileSubstringRequest), OpenAPI({ operationId: 'check_profile_substring_ban_status', summary: 'Check profile-substring ban status', responseSchema: BanCheckResponseSchema, statusCode: 200, security: ['adminApiKey'], tags: ['Admin'], description: 'Query whether any of the provided substrings are banned for the given scope.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.banManagementService.checkProfileSubstringBan(ctx.req.valid('json'))); }, ); app.post( '/admin/bans/file-sha/bulk-add', RateLimitMiddleware(RateLimitConfigs.ADMIN_BAN_OPERATION), requireAdminACL(AdminACLs.BAN_FILE_SHA_ADD), Validator('json', BulkBanFileShasRequest), OpenAPI({ operationId: 'bulk_ban_file_shas', summary: 'Bulk-ban file SHAs as a background job', responseSchema: BulkJobResponse, statusCode: 200, security: ['adminApiKey'], tags: ['Admin'], description: 'Enqueue a background job that bans many file SHAs at once. Returns a job_id immediately; observe progress at /admin/jobs/:job_id.', }), async (ctx) => { const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const body = ctx.req.valid('json'); const workerService = getWorkerService(); const jobId = await workerService.addJob( 'bulkBanFileShas', { sha256_list: body.sha256_list, admin_user_id: adminUserId.toString(), audit_log_reason: auditLogReason, }, {requestedByUserId: adminUserId, ...(auditLogReason && {auditLogReason})}, ); return ctx.json({job_id: jobId.toString()}); }, );}