// SPDX-License-Identifier: AGPL-3.0-or-later import {AdminACLs} from '@fluxer/constants/src/AdminACLs'; import {ListUserGuildsRequest, ListUserGuildsResponse} from '@fluxer/schema/src/domains/admin/AdminGuildSchemas'; import { AdminUsersMeResponse, CancelBulkMessageDeletionRequest, ChangeDobRequest, ChangeEmailRequest, ChangeUsernameRequest, ClearUserFieldsRequest, DeleteWebAuthnCredentialRequest, DisableForSuspiciousActivityRequest, DisableMfaRequest, ListUserChangeLogRequest, ListUserChangeLogResponseSchema, ListUserDmChannelsRequest, ListUserDmChannelsResponse, ListUserGroupDmChannelsRequest, ListUserGroupDmChannelsResponse, ListUserRelationshipsRequest, ListUserRelationshipsResponse, ListUserSessionsRequest, ListUserSessionsResponse, ListWebAuthnCredentialsRequest, LookupUserRequest, LookupUserResponse, RemoveUserRelationshipRequest, RemoveUserRelationshipsByCategoryRequest, RemoveUserRelationshipsResponse, ResendVerificationEmailRequest, ScheduleAccountDeletionRequest, SendPasswordResetRequest, SetUserAclsRequest, SetUserBotStatusRequest, SetUserSystemStatusRequest, SetUserTraitsRequest, TempBanUserRequest, TerminateSessionsRequest, TerminateSessionsResponse, UpdateHasVerifiedPhoneRequest, UpdatePremiumFlagsRequest, UpdateSuspiciousActivityFlagsRequest, UpdateUserFlagsRequest, UserMutationResponse, VerifyUserEmailRequest, } from '@fluxer/schema/src/domains/admin/AdminUserSchemas'; import {WebAuthnCredentialListResponse} from '@fluxer/schema/src/domains/auth/AuthSchemas'; import {createUserID} from '../../BrandedTypes'; import {requireAdminACL} from '../../middleware/AdminMiddleware'; import {RateLimitMiddleware} from '../../middleware/RateLimitMiddleware'; import {OpenAPI} from '../../middleware/ResponseTypeMiddleware'; import {RateLimitConfigs} from '../../RateLimitConfig'; import type {HonoApp} from '../../types/HonoEnv'; import {Validator} from '../../Validator'; import {mapUserToAdminResponse} from '../models/UserTypes'; export function UserAdminController(app: HonoApp) { app.get( '/admin/users/me', requireAdminACL(AdminACLs.AUTHENTICATE), OpenAPI({ operationId: 'get_authenticated_admin_user', summary: 'Get authenticated admin user', responseSchema: AdminUsersMeResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Get profile of currently authenticated admin user. Returns admin permissions, roles, and metadata. Requires AUTHENTICATE permission.', }), async (ctx) => { const adminUser = ctx.get('user'); const cacheService = ctx.get('cacheService'); return ctx.json({ user: await mapUserToAdminResponse(adminUser, cacheService), }); }, ); app.post( '/admin/users/lookup', RateLimitMiddleware(RateLimitConfigs.ADMIN_LOOKUP), requireAdminACL(AdminACLs.USER_LOOKUP), Validator('json', LookupUserRequest), OpenAPI({ operationId: 'lookup_user', summary: 'Lookup user', responseSchema: LookupUserResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Look up detailed user profile by ID, username, email, or phone. Returns account status, permissions, and metadata. Requires USER_LOOKUP permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json(await adminService.userService.lookupService.lookupUser(ctx.req.valid('json'), adminUserAcls)); }, ); app.post( '/admin/users/list-guilds', RateLimitMiddleware(RateLimitConfigs.ADMIN_LOOKUP), requireAdminACL(AdminACLs.USER_LIST_GUILDS), Validator('json', ListUserGuildsRequest), OpenAPI({ operationId: 'list_user_guilds', summary: 'List user guilds', responseSchema: ListUserGuildsResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'List all guilds a user is a member of. Shows roles and join dates. Requires USER_LIST_GUILDS permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.guildServiceAggregate.lookupService.listUserGuilds(ctx.req.valid('json'))); }, ); app.post( '/admin/users/list-dm-channels', RateLimitMiddleware(RateLimitConfigs.ADMIN_LOOKUP), requireAdminACL(AdminACLs.USER_LIST_DM_CHANNELS), Validator('json', ListUserDmChannelsRequest), OpenAPI({ operationId: 'list_user_dm_channels', summary: 'List user DM channels', responseSchema: ListUserDmChannelsResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'List historical one-to-one DM channels for a user with cursor pagination. Requires USER_LIST_DM_CHANNELS permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.userService.listUserDmChannels(ctx.req.valid('json'))); }, ); app.post( '/admin/users/list-group-dm-channels', RateLimitMiddleware(RateLimitConfigs.ADMIN_LOOKUP), requireAdminACL(AdminACLs.USER_LIST_DM_CHANNELS), Validator('json', ListUserGroupDmChannelsRequest), OpenAPI({ operationId: 'list_user_group_dm_channels', summary: 'List user group DM channels', responseSchema: ListUserGroupDmChannelsResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'List group DM channels for a user. Requires USER_LIST_DM_CHANNELS permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.userService.listUserGroupDmChannels(ctx.req.valid('json'))); }, ); app.post( '/admin/users/disable-mfa', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_MFA), Validator('json', DisableMfaRequest), OpenAPI({ operationId: 'disable_user_mfa', summary: 'Disable user MFA', responseSchema: null, statusCode: 204, security: 'adminApiKey', tags: 'Admin', description: 'Disable two-factor authentication for user account. Removes all authenticators. Creates audit log entry. Requires USER_UPDATE_MFA permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.userService.securityService.disableMfa(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/users/list-webauthn-credentials', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_MFA), Validator('json', ListWebAuthnCredentialsRequest), OpenAPI({ operationId: 'list_user_webauthn_credentials', summary: 'List user WebAuthn credentials', responseSchema: WebAuthnCredentialListResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'List all WebAuthn credentials (passkeys/security keys) registered for a user. Returns credential names, creation dates, and last usage. Creates audit log entry. Requires USER_UPDATE_MFA permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); return ctx.json( await adminService.userService.securityService.listWebAuthnCredentials( ctx.req.valid('json'), adminUserId, auditLogReason, ), ); }, ); app.post( '/admin/users/delete-webauthn-credential', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_MFA), Validator('json', DeleteWebAuthnCredentialRequest), OpenAPI({ operationId: 'delete_user_webauthn_credential', summary: 'Delete user WebAuthn credential', responseSchema: null, statusCode: 204, security: 'adminApiKey', tags: 'Admin', description: 'Delete a specific WebAuthn credential (passkey/security key) from a user account. Creates audit log entry. Requires USER_UPDATE_MFA permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.userService.securityService.deleteWebAuthnCredential( ctx.req.valid('json'), adminUserId, auditLogReason, ); return ctx.body(null, 204); }, ); app.post( '/admin/users/clear-fields', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_PROFILE), Validator('json', ClearUserFieldsRequest), OpenAPI({ operationId: 'clear_user_fields', summary: 'Clear user fields', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Clear or reset user profile fields such as bio, avatar, or status. Creates audit log entry. Requires USER_UPDATE_PROFILE permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.profileService.clearUserFields( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/set-bot-status', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_BOT_STATUS), Validator('json', SetUserBotStatusRequest), OpenAPI({ operationId: 'set_user_bot_status', summary: 'Set user bot status', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Mark or unmark a user account as a bot. Controls bot badge visibility and API permissions. Creates audit log entry. Requires USER_UPDATE_BOT_STATUS permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.profileService.setUserBotStatus( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/set-system-status', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_BOT_STATUS), Validator('json', SetUserSystemStatusRequest), OpenAPI({ operationId: 'set_user_system_status', summary: 'Set user system status', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Mark or unmark a user as a system account. System accounts have special permissions for automated operations. Creates audit log entry. Requires USER_UPDATE_BOT_STATUS permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.profileService.setUserSystemStatus( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/verify-email', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_EMAIL), Validator('json', VerifyUserEmailRequest), OpenAPI({ operationId: 'verify_user_email', summary: 'Verify user email', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Manually verify user email address without requiring confirmation link. Bypasses email verification requirement. Creates audit log entry. Requires USER_UPDATE_EMAIL permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.profileService.verifyUserEmail( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/resend-verification-email', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_EMAIL), Validator('json', ResendVerificationEmailRequest), OpenAPI({ operationId: 'admin_resend_verification_email', summary: 'Resend verification email', responseSchema: null, statusCode: 204, security: 'adminApiKey', tags: 'Admin', description: 'Resend the account verification email for a user. Creates audit log entry and honours email verification resend limits. Requires USER_UPDATE_EMAIL permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.userService.securityService.resendVerificationEmail( ctx.req.valid('json'), adminUserId, auditLogReason, ); return ctx.body(null, 204); }, ); app.post( '/admin/users/send-password-reset', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_EMAIL), Validator('json', SendPasswordResetRequest), OpenAPI({ operationId: 'send_password_reset', summary: 'Send password reset', responseSchema: null, statusCode: 204, security: 'adminApiKey', tags: 'Admin', description: 'Send password reset email to user with reset link. User must use link within expiry window. Creates audit log entry. Requires USER_UPDATE_EMAIL permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.userService.securityService.sendPasswordReset( ctx.req.valid('json'), adminUserId, auditLogReason, ); return ctx.body(null, 204); }, ); app.post( '/admin/users/change-username', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_USERNAME), Validator('json', ChangeUsernameRequest), OpenAPI({ operationId: 'change_user_username', summary: 'Change user username', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Change user username. New username must meet requirements and be unique. Creates audit log entry. Requires USER_UPDATE_USERNAME permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.profileService.changeUsername( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/change-email', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_EMAIL), Validator('json', ChangeEmailRequest), OpenAPI({ operationId: 'change_user_email', summary: 'Change user email', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Change user email address. New email must be valid and unique. Marks email as verified. Creates audit log entry. Requires USER_UPDATE_EMAIL permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.profileService.changeEmail( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/terminate-sessions', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_FLAGS), Validator('json', TerminateSessionsRequest), OpenAPI({ operationId: 'terminate_user_sessions', summary: 'Terminate user sessions', responseSchema: TerminateSessionsResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Terminate all active user sessions across devices. Forces user to re-authenticate on next connection. Creates audit log entry. Requires USER_UPDATE_FLAGS permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); return ctx.json( await adminService.userService.securityService.terminateSessions( ctx.req.valid('json'), adminUserId, auditLogReason, ), ); }, ); app.post( '/admin/users/temp-ban', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_TEMP_BAN), Validator('json', TempBanUserRequest), OpenAPI({ operationId: 'temp_ban_user', summary: 'Temp ban user', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Apply temporary ban to user account for specified duration. Prevents login and guild operations. Automatically lifts after expiry. Creates audit log entry. Requires USER_TEMP_BAN permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.banService.tempBanUser( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/unban', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_TEMP_BAN), Validator('json', DisableMfaRequest), OpenAPI({ operationId: 'unban_user', summary: 'Unban user', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Immediately remove temporary ban from user account. User can log in and access guilds again. Creates audit log entry. Requires USER_TEMP_BAN permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.banService.unbanUser( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/schedule-deletion', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_DELETE), Validator('json', ScheduleAccountDeletionRequest), OpenAPI({ operationId: 'schedule_account_deletion', summary: 'Schedule account deletion', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Schedule user account for deletion after grace period. Account will be fully deleted with all content unless cancellation is executed. Creates audit log entry. Requires USER_DELETE permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.deletionService.scheduleAccountDeletion( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/cancel-deletion', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_DELETE), Validator('json', DisableMfaRequest), OpenAPI({ operationId: 'cancel_account_deletion', summary: 'Cancel account deletion', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Cancel a scheduled account deletion. User account restoration prevents data loss. Creates audit log entry. Requires USER_DELETE permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.deletionService.cancelAccountDeletion( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/cancel-bulk-message-deletion', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_CANCEL_BULK_MESSAGE_DELETION), Validator('json', CancelBulkMessageDeletionRequest), OpenAPI({ operationId: 'admin_cancel_bulk_message_deletion', summary: 'Cancel bulk message deletion', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Cancel a scheduled bulk message deletion job for a user. Prevents deletion of user messages across guilds. Creates audit log entry. Requires USER_CANCEL_BULK_MESSAGE_DELETION permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.cancelBulkMessageDeletion( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/set-acls', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.ACL_SET_USER), Validator('json', SetUserAclsRequest), OpenAPI({ operationId: 'set_user_acls', summary: 'Set user ACLs', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Grant or revoke admin ACL permissions to user. Controls admin capabilities and panel access. Creates audit log entry. Requires ACL_SET_USER permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.securityService.setUserAcls( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/set-traits', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_TRAITS), Validator('json', SetUserTraitsRequest), OpenAPI({ operationId: 'set_user_traits', summary: 'Set user traits', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Set or update user trait attributes and profile metadata. Traits customize user display and features. Creates audit log entry. Requires USER_UPDATE_TRAITS permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.securityService.setUserTraits( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/update-flags', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_FLAGS), Validator('json', UpdateUserFlagsRequest), OpenAPI({ operationId: 'update_user_flags', summary: 'Update user flags', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Add or remove user flags to control account features and restrictions. Flags determine verification status and special properties. Creates audit log entry. Requires USER_UPDATE_FLAGS permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); const body = ctx.req.valid('json'); const userId = createUserID(body.user_id); const addFlags = body.add_flags.map((flag) => BigInt(flag)); const removeFlags = body.remove_flags.map((flag) => BigInt(flag)); return ctx.json( await adminService.userService.securityService.updateUserFlags({ userId, data: {addFlags, removeFlags}, adminUserId, auditLogReason, acls: adminUserAcls, }), ); }, ); app.post( '/admin/users/update-premium-flags', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_FLAGS), Validator('json', UpdatePremiumFlagsRequest), OpenAPI({ operationId: 'update_user_premium_flags', summary: 'Update user premium flags', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Add or remove premium-related flags on a user account (badge visibility, override, purchase block, etc). Creates audit log entry. Requires USER_UPDATE_FLAGS permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); const body = ctx.req.valid('json'); const userId = createUserID(body.user_id); const addFlags = body.add_flags; const removeFlags = body.remove_flags; return ctx.json( await adminService.userService.securityService.updatePremiumFlags({ userId, data: {addFlags, removeFlags}, adminUserId, auditLogReason, acls: adminUserAcls, }), ); }, ); app.post( '/admin/users/update-has-verified-phone', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_PHONE), Validator('json', UpdateHasVerifiedPhoneRequest), OpenAPI({ operationId: 'update_user_has_verified_phone', summary: 'Update user phone verification flag', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Set whether a user is treated as having completed phone verification. This is the only supported path for clearing the irreversible user-facing phone verification flag.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.securityService.updateHasVerifiedPhone( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/change-dob', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_DOB), Validator('json', ChangeDobRequest), OpenAPI({ operationId: 'change_user_dob', summary: 'Change user DOB', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Update user date of birth. May affect age-restricted content access. Creates audit log entry. Requires USER_UPDATE_DOB permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.profileService.changeDob( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/update-suspicious-activity-flags', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_UPDATE_SUSPICIOUS_ACTIVITY), Validator('json', UpdateSuspiciousActivityFlagsRequest), OpenAPI({ operationId: 'update_suspicious_activity_flags', summary: 'Update suspicious activity flags', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Flag user as suspicious for account abuse, fraud, or policy violations. Enables enforcement actions and rate limiting. Creates audit log entry. Requires USER_UPDATE_SUSPICIOUS_ACTIVITY permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.securityService.updateSuspiciousActivityFlags( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/disable-suspicious', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_DISABLE_SUSPICIOUS), Validator('json', DisableForSuspiciousActivityRequest), OpenAPI({ operationId: 'disable_user_suspicious', summary: 'Disable user for suspicious activity', responseSchema: UserMutationResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Disable user account due to suspicious activity or abuse. Account is locked pending review. User cannot access services. Creates audit log entry. Requires USER_DISABLE_SUSPICIOUS permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json( await adminService.userService.securityService.disableForSuspiciousActivity( ctx.req.valid('json'), adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/list-sessions', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_LIST_SESSIONS), Validator('json', ListUserSessionsRequest), OpenAPI({ operationId: 'list_user_sessions', summary: 'List user sessions', responseSchema: ListUserSessionsResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'List all active user sessions across devices. Shows device info, IP, last activity, and creation time. Requires USER_LIST_SESSIONS permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); const adminUserAcls = ctx.get('adminUserAcls'); const body = ctx.req.valid('json'); return ctx.json( await adminService.userService.securityService.listUserSessions( body.user_id, adminUserId, auditLogReason, adminUserAcls, ), ); }, ); app.post( '/admin/users/change-log', RateLimitMiddleware(RateLimitConfigs.ADMIN_LOOKUP), requireAdminACL(AdminACLs.USER_LOOKUP), Validator('json', ListUserChangeLogRequest), OpenAPI({ operationId: 'get_user_change_log', summary: 'Get user change log', responseSchema: ListUserChangeLogResponseSchema, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Retrieve complete change log history for a user. Shows all profile modifications, admin actions, and account changes with timestamps. Requires USER_LOOKUP permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserAcls = ctx.get('adminUserAcls'); return ctx.json(await adminService.userService.listUserChangeLog(ctx.req.valid('json'), adminUserAcls)); }, ); app.post( '/admin/users/list-relationships', RateLimitMiddleware(RateLimitConfigs.ADMIN_LOOKUP), requireAdminACL(AdminACLs.USER_LIST_RELATIONSHIPS), Validator('json', ListUserRelationshipsRequest), OpenAPI({ operationId: 'admin_list_user_relationships', summary: 'List user relationships', responseSchema: ListUserRelationshipsResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: "List a user's friends, incoming and outgoing friend requests, and blocked users. Requires USER_LIST_RELATIONSHIPS permission.", }), async (ctx) => { const adminService = ctx.get('adminService'); return ctx.json(await adminService.relationshipService.listRelationships(ctx.req.valid('json'))); }, ); app.post( '/admin/users/remove-relationship', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_REMOVE_RELATIONSHIP), Validator('json', RemoveUserRelationshipRequest), OpenAPI({ operationId: 'remove_user_relationship', summary: 'Remove user relationship', responseSchema: null, statusCode: 204, security: 'adminApiKey', tags: 'Admin', description: 'Remove a single relationship row for a user. For friend and outgoing_request, the mirror entry on the other user is also removed. Dispatches RELATIONSHIP_REMOVE gateway events. Requires USER_REMOVE_RELATIONSHIP permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); await adminService.relationshipService.removeRelationship(ctx.req.valid('json'), adminUserId, auditLogReason); return ctx.body(null, 204); }, ); app.post( '/admin/users/remove-relationships-by-category', RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY), requireAdminACL(AdminACLs.USER_REMOVE_RELATIONSHIP), Validator('json', RemoveUserRelationshipsByCategoryRequest), OpenAPI({ operationId: 'remove_user_relationships_by_category', summary: "Remove all of a user's relationships in a category", responseSchema: RemoveUserRelationshipsResponse, statusCode: 200, security: 'adminApiKey', tags: 'Admin', description: 'Bulk-remove every relationship of the chosen category (friend, incoming_request, outgoing_request, blocked) for a user. Mirror entries on the other party are removed for friend, incoming_request, and outgoing_request. Requires USER_REMOVE_RELATIONSHIP permission.', }), async (ctx) => { const adminService = ctx.get('adminService'); const adminUserId = ctx.get('adminUserId'); const auditLogReason = ctx.get('auditLogReason'); return ctx.json( await adminService.relationshipService.removeRelationshipsByCategory( ctx.req.valid('json'), adminUserId, auditLogReason, ), ); }, ); }