pushy

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-06-28 11:29:07 +08:00
parent 463a8d3978
commit 89acdfc239
67 changed files with 2736 additions and 306 deletions
+39 -16
View File
@@ -10,10 +10,11 @@
***********************************************************************************************************************************************************************/
const { Op } = require('sequelize');
const mdl_TierPlans = require('../../models/tiers/tier_plans.mdl');
const mdl_UserTiers = require('../../models/tiers/user_tiers.mdl');
const mdl_Payments = require('../../models/tiers/payments.mdl');
const mdl_Users = require('../../models/users/users.mdl');
const mdl_TierCategories = require('../../models/tiers/tier_categories.mdl');
const mdl_TierPlans = require('../../models/tiers/tier_plans.mdl');
const mdl_UserTiers = require('../../models/tiers/user_tiers.mdl');
const mdl_Payments = require('../../models/tiers/payments.mdl');
const mdl_Users = require('../../models/users/users.mdl');
const mdl_PlanCourses = require('../../models/tiers/plan_courses.mdl');
const { Course } = require('../../models/courses/courses.mdl');
@@ -84,14 +85,24 @@ exports.getPlan = async (req, res) => {
exports.createPlan = async (req, res) => {
try {
const { tier, label, duration_days, price, currency } = req.body;
if (!tier || !label || !duration_days || !price)
return R.error(res, 'tier, label, duration_days, and price are required.', 400);
const { tier_category_id, label, duration_days, price, currency } = req.body;
if (!tier_category_id || !label || !duration_days || !price)
return R.error(res, 'tier_category_id, label, duration_days, and price are required.', 400);
const plan = await mdl_TierPlans.create({ tier, label, duration_days, price, currency });
const category = await mdl_TierCategories.findByPk(tier_category_id);
if (!category || !category.is_active)
return R.error(res, 'Tier category not found or inactive.', 404);
if (category.is_default)
return R.error(res, 'Plans cannot be created under the default (Free) tier. Free access is automatic.', 400);
const plan = await mdl_TierPlans.create({
tier_category_id: category.tier_category_id,
tier: category.slug,
label, duration_days, price, currency,
});
const plain = plan.get({ plain: true });
logActivity(req.user?.user_id, 'create_tier_plan', { entityType: 'tier_plan', details: { label: plain.label, tier: plain.tier } });
// Serialize plan_id as string — CockroachDB BigInts exceed JS Number.MAX_SAFE_INTEGER
return R.success(res, 'Plan created.', { ...plain, plan_id: String(plain.plan_id) }, 201);
} catch (err) {
console.error('[ADMIN][CREATE PLAN]', err);
@@ -104,9 +115,21 @@ exports.updatePlan = async (req, res) => {
const plan = await mdl_TierPlans.findByPk(req.params.id);
if (!plan) return R.error(res, 'Plan not found.', 404);
const allowed = ['label', 'duration_days', 'price', 'currency', 'is_active'];
const allowed = ['label', 'duration_days', 'price', 'currency', 'is_active', 'tier_category_id'];
const updates = {};
allowed.forEach((k) => { if (req.body[k] !== undefined) updates[k] = req.body[k]; });
for (const k of allowed) {
if (req.body[k] !== undefined) updates[k] = req.body[k];
}
// If tier_category_id is being changed, sync the tier slug
if (updates.tier_category_id) {
const category = await mdl_TierCategories.findByPk(updates.tier_category_id);
if (!category || !category.is_active)
return R.error(res, 'Tier category not found or inactive.', 404);
if (category.is_default)
return R.error(res, 'Plans cannot be moved to the Free tier category.', 400);
updates.tier = category.slug;
}
await plan.update(updates);
logActivity(req.user?.user_id, 'update_tier_plan', { entityType: 'tier_plan', details: { label: plan.label } });
@@ -234,16 +257,16 @@ exports.getUserTiers = async (req, res) => {
exports.grantTier = async (req, res) => {
try {
const { user_id, tier, plan_id, notes } = req.body;
if (!user_id || !tier || !plan_id)
return R.error(res, 'user_id, tier, and plan_id are required.', 400);
const { user_id, plan_id, notes } = req.body;
if (!user_id || !plan_id)
return R.error(res, 'user_id and plan_id are required.', 400);
const user = await mdl_Users.findByPk(user_id);
if (!user) return R.error(res, 'User not found.', 404);
const plan = await mdl_TierPlans.findByPk(plan_id);
if (!plan || !plan.is_active) return R.error(res, 'Plan not found or inactive.', 404);
if (plan.tier !== tier) return R.error(res, 'Plan tier mismatch.', 400);
const tier = plan.tier;
await mdl_UserTiers.update(
{ status: 'expired' },
@@ -254,7 +277,7 @@ exports.grantTier = async (req, res) => {
const expiresAt = new Date(startsAt.getTime() + plan.duration_days * 86400000);
const newTier = await mdl_UserTiers.create({
user_id, tier, status: 'active',
user_id, tier, plan_id, status: 'active',
starts_at: startsAt,
expires_at: expiresAt,
granted_by: req.user.user_id,