diff --git a/configuracion.html b/configuracion.html index 3157f5e..967939f 100644 --- a/configuracion.html +++ b/configuracion.html @@ -1,355 +1,259 @@ -import express from "express"; -import cors from "cors"; -import bcrypt from "bcryptjs"; -import jwt from "jsonwebtoken"; -import pg from "pg"; + + + + + + Configuración - IntegraRepara + + + + + -const { Pool } = pg; -const app = express(); + -app.use(cors()); -app.use(express.json()); +
+
+ +
+ +
+

+ Configuración Avanzada +

-const { - DATABASE_URL, - JWT_SECRET, - EVOLUTION_BASE_URL, - EVOLUTION_API_KEY, - EVOLUTION_INSTANCE, -} = process.env; +
+ + + + + +
+
-if (!DATABASE_URL || !JWT_SECRET) { - console.error("❌ ERROR FATAL: Faltan variables de entorno"); - process.exit(1); -} +
-const pool = new Pool({ - connectionString: DATABASE_URL, - ssl: false -}); +
+
+ +

Gestor de Plantillas

+

Próximamente podrás editar tus textos aquí.

+
+
+ + -// ========================================== -// 🧠 AUTO-ACTUALIZACIÓN DB -// ========================================== -async function autoUpdateDB() { - const client = await pool.connect(); - try { - console.log("🔄 Verificando estructura DB..."); + - await client.query(` - CREATE TABLE IF NOT EXISTS users ( - id SERIAL PRIMARY KEY, - full_name TEXT NOT NULL, - phone TEXT NOT NULL, - email TEXT NOT NULL, - dni TEXT, - address TEXT, - password_hash TEXT NOT NULL, - is_verified BOOLEAN DEFAULT FALSE, - owner_id INT, - role TEXT DEFAULT 'operario', - created_at TIMESTAMP DEFAULT NOW() - ); - CREATE TABLE IF NOT EXISTS login_codes ( - id SERIAL PRIMARY KEY, - user_id INT REFERENCES users(id) ON DELETE CASCADE, - phone TEXT NOT NULL, - code_hash TEXT NOT NULL, - purpose TEXT DEFAULT 'register_verify', - consumed_at TIMESTAMP, - expires_at TIMESTAMP NOT NULL, - created_at TIMESTAMP DEFAULT NOW() - ); - CREATE TABLE IF NOT EXISTS guilds ( - id SERIAL PRIMARY KEY, - owner_id INT REFERENCES users(id) ON DELETE CASCADE, - name TEXT NOT NULL, - created_at TIMESTAMP DEFAULT NOW() - ); - CREATE TABLE IF NOT EXISTS user_guilds ( - user_id INT REFERENCES users(id) ON DELETE CASCADE, - guild_id INT REFERENCES guilds(id) ON DELETE CASCADE, - PRIMARY KEY (user_id, guild_id) - ); - CREATE TABLE IF NOT EXISTS companies ( - id SERIAL PRIMARY KEY, - owner_id INT REFERENCES users(id) ON DELETE CASCADE, - name TEXT NOT NULL, - cif TEXT, - email TEXT, - phone TEXT, - address TEXT, - created_at TIMESTAMP DEFAULT NOW() - ); - CREATE TABLE IF NOT EXISTS clients ( - id SERIAL PRIMARY KEY, - owner_id INT REFERENCES users(id) ON DELETE CASCADE, - full_name TEXT NOT NULL, - phone TEXT NOT NULL, - email TEXT, - addresses JSONB DEFAULT '[]', - notes TEXT, - created_at TIMESTAMP DEFAULT NOW() - ); - CREATE TABLE IF NOT EXISTS service_statuses ( - id SERIAL PRIMARY KEY, - owner_id INT REFERENCES users(id) ON DELETE CASCADE, - name TEXT NOT NULL, - color TEXT DEFAULT 'gray', - is_default BOOLEAN DEFAULT FALSE, - is_final BOOLEAN DEFAULT FALSE, - created_at TIMESTAMP DEFAULT NOW() - ); - - -- Tablas de Zonas (Simplificadas) - CREATE TABLE IF NOT EXISTS zones ( - id SERIAL PRIMARY KEY, - name TEXT NOT NULL, - owner_id INT, - created_at TIMESTAMP DEFAULT NOW() - ); - CREATE TABLE IF NOT EXISTS user_zones ( - user_id INT REFERENCES users(id) ON DELETE CASCADE, - zone_id INT REFERENCES zones(id) ON DELETE CASCADE, - PRIMARY KEY (user_id, zone_id) - ); + - CREATE TABLE IF NOT EXISTS services ( - id SERIAL PRIMARY KEY, - owner_id INT REFERENCES users(id) ON DELETE CASCADE, - client_id INT REFERENCES clients(id) ON DELETE SET NULL, - status_id INT REFERENCES service_statuses(id) ON DELETE SET NULL, - guild_id INT REFERENCES guilds(id) ON DELETE SET NULL, - assigned_to INT REFERENCES users(id) ON DELETE SET NULL, - title TEXT, - description TEXT, - contact_phone TEXT, - contact_name TEXT, - address TEXT, - email TEXT, - scheduled_date DATE DEFAULT CURRENT_DATE, - scheduled_time TIME DEFAULT CURRENT_TIME, - duration_minutes INT DEFAULT 30, - is_urgent BOOLEAN DEFAULT FALSE, - is_company BOOLEAN DEFAULT FALSE, - company_id INT REFERENCES companies(id) ON DELETE SET NULL, - company_ref TEXT, - internal_notes TEXT, - client_notes TEXT, - closed_at TIMESTAMP, - created_at TIMESTAMP DEFAULT NOW() - ); - CREATE TABLE IF NOT EXISTS service_logs ( - id SERIAL PRIMARY KEY, - service_id INT REFERENCES services(id) ON DELETE CASCADE, - user_id INT REFERENCES users(id) ON DELETE SET NULL, - old_status_id INT REFERENCES service_statuses(id), - new_status_id INT REFERENCES service_statuses(id), - comment TEXT, - created_at TIMESTAMP DEFAULT NOW() - ); - `); + -// RUTAS AUTH -app.post("/auth/register", async (req, res) => { const client = await pool.connect(); try { const { fullName, phone, address, dni, email, password } = req.body; const p = normalizePhone(phone); if (!fullName || !p || !email || !password) return res.status(400).json({ ok: false }); const passwordHash = await bcrypt.hash(password, 10); await client.query('BEGIN'); const insert = await client.query("INSERT INTO users (full_name, phone, address, dni, email, password_hash, role, owner_id) VALUES ($1, $2, $3, $4, $5, $6, 'admin', NULL) RETURNING id", [fullName, p, address, dni, email, passwordHash]); const userId = insert.rows[0].id; const code = genCode6(); const codeHash = await bcrypt.hash(code, 10); const expiresAt = new Date(Date.now() + 10 * 60 * 1000); await client.query("INSERT INTO login_codes (user_id, phone, code_hash, expires_at) VALUES ($1, $2, $3, $4)", [userId, p, codeHash, expiresAt]); await sendWhatsAppCode(p, code); await client.query('COMMIT'); res.json({ ok: true, phone: p }); } catch (e) { await client.query('ROLLBACK'); res.status(500).json({ ok: false }); } finally { client.release(); } }); -app.post("/auth/verify", async (req, res) => { try { const { phone, code } = req.body; const p = normalizePhone(phone); const q = await pool.query(`SELECT lc.*, u.id as uid, u.email, u.role, u.owner_id FROM login_codes lc JOIN users u ON lc.user_id = u.id WHERE lc.phone=$1 AND lc.consumed_at IS NULL AND lc.expires_at > NOW() ORDER BY lc.created_at DESC LIMIT 1`, [p]); if (q.rowCount === 0) return res.status(400).json({ ok: false }); const row = q.rows[0]; if (!(await bcrypt.compare(String(code), row.code_hash))) return res.status(400).json({ ok: false }); 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]); res.json({ ok: true, token: signToken({ id: row.uid, email: row.email, phone: p, role: row.role, owner_id: row.owner_id }) }); } catch (e) { res.status(500).json({ ok: false }); } }); -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 }); let user = null; for (const u of q.rows) { if (await bcrypt.compare(password, u.password_hash)) { user = u; break; } } if (!user) return res.status(401).json({ ok: false }); res.json({ ok: true, token: signToken(user) }); } catch(e) { res.status(500).json({ ok: false }); } }); -app.post("/auth/forgot-password", async (req, res) => { try { const { dni, phone } = req.body; const p = normalizePhone(phone); const q = await pool.query("SELECT id FROM users WHERE dni=$1 AND phone=$2", [dni, p]); if (q.rowCount === 0) return res.status(404).json({ ok: false }); const uid = q.rows[0].id; const code = genCode6(); const hash = await bcrypt.hash(code, 10); await pool.query("INSERT INTO login_codes (user_id, phone, code_hash, purpose, expires_at) VALUES ($1, $2, $3, 'password_reset', $4)", [uid, p, hash, new Date(Date.now()+600000)]); await sendWhatsAppCode(p, code); res.json({ ok: true }); } catch (e) { res.status(500).json({ ok: false }); } }); -app.post("/auth/reset-password", async (req, res) => { const client = await pool.connect(); try { const { phone, code, newPassword } = req.body; const p = normalizePhone(phone); const q = await client.query(`SELECT lc.*, u.id as uid FROM login_codes lc JOIN users u ON lc.user_id=u.id WHERE lc.phone=$1 AND lc.purpose='password_reset' AND lc.consumed_at IS NULL AND lc.expires_at>NOW() ORDER BY lc.created_at DESC LIMIT 1`, [p]); if(q.rowCount===0) return res.status(400).json({ok:false}); const row=q.rows[0]; if(!(await bcrypt.compare(String(code), row.code_hash))) return res.status(400).json({ok:false}); const hash=await bcrypt.hash(newPassword, 10); await client.query('BEGIN'); await client.query("UPDATE users SET password_hash=$1 WHERE id=$2",[hash, row.uid]); await client.query("UPDATE login_codes SET consumed_at=NOW() WHERE id=$1",[row.id]); await client.query('COMMIT'); res.json({ok:true}); } catch(e){await client.query('ROLLBACK'); res.status(500).json({ok:false});} finally{client.release();} }); +
+
+
-// ========================= -// GESTIÓN DE ESTADOS (NUEVO) -// ========================= -app.get("/statuses", authMiddleware, async (req, res) => { - try { - let q = await pool.query("SELECT * FROM service_statuses WHERE owner_id=$1 ORDER BY id ASC", [req.user.accountId]); - // Si no hay estados, crea los default - if (q.rowCount === 0) { - const defaults = [{name:'Pendiente',c:'gray',d:true,f:false},{name:'En Proceso',c:'blue',d:false,f:false},{name:'Terminado',c:'green',d:false,f:true},{name:'Cancelado',c:'red',d:false,f:true}]; - for (const s of defaults) await pool.query("INSERT INTO service_statuses (owner_id,name,color,is_default,is_final) VALUES ($1,$2,$3,$4,$5)", [req.user.accountId,s.name,s.c,s.d,s.f]); - q = await pool.query("SELECT * FROM service_statuses WHERE owner_id=$1 ORDER BY id ASC", [req.user.accountId]); +
Msg
+ + + + + \ No newline at end of file