require('dotenv').config(); const fs = require('fs').promises; const path = require('path'); const connectDB = require('../config/database'); const Terms = require('../models/terms'); const mongoose = require('mongoose'); /** * Migration: terms * Migrate Terms & Conditions data từ terms-conditions.json * Đã sửa để phù hợp với cấu trúc mới: hero, page, content * Created: 12:00:47 10/12/2025 */ async function migrate() { try { // Kết nối database await connectDB(); console.log('Starting migration: terms...'); // Đọc file terms-conditions.json const termsJsonPath = path.join(__dirname, '../data/terms-conditions.json'); console.log('Reading JSON file from:', termsJsonPath); const termsData = JSON.parse(await fs.readFile(termsJsonPath, 'utf8')); console.log('Terms data loaded successfully'); console.log('Data structure keys:', Object.keys(termsData)); // Kiểm tra cấu trúc và gọi method phù hợp if (termsData.hero && termsData.page && termsData.content) { // Cấu trúc mới - sử dụng migrateFromNewJson console.log('Detected new structure, using migrateFromNewJson...'); if (typeof Terms.migrateFromNewJson === 'function') { await Terms.migrateFromNewJson(termsData); console.log('Migration completed using migrateFromNewJson method'); } else { console.log('migrateFromNewJson not found, using custom logic...'); await migrateTermsData(termsData); } } else if (termsData.hero && termsData.termsHeader && termsData.sections) { // Cấu trúc cũ - sử dụng migrateFromJson console.log('Detected old structure, using migrateFromJson...'); if (typeof Terms.migrateFromJson === 'function') { await Terms.migrateFromJson(termsData); console.log('Migration completed using migrateFromJson method'); } else { await migrateTermsData(termsData); } } else { // Không xác định được cấu trúc console.error('Unknown data structure. Keys:', Object.keys(termsData)); throw new Error('Unknown terms data structure'); } console.log('Terms migration completed successfully!'); await mongoose.disconnect(); process.exit(0); } catch (error) { console.error('Migration error:', error); process.exit(1); } } /** * Custom migration logic cho terms data với cấu trúc mới * Chỉ có 3 phần: hero, page, content */ async function migrateTermsData(termsData) { try { console.log('Starting custom migration logic with new structure...'); // 1. Xóa dữ liệu cũ (tùy chọn) const existingTerms = await Terms.find({}); if (existingTerms.length > 0) { console.log(`Found ${existingTerms.length} existing terms documents`); // Có thể bỏ comment để xóa dữ liệu cũ nếu cần // await Terms.deleteMany({}); // console.log('Cleared existing terms data'); } // 2. Chuyển đổi từ cấu trúc cũ sang cấu trúc mới nếu cần let heroData, pageData, contentData; // Kiểm tra xem data có cấu trúc cũ hay mới if (termsData.hero && termsData.page && termsData.content) { // Đây là cấu trúc mới, sử dụng trực tiếp console.log('Using new structure (hero, page, content)'); heroData = termsData.hero; pageData = termsData.page; contentData = termsData.content; // Debug: kiểm tra content data console.log('contentData keys:', Object.keys(contentData)); console.log('contentData.content exists?', !!contentData.content); console.log('contentData.content length:', contentData.content ? contentData.content.length : 0); if (contentData.content && contentData.content.length > 0) { console.log('First content item type:', contentData.content[0].type); } } else if (termsData.hero && termsData.termsHeader && termsData.sections) { // Đây là cấu trúc cũ, cần chuyển đổi sang cấu trúc mới console.log('Converting from old structure to new structure...'); heroData = termsData.hero; pageData = convertOldPageToNew(termsData); contentData = convertOldSectionsToNew(termsData); } else { throw new Error('Unknown terms data structure'); } // 3. Tạo document mới cho terms const termsDocument = new Terms({ version: '2.0.0', // Tăng version vì cấu trúc thay đổi language: 'en', // Cấu trúc mới chỉ có 3 phần chính hero: { title: heroData.title, backgroundImage: heroData.backgroundImage, sectionClass: heroData.sectionClass || 'uk-section-default uk-section-overlap uk-preserve-color uk-light uk-position-relative', backgroundClasses: heroData.backgroundClasses || 'uk-background-norepeat uk-background-cover uk-background-top-center uk-section uk-section-xlarge', overlayStyle: heroData.overlayStyle || { backgroundColor: 'rgba(0, 0, 0, 0)' }, titleClass: heroData.titleClass || 'text-white text-[5vw] uk-text-center', enableScrollspy: heroData.enableScrollspy !== undefined ? heroData.enableScrollspy : true }, page: { title: pageData.title, divider: pageData.divider !== undefined ? pageData.divider : true, sectionClass: pageData.sectionClass || 'uk-section-default uk-section-overlap uk-section', titleClass: pageData.titleClass || 'text-[2.5vw] text-[#292c3d] uk-text-left@m uk-text-center', dividerClass: pageData.dividerClass || 'uk-divider-small uk-text-left@m uk-text-center' }, content: { sectionClass: contentData.sectionClass || 'uk-section-muted uk-section-overlap uk-section', textClass: contentData.textClass || 'uk-panel uk-margin text-[1vw]', content: contentData.content || [] }, // Metadata createdAt: new Date(), updatedAt: new Date(), isActive: true, migratedFromOldStructure: !termsData.content // Đánh dấu nếu được chuyển từ cấu trúc cũ }); // 4. Lưu vào database await termsDocument.save(); console.log('Terms document saved successfully with new structure'); // 5. Log thông tin console.log(`Created terms document with ID: ${termsDocument._id}`); console.log(`Hero title: ${termsDocument.hero.title}`); console.log(`Page title: ${termsDocument.page.title}`); console.log(`Content items count: ${termsDocument.content.content.length}`); // 6. Tạo thêm bản German nếu có const germanJsonPath = path.join(__dirname, '../data/terms-conditions.de.json'); try { const germanData = JSON.parse(await fs.readFile(germanJsonPath, 'utf8')); // Xác định cấu trúc của German data let germanHero, germanPage, germanContent; if (germanData.hero && germanData.page && germanData.content) { germanHero = germanData.hero; germanPage = germanData.page; germanContent = germanData.content; } else if (germanData.hero && germanData.termsHeader && germanData.sections) { germanHero = germanData.hero; germanPage = convertOldPageToNew(germanData); germanContent = convertOldSectionsToNew(germanData); } const germanTerms = new Terms({ ...termsDocument.toObject(), _id: new mongoose.Types.ObjectId(), // Tạo ID mới language: 'de', hero: { ...termsDocument.hero, title: germanHero.title || termsDocument.hero.title }, page: { ...termsDocument.page, title: germanPage.title || termsDocument.page.title }, content: { ...termsDocument.content, content: germanContent.content || termsDocument.content.content }, isActive: true }); await germanTerms.save(); console.log('German terms document created successfully'); } catch (error) { console.log('German version not found or error:', error.message); console.log('Continuing with English version only...'); } } catch (error) { console.error('Error in migrateTermsData:', error); throw error; } } /** * Chuyển đổi từ cấu trúc page cũ sang cấu trúc page mới */ function convertOldPageToNew(oldData) { return { title: oldData.termsHeader?.title || 'Terms & Conditions', divider: oldData.termsHeader?.divider !== false, sectionClass: oldData.termsHeader?.sectionClass || 'uk-section-default uk-section-overlap uk-section', titleClass: oldData.termsHeader?.titleClass || 'text-[2.5vw] text-[#292c3d] uk-text-left@m uk-text-center', dividerClass: oldData.termsHeader?.dividerClass || 'uk-divider-small uk-text-left@m uk-text-center' }; } /** * Chuyển đổi từ cấu trúc sections cũ sang cấu trúc content mới */ function convertOldSectionsToNew(oldData) { const contentItems = []; // Thêm disclaimer đầu tiên nếu có if (oldData.disclaimer?.text) { contentItems.push({ type: 'paragraph', text: oldData.disclaimer.text }); } // Thêm các sections if (oldData.sections && Array.isArray(oldData.sections)) { oldData.sections.forEach(section => { if (section.title && section.content) { const contentItem = { type: 'section', title: section.title, content: section.content }; // Thêm subsections nếu có if (section.subsections && section.subsections.length > 0) { contentItem.subsections = section.subsections.map(sub => ({ type: 'note', text: sub })); } // Thêm cancellation table nếu có if (section.fees) { contentItem.subsections = contentItem.subsections || []; contentItem.subsections.push({ type: 'cancellation_table', title: 'Standard Cancellation Fees', items: Object.entries(section.fees).map(([key, value]) => `${key}: ${value}`) }); } contentItems.push(contentItem); } }); } // Thêm footer note nếu có if (oldData.footerNote?.text) { contentItems.push({ type: 'paragraph', text: oldData.footerNote.text }); } return { sectionClass: oldData.layout?.termsSectionClass || 'uk-section-muted uk-section-overlap uk-section', textClass: oldData.layout?.textContentClass || 'uk-panel uk-margin text-[1vw]', content: contentItems }; } /** * Hàm backup data trước khi migration */ async function backupExistingData() { try { console.log('Creating backup of existing terms data...'); const existingTerms = await Terms.find({}); if (existingTerms.length > 0) { const backupPath = path.join(__dirname, '../backups/terms-backup-' + Date.now() + '.json'); // Tạo thư mục backup nếu chưa có await fs.mkdir(path.dirname(backupPath), { recursive: true }); await fs.writeFile(backupPath, JSON.stringify(existingTerms, null, 2)); console.log(`Backup created at: ${backupPath}`); } } catch (error) { console.error('Backup error:', error); } } // Chạy migration nếu được gọi trực tiếp if (require.main === module) { migrate(); } module.exports = { migrate, migrateTermsData };