mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
49 lines
2.4 KiB
JavaScript
49 lines
2.4 KiB
JavaScript
'use strict';
|
|
|
|
// Tier Plans v2: item-specific entitlement. Until now a Tier Plan purchase
|
|
// only recorded `user_tiers.plan_id` + tier rank; the actual courses/units/
|
|
// lessons granted were re-resolved LIVE off plan_courses/plan_units/
|
|
// plan_lessons on every access check, so editing a plan's bundle later
|
|
// silently changed what past purchasers could access, and a Unit-only bundle
|
|
// couldn't be distinguished from its parent Course's bundle.
|
|
//
|
|
// This table is the purchase-time snapshot: one row per exact item granted
|
|
// by a given user_tiers purchase. Access checks now look up rows here
|
|
// instead of comparing tier rank against item subscription (see hasItemGrant()
|
|
// in controllers/client/courses.controller.js).
|
|
//
|
|
// item_type is STRING + CHECK, not a real ENUM, mirroring
|
|
// products.purchasable_type (20270101000078) — CockroachDB can't create a
|
|
// new enum type via addColumn the way createTable can.
|
|
module.exports = {
|
|
async up(queryInterface, Sequelize) {
|
|
await queryInterface.createTable('user_tier_grants', {
|
|
id: { type: Sequelize.BIGINT, primaryKey: true, autoIncrement: true },
|
|
user_tier_id: { type: Sequelize.BIGINT, allowNull: false, references: { model: 'user_tiers', key: 'tier_id' }, onDelete: 'CASCADE' },
|
|
user_id: { type: Sequelize.BIGINT, allowNull: false, references: { model: 'users', key: 'user_id' }, onDelete: 'CASCADE' },
|
|
plan_id: { type: Sequelize.BIGINT, allowNull: true, references: { model: 'tier_plans', key: 'plan_id' }, onUpdate: 'CASCADE', onDelete: 'SET NULL' },
|
|
item_type: { type: Sequelize.STRING(10), allowNull: false },
|
|
item_id: { type: Sequelize.BIGINT, allowNull: false },
|
|
granted_at: { type: Sequelize.DATE, allowNull: false, defaultValue: Sequelize.NOW },
|
|
createdAt: { type: Sequelize.DATE, allowNull: false },
|
|
updatedAt: { type: Sequelize.DATE, allowNull: false },
|
|
});
|
|
|
|
await queryInterface.addConstraint('user_tier_grants', {
|
|
fields: ['item_type'],
|
|
type: 'check',
|
|
name: 'check_user_tier_grants_item_type',
|
|
where: { item_type: { [Sequelize.Op.in]: ['course', 'unit', 'lesson'] } },
|
|
});
|
|
|
|
await queryInterface.addIndex('user_tier_grants', ['user_id', 'item_type', 'item_id'], {
|
|
name: 'user_tier_grants_user_item_idx',
|
|
});
|
|
await queryInterface.addIndex('user_tier_grants', ['user_tier_id']);
|
|
},
|
|
|
|
async down(queryInterface) {
|
|
await queryInterface.dropTable('user_tier_grants');
|
|
},
|
|
};
|