forked from UKSOURCE/cms.hailearning.edu.vn
252 lines
7.3 KiB
JavaScript
252 lines
7.3 KiB
JavaScript
const { addBaseUrlToImages } = require("../utils/imageHelper");
|
||
const AboutUs = require("../models/aboutUs");
|
||
const Blog = require("../models/blog");
|
||
const jsonHelper = require("../utils/jsonHelper");
|
||
const writeAuditLog = require("../audit/writeAuditLog");
|
||
const diffObject = require("../audit/diffObject");
|
||
const AUDIT_ACTIONS = require("../constants/auditAction");
|
||
|
||
/**
|
||
* GET /api/about
|
||
* Lấy dữ liệu About Us (Public API cho website và CMS load dữ liệu)
|
||
*/
|
||
exports.getAbout = async (req, res) => {
|
||
try {
|
||
// Force no-cache headers
|
||
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||
res.setHeader("Pragma", "no-cache");
|
||
res.setHeader("Expires", "0");
|
||
|
||
const data = await AboutUs.getSingle();
|
||
const rawData = data.toObject();
|
||
|
||
// === Dynamic Blog News Section ===
|
||
const news = rawData.news || {};
|
||
let blogs = [];
|
||
|
||
// Nếu có chọn blog cụ thể
|
||
if (news.selectedBlogIds && news.selectedBlogIds.length > 0) {
|
||
blogs = await Blog.find({
|
||
_id: { $in: news.selectedBlogIds },
|
||
status: "published",
|
||
}).lean();
|
||
|
||
// Sắp xếp theo thứ tự đã chọn trong selectedBlogIds
|
||
blogs.sort((a, b) => {
|
||
return (
|
||
news.selectedBlogIds.indexOf(a._id.toString()) -
|
||
news.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
|
||
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
|
||
news.items = blogs.map((blog) => ({
|
||
title: blog.title,
|
||
category: blog.category && blog.category[0] ? blog.category[0] : "Visa",
|
||
date:
|
||
blog.publishedAt ||
|
||
new Date(blog.createdAt).toLocaleDateString("en-GB", {
|
||
day: "numeric",
|
||
month: "long",
|
||
year: "numeric",
|
||
}),
|
||
comments: blog.commentsCount || 0,
|
||
author: {
|
||
name: blog.author || "Admin",
|
||
avatar: "/assets/img/home-1/news/client.png", // Default avatar
|
||
},
|
||
link: `/blog/${blog.slug}`,
|
||
thumbnail: blog.featuredImage,
|
||
}));
|
||
|
||
rawData.news = news;
|
||
// ===============================
|
||
|
||
const baseUrl =
|
||
process.env.BACKEND_URL ?? `${req.protocol}://${req.get("host")}`;
|
||
const processedData = addBaseUrlToImages(rawData, baseUrl);
|
||
|
||
res.json(processedData);
|
||
} catch (error) {
|
||
console.error("Error getting about data:", error);
|
||
res.status(500).json({
|
||
success: false,
|
||
error: "Failed to get about data",
|
||
});
|
||
}
|
||
};
|
||
|
||
/**
|
||
* PUT /api/about
|
||
* Cập nhật dữ liệu About Us (Dùng cho AJAX từ CMS)
|
||
*/
|
||
exports.updateAbout = async (req, res) => {
|
||
try {
|
||
let updateData = req.body;
|
||
|
||
// Nếu dữ liệu gửi qua trường aboutJson (dạng string JSON)
|
||
if (updateData.aboutJson && typeof updateData.aboutJson === "string") {
|
||
try {
|
||
updateData = JSON.parse(updateData.aboutJson);
|
||
} catch (e) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
message: "Invalid JSON in aboutJson",
|
||
});
|
||
}
|
||
}
|
||
|
||
const doc = await AboutUs.getSingle();
|
||
|
||
// ✅ Capture BEFORE state
|
||
const beforeData = JSON.parse(JSON.stringify(doc.toObject()));
|
||
|
||
// Use .set() for better handling of nested objects/arrays in Mongoose
|
||
doc.set(updateData);
|
||
await doc.save();
|
||
|
||
// ✅ Capture AFTER state
|
||
const afterData = JSON.parse(JSON.stringify(doc.toObject()));
|
||
|
||
// ✅ AUDIT LOGGING - About Us Updated
|
||
const changes = diffObject(beforeData, afterData);
|
||
if (changes.length > 0) {
|
||
await writeAuditLog({
|
||
model: "AboutUs",
|
||
documentId: doc._id,
|
||
action: AUDIT_ACTIONS.UPDATE_ABOUT_US,
|
||
before: beforeData,
|
||
after: afterData,
|
||
changes,
|
||
req,
|
||
});
|
||
console.log(
|
||
`✅ Audit log created for About Us update: ${changes.length} changes`,
|
||
);
|
||
} else {
|
||
console.log("ℹ️ No changes detected for About Us update");
|
||
}
|
||
|
||
// Fetch fresh data for syncing and returning
|
||
const finalData = await AboutUs.findOne()
|
||
.select("-_id -__v -createdAt -updatedAt")
|
||
.lean();
|
||
|
||
// Update about.json file to keep it in sync
|
||
jsonHelper.writeJsonFile("about", finalData);
|
||
|
||
res.json({
|
||
success: true,
|
||
message: "About Us updated successfully",
|
||
data: finalData,
|
||
});
|
||
} catch (error) {
|
||
console.error("Error updating about data:", error);
|
||
res.status(500).json({
|
||
success: false,
|
||
error: "Failed to update about data: " + error.message,
|
||
});
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Render admin page (Dùng cho Admin UI)
|
||
*/
|
||
exports.index = async (req, res) => {
|
||
try {
|
||
const data = await AboutUs.getSingle();
|
||
const rawData = data.toObject();
|
||
|
||
// Lấy tất cả blog để chọn trong CMS
|
||
const allBlogs = await Blog.find({ status: "published" })
|
||
.sort({ createdAt: -1 })
|
||
.lean();
|
||
|
||
const activeTab = req.query.activeTab || "hero";
|
||
res.render("admin/aboutUs/index", {
|
||
layout: "layouts/main",
|
||
title: "About Us Management",
|
||
data: rawData,
|
||
allBlogs,
|
||
activeTab,
|
||
user: req.session.user,
|
||
currentPath: req.path,
|
||
frontendUrl: process.env.FRONTEND_URL || "http://localhost:3000",
|
||
backendUrl: process.env.BACKEND_URL || "http://localhost:3001",
|
||
});
|
||
} catch (err) {
|
||
console.error("Error in about index:", err);
|
||
req.flash("error_msg", "Error loading About Us page");
|
||
res.redirect("/admin/dashboard");
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Update method cho form-based submission (Admin UI - Post fallback)
|
||
*/
|
||
exports.update = async (req, res) => {
|
||
try {
|
||
let updateData = req.body;
|
||
if (updateData.aboutJson && typeof updateData.aboutJson === "string") {
|
||
try {
|
||
updateData = JSON.parse(updateData.aboutJson);
|
||
} catch (e) {
|
||
req.flash("error_msg", "Invalid JSON data");
|
||
return res.redirect("/admin/about-us");
|
||
}
|
||
}
|
||
|
||
const doc = await AboutUs.getSingle();
|
||
|
||
// ✅ Capture BEFORE state
|
||
const beforeData = JSON.parse(JSON.stringify(doc.toObject()));
|
||
|
||
doc.set(updateData);
|
||
await doc.save();
|
||
|
||
// ✅ Capture AFTER state
|
||
const afterData = JSON.parse(JSON.stringify(doc.toObject()));
|
||
|
||
// ✅ AUDIT LOGGING - About Us Updated
|
||
const changes = diffObject(beforeData, afterData);
|
||
if (changes.length > 0) {
|
||
await writeAuditLog({
|
||
model: "AboutUs",
|
||
documentId: doc._id,
|
||
action: AUDIT_ACTIONS.UPDATE_ABOUT_US,
|
||
before: beforeData,
|
||
after: afterData,
|
||
changes,
|
||
req,
|
||
});
|
||
}
|
||
|
||
const finalData = await AboutUs.findOne()
|
||
.select("-_id -__v -createdAt -updatedAt")
|
||
.lean();
|
||
jsonHelper.writeJsonFile("about", finalData);
|
||
|
||
req.flash("success_msg", "About Us updated successfully");
|
||
const activeTab = req.query.activeTab || "hero";
|
||
res.redirect(`/admin/about-us?activeTab=${activeTab}`);
|
||
} catch (err) {
|
||
console.error("Update error:", err);
|
||
req.flash("error_msg", "Error updating About Us: " + err.message);
|
||
res.redirect("/admin/about-us");
|
||
}
|
||
};
|
||
|
||
// Aliases for compatibility
|
||
exports.api = exports.getAbout;
|
||
exports.page = exports.getAbout;
|
||
exports.updateAboutUs = exports.updateAbout;
|