From c19ba7a411ee35b737d71c6fbbab0f87a032e5ec Mon Sep 17 00:00:00 2001 From: marsalva Date: Sat, 7 Mar 2026 15:28:36 +0000 Subject: [PATCH] Actualizar server.js --- server.js | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/server.js b/server.js index 159e4b6..cf71423 100644 --- a/server.js +++ b/server.js @@ -261,6 +261,19 @@ async function autoUpdateDB() { ); `); + -- 💬 CHAT Y NOTAS INTERNAS + CREATE TABLE IF NOT EXISTS service_communications ( + id SERIAL PRIMARY KEY, + scraped_id INT REFERENCES scraped_services(id) ON DELETE CASCADE, + owner_id INT REFERENCES users(id) ON DELETE CASCADE, + sender_id INT REFERENCES users(id) ON DELETE SET NULL, + sender_name TEXT NOT NULL, + sender_role TEXT, + message TEXT NOT NULL, + is_internal BOOLEAN DEFAULT FALSE, -- Si es TRUE, el operario NO lo ve + created_at TIMESTAMP DEFAULT NOW() + ); + // PARCHE DE ACTUALIZACIÓN await client.query(` DO $$ BEGIN @@ -2755,7 +2768,69 @@ app.get("/providers/credentials", authMiddleware, async (req, res) => { } }); +// ========================================== +// 💬 CHAT Y COMUNICACIÓN INTERNA (TIPO iTRAMIT) +// ========================================== +// 1. Obtener los mensajes de un expediente +app.get("/services/:id/chat", authMiddleware, async (req, res) => { + try { + const { id } = req.params; + const isOperario = req.user.role === 'operario'; + + // Si es operario, NO puede ver los mensajes marcados como "is_internal = true" + let query = ` + SELECT id, sender_id, sender_name, sender_role, message, is_internal, created_at + FROM service_communications + WHERE scraped_id = $1 AND owner_id = $2 + `; + + if (isOperario) { + query += ` AND is_internal = FALSE`; + } + + query += ` ORDER BY created_at ASC`; // Orden cronológico (chat) + + const q = await pool.query(query, [id, req.user.accountId]); + res.json({ ok: true, messages: q.rows }); + } catch (e) { + console.error("Error cargando chat:", e); + res.status(500).json({ ok: false }); + } +}); + +// 2. Enviar un nuevo mensaje (Oficina u Operario) +app.post("/services/:id/chat", authMiddleware, async (req, res) => { + try { + const { id } = req.params; + const { message, is_internal } = req.body; + + if (!message || message.trim() === "") { + return res.status(400).json({ ok: false, error: "El mensaje está vacío" }); + } + + // Bloqueo de seguridad: Un operario NUNCA puede crear una nota interna oculta + const isOperario = req.user.role === 'operario'; + const finalIsInternal = isOperario ? false : (is_internal || false); + + // Sacar el nombre y rol del que escribe + const userQ = await pool.query("SELECT full_name, role FROM users WHERE id=$1", [req.user.sub]); + const senderName = userQ.rows[0]?.full_name || "Usuario Desconocido"; + const senderRole = userQ.rows[0]?.role || "operario"; + + // Guardar el mensaje en la base de datos + await pool.query(` + INSERT INTO service_communications + (scraped_id, owner_id, sender_id, sender_name, sender_role, message, is_internal) + VALUES ($1, $2, $3, $4, $5, $6, $7) + `, [id, req.user.accountId, req.user.sub, senderName, senderRole, message.trim(), finalIsInternal]); + + res.json({ ok: true }); + } catch (e) { + console.error("Error enviando mensaje:", e); + res.status(500).json({ ok: false }); + } +}); // ========================================== // 🕒 EL RELOJ DEL SISTEMA (Ejecutar cada minuto)