UNPKG

skailan-core

Version:

Servicio de autenticación y multitenancy para Skailan.

114 lines 4.53 kB
import bcrypt from "bcryptjs"; import jwt from "jsonwebtoken"; // import { sendEmail } from "@skailan/email"; import crypto from "crypto"; const JWT_SECRET = process.env.JWT_SECRET || "skailan-secret"; // Función temporal para envío de email const sendEmail = async (to, subject, html) => { console.log(`📧 Email simulado enviado a ${to}: ${subject}`); return { success: true }; }; export class AuthService { static async register(prisma, email, password, name) { const existingUser = await prisma.user.findUnique({ where: { email } }); if (existingUser) { throw new Error("El email ya está registrado."); } const hashedPassword = await bcrypt.hash(password, 10); const newUser = await prisma.user.create({ data: { email, password: hashedPassword, name }, }); const token = jwt.sign({ id: newUser.id, email: newUser.email }, JWT_SECRET, { expiresIn: "7d", }); return { user: newUser, token }; } static async login(prisma, email, password) { const user = await prisma.user.findUnique({ where: { email } }); if (!user) { throw new Error("Credenciales inválidas."); } const isValidPassword = await bcrypt.compare(password, user.password); if (!isValidPassword) { throw new Error("Credenciales inválidas."); } const token = jwt.sign({ id: user.id, email: user.email }, JWT_SECRET, { expiresIn: "7d", }); return { user, token }; } static async getOrganizations(prisma, userId) { const memberships = await prisma.membership.findMany({ where: { userId }, include: { organization: true }, }); return memberships.map((m) => ({ id: m.organization?.id, name: m.organization?.name, slug: m.organization?.slug, role: m.role, status: m.status, })); } static async switchOrganization(prisma, userId, email, organizationId) { const membership = await prisma.membership.findUnique({ where: { userId_organizationId: { userId, organizationId } }, }); if (!membership) { throw new Error("No eres miembro de esta organización."); } const newToken = jwt.sign({ id: userId, email: email, activeOrg: organizationId }, JWT_SECRET, { expiresIn: "7d" }); return { token: newToken, activeOrg: organizationId }; } static async requestPasswordReset(prisma, email) { const user = await prisma.user.findUnique({ where: { email } }); if (!user) { throw new Error("Usuario no encontrado."); } const token = crypto.randomBytes(32).toString("hex"); const expiry = new Date(Date.now() + 1000 * 60 * 30); // 30 minutos await prisma.passwordResetToken.create({ data: { userId: user.id, token, expiry, }, }); const resetUrl = `${process.env.APP_URL}/reset-password?token=${token}`; await sendEmail(email, "Restablecer contraseña", ` <h1>Restablecer contraseña</h1> <p>Haz clic en el siguiente enlace para restablecer tu contraseña:</p> <a href="${resetUrl}">Restablecer contraseña</a> <p>Este enlace expira en 30 minutos.</p> `); return { message: "Se ha enviado un email con las instrucciones." }; } static async resetPassword(prisma, token, newPassword) { const resetToken = await prisma.passwordResetToken.findUnique({ where: { token }, include: { user: true }, }); if (!resetToken || resetToken.expiry < new Date()) { throw new Error("Token inválido o expirado."); } const hashedPassword = await bcrypt.hash(newPassword, 10); await prisma.user.update({ where: { id: resetToken.userId }, data: { password: hashedPassword }, }); await prisma.passwordResetToken.delete({ where: { token }, }); return { message: "Contraseña actualizada correctamente." }; } static async verifyToken(token) { try { const decoded = jwt.verify(token, JWT_SECRET); return decoded; } catch (error) { throw new Error("Token inválido."); } } } //# sourceMappingURL=AuthService.js.map