import bcrypt from 'bcryptjs' import crypto from 'crypto' import { AppError } from '../../http/errors' import { prisma } from '../../lib/prisma' import { sendTransactionalEmail } from '../../services/notificationService' import * as repo from './auth.company.repo' import type { output } from 'zod' import type { accountStartSchema } from './auth.account.schemas' type AccountStartInput = output function ensureDashboardBasePath(baseUrl: string) { try { const url = new URL(baseUrl) const pathname = url.pathname.replace(/\/$/, '') if (!pathname.endsWith('/dashboard')) url.pathname = `${pathname}/dashboard` return url.toString().replace(/\/$/, '') } catch { const trimmed = baseUrl.replace(/\/$/, '') return trimmed.endsWith('/dashboard') ? trimmed : `${trimmed}/dashboard` } } /** * Minimal account creation — Step 0 of progressive signup. * Creates a DRAFT company + OWNER employee, sends a verification email. * The user must verify their email before they can log in. */ export async function startAccount(body: AccountStartInput) { if (await repo.findEmployeeByEmail(body.email)) { throw new AppError('An account with this email already exists', 409, 'email_taken') } const slug = `company-${Date.now().toString(36)}` const passwordHash = await bcrypt.hash(body.password, 12) const verificationToken = crypto.randomBytes(32).toString('hex') const result = await prisma.$transaction(async (tx: any) => { const now = new Date() const trialEndAt = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000) const company = await tx.company.create({ data: { name: '', slug, email: body.email, address: {}, status: 'TRIALING', }, }) await tx.brandSettings.create({ data: { companyId: company.id, displayName: body.email.split('@')[0] || 'My Workspace', subdomain: slug, defaultLocale: body.preferredLanguage, defaultCurrency: 'MAD', }, }) await tx.subscription.create({ data: { companyId: company.id, plan: 'STARTER', billingPeriod: 'MONTHLY', currency: 'MAD', status: 'TRIALING', trialStartAt: now, trialEndAt, currentPeriodStart: now, currentPeriodEnd: trialEndAt, }, }) const employee = await tx.employee.create({ data: { companyId: company.id, clerkUserId: `local_owner_${company.id}`, firstName: '', lastName: '', email: body.email, passwordHash, emailVerificationToken: verificationToken, role: 'OWNER', preferredLanguage: body.preferredLanguage, isActive: true, }, include: { company: true }, }) return { employee, company } }) const dashboardUrl = ensureDashboardBasePath( process.env.DASHBOARD_URL ?? process.env.NEXT_PUBLIC_DASHBOARD_URL ?? 'http://localhost:3000/dashboard', ) const verifyUrl = `${dashboardUrl}/verify-email?token=${encodeURIComponent(verificationToken)}` const emailSubjects: Record<'en' | 'fr' | 'ar', string> = { en: 'Verify your email — RentalDriveGo', fr: 'Vérifiez votre email — RentalDriveGo', ar: 'تحقق من بريدك الإلكتروني — RentalDriveGo', } const emailBodies: Record<'en' | 'fr' | 'ar', string> = { en: `

Welcome to RentalDriveGo!

Click the link below to verify your email and activate your account:

Verify Email

If you did not create this account, you can safely ignore this email.

`, fr: `

Bienvenue sur RentalDriveGo !

Cliquez sur le lien ci-dessous pour vérifier votre email et activer votre compte :

Vérifier l'email

Si vous n'avez pas créé ce compte, ignorez cet email.

`, ar: `

مرحباً بك في RentalDriveGo!

انقر على الرابط أدناه للتحقق من بريدك الإلكتروني وتفعيل حسابك:

تحقق من البريد الإلكتروني

إذا لم تقم بإنشاء هذا الحساب، يمكنك تجاهل هذا البريد الإلكتروني.

`, } const emailTexts: Record<'en' | 'fr' | 'ar', string> = { en: `Welcome to RentalDriveGo!\n\nVerify your email by visiting:\n${verifyUrl}\n\nIf you did not create this account, you can safely ignore this email.`, fr: `Bienvenue sur RentalDriveGo !\n\nVérifiez votre email en visitant :\n${verifyUrl}\n\nSi vous n'avez pas créé ce compte, ignorez cet email.`, ar: `مرحباً بك في RentalDriveGo!\n\nتحقق من بريدك الإلكتروني بزيارة:\n${verifyUrl}\n\nإذا لم تقم بإنشاء هذا الحساب، يمكنك تجاهل هذا البريد الإلكتروني.`, } const lang: 'en' | 'fr' | 'ar' = body.preferredLanguage === 'ar' || body.preferredLanguage === 'fr' ? body.preferredLanguage : 'en' await sendTransactionalEmail({ to: body.email, subject: emailSubjects[lang], html: emailBodies[lang], text: emailTexts[lang], }).catch((err) => { console.error('[AccountStart] Verification email delivery failed:', err?.message ?? String(err)) console.error('[AccountStart] SMTP config — host:', process.env.MAIL_HOST ?? 'not set', '| port:', process.env.MAIL_PORT ?? 'not set', '| user:', process.env.MAIL_USERNAME ? '***' : 'not set', '| pass:', process.env.MAIL_PASSWORD ? '***' : 'not set') console.error('[AccountStart] Resend config — apiKey:', process.env.RESEND_API_KEY ? (process.env.RESEND_API_KEY.startsWith('re_') ? 'valid' : 'placeholder') : 'not set') }) return { message: 'Account created. Please check your email to verify your address before signing in.', email: body.email, } }