feat: add admin-only status page with cache stats
- New /api/dashboard/status endpoint (admin-only, 403 for non-admins) - Returns server info (uptime, Node version, memory usage) - Returns polling mode and interval - Returns cache stats: entry count, total size, per-key breakdown with item count, size in KB, and TTL remaining - Status button in admin controls header - Collapsible status panel with grid layout - Responsive: single column on mobile
This commit is contained in:
@@ -600,4 +600,38 @@ router.get('/user-summary', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Admin-only status page with cache stats
|
||||
router.get('/status', (req, res) => {
|
||||
try {
|
||||
const userCookie = req.cookies.emby_user;
|
||||
if (!userCookie) {
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
const user = JSON.parse(userCookie);
|
||||
if (!user.isAdmin) {
|
||||
return res.status(403).json({ error: 'Admin access required' });
|
||||
}
|
||||
|
||||
const cacheStats = cache.getStats();
|
||||
const uptime = process.uptime();
|
||||
|
||||
res.json({
|
||||
server: {
|
||||
uptimeSeconds: Math.floor(uptime),
|
||||
nodeVersion: process.version,
|
||||
memoryUsageMB: Math.round(process.memoryUsage().rss / 1024 / 1024 * 10) / 10,
|
||||
heapUsedMB: Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 10) / 10,
|
||||
heapTotalMB: Math.round(process.memoryUsage().heapTotal / 1024 / 1024 * 10) / 10
|
||||
},
|
||||
polling: {
|
||||
enabled: POLLING_ENABLED,
|
||||
intervalMs: POLLING_ENABLED ? require('../utils/poller').POLL_INTERVAL : 0
|
||||
},
|
||||
cache: cacheStats
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: 'Failed to get status', details: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -29,6 +29,40 @@ class MemoryCache {
|
||||
clear() {
|
||||
this.store.clear();
|
||||
}
|
||||
|
||||
getStats() {
|
||||
const now = Date.now();
|
||||
const entries = [];
|
||||
let totalSize = 0;
|
||||
|
||||
for (const [key, entry] of this.store.entries()) {
|
||||
const json = JSON.stringify(entry.value);
|
||||
const sizeBytes = Buffer.byteLength(json, 'utf8');
|
||||
totalSize += sizeBytes;
|
||||
const ttlRemaining = Math.max(0, entry.expiresAt - now);
|
||||
const expired = now > entry.expiresAt;
|
||||
let itemCount = null;
|
||||
if (Array.isArray(entry.value)) {
|
||||
itemCount = entry.value.length;
|
||||
} else if (entry.value && typeof entry.value === 'object') {
|
||||
if (Array.isArray(entry.value.records)) itemCount = entry.value.records.length;
|
||||
else if (Array.isArray(entry.value.slots)) itemCount = entry.value.slots.length;
|
||||
}
|
||||
entries.push({
|
||||
key,
|
||||
sizeBytes,
|
||||
itemCount,
|
||||
ttlRemainingMs: ttlRemaining,
|
||||
expired
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
entryCount: this.store.size,
|
||||
totalSizeBytes: totalSize,
|
||||
entries
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const cache = new MemoryCache();
|
||||
|
||||
Reference in New Issue
Block a user