Skip to content

Commit a701f03

Browse files
committed
Update server index.js and public index.html
1 parent 070751c commit a701f03

File tree

2 files changed

+220
-0
lines changed

2 files changed

+220
-0
lines changed

server/index.js

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ const stateManager = require('./state');
66
// Import metrics history system
77
const metricsHistory = require('./metricsHistory');
88

9+
// Import diagnostic tool
10+
const DiagnosticTool = require('./diagnostics');
11+
912
// --- BEGIN Configuration Loading using configLoader ---
1013
const { loadConfiguration, ConfigurationError } = require('./configLoader');
1114

@@ -405,6 +408,159 @@ app.get('/api/charts', async (req, res) => {
405408
}
406409
});
407410

411+
// Simple diagnostic endpoint for troubleshooting
412+
app.get('/api/diagnostics-simple', async (req, res) => {
413+
try {
414+
// Delete from require cache to force reload
415+
delete require.cache[require.resolve('./diagnostics-simple')];
416+
const SimpleDiagnosticTool = require('./diagnostics-simple');
417+
const tool = new SimpleDiagnosticTool(stateManager, apiClients, pbsApiClients);
418+
const report = await tool.runDiagnostics();
419+
res.json(report);
420+
} catch (error) {
421+
console.error("Simple diagnostics error:", error);
422+
res.status(500).json({ error: error.message });
423+
}
424+
});
425+
426+
// Direct state inspection endpoint
427+
app.get('/api/diagnostics-state', (req, res) => {
428+
try {
429+
const state = stateManager.getState();
430+
const summary = {
431+
timestamp: new Date().toISOString(),
432+
last_update: state.lastUpdate,
433+
update_age_seconds: state.lastUpdate ? Math.floor((Date.now() - new Date(state.lastUpdate).getTime()) / 1000) : null,
434+
guests_count: state.guests?.length || 0,
435+
nodes_count: state.nodes?.length || 0,
436+
pbs_count: state.pbs?.length || 0,
437+
sample_guests: state.guests?.slice(0, 5).map(g => ({
438+
vmid: g.vmid,
439+
name: g.name,
440+
type: g.type,
441+
status: g.status
442+
})) || [],
443+
sample_backups: [],
444+
errors: state.errors || []
445+
};
446+
447+
// Get sample backups
448+
if (state.pbs && Array.isArray(state.pbs)) {
449+
state.pbs.forEach(pbsInstance => {
450+
if (pbsInstance.datastores) {
451+
pbsInstance.datastores.forEach(ds => {
452+
if (ds.snapshots && ds.snapshots.length > 0) {
453+
ds.snapshots.slice(0, 5).forEach(snap => {
454+
summary.sample_backups.push({
455+
store: ds.store,
456+
backup_id: snap['backup-id'],
457+
backup_type: snap['backup-type'],
458+
backup_time: new Date(snap['backup-time'] * 1000).toISOString()
459+
});
460+
});
461+
}
462+
});
463+
}
464+
});
465+
}
466+
467+
res.json(summary);
468+
} catch (error) {
469+
console.error("State inspection error:", error);
470+
res.status(500).json({ error: error.message });
471+
}
472+
});
473+
474+
// Quick diagnostic check endpoint
475+
app.get('/api/diagnostics/check', async (req, res) => {
476+
try {
477+
// Use cached result if available and recent
478+
const cacheKey = 'diagnosticCheck';
479+
const cached = global.diagnosticCache?.[cacheKey];
480+
if (cached && (Date.now() - cached.timestamp) < 60000) { // Cache for 1 minute
481+
return res.json(cached.result);
482+
}
483+
484+
// Run a quick check
485+
delete require.cache[require.resolve('./diagnostics-fixed')];
486+
const DiagnosticToolFixed = require('./diagnostics-fixed');
487+
const diagnosticTool = new DiagnosticToolFixed(stateManager, metricsHistory, apiClients, pbsApiClients);
488+
const report = await diagnosticTool.runDiagnostics();
489+
490+
const hasIssues = report.recommendations &&
491+
report.recommendations.some(r => r.severity === 'critical' || r.severity === 'warning');
492+
493+
const result = {
494+
hasIssues,
495+
criticalCount: report.recommendations?.filter(r => r.severity === 'critical').length || 0,
496+
warningCount: report.recommendations?.filter(r => r.severity === 'warning').length || 0
497+
};
498+
499+
// Cache the result
500+
if (!global.diagnosticCache) global.diagnosticCache = {};
501+
global.diagnosticCache[cacheKey] = { timestamp: Date.now(), result };
502+
503+
res.json(result);
504+
} catch (error) {
505+
console.error("Error in diagnostic check:", error);
506+
res.json({ hasIssues: false }); // Don't show icon on error
507+
}
508+
});
509+
510+
// Raw state endpoint - shows everything
511+
app.get('/api/raw-state', (req, res) => {
512+
const state = stateManager.getState();
513+
const rawState = stateManager.state || {};
514+
res.json({
515+
lastUpdate: state.lastUpdate,
516+
statsLastUpdated: state.stats?.lastUpdated,
517+
rawStateLastUpdated: rawState.stats?.lastUpdated,
518+
guestsLength: state.guests?.length,
519+
rawGuestsLength: rawState.guests?.length,
520+
guestsType: Array.isArray(state.guests) ? 'array' : typeof state.guests,
521+
allKeys: Object.keys(state),
522+
rawKeys: Object.keys(rawState),
523+
serverUptime: process.uptime(),
524+
// Sample guest to see structure
525+
firstGuest: state.guests?.[0],
526+
rawFirstGuest: rawState.guests?.[0]
527+
});
528+
});
529+
530+
// --- Diagnostic Endpoint ---
531+
app.get('/api/diagnostics', async (req, res) => {
532+
try {
533+
console.log('Running diagnostics...');
534+
// Force reload the diagnostic module to get latest changes
535+
delete require.cache[require.resolve('./diagnostics-fixed')];
536+
const DiagnosticToolFixed = require('./diagnostics-fixed');
537+
const diagnosticTool = new DiagnosticToolFixed(stateManager, metricsHistory, apiClients, pbsApiClients);
538+
const report = await diagnosticTool.runDiagnostics();
539+
540+
// Format the report for easy reading
541+
const formattedReport = {
542+
...report,
543+
summary: {
544+
hasIssues: report.recommendations && report.recommendations.some(r => r.severity === 'critical'),
545+
criticalIssues: report.recommendations ? report.recommendations.filter(r => r.severity === 'critical').length : 0,
546+
warnings: report.recommendations ? report.recommendations.filter(r => r.severity === 'warning').length : 0,
547+
info: report.recommendations ? report.recommendations.filter(r => r.severity === 'info').length : 0,
548+
isTimingIssue: report.state && report.state.dataAge === null && report.state.serverUptime < 90
549+
}
550+
};
551+
552+
res.json(formattedReport);
553+
} catch (error) {
554+
console.error("Error running diagnostics:", error);
555+
console.error("Stack trace:", error.stack);
556+
res.status(500).json({
557+
error: "Failed to run diagnostics",
558+
details: error.message,
559+
stack: error.stack
560+
});
561+
}
562+
});
563+
408564
// --- WebSocket Setup ---
409565
const io = new Server(server, {
410566
// Optional: Configure CORS for Socket.IO if needed, separate from Express CORS

src/public/index.html

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@
188188
<span class="text-lg font-medium text-gray-800 dark:text-gray-200">Pulse</span>
189189
</div>
190190
<div class="header-controls flex justify-end items-center gap-4 md:flex-1">
191+
<a id="diagnostics-icon" href="/diagnostics.html" target="_blank" class="hidden p-1 rounded-md text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 focus:outline-none relative" title="Run Diagnostics" aria-label="Run diagnostics">
192+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
193+
<path stroke-linecap="round" stroke-linejoin="round" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
194+
</svg>
195+
<span id="diagnostics-badge" class="absolute -top-1 -right-1 h-2 w-2 bg-red-500 rounded-full hidden animate-pulse"></span>
196+
</a>
191197
<button id="theme-toggle-button" type="button" class="p-1 rounded-md text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 focus:outline-none" aria-label="Toggle theme">
192198
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 block dark:hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
193199
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
@@ -533,6 +539,64 @@
533539
<script>window.PulseApp = {};</script>
534540
<!-- <script src="/app.js" defer></script> -->
535541
<script src="/js/config.js" defer></script>
542+
<script>
543+
// Diagnostic icon visibility checker
544+
(function() {
545+
const checkDiagnostics = async () => {
546+
try {
547+
const response = await fetch('/api/diagnostics/check');
548+
if (response.ok) {
549+
const data = await response.json();
550+
const icon = document.getElementById('diagnostics-icon');
551+
const badge = document.getElementById('diagnostics-badge');
552+
553+
if (data.hasIssues) {
554+
icon.classList.remove('hidden');
555+
if (data.severity === 'error') {
556+
badge.classList.remove('hidden');
557+
} else {
558+
badge.classList.add('hidden');
559+
}
560+
561+
// Update tooltip with issue count
562+
if (data.issueCount) {
563+
icon.title = `Run Diagnostics (${data.issueCount} issue${data.issueCount > 1 ? 's' : ''} detected)`;
564+
}
565+
} else {
566+
icon.classList.add('hidden');
567+
}
568+
}
569+
} catch (error) {
570+
console.error('Failed to check diagnostics:', error);
571+
}
572+
};
573+
574+
// Check immediately after DOM loads
575+
if (document.readyState === 'loading') {
576+
document.addEventListener('DOMContentLoaded', checkDiagnostics);
577+
} else {
578+
checkDiagnostics();
579+
}
580+
581+
// Check periodically
582+
setInterval(checkDiagnostics, 300000); // Every 5 minutes
583+
584+
// Also check when socket reconnects
585+
if (window.io) {
586+
const checkOnConnect = () => {
587+
const socket = window.PulseApp?.socket;
588+
if (socket) {
589+
socket.on('connect', () => {
590+
setTimeout(checkDiagnostics, 5000); // Check 5 seconds after connection
591+
});
592+
} else {
593+
setTimeout(checkOnConnect, 100);
594+
}
595+
};
596+
checkOnConnect();
597+
}
598+
})();
599+
</script>
536600
<script src="/js/state.js" defer></script>
537601
<script src="/js/debounce.js" defer></script>
538602
<script src="/js/virtual-scroll.js" defer></script>

0 commit comments

Comments
 (0)