Refactor Homepage, About, Footer (Controller,Model, View, Data), Update: Dashboard, Delete old file

This commit is contained in:
2026-04-22 15:30:09 +07:00
parent cb5e4b0ceb
commit 3ede974215
67 changed files with 3003 additions and 14780 deletions
+101 -145
View File
@@ -1,169 +1,125 @@
const { addBaseUrlToImages } = require("../utils/imageHelper");
const Footer = require("../models/footer");
const writeAuditLog = require("../audit/writeAuditLog");
const diffObject = require("../audit/diffObject");
const AUDIT_ACTIONS = require("../constants/auditAction");
const { addBaseUrlToImages } = require("../utils/imageHelper");
// GET /api/footer - Public API cho website và CMS load dữ liệu
exports.getFooter = async (req, res) => {
try {
const footer = await Footer.getSingle();
const processedData = addBaseUrlToImages(footer.toObject());
// Helpers
const getFooterDoc = async () => Footer.findOne().sort({ updatedAt: -1 });
const getFooterData = async () => (await getFooterDoc())?.toObject() || {};
res.json(processedData);
} catch (error) {
console.error("Error getting footer:", error);
res.status(500).json({
error: "Failed to get footer data",
});
}
};
const getDefaultFooterData = () => ({
brand: {
logo: {
image: "",
href: "/",
},
description: "",
social: [],
},
explore: {
heading: "",
links: [],
},
contact: {
heading: "",
address: "",
phone: "",
email: "",
},
newsletter: {
heading: "",
description: "",
placeholder: "",
buttonText: "",
},
bottom: {
copyright: "",
links: [],
},
});
// PUT /api/admin/footer - Update toàn bộ footer cho CMS
exports.updateFooter = async (req, res) => {
try {
let updateData = req.body;
console.log("=== FOOTER UPDATE REQUEST RECEIVED ===");
console.log("Raw body:", JSON.stringify(req.body, null, 2));
// Nếu có footerJson, parse nó (tương tự Header logic)
if (updateData.footerJson && typeof updateData.footerJson === "string") {
try {
const parsedData = JSON.parse(updateData.footerJson);
console.log("✓ Parsed footerJson successfully:", parsedData);
updateData = parsedData;
} catch (e) {
console.error("✗ Error parsing footerJson:", e.message);
return res.status(400).json({
success: false,
message: "Invalid JSON in footerJson: " + e.message,
});
}
}
// Lấy footer hiện tại hoặc tạo mới (giống Header logic)
let footer = await Footer.findOne();
if (!footer) {
console.log("No existing footer found, creating new one");
footer = new Footer(updateData);
await footer.save();
console.log("✓ Footer created:", footer._id);
} else {
console.log("✓ Found existing footer:", footer._id);
// Merge với dữ liệu cũ thay vì overwrite (giống Header)
Object.assign(footer, updateData);
await footer.save();
console.log("✓ Footer updated successfully");
}
const processedData = addBaseUrlToImages(footer.toObject());
console.log("Updated footer data:", JSON.stringify(processedData, null, 2));
res.json({
success: true,
message: "Footer updated successfully",
data: processedData,
});
} catch (error) {
console.error("✗ Error updating footer:", error);
res.status(500).json({
success: false,
error: "Failed to update footer: " + error.message,
});
}
};
// Render admin view (giữ lại cho UI hiện tại)
// Admin: render management view with data from MongoDB
exports.index = async (req, res) => {
try {
const data = await Footer.getSingle();
const processedData = addBaseUrlToImages(data.toObject());
let data = await getFooterData();
const defaults = getDefaultFooterData();
res.render("admin/footer/index", {
title: "Footer Management",
data: processedData,
// Merge defaults for any missing sections
const sections = Object.keys(defaults);
sections.forEach((s) => {
data[s] = data[s] || defaults[s];
});
} catch (error) {
console.error("Error in footer index:", error);
req.flash("error_msg", "An error occurred while loading the page");
res.redirect("/admin/dashboard");
return res.render("admin/footer/index", {
layout: "layouts/main",
title: "Footer Management",
data,
currentPath: req.path,
user: req.session.user,
});
} catch (err) {
console.error("Footer index error:", err);
req.flash("error_msg", "Error loading footer data");
return req.session.save(() => res.redirect("/admin/dashboard"));
}
};
// Update method cho form hiện tại (giống Header pattern)
// Admin: parse req.body sections and save to MongoDB
exports.update = async (req, res) => {
try {
let updateData = req.body;
const sections = ["brand", "explore", "contact", "newsletter", "bottom"];
console.log("=== FOOTER FORM UPDATE REQUEST RECEIVED ===");
console.log("Raw body:", JSON.stringify(req.body, null, 2));
let doc = await getFooterDoc();
// Nếu có footerJson, parse nó (giống Header logic)
if (updateData.footerJson && typeof updateData.footerJson === "string") {
try {
const parsedData = JSON.parse(updateData.footerJson);
console.log("✓ Parsed footerJson successfully:", parsedData);
updateData = parsedData;
} catch (e) {
console.error("✗ Error parsing footerJson:", e.message);
req.flash("error_msg", "Invalid JSON in footerJson: " + e.message);
return res.redirect("/admin/footer");
if (!doc) {
doc = new Footer({});
}
let hasChanges = false;
for (const section of sections) {
if (req.body[section]) {
try {
const payload = JSON.parse(req.body[section]);
doc[section] = payload;
doc.markModified(section);
hasChanges = true;
} catch (e) {
console.error(`Invalid JSON for ${section}:`, e.message);
}
}
}
// Lấy footer hiện tại hoặc tạo mới (giống Header)
let footer = await Footer.findOne();
// ✅ Capture BEFORE state
const beforeData = footer
? JSON.parse(JSON.stringify(footer.toObject()))
: {};
if (!footer) {
console.log("No existing footer found, creating new one");
footer = new Footer(updateData);
await footer.save();
console.log("✓ Footer created:", footer._id);
req.flash("success_msg", "Footer created successfully");
} else {
console.log("✓ Found existing footer:", footer._id);
// Merge với dữ liệu cũ (giống Header)
Object.assign(footer, updateData);
await footer.save();
// ✅ Capture AFTER state
const afterData = JSON.parse(JSON.stringify(footer.toObject()));
// ✅ AUDIT LOGGING - Footer Updated
const changes = diffObject(beforeData, afterData);
if (changes.length > 0) {
await writeAuditLog({
model: "Footer",
documentId: footer._id,
action: AUDIT_ACTIONS.UPDATE_FOOTER,
before: beforeData,
after: afterData,
changes,
req,
});
}
console.log("✓ Footer updated successfully");
req.flash("success_msg", "Footer updated successfully");
if (!hasChanges) {
req.flash("info_msg", "No changes were made");
return req.session.save(() => res.redirect("/admin/footer"));
}
const activeTab = req.body.activeTab || "about";
res.redirect(`/admin/footer?activeTab=${activeTab}`);
await doc.save();
req.flash("success_msg", "Footer configuration has been updated!");
return req.session.save(() => res.redirect("/admin/footer"));
} catch (err) {
console.error("✗ Error updating footer:", err);
req.flash("error_msg", err.message || "Error updating footer");
res.redirect("/admin/footer");
console.error("Footer update error:", err);
req.flash("error_msg", `Update error: ${err.message}`);
return req.session.save(() => res.redirect("/admin/footer"));
}
};
// Legacy API endpoints (giữ lại cho tương thích)
exports.api = exports.getFooter;
exports.getFooterData = exports.getFooter;
// Public API: return JSON data for frontend
exports.api = async (req, res) => {
try {
let data = await getFooterData();
if (!data || Object.keys(data).length === 0) {
data = getDefaultFooterData();
}
const baseUrl =
process.env.BACKEND_URL || `${req.protocol}://${req.get("host")}`;
return res.json(addBaseUrlToImages(data, baseUrl));
} catch (err) {
console.error("Footer API error:", err);
return res.status(500).json({ error: "Error loading footer data" });
}
};
// Aliases for routes/index.js compatibility
exports.getFooter = exports.api;
exports.updateFooter = exports.update;