forked from UKSOURCE/cms.hailearning.edu.vn
feat: standardize admin form limits and guidance
This commit is contained in:
@@ -2,10 +2,33 @@ const { addBaseUrlToImages } = require("../utils/imageHelper");
|
||||
const AboutUs = require("../models/aboutUs");
|
||||
const Blog = require("../models/blog");
|
||||
const jsonHelper = require("../utils/jsonHelper");
|
||||
const {
|
||||
validateLengthRules,
|
||||
summarizeLengthErrors,
|
||||
} = require("../utils/lengthValidation");
|
||||
const {
|
||||
ABOUT_US_LENGTH_RULES,
|
||||
} = require("../constants/contentLengthRules");
|
||||
const writeAuditLog = require("../audit/writeAuditLog");
|
||||
const diffObject = require("../audit/diffObject");
|
||||
const AUDIT_ACTIONS = require("../constants/auditAction");
|
||||
|
||||
const handleLengthValidation = (validation, req, res, options = {}) => {
|
||||
const message =
|
||||
summarizeLengthErrors(validation, 3) || "One or more fields exceed the allowed length.";
|
||||
|
||||
if (options.json) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: message,
|
||||
errors: validation.errors,
|
||||
});
|
||||
}
|
||||
|
||||
req.flash("error_msg", message);
|
||||
return res.redirect(options.redirectTo || "/admin/about-us");
|
||||
};
|
||||
|
||||
/**
|
||||
* GET /api/about
|
||||
* Lấy dữ liệu About Us (Public API cho website và CMS load dữ liệu)
|
||||
@@ -110,6 +133,11 @@ exports.updateAbout = async (req, res) => {
|
||||
// ✅ Capture BEFORE state
|
||||
const beforeData = JSON.parse(JSON.stringify(doc.toObject()));
|
||||
|
||||
const validation = validateLengthRules(updateData, ABOUT_US_LENGTH_RULES);
|
||||
if (!validation.valid) {
|
||||
return handleLengthValidation(validation, req, res, { json: true });
|
||||
}
|
||||
|
||||
// Use .set() for better handling of nested objects/arrays in Mongoose
|
||||
doc.set(updateData);
|
||||
await doc.save();
|
||||
@@ -210,6 +238,13 @@ exports.update = async (req, res) => {
|
||||
// ✅ Capture BEFORE state
|
||||
const beforeData = JSON.parse(JSON.stringify(doc.toObject()));
|
||||
|
||||
const validation = validateLengthRules(updateData, ABOUT_US_LENGTH_RULES);
|
||||
if (!validation.valid) {
|
||||
return handleLengthValidation(validation, req, res, {
|
||||
redirectTo: `/admin/about-us?activeTab=${req.query.activeTab || "hero"}`,
|
||||
});
|
||||
}
|
||||
|
||||
doc.set(updateData);
|
||||
await doc.save();
|
||||
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
const {addBaseUrlToImages} = require("../utils/imageHelper");
|
||||
const Activity = require("../models/activity");
|
||||
const mongoose = require('mongoose');
|
||||
const {
|
||||
validateLengthRules,
|
||||
summarizeLengthErrors,
|
||||
} = require("../utils/lengthValidation");
|
||||
const {
|
||||
ACTIVITY_LENGTH_RULES,
|
||||
} = require("../constants/contentLengthRules");
|
||||
|
||||
const getActivityLengthMessage = (validation) =>
|
||||
summarizeLengthErrors(validation, 3) ||
|
||||
"One or more fields exceed the allowed length.";
|
||||
|
||||
const redirectWithLengthError = (req, res, path, validation) => {
|
||||
req.flash("error_msg", getActivityLengthMessage(validation));
|
||||
return req.session.save(() => res.redirect(path));
|
||||
};
|
||||
|
||||
// -------------------- Public (API) exports --------------------
|
||||
|
||||
@@ -302,6 +318,15 @@ exports.updateFilters = async (req, res) => {
|
||||
try {
|
||||
// Provide minimal valid fields when inserting a new filters document so
|
||||
// schema validators (e.g., age validator) do not fail on upsert.
|
||||
const filterLengthValidation = validateLengthRules(
|
||||
{ filters: sanitizedFilters },
|
||||
ACTIVITY_LENGTH_RULES,
|
||||
);
|
||||
if (!filterLengthValidation.valid) {
|
||||
req.flash("error_msg", getActivityLengthMessage(filterLengthValidation));
|
||||
return res.redirect("/admin/activity");
|
||||
}
|
||||
|
||||
const setOnInsert = {
|
||||
name: "_filters_doc",
|
||||
price: 0,
|
||||
@@ -353,6 +378,14 @@ exports.updateHero = async (req, res) => {
|
||||
bannerImageBooking: bannerImageBooking || '/uploads/banner/b9.jpg',
|
||||
};
|
||||
|
||||
const heroLengthValidation = validateLengthRules(
|
||||
{ hero },
|
||||
ACTIVITY_LENGTH_RULES,
|
||||
);
|
||||
if (!heroLengthValidation.valid) {
|
||||
return redirectWithLengthError(req, res, "/admin/activity", heroLengthValidation);
|
||||
}
|
||||
|
||||
// Update all activity docs to keep hero consistent
|
||||
await Activity.updateMany({ isFiltersDoc: { $ne: true } }, { $set: { hero } });
|
||||
|
||||
@@ -413,6 +446,16 @@ exports.create = async (req, res) => {
|
||||
try {
|
||||
const activityData = parseActivityFormData(req.body);
|
||||
|
||||
const lengthValidation = validateLengthRules(activityData, ACTIVITY_LENGTH_RULES);
|
||||
if (!lengthValidation.valid) {
|
||||
return redirectWithLengthError(
|
||||
req,
|
||||
res,
|
||||
"/admin/activity/create",
|
||||
lengthValidation,
|
||||
);
|
||||
}
|
||||
|
||||
const newActivity = new Activity(activityData);
|
||||
await newActivity.save();
|
||||
|
||||
@@ -465,6 +508,16 @@ exports.update = async (req, res) => {
|
||||
// Force status to active on update (always set isActive true when editing)
|
||||
activityData.isActive = true;
|
||||
|
||||
const lengthValidation = validateLengthRules(activityData, ACTIVITY_LENGTH_RULES);
|
||||
if (!lengthValidation.valid) {
|
||||
return redirectWithLengthError(
|
||||
req,
|
||||
res,
|
||||
`/admin/activity/${req.params.id}/edit`,
|
||||
lengthValidation,
|
||||
);
|
||||
}
|
||||
|
||||
await Activity.findByIdAndUpdate(req.params.id, activityData, {new: true});
|
||||
|
||||
req.flash("success_msg", "Activity updated successfully");
|
||||
|
||||
@@ -2,6 +2,17 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { addBaseUrlToImages } = require("../utils/imageHelper");
|
||||
const Booking = require("../models/booking");
|
||||
const {
|
||||
validateLengthRules,
|
||||
summarizeLengthErrors,
|
||||
} = require("../utils/lengthValidation");
|
||||
const {
|
||||
BOOKING_LENGTH_RULES,
|
||||
} = require("../constants/contentLengthRules");
|
||||
|
||||
const getBookingLengthMessage = (validation) =>
|
||||
summarizeLengthErrors(validation, 3) ||
|
||||
"One or more fields exceed the allowed length.";
|
||||
|
||||
// -------------------- Public helpers --------------------
|
||||
const getBookingData = async () => {
|
||||
@@ -398,6 +409,12 @@ exports.update = async (req, res) => {
|
||||
return req.session.save(() => res.redirect("/admin/booking"));
|
||||
}
|
||||
|
||||
const lengthValidation = validateLengthRules(updateData, BOOKING_LENGTH_RULES);
|
||||
if (!lengthValidation.valid) {
|
||||
req.flash("error_msg", getBookingLengthMessage(lengthValidation));
|
||||
return req.session.save(() => res.redirect("/admin/booking"));
|
||||
}
|
||||
|
||||
// Validate data structure
|
||||
const validation = validateBookingData(updateData);
|
||||
if (!validation.isValid) {
|
||||
@@ -546,4 +563,4 @@ const getFinalBooking = (staticBooking, dbBooking) => {
|
||||
}
|
||||
|
||||
return merged;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -7,13 +7,13 @@ const formController = {
|
||||
try {
|
||||
res.render('admin/form/index', {
|
||||
layout: 'layouts/admin',
|
||||
title: 'Quản lý Form',
|
||||
title: 'Form Management',
|
||||
user: req.session.user,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error loading form management page:', error);
|
||||
res.status(500).render('error', {
|
||||
message: 'Lỗi khi tải trang quản lý form',
|
||||
message: 'Failed to load the form management page',
|
||||
error: error
|
||||
});
|
||||
}
|
||||
@@ -29,16 +29,16 @@ const formController = {
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Cập nhật form thành công'
|
||||
message: 'Form settings updated successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error updating form:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Lỗi khi cập nhật form'
|
||||
message: 'Failed to update form settings'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = formController;
|
||||
module.exports = formController;
|
||||
|
||||
@@ -118,7 +118,7 @@ const normalizeStoredImagePath = (imagePath) => {
|
||||
const getDefaultFloatingContactData = () => ({
|
||||
enabled: true,
|
||||
position: "bottom-right",
|
||||
panelTitle: "Anh chị cần tư vấn hay hỗ trợ gì thêm không ạ?",
|
||||
panelTitle: "Do you need any additional advice or support?",
|
||||
brand: {
|
||||
imageSrc: "/assets/img/logo/black-logo.svg",
|
||||
imageAlt: "HAI Learning",
|
||||
@@ -132,7 +132,7 @@ const getDefaultFloatingContactData = () => ({
|
||||
id: "facebook",
|
||||
platform: "facebook",
|
||||
enabled: true,
|
||||
label: "Nhắn tin qua Facebook",
|
||||
label: "Message via Facebook",
|
||||
subtitle: "facebook.com/hailearning.edu.vn",
|
||||
href: "https://www.facebook.com/hailearning.edu.vn/",
|
||||
iconImage: "/uploads/home/floating-contact/Facebook_Logo_Primary.webp",
|
||||
@@ -145,7 +145,7 @@ const getDefaultFloatingContactData = () => ({
|
||||
id: "zalo",
|
||||
platform: "zalo",
|
||||
enabled: true,
|
||||
label: "Nhắn tin qua Zalo",
|
||||
label: "Message via Zalo",
|
||||
subtitle: "zalo.me/84961834040",
|
||||
href: "https://zalo.me/84961834040",
|
||||
iconImage: "/uploads/home/floating-contact/Icon_of_Zalo.svg.webp",
|
||||
|
||||
@@ -8,7 +8,7 @@ exports.getAllPages = async (req, res) => {
|
||||
const pages = content.pages || [];
|
||||
|
||||
res.render('admin/pages/index', {
|
||||
title: 'Quản lý trang',
|
||||
title: 'Page Management',
|
||||
pages
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -21,7 +21,7 @@ exports.getAllPages = async (req, res) => {
|
||||
// Hiển thị form tạo trang mới
|
||||
exports.getAddPage = (req, res) => {
|
||||
res.render('admin/pages/add', {
|
||||
title: 'Thêm trang mới'
|
||||
title: 'Add New Page'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -95,7 +95,7 @@ exports.getEditPage = async (req, res) => {
|
||||
}
|
||||
|
||||
res.render('admin/pages/edit', {
|
||||
title: 'Chỉnh sửa trang',
|
||||
title: 'Edit Page',
|
||||
page
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -225,4 +225,4 @@ exports.getPageBySlug = async (req, res) => {
|
||||
message: 'An error occurred while loading the page. Please try again later.'
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
const Pricing = require("../models/pricing");
|
||||
const {
|
||||
validateLengthRules,
|
||||
summarizeLengthErrors,
|
||||
} = require("../utils/lengthValidation");
|
||||
const {
|
||||
PRICING_LENGTH_RULES,
|
||||
} = require("../constants/contentLengthRules");
|
||||
const writeAuditLog = require("../audit/writeAuditLog");
|
||||
const diffObject = require("../audit/diffObject");
|
||||
const AUDIT_ACTIONS = require("../constants/auditAction");
|
||||
|
||||
const getLengthValidationMessage = (validation) =>
|
||||
summarizeLengthErrors(validation, 3) ||
|
||||
"One or more fields exceed the allowed length.";
|
||||
|
||||
// ==================== CMS ADMIN FUNCTIONS ====================
|
||||
|
||||
// Render admin page for pricing management
|
||||
@@ -86,6 +97,20 @@ exports.update = async (req, res) => {
|
||||
? JSON.parse(testimonials)
|
||||
: testimonials;
|
||||
|
||||
const validation = validateLengthRules(
|
||||
{
|
||||
hero: heroData,
|
||||
pricingSection: pricingSectionData,
|
||||
plans: plansData,
|
||||
testimonials: testimonialsData,
|
||||
},
|
||||
PRICING_LENGTH_RULES,
|
||||
);
|
||||
if (!validation.valid) {
|
||||
req.flash("error", getLengthValidationMessage(validation));
|
||||
return res.redirect("/admin/pricing");
|
||||
}
|
||||
|
||||
let pricing = await Pricing.findOne({ name: "default" });
|
||||
|
||||
// ✅ Capture BEFORE state
|
||||
|
||||
@@ -7,11 +7,11 @@ exports.getSettings = async (req, res) => {
|
||||
const content = readJsonFile('content');
|
||||
const settings = content.settings || {
|
||||
siteName: 'CMS-SIMS',
|
||||
description: 'Hệ thống quản lý nội dung đơn giản'
|
||||
description: 'Simple content management system'
|
||||
};
|
||||
|
||||
res.render('admin/settings', {
|
||||
title: 'Cài đặt hệ thống',
|
||||
title: 'System Settings',
|
||||
settings
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -53,4 +53,4 @@ exports.updateSettings = async (req, res) => {
|
||||
req.flash('error_msg', 'Error updating settings');
|
||||
res.redirect('/admin/settings');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user