import { prisma, tenantDb } from '../config/database.js'; import { hashPassword } from '../auth/passwords.js'; import { generateAccessToken, generateRefreshToken } from '../auth/tokens.js'; import type { DespachoSignupRequest } from '@horux/shared'; import type { JWTPayload, Role } from '@horux/shared'; import { emailService } from './email/email.service.js'; export async function signupDespacho(data: DespachoSignupRequest) { const { despacho, owner } = data; const existingUser = await prisma.user.findUnique({ where: { email: owner.email } }); if (existingUser) { throw new Error('Ya existe un usuario con este email'); } const passwordHash = await hashPassword(owner.password); const tenantSlug = `despacho_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`; const databaseName = `horux_${tenantSlug}`; const result = await prisma.$transaction(async (tx) => { const tenant = await tx.tenant.create({ data: { nombre: despacho.nombre, rfc: tenantSlug.toUpperCase(), plan: 'trial', databaseName: databaseName, verticalProfile: despacho.verticalProfile as any, dbMode: 'MANAGED', dbSchemaVersion: 0, trialEndsAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), codigoPostal: despacho.codigoPostal, }, }); const user = await tx.user.create({ data: { email: owner.email.toLowerCase(), passwordHash, nombre: owner.nombre, lastTenantId: tenant.id, }, }); const ownerRole = await tx.rol.findUnique({ where: { nombre: 'owner' } }); if (!ownerRole) throw new Error('Rol owner no encontrado en BD'); await tx.tenantMembership.create({ data: { userId: user.id, tenantId: tenant.id, rolId: ownerRole.id, isOwner: true, }, }); return { tenant, user }; }); try { await tenantDb.provisionDatabase(tenantSlug, databaseName); } catch (err: any) { await prisma.tenant.delete({ where: { id: result.tenant.id } }).catch(() => {}); await prisma.user.delete({ where: { id: result.user.id } }).catch(() => {}); throw new Error(`Error al crear base de datos del despacho: ${err.message}`); } const payload: Omit = { userId: result.user.id, email: result.user.email, role: 'owner' as Role, tenantId: result.tenant.id, tokenVersion: 0, }; const accessToken = generateAccessToken(payload); const refreshToken = generateRefreshToken(payload); await prisma.refreshToken.create({ data: { userId: result.user.id, token: refreshToken, expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), }, }); // Send welcome email (fire-and-forget) emailService.sendDespachoWelcome(owner.email, { nombre: result.user.nombre, despachoNombre: result.tenant.nombre, email: result.user.email, }).catch(err => console.error('[Signup] Welcome email failed:', err)); return { accessToken, refreshToken, user: { id: result.user.id, email: result.user.email, nombre: result.user.nombre, role: 'owner' as Role, tenantId: result.tenant.id, tenantName: result.tenant.nombre, tenantRfc: result.tenant.rfc, plan: result.tenant.plan, tenants: [{ id: result.tenant.id, nombre: result.tenant.nombre, rfc: result.tenant.rfc, plan: result.tenant.plan, role: 'owner' as Role, isOwner: true, }], }, }; }