Bypass de pruebas: solo se llaman números en RETELL_ALLOWED_NUMBERS
Guarda en iniciarLlamadaSaliente: si RETELL_ALLOWED_NUMBERS tiene valor (CSV de E.164), solo se lanza la llamada a esos números; el resto se omite (devuelve null, funnel en simulado). Vacío = se llama a todos. Protege tanto el form (procesarLead) como el canal llamada (pedirLlamada). En prod = +34651194617. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,9 @@ DATABASE_URL="postgresql://postgres:reformix@localhost:5432/reformix"
|
||||
RETELL_API_KEY=""
|
||||
RETELL_AGENT_ID=""
|
||||
RETELL_FROM_NUMBER="" # número de origen en E.164, p. ej. +34910000000
|
||||
# Allowlist de pruebas: si tiene valor (CSV de E.164), SOLO se llama a esos números; el resto se
|
||||
# omite (la llamada no se lanza). Vaciar para volver a llamar a todos. Ej: "+34651194617"
|
||||
RETELL_ALLOWED_NUMBERS=""
|
||||
|
||||
# EP de ingesta del lead (/api/leads/:id/ingesta). Clave compartida que valida al llamante
|
||||
# externo (Authorization: Bearer ...). Sin ella, el EP responde 401.
|
||||
|
||||
@@ -12,6 +12,8 @@ const schema = z.object({
|
||||
RETELL_API_KEY: opcional,
|
||||
RETELL_AGENT_ID: opcional,
|
||||
RETELL_FROM_NUMBER: opcional,
|
||||
// Allowlist de pruebas: si tiene valor (CSV de números), SOLO se llaman esos; vacío = todos.
|
||||
RETELL_ALLOWED_NUMBERS: opcional,
|
||||
// EP de ingesta del lead: clave compartida que valida al llamante externo.
|
||||
FUNNEL_API_KEY: opcional,
|
||||
// SMTP para enviar el presupuesto y el enlace al formulario.
|
||||
@@ -32,6 +34,7 @@ export const env = schema.parse({
|
||||
RETELL_API_KEY: process.env.RETELL_API_KEY,
|
||||
RETELL_AGENT_ID: process.env.RETELL_AGENT_ID,
|
||||
RETELL_FROM_NUMBER: process.env.RETELL_FROM_NUMBER,
|
||||
RETELL_ALLOWED_NUMBERS: process.env.RETELL_ALLOWED_NUMBERS,
|
||||
FUNNEL_API_KEY: process.env.FUNNEL_API_KEY,
|
||||
SMTP_HOST: process.env.SMTP_HOST,
|
||||
SMTP_PORT: process.env.SMTP_PORT,
|
||||
|
||||
@@ -50,6 +50,18 @@ export async function iniciarLlamadaSaliente(opts: {
|
||||
const toNumber = normalizarTelefonoEs(opts.telefono);
|
||||
if (!toNumber) return null;
|
||||
|
||||
// Bypass de seguridad (fase de pruebas): si RETELL_ALLOWED_NUMBERS tiene valor, SOLO se llama a
|
||||
// esos números; cualquier otro se omite (devuelve null, el funnel sigue en simulado). Vaciar la
|
||||
// variable para volver a llamar a todos.
|
||||
const allow = (env.RETELL_ALLOWED_NUMBERS ?? '')
|
||||
.split(',')
|
||||
.map((s) => normalizarTelefonoEs(s.trim()))
|
||||
.filter((n): n is string => Boolean(n));
|
||||
if (allow.length > 0 && !allow.includes(toNumber)) {
|
||||
console.warn(`Retell: ${toNumber} no está en RETELL_ALLOWED_NUMBERS; llamada omitida (bypass).`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const body: Record<string, unknown> = {
|
||||
from_number: env.RETELL_FROM_NUMBER,
|
||||
to_number: toNumber,
|
||||
|
||||
Reference in New Issue
Block a user