API Country

This commit is contained in:
r2xrzh9q2z-lab
2026-02-04 13:12:20 +07:00
parent 050f6b7776
commit 85f6295824
2 changed files with 176 additions and 81 deletions

View File

@@ -44,7 +44,15 @@ const addBaseUrlToImages = (data, baseUrl) => {
return data;
};
const Visa = require("../models/visa");
const slugify = require("slugify");
const createSlug = (text) => {
return slugify(text, {
lower: true, // Chuyển về chữ thường
strict: true, // Loại bỏ ký tự đặc biệt
locale: "vi", // Xử lý tiếng Việt chuẩn
trim: true,
});
};
// -------------------- Helper Functions --------------------
// Get visa data from MongoDB
@@ -108,24 +116,65 @@ exports.index = async (req, res) => {
// Get single country for edit
exports.getCountry = async (req, res) => {
console.log("--------------------------------------------------");
console.log("🚀 [GET] Request nhận được tại /visa/edit/:id");
try {
const { slug } = req.params;
const { id } = req.params;
console.log("📍 ID từ Params (URL):", id, "| Kiểu dữ liệu:", typeof id);
const visaData = await getVisaData();
if (!visaData.hero || !visaData.hero.summaryList) {
return res.status(404).json({ error: "Visa data not found" });
// Kiểm tra cấu trúc dữ liệu tổng
if (!visaData) {
console.error("❌ Lỗi: Hàm getVisaData() trả về null/undefined");
return res.status(404).json({ error: "Dữ liệu gốc không tồn tại" });
}
const country = visaData.hero.summaryList.find((c) => c.slug === slug);
if (!visaData.hero || !visaData.hero.summaryList) {
console.error("❌ Lỗi: Cấu trúc visaData.hero.summaryList không hợp lệ");
return res
.status(404)
.json({ error: "Không tìm thấy danh sách quốc gia" });
}
console.log(
"📊 Tổng số quốc gia hiện có trong mảng:",
visaData.hero.summaryList.length,
);
// 2. Tìm quốc gia theo ID
const targetId = parseInt(id);
console.log("🔍 Đang tìm kiếm Quốc gia có ID (sau khi parse):", targetId);
const country = visaData.hero.summaryList.find((c) => {
// Log từng phần tử để kiểm tra kiểu dữ liệu của c.id trong DB
// console.log(`Checking country: ${c.name} | ID in DB: ${c.id} (${typeof c.id})`);
return c.id === targetId;
});
if (!country) {
return res.status(404).json({ error: `Country "${slug}" not found` });
console.warn(`⚠️ Không tìm thấy quốc gia nào khớp với ID: ${targetId}`);
// In ra danh sách ID hiện có để so sánh
const existingIds = visaData.hero.summaryList.map((c) => c.id);
console.log("🆔 Các ID hiện có trong Database:", existingIds);
return res.status(404).json({
success: false,
error: `Không tìm thấy quốc gia có ID: ${id}`,
});
}
res.json(country);
console.log("✅ Tìm thấy dữ liệu quốc gia:", country.name);
// 3. Trả về dữ liệu
res.json({
success: true,
country: country,
});
} catch (err) {
console.error("Get country error:", err);
res.status(500).json({ error: "Error loading country" });
console.error("🔴 Lỗi nghiêm trọng tại getCountry Controller:", err);
res.status(500).json({ error: "Lỗi hệ thống khi tải thông tin quốc gia" });
}
};
@@ -195,19 +244,12 @@ exports.addCountry = async (req, res) => {
}
// Validate required fields
if (!req.body.name || !req.body.slug) {
return res.status(400).json({ error: "Name and slug are required" });
}
// Check if slug already exists
const slugExists = visaData.hero.summaryList.some(
(c) => c.slug === req.body.slug,
);
if (slugExists) {
return res
.status(400)
.json({ error: `Slug "${req.body.slug}" already exists` });
if (!req.body.name) {
return res.status(400).json({ error: "Name is required" });
}
const finalSlug = req.body.slug
? createSlug(req.body.slug)
: createSlug(req.body.name);
// Parse services array
let services = [];
@@ -240,7 +282,7 @@ exports.addCountry = async (req, res) => {
const newCountry = {
id: req.body.id || getNextCountryId(visaData.hero.summaryList),
name: req.body.name,
slug: req.body.slug,
slug: finalSlug,
icon: req.body.icon || "",
services: services,
...(detailedView && { detailedView }),
@@ -278,77 +320,88 @@ exports.addCountry = async (req, res) => {
// Update single country
exports.updateCountry = async (req, res) => {
try {
const { slug } = req.params;
// 1. Lấy ID từ params (URL)
const { id } = req.params;
let visaData = await getVisaData();
if (!visaData.hero || !visaData.hero.summaryList) {
return res.status(400).json({ error: "Invalid visa data structure" });
}
// if (!visaData || !visaData.hero || !visaData.hero.summaryList) {
// return res
// .status(400)
// .json({ error: "Cấu trúc dữ liệu Visa không hợp lệ" });
// }
// 2. Tìm index theo ID (Chuyển về Number để so sánh chính xác)
const countryIndex = visaData.hero.summaryList.findIndex(
(c) => c.slug === slug,
(c) => c.id === parseInt(id),
);
if (countryIndex === -1) {
return res.status(404).json({ error: `Country "${slug}" not found` });
return res
.status(404)
.json({ error: `Không tìm thấy quốc gia có ID: ${id}` });
}
const currentCountry = visaData.hero.summaryList[countryIndex];
// Parse services array
let services = currentCountry.services || [];
if (req.body.services) {
if (typeof req.body.services === "string") {
let finalSlug = currentCountry.slug;
if (req.body.name) {
// Nếu name thay đổi, ta có thể cập nhật lại slug (tùy nhu cầu SEO)
// Ở đây ưu tiên: Nếu có slug mới truyền lên thì dùng, không thì tạo từ name mới
finalSlug = req.body.slug
? createSlug(req.body.slug)
: createSlug(req.body.name);
}
// 3. Xử lý dữ liệu từ req.body
// Vì Client đã gửi JSON stringify, ta lấy trực tiếp hoặc parse nếu cần
let services = req.body.services;
if (typeof services === "string") {
try {
services = JSON.parse(req.body.services);
services = JSON.parse(services);
} catch (e) {
services = [req.body.services];
}
} else if (Array.isArray(req.body.services)) {
services = req.body.services;
services = [services];
}
}
// Parse detailedView if provided (optional)
let detailedView = currentCountry.detailedView;
if (req.body.detailedView) {
let detailedView = req.body.detailedView;
if (typeof detailedView === "string") {
try {
detailedView =
typeof req.body.detailedView === "string"
? JSON.parse(req.body.detailedView)
: req.body.detailedView;
detailedView = JSON.parse(detailedView);
} catch (e) {
console.warn("Could not parse detailedView");
detailedView = currentCountry.detailedView;
}
}
// Update country data
// 4. Cập nhật Object quốc gia
const updatedCountry = {
...currentCountry,
...currentCountry, // Giữ các trường cũ
id: parseInt(id), // Đảm bảo ID không đổi
name: req.body.name || currentCountry.name,
slug: req.body.slug || currentCountry.slug, // Allow slug update
slug: finalSlug,
icon: req.body.icon || currentCountry.icon,
services: services,
...(detailedView && { detailedView }),
services: Array.isArray(services) ? services : currentCountry.services,
detailedView: detailedView || currentCountry.detailedView,
};
// 5. Cập nhật vào mảng chính
visaData.hero.summaryList[countryIndex] = updatedCountry;
// Update database
const updatedData = {
...(visaData.toObject ? visaData.toObject() : visaData),
};
if (visaData._id) {
await Visa.findByIdAndUpdate(visaData._id, updatedData, { new: true });
} else {
await Visa.create(updatedData);
// 6. Lưu vào Database
if (visaData.markModified) {
// Bắt buộc với Mongoose khi thay đổi nội dung bên trong Array/Object
visaData.markModified("hero.summaryList");
}
console.log(`✅ Country "${updatedCountry.name}" updated successfully`);
if (visaData._id) {
await visaData.save(); // Sử dụng save() trực tiếp nếu visaData là Mongoose Document
} else {
await Visa.create(visaData);
}
console.log(
`✅ Country "${updatedCountry.name}" updated successfully by ID: ${id}`,
);
res.json({
success: true,
message: `Country "${updatedCountry.name}" updated successfully`,
message: `Quốc gia "${updatedCountry.name}" đã được cập nhật thành công`,
country: updatedCountry,
});
} catch (err) {

View File

@@ -385,8 +385,6 @@ router.post(
ensureAuthenticated,
insuranceController.update,
);
<<<<<<< HEAD
=======
// Service routes
router.get("/service", ensureAuthenticated, serviceController.index);
@@ -413,7 +411,6 @@ router.post(
serviceController.updateDetails,
);
>>>>>>> a255d09ef0a6eb0c487595aac19cefbf729d78a2
// Test Image Paths route
router.get("/test-images", ensureAuthenticated, (req, res) => {
const fs = require("fs");
@@ -478,7 +475,12 @@ router.get("/test-images", ensureAuthenticated, (req, res) => {
});
// Display visa management page
router.get("/visa", visaController.index);
router.get("/visa", ensureAuthenticated, visaController.index);
// Get country data for editing
router.get("/visa/edit/:id", ensureAuthenticated, visaController.getCountry);
router.get("/visa/country", visaController.apiCountries);
// Update hero title
router.post("/visa/update", ensureAuthenticated, visaController.update);
@@ -488,7 +490,7 @@ router.post("/visa/add", ensureAuthenticated, visaController.addCountry);
// Update single country
router.put(
"/visa/update/:slug",
"/visa/update/:id",
ensureAuthenticated,
visaController.updateCountry,
);
@@ -509,21 +511,61 @@ router.post("/blog/:id/edit", ensureAuthenticated, blogController.update);
router.post("/blog/:id/delete", ensureAuthenticated, blogController.destroy);
// Blog Categories Management
router.get("/blog/categories", ensureAuthenticated, blogCategoryController.index);
router.get("/blog/categories/create", ensureAuthenticated, blogCategoryController.create);
router.post("/blog/categories/create", ensureAuthenticated, blogCategoryController.store);
router.get("/blog/categories/:id/edit", ensureAuthenticated, blogCategoryController.edit);
router.post("/blog/categories/:id/edit", ensureAuthenticated, blogCategoryController.update);
router.post("/blog/categories/:id/delete", ensureAuthenticated, blogCategoryController.destroy);
router.post("/blog/categories/quick-create", ensureAuthenticated, blogCategoryController.quickCreate);
router.get(
"/blog/categories",
ensureAuthenticated,
blogCategoryController.index,
);
router.get(
"/blog/categories/create",
ensureAuthenticated,
blogCategoryController.create,
);
router.post(
"/blog/categories/create",
ensureAuthenticated,
blogCategoryController.store,
);
router.get(
"/blog/categories/:id/edit",
ensureAuthenticated,
blogCategoryController.edit,
);
router.post(
"/blog/categories/:id/edit",
ensureAuthenticated,
blogCategoryController.update,
);
router.post(
"/blog/categories/:id/delete",
ensureAuthenticated,
blogCategoryController.destroy,
);
router.post(
"/blog/categories/quick-create",
ensureAuthenticated,
blogCategoryController.quickCreate,
);
// Blog Tags Management
router.get("/blog/tags", ensureAuthenticated, blogTagController.index);
router.get("/blog/tags/create", ensureAuthenticated, blogTagController.create);
router.post("/blog/tags/create", ensureAuthenticated, blogTagController.store);
router.get("/blog/tags/:id/edit", ensureAuthenticated, blogTagController.edit);
router.post("/blog/tags/:id/edit", ensureAuthenticated, blogTagController.update);
router.post("/blog/tags/:id/delete", ensureAuthenticated, blogTagController.destroy);
router.post("/blog/tags/quick-create", ensureAuthenticated, blogTagController.quickCreate);
router.post(
"/blog/tags/:id/edit",
ensureAuthenticated,
blogTagController.update,
);
router.post(
"/blog/tags/:id/delete",
ensureAuthenticated,
blogTagController.destroy,
);
router.post(
"/blog/tags/quick-create",
ensureAuthenticated,
blogTagController.quickCreate,
);
module.exports = router;