add email verification, add cf validation for debounce

This commit is contained in:
Esteban Paz 2026-08-08 22:05:38 -05:00
parent d9e366b521
commit dec0da3455
4 changed files with 34 additions and 1 deletions

1
.gitignore vendored
View File

@ -25,6 +25,7 @@ pnpm-debug.log*
/generated/prisma /generated/prisma
prisma/*.db prisma/*.db
prisma/dev.db prisma/dev.db
data/
# opencode - agentes y scripts locales (no subir a producción) # opencode - agentes y scripts locales (no subir a producción)
opencode/ opencode/

View File

@ -1,6 +1,7 @@
import type { APIRoute } from "astro"; import type { APIRoute } from "astro";
import { appendRegistrationToSheet } from "../lib/googleSheets"; import { appendRegistrationToSheet } from "../lib/googleSheets";
import { sendEmailCf } from "../lib/email"; import { sendEmailCf } from "../lib/email";
import { emailExistsInRegistry, addEmailToRegistry } from "../lib/emailsRegistry";
export const prerender = false; export const prerender = false;
const COLUMNS = [ const COLUMNS = [
@ -101,6 +102,13 @@ export const POST: APIRoute = async ({ request }) => {
timeZone: "America/Puerto_Rico", timeZone: "America/Puerto_Rico",
}); });
if (emailExistsInRegistry(formData.correo)) {
return Response.json(
{ success: false, message: "Este correo ya está registrado. No se permiten 2 registros." },
{ status: 409 }
);
}
const row = COLUMNS.map((key) => flattenValue(formData[key])); const row = COLUMNS.map((key) => flattenValue(formData[key]));
row.push(timestamp); row.push(timestamp);
@ -111,6 +119,8 @@ export const POST: APIRoute = async ({ request }) => {
apellidos: formData.apellido, apellidos: formData.apellido,
}, formData.correo, "CENTRO_DEL_REINO_PAZ_Y_JUSTICIA_POSTULACION"); }, formData.correo, "CENTRO_DEL_REINO_PAZ_Y_JUSTICIA_POSTULACION");
addEmailToRegistry(formData.correo);
return Response.json(result); return Response.json(result);
} catch (error) { } catch (error) {
console.error("Error en /api/formulario/send:", error); console.error("Error en /api/formulario/send:", error);

View File

@ -96,7 +96,7 @@ export async function sendEmailCf(data: Record<string, string>, to: string, type
text, text,
}); });
if (response.permanent_bounces.includes(to)) { if (!response.delivered.includes(to)) {
console.error("Cloudflare email not delivered", response); console.error("Cloudflare email not delivered", response);
throw new Error("El correo no fue entregado"); throw new Error("El correo no fue entregado");
} }

View File

@ -0,0 +1,22 @@
import fs from "node:fs";
import path from "node:path";
const REGISTRY_PATH = path.resolve(process.cwd(), "data", "emails.txt");
export function emailExistsInRegistry(email: string): boolean {
try {
if (!fs.existsSync(REGISTRY_PATH)) return false;
const content = fs.readFileSync(REGISTRY_PATH, "utf-8");
return content.split("\n").some((line) => line.trim().toLowerCase() === email.trim().toLowerCase());
} catch {
return false;
}
}
export function addEmailToRegistry(email: string): void {
const dir = path.dirname(REGISTRY_PATH);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.appendFileSync(REGISTRY_PATH, email.trim().toLowerCase() + "\n");
}