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>
82 lines
3.0 KiB
JavaScript
82 lines
3.0 KiB
JavaScript
'use strict';
|
|
|
|
// Generalizes `products` from Course-only (`course_id`) to a polymorphic
|
|
// target (`purchasable_type` + `purchasable_id`) so standalone Units and
|
|
// Lessons can also be sold individually, alongside plan-tier gating — see
|
|
// utils/purchasable.util.js for the resolver used by every consumer.
|
|
//
|
|
// purchasable_type is STRING + explicit CHECK, not a real ENUM (CockroachDB
|
|
// can't create a new enum type via addColumn the way createTable can — same
|
|
// caveat as 20270101000076-add-assets-transcode-status.js).
|
|
//
|
|
// Dropping `course_id` also drops its FK (`onDelete: 'CASCADE'` from
|
|
// 20270101000019-create-products.js) — referential integrity across the three
|
|
// possible targets is now enforced at the application layer only, same as
|
|
// course_purchases.product_id already is today.
|
|
//
|
|
// Touches a table with live production rows (real courses have real
|
|
// products/course_purchases). NOT auto-run — test against a local/staging
|
|
// copy of the DB first; do not execute against the shared dev/prod database
|
|
// without explicit confirmation.
|
|
module.exports = {
|
|
async up(queryInterface, Sequelize) {
|
|
await queryInterface.addColumn('products', 'purchasable_type', {
|
|
type: Sequelize.STRING(10),
|
|
allowNull: false,
|
|
defaultValue: 'course',
|
|
});
|
|
|
|
await queryInterface.addConstraint('products', {
|
|
fields: ['purchasable_type'],
|
|
type: 'check',
|
|
name: 'check_products_purchasable_type',
|
|
where: { purchasable_type: { [Sequelize.Op.in]: ['course', 'unit', 'lesson'] } },
|
|
});
|
|
|
|
await queryInterface.addColumn('products', 'purchasable_id', {
|
|
type: Sequelize.BIGINT,
|
|
allowNull: true, // filled by the backfill below, then locked to NOT NULL
|
|
});
|
|
|
|
await queryInterface.sequelize.query(`
|
|
UPDATE products SET purchasable_type = 'course', purchasable_id = course_id;
|
|
`);
|
|
|
|
await queryInterface.changeColumn('products', 'purchasable_id', {
|
|
type: Sequelize.BIGINT,
|
|
allowNull: false,
|
|
});
|
|
|
|
await queryInterface.removeIndex('products', ['course_id']);
|
|
await queryInterface.removeColumn('products', 'course_id');
|
|
|
|
await queryInterface.addIndex('products', ['purchasable_type', 'purchasable_id'], {
|
|
name: 'products_purchasable_type_id',
|
|
});
|
|
},
|
|
|
|
async down(queryInterface, Sequelize) {
|
|
await queryInterface.removeIndex('products', 'products_purchasable_type_id');
|
|
|
|
await queryInterface.addColumn('products', 'course_id', {
|
|
type: Sequelize.BIGINT,
|
|
allowNull: true,
|
|
});
|
|
|
|
await queryInterface.sequelize.query(`
|
|
UPDATE products SET course_id = purchasable_id WHERE purchasable_type = 'course';
|
|
`);
|
|
|
|
await queryInterface.changeColumn('products', 'course_id', {
|
|
type: Sequelize.BIGINT,
|
|
allowNull: false,
|
|
});
|
|
|
|
await queryInterface.addIndex('products', ['course_id']);
|
|
|
|
await queryInterface.removeColumn('products', 'purchasable_id');
|
|
await queryInterface.removeConstraint('products', 'check_products_purchasable_type');
|
|
await queryInterface.removeColumn('products', 'purchasable_type');
|
|
},
|
|
};
|