Initial SeConectai briefing system
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
"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<CadastroState> {
|
||||
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");
|
||||
}
|
||||
Reference in New Issue
Block a user