Files
Horux360/apps/api/src/services/usuarios.service.ts
Consultoria AS 69efb585d3 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>
2026-01-25 01:22:34 +00:00

198 lines
4.6 KiB
TypeScript

import { prisma } from '../config/database.js';
import bcrypt from 'bcryptjs';
import type { UserListItem, UserInvite, UserUpdate } from '@horux/shared';
export async function getUsuarios(tenantId: string): Promise<UserListItem[]> {
const users = await prisma.user.findMany({
where: { tenantId },
select: {
id: true,
email: true,
nombre: true,
role: true,
active: true,
lastLogin: true,
createdAt: true,
},
orderBy: { createdAt: 'desc' },
});
return users.map(u => ({
...u,
lastLogin: u.lastLogin?.toISOString() || null,
createdAt: u.createdAt.toISOString(),
}));
}
export async function inviteUsuario(tenantId: string, data: UserInvite): Promise<UserListItem> {
// Check tenant user limit
const tenant = await prisma.tenant.findUnique({
where: { id: tenantId },
select: { usersLimit: true },
});
const currentCount = await prisma.user.count({ where: { tenantId } });
if (currentCount >= (tenant?.usersLimit || 1)) {
throw new Error('Límite de usuarios alcanzado para este plan');
}
// Generate temporary password
const tempPassword = Math.random().toString(36).slice(-8);
const passwordHash = await bcrypt.hash(tempPassword, 12);
const user = await prisma.user.create({
data: {
tenantId,
email: data.email,
passwordHash,
nombre: data.nombre,
role: data.role,
},
select: {
id: true,
email: true,
nombre: true,
role: true,
active: true,
lastLogin: true,
createdAt: true,
},
});
// In production, send email with tempPassword
console.log(`Temporary password for ${data.email}: ${tempPassword}`);
return {
...user,
lastLogin: user.lastLogin?.toISOString() || null,
createdAt: user.createdAt.toISOString(),
};
}
export async function updateUsuario(
tenantId: string,
userId: string,
data: UserUpdate
): Promise<UserListItem> {
const user = await prisma.user.update({
where: { id: userId, tenantId },
data: {
...(data.nombre && { nombre: data.nombre }),
...(data.role && { role: data.role }),
...(data.active !== undefined && { active: data.active }),
},
select: {
id: true,
email: true,
nombre: true,
role: true,
active: true,
lastLogin: true,
createdAt: true,
},
});
return {
...user,
lastLogin: user.lastLogin?.toISOString() || null,
createdAt: user.createdAt.toISOString(),
};
}
export async function deleteUsuario(tenantId: string, userId: string): Promise<void> {
await prisma.user.delete({
where: { id: userId, tenantId },
});
}
/**
* Obtiene todos los usuarios de todas las empresas (solo admin global)
*/
export async function getAllUsuarios(): Promise<UserListItem[]> {
const users = await prisma.user.findMany({
select: {
id: true,
email: true,
nombre: true,
role: true,
active: true,
lastLogin: true,
createdAt: true,
tenantId: true,
tenant: {
select: {
nombre: true,
},
},
},
orderBy: [{ tenant: { nombre: 'asc' } }, { createdAt: 'desc' }],
});
return users.map(u => ({
id: u.id,
email: u.email,
nombre: u.nombre,
role: u.role,
active: u.active,
lastLogin: u.lastLogin?.toISOString() || null,
createdAt: u.createdAt.toISOString(),
tenantId: u.tenantId,
tenantName: u.tenant.nombre,
}));
}
/**
* Actualiza un usuario globalmente (puede cambiar de tenant)
*/
export async function updateUsuarioGlobal(
userId: string,
data: UserUpdate & { tenantId?: string }
): Promise<UserListItem> {
const user = await prisma.user.update({
where: { id: userId },
data: {
...(data.nombre && { nombre: data.nombre }),
...(data.role && { role: data.role }),
...(data.active !== undefined && { active: data.active }),
...(data.tenantId && { tenantId: data.tenantId }),
},
select: {
id: true,
email: true,
nombre: true,
role: true,
active: true,
lastLogin: true,
createdAt: true,
tenantId: true,
tenant: {
select: {
nombre: true,
},
},
},
});
return {
id: user.id,
email: user.email,
nombre: user.nombre,
role: user.role,
active: user.active,
lastLogin: user.lastLogin?.toISOString() || null,
createdAt: user.createdAt.toISOString(),
tenantId: user.tenantId,
tenantName: user.tenant.nombre,
};
}
/**
* Elimina un usuario globalmente
*/
export async function deleteUsuarioGlobal(userId: string): Promise<void> {
await prisma.user.delete({
where: { id: userId },
});
}