From 9e20acd6470c5fc6064372f1e924300c911905d6 Mon Sep 17 00:00:00 2001 From: Anso Date: Mon, 25 May 2026 12:10:40 -0400 Subject: [PATCH] chore(dashboard): add developer-mode timing diagnostics on dashboard routes (#1219) Both dashboard endpoints (/configuration and /stack-restarts) execute on the hot path of every dashboard mount or refresh, and one of them (/configuration via buildLocalConfigurationStatus) reads from a dozen database tables. When an operator reports a slow dashboard on a large deployment, there is currently no instrumentation to point at which endpoint is the offender. Gate two new debug lines behind isDebugEnabled (the existing developer-mode flag, sourced from DatabaseService.global_settings.developer_mode). Each line reports the elapsed milliseconds and a single contextual field (nodeId, row count, days window). Both endpoints exit unchanged when developer mode is off; the timing measurement and console call are skipped entirely, not just suppressed. Both error paths already log via console.error and stay that way; per backend/src/utils/debug.ts comments, error paths are exempt from the debug gate. --- backend/src/routes/dashboard.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/backend/src/routes/dashboard.ts b/backend/src/routes/dashboard.ts index 7904ca55..692895ba 100644 --- a/backend/src/routes/dashboard.ts +++ b/backend/src/routes/dashboard.ts @@ -2,6 +2,7 @@ import { Router, type Request, type Response } from 'express'; import { DatabaseService, type StackRestartSummary } from '../services/DatabaseService'; import { CloudBackupService } from '../services/CloudBackupService'; import { effectiveTier, effectiveVariant } from '../middleware/tierGates'; +import { isDebugEnabled } from '../utils/debug'; import type { LicenseTier, LicenseVariant } from '../services/license-types'; export const dashboardRouter = Router(); @@ -157,12 +158,20 @@ export function buildLocalConfigurationStatus( // All routes below are protected by the global authGate mounted at app.use('/api', authGate) dashboardRouter.get('/configuration', (req: Request, res: Response): void => { try { + const debug = isDebugEnabled(); + const startedAt = debug ? Date.now() : 0; const nodeId = req.nodeId ?? 0; const userId = req.user?.userId ?? 0; const tier = effectiveTier(req); const variant = effectiveVariant(req); - res.json(buildLocalConfigurationStatus(nodeId, userId, tier, variant)); + const payload = buildLocalConfigurationStatus(nodeId, userId, tier, variant); + if (debug) { + console.debug( + `[Dashboard:debug] /configuration built in ${Date.now() - startedAt} ms (nodeId=${nodeId})`, + ); + } + res.json(payload); } catch (error) { console.error('[Dashboard] Failed to build configuration status:', error); res.status(500).json({ error: 'Failed to fetch configuration status' }); @@ -171,12 +180,19 @@ dashboardRouter.get('/configuration', (req: Request, res: Response): void => { dashboardRouter.get('/stack-restarts', (req: Request, res: Response): void => { try { + const debug = isDebugEnabled(); + const startedAt = debug ? Date.now() : 0; const db = DatabaseService.getInstance(); const nodeId = req.nodeId ?? 0; const rawDays = parseInt(String(req.query['days'] ?? '7'), 10); const days = isNaN(rawDays) || rawDays < 1 ? 7 : Math.min(rawDays, 30); const result: StackRestartSummary[] = db.getStackRestartSummary(nodeId, days); + if (debug) { + console.debug( + `[Dashboard:debug] /stack-restarts returned ${result.length} rows for nodeId=${nodeId} over ${days}d in ${Date.now() - startedAt} ms`, + ); + } res.json(result); } catch (error) { console.error('[Dashboard] Failed to fetch stack restarts:', error);