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
@@ -0,0 +1,170 @@
'use strict';
const mdl_TierCategories = require('../../models/tiers/tier_categories.mdl');
const mdl_TierPlans = require('../../models/tiers/tier_plans.mdl');
const mdl_PlanPolicies = require('../../models/tiers/plan_policies.mdl');
const mdl_SystemBadges = require('../../models/system_badges/system_badges.mdl');
const Asset = require('../../models/assets/assets.mdl');
const R = require('../../utils/response.util');
const logActivity = require('../../utils/logActivity.util');
require('../../models/tiers/tier.associations');
const VALID_RULE_TYPES = new Set([
'course_subscription_access',
'required_active_tier',
'group_restriction',
]);
async function validateRules(rules) {
if (!Array.isArray(rules)) return 'access_rules must be an array.';
// Load valid tier slugs dynamically from DB
const categories = await mdl_TierCategories.findAll({ attributes: ['slug'] });
const validSlugs = new Set(categories.map((c) => c.slug));
for (const rule of rules) {
if (!VALID_RULE_TYPES.has(rule.type)) return `Unknown rule type: ${rule.type}`;
if (rule.type === 'course_subscription_access') {
if (!Array.isArray(rule.levels) || !rule.levels.length)
return 'course_subscription_access.levels must be a non-empty array of tier slugs.';
}
if (rule.type === 'required_active_tier') {
if (!rule.tier || !validSlugs.has(rule.tier))
return `required_active_tier.tier must be a valid tier category slug.`;
}
if (rule.type === 'group_restriction') {
if (!Array.isArray(rule.group_ids))
return 'group_restriction.group_ids must be an array.';
}
}
return null;
}
const ASSET_ATTRS = ['asset_id', 'file_url', 'display_name', 'mime_type'];
// ─── PLAN POLICIES ────────────────────────────────────────────────────────────
exports.getPlanPolicy = async (req, res) => {
try {
const plan = await mdl_TierPlans.findByPk(req.params.planId);
if (!plan) return R.error(res, 'Plan not found.', 404);
const policy = await mdl_PlanPolicies.findOne({ where: { plan_id: req.params.planId } });
return R.success(res, 'Policy retrieved.', policy ?? null);
} catch (err) {
console.error('[ADMIN][GET PLAN POLICY]', err);
return R.error(res, 'Could not retrieve policy.', 500);
}
};
exports.upsertPlanPolicy = async (req, res) => {
try {
const { planId } = req.params;
const plan = await mdl_TierPlans.findByPk(planId);
if (!plan) return R.error(res, 'Plan not found.', 404);
const { access_rules } = req.body;
let parsedRules = [];
if (access_rules !== undefined) {
try {
parsedRules = typeof access_rules === 'string' ? JSON.parse(access_rules) : access_rules;
} catch {
return R.error(res, 'access_rules is not valid JSON.', 400);
}
const err = await validateRules(parsedRules);
if (err) return R.error(res, err, 400);
}
let existing = await mdl_PlanPolicies.findOne({ where: { plan_id: planId } });
const payload = {
plan_id: planId,
access_rules: access_rules !== undefined ? parsedRules : (existing?.access_rules ?? []),
};
if (!existing) {
existing = await mdl_PlanPolicies.create(payload);
logActivity(req.user?.user_id, 'create_plan_policy', { entityType: 'plan_policy', details: { plan_id: planId } });
} else {
await existing.update(payload);
logActivity(req.user?.user_id, 'update_plan_policy', { entityType: 'plan_policy', details: { plan_id: planId } });
}
const result = await mdl_PlanPolicies.findOne({ where: { plan_id: planId } });
return R.success(res, 'Policy saved.', result);
} catch (err) {
console.error('[ADMIN][UPSERT PLAN POLICY]', err);
return R.error(res, 'Could not save policy.', 500);
}
};
// ─── SYSTEM BADGES ────────────────────────────────────────────────────────────
exports.getSystemBadges = async (req, res) => {
try {
const badges = await mdl_SystemBadges.findAll({
include: [{ model: Asset, as: 'asset', attributes: ASSET_ATTRS, required: false }],
order: [['key', 'ASC']],
});
return R.success(res, 'System badges retrieved.', badges);
} catch (err) {
console.error('[ADMIN][GET SYSTEM BADGES]', err);
return R.error(res, 'Could not retrieve system badges.', 500);
}
};
exports.getSystemBadge = async (req, res) => {
try {
const badge = await mdl_SystemBadges.findOne({
where: { key: req.params.key },
include: [{ model: Asset, as: 'asset', attributes: ASSET_ATTRS, required: false }],
});
if (!badge) return R.error(res, 'System badge not found.', 404);
return R.success(res, 'System badge retrieved.', badge);
} catch (err) {
console.error('[ADMIN][GET SYSTEM BADGE]', err);
return R.error(res, 'Could not retrieve system badge.', 500);
}
};
exports.upsertSystemBadge = async (req, res) => {
try {
const { key } = req.params;
const { asset_id, label, description, information, active_from, active_until } = req.body;
let existing = await mdl_SystemBadges.findOne({ where: { key } });
const payload = {
key,
asset_id: asset_id !== undefined ? (asset_id || null) : existing?.asset_id ?? null,
label: label ?? existing?.label ?? key,
description: description ?? existing?.description ?? null,
information: information ?? existing?.information ?? null,
active_from: active_from !== undefined ? (active_from || null) : existing?.active_from ?? null,
active_until: active_until !== undefined ? (active_until || null) : existing?.active_until ?? null,
};
if (!existing) {
existing = await mdl_SystemBadges.create(payload);
logActivity(req.user?.user_id, 'create_system_badge', { entityType: 'system_badge', details: { key } });
} else {
await existing.update(payload);
logActivity(req.user?.user_id, 'update_system_badge', { entityType: 'system_badge', details: { key } });
}
const result = await mdl_SystemBadges.findOne({
where: { key },
include: [{ model: Asset, as: 'asset', attributes: ASSET_ATTRS, required: false }],
});
return R.success(res, 'System badge saved.', result);
} catch (err) {
console.error('[ADMIN][UPSERT SYSTEM BADGE]', err);
return R.error(res, 'Could not save system badge.', 500);
}
};