const Migration = require('../models/migration'); /** * Kiểm tra xem migration đã chạy chưa * @param {string} migrationName - Tên của migration * @returns {Promise} - true nếu đã chạy, false nếu chưa */ async function hasRun(migrationName) { try { const migration = await Migration.findOne({ name: migrationName }); return !!migration; } catch (error) { console.error(`Error checking migration ${migrationName}:`, error); throw error; } } /** * Đánh dấu migration đã chạy * @param {string} migrationName - Tên của migration * @param {number} batch - Số batch (mặc định là batch hiện tại + 1) * @returns {Promise} - Migration document đã tạo */ async function markAsRun(migrationName, batch = null) { try { // Nếu không có batch, lấy batch cao nhất + 1 if (batch === null) { const lastBatch = await Migration.findOne().sort({ batch: -1 }); batch = lastBatch ? lastBatch.batch + 1 : 1; } const migration = await Migration.create({ name: migrationName, batch: batch, ranAt: new Date() }); return migration; } catch (error) { // Nếu migration đã tồn tại (unique constraint), trả về migration hiện có if (error.code === 11000) { return await Migration.findOne({ name: migrationName }); } console.error(`Error marking migration ${migrationName} as run:`, error); throw error; } } /** * Lấy danh sách tất cả các migration đã chạy * @returns {Promise} - Danh sách các migration đã chạy */ async function getRanMigrations() { try { const migrations = await Migration.find().sort({ batch: 1, ranAt: 1 }); return migrations; } catch (error) { console.error('Error getting ran migrations:', error); throw error; } } /** * Lấy danh sách các migration đã chạy theo batch * @param {number} batch - Số batch * @returns {Promise} - Danh sách các migration trong batch */ async function getMigrationsByBatch(batch) { try { const migrations = await Migration.find({ batch }).sort({ ranAt: 1 }); return migrations; } catch (error) { console.error(`Error getting migrations for batch ${batch}:`, error); throw error; } } /** * Xóa migration khỏi bảng tracking (rollback) * @param {string} migrationName - Tên của migration * @returns {Promise} - true nếu xóa thành công */ async function rollback(migrationName) { try { const result = await Migration.deleteOne({ name: migrationName }); return result.deletedCount > 0; } catch (error) { console.error(`Error rolling back migration ${migrationName}:`, error); throw error; } } /** * Xóa tất cả migration trong một batch (rollback batch) * @param {number} batch - Số batch cần rollback * @returns {Promise} - Số lượng migration đã xóa */ async function rollbackBatch(batch) { try { const result = await Migration.deleteMany({ batch }); return result.deletedCount; } catch (error) { console.error(`Error rolling back batch ${batch}:`, error); throw error; } } /** * Lấy batch số cao nhất * @returns {Promise} - Batch số cao nhất */ async function getLastBatch() { try { const lastBatch = await Migration.findOne().sort({ batch: -1 }); return lastBatch ? lastBatch.batch : 0; } catch (error) { console.error('Error getting last batch:', error); throw error; } } /** * Chạy migration với tracking tự động * @param {string} migrationName - Tên của migration * @param {Function} migrationFunction - Hàm migration cần chạy * @returns {Promise} - Kết quả migration */ async function runMigration(migrationName, migrationFunction) { try { // Kiểm tra xem đã chạy chưa if (await hasRun(migrationName)) { console.log(`⏭️ Migration ${migrationName} đã chạy, bỏ qua...`); return { skipped: true, name: migrationName }; } console.log(`🔄 Đang chạy migration: ${migrationName}...`); // Chạy migration function await migrationFunction(); // Đánh dấu đã chạy const migration = await markAsRun(migrationName); console.log(`✅ Migration ${migrationName} đã chạy thành công! (Batch: ${migration.batch})`); return { success: true, name: migrationName, batch: migration.batch, ranAt: migration.ranAt }; } catch (error) { console.error(`❌ Lỗi khi chạy migration ${migrationName}:`, error); throw error; } } module.exports = { hasRun, markAsRun, getRanMigrations, getMigrationsByBatch, rollback, rollbackBatch, getLastBatch, runMigration };