webupdate

This commit is contained in:
Local Server
2026-01-18 02:22:05 -06:00
parent 6fc159051a
commit 2a2a3d99e5
135 changed files with 54897 additions and 9825 deletions

View File

@@ -16,6 +16,14 @@ const {
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
@@ -23,52 +31,15 @@ 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, optimized with index hints
router.get(
"/products",
cacheMiddleware(300000),
asyncHandler(async (req, res) => {
const result = await query(
`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
LIMIT 100` // Prevent full table scan
);
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
@@ -77,19 +48,13 @@ router.get(
cacheMiddleware(600000, (req) => `featured:${req.query.limit || 4}`),
asyncHandler(async (req, res) => {
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, ${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
LIMIT $1`,
[limit]
);
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
@@ -97,61 +62,25 @@ router.get(
"/products/:identifier",
cacheMiddleware(900000, (req) => `product:${req.params.identifier}`),
asyncHandler(async (req, res) => {
const { identifier } = req.params;
// Optimized UUID check
const isUUID = identifier.length === 36 && identifier.indexOf("-") === 8;
// 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,
'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),
'[]'::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]
);
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), // 30 minutes cache
cacheMiddleware(1800000),
asyncHandler(async (req, res) => {
const result = await query(
`SELECT DISTINCT category
FROM products
WHERE isactive = true AND category IS NOT NULL AND category != ''
ORDER BY category ASC`
);
const result = await query(buildCategoriesQuery());
sendSuccess(res, { categories: result.rows.map((row) => row.category) });
})
}),
);
// Get site settings
@@ -160,46 +89,39 @@ router.get(
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), // 15 minutes cache
cacheMiddleware(900000),
asyncHandler(async (req, res) => {
const result = await query(
"SELECT * FROM homepagesections ORDER BY displayorder ASC"
"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), // 10 minutes cache
cacheMiddleware(600000),
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`
);
const result = await query(buildPortfolioQuery());
sendSuccess(res, { projects: result.rows });
})
}),
);
// Get blog posts - Cached for 5 minutes
router.get(
"/blog/posts",
cacheMiddleware(300000), // 5 minutes cache
cacheMiddleware(300000),
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`
);
const result = await query(buildBlogQuery());
sendSuccess(res, { posts: result.rows });
})
}),
);
// Get single blog post by slug
@@ -208,7 +130,7 @@ router.get(
asyncHandler(async (req, res) => {
const result = await query(
"SELECT * FROM blogposts WHERE slug = $1 AND ispublished = true",
[req.params.slug]
[req.params.slug],
);
if (result.rows.length === 0) {
@@ -216,7 +138,7 @@ router.get(
}
sendSuccess(res, { post: result.rows[0] });
})
}),
);
// Get custom pages - Cached for 10 minutes
@@ -224,35 +146,48 @@ 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`
);
const result = await query(buildPagesQuery());
sendSuccess(res, { pages: result.rows });
})
}),
);
// Get single page by slug - Cached for 15 minutes
// Get single page by slug - Cache disabled for immediate updates
router.get(
"/pages/:slug",
cacheMiddleware(900000, (req) => `page:${req.params.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
`SELECT id, title, slug, pagecontent as content, metatitle, metadescription, pagedata
FROM pages
WHERE slug = $1 AND isactive = true`,
[req.params.slug]
[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
@@ -261,13 +196,13 @@ router.get(
cacheMiddleware(1800000),
asyncHandler(async (req, res) => {
const result = await query(
"SELECT settings FROM site_settings WHERE key = 'menu'"
"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
@@ -275,11 +210,11 @@ router.get(
"/homepage/settings",
asyncHandler(async (req, res) => {
const result = await query(
"SELECT settings FROM site_settings WHERE key = 'homepage'"
"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)
@@ -287,10 +222,10 @@ 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"
"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)
@@ -298,7 +233,7 @@ router.get(
"/menu",
asyncHandler(async (req, res) => {
const result = await query(
"SELECT settings FROM site_settings WHERE key = 'menu'"
"SELECT settings FROM site_settings WHERE key = 'menu'",
);
if (result.rows.length === 0) {
@@ -320,7 +255,7 @@ router.get(
// Filter only visible items for public
const visibleItems = items.filter((item) => item.visible !== false);
sendSuccess(res, { items: visibleItems });
})
}),
);
module.exports = router;