const mongoose = require("mongoose"); const connectDB = require("../config/database"); const AboutUs = require("../models/aboutUs"); const jsonHelper = require("../utils/jsonHelper"); const GENERIC_PLACEHOLDER = "7281.jpg"; const ABOUT_DEFAULTS = { heroBackground: "/uploads/about/breadcrumb.jpg", featuresBackground: "/assets/img/home-3/choose-us/pricing-bg.jpg", featuresImage: "/uploads/about/businessman.jpg", missionIcons: [ "/assets/img/home-3/choose-us/icon-1.png", "/assets/img/home-3/choose-us/icon-2.png", "/assets/img/home-3/choose-us/icon-3.png", ], featureIcons: [ "/assets/img/home-3/choose-us/icon-1.png", "/assets/img/home-3/choose-us/icon-2.png", "/assets/img/home-3/choose-us/icon-3.png", ], }; const trimString = (value) => typeof value === "string" ? value.trim() : ""; const normalizePath = (value) => { const trimmed = trimString(value); if (!trimmed) { return ""; } if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) { return trimmed; } return trimmed.startsWith("/") ? trimmed : `/${trimmed}`; }; const isPlaceholderLike = (value) => { const normalized = normalizePath(value).toLowerCase(); return !normalized || normalized.endsWith(`/${GENERIC_PLACEHOLDER}`); }; const normalizeButton = (value = {}) => ({ label: trimString(value?.label), href: trimString(value?.href), }); const repairIconItems = (items, fallbacks) => Array.isArray(items) ? items.map((item, index) => ({ ...item, icon: isPlaceholderLike(item?.icon) ? fallbacks[index % fallbacks.length] : normalizePath(item?.icon), })) : []; const buildCanonicalAboutData = (source = {}) => ({ hero: { title: trimString(source?.hero?.title), breadcrumb: Array.isArray(source?.hero?.breadcrumb) ? source.hero.breadcrumb.map(trimString).filter(Boolean) : [], backgroundImage: isPlaceholderLike(source?.hero?.backgroundImage) ? ABOUT_DEFAULTS.heroBackground : normalizePath(source?.hero?.backgroundImage), }, intro: { subheading: trimString(source?.intro?.subheading), heading: trimString(source?.intro?.heading), description: trimString(source?.intro?.description), image: normalizePath(source?.intro?.image), }, mission: { subheading: trimString(source?.mission?.subheading), heading: trimString(source?.mission?.heading), description: trimString(source?.mission?.description), images: { main: normalizePath(source?.mission?.images?.main), secondary: normalizePath(source?.mission?.images?.secondary), }, items: repairIconItems(source?.mission?.items, ABOUT_DEFAULTS.missionIcons) .map((item) => ({ icon: item.icon, label: trimString(item?.label), description: trimString(item?.description), })) .filter((item) => item.icon || item.label || item.description), features: Array.isArray(source?.mission?.features) ? source.mission.features.map(trimString).filter(Boolean) : [], ctaButton: normalizeButton(source?.mission?.ctaButton), }, features: { backgroundImage: isPlaceholderLike(source?.features?.backgroundImage) ? ABOUT_DEFAULTS.featuresBackground : normalizePath(source?.features?.backgroundImage), subheading: trimString(source?.features?.subheading), heading: trimString(source?.features?.heading), description: trimString(source?.features?.description), image: isPlaceholderLike(source?.features?.image) ? ABOUT_DEFAULTS.featuresImage : normalizePath(source?.features?.image), items: repairIconItems(source?.features?.items, ABOUT_DEFAULTS.featureIcons) .map((item) => ({ icon: item.icon, title: trimString(item?.title), description: trimString(item?.description), })) .filter((item) => item.icon || item.title || item.description), ctaButton: normalizeButton(source?.features?.ctaButton), }, news: { subheading: trimString(source?.news?.subheading), heading: trimString(source?.news?.heading), ctaButton: normalizeButton(source?.news?.ctaButton), selectedBlogIds: Array.isArray(source?.news?.selectedBlogIds) ? source.news.selectedBlogIds.filter(Boolean) : [], items: [], }, }); async function up() { await connectDB(); try { const doc = await AboutUs.getSingle(); const repaired = buildCanonicalAboutData(doc.toObject()); doc.set(repaired); await doc.save(); jsonHelper.writeJsonFile("about", repaired); console.log("✓ Repaired About CMS contract"); console.log(` - Database: ${mongoose.connection.db.databaseName}`); console.log(" - Canonicalized About singleton fields"); console.log(" - Backfilled hero/features/images/icons when placeholder-like"); } catch (error) { console.error("✗ Failed to repair About contract:", error); throw error; } finally { await mongoose.disconnect(); } } async function down() { console.log( "ℹ Rollback skipped for 2026_04_10_210000_repair_about_contract because the migration normalizes live content in place.", ); } if (require.main === module) { up() .then(() => { console.log("\n✓ Migration script completed"); process.exit(0); }) .catch((error) => { console.error("\n✗ Migration script failed:", error); process.exit(1); }); } module.exports = { up, down };