forked from UKSOURCE/cms.hailearning.edu.vn
190 lines
5.6 KiB
JavaScript
190 lines
5.6 KiB
JavaScript
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();
|
|
|