Fix: Restore website functionality - all pages and APIs working

This commit is contained in:
Local Server
2026-01-14 07:16:04 -06:00
parent dc58a8ae5f
commit 9f659a2c59
41 changed files with 10890 additions and 3029 deletions

View File

@@ -17,10 +17,58 @@ const pool = new Pool({
keepAlive: true, // TCP keepalive
keepAliveInitialDelayMillis: 10000,
statement_timeout: 30000, // 30s query timeout
query_timeout: 30000, // SAFEGUARD: Force query timeout at pool level
});
pool.on("connect", () => logger.info("✓ PostgreSQL connected"));
pool.on("error", (err) => logger.error("PostgreSQL error:", err));
// SAFEGUARD: Track pool health
let poolConnected = false;
let connectionAttempts = 0;
const MAX_CONNECTION_ATTEMPTS = 3;
pool.on("connect", (client) => {
poolConnected = true;
connectionAttempts = 0;
logger.info("✓ PostgreSQL connected", {
total: pool.totalCount,
idle: pool.idleCount,
waiting: pool.waitingCount,
});
});
pool.on("error", (err, client) => {
poolConnected = false;
connectionAttempts++;
logger.error("💥 PostgreSQL pool error", {
error: err.message,
code: err.code,
attempts: connectionAttempts,
pool: {
total: pool.totalCount,
idle: pool.idleCount,
waiting: pool.waitingCount,
},
});
// SAFEGUARD: Critical failure detection
if (connectionAttempts >= MAX_CONNECTION_ATTEMPTS) {
logger.error(
"🚨 Database connection critically unstable - manual intervention required"
);
}
});
pool.on("acquire", (client) => {
logger.debug("Pool client acquired", {
total: pool.totalCount,
idle: pool.idleCount,
});
});
pool.on("release", (err, client) => {
if (err) {
logger.warn("Client released with error", { error: err.message });
}
});
// Query cache for SELECT statements with crypto-based keys
const queryCache = new Map();
@@ -28,6 +76,7 @@ const queryCacheOrder = []; // LRU tracking
const QUERY_CACHE_TTL = 15000; // 15 seconds (increased)
const QUERY_CACHE_MAX_SIZE = 500; // 500 cached queries (increased)
const SLOW_QUERY_THRESHOLD = 50; // 50ms threshold (stricter)
const QUERY_TIMEOUT = 35000; // SAFEGUARD: 35s query timeout (slightly higher than pool's 30s)
// Generate fast cache key using crypto hash
const getCacheKey = (text, params) => {
@@ -53,7 +102,22 @@ const query = async (text, params) => {
}
try {
const res = await pool.query(text, params);
// SAFEGUARD: Add query timeout wrapper
const queryPromise = pool.query(text, params);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(
new Error(
`Query timeout after ${QUERY_TIMEOUT}ms: ${text.substring(
0,
50
)}...`
)
);
}, QUERY_TIMEOUT);
});
const res = await Promise.race([queryPromise, timeoutPromise]);
const duration = Date.now() - start;
// Cache SELECT queries with LRU eviction
@@ -84,11 +148,22 @@ const query = async (text, params) => {
} catch (error) {
const duration = Date.now() - start;
logger.error("Query error", {
text: text.substring(0, 100),
error: error.message,
duration,
code: error.code,
duration,
text: text.substring(0, 100),
});
// SAFEGUARD: Clear potentially corrupted cache entry
if (isSelect) {
const cacheKey = getCacheKey(text, params);
queryCache.delete(cacheKey);
const index = queryCacheOrder.indexOf(cacheKey);
if (index > -1) {
queryCacheOrder.splice(index, 1);
}
}
throw error;
}
};
@@ -141,34 +216,75 @@ const clearQueryCache = (pattern) => {
};
// Health check with pool metrics
const healthCheck = async () => {
try {
const result = await query(
"SELECT NOW() as time, current_database() as database"
const healthCheck = async (timeoutMs = 5000) => {
// SAFEGUARD: Wrap health check in timeout promise
const healthPromise = (async () => {
try {
const result = await query(
"SELECT NOW() as time, current_database() as database"
);
return {
healthy: true,
database: result.rows[0].database,
timestamp: result.rows[0].time,
pool: {
total: pool.totalCount,
idle: pool.idleCount,
waiting: pool.waitingCount,
connected: poolConnected,
},
cache: {
size: queryCache.size,
maxSize: QUERY_CACHE_MAX_SIZE,
},
};
} catch (error) {
logger.error("Database health check failed:", error);
return {
healthy: false,
error: error.message,
pool: {
total: pool.totalCount,
idle: pool.idleCount,
waiting: pool.waitingCount,
connected: poolConnected,
},
};
}
})();
// SAFEGUARD: Add timeout protection
const timeoutPromise = new Promise((_, reject) => {
setTimeout(
() => reject(new Error(`Health check timeout after ${timeoutMs}ms`)),
timeoutMs
);
return {
healthy: true,
database: result.rows[0].database,
timestamp: result.rows[0].time,
pool: {
total: pool.totalCount,
idle: pool.idleCount,
waiting: pool.waitingCount,
},
cache: {
size: queryCache.size,
maxSize: QUERY_CACHE_MAX_SIZE,
},
};
});
return Promise.race([healthPromise, timeoutPromise]);
};
// SAFEGUARD: Graceful pool shutdown for scripts/testing
const closePool = async () => {
try {
await pool.end();
logger.info("Database pool closed gracefully");
return true;
} catch (error) {
logger.error("Database health check failed:", error);
return {
healthy: false,
error: error.message,
};
logger.error("Error closing database pool:", error);
return false;
}
};
// SAFEGUARD: Get pool status for monitoring
const getPoolStatus = () => ({
total: pool.totalCount,
idle: pool.idleCount,
waiting: pool.waitingCount,
connected: poolConnected,
cacheSize: queryCache.size,
});
module.exports = {
pool,
query,
@@ -176,4 +292,6 @@ module.exports = {
batchQuery,
clearQueryCache,
healthCheck,
closePool,
getPoolStatus,
};