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 router.get( "/settings", asyncHandler(async (req, res) => { const result = await query("SELECT * FROM sitesettings LIMIT 1"); sendSuccess(res, { settings: result.rows[0] || {} }); }), ); // 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;