"use server"; import argon2 from "argon2"; import { redirect } from "next/navigation"; import { db } from "@/lib/db"; import { encryptCpf, hashCpf, onlyDigits } from "@/lib/crypto"; import { cadastroSchema, normalizeWhatsapp } from "@/lib/validators"; import { sendMail } from "@/lib/mail"; import { checkRateLimit } from "@/lib/rate-limit"; export type CadastroState = { ok: boolean; message?: string; }; export async function cadastrarCliente(_state: CadastroState, formData: FormData): Promise { const emailForLimit = formData.get("email")?.toString().trim().toLowerCase() ?? "sem-email"; const limit = checkRateLimit(`cadastro:${emailForLimit}`, 5, 60 * 60 * 1000); if (!limit.allowed) { return { ok: false, message: "Muitas tentativas. Tente novamente mais tarde." }; } const parsed = cadastroSchema.safeParse({ nomeCompleto: formData.get("nomeCompleto"), cpf: formData.get("cpf"), whatsapp: formData.get("whatsapp"), email: formData.get("email"), senha: formData.get("senha"), confirmarSenha: formData.get("confirmarSenha"), tipo: formData.get("tipo"), razaoSocial: formData.get("razaoSocial") || undefined, cnpj: formData.get("cnpj") || undefined, cep: formData.get("cep") || undefined, logradouro: formData.get("logradouro") || undefined, numero: formData.get("numero") || undefined, complemento: formData.get("complemento") || undefined, bairro: formData.get("bairro") || undefined, cidade: formData.get("cidade") || undefined, uf: formData.get("uf") || undefined, lgpdAceite: formData.get("lgpdAceite") }); if (!parsed.success) { return { ok: false, message: parsed.error.issues[0]?.message ?? "Revise os dados informados." }; } const data = parsed.data; const cpfHash = hashCpf(data.cpf); const whatsapp = normalizeWhatsapp(data.whatsapp); if (!whatsapp) { return { ok: false, message: "Informe um WhatsApp com DDD." }; } try { await db.$transaction(async (tx) => { const exists = await tx.user.findFirst({ where: { OR: [{ email: data.email }, { cliente: { cpfHash } }] }, select: { id: true } }); if (exists) { throw new Error("DUPLICADO"); } await tx.user.create({ data: { email: data.email, senhaHash: await argon2.hash(data.senha), papel: "CLIENTE", cliente: { create: { nomeCompleto: data.nomeCompleto, cpfCifrado: encryptCpf(data.cpf), cpfHash, whatsapp, tipo: data.tipo, razaoSocial: data.tipo === "PJ" ? data.razaoSocial : null, cnpj: data.tipo === "PJ" && data.cnpj ? onlyDigits(data.cnpj) : null, cep: data.cep ? onlyDigits(data.cep) : null, logradouro: data.logradouro || null, numero: data.numero || null, complemento: data.complemento || null, bairro: data.bairro || null, cidade: data.cidade || null, uf: data.uf || null, lgpdAceiteEm: new Date() } } } }); }); } catch (error) { if (error instanceof Error && error.message === "DUPLICADO") { return { ok: false, message: "Nao foi possivel concluir o cadastro com os dados informados." }; } return { ok: false, message: "Nao foi possivel concluir o cadastro agora. Tente novamente." }; } try { await sendMail({ to: data.email, subject: "Cadastro criado - Briefing SeConectai", text: [ "Seu cadastro foi criado com sucesso.", "", "Acesse o sistema para iniciar ou acompanhar seu briefing.", "", "Este e-mail nao inclui CPF completo nem senhas." ].join("\n") }); } catch { // Falha de e-mail nao desfaz cadastro. } redirect("/login"); }