const express = require("express"); const { query } = require("../config/database"); const logger = require("../config/logger"); const { asyncHandler } = require("../middleware/errorHandler"); const { sendSuccess, sendError, sendNotFound, } = require("../utils/responseHelpers"); const router = express.Router(); const handleDatabaseError = (res, error, context) => { logger.error(`${context} error:`, error); sendError(res); }; // Get all products router.get( "/products", asyncHandler(async (req, res) => { const result = await query( `SELECT p.id, p.name, p.slug, p.shortdescription, p.description, p.price, p.category, p.stockquantity, p.sku, p.weight, p.dimensions, p.material, p.isfeatured, p.isbestseller, p.createdat, json_agg( json_build_object( 'id', pi.id, 'image_url', pi.image_url, 'color_variant', pi.color_variant, 'alt_text', pi.alt_text, 'is_primary', pi.is_primary ) ORDER BY pi.display_order, pi.created_at ) FILTER (WHERE pi.id IS NOT NULL) as images FROM products p LEFT JOIN product_images pi ON pi.product_id = p.id WHERE p.isactive = true GROUP BY p.id ORDER BY p.createdat DESC` ); sendSuccess(res, { products: result.rows }); }) ); // Get featured products router.get( "/products/featured", asyncHandler(async (req, res) => { const limit = parseInt(req.query.limit) || 4; const result = await query( `SELECT p.id, p.name, p.slug, p.shortdescription, p.price, p.category, json_agg( json_build_object( 'image_url', pi.image_url, 'color_variant', pi.color_variant, 'alt_text', pi.alt_text ) ORDER BY pi.display_order, pi.created_at ) FILTER (WHERE pi.id IS NOT NULL) as images FROM products p LEFT JOIN product_images pi ON pi.product_id = p.id WHERE p.isactive = true AND p.isfeatured = true GROUP BY p.id ORDER BY p.createdat DESC LIMIT $1`, [limit] ); sendSuccess(res, { products: result.rows }); }) ); // Get single product by ID or slug router.get( "/products/:identifier", asyncHandler(async (req, res) => { const { identifier } = req.params; // Check if identifier is a UUID const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test( identifier ); // Try to find by ID first, then by slug if not UUID let result; if (isUUID) { result = await query( `SELECT p.*, json_agg( json_build_object( 'id', pi.id, 'image_url', pi.image_url, 'color_variant', pi.color_variant, 'alt_text', pi.alt_text, 'display_order', pi.display_order, 'is_primary', pi.is_primary ) ORDER BY pi.display_order, pi.created_at ) FILTER (WHERE pi.id IS NOT NULL) as images FROM products p LEFT JOIN product_images pi ON pi.product_id = p.id WHERE p.id = $1 AND p.isactive = true GROUP BY p.id`, [identifier] ); } else { // Try both ID and slug for non-UUID identifiers result = await query( `SELECT p.*, json_agg( json_build_object( 'id', pi.id, 'image_url', pi.image_url, 'color_variant', pi.color_variant, 'alt_text', pi.alt_text, 'display_order', pi.display_order, 'is_primary', pi.is_primary ) ORDER BY pi.display_order, pi.created_at ) FILTER (WHERE pi.id IS NOT NULL) as images FROM products p LEFT JOIN product_images pi ON pi.product_id = p.id WHERE (p.id = $1 OR p.slug = $1) AND p.isactive = true GROUP BY p.id`, [identifier] ); } if (result.rows.length === 0) { return sendNotFound(res, "Product"); } sendSuccess(res, { product: result.rows[0] }); }) ); // 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 router.get( "/homepage/sections", asyncHandler(async (req, res) => { const result = await query( "SELECT * FROM homepagesections ORDER BY displayorder ASC" ); sendSuccess(res, { sections: result.rows }); }) ); // Get portfolio projects router.get( "/portfolio/projects", asyncHandler(async (req, res) => { const result = await query( `SELECT id, title, description, featuredimage, images, category, categoryid, isactive, createdat FROM portfolioprojects WHERE isactive = true ORDER BY displayorder ASC, createdat DESC` ); sendSuccess(res, { projects: result.rows }); }) ); // Get blog posts router.get( "/blog/posts", asyncHandler(async (req, res) => { const result = await query( `SELECT id, title, slug, excerpt, content, imageurl, ispublished, createdat FROM blogposts WHERE ispublished = true ORDER BY createdat DESC` ); 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 router.get( "/pages", asyncHandler(async (req, res) => { const result = await query( `SELECT id, title, slug, pagecontent as content, metatitle, metadescription, isactive, createdat FROM pages WHERE isactive = true ORDER BY createdat DESC` ); sendSuccess(res, { pages: result.rows }); }) ); // Get single page by slug router.get( "/pages/:slug", asyncHandler(async (req, res) => { const result = await query( "SELECT id, title, slug, pagecontent as content, metatitle, metadescription FROM pages WHERE slug = $1 AND isactive = true", [req.params.slug] ); if (result.rows.length === 0) { return sendNotFound(res, "Page"); } sendSuccess(res, { page: result.rows[0] }); }) ); // Get menu items for frontend navigation router.get( "/menu", 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;