feat: add global user administration for admin users

Backend:
- Add getAllUsuarios() to get users from all tenants
- Add updateUsuarioGlobal() to edit users and change their tenant
- Add deleteUsuarioGlobal() for global user deletion
- Add global admin check based on tenant RFC
- Add new API routes: /usuarios/global/*

Frontend:
- Add UserListItem.tenantId and tenantName fields
- Add /admin/usuarios page with full user management
- Support filtering by tenant and search
- Inline editing for name, role, and tenant assignment
- Group users by company for better organization
- Add "Admin Usuarios" menu item for admin navigation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Consultoria AS
2026-01-25 01:22:34 +00:00
parent 2655a51a99
commit 69efb585d3
8 changed files with 522 additions and 3 deletions

View File

@@ -1,6 +1,21 @@
import { Request, Response, NextFunction } from 'express';
import * as usuariosService from '../services/usuarios.service.js';
import { AppError } from '../utils/errors.js';
import { prisma } from '../config/database.js';
// RFC del tenant administrador global
const ADMIN_TENANT_RFC = 'AASI940812GM6';
async function isGlobalAdmin(req: Request): Promise<boolean> {
if (req.user!.role !== 'admin') return false;
const tenant = await prisma.tenant.findUnique({
where: { id: req.user!.tenantId },
select: { rfc: true },
});
return tenant?.rfc === ADMIN_TENANT_RFC;
}
export async function getUsuarios(req: Request, res: Response, next: NextFunction) {
try {
@@ -11,6 +26,21 @@ export async function getUsuarios(req: Request, res: Response, next: NextFunctio
}
}
/**
* Obtiene todos los usuarios de todas las empresas (solo admin global)
*/
export async function getAllUsuarios(req: Request, res: Response, next: NextFunction) {
try {
if (!(await isGlobalAdmin(req))) {
throw new AppError(403, 'Solo el administrador global puede ver todos los usuarios');
}
const usuarios = await usuariosService.getAllUsuarios();
res.json(usuarios);
} catch (error) {
next(error);
}
}
export async function inviteUsuario(req: Request, res: Response, next: NextFunction) {
try {
if (req.user!.role !== 'admin') {
@@ -28,7 +58,8 @@ export async function updateUsuario(req: Request, res: Response, next: NextFunct
if (req.user!.role !== 'admin') {
throw new AppError(403, 'Solo administradores pueden modificar usuarios');
}
const usuario = await usuariosService.updateUsuario(req.user!.tenantId, req.params.id, req.body);
const userId = req.params.id as string;
const usuario = await usuariosService.updateUsuario(req.user!.tenantId, userId, req.body);
res.json(usuario);
} catch (error) {
next(error);
@@ -40,10 +71,49 @@ export async function deleteUsuario(req: Request, res: Response, next: NextFunct
if (req.user!.role !== 'admin') {
throw new AppError(403, 'Solo administradores pueden eliminar usuarios');
}
if (req.params.id === req.user!.id) {
const userId = req.params.id as string;
if (userId === req.user!.userId) {
throw new AppError(400, 'No puedes eliminar tu propia cuenta');
}
await usuariosService.deleteUsuario(req.user!.tenantId, req.params.id);
await usuariosService.deleteUsuario(req.user!.tenantId, userId);
res.status(204).send();
} catch (error) {
next(error);
}
}
/**
* Actualiza un usuario globalmente (puede cambiar de empresa)
*/
export async function updateUsuarioGlobal(req: Request, res: Response, next: NextFunction) {
try {
if (!(await isGlobalAdmin(req))) {
throw new AppError(403, 'Solo el administrador global puede modificar usuarios globalmente');
}
const userId = req.params.id as string;
if (userId === req.user!.userId && req.body.tenantId) {
throw new AppError(400, 'No puedes cambiar tu propia empresa');
}
const usuario = await usuariosService.updateUsuarioGlobal(userId, req.body);
res.json(usuario);
} catch (error) {
next(error);
}
}
/**
* Elimina un usuario globalmente
*/
export async function deleteUsuarioGlobal(req: Request, res: Response, next: NextFunction) {
try {
if (!(await isGlobalAdmin(req))) {
throw new AppError(403, 'Solo el administrador global puede eliminar usuarios globalmente');
}
const userId = req.params.id as string;
if (userId === req.user!.userId) {
throw new AppError(400, 'No puedes eliminar tu propia cuenta');
}
await usuariosService.deleteUsuarioGlobal(userId);
res.status(204).send();
} catch (error) {
next(error);