This commit is contained in:
Keiran 2025-09-28 18:34:24 +01:00
parent 064c6b4333
commit 5b677ea480

View File

@ -8,7 +8,6 @@ require("dotenv").config();
const app = express(); const app = express();
const PORT = process.env.PORT || 3001; const PORT = process.env.PORT || 3001;
// Middleware
app.use(helmet()); app.use(helmet());
app.use( app.use(
cors({ cors({
@ -20,7 +19,6 @@ app.use(
); );
app.use(express.json()); app.use(express.json());
// Auth middleware
const authenticateAdmin = (req, res, next) => { const authenticateAdmin = (req, res, next) => {
const adminPassword = req.headers.authorization?.replace("Bearer ", ""); const adminPassword = req.headers.authorization?.replace("Bearer ", "");
if (adminPassword !== process.env.ADMIN_PASSWORD) { if (adminPassword !== process.env.ADMIN_PASSWORD) {
@ -29,11 +27,9 @@ const authenticateAdmin = (req, res, next) => {
next(); next();
}; };
// File paths
const DISCOUNT_CODES_PATH = path.join(__dirname, "data", "discountCodes.json"); const DISCOUNT_CODES_PATH = path.join(__dirname, "data", "discountCodes.json");
const PRODUCTS_PATH = path.join(__dirname, "data", "products.js"); const PRODUCTS_PATH = path.join(__dirname, "data", "products.js");
// In-memory cache
let cache = { let cache = {
discountCodes: null, discountCodes: null,
products: null, products: null,
@ -43,7 +39,6 @@ let cache = {
}, },
}; };
// Helper functions
const clearCache = () => { const clearCache = () => {
cache.discountCodes = null; cache.discountCodes = null;
cache.products = null; cache.products = null;
@ -78,17 +73,15 @@ const writeDiscountCodes = async (codes) => {
const readProducts = async () => { const readProducts = async () => {
try { try {
const data = await fs.readFile(PRODUCTS_PATH, "utf8"); const data = await fs.readFile(PRODUCTS_PATH, "utf8");
// Extract the products array from the JS file
const match = data.match( const match = data.match(
/export const products: Product\[\] = (\[[\s\S]*?\]);/, /export const products: Product\[\] = (\[[\s\S]*?\]);/,
); );
if (match) { if (match) {
// Convert JS object notation to JSON
const productsString = match[1] const productsString = match[1]
.replace(/(\w+):/g, '"$1":') // Quote keys .replace(/(\w+):/g, '"$1":')
.replace(/'/g, '"') // Single quotes to double quotes .replace(/'/g, '"')
.replace(/,\s*}/g, "}") // Remove trailing commas .replace(/,\s*}/g, "}")
.replace(/,\s*]/g, "]"); // Remove trailing commas in arrays .replace(/,\s*]/g, "]");
return JSON.parse(productsString); return JSON.parse(productsString);
} }
@ -104,8 +97,8 @@ const writeProducts = async (products) => {
const jsContent = `import type { Product } from "../types/product"; const jsContent = `import type { Product } from "../types/product";
export const products: Product[] = ${JSON.stringify(products, null, 2) export const products: Product[] = ${JSON.stringify(products, null, 2)
.replace(/"([^"]+)":/g, "$1:") // Remove quotes from keys .replace(/"([^"]+)":/g, "$1:")
.replace(/"/g, '"')}; // Keep string quotes .replace(/"/g, '"')};
`; `;
await fs.writeFile(PRODUCTS_PATH, jsContent, "utf8"); await fs.writeFile(PRODUCTS_PATH, jsContent, "utf8");
return true; return true;
@ -115,14 +108,11 @@ export const products: Product[] = ${JSON.stringify(products, null, 2)
} }
}; };
// Routes
// Health check
app.get("/health", (req, res) => { app.get("/health", (req, res) => {
res.json({ status: "ok", timestamp: new Date().toISOString() }); res.json({ status: "ok", timestamp: new Date().toISOString() });
}); });
// Get discount codes
app.get("/api/discount-codes", async (req, res) => { app.get("/api/discount-codes", async (req, res) => {
try { try {
if (!cache.discountCodes) { if (!cache.discountCodes) {
@ -138,7 +128,6 @@ app.get("/api/discount-codes", async (req, res) => {
} }
}); });
// Add discount code
app.post("/api/discount-codes", authenticateAdmin, async (req, res) => { app.post("/api/discount-codes", authenticateAdmin, async (req, res) => {
try { try {
const { code, percentage, expiration, description } = req.body; const { code, percentage, expiration, description } = req.body;
@ -167,7 +156,6 @@ app.post("/api/discount-codes", authenticateAdmin, async (req, res) => {
return res.status(500).json({ error: "Failed to save discount code" }); return res.status(500).json({ error: "Failed to save discount code" });
} }
// Clear cache to force refresh
clearCache(); clearCache();
res.json({ success: true, code: newCode }); res.json({ success: true, code: newCode });
@ -177,7 +165,6 @@ app.post("/api/discount-codes", authenticateAdmin, async (req, res) => {
} }
}); });
// Delete discount code
app.delete("/api/discount-codes/:code", authenticateAdmin, async (req, res) => { app.delete("/api/discount-codes/:code", authenticateAdmin, async (req, res) => {
try { try {
const { code } = req.params; const { code } = req.params;
@ -195,7 +182,6 @@ app.delete("/api/discount-codes/:code", authenticateAdmin, async (req, res) => {
return res.status(500).json({ error: "Failed to delete discount code" }); return res.status(500).json({ error: "Failed to delete discount code" });
} }
// Clear cache to force refresh
clearCache(); clearCache();
res.json({ success: true }); res.json({ success: true });
@ -205,7 +191,6 @@ app.delete("/api/discount-codes/:code", authenticateAdmin, async (req, res) => {
} }
}); });
// Get products
app.get("/api/products", async (req, res) => { app.get("/api/products", async (req, res) => {
try { try {
if (!cache.products) { if (!cache.products) {
@ -221,7 +206,6 @@ app.get("/api/products", async (req, res) => {
} }
}); });
// Update product price
app.put("/api/products/:productId", authenticateAdmin, async (req, res) => { app.put("/api/products/:productId", authenticateAdmin, async (req, res) => {
try { try {
const { productId } = req.params; const { productId } = req.params;
@ -245,7 +229,6 @@ app.put("/api/products/:productId", authenticateAdmin, async (req, res) => {
return res.status(500).json({ error: "Failed to update product" }); return res.status(500).json({ error: "Failed to update product" });
} }
// Clear cache to force refresh
clearCache(); clearCache();
res.json({ success: true, product: products[productIndex] }); res.json({ success: true, product: products[productIndex] });
@ -255,7 +238,6 @@ app.put("/api/products/:productId", authenticateAdmin, async (req, res) => {
} }
}); });
// Get cache status (for debugging)
app.get("/api/cache-status", (req, res) => { app.get("/api/cache-status", (req, res) => {
res.json({ res.json({
cache: { cache: {
@ -271,24 +253,20 @@ app.get("/api/cache-status", (req, res) => {
}); });
}); });
// Clear cache endpoint (for debugging)
app.post("/api/clear-cache", authenticateAdmin, (req, res) => { app.post("/api/clear-cache", authenticateAdmin, (req, res) => {
clearCache(); clearCache();
res.json({ success: true, message: "Cache cleared" }); res.json({ success: true, message: "Cache cleared" });
}); });
// Error handling middleware
app.use((error, req, res, next) => { app.use((error, req, res, next) => {
console.error("Unhandled error:", error); console.error("Unhandled error:", error);
res.status(500).json({ error: "Internal server error" }); res.status(500).json({ error: "Internal server error" });
}); });
// 404 handler
app.use((req, res) => { app.use((req, res) => {
res.status(404).json({ error: "Not found" }); res.status(404).json({ error: "Not found" });
}); });
// Ensure data directory exists
const initializeDataDirectory = async () => { const initializeDataDirectory = async () => {
const dataDir = path.join(__dirname, "data"); const dataDir = path.join(__dirname, "data");
try { try {
@ -296,14 +274,12 @@ const initializeDataDirectory = async () => {
} catch { } catch {
await fs.mkdir(dataDir, { recursive: true }); await fs.mkdir(dataDir, { recursive: true });
// Create initial discount codes file if it doesn't exist
try { try {
await fs.access(DISCOUNT_CODES_PATH); await fs.access(DISCOUNT_CODES_PATH);
} catch { } catch {
await fs.writeFile(DISCOUNT_CODES_PATH, "{}", "utf8"); await fs.writeFile(DISCOUNT_CODES_PATH, "{}", "utf8");
} }
// Create initial products file if it doesn't exist
try { try {
await fs.access(PRODUCTS_PATH); await fs.access(PRODUCTS_PATH);
} catch { } catch {
@ -355,7 +331,6 @@ export const products: Product[] = [
} }
}; };
// Start server
const startServer = async () => { const startServer = async () => {
try { try {
await initializeDataDirectory(); await initializeDataDirectory();