Files
SkyArtShop/backend/routes/public.js

262 lines
7.0 KiB
JavaScript
Raw Normal View History

const express = require("express");
2026-01-04 17:52:37 -06:00
const { query, batchQuery } = require("../config/database");
2025-12-19 20:44:46 -06:00
const logger = require("../config/logger");
const { asyncHandler } = require("../middleware/errorHandler");
2026-01-01 22:24:30 -06:00
const { cacheMiddleware, cache } = require("../middleware/cache");
2026-01-04 17:52:37 -06:00
const {
addCacheHeaders,
fieldFilter,
paginate,
trackResponseTime,
generateETag,
optimizeJSON,
} = require("../middleware/apiOptimization");
2025-12-19 20:44:46 -06:00
const {
sendSuccess,
sendError,
sendNotFound,
} = require("../utils/responseHelpers");
2026-01-18 02:22:05 -06:00
const {
buildProductQuery,
buildSingleProductQuery,
buildBlogQuery,
buildPagesQuery,
buildPortfolioQuery,
buildCategoriesQuery,
} = require("../utils/queryBuilders");
const router = express.Router();
2026-01-04 17:52:37 -06:00
// 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
2025-12-19 20:44:46 -06:00
router.get(
"/products",
2026-01-04 17:52:37 -06:00
cacheMiddleware(300000),
2025-12-19 20:44:46 -06:00
asyncHandler(async (req, res) => {
2026-01-18 02:22:05 -06:00
const queryText = buildProductQuery({ limit: 100 });
const result = await query(queryText);
2025-12-19 20:44:46 -06:00
sendSuccess(res, { products: result.rows });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2026-01-04 17:52:37 -06:00
// Get featured products - Cached for 10 minutes, optimized with index scan
2025-12-19 20:44:46 -06:00
router.get(
"/products/featured",
2026-01-04 17:52:37 -06:00
cacheMiddleware(600000, (req) => `featured:${req.query.limit || 4}`),
2025-12-19 20:44:46 -06:00
asyncHandler(async (req, res) => {
2026-01-04 17:52:37 -06:00
const limit = Math.min(parseInt(req.query.limit) || 4, 20);
2026-01-18 02:22:05 -06:00
const queryText = buildProductQuery({
where: "p.isactive = true AND p.isfeatured = true",
limit,
});
const result = await query(queryText);
2025-12-19 20:44:46 -06:00
sendSuccess(res, { products: result.rows });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2026-01-04 17:52:37 -06:00
// Get single product by ID or slug - Cached for 15 minutes
2025-12-19 20:44:46 -06:00
router.get(
2025-12-24 00:13:23 -06:00
"/products/:identifier",
2026-01-04 17:52:37 -06:00
cacheMiddleware(900000, (req) => `product:${req.params.identifier}`),
2025-12-19 20:44:46 -06:00
asyncHandler(async (req, res) => {
2026-01-18 02:22:05 -06:00
const { text, values } = buildSingleProductQuery(req.params.identifier);
const result = await query(text, values);
2025-12-19 20:44:46 -06:00
if (result.rows.length === 0) {
2025-12-19 20:44:46 -06:00
return sendNotFound(res, "Product");
}
2025-12-19 20:44:46 -06:00
sendSuccess(res, { product: result.rows[0] });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2026-01-01 22:24:30 -06:00
// Get all product categories - Cached for 30 minutes
router.get(
"/categories",
2026-01-18 02:22:05 -06:00
cacheMiddleware(1800000),
2026-01-01 22:24:30 -06:00
asyncHandler(async (req, res) => {
2026-01-18 02:22:05 -06:00
const result = await query(buildCategoriesQuery());
2026-01-01 22:24:30 -06:00
sendSuccess(res, { categories: result.rows.map((row) => row.category) });
2026-01-18 02:22:05 -06:00
}),
2026-01-01 22:24:30 -06:00
);
// Get site settings
2025-12-19 20:44:46 -06:00
router.get(
"/settings",
asyncHandler(async (req, res) => {
const result = await query("SELECT * FROM sitesettings LIMIT 1");
2025-12-19 20:44:46 -06:00
sendSuccess(res, { settings: result.rows[0] || {} });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2026-01-01 22:24:30 -06:00
// Get homepage sections - Cached for 15 minutes
2025-12-19 20:44:46 -06:00
router.get(
"/homepage/sections",
2026-01-18 02:22:05 -06:00
cacheMiddleware(900000),
2025-12-19 20:44:46 -06:00
asyncHandler(async (req, res) => {
const result = await query(
2026-01-18 02:22:05 -06:00
"SELECT * FROM homepagesections ORDER BY displayorder ASC",
);
2025-12-19 20:44:46 -06:00
sendSuccess(res, { sections: result.rows });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2026-01-18 02:22:05 -06:00
2026-01-01 22:24:30 -06:00
// Get portfolio projects - Cached for 10 minutes
2025-12-19 20:44:46 -06:00
router.get(
"/portfolio/projects",
2026-01-18 02:22:05 -06:00
cacheMiddleware(600000),
2025-12-19 20:44:46 -06:00
asyncHandler(async (req, res) => {
2026-01-18 02:22:05 -06:00
const result = await query(buildPortfolioQuery());
2025-12-19 20:44:46 -06:00
sendSuccess(res, { projects: result.rows });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2026-01-01 22:24:30 -06:00
// Get blog posts - Cached for 5 minutes
2025-12-19 20:44:46 -06:00
router.get(
"/blog/posts",
2026-01-18 02:22:05 -06:00
cacheMiddleware(300000),
2025-12-19 20:44:46 -06:00
asyncHandler(async (req, res) => {
2026-01-18 02:22:05 -06:00
const result = await query(buildBlogQuery());
2025-12-19 20:44:46 -06:00
sendSuccess(res, { posts: result.rows });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2025-12-14 01:54:40 -06:00
// Get single blog post by slug
2025-12-19 20:44:46 -06:00
router.get(
"/blog/posts/:slug",
asyncHandler(async (req, res) => {
2025-12-14 01:54:40 -06:00
const result = await query(
"SELECT * FROM blogposts WHERE slug = $1 AND ispublished = true",
2026-01-18 02:22:05 -06:00
[req.params.slug],
2025-12-14 01:54:40 -06:00
);
2025-12-19 20:44:46 -06:00
2025-12-14 01:54:40 -06:00
if (result.rows.length === 0) {
2025-12-19 20:44:46 -06:00
return sendNotFound(res, "Blog post");
2025-12-14 01:54:40 -06:00
}
2025-12-19 20:44:46 -06:00
sendSuccess(res, { post: result.rows[0] });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2025-12-14 01:54:40 -06:00
2026-01-04 17:52:37 -06:00
// Get custom pages - Cached for 10 minutes
2025-12-19 20:44:46 -06:00
router.get(
"/pages",
2026-01-04 17:52:37 -06:00
cacheMiddleware(600000),
2025-12-19 20:44:46 -06:00
asyncHandler(async (req, res) => {
2026-01-18 02:22:05 -06:00
const result = await query(buildPagesQuery());
2025-12-19 20:44:46 -06:00
sendSuccess(res, { pages: result.rows });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2025-12-14 01:54:40 -06:00
2026-01-18 02:22:05 -06:00
// Get single page by slug - Cache disabled for immediate updates
2025-12-19 20:44:46 -06:00
router.get(
"/pages/:slug",
asyncHandler(async (req, res) => {
2026-01-18 02:22:05 -06:00
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",
});
2025-12-14 01:54:40 -06:00
const result = await query(
2026-01-18 02:22:05 -06:00
`SELECT id, title, slug, pagecontent as content, metatitle, metadescription, pagedata
2026-01-04 17:52:37 -06:00
FROM pages
WHERE slug = $1 AND isactive = true`,
2026-01-18 02:22:05 -06:00
[req.params.slug],
2025-12-14 01:54:40 -06:00
);
2025-12-19 20:44:46 -06:00
2025-12-14 01:54:40 -06:00
if (result.rows.length === 0) {
2026-01-18 02:22:05 -06:00
console.log("Page not found for slug:", req.params.slug);
2025-12-19 20:44:46 -06:00
return sendNotFound(res, "Page");
2025-12-14 01:54:40 -06:00
}
2025-12-19 20:44:46 -06:00
2026-01-18 02:22:05 -06:00
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",
);
2025-12-19 20:44:46 -06:00
sendSuccess(res, { page: result.rows[0] });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2025-12-14 01:54:40 -06:00
2026-01-04 17:52:37 -06:00
// Get menu items for frontend navigation - Cached for 30 minutes
2025-12-19 20:44:46 -06:00
router.get(
"/menu",
2026-01-04 17:52:37 -06:00
cacheMiddleware(1800000),
2025-12-19 20:44:46 -06:00
asyncHandler(async (req, res) => {
2025-12-14 01:54:40 -06:00
const result = await query(
2026-01-18 02:22:05 -06:00
"SELECT settings FROM site_settings WHERE key = 'menu'",
2025-12-14 01:54:40 -06:00
);
const items =
result.rows.length > 0 ? result.rows[0].settings.items || [] : [];
const visibleItems = items.filter((item) => item.visible !== false);
2025-12-19 20:44:46 -06:00
sendSuccess(res, { items: visibleItems });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2025-12-14 01:54:40 -06:00
// Get homepage settings for frontend
2025-12-19 20:44:46 -06:00
router.get(
"/homepage/settings",
asyncHandler(async (req, res) => {
2025-12-14 01:54:40 -06:00
const result = await query(
2026-01-18 02:22:05 -06:00
"SELECT settings FROM site_settings WHERE key = 'homepage'",
2025-12-14 01:54:40 -06:00
);
const settings = result.rows.length > 0 ? result.rows[0].settings : {};
2025-12-19 20:44:46 -06:00
sendSuccess(res, { settings });
2026-01-18 02:22:05 -06:00
}),
2025-12-19 20:44:46 -06:00
);
2025-12-14 01:54:40 -06:00
2025-12-24 00:13:23 -06:00
// Get all team members (public)
router.get(
"/team-members",
asyncHandler(async (req, res) => {
const result = await query(
2026-01-18 02:22:05 -06:00
"SELECT id, name, position, bio, image_url FROM team_members ORDER BY display_order ASC, created_at DESC",
2025-12-24 00:13:23 -06:00
);
sendSuccess(res, { teamMembers: result.rows });
2026-01-18 02:22:05 -06:00
}),
2025-12-24 00:13:23 -06:00
);
// Get menu items (public)
router.get(
"/menu",
asyncHandler(async (req, res) => {
const result = await query(
2026-01-18 02:22:05 -06:00
"SELECT settings FROM site_settings WHERE key = 'menu'",
2025-12-24 00:13:23 -06:00
);
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 });
2026-01-18 02:22:05 -06:00
}),
2025-12-24 00:13:23 -06:00
);
module.exports = router;