forked from UKSOURCE/cms.hailearning.edu.vn
first commit
This commit is contained in:
37
utils/imageHelper.js
Normal file
37
utils/imageHelper.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Thêm BACKEND_URL vào đường dẫn hình ảnh
|
||||
* @param {Object} data - Dữ liệu cần xử lý
|
||||
* @returns {Object} - Dữ liệu đã được xử lý với đường dẫn hình ảnh đầy đủ
|
||||
*/
|
||||
function addBaseUrlToImages(data, baseUrl) {
|
||||
// baseUrl can be passed explicitly (e.g., from req), otherwise fall back to env
|
||||
const BACKEND_URL = baseUrl || process.env.BACKEND_URL || '';
|
||||
|
||||
// Tạo bản sao sâu để tránh thay đổi dữ liệu gốc
|
||||
const processedData = JSON.parse(JSON.stringify(data));
|
||||
|
||||
// Hàm đệ quy để xử lý tất cả các URL hình ảnh trong đối tượng
|
||||
const processObject = (obj) => {
|
||||
if (!obj || typeof obj !== 'object') return;
|
||||
|
||||
Object.keys(obj).forEach(key => {
|
||||
// Kiểm tra nếu thuộc tính chứa đường dẫn hình ảnh bắt đầu bằng /uploads/
|
||||
if (typeof obj[key] === 'string' && obj[key].startsWith('/uploads/')) {
|
||||
// Thêm BACKEND_URL nếu đường dẫn chưa có http
|
||||
if (!obj[key].startsWith('http')) {
|
||||
obj[key] = `${BACKEND_URL}${obj[key]}`;
|
||||
}
|
||||
} else if (typeof obj[key] === 'object') {
|
||||
// Đệ quy xử lý các đối tượng và mảng lồng nhau
|
||||
processObject(obj[key]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
processObject(processedData);
|
||||
return processedData;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addBaseUrlToImages
|
||||
};
|
||||
40
utils/jsonHelper.js
Normal file
40
utils/jsonHelper.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Đọc dữ liệu từ file JSON
|
||||
* @param {string} fileName - Tên file JSON cần đọc (không cần đuôi .json)
|
||||
* @returns {Object} - Dữ liệu từ file JSON
|
||||
*/
|
||||
function readJsonFile(fileName) {
|
||||
try {
|
||||
const filePath = path.join(__dirname, '../data', `${fileName}.json`);
|
||||
const data = fs.readFileSync(filePath, 'utf8');
|
||||
return JSON.parse(data);
|
||||
} catch (error) {
|
||||
console.error(`Lỗi khi đọc file ${fileName}.json:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ghi dữ liệu vào file JSON
|
||||
* @param {string} fileName - Tên file JSON cần ghi (không cần đuôi .json)
|
||||
* @param {Object} data - Dữ liệu cần ghi vào file
|
||||
* @returns {boolean} - Kết quả ghi file
|
||||
*/
|
||||
function writeJsonFile(fileName, data) {
|
||||
try {
|
||||
const filePath = path.join(__dirname, '../data', `${fileName}.json`);
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`Lỗi khi ghi file ${fileName}.json:`, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readJsonFile,
|
||||
writeJsonFile
|
||||
};
|
||||
168
utils/migrationHelper.js
Normal file
168
utils/migrationHelper.js
Normal file
@@ -0,0 +1,168 @@
|
||||
const Migration = require('../models/migration');
|
||||
|
||||
/**
|
||||
* Kiểm tra xem migration đã chạy chưa
|
||||
* @param {string} migrationName - Tên của migration
|
||||
* @returns {Promise<boolean>} - 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<Object>} - 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<Array>} - 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<Array>} - 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<boolean>} - 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<number>} - 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<number>} - 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<Object>} - 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
|
||||
};
|
||||
|
||||
49
utils/shareLinkHelper.js
Normal file
49
utils/shareLinkHelper.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Prefix FRONTEND_URL to social share links stored as relative paths.
|
||||
* - Deep-clones the input and only modifies values under `social.shareLinks` that
|
||||
* are strings starting with '/'.
|
||||
* - Leaves absolute URLs (http/https) unchanged.
|
||||
* - Normalizes to avoid double slashes between FRONTEND_URL and the path.
|
||||
*
|
||||
* @param {Object} data - object that may contain `social.shareLinks` (e.g., blog object)
|
||||
* @returns {Object} - deep-cloned object with prefixed share links
|
||||
*/
|
||||
function addFrontendUrlToShareLinks(data) {
|
||||
const FRONTEND_URL = (process.env.FRONTEND_URL || '').replace(/\/$/, '');
|
||||
|
||||
if (!data || typeof data !== 'object') return data;
|
||||
|
||||
// Deep clone to avoid mutating original
|
||||
const cloned = JSON.parse(JSON.stringify(data));
|
||||
|
||||
try {
|
||||
if (cloned.social && typeof cloned.social === 'object') {
|
||||
const share = cloned.social.shareLinks;
|
||||
if (share && typeof share === 'object') {
|
||||
Object.keys(share).forEach(key => {
|
||||
const val = share[key];
|
||||
if (typeof val === 'string' && val.length > 0) {
|
||||
// If already absolute URL, leave it
|
||||
if (/^https?:\/\//i.test(val)) return;
|
||||
|
||||
// Only prefix relative paths that start with '/'
|
||||
if (val.startsWith('/')) {
|
||||
// Avoid double slashes
|
||||
share[key] = FRONTEND_URL ? `${FRONTEND_URL}${val}` : val;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// In case of unexpected structure, return cloned original
|
||||
console.error('addFrontendUrlToShareLinks error:', err);
|
||||
return cloned;
|
||||
}
|
||||
|
||||
return cloned;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addFrontendUrlToShareLinks,
|
||||
};
|
||||
Reference in New Issue
Block a user