const { addBaseUrlToImages } = require("../utils/imageHelper"); const { normalizeIconClass } = require("../utils/iconOptions"); const RequestInfo = require("../models/requestInfo"); const writeAuditLog = require("../audit/writeAuditLog"); const diffObject = require("../audit/diffObject"); const AUDIT_ACTIONS = require("../constants/auditAction"); const SLUG = "request-info"; function emptyPageShape() { return { metadata: { title: "", description: "", keywords: "", ogImage: "" }, hero: { badge: "", titleMain: "", titleHighlight: "", description: "", valueProps: [], }, form: { heading: "", description: "", fields: { firstName: { name: "", label: "", placeholder: "", required: true }, lastName: { name: "", label: "", placeholder: "", required: true }, email: { name: "", label: "", placeholder: "", required: true }, phone: { name: "", label: "", placeholder: "", required: false }, program: { name: "", label: "", placeholder: "", required: true, options: [] }, timeline: { name: "", label: "", options: [] }, }, consentText: "", submitLabel: "", footerText: "", successMessage: "", errorMessage: "", }, }; } function parseJsonField(raw) { if (raw == null || raw === "") return null; if (typeof raw === "object") return raw; try { return JSON.parse(raw); } catch { return null; } } /** Chuẩn hoá icon theo whitelist (valueProps). */ function sanitizeIcons(payload) { if (!payload) return payload; if (payload.hero?.valueProps?.length) { payload.hero.valueProps = payload.hero.valueProps.map((vp) => ({ ...vp, icon: normalizeIconClass(vp.icon), })); } return payload; } async function getDocument() { return RequestInfo.findOne({ slug: SLUG }).lean(); } /** * GET /api/request-info — public payload cho frontend LAMS. * Response: { metadata, hero, form } với ảnh đã gắn full URL. */ exports.api = async (req, res) => { try { const doc = await getDocument(); if (!doc) { return res.status(404).json({ error: "Request Info content not found" }); } const baseUrl = process.env.BACKEND_URL ?? `${req.protocol}://${req.get("host")}`; const body = { metadata: doc.metadata || {}, hero: doc.hero || {}, form: doc.form || {}, }; res.json(addBaseUrlToImages(body, baseUrl)); } catch (err) { console.error("requestInfo.api:", err); res.status(500).json({ error: "Error loading Request Info data" }); } }; /** * GET /admin/request-info — Admin view. */ exports.index = async (req, res) => { try { const doc = await getDocument(); const data = doc ? { metadata: doc.metadata || {}, hero: doc.hero || {}, form: doc.form || {}, } : emptyPageShape(); const { ICON_OPTIONS } = require("../utils/iconOptions"); const frontendUrl = process.env.FRONTEND_URL || ""; res.render("admin/request-info/index", { title: "Request Info", layout: "layouts/main", data, iconOptions: ICON_OPTIONS, frontendUrl, currentPath: req.path, user: req.session.user, }); } catch (error) { console.error("requestInfo.index:", error); req.flash("error_msg", "Could not load Request Info page"); res.redirect("/admin/dashboard"); } }; /** * POST /admin/request-info/update — Lưu dữ liệu, tạo audit log. */ exports.update = async (req, res) => { try { const metadata = parseJsonField(req.body.metadata); const hero = parseJsonField(req.body.hero); const form = parseJsonField(req.body.form); if (!metadata || !hero || !form) { req.flash("error_msg", "Invalid form payload. Please try again."); return res.redirect("/admin/request-info"); } const payload = sanitizeIcons({ metadata, hero, form }); let doc = await RequestInfo.findOne({ slug: SLUG }); const beforeData = doc ? JSON.parse(JSON.stringify(doc.toObject())) : {}; if (!doc) { doc = new RequestInfo({ slug: SLUG, ...payload }); } else { doc.set(payload); } await doc.save(); const afterData = JSON.parse(JSON.stringify(doc.toObject())); const changes = diffObject(beforeData, afterData); if (changes.length > 0) { await writeAuditLog({ model: "RequestInfo", documentId: doc._id, action: AUDIT_ACTIONS.UPDATE_REQUEST_INFO, before: beforeData, after: afterData, changes, req, }); } req.flash("success_msg", "Request Info updated successfully"); res.redirect("/admin/request-info"); } catch (err) { console.error("requestInfo.update:", err); req.flash("error_msg", err.message || "Error updating Request Info"); res.redirect("/admin/request-info"); } };