'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); } };