import { prisma } from './prisma'; import { encryptSecrets, decryptSecrets } from './organization'; import { AuthService } from './auth'; import { auditService } from './audit'; import { scheduleEmail } from './queue'; import { z } from 'zod'; import { OrganizationSchema } from '@repo/shared-types'; export const OrganizationCreationSchema = OrganizationSchema.extend({ slug: z.string().min(3).regex(/^[a-z0-9-]+$/), adminEmail: z.string().email(), adminName: z.string().min(2), password: z.string().min(6).optional(), mode: z.enum(['CRM_MARKETING', 'PEDAGOGY', 'CUSTOMER_SERVICE']).default('PEDAGOGY'), useCase: z.enum(['EDUCATION', 'CRM_WHATSAPP']).default('EDUCATION'), isCrmActive: z.boolean().optional(), isEdTechActive: z.boolean().optional() }); export type OrganizationCreationInput = z.infer; export interface CreateOrganizationResult { organization: ReturnType; admin: { id: string; email: string; tempPassword: string }; } export async function createOrganizationWithAdmin( input: OrganizationCreationInput, actorId?: string ): Promise { const { adminEmail, adminName, password, slug, mode, useCase, isCrmActive, isEdTechActive, ...orgData } = input; const existing = await prisma.organization.findUnique({ where: { slug } }); if (existing) throw Object.assign(new Error('Slug already taken'), { code: 'SLUG_TAKEN' }); const data = encryptSecrets(orgData); const finalIsCrmActive = isCrmActive ?? (useCase === 'CRM_WHATSAPP'); const finalIsEdTechActive = isEdTechActive ?? (useCase === 'EDUCATION'); const { org, user, tempPassword } = await prisma.$transaction(async (tx) => { const org = await tx.organization.create({ data: { ...data, slug, mode, useCase, isCrmActive: finalIsCrmActive, isEdTechActive: finalIsEdTechActive } }); const tempPassword = password || Math.random().toString(36).slice(-10); const passwordHash = await AuthService.hashPassword(tempPassword); const user = await tx.user.create({ data: { email: adminEmail, name: adminName, passwordHash, role: 'ORG_ADMIN', organizationId: org.id } }); return { org, user, tempPassword }; }); await scheduleEmail({ to: adminEmail, subject: `Bienvenue chez Xamlé Studio - ${org.name}`, params: { name: adminName, organizationName: org.name, loginUrl: `https://${slug}.xamle.studio/login`, resetUrl: `https://${slug}.xamle.studio/reset-password` }, templateId: 1 }); await auditService.log({ action: 'ORGANIZATION_CREATED', actorId, resourceId: org.id, details: { name: org.name, slug: org.slug } }); return { organization: decryptSecrets(org), admin: { id: user.id, email: user.email ?? adminEmail, tempPassword } }; }