Actualizar server.js

This commit is contained in:
2026-04-02 20:05:45 +00:00
parent 233c11c6b8
commit 4e942d0130

116
server.js
View File

@@ -4432,6 +4432,122 @@ app.post("/webhook/stripe", async (req, res) => {
}
});
// ==========================================
// 🛡️ MÓDULO SAAS: PLANES DE PROTECCIÓN
// ==========================================
// 1. DASHBOARD: Estadísticas
app.get("/protection/dashboard", authMiddleware, async (req, res) => {
const cid = req.user.accountId;
try {
const [statsQ, actQ, topQ] = await Promise.all([
pool.query(`
SELECT
(SELECT COUNT(*) FROM protection_subscriptions WHERE company_id = $1) as total,
(SELECT COALESCE(SUM(p.price), 0) FROM protection_subscriptions s JOIN protection_plans p ON s.plan_id = p.id WHERE s.company_id = $1 AND s.status = 'activo') as mrr,
(SELECT COALESCE(SUM(urgencies_used), 0) FROM protection_subscriptions WHERE company_id = $1) as urgUsed,
(SELECT COALESCE(SUM(bricos_used), 0) FROM protection_subscriptions WHERE company_id = $1) as briUsed,
(SELECT COUNT(*) FROM protection_subscriptions WHERE company_id = $1 AND payment_status = 'impagado') as unpaid
`, [cid]),
pool.query("SELECT * FROM protection_activity WHERE company_id = $1 ORDER BY created_at DESC LIMIT 10", [cid]),
pool.query(`
SELECT p.*, COUNT(s.id) as users
FROM protection_plans p
LEFT JOIN protection_subscriptions s ON p.id = s.plan_id
WHERE p.company_id = $1
GROUP BY p.id
`, [cid])
]);
res.json({ ok: true, stats: statsQ.rows[0], activity: actQ.rows, topPlans: topQ.rows });
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
});
// 2. PLANES
app.get("/protection/plans", authMiddleware, async (req, res) => {
try {
const q = await pool.query("SELECT * FROM protection_plans WHERE company_id = $1 ORDER BY created_at DESC", [req.user.accountId]);
res.json({ ok: true, plans: q.rows });
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
});
app.post("/protection/plans", authMiddleware, async (req, res) => {
const cid = req.user.accountId;
const { name, type, price, renewal, urgencies, bricos, coverages } = req.body;
try {
await pool.query(
"INSERT INTO protection_plans (company_id, name, type, price, renewal_price, urgencies_limit, bricos_limit, coverages) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)",
[cid, name, type, price, renewal, urgencies, bricos, coverages]
);
await pool.query("INSERT INTO protection_activity (company_id, type, description) VALUES ($1, 'alta', $2)", [cid, `Nuevo plan creado: ${name}`]);
res.json({ ok: true });
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
});
// 3. SUSCRIPTORES
app.get("/protection/subscribers", authMiddleware, async (req, res) => {
try {
const q = await pool.query(`
SELECT s.*, p.name as plan_name, p.price as plan_price, p.urgencies_limit, p.bricos_limit
FROM protection_subscriptions s
LEFT JOIN protection_plans p ON s.plan_id = p.id
WHERE s.company_id = $1 ORDER BY s.created_at DESC
`, [req.user.accountId]);
res.json({ ok: true, subscribers: q.rows });
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
});
app.post("/protection/subscribers", authMiddleware, async (req, res) => {
const cid = req.user.accountId;
const { plan_id, name, dni, phone, payment_status, status, renewal_date, bricos_used, urgencies_used } = req.body;
try {
const d = renewal_date ? renewal_date : null;
await pool.query(
`INSERT INTO protection_subscriptions
(company_id, plan_id, client_name, client_dni, client_phone, payment_status, status, renewal_date, bricos_used, urgencies_used)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`,
[cid, plan_id, name, dni, phone, payment_status, status, d, bricos_used, urgencies_used]
);
await pool.query("INSERT INTO protection_activity (company_id, type, description) VALUES ($1, 'alta', $2)", [cid, `Suscripción creada: ${name}`]);
res.json({ ok: true });
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
});
app.put("/protection/subscribers/:id/toggle", authMiddleware, async (req, res) => {
const cid = req.user.accountId;
const { id } = req.params;
const { field, value } = req.body;
try {
if (!['payment_status', 'status'].includes(field)) throw new Error("Invalid field");
await pool.query(`UPDATE protection_subscriptions SET ${field} = $1 WHERE id = $2 AND company_id = $3`, [value, id, cid]);
await pool.query("INSERT INTO protection_activity (company_id, type, description) VALUES ($1, 'cobro', $2)", [cid, `Cambiado a ${value} en ID: ${id}`]);
res.json({ ok: true });
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
});
// 4. CONFIGURACIÓN
app.get("/protection/config", authMiddleware, async (req, res) => {
try {
const q = await pool.query("SELECT * FROM protection_config WHERE company_id = $1", [req.user.accountId]);
res.json({ ok: true, config: q.rows[0] || {} });
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
});
app.post("/protection/config", authMiddleware, async (req, res) => {
const cid = req.user.accountId;
const { name, email, phone, auto_renew, pre_notice, billing_method, contract_text } = req.body;
try {
await pool.query(`
INSERT INTO protection_config (company_id, name, email, phone, auto_renew, pre_notice, billing_method, contract_text)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8) ON CONFLICT (company_id) DO UPDATE SET
name=EXCLUDED.name, email=EXCLUDED.email, phone=EXCLUDED.phone, auto_renew=EXCLUDED.auto_renew,
pre_notice=EXCLUDED.pre_notice, billing_method=EXCLUDED.billing_method, contract_text=EXCLUDED.contract_text
`, [cid, name, email, phone, auto_renew, pre_notice, billing_method, contract_text]);
res.json({ ok: true });
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
});
// ==========================================
// 🕒 EL RELOJ DEL SISTEMA (Ejecutar cada minuto)
// ==========================================