Files
uldp-degree-mangement-system/scripts/2026_02_03_645124_visa.js
2026-02-04 09:17:57 +07:00

337 lines
10 KiB
JavaScript

// scripts/migrateVisa.js
require("dotenv").config();
const fs = require("fs").promises;
const path = require("path");
const mongoose = require("mongoose");
const Visa = require("../models/visa");
// 1. Đọc file JSON
async function loadVisaData() {
const filePath = path.join(__dirname, "..", "data", "visa.json");
const raw = await fs.readFile(filePath, "utf8");
return JSON.parse(raw);
}
// 2. Hàm Transform: Đổ dữ liệu từ JSON vào đúng Schema
function transformVisa(sourceData) {
// JSON có structure hero.title và hero.summaryList
return {
hero: {
title: sourceData.hero?.title || "Visa",
summaryList: Array.isArray(sourceData.hero?.summaryList)
? sourceData.hero.summaryList.map((country) =>
transformCountry(country),
)
: [],
},
updatedAt: new Date(),
};
}
// Helper function: Transform individual country
function transformCountry(source) {
return {
id: source.id || 0,
name: source.name || "",
slug: source.slug || "",
icon: source.icon || "",
services: Array.isArray(source.services) ? source.services : [],
detailedView: source.detailedView
? transformDetailedView(source.detailedView)
: null,
};
}
// Helper function: Transform DetailedView
function transformDetailedView(source) {
return {
activeCountry: source.activeCountry
? transformActiveCountry(source.activeCountry)
: null,
relatedCountries: Array.isArray(source.relatedCountries)
? source.relatedCountries.map((country) => ({
id: country.id || 0,
name: country.name || "",
icon: country.icon || "",
}))
: [],
contactInfo: source.contactInfo
? transformContactInfo(source.contactInfo)
: null,
};
}
// Helper function: Transform ActiveCountry
function transformActiveCountry(source) {
return {
id: source.id || 0,
name: source.name || "",
title: source.title || "",
mainImage: source.mainImage || "",
description: source.description || "",
additionalInfo: source.additionalInfo || "",
tagline: source.tagline || "",
visaTypes: Array.isArray(source.visaTypes)
? source.visaTypes.map((type) => ({
category: type.category || "",
items: Array.isArray(type.items)
? type.items.map((item) => ({
title: item.title || "",
description: item.description || "",
}))
: [],
}))
: [],
visaProcess: source.visaProcess
? transformVisaProcess(source.visaProcess)
: null,
gallery: Array.isArray(source.gallery) ? source.gallery : [],
visaCategories: source.visaCategories
? transformVisaCategories(source.visaCategories)
: null,
visaService: source.visaService
? transformVisaService(source.visaService)
: null,
};
}
// Helper function: Transform VisaProcess
function transformVisaProcess(source) {
return {
title: source.title || "",
steps: Array.isArray(source.steps)
? source.steps.map((step) => ({
number: step.number || "",
title: step.title || "",
description: step.description || "",
}))
: [],
};
}
// Helper function: Transform VisaCategories
function transformVisaCategories(source) {
return {
title: source.title || "",
steps: Array.isArray(source.steps) ? source.steps : [],
};
}
// Helper function: Transform VisaService
function transformVisaService(source) {
return {
title: source.title || "",
steps: Array.isArray(source.steps)
? source.steps.map((step) => ({
number: step.number || "",
title: step.title || "",
description: step.description || "",
}))
: [],
};
}
// Helper function: Transform ContactInfo
function transformContactInfo(source) {
return {
img: source.img || "",
sectionTitle: source.sectionTitle || "Visa & Immigration",
helpText: source.helpText || "Need Help?",
phone: {
label: source.phone?.label || "Call Us",
value: source.phone?.value || "",
link: source.phone?.link || "",
},
email: {
label: source.email?.label || "Mail Us",
value: source.email?.value || "",
link: source.email?.link || "",
},
location: {
label: source.location?.label || "Location",
address: source.location?.address || "",
},
};
}
// 3. Validate data before migration
function validateVisaData(visaData) {
const errors = [];
if (!visaData.hero) {
errors.push("Missing hero section");
}
if (!visaData.hero?.title) {
console.warn("⚠️ Hero title is missing, using default 'Visa'");
}
if (!Array.isArray(visaData.hero?.summaryList)) {
errors.push("summaryList must be an array");
} else if (visaData.hero.summaryList.length === 0) {
errors.push("summaryList is empty");
} else {
// Validate each country
visaData.hero.summaryList.forEach((country, idx) => {
if (!country.name || !country.slug) {
errors.push(`Country at index ${idx}: missing name or slug`);
}
if (country.detailedView) {
if (!country.detailedView.activeCountry) {
console.warn(
`⚠️ Country "${country.name}" (${idx}): missing activeCountry details`,
);
}
if (!Array.isArray(country.detailedView.relatedCountries)) {
errors.push(
`Country "${country.name}" (${idx}): relatedCountries must be array`,
);
}
}
});
}
return errors;
}
// Helper function: Get data summary
function getDataSummary(visaData) {
const summary = {
heroTitle: visaData.hero?.title || "N/A",
totalCountries: visaData.hero?.summaryList?.length || 0,
withDetails: 0,
withoutDetails: 0,
byCountry: [],
};
if (visaData.hero?.summaryList) {
visaData.hero.summaryList.forEach((country) => {
const hasDetails = !!country.detailedView?.activeCountry;
const relatedCount = country.detailedView?.relatedCountries?.length || 0;
if (hasDetails) {
summary.withDetails++;
} else {
summary.withoutDetails++;
}
summary.byCountry.push({
name: country.name,
slug: country.slug,
hasDetails,
relatedCountries: relatedCount,
services: country.services?.length || 0,
});
});
}
return summary;
}
// 4. Chạy Migration
async function migrate() {
try {
// Kết nối DB
console.log("🔗 Connecting to MongoDB...");
await mongoose.connect(process.env.MONGODB_URI);
console.log("✅ Connected to MongoDB\n");
// A. Lấy dữ liệu thô
console.log("📖 Loading visa data from JSON...");
const rawData = await loadVisaData();
console.log("✅ JSON data loaded\n");
// B. Chuẩn hóa dữ liệu theo Schema
console.log("🔄 Transforming data structure...");
const visaData = transformVisa(rawData);
console.log("✅ Data transformation completed\n");
// C. Validate dữ liệu
console.log("✔️ Validating data structure...");
const errors = validateVisaData(visaData);
if (errors.length > 0) {
console.error("❌ Validation errors found:");
errors.forEach((err, idx) => console.error(` ${idx + 1}. ${err}`));
process.exit(1);
}
console.log("✅ Data validation passed\n");
// D. Get summary
const summary = getDataSummary(visaData);
console.log("📊 Migration Summary:");
console.log(` Hero Title: "${summary.heroTitle}"`);
console.log(` Total countries: ${summary.totalCountries}`);
console.log(` With details: ${summary.withDetails}`);
console.log(` Without details: ${summary.withoutDetails}`);
console.log(`\n Country Details:`);
summary.byCountry.forEach((country) => {
const detailBadge = country.hasDetails ? "✅" : "❌";
const detailText = country.hasDetails
? `(${country.relatedCountries} related)`
: "(basic only)";
console.log(
` ${detailBadge} ${country.name.padEnd(20)} (${country.slug.padEnd(
12,
)}) - ${country.services} services ${detailText}`,
);
});
console.log("");
// E. Lưu vào DB (Upsert: Có rồi thì update, chưa có thì tạo)
const existingDoc = await Visa.findOne().sort({ updatedAt: -1 });
if (existingDoc) {
console.log("📝 Updating existing Visa document...");
console.log(` Document ID: ${existingDoc._id}`);
const updated = await Visa.findByIdAndUpdate(
existingDoc._id,
{ $set: visaData },
{ new: true },
);
console.log("✅ Visa document updated successfully");
console.log(` Updated at: ${updated.updatedAt}`);
} else {
console.log("📝 Creating NEW Visa document...");
const newDoc = await Visa.create(visaData);
console.log("✅ Visa document created successfully");
console.log(` Document ID: ${newDoc._id}`);
console.log(` Created at: ${newDoc.createdAt}`);
}
console.log("\n✨ Visa migration completed successfully!");
} catch (error) {
console.error("\n❌ Migration failed:");
console.error(` Error: ${error.message}`);
if (error.name === "ValidationError") {
console.error("\n Validation Errors:");
Object.keys(error.errors).forEach((field) => {
console.error(` - ${field}: ${error.errors[field].message}`);
});
}
if (error.stack) {
console.error("\n📋 Stack trace:");
console.error(error.stack);
}
process.exit(1);
} finally {
await mongoose.connection.close();
console.log("\n🔌 MongoDB connection closed");
process.exit(0);
}
}
// Run migration
console.log("🚀 Starting Visa Migration...\n");
migrate();