265 lines
7.1 KiB
JavaScript
265 lines
7.1 KiB
JavaScript
const express = require("express");
|
|
const { query, batchQuery } = require("../config/database");
|
|
const logger = require("../config/logger");
|
|
const { asyncHandler } = require("../middleware/errorHandler");
|
|
const { cacheMiddleware, cache } = require("../middleware/cache");
|
|
const {
|
|
addCacheHeaders,
|
|
fieldFilter,
|
|
paginate,
|
|
trackResponseTime,
|
|
generateETag,
|
|
optimizeJSON,
|
|
} = require("../middleware/apiOptimization");
|
|
const {
|
|
sendSuccess,
|
|
sendError,
|
|
sendNotFound,
|
|
} = require("../utils/responseHelpers");
|
|
const {
|
|
buildProductQuery,
|
|
buildSingleProductQuery,
|
|
buildBlogQuery,
|
|
buildPagesQuery,
|
|
buildPortfolioQuery,
|
|
buildCategoriesQuery,
|
|
} = require("../utils/queryBuilders");
|
|
const router = express.Router();
|
|
|
|
// Apply global optimizations to all routes
|
|
router.use(trackResponseTime);
|
|
router.use(fieldFilter);
|
|
router.use(optimizeJSON);
|
|
|
|
// Get all products - Cached for 5 minutes, optimized with index hints
|
|
router.get(
|
|
"/products",
|
|
cacheMiddleware(300000),
|
|
asyncHandler(async (req, res) => {
|
|
const queryText = buildProductQuery({ limit: 100 });
|
|
const result = await query(queryText);
|
|
sendSuccess(res, { products: result.rows });
|
|
}),
|
|
);
|
|
|
|
// Get featured products - Cached for 10 minutes, optimized with index scan
|
|
router.get(
|
|
"/products/featured",
|
|
cacheMiddleware(600000, (req) => `featured:${req.query.limit || 4}`),
|
|
asyncHandler(async (req, res) => {
|
|
const limit = Math.min(parseInt(req.query.limit) || 4, 20);
|
|
const queryText = buildProductQuery({
|
|
where: "p.isactive = true AND p.isfeatured = true",
|
|
limit,
|
|
});
|
|
const result = await query(queryText);
|
|
sendSuccess(res, { products: result.rows });
|
|
}),
|
|
);
|
|
|
|
// Get single product by ID or slug - Cached for 15 minutes
|
|
router.get(
|
|
"/products/:identifier",
|
|
cacheMiddleware(900000, (req) => `product:${req.params.identifier}`),
|
|
asyncHandler(async (req, res) => {
|
|
const { text, values } = buildSingleProductQuery(req.params.identifier);
|
|
const result = await query(text, values);
|
|
|
|
if (result.rows.length === 0) {
|
|
return sendNotFound(res, "Product");
|
|
}
|
|
|
|
sendSuccess(res, { product: result.rows[0] });
|
|
}),
|
|
);
|
|
|
|
// Get all product categories - Cached for 30 minutes
|
|
router.get(
|
|
"/categories",
|
|
cacheMiddleware(1800000),
|
|
asyncHandler(async (req, res) => {
|
|
const result = await query(buildCategoriesQuery());
|
|
sendSuccess(res, { categories: result.rows.map((row) => row.category) });
|
|
}),
|
|
);
|
|
|
|
// Get site settings (from site_settings table with key='general')
|
|
router.get(
|
|
"/settings",
|
|
asyncHandler(async (req, res) => {
|
|
const result = await query(
|
|
"SELECT settings FROM site_settings WHERE key = 'general'",
|
|
);
|
|
const settings = result.rows.length > 0 ? result.rows[0].settings : {};
|
|
sendSuccess(res, settings);
|
|
}),
|
|
);
|
|
|
|
// Get homepage sections - Cached for 15 minutes
|
|
router.get(
|
|
"/homepage/sections",
|
|
cacheMiddleware(900000),
|
|
asyncHandler(async (req, res) => {
|
|
const result = await query(
|
|
"SELECT * FROM homepagesections ORDER BY displayorder ASC",
|
|
);
|
|
sendSuccess(res, { sections: result.rows });
|
|
}),
|
|
);
|
|
|
|
// Get portfolio projects - Cached for 10 minutes
|
|
router.get(
|
|
"/portfolio/projects",
|
|
cacheMiddleware(600000),
|
|
asyncHandler(async (req, res) => {
|
|
const result = await query(buildPortfolioQuery());
|
|
sendSuccess(res, { projects: result.rows });
|
|
}),
|
|
);
|
|
|
|
// Get blog posts - Cached for 5 minutes
|
|
router.get(
|
|
"/blog/posts",
|
|
cacheMiddleware(300000),
|
|
asyncHandler(async (req, res) => {
|
|
const result = await query(buildBlogQuery());
|
|
sendSuccess(res, { posts: result.rows });
|
|
}),
|
|
);
|
|
|
|
// Get single blog post by slug
|
|
router.get(
|
|
"/blog/posts/:slug",
|
|
asyncHandler(async (req, res) => {
|
|
const result = await query(
|
|
"SELECT * FROM blogposts WHERE slug = $1 AND ispublished = true",
|
|
[req.params.slug],
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return sendNotFound(res, "Blog post");
|
|
}
|
|
|
|
sendSuccess(res, { post: result.rows[0] });
|
|
}),
|
|
);
|
|
|
|
// Get custom pages - Cached for 10 minutes
|
|
router.get(
|
|
"/pages",
|
|
cacheMiddleware(600000),
|
|
asyncHandler(async (req, res) => {
|
|
const result = await query(buildPagesQuery());
|
|
sendSuccess(res, { pages: result.rows });
|
|
}),
|
|
);
|
|
|
|
// Get single page by slug - Cache disabled for immediate updates
|
|
router.get(
|
|
"/pages/:slug",
|
|
asyncHandler(async (req, res) => {
|
|
console.log("=== PUBLIC PAGE REQUEST ===");
|
|
console.log("Requested slug:", req.params.slug);
|
|
|
|
// Add no-cache headers
|
|
res.set({
|
|
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
Pragma: "no-cache",
|
|
Expires: "0",
|
|
});
|
|
|
|
const result = await query(
|
|
`SELECT id, title, slug, pagecontent as content, metatitle, metadescription, pagedata
|
|
FROM pages
|
|
WHERE slug = $1 AND isactive = true`,
|
|
[req.params.slug],
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
console.log("Page not found for slug:", req.params.slug);
|
|
return sendNotFound(res, "Page");
|
|
}
|
|
|
|
console.log("=== RETURNING PAGE DATA ===");
|
|
console.log("Page found, ID:", result.rows[0].id);
|
|
console.log(
|
|
"PageData:",
|
|
result.rows[0].pagedata
|
|
? JSON.stringify(result.rows[0].pagedata).substring(0, 200) + "..."
|
|
: "null",
|
|
);
|
|
|
|
sendSuccess(res, { page: result.rows[0] });
|
|
}),
|
|
);
|
|
|
|
// Get menu items for frontend navigation - Cached for 30 minutes
|
|
router.get(
|
|
"/menu",
|
|
cacheMiddleware(1800000),
|
|
asyncHandler(async (req, res) => {
|
|
const result = await query(
|
|
"SELECT settings FROM site_settings WHERE key = 'menu'",
|
|
);
|
|
const items =
|
|
result.rows.length > 0 ? result.rows[0].settings.items || [] : [];
|
|
const visibleItems = items.filter((item) => item.visible !== false);
|
|
sendSuccess(res, { items: visibleItems });
|
|
}),
|
|
);
|
|
|
|
// Get homepage settings for frontend
|
|
router.get(
|
|
"/homepage/settings",
|
|
asyncHandler(async (req, res) => {
|
|
const result = await query(
|
|
"SELECT settings FROM site_settings WHERE key = 'homepage'",
|
|
);
|
|
const settings = result.rows.length > 0 ? result.rows[0].settings : {};
|
|
sendSuccess(res, { settings });
|
|
}),
|
|
);
|
|
|
|
// Get all team members (public)
|
|
router.get(
|
|
"/team-members",
|
|
asyncHandler(async (req, res) => {
|
|
const result = await query(
|
|
"SELECT id, name, position, bio, image_url FROM team_members ORDER BY display_order ASC, created_at DESC",
|
|
);
|
|
sendSuccess(res, { teamMembers: result.rows });
|
|
}),
|
|
);
|
|
|
|
// Get menu items (public)
|
|
router.get(
|
|
"/menu",
|
|
asyncHandler(async (req, res) => {
|
|
const result = await query(
|
|
"SELECT settings FROM site_settings WHERE key = 'menu'",
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return sendSuccess(res, { items: [] });
|
|
}
|
|
|
|
// Parse JSON settings if it's a string
|
|
let settings = result.rows[0].settings;
|
|
if (typeof settings === "string") {
|
|
try {
|
|
settings = JSON.parse(settings);
|
|
} catch (e) {
|
|
logger.error("Failed to parse menu settings:", e);
|
|
return sendSuccess(res, { items: [] });
|
|
}
|
|
}
|
|
|
|
const items = settings.items || [];
|
|
// Filter only visible items for public
|
|
const visibleItems = items.filter((item) => item.visible !== false);
|
|
sendSuccess(res, { items: visibleItems });
|
|
}),
|
|
);
|
|
|
|
module.exports = router;
|