const { addBaseUrlToImages, getFullImageUrl } = require("../utils/imageHelper"); const Home = require("../models/home"); const Blog = require("../models/blog"); // Các hàm hỗ trợ const getHomeDoc = async () => Home.findOne().sort({ updatedAt: -1 }); const getHomeData = async () => (await getHomeDoc())?.toObject() || {}; const getDefaultHomeData = () => ({ hero: { title: "", subtitle: "", description: "", backgroundImage: "", videoUrl: "", primaryButton: {}, secondaryButton: {} }, whyChooseUs: { heading: "", subheading: "", description: "", items: [], features: [], ctaButton: {} }, visaSolutions: { heading: "", subheading: "", items: [] }, visaCountries: { heading: "", subheading: "", description: "", countries: [], ctaButton: {} }, testimonials: { heading: "", subheading: "", videoUrl: "", videoThumbnail: "", items: [] }, videoGallery: { heading: "", videoUrl: "", thumbnail: "" }, faq: { heading: "", subheading: "", description: "", ctaButton: {}, items: [] }, achievements: { heading: "", subheading: "", items: [] }, partners: { visaConsultancy: { items: [] }, brands: { items: [] } }, blogPreview: { heading: "Latest Insights & Updates", subheading: "Visa Tips & Guides", ctaButton: { label: "View All Articles", href: "/blog" }, items: [], selectedBlogIds: [] // Array of manually selected blog IDs }, }); // Admin: Xem trang quản lý exports.index = async (req, res) => { try { let data = await getHomeData(); const defaults = getDefaultHomeData(); // Merge dữ liệu mặc định cho tất cả các phần const sections = Object.keys(defaults); sections.forEach(s => { data[s] = data[s] || defaults[s]; }); const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000"; const backendUrl = process.env.BACKEND_URL || "http://localhost:3001"; // Lấy tất cả blog để chọn trong CMS const allBlogs = await Blog.find({ status: "published" }).sort({ createdAt: -1 }).lean(); return res.render("admin/home/index", { layout: "layouts/main", title: "Home Management", data, allBlogs, frontendUrl, backendUrl, getFullImageUrl, currentPath: req.path, user: req.session.user, }); } catch (err) { console.error("Home index error:", err); req.flash("error_msg", "Error loading home data"); return req.session.save(() => res.redirect("/admin/dashboard")); } }; // Admin: Cập nhật dữ liệu (tập trung vào achievements, partners; các phần khác giữ nguyên nếu không có dữ liệu mới) exports.update = async (req, res) => { try { const sections = [ "hero", "whyChooseUs", "visaSolutions", "visaCountries", "testimonials", "videoGallery", "faq", "achievements", "partners", "blogPreview" ]; let doc = await getHomeDoc(); if (!doc) { doc = new Home({}); } let hasChanges = false; for (const section of sections) { if (req.body[section]) { try { const payload = JSON.parse(req.body[section]); // Gán trực tiếp vào doc, Mongoose sẽ tự check schema doc[section] = payload; doc.markModified(section); hasChanges = true; } catch (e) { console.error(`Invalid JSON for ${section}:`, e); } } } if (!hasChanges) { req.flash("info_msg", "No changes were made"); return req.session.save(() => res.redirect("/admin/home")); } await doc.save(); req.flash("success_msg", "Home page configuration has been updated!"); return req.session.save(() => res.redirect("/admin/home")); } catch (err) { console.error("Home update error:", err); req.flash("error_msg", `Update error: ${err.message}`); return req.session.save(() => res.redirect("/admin/home")); } }; // Public API// API lấy danh sách blog cho CMS exports.apiGetBlogs = async (req, res) => { try { const blogs = await Blog.find({ status: "published" }).sort({ createdAt: -1 }).select("title slug featuredImage author publishedAt").lean(); res.json(blogs); } catch (err) { res.status(500).json({ error: err.message }); } }; exports.api = async (req, res) => { try { let data = await getHomeData(); const baseUrl = process.env.BACKEND_URL || `${req.protocol}://${req.get("host")}`; // === Xử lý Blog Preview động === const blogPreview = data.blogPreview || {}; let blogs = []; // Nếu có chọn blog cụ thể if (blogPreview.selectedBlogIds && blogPreview.selectedBlogIds.length > 0) { blogs = await Blog.find({ _id: { $in: blogPreview.selectedBlogIds }, status: "published" }).lean(); // Sắp xếp theo thứ tự đã chọn trong selectedBlogIds blogs.sort((a, b) => { return blogPreview.selectedBlogIds.indexOf(a._id.toString()) - blogPreview.selectedBlogIds.indexOf(b._id.toString()); }); } // Nếu không chọn hoặc chọn nhưng không đủ, lấy thêm 3 bài mới nhất (hoặc bù vào) if (blogs.length === 0) { blogs = await Blog.find({ status: "published" }) .sort({ createdAt: -1 }) .limit(3) .lean(); } // Map dữ liệu blog sang format mà frontend mong đợi blogPreview.items = blogs.map(blog => ({ title: blog.title, excerpt: blog.excerpt, category: blog.category && blog.category[0] ? blog.category[0] : "Visa", date: blog.publishedAt || blog.createdAt, author: { name: blog.author || "Admin", avatar: "" // Frontend đang tự xử lý hoặc dùng logo hệ thống }, comments: blog.commentsCount || 0, link: `/blog/${blog.slug}`, thumbnail: blog.featuredImage })); data.blogPreview = blogPreview; // =============================== const processed = addBaseUrlToImages(data, baseUrl); return res.json(processed); } catch (err) { console.error("Home API error:", err); return res.status(500).json({ error: "Error loading home data" }); } };