Actualizar server.js
This commit is contained in:
77
server.js
77
server.js
@@ -317,15 +317,11 @@ function signToken(user) { const accountId = user.owner_id || user.id; return jw
|
|||||||
function authMiddleware(req, res, next) { const h = req.headers.authorization || ""; const token = h.startsWith("Bearer ") ? h.slice(7) : ""; if (!token) return res.status(401).json({ ok: false, error: "No token" }); try { req.user = jwt.verify(token, JWT_SECRET); next(); } catch { return res.status(401).json({ ok: false, error: "Token inválido" }); } }
|
function authMiddleware(req, res, next) { const h = req.headers.authorization || ""; const token = h.startsWith("Bearer ") ? h.slice(7) : ""; if (!token) return res.status(401).json({ ok: false, error: "No token" }); try { req.user = jwt.verify(token, JWT_SECRET); next(); } catch { return res.status(401).json({ ok: false, error: "Token inválido" }); } }
|
||||||
function genCode6() { return String(Math.floor(100000 + Math.random() * 900000)); }
|
function genCode6() { return String(Math.floor(100000 + Math.random() * 900000)); }
|
||||||
|
|
||||||
// 🛡️ MIDDLEWARE DE PLANES (CORREGIDO)
|
// 🛡️ MIDDLEWARE DE PLANES
|
||||||
async function requirePlan(req, res, next, feature) {
|
async function requirePlan(req, res, next, feature) {
|
||||||
try {
|
try {
|
||||||
// Quitamos subscription_status para que no dé error en BD actualizadas
|
|
||||||
const q = await pool.query("SELECT plan_tier FROM users WHERE id=$1", [req.user.accountId]);
|
const q = await pool.query("SELECT plan_tier FROM users WHERE id=$1", [req.user.accountId]);
|
||||||
|
const userPlan = 'pro'; // Forzamos a 'pro' para tus pruebas actuales sin límites
|
||||||
// ⚠️ TRUCO TEMPORAL: Forzamos a que el sistema te lea como 'pro' para que puedas probar WhatsApp sin bloqueos
|
|
||||||
const userPlan = 'pro'; // Cuando quieras restringir planes, cambia esto por: q.rows[0]?.plan_tier || 'free';
|
|
||||||
|
|
||||||
const limits = PLAN_LIMITS[userPlan];
|
const limits = PLAN_LIMITS[userPlan];
|
||||||
|
|
||||||
if (!limits || !limits[feature]) {
|
if (!limits || !limits[feature]) {
|
||||||
@@ -338,6 +334,71 @@ async function requirePlan(req, res, next, feature) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 🔐 RUTAS DE AUTENTICACIÓN (LOGIN) - RESTAURADAS
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
app.post("/auth/login", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { email, password } = req.body;
|
||||||
|
if (!email || !password) return res.status(400).json({ ok: false, error: "Faltan credenciales" });
|
||||||
|
|
||||||
|
// Buscamos al usuario por email o por teléfono
|
||||||
|
const q = await pool.query("SELECT * FROM users WHERE email = $1 OR phone = $1", [email]);
|
||||||
|
if (q.rowCount === 0) return res.status(401).json({ ok: false, error: "Usuario no encontrado" });
|
||||||
|
|
||||||
|
const user = q.rows[0];
|
||||||
|
if (user.status !== 'active') return res.status(401).json({ ok: false, error: "Cuenta desactivada. Contacta con el administrador." });
|
||||||
|
|
||||||
|
const valid = await bcrypt.compare(password, user.password_hash);
|
||||||
|
if (!valid) return res.status(401).json({ ok: false, error: "Contraseña incorrecta" });
|
||||||
|
|
||||||
|
const token = signToken(user);
|
||||||
|
res.json({ ok: true, token, role: user.role, name: user.full_name });
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Login error:", e);
|
||||||
|
res.status(500).json({ ok: false, error: "Error de servidor al iniciar sesión" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/auth/register", async (req, res) => {
|
||||||
|
const client = await pool.connect();
|
||||||
|
try {
|
||||||
|
const { fullName, email, phone, password } = req.body;
|
||||||
|
if (!email || !password) return res.status(400).json({ ok: false, error: "Faltan datos requeridos" });
|
||||||
|
|
||||||
|
const hash = await bcrypt.hash(password, 10);
|
||||||
|
await client.query('BEGIN');
|
||||||
|
|
||||||
|
const insert = await client.query(
|
||||||
|
"INSERT INTO users (full_name, email, phone, password_hash, role, status) VALUES ($1, $2, $3, $4, 'admin', 'active') RETURNING id",
|
||||||
|
[fullName || 'Admin', email, normalizePhone(phone), hash]
|
||||||
|
);
|
||||||
|
const newId = insert.rows[0].id;
|
||||||
|
|
||||||
|
// Al ser registro, él es su propio dueño (owner_id = id)
|
||||||
|
await client.query("UPDATE users SET owner_id = id WHERE id = $1", [newId]);
|
||||||
|
await client.query('COMMIT');
|
||||||
|
res.json({ ok: true, message: "Usuario creado. Ya puedes iniciar sesión." });
|
||||||
|
} catch (e) {
|
||||||
|
await client.query('ROLLBACK');
|
||||||
|
console.error("Register error:", e);
|
||||||
|
res.status(400).json({ ok: false, error: "El correo o teléfono ya están en uso." });
|
||||||
|
} finally {
|
||||||
|
client.release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/auth/me", authMiddleware, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const q = await pool.query("SELECT id, full_name, email, role, company_slug, plan_tier FROM users WHERE id = $1", [req.user.sub]);
|
||||||
|
if(q.rowCount === 0) return res.status(404).json({ok: false});
|
||||||
|
res.json({ ok: true, user: q.rows[0] });
|
||||||
|
} catch(e) {
|
||||||
|
res.status(500).json({ ok: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// --- WHATSAPP UTILS ---
|
// --- WHATSAPP UTILS ---
|
||||||
async function sendWhatsAppCode(phone, code) {
|
async function sendWhatsAppCode(phone, code) {
|
||||||
if (!EVOLUTION_BASE_URL || !EVOLUTION_API_KEY || !EVOLUTION_INSTANCE) { console.error("❌ Faltan datos WhatsApp"); return; }
|
if (!EVOLUTION_BASE_URL || !EVOLUTION_API_KEY || !EVOLUTION_INSTANCE) { console.error("❌ Faltan datos WhatsApp"); return; }
|
||||||
@@ -506,10 +567,6 @@ app.post("/public/assignment/respond", async (req, res) => {
|
|||||||
// 🌐 RUTAS PÚBLICAS: PORTAL DEL CLIENTE (SIN FRICCIÓN)
|
// 🌐 RUTAS PÚBLICAS: PORTAL DEL CLIENTE (SIN FRICCIÓN)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// 🌐 RUTAS PÚBLICAS: PORTAL DEL CLIENTE (SIN FRICCIÓN)
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
// 1. Cargar datos del cliente, logo y empresa
|
// 1. Cargar datos del cliente, logo y empresa
|
||||||
app.get("/public/portal/:token", async (req, res) => {
|
app.get("/public/portal/:token", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user