skailan-core
Version:
Servicio de autenticación y multitenancy para Skailan.
114 lines • 4.53 kB
JavaScript
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