Files
carmanagement/apps/api/src/modules/auth/auth.company.service.ts
T
root f22e0d45e1
Build & Deploy / Build & Push Docker Image (push) Successful in 7m57s
Test / Type Check (all packages) (push) Successful in 4m27s
Build & Deploy / Deploy to VPS (push) Successful in 7s
Test / API Unit Tests (push) Failing after 3m2s
Test / Homepage Unit Tests (push) Successful in 4m3s
Test / Storefront Unit Tests (push) Successful in 3m32s
Test / Admin Unit Tests (push) Successful in 3m27s
Test / Dashboard Unit Tests (push) Successful in 3m3s
Test / API Integration Tests (push) Failing after 3m53s
fix subscription page
2026-06-29 23:15:55 -04:00

124 lines
4.3 KiB
TypeScript

import bcrypt from 'bcryptjs'
import { AppError } from '../../http/errors'
import { prisma } from '../../lib/prisma'
import { sendNotification } from '../../services/notificationService'
import {
coerceNotificationLocale,
localizeBillingPeriod,
localizePlanName,
} from '../../services/notificationLocalizationService'
import { presentCompanySignup } from './auth.presenter'
import * as repo from './auth.company.repo'
import { companySignupSchema } from './auth.company.schemas'
import type { output } from 'zod'
type CompanySignupInput = output<typeof companySignupSchema>
const TRIAL_PERIOD_DAYS = 30
function slugify(value: string) {
return value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 50) || 'company'
}
async function generateUniqueSlug(baseName: string) {
const base = slugify(baseName)
for (let attempt = 0; attempt < 25; attempt++) {
const slug = attempt === 0 ? base : `${base}-${attempt + 1}`
const existing = await repo.findCompanyBySlug(slug)
if (!existing) return slug
}
return `${base}-${Date.now().toString(36)}`
}
export async function signup(body: CompanySignupInput) {
if (await repo.findCompanyByEmail(body.companyEmail)) {
throw new AppError('A company account with this contact email already exists', 409, 'company_email_taken')
}
if (await repo.findEmployeeByEmail(body.email)) {
throw new AppError('An employee account with this owner email already exists', 409, 'owner_email_taken')
}
const slug = await generateUniqueSlug(body.companyName)
const now = new Date()
const trialEndAt = new Date(now.getTime() + TRIAL_PERIOD_DAYS * 24 * 60 * 60 * 1000)
const passwordHash = await bcrypt.hash(body.password, 12)
const result = await prisma.$transaction((tx: any) => repo.createCompanySignup(tx, {
companyName: body.companyName,
legalName: body.legalName,
slug,
ownerEmail: body.email,
companyEmail: body.companyEmail,
companyPhone: body.companyPhone,
streetAddress: body.streetAddress,
city: body.city,
country: body.country,
zipCode: body.zipCode,
legalForm: body.legalForm,
managerName: `${body.firstName} ${body.lastName}`.trim(),
iceNumber: body.iceNumber,
taxId: body.taxId,
operatingLicenseNumber: body.operatingLicenseNumber,
operatingLicenseIssuedAt: body.operatingLicenseIssuedAt,
operatingLicenseIssuedBy: body.operatingLicenseIssuedBy,
fax: body.fax,
yearsActive: body.yearsActive,
representativeName: body.representativeName,
representativeTitle: body.representativeTitle,
responsibleName: body.responsibleName,
responsibleRole: body.responsibleRole,
responsibleIdentityNumber: body.responsibleIdentityNumber,
responsibleQualification: body.responsibleQualification,
responsiblePhone: body.responsiblePhone,
responsibleEmail: body.responsibleEmail,
currency: body.currency,
registrationNumber: body.registrationNumber,
plan: body.plan,
billingPeriod: body.billingPeriod,
preferredLanguage: body.preferredLanguage,
firstName: body.firstName,
lastName: body.lastName,
passwordHash,
now,
trialEndAt,
}))
const lang = coerceNotificationLocale(body.preferredLanguage)
const emailResult = await sendNotification({
type: 'ACCOUNT_CREATED',
companyId: result.company.id,
employeeId: result.employee.id,
email: body.email,
channels: ['EMAIL', 'IN_APP'],
locale: lang,
templateKey: 'account.created',
templateVariables: {
firstName: body.firstName,
companyName: body.companyName,
planName: localizePlanName(body.plan, lang),
billingPeriodLabel: localizeBillingPeriod(body.billingPeriod, lang),
currency: body.currency,
paymentProvider: body.paymentProvider,
trialEndDate: trialEndAt,
},
}).catch(() => [])
const emailDelivery = (emailResult as Array<{ channel?: string }>).find((entry) => entry.channel === 'EMAIL')
return presentCompanySignup({
company: result.company,
trialEndAt,
emailDelivery,
})
}
export function completeSignupDisabled() {
throw new AppError('Clerk-based signup has been removed. Use /auth/company/signup instead.', 410, 'disabled')
}
export function verifyEmailDisabled() {
throw new AppError('Email verification resend via Clerk has been removed from this project.', 410, 'disabled')
}