From 968bb4412f78e54f101f6c7fee0067419734afaf Mon Sep 17 00:00:00 2001 From: marsalva Date: Sat, 7 Feb 2026 22:21:43 +0000 Subject: [PATCH] Actualizar server.js --- server.js | 74 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/server.js b/server.js index 4597887..b709425 100644 --- a/server.js +++ b/server.js @@ -4,13 +4,15 @@ import bcrypt from "bcryptjs"; import jwt from "jsonwebtoken"; import pg from "pg"; +// Nota: En Node.js v18/v20 'fetch' ya es nativo, no hace falta importarlo. + const { Pool } = pg; const app = express(); // ========================= // MIDDLEWARES // ========================= -app.use(cors()); // Habilita conexiones desde tu web +app.use(cors()); // Importante para que la web no de error de bloqueo app.use(express.json()); // ========================= @@ -30,26 +32,27 @@ if (!DATABASE_URL || !JWT_SECRET) { } // ========================= -// CONEXI脫N BASE DE DATOS (CORREGIDO) +// CONEXI脫N BASE DE DATOS // ========================= const pool = new Pool({ connectionString: DATABASE_URL, - ssl: false // <--- ESTO ARREGLA EL ERROR DE REINICIO + ssl: false // <--- IMPORTANTE: 'false' para que funcione en Coolify interno }); // ========================= -// RUTA DE "ESTOY VIVO" +// RUTA DE "ESTOY VIVO" (Health Check) // ========================= app.get("/", (req, res) => { res.status(200).send("馃殌 IntegraRepara API v1.0 - Online"); }); // ========================= -// FUNCIONES DE AYUDA +// FUNCIONES DE AYUDA (Helpers) // ========================= function normalizePhone(phone) { let p = String(phone || "").trim().replace(/\s+/g, "").replace(/-/g, ""); if (!p) return ""; + // L贸gica simple para Espa帽a if (!p.startsWith("+") && /^[6789]\d{8}$/.test(p)) { return "+34" + p; } @@ -68,7 +71,7 @@ function signToken(user) { ); } -// Middleware para proteger rutas +// Middleware de seguridad para rutas protegidas function authMiddleware(req, res, next) { const h = req.headers.authorization || ""; const token = h.startsWith("Bearer ") ? h.slice(7) : ""; @@ -99,7 +102,6 @@ async function sendWhatsAppCode(phone, code) { text: `馃攼 Tu c贸digo IntegraRepara es: *${code}*\n\nNo lo compartas.` }; - // Usamos fetch nativo de Node.js const res = await fetch(url, { method: "POST", headers: { @@ -122,7 +124,7 @@ async function sendWhatsAppCode(phone, code) { } // ========================= -// RUTAS +// RUTAS DE LA API // ========================= // 1. REGISTRO @@ -176,6 +178,7 @@ app.post("/auth/register", async (req, res) => { [userId, p, codeHash, expiresAt] ); + // Enviar WhatsApp (sin esperar bloqueante para no fallar la DB si tarda) await sendWhatsAppCode(p, code); await client.query('COMMIT'); @@ -190,7 +193,7 @@ app.post("/auth/register", async (req, res) => { } }); -// 2. VERIFICAR C脫DIGO +// 2. VERIFICAR C脫DIGO (Aqu铆 estaba tu error de sintaxis, ya est谩 corregido) app.post("/auth/verify", async (req, res) => { try { const { phone, code } = req.body; @@ -207,7 +210,56 @@ app.post("/auth/verify", async (req, res) => { if (q.rowCount === 0) return res.status(400).json({ ok: false, error: "C贸digo inv谩lido o expirado" }); - const valid = await bcrypt.compare(String(code), q.rows[0].code_hash); + const row = q.rows[0]; // Definimos 'row' aqu铆 para usarlo abajo + const valid = await bcrypt.compare(String(code), row.code_hash); + if (!valid) return res.status(400).json({ ok: false, error: "C贸digo incorrecto" }); - await pool.query("UPDATE login_codes SET consumed_at=NOW() WHERE id=$1 \ No newline at end of file + // --- CORRECCI脫N DE SINTAXIS AQUI --- + await pool.query("UPDATE login_codes SET consumed_at=NOW() WHERE id=$1", [row.id]); + await pool.query("UPDATE users SET is_verified=TRUE WHERE id=$1", [row.uid]); + // ----------------------------------- + + const token = signToken({ id: row.uid, email: row.email, phone: p }); + res.json({ ok: true, token }); + + } catch (e) { + console.error("Error Verify:", e); + res.status(500).json({ ok: false, error: "Error verificando c贸digo" }); + } +}); + +// 3. LOGIN (Email + Pass) +app.post("/auth/login", async (req, res) => { + try { + const { email, password } = req.body; + const q = await pool.query("SELECT * FROM users WHERE email=$1", [email]); + + if (q.rowCount === 0) return res.status(401).json({ ok: false, error: "Credenciales inv谩lidas" }); + + const u = q.rows[0]; + const match = await bcrypt.compare(password, u.password_hash); + + if (!match) return res.status(401).json({ ok: false, error: "Credenciales inv谩lidas" }); + if (!u.is_verified) return res.status(403).json({ ok: false, error: "Cuenta no verificada" }); + + res.json({ ok: true, token: signToken(u) }); + } catch(e) { + console.error("Error Login:", e); + res.status(500).json({ ok: false, error: "Error en login" }); + } +}); + +// 4. SERVICIOS (Protegido) +app.get("/services", authMiddleware, async (req, res) => { + try { + const q = await pool.query("SELECT * FROM services WHERE user_id=$1 ORDER BY created_at DESC", [req.user.sub]); + res.json({ ok: true, services: q.rows }); + } catch (e) { + res.status(500).json({ ok: false, error: "Error al obtener servicios" }); + } +}); + +// ARRANCAR SERVIDOR +const port = process.env.PORT || 3000; +app.listen(port, "0.0.0.0", () => console.log(`馃殌 Server OK en puerto ${port}`)); \ No newline at end of file