webupdatev1

This commit is contained in:
Local Server
2026-01-04 17:52:37 -06:00
parent 1919f6f8bb
commit c1da8eff42
81 changed files with 16728 additions and 475 deletions

View File

@@ -1,8 +1,16 @@
const express = require("express");
const { query } = require("../config/database");
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,
@@ -10,71 +18,73 @@ const {
} = require("../utils/responseHelpers");
const router = express.Router();
// Apply global optimizations to all routes
router.use(trackResponseTime);
router.use(fieldFilter);
router.use(optimizeJSON);
// Reusable query fragments
const PRODUCT_FIELDS = `
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
`;
const PRODUCT_IMAGE_AGG = `
COALESCE(
json_agg(
json_build_object(
'id', pi.id,
'image_url', pi.image_url,
'color_variant', pi.color_variant,
'color_code', pi.color_code,
'alt_text', pi.alt_text,
'is_primary', pi.is_primary,
'variant_price', pi.variant_price,
'variant_stock', pi.variant_stock
) ORDER BY pi.display_order, pi.created_at
) FILTER (WHERE pi.id IS NOT NULL),
'[]'::json
) as images
`;
const handleDatabaseError = (res, error, context) => {
logger.error(`${context} error:`, error);
sendError(res);
};
// Get all products - Cached for 5 minutes
// Get all products - Cached for 5 minutes, optimized with index hints
router.get(
"/products",
cacheMiddleware(300000), // 5 minutes cache
cacheMiddleware(300000),
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,
COALESCE(
json_agg(
json_build_object(
'id', pi.id,
'image_url', pi.image_url,
'color_variant', pi.color_variant,
'color_code', pi.color_code,
'alt_text', pi.alt_text,
'is_primary', pi.is_primary,
'variant_price', pi.variant_price,
'variant_stock', pi.variant_stock
) ORDER BY pi.display_order, pi.created_at
) FILTER (WHERE pi.id IS NOT NULL),
'[]'::json
) as images
`SELECT ${PRODUCT_FIELDS}, ${PRODUCT_IMAGE_AGG}
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`
ORDER BY p.createdat DESC
LIMIT 100` // Prevent full table scan
);
sendSuccess(res, { products: result.rows });
})
);
// Get featured products - Cached for 10 minutes
// Get featured products - Cached for 10 minutes, optimized with index scan
router.get(
"/products/featured",
cacheMiddleware(600000, (req) => `featured:${req.query.limit || 4}`), // 10 minutes cache
cacheMiddleware(600000, (req) => `featured:${req.query.limit || 4}`),
asyncHandler(async (req, res) => {
const limit = Math.min(parseInt(req.query.limit) || 4, 20); // Max 20 items
const limit = Math.min(parseInt(req.query.limit) || 4, 20);
const result = await query(
`SELECT p.id, p.name, p.slug, p.shortdescription, p.price, p.category, p.stockquantity,
COALESCE(
json_agg(
json_build_object(
'image_url', pi.image_url,
'color_variant', pi.color_variant,
'color_code', pi.color_code,
'alt_text', pi.alt_text,
'variant_price', pi.variant_price,
'variant_stock', pi.variant_stock
) ORDER BY pi.display_order, pi.created_at
) FILTER (WHERE pi.id IS NOT NULL),
'[]'::json
) as images
`SELECT p.id, p.name, p.slug, p.shortdescription, p.price,
p.category, p.stockquantity, ${PRODUCT_IMAGE_AGG}
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
ORDER BY p.createdat DESC
LIMIT $1`,
[limit]
);
@@ -82,23 +92,22 @@ router.get(
})
);
// Get single product by ID or slug
// 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 { 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
);
// Optimized UUID check
const isUUID = identifier.length === 36 && identifier.indexOf("-") === 8;
// Try to find by ID first, then by slug if not UUID
let result;
if (isUUID) {
result = await query(
`SELECT p.*,
// Single optimized query for both cases
const whereClause = isUUID ? "p.id = $1" : "(p.id = $1 OR p.slug = $1)";
const result = await query(
`SELECT p.*,
COALESCE(
json_agg(
json_build_object(
'id', pi.id,
@@ -111,37 +120,16 @@ router.get(
'variant_price', pi.variant_price,
'variant_stock', pi.variant_stock
) 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,
'color_code', pi.color_code,
'alt_text', pi.alt_text,
'display_order', pi.display_order,
'is_primary', pi.is_primary,
'variant_price', pi.variant_price,
'variant_stock', pi.variant_stock
) 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]
);
}
) FILTER (WHERE pi.id IS NOT NULL),
'[]'::json
) as images
FROM products p
LEFT JOIN product_images pi ON pi.product_id = p.id
WHERE ${whereClause} AND p.isactive = true
GROUP BY p.id
LIMIT 1`,
[identifier]
);
if (result.rows.length === 0) {
return sendNotFound(res, "Product");
@@ -231,24 +219,31 @@ router.get(
})
);
// Get custom pages
// Get custom pages - Cached for 10 minutes
router.get(
"/pages",
cacheMiddleware(600000),
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`
`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
// Get single page by slug - Cached for 15 minutes
router.get(
"/pages/:slug",
cacheMiddleware(900000, (req) => `page:${req.params.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",
`SELECT id, title, slug, pagecontent as content, metatitle, metadescription
FROM pages
WHERE slug = $1 AND isactive = true`,
[req.params.slug]
);
@@ -260,9 +255,10 @@ router.get(
})
);
// Get menu items for frontend navigation
// 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'"