diff --git a/server.js b/server.js index aeb6393..445fde5 100644 --- a/server.js +++ b/server.js @@ -382,28 +382,38 @@ async function requirePlan(req, res, next, feature) { // ========================================== app.get("/public/portal/:token", async (req, res) => { + console.log("------------------------------------------"); + console.log("👉 [PORTAL] 1. Entrando a ruta portal con token:", req.params.token, "y service:", req.query.service); + try { const { token } = req.params; - const serviceId = req.query.service; // Recogemos el ID del enlace + const serviceId = req.query.service; - // 1. Buscamos al cliente por su token + console.log("👉 [PORTAL] 2. Buscando cliente en BD..."); const qClient = await pool.query("SELECT * FROM clients WHERE portal_token = $1 LIMIT 1", [token]); - if (qClient.rowCount === 0) return res.status(404).json({ ok: false, error: "Enlace no válido" }); - + + if (qClient.rowCount === 0) { + console.log("❌ [PORTAL] Cliente no encontrado"); + return res.status(404).json({ ok: false, error: "Enlace no válido" }); + } + const client = qClient.rows[0]; const ownerId = client.owner_id; + console.log(`✅ [PORTAL] Cliente encontrado: ${client.full_name} (Dueño: ${ownerId})`); - // 2. Buscamos los datos de la empresa + console.log("👉 [PORTAL] 3. Buscando datos de la empresa..."); const qConfig = await pool.query("SELECT full_name, company_logo FROM users WHERE id = $1", [ownerId]); const company = { name: qConfig.rows[0]?.full_name || "IntegraRepara", logo: qConfig.rows[0]?.company_logo || null }; + console.log(`✅ [PORTAL] Empresa: ${company.name}`); let qServices; - - // 3. LA SOLUCIÓN DEFINITIVA: Si el enlace trae ID, buscamos ESE expediente exacto y nada más + console.log("👉 [PORTAL] 4. Lanzando consulta SQL de servicios..."); + if (serviceId && !isNaN(parseInt(serviceId))) { + console.log("👉 [PORTAL] Tipo de búsqueda: POR ID EXACTO (" + serviceId + ")"); qServices = await pool.query(` SELECT s.id, s.service_ref, s.is_urgent, s.raw_data, s.created_at, st.name as real_status_name, st.is_final as is_status_final, @@ -413,9 +423,8 @@ app.get("/public/portal/:token", async (req, res) => { LEFT JOIN service_statuses st ON st.id::text = (s.raw_data->>'status_operativo')::text WHERE s.id = $1 AND s.owner_id = $2 AND s.provider != 'SYSTEM_BLOCK' `, [parseInt(serviceId), ownerId]); - } - // 4. Si alguien abre el portal sin ID, buscamos por teléfono usando un filtro general seguro - else { + } else { + console.log("👉 [PORTAL] Tipo de búsqueda: POR TELÉFONO"); let phoneMatch = String(client.phone || "").replace(/[^0-9]/g, ""); if (phoneMatch.length > 9) phoneMatch = phoneMatch.slice(-9); if (phoneMatch.length < 6) phoneMatch = "TELEFONO_FALSO_123"; @@ -432,8 +441,9 @@ app.get("/public/portal/:token", async (req, res) => { ORDER BY s.created_at DESC `, [ownerId, `%${phoneMatch}%`]); } + + console.log(`✅ [PORTAL] Consulta OK. Encontrados: ${qServices.rowCount} servicios.`); - // 5. Formatear datos para el panel del cliente const formattedServices = qServices.rows.map(s => { return { id: s.id, @@ -449,11 +459,19 @@ app.get("/public/portal/:token", async (req, res) => { }; }); + console.log("✅ [PORTAL] 5. Todo procesado. Enviando al navegador."); res.json({ ok: true, client: { name: client.full_name }, company, services: formattedServices }); } catch (e) { - console.error("🔥 ERROR CRÍTICO EN PORTAL:", e.message); - res.status(500).json({ ok: false, error: e.message || "Error del servidor" }); + console.error("🔥 [PORTAL] ERROR CRÍTICO CAPTURADO:"); + console.error(e); // Esto imprimirá el error real en Coolify + + // Magia: Le enviamos el error al navegador para que tú lo puedas leer + res.status(500).json({ + ok: false, + error_mensaje_real: e.message, + error_stack: e.stack + }); } });