Actualizar server.js

This commit is contained in:
2026-03-15 20:05:19 +00:00
parent bf5f4951f2
commit bdb4ba8c3b

View File

@@ -906,6 +906,79 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
}
}
// ==========================================
// 📱 OTP PARA PORTAL DEL CLIENTE (ACCESO WEB)
// ==========================================
app.post("/public/auth/request-otp", async (req, res) => {
try {
const { phone, owner_id } = req.body;
if (!phone || !owner_id) return res.status(400).json({ ok: false, error: "Faltan datos" });
const p = normalizePhone(phone);
// Generamos un código aleatorio de 4 dígitos
const code = String(Math.floor(1000 + Math.random() * 9000));
const codeHash = await bcrypt.hash(code, 10);
// Lo guardamos en la tabla de login_codes (ponemos user_id NULL porque es un cliente, no un operario)
await pool.query(`
INSERT INTO login_codes (user_id, phone, code_hash, purpose, expires_at)
VALUES (NULL, $1, $2, 'client_portal', NOW() + INTERVAL '15 minutes')
`, [p, codeHash]);
const msg = `👋 *Asistencia Técnica*\n\nTu código seguro de acceso al portal es: *${code}*\n\nCaduca en 15 minutos.`;
// Enviamos el WhatsApp usando la instancia de la empresa correspondiente
sendWhatsAppAuto(p, msg, `cliente_${owner_id}`, false).catch(console.error);
res.json({ ok: true });
} catch (e) {
console.error("Error solicitando OTP cliente:", e);
res.status(500).json({ ok: false });
}
});
app.post("/public/auth/verify-otp", async (req, res) => {
try {
const { phone, code, owner_id } = req.body;
const p = normalizePhone(phone);
// 1. Buscamos si el código existe, es de cliente, y no está caducado
const q = await pool.query(`
SELECT id, code_hash FROM login_codes
WHERE phone = $1 AND purpose = 'client_portal' AND consumed_at IS NULL AND expires_at > NOW()
ORDER BY created_at DESC LIMIT 1
`, [p]);
if (q.rowCount === 0) return res.status(400).json({ ok: false, error: "Código inválido o caducado" });
// 2. Comprobamos que el código de 4 dígitos coincide
const valid = await bcrypt.compare(String(code), q.rows[0].code_hash);
if (!valid) return res.status(400).json({ ok: false, error: "Código incorrecto" });
// 3. Quemamos el código para que no se pueda usar 2 veces
await pool.query("UPDATE login_codes SET consumed_at = NOW() WHERE id = $1", [q.rows[0].id]);
// 4. Magia: Buscamos si este cliente ya nos ha llamado antes para pre-rellenar su formulario
const cleanPhoneForSearch = String(phone).replace(/\D/g, "");
const clientQ = await pool.query(
"SELECT full_name, addresses FROM clients WHERE phone LIKE $1 AND owner_id = $2 LIMIT 1",
[`%${cleanPhoneForSearch}%`, owner_id]
);
let clientData = null;
let exists = false;
if (clientQ.rowCount > 0) {
clientData = clientQ.rows[0];
exists = true;
}
res.json({ ok: true, exists, client: clientData });
} catch (e) {
console.error("Error verificando OTP cliente:", e);
res.status(500).json({ ok: false });
}
});
// ==========================================
// 🌐 EMBUDO PÚBLICO DE ENTRADA DE CLIENTES
// ==========================================