diff --git a/server.js b/server.js index 9899599..018369f 100644 --- a/server.js +++ b/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 // ========================================== @@ -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]); }