Actualizar server.js
This commit is contained in:
105
server.js
105
server.js
@@ -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
|
// 🌐 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)
|
// 2. BUSCAR OPERARIO AL AZAR (Si es NORMAL)
|
||||||
let assignedWorkerId = null;
|
let assignedWorkerId = null;
|
||||||
if (!is_urgent) {
|
if (!is_urgent) {
|
||||||
const qWorker = await clientDb.query(`
|
let qWorker = await clientDb.query(`
|
||||||
SELECT u.id FROM users u
|
SELECT u.id FROM users u
|
||||||
JOIN user_guilds ug ON u.id = ug.user_id
|
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
|
ORDER BY RANDOM() LIMIT 1
|
||||||
`, [targetOwnerId, guild_id]);
|
`, [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) {
|
if (qWorker.rowCount > 0) {
|
||||||
assignedWorkerId = qWorker.rows[0].id;
|
assignedWorkerId = qWorker.rows[0].id;
|
||||||
} else {
|
} 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
|
// 4. SI ES URGENTE, DISPARAMOS EL WHATSAPP DIRECTAMENTE
|
||||||
if (is_urgent) {
|
if (is_urgent) {
|
||||||
// Buscamos un operario activo de ese gremio
|
// 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
|
SELECT u.id, u.full_name, u.phone FROM users u
|
||||||
JOIN user_guilds ug ON u.id = ug.user_id
|
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
|
ORDER BY RANDOM() LIMIT 1
|
||||||
`, [targetOwnerId, guild_id]);
|
`, [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) {
|
if (workersQ.rowCount > 0) {
|
||||||
const worker = workersQ.rows[0];
|
const worker = workersQ.rows[0];
|
||||||
const token = crypto.randomBytes(16).toString('hex');
|
const token = crypto.randomBytes(16).toString('hex');
|
||||||
@@ -1080,16 +1018,15 @@ app.post("/public/new-request", async (req, res) => {
|
|||||||
// Enviamos el WA al operario
|
// Enviamos el WA al operario
|
||||||
const msg = `🚨 *URGENCIA PARTICULAR (WEB)*\n📍 Zona: Asignada\n🔗 https://portal.integrarepara.es/aceptar.html?t=${token}`;
|
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);
|
sendWhatsAppAuto(worker.phone, msg, `cliente_${targetOwnerId}`, false).catch(console.error);
|
||||||
|
|
||||||
// Dejamos huella
|
// Dejamos huella
|
||||||
await clientDb.query(
|
await clientDb.query(
|
||||||
"INSERT INTO scraped_service_logs (scraped_id, user_name, action, details) VALUES ($1, 'Sistema Automático', 'Bolsa de Urgencias', $2)",
|
"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 {
|
} 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]);
|
await clientDb.query("UPDATE scraped_services SET automation_status = 'failed' WHERE id = $1", [newServiceId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user