Add mobile dashboard, allow longer auth sessions

This commit is contained in:
2026-07-20 11:51:09 -04:00
parent d779228485
commit 6c973f0a9d
13 changed files with 1219 additions and 67 deletions
+16 -8
View File
@@ -12,13 +12,20 @@ export function createAuthRoutes({ pool }) {
// shared/auth/middleware.js and is used by downstream services. Auth-server's surface is
// small enough that a local copy is fine; the security boundary is the JWT verify step.
async function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Authentication required' });
}
const token = authHeader.split(' ')[1];
let decoded;
try {
decoded = jwt.verify(token, process.env.JWT_SECRET);
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
// DB failures are 500, not 401 — the frontend ends the session on 401,
// and a transient outage must not log users out.
try {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Authentication required' });
}
const token = authHeader.split(' ')[1];
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const result = await pool.query(
'SELECT id, username, email, is_admin, rocket_chat_user_id FROM users WHERE id = $1',
[decoded.userId]
@@ -29,7 +36,8 @@ export function createAuthRoutes({ pool }) {
req.user = result.rows[0];
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
console.error('Auth DB error:', error);
res.status(500).json({ error: 'Server error' });
}
}
@@ -58,7 +66,7 @@ export function createAuthRoutes({ pool }) {
const token = jwt.sign(
{ userId: user.id, username: user.username },
process.env.JWT_SECRET,
{ expiresIn: '8h' }
{ expiresIn: '7d' }
);
const permissions = await getUserPermissions(user.id);
res.json({
+5
View File
@@ -39,6 +39,11 @@ logger.info({
const app = express();
const port = Number(process.env.AUTH_PORT) || 3011;
// Behind Caddy on localhost: without this, req.ip is ::1 for every request and
// the rate limiters put ALL users in one shared bucket (10 logins/15min for
// the whole office). Same setting as inventory-server's server.js.
app.set('trust proxy', 'loopback');
const pool = new Pool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
@@ -314,7 +314,6 @@ async function syncSettingsProductTable() {
lead_time_days,
days_of_stock,
safety_stock,
forecast_method,
exclude_from_forecast
)
SELECT
@@ -322,7 +321,6 @@ async function syncSettingsProductTable() {
CAST(NULL AS INTEGER),
CAST(NULL AS INTEGER),
COALESCE((SELECT setting_value::int FROM settings_global WHERE setting_key = 'default_safety_stock_units'), 0),
CAST(NULL AS VARCHAR),
FALSE
FROM
public.products p
+7 -9
View File
@@ -136,7 +136,7 @@ router.put('/products/:pid', async (req, res) => {
const pool = req.app.locals.pool;
try {
const { pid } = req.params;
const { lead_time_days, days_of_stock, safety_stock, forecast_method, exclude_from_forecast } = req.body;
const { lead_time_days, days_of_stock, safety_stock, exclude_from_forecast } = req.body;
console.log(`[Config Route] Updating product settings for ${pid}:`, req.body);
@@ -149,10 +149,10 @@ router.put('/products/:pid', async (req, res) => {
if (checkProduct.length === 0) {
// Insert if it doesn't exist
await pool.query(
`INSERT INTO settings_product
(pid, lead_time_days, days_of_stock, safety_stock, forecast_method, exclude_from_forecast)
VALUES ($1, $2, $3, $4, $5, $6)`,
[pid, lead_time_days, days_of_stock, safety_stock, forecast_method, exclude_from_forecast]
`INSERT INTO settings_product
(pid, lead_time_days, days_of_stock, safety_stock, exclude_from_forecast)
VALUES ($1, $2, $3, $4, $5)`,
[pid, lead_time_days, days_of_stock, safety_stock, exclude_from_forecast]
);
} else {
// Update if it exists
@@ -161,11 +161,10 @@ router.put('/products/:pid', async (req, res) => {
SET lead_time_days = $2,
days_of_stock = $3,
safety_stock = $4,
forecast_method = $5,
exclude_from_forecast = $6,
exclude_from_forecast = $5,
updated_at = CURRENT_TIMESTAMP
WHERE pid::text = $1`,
[pid, lead_time_days, days_of_stock, safety_stock, forecast_method, exclude_from_forecast]
[pid, lead_time_days, days_of_stock, safety_stock, exclude_from_forecast]
);
}
@@ -190,7 +189,6 @@ router.post('/products/:pid/reset', async (req, res) => {
SET lead_time_days = NULL,
days_of_stock = NULL,
safety_stock = 0,
forecast_method = NULL,
exclude_from_forecast = false,
updated_at = CURRENT_TIMESTAMP
WHERE pid::text = $1`,