import { isIP } from 'node:net'; import { ConflictException } from '@nestjs/common'; import { EntityManager } from 'typeorm'; import type { AuthPrincipal, RequestWithContext, } from '../../common/http/request-context'; import { AuditSource } from '../../database/entities'; const REQUIRED_RECOVERY_PERMISSIONS = [ 'roles.manage', 'users.assign_roles', ] as const; export function administrationAuditContext( principal: AuthPrincipal, request: RequestWithContext, ) { const candidateIp = request.ip || request.socket.remoteAddress || ''; const rawUserAgent = request.header('user-agent')?.trim(); return { actorUserId: principal.userId, actorUsername: principal.username, requestId: request.requestId, source: AuditSource.WEB, ip: isIP(candidateIp) ? candidateIp : null, userAgent: rawUserAgent ? rawUserAgent.slice(0, 2048) : null, }; } export async function assertAdministrativeRecoveryRemains( manager: EntityManager, ): Promise { const [row] = (await manager.query( ` SELECT COUNT(*)::integer AS count FROM ( SELECT user_account.id FROM users user_account INNER JOIN user_roles user_role ON user_role.user_id = user_account.id INNER JOIN role_permissions role_permission ON role_permission.role_id = user_role.role_id INNER JOIN permissions permission ON permission.id = role_permission.permission_id WHERE user_account.status = 'ACTIVE' AND permission.code = ANY($1::varchar[]) GROUP BY user_account.id HAVING COUNT(DISTINCT permission.code) = $2 ) administrators `, [REQUIRED_RECOVERY_PERMISSIONS, REQUIRED_RECOVERY_PERMISSIONS.length], )) as Array<{ count: number }>; if (!row || Number(row.count) < 1) { throw new ConflictException({ code: 'LAST_ADMINISTRATOR_PROTECTED', message: 'Debe permanecer al menos un usuario activo capaz de administrar roles y asignaciones', }); } } export function isUniqueViolation(error: unknown): boolean { return ( typeof error === 'object' && error !== null && 'code' in error && (error as { code?: unknown }).code === '23505' ); }