mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
assets and tier plans revamp
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -39,6 +39,13 @@ const Asset = sequelize.define("Asset", {
|
||||
thumbnail_url: { type: DataTypes.STRING(512), label: "", order: 0, hidden: true },
|
||||
thumbnail_storage_key: { type: DataTypes.STRING(512), label: "", order: 0, hidden: true },
|
||||
|
||||
// ─── Background remux (.mov/.mkv → faststart .mp4) ───────────────────────
|
||||
// "none" — asset never needed a remux (not video, or already a fast format).
|
||||
// STRING, not ENUM — matches the STRING+CHECK column (see migration
|
||||
// 20270101000076), not a real Postgres enum type.
|
||||
transcode_status: { type: DataTypes.STRING(20), defaultValue: "none", label: "", order: 0, hidden: true },
|
||||
transcode_error: { type: DataTypes.TEXT, label: "", order: 0, hidden: true },
|
||||
|
||||
// ─── Description ──────────────────────────────────────────────────────────
|
||||
description: { type: DataTypes.TEXT, label: "Description", hidden: true },
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const Lesson = sequelize.define("Lesson", {
|
||||
|
||||
title: { type: DataTypes.STRING(255), allowNull: false, label: "Title", hidden: false, order: 1, filterable: true },
|
||||
// order 2 is reserved for the computed "Affiliated" (course_count) column, order 3 for computed "Course Status" — see LESSON_LIST_COMPUTED in lessons.controller.js
|
||||
subscription: { type: DataTypes.STRING(50), allowNull: true, label: "Subscription", hidden: false, order: 4, filterable: true, comment: "Optional direct tier gate for standalone lessons — null means open (or gated only via an attached unit)." },
|
||||
description: { type: DataTypes.TEXT, allowNull: true, label: "Description", hidden: true, order: 0, filterable: false },
|
||||
|
||||
duration_seconds: { type: DataTypes.INTEGER, allowNull: true, defaultValue: 0, filterable: false }, // computed from blocks on save
|
||||
|
||||
@@ -2,10 +2,22 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const sequelize = require('../../config/db.config');
|
||||
const { Course } = require('./courses.mdl');
|
||||
const Unit = require('./units.mdl');
|
||||
const Lesson = require('./lessons.mdl');
|
||||
|
||||
// Polymorphic target — a product is sold against exactly one Course, Unit, or
|
||||
// Lesson (purchasable_type + purchasable_id), not just courses. See
|
||||
// utils/purchasable.util.js for the type -> model/checkout-path resolver used
|
||||
// by every consumer (access checks, admin CRUD, checkout).
|
||||
//
|
||||
// constraints: false below because purchasable_id doesn't point at a single
|
||||
// table — referential integrity across the three possible targets is
|
||||
// enforced at the application layer only (see
|
||||
// 20270101000078-generalize-products-purchasable.js).
|
||||
const mdl_Product = sequelize.define('Product', {
|
||||
id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
|
||||
course_id: { type: DataTypes.BIGINT, allowNull: false },
|
||||
id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
|
||||
purchasable_type: { type: DataTypes.STRING(10), allowNull: false, defaultValue: 'course' },
|
||||
purchasable_id: { type: DataTypes.BIGINT, allowNull: false },
|
||||
name: { type: DataTypes.STRING(200), allowNull: false },
|
||||
description: { type: DataTypes.TEXT, allowNull: true },
|
||||
price: { type: DataTypes.DECIMAL(10, 2), allowNull: false },
|
||||
@@ -18,8 +30,12 @@ const mdl_Product = sequelize.define('Product', {
|
||||
paranoid: true,
|
||||
});
|
||||
|
||||
// A course has one product listing; a product belongs to one course
|
||||
mdl_Product.belongsTo(Course, { foreignKey: 'course_id', as: 'course' });
|
||||
Course.hasOne(mdl_Product, { foreignKey: 'course_id', as: 'product' });
|
||||
// Scoped hasOne per target type — Sequelize automatically adds the matching
|
||||
// purchasable_type filter to the join, so existing
|
||||
// `include: [{ model: mdl_Product, as: 'product' }]` call sites on Course
|
||||
// keep working unchanged.
|
||||
Course.hasOne(mdl_Product, { foreignKey: 'purchasable_id', constraints: false, scope: { purchasable_type: 'course' }, as: 'product' });
|
||||
Unit.hasOne(mdl_Product, { foreignKey: 'purchasable_id', constraints: false, scope: { purchasable_type: 'unit' }, as: 'product' });
|
||||
Lesson.hasOne(mdl_Product, { foreignKey: 'purchasable_id', constraints: false, scope: { purchasable_type: 'lesson' }, as: 'product' });
|
||||
|
||||
module.exports = mdl_Product;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/***********************************************************************************************************************************************************************
|
||||
* File Name: plan_lessons.mdl.js
|
||||
* Type of Program: Model
|
||||
* Description: Junction table — links standalone Lessons to a specific tier plan.
|
||||
* UNIQUE on lesson_id enforces one lesson belongs to one plan only.
|
||||
* Mirrors plan_courses.mdl.js — bundling/display only, not an
|
||||
* access-control mechanism (see canAccessLesson).
|
||||
* Author: Kenneth Obsequio (@lash0000)
|
||||
* Date Created: Aug. 1, 2026
|
||||
***********************************************************************************************************************************************************************/
|
||||
const { DataTypes } = require('sequelize');
|
||||
const sequelize = require('../../config/db.config');
|
||||
|
||||
const mdl_PlanLessons = sequelize.define('PlanLesson', {
|
||||
id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
|
||||
plan_id: { type: DataTypes.BIGINT, allowNull: false },
|
||||
lesson_id: { type: DataTypes.BIGINT, allowNull: false },
|
||||
}, {
|
||||
tableName: 'plan_lessons',
|
||||
timestamps: true,
|
||||
paranoid: false,
|
||||
});
|
||||
|
||||
module.exports = mdl_PlanLessons;
|
||||
@@ -0,0 +1,24 @@
|
||||
/***********************************************************************************************************************************************************************
|
||||
* File Name: plan_units.mdl.js
|
||||
* Type of Program: Model
|
||||
* Description: Junction table — links standalone Units to a specific tier plan.
|
||||
* UNIQUE on unit_id enforces one unit belongs to one plan only.
|
||||
* Mirrors plan_courses.mdl.js — bundling/display only, not an
|
||||
* access-control mechanism (see canAccessUnit).
|
||||
* Author: Kenneth Obsequio (@lash0000)
|
||||
* Date Created: Aug. 1, 2026
|
||||
***********************************************************************************************************************************************************************/
|
||||
const { DataTypes } = require('sequelize');
|
||||
const sequelize = require('../../config/db.config');
|
||||
|
||||
const mdl_PlanUnits = sequelize.define('PlanUnit', {
|
||||
id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
|
||||
plan_id: { type: DataTypes.BIGINT, allowNull: false },
|
||||
unit_id: { type: DataTypes.BIGINT, allowNull: false },
|
||||
}, {
|
||||
tableName: 'plan_units',
|
||||
timestamps: true,
|
||||
paranoid: false,
|
||||
});
|
||||
|
||||
module.exports = mdl_PlanUnits;
|
||||
@@ -4,10 +4,14 @@ const mdl_TierPlans = require('./tier_plans.mdl');
|
||||
const mdl_UserTiers = require('./user_tiers.mdl');
|
||||
const mdl_Payments = require('./payments.mdl');
|
||||
const mdl_PlanCourses = require('./plan_courses.mdl');
|
||||
const mdl_PlanUnits = require('./plan_units.mdl');
|
||||
const mdl_PlanLessons = require('./plan_lessons.mdl');
|
||||
const mdl_PlanPolicies = require('./plan_policies.mdl');
|
||||
const mdl_SystemBadges = require('../system_badges/system_badges.mdl');
|
||||
const Asset = require('../assets/assets.mdl');
|
||||
const { Course } = require('../courses/courses.mdl');
|
||||
const Unit = require('../courses/units.mdl');
|
||||
const Lesson = require('../courses/lessons.mdl');
|
||||
|
||||
// ─── TierCategory ─────────────────────────────────────────────────────────────
|
||||
mdl_TierCategories.belongsTo(Asset, { foreignKey: 'badge_asset_id', as: 'badgeAsset' });
|
||||
@@ -45,6 +49,42 @@ mdl_PlanCourses.belongsTo(Course, { foreignKey: 'course_id', as: 'course'
|
||||
Course.hasOne(mdl_PlanCourses, { as: 'planCourse', foreignKey: 'course_id' });
|
||||
mdl_TierPlans.hasMany(mdl_PlanCourses, { as: 'planCourses', foreignKey: 'plan_id' });
|
||||
|
||||
// ─── Plan ↔ Units ─────────────────────────────────────────────────────────────
|
||||
mdl_TierPlans.belongsToMany(Unit, {
|
||||
through: mdl_PlanUnits,
|
||||
foreignKey: 'plan_id',
|
||||
otherKey: 'unit_id',
|
||||
as: 'units',
|
||||
});
|
||||
Unit.belongsToMany(mdl_TierPlans, {
|
||||
through: mdl_PlanUnits,
|
||||
foreignKey: 'unit_id',
|
||||
otherKey: 'plan_id',
|
||||
as: 'plans',
|
||||
});
|
||||
mdl_PlanUnits.belongsTo(mdl_TierPlans, { foreignKey: 'plan_id', as: 'plan' });
|
||||
mdl_PlanUnits.belongsTo(Unit, { foreignKey: 'unit_id', as: 'unit' });
|
||||
Unit.hasOne(mdl_PlanUnits, { as: 'planUnit', foreignKey: 'unit_id' });
|
||||
mdl_TierPlans.hasMany(mdl_PlanUnits, { as: 'planUnits', foreignKey: 'plan_id' });
|
||||
|
||||
// ─── Plan ↔ Lessons ───────────────────────────────────────────────────────────
|
||||
mdl_TierPlans.belongsToMany(Lesson, {
|
||||
through: mdl_PlanLessons,
|
||||
foreignKey: 'plan_id',
|
||||
otherKey: 'lesson_id',
|
||||
as: 'lessons',
|
||||
});
|
||||
Lesson.belongsToMany(mdl_TierPlans, {
|
||||
through: mdl_PlanLessons,
|
||||
foreignKey: 'lesson_id',
|
||||
otherKey: 'plan_id',
|
||||
as: 'plans',
|
||||
});
|
||||
mdl_PlanLessons.belongsTo(mdl_TierPlans, { foreignKey: 'plan_id', as: 'plan' });
|
||||
mdl_PlanLessons.belongsTo(Lesson, { foreignKey: 'lesson_id', as: 'lesson' });
|
||||
Lesson.hasOne(mdl_PlanLessons, { as: 'planLesson', foreignKey: 'lesson_id' });
|
||||
mdl_TierPlans.hasMany(mdl_PlanLessons, { as: 'planLessons', foreignKey: 'plan_id' });
|
||||
|
||||
// ─── UserTier → Plan ──────────────────────────────────────────────────────────
|
||||
mdl_UserTiers.belongsTo(mdl_TierPlans, { foreignKey: 'plan_id', as: 'plan' });
|
||||
mdl_TierPlans.hasMany(mdl_UserTiers, { foreignKey: 'plan_id', as: 'userTiers' });
|
||||
@@ -62,6 +102,8 @@ module.exports = {
|
||||
mdl_UserTiers,
|
||||
mdl_Payments,
|
||||
mdl_PlanCourses,
|
||||
mdl_PlanUnits,
|
||||
mdl_PlanLessons,
|
||||
mdl_PlanPolicies,
|
||||
mdl_SystemBadges,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user