require('dotenv').config(); const path = require("path"); const fs = require("fs"); const { execSync } = require("child_process"); const connectDB = require("../config/database"); const migrationHelper = require("../utils/migrationHelper"); /** * Tự động phát hiện tất cả các file script trong thư mục scripts * Loại trừ các file quản lý migration và file không phải .js */ function discoverMigrations() { const scriptsDir = __dirname; const files = fs.readdirSync(scriptsDir); // Danh sách các file quản lý migration cần loại trừ const excludeFiles = [ 'migrate-all.js', 'migrate-status.js', 'migrate-rollback.js', 'migrate-fresh.js', 'make-migration.js', 'MIGRATION_README.md' ]; const migrations = files .filter(file => { // Lấy tất cả file .js, trừ các file quản lý return file.endsWith('.js') && !excludeFiles.includes(file); }) .map(file => { // Tạo tên migration từ tên file (bỏ .js) const name = file.replace('.js', ''); return { name: name, script: file }; }) .sort((a, b) => { // Sắp xếp theo tên để đảm bảo thứ tự nhất quán return a.name.localeCompare(b.name); }); return migrations; } /** * Chạy migration script (suppress output) * Sử dụng child_process để chạy script độc lập vì các script tự quản lý DB connection * Output từ script sẽ bị suppress để chỉ hiển thị status */ async function runMigrationScript(migration) { return new Promise((resolve, reject) => { try { // Chạy script bằng child_process với stdio: 'pipe' để suppress output // Nhưng vẫn capture stderr để có thể hiển thị lỗi nếu cần const result = execSync(`node scripts/${migration.script}`, { stdio: ['ignore', 'pipe', 'pipe'], // stdin: ignore, stdout: pipe, stderr: pipe cwd: path.join(__dirname, ".."), encoding: 'utf8' }); resolve(); } catch (error) { // Attach stderr vào error để có thể hiển thị sau if (error.stderr) { error.stderr = error.stderr; } reject(error); } }); } /** * Hiển thị bảng kết quả migration đơn giản */ function displayResults(results) { console.log("\nRunning migrations...\n"); // Tìm độ dài tên migration dài nhất để format bảng const maxNameLength = Math.max(...results.map(r => r.name.length), 20); const statusWidth = 10; const totalWidth = maxNameLength + statusWidth + 7; // 7 = spaces and separators // Header console.log("=".repeat(totalWidth)); console.log(`${'Migration'.padEnd(maxNameLength)} | ${'Status'.padEnd(statusWidth)}`); console.log("=".repeat(totalWidth)); // Rows results.forEach(result => { let statusText = ""; if (result.status === 'DONE') { statusText = "DONE".padEnd(statusWidth); } else if (result.status === 'FAIL') { statusText = "FAIL".padEnd(statusWidth); } console.log(`${result.name.padEnd(maxNameLength)} | ${statusText}`); }); // Footer console.log("=".repeat(totalWidth)); // Summary const doneCount = results.filter(r => r.status === 'DONE').length; const failCount = results.filter(r => r.status === 'FAIL').length; console.log(""); if (doneCount > 0) { console.log(`${doneCount} migration(s) completed`); } if (failCount > 0) { console.log(`${failCount} migration(s) failed`); } console.log(""); } /** * Hàm chính để chạy lại tất cả migrations từ đầu (fresh) * Xóa tất cả tracking và chạy lại từ đầu */ async function runFreshMigrations() { const mongoose = require('mongoose'); let ownConn = false; try { const wasConnected = mongoose.connection.readyState === 1; await connectDB(); if (!wasConnected) ownConn = true; // Tự động phát hiện migrations const migrations = discoverMigrations(); // Xóa tất cả tracking migrations const Migration = require('../models/migration'); await Migration.deleteMany({}); const batch = 1; // Batch mới bắt đầu từ 1 const results = []; // Chạy từng migration for (let i = 0; i < migrations.length; i++) { const migration = migrations[i]; try { // Chạy migration script (output bị suppress) await runMigrationScript(migration); if (mongoose.connection.readyState !== 1) { await connectDB(); } await migrationHelper.markAsRun(migration.name, batch); results.push({ name: migration.name, status: 'DONE' }); } catch (error) { results.push({ name: migration.name, status: 'FAIL', error: error.message }); // Hiển thị bảng kết quả trước khi exit displayResults(results); console.error(`\n❌ Migration "${migration.name}" failed: ${error.message}`); if (error.stderr) { console.error(error.stderr.toString()); } if (ownConn && mongoose.connection.readyState === 1) { await mongoose.disconnect(); } process.exit(1); } } // Hiển thị bảng kết quả displayResults(results); if (ownConn && mongoose.connection.readyState === 1) { await mongoose.disconnect(); } process.exit(0); } catch (error) { console.error("\n❌ Error:", error.message); if (ownConn && mongoose.connection.readyState === 1) { await mongoose.disconnect(); } process.exit(1); } } // Chạy hàm chính runFreshMigrations();