add landing and subscription plans

Signed-off-by: Omar Sánchez Pizarro <omar.sanchez@pistacero.net>
This commit is contained in:
Omar Sánchez Pizarro
2026-01-20 23:49:19 +01:00
parent 05f0455744
commit 6ec8855c00
79 changed files with 8839 additions and 361 deletions

View File

@@ -0,0 +1,208 @@
import express from 'express';
import { basicAuthMiddleware } from '../middlewares/auth.js';
import { adminAuthMiddleware } from '../middlewares/adminAuth.js';
import { getUser, getUserSubscription, updateUserSubscription, getWorkerCount } from '../services/mongodb.js';
import { SUBSCRIPTION_PLANS, getPlan } from '../config/subscriptionPlans.js';
const router = express.Router();
// Obtener planes disponibles (público)
router.get('/plans', (req, res) => {
try {
const plans = Object.values(SUBSCRIPTION_PLANS).map(plan => ({
id: plan.id,
name: plan.name,
description: plan.description,
price: plan.price,
limits: {
maxWorkers: plan.limits.maxWorkers,
maxNotificationsPerDay: plan.limits.maxNotificationsPerDay,
platforms: plan.limits.platforms,
},
features: plan.features,
}));
res.json({ plans });
} catch (error) {
console.error('Error obteniendo planes:', error);
res.status(500).json({ error: error.message });
}
});
// Obtener suscripción del usuario actual
router.get('/me', basicAuthMiddleware, async (req, res) => {
try {
const username = req.user.username;
const subscription = await getUserSubscription(username);
const user = await getUser(username);
const workerCount = await getWorkerCount(username);
const planId = subscription?.planId || 'free';
const plan = getPlan(planId);
res.json({
subscription: {
planId,
plan: {
id: plan.id,
name: plan.name,
description: plan.description,
price: plan.price,
limits: plan.limits,
features: plan.features,
},
status: subscription?.status || 'active',
currentPeriodStart: subscription?.currentPeriodStart || user?.createdAt,
currentPeriodEnd: subscription?.currentPeriodEnd || null,
cancelAtPeriodEnd: subscription?.cancelAtPeriodEnd || false,
},
usage: {
workers: workerCount,
maxWorkers: plan.limits.maxWorkers === -1 ? 'Ilimitado' : plan.limits.maxWorkers,
},
});
} catch (error) {
console.error('Error obteniendo suscripción:', error);
res.status(500).json({ error: error.message });
}
});
// Actualizar suscripción (requiere admin o para el propio usuario en caso de cancelación)
router.put('/me', basicAuthMiddleware, async (req, res) => {
try {
const username = req.user.username;
const { planId, status, cancelAtPeriodEnd } = req.body;
if (!planId) {
return res.status(400).json({ error: 'planId es requerido' });
}
// Verificar que el plan existe
const plan = getPlan(planId);
if (!plan) {
return res.status(400).json({ error: 'Plan no válido' });
}
// Solo permitir actualizar a plan gratuito o cancelar suscripción
// Para actualizar a planes de pago, se requiere integración con pasarela de pago
if (planId !== 'free' && !req.user.role === 'admin') {
return res.status(403).json({
error: 'Para actualizar a un plan de pago, contacta con soporte o usa la pasarela de pago'
});
}
const subscription = await getUserSubscription(username);
// Calcular fechas del período
const now = new Date();
let currentPeriodStart = subscription?.currentPeriodStart || now;
let currentPeriodEnd = null;
if (planId !== 'free') {
// Para planes de pago, establecer período mensual o anual según corresponda
// Por ahora, asumimos mensual (30 días)
currentPeriodEnd = new Date(now);
currentPeriodEnd.setMonth(currentPeriodEnd.getMonth() + 1);
}
await updateUserSubscription(username, {
planId,
status: status || 'active',
currentPeriodStart,
currentPeriodEnd,
cancelAtPeriodEnd: cancelAtPeriodEnd || false,
});
console.log(`✅ Suscripción actualizada para ${username}: ${planId}`);
res.json({
success: true,
message: 'Suscripción actualizada correctamente',
subscription: {
planId,
status: status || 'active',
currentPeriodStart,
currentPeriodEnd,
cancelAtPeriodEnd: cancelAtPeriodEnd || false,
},
});
} catch (error) {
console.error('Error actualizando suscripción:', error);
res.status(500).json({ error: error.message });
}
});
// Obtener suscripción de cualquier usuario (solo admin)
router.get('/:username', basicAuthMiddleware, adminAuthMiddleware, async (req, res) => {
try {
const { username } = req.params;
const subscription = await getUserSubscription(username);
const user = await getUser(username);
const workerCount = await getWorkerCount(username);
const planId = subscription?.planId || 'free';
const plan = getPlan(planId);
res.json({
subscription: {
planId,
plan: {
id: plan.id,
name: plan.name,
description: plan.description,
price: plan.price,
limits: plan.limits,
features: plan.features,
},
status: subscription?.status || 'active',
currentPeriodStart: subscription?.currentPeriodStart || user?.createdAt,
currentPeriodEnd: subscription?.currentPeriodEnd || null,
cancelAtPeriodEnd: subscription?.cancelAtPeriodEnd || false,
},
usage: {
workers: workerCount,
maxWorkers: plan.limits.maxWorkers === -1 ? 'Ilimitado' : plan.limits.maxWorkers,
},
});
} catch (error) {
console.error('Error obteniendo suscripción:', error);
res.status(500).json({ error: error.message });
}
});
// Actualizar suscripción de cualquier usuario (solo admin)
router.put('/:username', basicAuthMiddleware, adminAuthMiddleware, async (req, res) => {
try {
const { username } = req.params;
const { planId, status, currentPeriodStart, currentPeriodEnd, cancelAtPeriodEnd } = req.body;
if (!planId) {
return res.status(400).json({ error: 'planId es requerido' });
}
// Verificar que el plan existe
const plan = getPlan(planId);
if (!plan) {
return res.status(400).json({ error: 'Plan no válido' });
}
await updateUserSubscription(username, {
planId,
status: status || 'active',
currentPeriodStart: currentPeriodStart ? new Date(currentPeriodStart) : new Date(),
currentPeriodEnd: currentPeriodEnd ? new Date(currentPeriodEnd) : null,
cancelAtPeriodEnd: cancelAtPeriodEnd || false,
});
console.log(`✅ Suscripción actualizada para ${username}: ${planId} por admin ${req.user.username}`);
res.json({
success: true,
message: `Suscripción de ${username} actualizada correctamente`,
});
} catch (error) {
console.error('Error actualizando suscripción:', error);
res.status(500).json({ error: error.message });
}
});
export default router;