Spaces:
Sleeping
Sleeping
| /** | |
| * Logica de negocio del modulo de autenticacion. | |
| * | |
| * Responsabilidades: | |
| * - login({ email, password }) β buscar usuario, comparar hash con bcrypt, | |
| * verificar que este activo (isActive) y firmar JWT. | |
| * - logout({ jti, exp }) β invalidar el token activo anadiendo su jti | |
| * a la denylist en memoria hasta su expiracion original. | |
| * | |
| * Seguridad: | |
| * - Mensaje generico en fallo de login ("Email or password is incorrect") | |
| * para no revelar si el email existe. | |
| * - Bcrypt con salt rounds configurable (BCRYPT_ROUNDS, default 10). | |
| * - JWT firmado con HS256 y expiracion (JWT_EXPIRES_IN). | |
| * - Logout invalida el jti del token; el cliente debe descartar el token. | |
| * | |
| * Devuelve: | |
| * login β { token: string, user: { id, email } } | |
| * logout β void | |
| */ | |
| import bcrypt from 'bcryptjs'; | |
| import { prisma } from '../utils/prisma.js'; | |
| import { HttpError } from '../utils/apiResponse.js'; | |
| import { signToken, addToDenylist } from './jwt.js'; | |
| const INVALID_CREDENTIALS = new HttpError(401, 'INVALID_CREDENTIALS', 'Email or password is incorrect'); | |
| function buildUserResponse(user) { | |
| return { | |
| id: user.id, | |
| email: user.email, | |
| telegramChatId: user.telegramChatId, | |
| telegramBotToken: user.telegramBotToken, | |
| telegramAlertsEnabled: user.telegramAlertsEnabled, | |
| }; | |
| } | |
| export const login = async ({ email, password }) => { | |
| const user = await prisma.user.findUnique({ where: { email } }); | |
| if (!user || !user.isActive) throw INVALID_CREDENTIALS; | |
| const passwordOk = await bcrypt.compare(password, user.passwordHash); | |
| if (!passwordOk) throw INVALID_CREDENTIALS; | |
| const token = signToken({ sub: user.id, email: user.email }); | |
| return { | |
| token, | |
| user: buildUserResponse(user), | |
| }; | |
| }; | |
| export const register = async ({ email, password }) => { | |
| const existing = await prisma.user.findUnique({ where: { email } }); | |
| if (existing) { | |
| throw new HttpError(409, 'EMAIL_EXISTS', 'Email already registered'); | |
| } | |
| const passwordHash = await bcrypt.hash(password, 10); | |
| const user = await prisma.user.create({ | |
| data: { email, passwordHash, isActive: true }, | |
| }); | |
| const token = signToken({ sub: user.id, email: user.email }); | |
| return { | |
| token, | |
| user: buildUserResponse(user), | |
| }; | |
| }; | |
| export const logout = ({ jti, exp }) => { | |
| if (jti && exp) addToDenylist(jti, exp); | |
| }; | |
| export const updateTelegram = async (userId, { telegramBotToken, telegramChatId, telegramAlertsEnabled }) => { | |
| const data = {}; | |
| if (telegramBotToken !== undefined) data.telegramBotToken = telegramBotToken || null; | |
| if (telegramChatId !== undefined) data.telegramChatId = telegramChatId || null; | |
| if (telegramAlertsEnabled !== undefined) data.telegramAlertsEnabled = !!telegramAlertsEnabled; | |
| const user = await prisma.user.update({ | |
| where: { id: userId }, | |
| data, | |
| }); | |
| return { user: buildUserResponse(user) }; | |
| }; | |