chore: estructura inicial del MVP
Backend (Express + MariaDB) y frontend (React + Vite, identidad Fluent/Windows) con el nucleo funcional: seguridad, organizacion, proyectos, actividades, subtareas, dependencias, dashboard, calendario, gantt, comentarios, adjuntos, notificaciones in-app y catalogos. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import bcrypt from 'bcryptjs';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { pool } from '../../config/db.js';
|
||||
import { env } from '../../config/env.js';
|
||||
import { HttpError } from '../../utils/http.js';
|
||||
import { logAudit } from '../../utils/audit.js';
|
||||
|
||||
export async function login(email, password) {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT u.id, u.name, u.email, u.password_hash, u.is_active, u.department_id, r.code AS role_code, r.name AS role_name
|
||||
FROM users u JOIN roles r ON r.id = u.role_id
|
||||
WHERE u.email = ? LIMIT 1`,
|
||||
[email]
|
||||
);
|
||||
const user = rows[0];
|
||||
if (!user || !user.is_active) throw new HttpError(401, 'Credenciales invalidas');
|
||||
|
||||
const validPassword = await bcrypt.compare(password, user.password_hash);
|
||||
if (!validPassword) throw new HttpError(401, 'Credenciales invalidas');
|
||||
|
||||
const token = jwt.sign({ sub: user.id }, env.jwtSecret, { expiresIn: env.jwtExpires });
|
||||
|
||||
await logAudit({ userId: user.id, action: 'login', entityType: 'auth', entityId: user.id });
|
||||
|
||||
return {
|
||||
token,
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
roleCode: user.role_code,
|
||||
roleName: user.role_name,
|
||||
departmentId: user.department_id,
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user