Auth fixes, show correct cost each value on pos

This commit is contained in:
2026-05-28 14:15:13 -04:00
parent 421b3d5922
commit 8c707e28ea
21 changed files with 564 additions and 82 deletions
@@ -51,7 +51,8 @@ router.get('/stats', async (req, res) => {
console.log(`[STATS] Getting DB connection... (excludeCherryBox: ${excludeCB})`);
const { connection, release } = await getDbConnection();
console.log(`[STATS] DB connection obtained in ${Date.now() - startTime}ms`);
try {
const { whereClause, params, dateRange } = getTimeRangeConditions(timeRange, startDate, endDate);
// Main order stats query (optionally excludes Cherry Box orders)
@@ -374,33 +375,27 @@ router.get('/stats', async (req, res) => {
}
};
return { response, release };
};
// Race between the main operation and timeout
let result;
try {
result = await Promise.race([mainOperation(), timeoutPromise]);
} catch (error) {
// If it's a timeout, we don't have a release function to call
if (error.message.includes('timeout')) {
console.log(`[STATS] Request timed out in ${Date.now() - startTime}ms`);
throw error;
return response;
} finally {
// Always release the connection regardless of whether the outer Promise.race
// used our result. If the timeout wins, this IIFE keeps running in the
// background until MySQL responds, then this finally releases. Without it,
// every timed-out request permanently leaks one pool slot.
release();
}
// For other errors, re-throw
throw error;
}
const { response, release } = result;
// Release connection back to pool
if (release) release();
};
const response = await Promise.race([mainOperation(), timeoutPromise]);
console.log(`[STATS] Request completed in ${Date.now() - startTime}ms`);
res.json(response);
} catch (error) {
console.error('Error in /stats:', error);
if (error.message.includes('timeout')) {
console.log(`[STATS] Request timed out in ${Date.now() - startTime}ms`);
} else {
console.error('Error in /stats:', error);
}
console.log(`[STATS] Request failed in ${Date.now() - startTime}ms`);
res.status(500).json({ error: error.message });
}
@@ -22,6 +22,7 @@ router.get('/', async (req, res) => {
console.log(`[OPERATIONS-METRICS] Getting DB connection...`);
const { connection, release } = await getDbConnection();
console.log(`[OPERATIONS-METRICS] DB connection obtained in ${Date.now() - startTime}ms`);
try {
const { whereClause, params, dateRange } = getTimeRangeConditions(timeRange, startDate, endDate);
@@ -370,29 +371,26 @@ router.get('/', async (req, res) => {
trend,
};
return { response, release };
return response;
} finally {
// Always release the connection regardless of who wins Promise.race.
// If the timeout wins, this IIFE keeps running until MySQL responds; this
// finally ensures the connection still returns to the pool.
release();
}
};
let result;
try {
result = await Promise.race([mainOperation(), timeoutPromise]);
} catch (error) {
if (error.message.includes('timeout')) {
console.log(`[OPERATIONS-METRICS] Request timed out in ${Date.now() - startTime}ms`);
throw error;
}
throw error;
}
const { response, release } = result;
if (release) release();
const response = await Promise.race([mainOperation(), timeoutPromise]);
console.log(`[OPERATIONS-METRICS] Request completed in ${Date.now() - startTime}ms`);
res.json(response);
} catch (error) {
console.error('Error in /operations-metrics:', error);
if (error.message.includes('timeout')) {
console.log(`[OPERATIONS-METRICS] Request timed out in ${Date.now() - startTime}ms`);
} else {
console.error('Error in /operations-metrics:', error);
}
console.log(`[OPERATIONS-METRICS] Request failed in ${Date.now() - startTime}ms`);
res.status(500).json({ error: error.message });
}
@@ -281,6 +281,7 @@ router.get('/', async (req, res) => {
console.log(`[PAYROLL-METRICS] Getting DB connection...`);
const { connection, release } = await getDbConnection();
console.log(`[PAYROLL-METRICS] DB connection obtained in ${Date.now() - startTime}ms`);
try {
// Build query for the pay period
const periodStart = payPeriod.start.toJSDate();
@@ -373,29 +374,26 @@ router.get('/', async (req, res) => {
byWeek: hoursData.byWeek,
};
return { response, release };
return response;
} finally {
// Always release the connection regardless of who wins Promise.race.
// If the timeout wins, this IIFE keeps running until MySQL responds; this
// finally ensures the connection still returns to the pool.
release();
}
};
let result;
try {
result = await Promise.race([mainOperation(), timeoutPromise]);
} catch (error) {
if (error.message.includes('timeout')) {
console.log(`[PAYROLL-METRICS] Request timed out in ${Date.now() - startTime}ms`);
throw error;
}
throw error;
}
const { response, release } = result;
if (release) release();
const response = await Promise.race([mainOperation(), timeoutPromise]);
console.log(`[PAYROLL-METRICS] Request completed in ${Date.now() - startTime}ms`);
res.json(response);
} catch (error) {
console.error('Error in /payroll-metrics:', error);
if (error.message.includes('timeout')) {
console.log(`[PAYROLL-METRICS] Request timed out in ${Date.now() - startTime}ms`);
} else {
console.error('Error in /payroll-metrics:', error);
}
console.log(`[PAYROLL-METRICS] Request failed in ${Date.now() - startTime}ms`);
res.status(500).json({ error: error.message });
}
@@ -66,6 +66,11 @@ if (!process.env.JWT_SECRET) {
const app = express();
const PORT = Number(process.env.ACOT_PORT) || 3012;
// Trust X-Forwarded-* only when the immediate hop is loopback (Caddy on the same
// host). Required for the KIOSK_IPS bypass in shared/auth/middleware.js to see
// real client IPs instead of 127.0.0.1.
app.set('trust proxy', 'loopback');
// Postgres pool for authenticate() (user/permission lookups against inventory_db).
// All MySQL access goes through db/connection.js (separate, ssh-tunneled).
const pool = new Pool({
+5
View File
@@ -62,6 +62,11 @@ if (!process.env.JWT_SECRET) {
const app = express();
const PORT = Number(process.env.DASHBOARD_PORT) || 3015;
// Trust X-Forwarded-* only when the immediate hop is loopback (Caddy on the same
// host). Required for the KIOSK_IPS bypass in shared/auth/middleware.js to see
// real client IPs instead of 127.0.0.1.
app.set('trust proxy', 'loopback');
// Single Postgres pool — used by authenticate() to load user permissions.
// All four vendors share this pool (auth lookups are the only DB hits at runtime).
const pool = createPool('DB');