feat: add integration and setup tests and complete code review fixes
This commit is contained in:
@@ -8,13 +8,18 @@ const fallbackStore = require("./src/utils/fallback-store");
|
||||
const logger = require("./src/utils/logger");
|
||||
const pdfGenerator = require("./src/utils/pdf-generator");
|
||||
const metrics = require("./src/utils/metrics");
|
||||
const security = require("./src/utils/security");
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3049;
|
||||
|
||||
// Middleware
|
||||
app.use(express.json());
|
||||
app.use(express.static("public"));
|
||||
|
||||
// Security middleware
|
||||
app.use(security.securityHeaders);
|
||||
app.use(security.requestSizeLimit);
|
||||
app.use(security.securityLogging);
|
||||
|
||||
// Request logging middleware
|
||||
app.use((req, res, next) => {
|
||||
@@ -29,6 +34,9 @@ app.use((req, res, next) => {
|
||||
// Prometheus metrics middleware
|
||||
app.use(metrics.metricsMiddleware);
|
||||
|
||||
// Apply general rate limiting to all routes
|
||||
app.use(security.generalLimiter);
|
||||
|
||||
// Health check endpoint
|
||||
app.get("/health", async (req, res) => {
|
||||
const redisHealthy = redisClient.isHealthy();
|
||||
@@ -75,7 +83,7 @@ app.get("/events", async (req, res) => {
|
||||
});
|
||||
|
||||
// Get specific event stats
|
||||
app.get("/events/:eventId", async (req, res) => {
|
||||
app.get("/events/:eventId", security.validateEventId, async (req, res) => {
|
||||
try {
|
||||
const eventId = req.params.eventId;
|
||||
let eventStats;
|
||||
@@ -108,109 +116,256 @@ app.get("/events/:eventId", async (req, res) => {
|
||||
});
|
||||
|
||||
// Purchase ticket endpoint (multi-event)
|
||||
app.post("/buy/:eventId", async (req, res) => {
|
||||
const startTime = Date.now();
|
||||
const eventId = req.params.eventId;
|
||||
const purchaseId = uuidv4();
|
||||
const timestamp = new Date().toISOString();
|
||||
app.post(
|
||||
"/buy/:eventId",
|
||||
security.purchaseLimiter,
|
||||
security.validateEventId,
|
||||
async (req, res) => {
|
||||
const startTime = Date.now();
|
||||
const eventId = req.params.eventId;
|
||||
const purchaseId = uuidv4();
|
||||
const timestamp = new Date().toISOString();
|
||||
|
||||
try {
|
||||
let result;
|
||||
try {
|
||||
let result;
|
||||
|
||||
// Try Redis first
|
||||
if (redisClient.isHealthy()) {
|
||||
// Try Redis first
|
||||
if (redisClient.isHealthy()) {
|
||||
try {
|
||||
const luaResult = await redisClient.purchaseTicket(
|
||||
eventId,
|
||||
purchaseId,
|
||||
timestamp
|
||||
);
|
||||
|
||||
if (luaResult[0]) {
|
||||
// Success - generate PDF ticket
|
||||
try {
|
||||
// Get event details for PDF
|
||||
const eventStats = await redisClient.getEventStats(eventId);
|
||||
|
||||
const pdfStartTime = Date.now();
|
||||
const pdfResult = await pdfGenerator.generateTicketPDF({
|
||||
ticketId: luaResult[0],
|
||||
eventId,
|
||||
purchaseId,
|
||||
eventName: eventStats?.name || `Event ${eventId}`,
|
||||
eventDescription:
|
||||
eventStats?.description || "Event description not available",
|
||||
timestamp,
|
||||
soldCount: luaResult[2],
|
||||
});
|
||||
|
||||
// Record PDF generation metrics
|
||||
const pdfDuration = (Date.now() - pdfStartTime) / 1000;
|
||||
metrics.recordPDFGeneration(
|
||||
pdfResult.success ? "success" : "failed",
|
||||
pdfDuration
|
||||
);
|
||||
|
||||
result = {
|
||||
success: true,
|
||||
ticket: luaResult[0],
|
||||
purchaseId,
|
||||
eventId,
|
||||
soldCount: luaResult[2],
|
||||
message: "Ticket purchased successfully!",
|
||||
usingFallback: false,
|
||||
pdf: {
|
||||
generated: pdfResult.success,
|
||||
filename: pdfResult.filename,
|
||||
downloadUrl: `/tickets/${purchaseId}`,
|
||||
},
|
||||
};
|
||||
|
||||
// Record metrics for successful purchase
|
||||
metrics.recordTicketSale(eventId, "success");
|
||||
metrics.updateTicketMetrics(
|
||||
eventId,
|
||||
luaResult[2],
|
||||
luaResult[3] || 0
|
||||
);
|
||||
|
||||
logger.logPurchase(eventId, luaResult[0], purchaseId, true);
|
||||
logger.info(`PDF ticket generated for purchase ${purchaseId}`);
|
||||
} catch (pdfError) {
|
||||
logger.error("PDF generation failed:", pdfError);
|
||||
|
||||
// Still return success for ticket purchase, but note PDF failure
|
||||
result = {
|
||||
success: true,
|
||||
ticket: luaResult[0],
|
||||
purchaseId,
|
||||
eventId,
|
||||
soldCount: luaResult[2],
|
||||
message:
|
||||
"Ticket purchased successfully! (PDF generation failed)",
|
||||
usingFallback: false,
|
||||
pdf: {
|
||||
generated: false,
|
||||
error: "PDF generation failed",
|
||||
},
|
||||
};
|
||||
|
||||
// Record metrics for successful purchase (even with PDF failure)
|
||||
metrics.recordTicketSale(eventId, "success");
|
||||
metrics.updateTicketMetrics(
|
||||
eventId,
|
||||
luaResult[2],
|
||||
luaResult[3] || 0
|
||||
);
|
||||
|
||||
logger.logPurchase(eventId, luaResult[0], purchaseId, true);
|
||||
}
|
||||
} else {
|
||||
// Failed - handle specific error
|
||||
const errorCode = luaResult[1];
|
||||
let statusCode = 400;
|
||||
let message = "Purchase failed";
|
||||
|
||||
switch (errorCode) {
|
||||
case "EVENT_NOT_FOUND":
|
||||
statusCode = 404;
|
||||
message = "Event not found";
|
||||
break;
|
||||
case "NO_TICKETS_AVAILABLE":
|
||||
statusCode = 409;
|
||||
message = "No tickets available for this event";
|
||||
break;
|
||||
}
|
||||
|
||||
// Record metrics for failed purchase
|
||||
metrics.recordTicketSale(eventId, "failed");
|
||||
|
||||
logger.logPurchase(eventId, null, purchaseId, false, errorCode);
|
||||
return res.status(statusCode).json({
|
||||
success: false,
|
||||
message,
|
||||
errorCode,
|
||||
eventId,
|
||||
purchaseId,
|
||||
});
|
||||
}
|
||||
} catch (redisError) {
|
||||
logger.error(
|
||||
"Redis purchase failed, attempting fallback:",
|
||||
redisError
|
||||
);
|
||||
// Activate fallback if not already active
|
||||
if (!fallbackStore.isActive) {
|
||||
fallbackStore.activate("Redis purchase operation failed");
|
||||
// Try to sync with Redis data if possible
|
||||
setTimeout(() => {
|
||||
if (fallbackStore.isActive && fallbackStore.events.size === 0) {
|
||||
fallbackStore.attemptReseed();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
throw redisError; // Will be caught by outer try-catch for fallback
|
||||
}
|
||||
} else {
|
||||
throw new Error("Redis not available");
|
||||
}
|
||||
|
||||
const responseTime = Date.now() - startTime;
|
||||
result.responseTime = `${responseTime}ms`;
|
||||
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
// Fallback to in-memory store
|
||||
try {
|
||||
const luaResult = await redisClient.purchaseTicket(
|
||||
if (!fallbackStore.isActive) {
|
||||
fallbackStore.activate("Redis connection failed during purchase");
|
||||
// Try to sync with Redis data if possible
|
||||
setTimeout(() => {
|
||||
if (fallbackStore.isActive && fallbackStore.events.size === 0) {
|
||||
fallbackStore.attemptReseed();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
const fallbackResult = fallbackStore.purchaseTicket(
|
||||
eventId,
|
||||
purchaseId,
|
||||
timestamp
|
||||
purchaseId
|
||||
);
|
||||
|
||||
if (luaResult[0]) {
|
||||
// Success - generate PDF ticket
|
||||
if (fallbackResult.success) {
|
||||
// Generate PDF for fallback purchase
|
||||
try {
|
||||
// Get event details for PDF
|
||||
const eventStats = await redisClient.getEventStats(eventId);
|
||||
const eventStats = fallbackStore.getEventStats(eventId);
|
||||
|
||||
const pdfStartTime = Date.now();
|
||||
const pdfResult = await pdfGenerator.generateTicketPDF({
|
||||
ticketId: luaResult[0],
|
||||
ticketId: fallbackResult.ticket,
|
||||
eventId,
|
||||
purchaseId,
|
||||
eventName: eventStats?.name || `Event ${eventId}`,
|
||||
eventDescription:
|
||||
eventStats?.description || "Event description not available",
|
||||
timestamp,
|
||||
soldCount: luaResult[2],
|
||||
soldCount: fallbackResult.soldCount,
|
||||
});
|
||||
|
||||
// Record PDF generation metrics
|
||||
const pdfDuration = (Date.now() - pdfStartTime) / 1000;
|
||||
metrics.recordPDFGeneration(
|
||||
pdfResult.success ? "success" : "failed",
|
||||
pdfDuration
|
||||
);
|
||||
|
||||
result = {
|
||||
const responseTime = Date.now() - startTime;
|
||||
res.json({
|
||||
success: true,
|
||||
ticket: luaResult[0],
|
||||
ticket: fallbackResult.ticket,
|
||||
purchaseId,
|
||||
eventId,
|
||||
soldCount: luaResult[2],
|
||||
message: "Ticket purchased successfully!",
|
||||
usingFallback: false,
|
||||
soldCount: fallbackResult.soldCount,
|
||||
message: "Ticket purchased successfully (fallback mode)!",
|
||||
usingFallback: true,
|
||||
responseTime: `${responseTime}ms`,
|
||||
pdf: {
|
||||
generated: pdfResult.success,
|
||||
filename: pdfResult.filename,
|
||||
downloadUrl: `/tickets/${purchaseId}`,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// Record metrics for successful purchase
|
||||
metrics.recordTicketSale(eventId, "success");
|
||||
// Record metrics for successful fallback purchase
|
||||
metrics.recordTicketSale(eventId, "success_fallback");
|
||||
metrics.updateTicketMetrics(
|
||||
eventId,
|
||||
luaResult[2],
|
||||
luaResult[3] || 0
|
||||
fallbackResult.soldCount,
|
||||
fallbackResult.remainingTickets || 0
|
||||
);
|
||||
|
||||
logger.logPurchase(eventId, luaResult[0], purchaseId, true);
|
||||
logger.info(`PDF ticket generated for purchase ${purchaseId}`);
|
||||
logger.info(
|
||||
`PDF ticket generated for fallback purchase ${purchaseId}`
|
||||
);
|
||||
} catch (pdfError) {
|
||||
logger.error("PDF generation failed:", pdfError);
|
||||
logger.error("PDF generation failed in fallback mode:", pdfError);
|
||||
|
||||
// Still return success for ticket purchase, but note PDF failure
|
||||
result = {
|
||||
const responseTime = Date.now() - startTime;
|
||||
res.json({
|
||||
success: true,
|
||||
ticket: luaResult[0],
|
||||
ticket: fallbackResult.ticket,
|
||||
purchaseId,
|
||||
eventId,
|
||||
soldCount: luaResult[2],
|
||||
message: "Ticket purchased successfully! (PDF generation failed)",
|
||||
usingFallback: false,
|
||||
soldCount: fallbackResult.soldCount,
|
||||
message:
|
||||
"Ticket purchased successfully (fallback mode, PDF generation failed)!",
|
||||
usingFallback: true,
|
||||
responseTime: `${responseTime}ms`,
|
||||
pdf: {
|
||||
generated: false,
|
||||
error: "PDF generation failed",
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// Record metrics for successful purchase (even with PDF failure)
|
||||
metrics.recordTicketSale(eventId, "success");
|
||||
// Record metrics for successful fallback purchase (even with PDF failure)
|
||||
metrics.recordTicketSale(eventId, "success_fallback");
|
||||
metrics.updateTicketMetrics(
|
||||
eventId,
|
||||
luaResult[2],
|
||||
luaResult[3] || 0
|
||||
fallbackResult.soldCount,
|
||||
fallbackResult.remainingTickets || 0
|
||||
);
|
||||
|
||||
logger.logPurchase(eventId, luaResult[0], purchaseId, true);
|
||||
}
|
||||
} else {
|
||||
// Failed - handle specific error
|
||||
const errorCode = luaResult[1];
|
||||
let statusCode = 400;
|
||||
let message = "Purchase failed";
|
||||
|
||||
switch (errorCode) {
|
||||
switch (fallbackResult.error) {
|
||||
case "EVENT_NOT_FOUND":
|
||||
statusCode = 404;
|
||||
message = "Event not found";
|
||||
@@ -221,212 +376,84 @@ app.post("/buy/:eventId", async (req, res) => {
|
||||
break;
|
||||
}
|
||||
|
||||
// Record metrics for failed purchase
|
||||
metrics.recordTicketSale(eventId, "failed");
|
||||
// Record metrics for failed fallback purchase
|
||||
metrics.recordTicketSale(eventId, "failed_fallback");
|
||||
|
||||
logger.logPurchase(eventId, null, purchaseId, false, errorCode);
|
||||
return res.status(statusCode).json({
|
||||
logger.logPurchase(
|
||||
eventId,
|
||||
null,
|
||||
purchaseId,
|
||||
false,
|
||||
fallbackResult.error
|
||||
);
|
||||
res.status(statusCode).json({
|
||||
success: false,
|
||||
message,
|
||||
errorCode,
|
||||
errorCode: fallbackResult.error,
|
||||
eventId,
|
||||
purchaseId,
|
||||
});
|
||||
}
|
||||
} catch (redisError) {
|
||||
logger.error("Redis purchase failed, attempting fallback:", redisError);
|
||||
// Activate fallback if not already active
|
||||
if (!fallbackStore.isActive) {
|
||||
fallbackStore.activate("Redis purchase operation failed");
|
||||
// Try to sync with Redis data if possible
|
||||
setTimeout(() => {
|
||||
if (fallbackStore.isActive && fallbackStore.events.size === 0) {
|
||||
fallbackStore.attemptReseed();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
throw redisError; // Will be caught by outer try-catch for fallback
|
||||
}
|
||||
} else {
|
||||
throw new Error("Redis not available");
|
||||
}
|
||||
|
||||
const responseTime = Date.now() - startTime;
|
||||
result.responseTime = `${responseTime}ms`;
|
||||
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
// Fallback to in-memory store
|
||||
try {
|
||||
if (!fallbackStore.isActive) {
|
||||
fallbackStore.activate("Redis connection failed during purchase");
|
||||
// Try to sync with Redis data if possible
|
||||
setTimeout(() => {
|
||||
if (fallbackStore.isActive && fallbackStore.events.size === 0) {
|
||||
fallbackStore.attemptReseed();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
const fallbackResult = fallbackStore.purchaseTicket(eventId, purchaseId);
|
||||
|
||||
if (fallbackResult.success) {
|
||||
// Generate PDF for fallback purchase
|
||||
try {
|
||||
const eventStats = fallbackStore.getEventStats(eventId);
|
||||
|
||||
const pdfResult = await pdfGenerator.generateTicketPDF({
|
||||
ticketId: fallbackResult.ticket,
|
||||
eventId,
|
||||
purchaseId,
|
||||
eventName: eventStats?.name || `Event ${eventId}`,
|
||||
eventDescription:
|
||||
eventStats?.description || "Event description not available",
|
||||
timestamp,
|
||||
soldCount: fallbackResult.soldCount,
|
||||
});
|
||||
|
||||
const responseTime = Date.now() - startTime;
|
||||
res.json({
|
||||
success: true,
|
||||
ticket: fallbackResult.ticket,
|
||||
purchaseId,
|
||||
eventId,
|
||||
soldCount: fallbackResult.soldCount,
|
||||
message: "Ticket purchased successfully (fallback mode)!",
|
||||
usingFallback: true,
|
||||
responseTime: `${responseTime}ms`,
|
||||
pdf: {
|
||||
generated: pdfResult.success,
|
||||
filename: pdfResult.filename,
|
||||
downloadUrl: `/tickets/${purchaseId}`,
|
||||
},
|
||||
});
|
||||
|
||||
// Record metrics for successful fallback purchase
|
||||
metrics.recordTicketSale(eventId, "success_fallback");
|
||||
metrics.updateTicketMetrics(
|
||||
eventId,
|
||||
fallbackResult.soldCount,
|
||||
fallbackResult.remainingTickets || 0
|
||||
);
|
||||
|
||||
logger.info(
|
||||
`PDF ticket generated for fallback purchase ${purchaseId}`
|
||||
);
|
||||
} catch (pdfError) {
|
||||
logger.error("PDF generation failed in fallback mode:", pdfError);
|
||||
|
||||
const responseTime = Date.now() - startTime;
|
||||
res.json({
|
||||
success: true,
|
||||
ticket: fallbackResult.ticket,
|
||||
purchaseId,
|
||||
eventId,
|
||||
soldCount: fallbackResult.soldCount,
|
||||
message:
|
||||
"Ticket purchased successfully (fallback mode, PDF generation failed)!",
|
||||
usingFallback: true,
|
||||
responseTime: `${responseTime}ms`,
|
||||
pdf: {
|
||||
generated: false,
|
||||
error: "PDF generation failed",
|
||||
},
|
||||
});
|
||||
|
||||
// Record metrics for successful fallback purchase (even with PDF failure)
|
||||
metrics.recordTicketSale(eventId, "success_fallback");
|
||||
metrics.updateTicketMetrics(
|
||||
eventId,
|
||||
fallbackResult.soldCount,
|
||||
fallbackResult.remainingTickets || 0
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let statusCode = 400;
|
||||
let message = "Purchase failed";
|
||||
} catch (fallbackError) {
|
||||
logger.error("Both Redis and fallback failed:", fallbackError);
|
||||
|
||||
switch (fallbackResult.error) {
|
||||
case "EVENT_NOT_FOUND":
|
||||
statusCode = 404;
|
||||
message = "Event not found";
|
||||
break;
|
||||
case "NO_TICKETS_AVAILABLE":
|
||||
statusCode = 409;
|
||||
message = "No tickets available for this event";
|
||||
break;
|
||||
}
|
||||
// Record metrics for system failure
|
||||
metrics.recordTicketSale(eventId, "system_error");
|
||||
|
||||
// Record metrics for failed fallback purchase
|
||||
metrics.recordTicketSale(eventId, "failed_fallback");
|
||||
logger.logPurchase(eventId, null, purchaseId, false, fallbackError);
|
||||
|
||||
logger.logPurchase(
|
||||
eventId,
|
||||
null,
|
||||
purchaseId,
|
||||
false,
|
||||
fallbackResult.error
|
||||
);
|
||||
res.status(statusCode).json({
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message,
|
||||
errorCode: fallbackResult.error,
|
||||
message: "System temporarily unavailable",
|
||||
eventId,
|
||||
purchaseId,
|
||||
usingFallback: true,
|
||||
});
|
||||
}
|
||||
} catch (fallbackError) {
|
||||
logger.error("Both Redis and fallback failed:", fallbackError);
|
||||
|
||||
// Record metrics for system failure
|
||||
metrics.recordTicketSale(eventId, "system_error");
|
||||
|
||||
logger.logPurchase(eventId, null, purchaseId, false, fallbackError);
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "System temporarily unavailable",
|
||||
eventId,
|
||||
purchaseId,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// Download ticket PDF endpoint
|
||||
app.get("/tickets/:purchaseId", async (req, res) => {
|
||||
try {
|
||||
const purchaseId = req.params.purchaseId;
|
||||
app.get(
|
||||
"/tickets/:purchaseId",
|
||||
security.validatePurchaseId,
|
||||
async (req, res) => {
|
||||
try {
|
||||
const purchaseId = req.params.purchaseId;
|
||||
|
||||
if (!pdfGenerator.ticketExists(purchaseId)) {
|
||||
return res.status(404).json({
|
||||
if (!pdfGenerator.ticketExists(purchaseId)) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Ticket not found",
|
||||
});
|
||||
}
|
||||
|
||||
const filepath = pdfGenerator.getTicketPath(purchaseId);
|
||||
const filename = `ticket-${purchaseId}.pdf`;
|
||||
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`attachment; filename="${filename}"`
|
||||
);
|
||||
|
||||
const fileStream = require("fs").createReadStream(filepath);
|
||||
fileStream.pipe(res);
|
||||
|
||||
logger.info(`PDF ticket downloaded: ${purchaseId}`);
|
||||
} catch (error) {
|
||||
logger.error("Error downloading ticket:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Ticket not found",
|
||||
message: "Failed to download ticket",
|
||||
});
|
||||
}
|
||||
|
||||
const filepath = pdfGenerator.getTicketPath(purchaseId);
|
||||
const filename = `ticket-${purchaseId}.pdf`;
|
||||
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
|
||||
|
||||
const fileStream = require("fs").createReadStream(filepath);
|
||||
fileStream.pipe(res);
|
||||
|
||||
logger.info(`PDF ticket downloaded: ${purchaseId}`);
|
||||
} catch (error) {
|
||||
logger.error("Error downloading ticket:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Failed to download ticket",
|
||||
});
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// PDF management endpoint
|
||||
app.get("/admin/pdf-stats", async (req, res) => {
|
||||
app.get("/admin/pdf-stats", security.adminLimiter, async (req, res) => {
|
||||
try {
|
||||
const stats = pdfGenerator.getStats();
|
||||
res.json({
|
||||
@@ -443,27 +470,32 @@ app.get("/admin/pdf-stats", async (req, res) => {
|
||||
});
|
||||
|
||||
// Cleanup old tickets endpoint
|
||||
app.post("/admin/cleanup-tickets", async (req, res) => {
|
||||
try {
|
||||
const maxAgeHours = req.body.maxAgeHours || 24;
|
||||
const deletedCount = await pdfGenerator.cleanupOldTickets(maxAgeHours);
|
||||
app.post(
|
||||
"/admin/cleanup-tickets",
|
||||
security.adminLimiter,
|
||||
security.validateCleanupRequest,
|
||||
async (req, res) => {
|
||||
try {
|
||||
const maxAgeHours = req.body.maxAgeHours || 24;
|
||||
const deletedCount = await pdfGenerator.cleanupOldTickets(maxAgeHours);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `Cleaned up ${deletedCount} old tickets`,
|
||||
deletedCount,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("Error cleaning up tickets:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Failed to cleanup tickets",
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
message: `Cleaned up ${deletedCount} old tickets`,
|
||||
deletedCount,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("Error cleaning up tickets:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Failed to cleanup tickets",
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// Seed fallback store endpoint
|
||||
app.post("/admin/seed-fallback", async (req, res) => {
|
||||
app.post("/admin/seed-fallback", security.adminLimiter, async (req, res) => {
|
||||
try {
|
||||
if (redisClient.isHealthy()) {
|
||||
// Activate fallback store temporarily for seeding
|
||||
|
||||
Reference in New Issue
Block a user