Files
reformix-hackaton/mvp/b2c/src/lib/api/bot-request.ts
Carlos Narro 8b96037dad Añade EPs HTTP para que el bot de WhatsApp pueble la BD
El bot (Luisa) es externo y no toca Postgres directamente. Cuatro endpoints
autenticados con Bearer FUNNEL_API_KEY, validados con zod:

- POST /api/leads/:id/conversacion  → turno de chat (+ estado_wa/bot_step)
- POST /api/leads/:id/perfil         → update parcial del lead (extracción)
- POST /api/leads/:id/calificacion   → upsert de lead_calificacion
- POST /api/leads/:id/intento        → registro en intentos_contacto

Helpers compartidos lib/api/funnel-auth.ts (autorizado + jsonResponse) y
lib/api/bot-request.ts (validarBotRequest: auth + JSON + zod + lead existe).
La ruta de ingesta se refactoriza para reutilizar funnel-auth (DRY).
Schemas puros en lib/funnel/bot-schemas.ts con tests, y doc en api-docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 20:04:11 +02:00

35 lines
1.3 KiB
TypeScript

import type { z } from 'zod';
import { eq } from 'drizzle-orm';
import { db } from '@/db';
import { leads } from '@/db/schema';
import { autorizado, jsonResponse } from '@/lib/api/funnel-auth';
// Valida una petición de los EPs del bot: auth Bearer + JSON + zod + que el lead exista.
// Devuelve { error } con la Response lista, o { leadId, body } ya validado.
export async function validarBotRequest<S extends z.ZodTypeAny>(
req: Request,
params: Promise<{ id: string }>,
schema: S,
): Promise<{ error: Response } | { leadId: string; body: z.infer<S> }> {
if (!autorizado(req)) return { error: jsonResponse({ ok: false, error: 'No autorizado.' }, 401) };
const { id } = await params;
let raw: unknown;
try {
raw = await req.json();
} catch {
return { error: jsonResponse({ ok: false, error: 'JSON inválido.' }, 422) };
}
const parsed = schema.safeParse(raw);
if (!parsed.success) {
return {
error: jsonResponse({ ok: false, error: parsed.error.issues[0]?.message ?? 'Datos inválidos.' }, 422),
};
}
const [lead] = await db.select({ id: leads.id }).from(leads).where(eq(leads.id, id)).limit(1);
if (!lead) return { error: jsonResponse({ ok: false, error: 'Lead no encontrado.' }, 404) };
return { leadId: id, body: parsed.data };
}