'use strict'; module.exports = { async up(queryInterface, Sequelize) { await queryInterface.createTable('user_bans', { ban_id: { type: Sequelize.BIGINT, primaryKey: true, autoIncrement: true }, user_id: { type: Sequelize.BIGINT, allowNull: false, references: { model: 'users', key: 'user_id' }, onDelete: 'CASCADE' }, banned_by: { type: Sequelize.BIGINT, allowNull: false, references: { model: 'users', key: 'user_id' } }, reason: { type: Sequelize.TEXT, allowNull: false }, ban_type: { type: Sequelize.STRING(20), allowNull: false }, banned_at: { type: Sequelize.DATE, allowNull: false, defaultValue: Sequelize.NOW }, expires_at: { type: Sequelize.DATE, allowNull: true }, is_lifted: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: false }, lifted_at: { type: Sequelize.DATE, allowNull: true }, lifted_by: { type: Sequelize.BIGINT, allowNull: true, references: { model: 'users', key: 'user_id' } }, lift_reason: { type: Sequelize.TEXT, allowNull: true }, createdAt: { type: Sequelize.DATE, allowNull: false, defaultValue: Sequelize.NOW }, updatedAt: { type: Sequelize.DATE, allowNull: false, defaultValue: Sequelize.NOW }, }); await queryInterface.addConstraint('user_bans', { fields: ['ban_type'], type: 'check', name: 'check_ban_type', where: { ban_type: { [Sequelize.Op.in]: ['temporary', 'permanent'] } }, }); await queryInterface.addIndex('user_bans', ['user_id'], { name: 'idx_user_bans_user_id' }); // Partial index — only rows relevant to the ban-expiry cron sweep. await queryInterface.sequelize.query(` CREATE INDEX idx_user_bans_expires_at ON user_bans (expires_at) WHERE is_lifted = false AND ban_type = 'temporary'; `); }, async down(queryInterface) { await queryInterface.dropTable('user_bans'); }, };