import { Controller, Get, Query, Req } from '@nestjs/common'; import { ApiCookieAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; import type { Request } from 'express'; import { AuditWriter } from '../audit/audit.service'; import { AdminUsersReader, type AdminUsersPage } from './admin-users-reader.service'; import { RequireAdmin } from './require-admin.decorator'; import { AdminUsersQueryDto } from './users-query.dto'; /** * `GET /api/admin/users` — paginated, filterable view of the BFF's * user directory per ADR-0020's v1 admin catalogue. Gated by * `@RequireAdmin` and emits `admin.users.query` on every read so * the directory browsing surface is itself auditable. * * Mirrors `AdminAuditController`'s shape so the SPA's data flow * (filters → service → audit emit → response) follows the same * pattern across admin modules. */ @ApiTags('admin (user directory)') @ApiCookieAuth('portal_admin_session') @Controller('admin/users') @RequireAdmin() export class AdminUsersController { constructor( private readonly usersReader: AdminUsersReader, private readonly audit: AuditWriter, ) {} @ApiOperation({ summary: 'Paginated user-directory query — emits `admin.users.query` on every call', }) @Get() async list(@Req() req: Request, @Query() filters: AdminUsersQueryDto): Promise { const page = await this.usersReader.findUsers(filters); // Guard guarantees `req.session.user`; defensively narrow. const actorOid = req.session.user?.oid; if (actorOid !== undefined) { await this.audit.adminUsersQuery({ actor: { oid: actorOid }, filters: { ...filters }, resultCount: page.items.length, }); } return page; } }