fix: Correcciones de errores y mejoras del dashboard CFO
- Corregido auth.service.ts para usar estructura multi-tenant correcta (user_tenants) - Dashboard rediseñado para CFO digital (ingresos/egresos, CFDIs, alertas) - Sidebar actualizado con rutas correctas (Métricas, Transacciones, CFDIs, Reportes, Asistente IA) - Agregadas páginas de Perfil (/profile) y Configuración (/settings) - Corregidos errores de TypeScript (strict mode, tipos duplicados) - Actualizado docker-compose.yml a PostgreSQL 16 - Corregidas migraciones SQL (índices IMMUTABLE, constraints) - Configuración ESM modules en packages - CORS configurado para acceso de red local Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -149,8 +149,8 @@ export class AuthService {
|
||||
const passwordHash = await hashPassword(input.password);
|
||||
|
||||
await client.query(
|
||||
`INSERT INTO public.users (id, email, password_hash, first_name, last_name, role, tenant_id, is_active, email_verified, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW(), NOW())`,
|
||||
`INSERT INTO public.users (id, email, password_hash, first_name, last_name, default_role, is_active, is_email_verified, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW())`,
|
||||
[
|
||||
userId,
|
||||
input.email.toLowerCase(),
|
||||
@@ -158,12 +158,18 @@ export class AuthService {
|
||||
input.firstName,
|
||||
input.lastName,
|
||||
'owner',
|
||||
tenantId,
|
||||
true,
|
||||
false,
|
||||
]
|
||||
);
|
||||
|
||||
// Associate user with tenant via user_tenants
|
||||
await client.query(
|
||||
`INSERT INTO public.user_tenants (user_id, tenant_id, role, is_active, accepted_at, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, NOW(), NOW(), NOW())`,
|
||||
[userId, tenantId, 'owner', true]
|
||||
);
|
||||
|
||||
// Create session
|
||||
const sessionId = uuidv4();
|
||||
const tokens = jwtService.generateTokenPair(
|
||||
@@ -178,11 +184,12 @@ export class AuthService {
|
||||
);
|
||||
|
||||
const refreshTokenHash = hashToken(tokens.refreshToken);
|
||||
const tokenHash = hashToken(tokens.accessToken);
|
||||
|
||||
await client.query(
|
||||
`INSERT INTO public.user_sessions (id, user_id, tenant_id, refresh_token_hash, expires_at, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, NOW())`,
|
||||
[sessionId, userId, tenantId, refreshTokenHash, jwtService.getRefreshTokenExpiration()]
|
||||
`INSERT INTO public.user_sessions (id, user_id, tenant_id, token_hash, refresh_token_hash, expires_at, created_at, last_activity_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW())`,
|
||||
[sessionId, userId, tenantId, tokenHash, refreshTokenHash, jwtService.getRefreshTokenExpiration()]
|
||||
);
|
||||
|
||||
await client.query('COMMIT');
|
||||
@@ -225,12 +232,17 @@ export class AuthService {
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
// Find user by email
|
||||
// Find user by email - join through user_tenants for multi-tenant support
|
||||
const userResult = await client.query(
|
||||
`SELECT u.*, t.schema_name, t.name as tenant_name, t.slug as tenant_slug
|
||||
`SELECT u.id, u.email, u.password_hash, u.first_name, u.last_name, u.is_active,
|
||||
ut.role, ut.tenant_id,
|
||||
t.schema_name, t.name as tenant_name, t.slug as tenant_slug, t.status as tenant_status
|
||||
FROM public.users u
|
||||
JOIN public.tenants t ON u.tenant_id = t.id
|
||||
WHERE u.email = $1 AND u.is_active = true AND t.is_active = true`,
|
||||
LEFT JOIN public.user_tenants ut ON u.id = ut.user_id AND ut.is_active = true
|
||||
LEFT JOIN public.tenants t ON ut.tenant_id = t.id
|
||||
WHERE u.email = $1 AND u.is_active = true
|
||||
ORDER BY ut.created_at DESC
|
||||
LIMIT 1`,
|
||||
[input.email.toLowerCase()]
|
||||
);
|
||||
|
||||
@@ -249,28 +261,35 @@ export class AuthService {
|
||||
throw new AuthenticationError('Credenciales invalidas');
|
||||
}
|
||||
|
||||
// Determine role - use tenant role if available, otherwise use default_role
|
||||
const userRole = user.role || 'viewer';
|
||||
const tenantId = user.tenant_id;
|
||||
const schemaName = user.schema_name;
|
||||
|
||||
// Create session
|
||||
const sessionId = uuidv4();
|
||||
const tokens = jwtService.generateTokenPair(
|
||||
{
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
role: user.role,
|
||||
tenant_id: user.tenant_id,
|
||||
schema_name: user.schema_name,
|
||||
role: userRole,
|
||||
tenant_id: tenantId,
|
||||
schema_name: schemaName,
|
||||
},
|
||||
sessionId
|
||||
);
|
||||
|
||||
const refreshTokenHash = hashToken(tokens.refreshToken);
|
||||
const tokenHash = hashToken(tokens.accessToken);
|
||||
|
||||
await client.query(
|
||||
`INSERT INTO public.user_sessions (id, user_id, tenant_id, refresh_token_hash, user_agent, ip_address, expires_at, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())`,
|
||||
`INSERT INTO public.user_sessions (id, user_id, tenant_id, token_hash, refresh_token_hash, user_agent, ip_address, expires_at, created_at, last_activity_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW())`,
|
||||
[
|
||||
sessionId,
|
||||
user.id,
|
||||
user.tenant_id,
|
||||
tenantId,
|
||||
tokenHash,
|
||||
refreshTokenHash,
|
||||
userAgent || null,
|
||||
ipAddress || null,
|
||||
@@ -281,9 +300,9 @@ export class AuthService {
|
||||
// Update last login
|
||||
await client.query('UPDATE public.users SET last_login_at = NOW() WHERE id = $1', [user.id]);
|
||||
|
||||
auditLog('LOGIN_SUCCESS', user.id, user.tenant_id, { userAgent, ipAddress });
|
||||
auditLog('LOGIN_SUCCESS', user.id, tenantId, { userAgent, ipAddress });
|
||||
|
||||
logger.info('User logged in', { userId: user.id, tenantId: user.tenant_id });
|
||||
logger.info('User logged in', { userId: user.id, tenantId });
|
||||
|
||||
return {
|
||||
user: {
|
||||
@@ -291,14 +310,14 @@ export class AuthService {
|
||||
email: user.email,
|
||||
first_name: user.first_name,
|
||||
last_name: user.last_name,
|
||||
role: user.role,
|
||||
tenant_id: user.tenant_id,
|
||||
role: userRole,
|
||||
tenant_id: tenantId,
|
||||
},
|
||||
tenant: {
|
||||
id: user.tenant_id,
|
||||
id: tenantId,
|
||||
name: user.tenant_name,
|
||||
slug: user.tenant_slug,
|
||||
schema_name: user.schema_name,
|
||||
schema_name: schemaName,
|
||||
},
|
||||
tokens,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user