Actualizar server.js

This commit is contained in:
2026-03-14 20:19:35 +00:00
parent 514ffe8611
commit 2108cb644c

105
server.js
View File

@@ -905,82 +905,6 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
}
}
// ==========================================
// 🔐 OTP PARA CLIENTES (VERIFICACIÓN WHATSAPP)
// ==========================================
// 1. Solicitar el código de 4 cifras
app.post("/public/auth/request-otp", async (req, res) => {
try {
const { phone, owner_id } = req.body;
if (!phone) return res.status(400).json({ ok: false });
// Asumimos el ID de la empresa (Para el cliente público será 1)
const targetOwnerId = owner_id || 1;
const cleanPhone = phone.replace(/\D/g, "");
// Generar código de 4 cifras (ej: 4829)
const code = String(Math.floor(1000 + Math.random() * 9000));
const hash = await bcrypt.hash(code, 10);
// Guardamos el código en la base de datos
await pool.query(`
INSERT INTO login_codes (phone, code_hash, purpose, expires_at)
VALUES ($1, $2, 'client_portal', NOW() + INTERVAL '15 minutes')
`, [cleanPhone, hash]);
const msg = `🔐 Tu código de acceso para solicitar asistencia es: *${code}*\n\nSi no has solicitado este código, ignora este mensaje.`;
// 👇 SOLUCIÓN: Usamos EXACTAMENTE el mismo nombre que crea tu panel web
const instanceName = `cliente_${targetOwnerId}`;
console.log(`[OTP] Enviando WhatsApp a ${cleanPhone} por la instancia: ${instanceName}`);
sendWhatsAppAuto("34" + cleanPhone, msg, instanceName, false).catch(console.error);
res.json({ ok: true });
} catch(e) {
console.error("Error OTP Request:", e);
res.status(500).json({ ok: false });
}
});
// 2. Verificar el código y comprobar si el cliente existe
app.post("/public/auth/verify-otp", async (req, res) => {
try {
const { phone, code, owner_id } = req.body;
const cleanPhone = phone.replace(/\D/g, "");
const targetOwnerId = owner_id || 1;
// Buscamos el último código válido
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
`, [cleanPhone]);
if (q.rowCount === 0) return res.status(400).json({ ok: false, error: "Código inválido o caducado" });
// Comparamos el código que ha puesto el cliente con el de la BBDD
const valid = await bcrypt.compare(code, q.rows[0].code_hash);
if (!valid) return res.status(400).json({ ok: false, error: "Código incorrecto" });
// Lo marcamos como usado
await pool.query("UPDATE login_codes SET consumed_at = NOW() WHERE id = $1", [q.rows[0].id]);
// VERIFICAMOS SI EL CLIENTE YA EXISTE EN NUESTRA BASE DE DATOS
const qClient = await pool.query("SELECT id, full_name, addresses, portal_token FROM clients WHERE phone LIKE $1 AND owner_id = $2 LIMIT 1", [`%${cleanPhone}%`, targetOwnerId]);
if (qClient.rowCount > 0) {
res.json({ ok: true, exists: true, client: qClient.rows[0] });
} else {
res.json({ ok: true, exists: false });
}
} catch(e) {
console.error("Error OTP Verify:", e);
res.status(500).json({ ok: false });
}
});
// ==========================================
// 🌐 EMBUDO PÚBLICO DE ENTRADA DE CLIENTES
// ==========================================
@@ -1016,17 +940,22 @@ app.post("/public/new-request", async (req, res) => {
// 2. BUSCAR OPERARIO AL AZAR (Si es NORMAL)
let assignedWorkerId = null;
if (!is_urgent) {
const qWorker = await clientDb.query(`
let qWorker = await clientDb.query(`
SELECT u.id FROM users u
JOIN user_guilds ug ON u.id = ug.user_id
WHERE u.owner_id = $1 AND u.role = 'operario' AND u.status = 'active' AND ug.guild_id = $2
WHERE u.owner_id = $1 AND u.status = 'active' AND ug.guild_id = $2
ORDER BY RANDOM() LIMIT 1
`, [targetOwnerId, guild_id]);
// 🛡️ FALLBACK: Si nadie tiene ese gremio, coge a CUALQUIERA activo
if (qWorker.rowCount === 0) {
qWorker = await clientDb.query("SELECT id FROM users WHERE owner_id = $1 AND status = 'active' ORDER BY RANDOM() LIMIT 1", [targetOwnerId]);
}
if (qWorker.rowCount > 0) {
assignedWorkerId = qWorker.rows[0].id;
} else {
return res.status(400).json({ ok: false, error: "No hay técnicos disponibles para cita normal en este gremio. Llama a la oficina." });
return res.status(400).json({ ok: false, error: "No hay técnicos disponibles. Llama a la oficina." });
}
}
@@ -1060,13 +989,22 @@ app.post("/public/new-request", async (req, res) => {
// 4. SI ES URGENTE, DISPARAMOS EL WHATSAPP DIRECTAMENTE
if (is_urgent) {
// Buscamos un operario activo de ese gremio
const workersQ = await clientDb.query(`
let workersQ = await clientDb.query(`
SELECT u.id, u.full_name, u.phone FROM users u
JOIN user_guilds ug ON u.id = ug.user_id
WHERE u.owner_id = $1 AND u.role = 'operario' AND u.status = 'active' AND ug.guild_id = $2
WHERE u.owner_id = $1 AND u.status = 'active' AND ug.guild_id = $2
ORDER BY RANDOM() LIMIT 1
`, [targetOwnerId, guild_id]);
// 🛡️ FALLBACK URGENCIA: Si nadie tiene ese gremio (o es admin), coge a CUALQUIERA activo. ¡Es urgente!
if (workersQ.rowCount === 0) {
workersQ = await clientDb.query(`
SELECT id, full_name, phone FROM users
WHERE owner_id = $1 AND status = 'active'
ORDER BY RANDOM() LIMIT 1
`, [targetOwnerId]);
}
if (workersQ.rowCount > 0) {
const worker = workersQ.rows[0];
const token = crypto.randomBytes(16).toString('hex');
@@ -1080,16 +1018,15 @@ app.post("/public/new-request", async (req, res) => {
// Enviamos el WA al operario
const msg = `🚨 *URGENCIA PARTICULAR (WEB)*\n📍 Zona: Asignada\n🔗 https://portal.integrarepara.es/aceptar.html?t=${token}`;
// NOTA: Usamos `cliente_${targetOwnerId}` para que use la sesión de la empresa correcta
sendWhatsAppAuto(worker.phone, msg, `cliente_${targetOwnerId}`, false).catch(console.error);
// Dejamos huella
await clientDb.query(
"INSERT INTO scraped_service_logs (scraped_id, user_name, action, details) VALUES ($1, 'Sistema Automático', 'Bolsa de Urgencias', $2)",
[newServiceId, `Notificación enviada a: ${worker.full_name}`]
[newServiceId, `Notificación enviada a: ${worker.full_name} (${worker.phone})`]
);
} else {
// Si no hay operarios en ese gremio, lo marcamos como fallido en la bolsa
// Solo fallará si literalmente tienes 0 usuarios activos en el sistema
await clientDb.query("UPDATE scraped_services SET automation_status = 'failed' WHERE id = $1", [newServiceId]);
}