Invite Diagnostics + Cleanup and Reboot

This commit is contained in:
Raven Scott
2025-12-17 21:44:58 -05:00
parent 8a82681054
commit eecd85c066
7 changed files with 413 additions and 77 deletions
+248 -69
View File
@@ -667,9 +667,204 @@ function closeDiagnosticResult(resultId) {
}
// Run invite diagnostics
// Clean DNS Pass storage
async function cleanDnsPassStorage() {
const buttonEl = document.querySelector('button[onclick="cleanDnsPassStorage()"]');
// Fetch current status to provide better warnings
let statusInfo = {};
try {
const response = await fetch('/api/status');
if (response.ok) {
statusInfo = await response.json();
}
} catch (err) {
console.warn('Could not fetch status for confirmation dialog:', err);
}
let warningMessage = '⚠️ WARNING: This will delete all DNS Pass data and immediately shut down the system for reinitialization.\n\n';
if (statusInfo.peersCount > 0) {
warningMessage += `${statusInfo.peersCount} peers are currently connected - they will be disconnected\n`;
}
if (statusInfo.isMaster && statusInfo.isConnected) {
warningMessage += '• This is a master node with active connections - other nodes will lose connectivity\n';
}
warningMessage += '\n• The system will shut down immediately after cleanup\n• You must manually restart P2NS\n• This action cannot be undone\n\nContinue?';
if (!confirm(warningMessage)) {
return;
}
// Show loading indicator
if (buttonEl) {
buttonEl.disabled = true;
buttonEl.innerHTML = '<span class="inline-block animate-spin rounded-full h-4 w-4 border-t-2 border-white mr-2"></span>Cleaning & Shutting Down...';
}
try {
const response = await fetch('/api/admin/clean-dns-storage', {
method: 'POST'
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
const result = await response.json();
if (window.showNotification) {
window.showNotification('🧹 DNS Pass storage cleaned. System shutting down for reinitialization...', 'success');
}
// Clear any existing invite diagnostics results since the state has changed
clearInviteDiagnosticsResults();
// Show shutdown message
showShutdownMessage(result);
// The system will shut down, so the connection will be lost
// This is expected behavior
} catch (err) {
console.error('Clean DNS storage failed:', err);
if (window.showNotification) window.showNotification('Failed to clean DNS storage: ' + err.message, 'error');
} finally {
// Restore button
if (buttonEl) {
buttonEl.disabled = false;
buttonEl.innerHTML = '🗑️ Clean DNS Storage';
}
}
}
// Clear existing invite diagnostics results
function clearInviteDiagnosticsResults() {
const resultsContainer = document.getElementById('diagnostics-results');
if (resultsContainer) {
// Remove all invite diagnostics results (those with IDs starting with 'invite-diagnostics-')
const inviteResults = resultsContainer.querySelectorAll('[id^="invite-diagnostics-"]');
inviteResults.forEach(result => result.remove());
}
}
// Show restart required message prominently
async function showRestartRequiredMessage() {
const resultId = 'restart-required-' + Date.now();
// Fetch current status to get PID and master status
let pid = 'PID';
let isMaster = false;
try {
const response = await fetch('/api/status');
if (response.ok) {
const status = await response.json();
pid = status.pid || 'PID';
isMaster = status.isMaster || false;
}
} catch (err) {
console.warn('Could not fetch status for restart message:', err);
}
const resultDiv = document.createElement('div');
resultDiv.id = resultId;
resultDiv.className = 'bg-orange-900 border border-orange-600 rounded-lg p-3 mb-2 border-l-4 border-l-orange-500';
const masterFlag = isMaster ? '--master' : '';
resultDiv.innerHTML = `
<div class="flex items-start">
<div class="flex-shrink-0">
<span class="text-xl">⚠️</span>
</div>
<div class="ml-3 flex-1">
<h4 class="text-base font-semibold text-orange-200 mb-1">System Restart Required</h4>
<p class="text-orange-100 text-sm mb-2">
DNS Pass storage cleaned. Process must restart for reinitialization.
</p>
<div class="bg-orange-800 rounded p-2 mb-2">
<p class="text-xs text-orange-200 font-mono">
sudo kill ${pid} && sudo node p2ns.js ${masterFlag}
</p>
</div>
<div class="flex space-x-1">
<button onclick="hideRestartMessage('${resultId}')" class="px-2 py-0.5 bg-orange-700 text-orange-100 rounded hover:bg-orange-600 text-xs">
×
</button>
</div>
</div>
</div>
`;
// Add to diagnostics results container at the top
const resultsContainer = document.getElementById('diagnostics-results');
if (resultsContainer) {
resultsContainer.insertBefore(resultDiv, resultsContainer.firstChild);
}
}
// Show shutdown message
function showShutdownMessage(result) {
const resultId = 'shutdown-message-' + Date.now();
const resultDiv = document.createElement('div');
resultDiv.id = resultId;
resultDiv.className = 'bg-red-900 border border-red-600 rounded-lg p-3 mb-2 border-l-4 border-l-red-500';
resultDiv.innerHTML = `
<div class="flex items-start">
<div class="flex-shrink-0">
<span class="text-xl">🔄</span>
</div>
<div class="ml-3 flex-1">
<h4 class="text-base font-semibold text-red-200 mb-1">System Shutting Down</h4>
<p class="text-red-100 text-sm mb-2">
DNS Pass storage cleaned. Process shutting down for reinitialization.
</p>
<div class="bg-red-800 rounded p-2 mb-2">
<p class="text-xs text-red-200 font-mono">
${result.restartInstructions || 'sudo node p2ns.js' + (window.p2nsIsMaster ? ' --master' : '')}
</p>
</div>
<div class="flex space-x-1">
<button onclick="hideShutdownMessage('${resultId}')" class="px-2 py-0.5 bg-red-700 text-red-100 rounded hover:bg-red-600 text-xs">
×
</button>
</div>
</div>
</div>
`;
// Add to diagnostics results container at the top
const resultsContainer = document.getElementById('diagnostics-results');
if (resultsContainer) {
resultsContainer.insertBefore(resultDiv, resultsContainer.firstChild);
}
}
// Hide shutdown message
function hideShutdownMessage(resultId) {
const resultDiv = document.getElementById(resultId);
if (resultDiv) {
resultDiv.remove();
}
}
// Hide restart message
function hideRestartMessage(resultId) {
const resultDiv = document.getElementById(resultId);
if (resultDiv) {
resultDiv.remove();
}
}
async function runInviteDiagnostics() {
const buttonEl = document.querySelector('button[onclick="runInviteDiagnostics()"]');
// Clear previous invite diagnostics results before running new test
clearInviteDiagnosticsResults();
// Show loading indicator
if (buttonEl) {
buttonEl.disabled = true;
@@ -702,111 +897,95 @@ function displayInviteDiagnostics(diagnostics) {
const resultId = 'invite-diagnostics-' + Date.now();
const resultDiv = document.createElement('div');
resultDiv.id = resultId;
resultDiv.className = 'bg-gray-800 rounded-lg p-4 mb-4 border border-gray-600';
resultDiv.className = 'bg-gray-800 rounded-lg p-3 mb-2 border border-gray-600';
const headerDiv = document.createElement('div');
headerDiv.className = 'flex justify-between items-center mb-3';
headerDiv.className = 'flex justify-between items-center mb-2';
const titleDiv = document.createElement('div');
titleDiv.className = 'flex items-center';
titleDiv.innerHTML = `
<h3 class="text-lg font-semibold text-white">Invite Diagnostics</h3>
<span class="ml-2 text-sm text-gray-400">${new Date(diagnostics.timestamp).toLocaleString()}</span>
<h4 class="text-base font-semibold text-white">Invite Diagnostics</h4>
<span class="ml-2 text-xs text-gray-400">${new Date(diagnostics.timestamp).toLocaleString()}</span>
`;
const buttonContainer = document.createElement('div');
buttonContainer.className = 'flex space-x-2';
buttonContainer.className = 'flex space-x-1';
headerDiv.appendChild(titleDiv);
headerDiv.appendChild(buttonContainer);
const contentDiv = document.createElement('div');
contentDiv.className = 'space-y-3';
contentDiv.className = 'space-y-2';
// Compact status summary
const statusItems = [
{ label: 'Type', value: diagnostics.nodeType, color: 'text-white' },
{ label: 'DNS Pass', value: diagnostics.dnsPassInitialized ? 'Yes' : 'No', color: diagnostics.dnsPassInitialized ? 'text-green-400' : 'text-red-400' },
{ label: 'Peers', value: `${diagnostics.connectedPeers} connected`, color: 'text-white' }
];
// Basic info
contentDiv.innerHTML += `
<div class="grid grid-cols-2 gap-4">
<div class="bg-gray-700 rounded p-3">
<div class="text-sm text-gray-400">Node Type</div>
<div class="text-lg font-semibold text-white">${diagnostics.nodeType}</div>
</div>
<div class="bg-gray-700 rounded p-3">
<div class="text-sm text-gray-400">DNS Pass Initialized</div>
<div class="text-lg font-semibold ${diagnostics.dnsPassInitialized ? 'text-green-400' : 'text-red-400'}">
${diagnostics.dnsPassInitialized ? 'Yes' : 'No'}
<div class="grid grid-cols-3 gap-2 text-sm">
${statusItems.map(item => `
<div class="bg-gray-700 rounded px-2 py-1">
<div class="text-gray-400 text-xs">${item.label}</div>
<div class="font-semibold ${item.color}">${item.value}</div>
</div>
</div>
`).join('')}
</div>
`;
// Peer connections
contentDiv.innerHTML += `
<div class="bg-gray-700 rounded p-3">
<div class="text-sm text-gray-400 mb-2">Peer Connections</div>
<div class="text-lg font-semibold text-white">${diagnostics.connectedPeers} connected</div>
</div>
`;
// Alerts and issues in compact format
const alerts = [];
// Failed peers
if (diagnostics.failedInvitePeers.length > 0) {
contentDiv.innerHTML += `
<div class="bg-red-900 border border-red-600 rounded p-3">
<div class="text-sm text-red-400 mb-2">Failed Invite Peers</div>
<div class="text-sm text-red-300">${diagnostics.failedInvitePeers.join(', ')}</div>
</div>
`;
alerts.push(`<div class="text-red-300 text-xs">❌ ${diagnostics.failedInvitePeers.length} failed peers</div>`);
}
// Pending ACKs
if (diagnostics.pendingInviteAcks.length > 0) {
contentDiv.innerHTML += `
<div class="bg-yellow-900 border border-yellow-600 rounded p-3">
<div class="text-sm text-yellow-400 mb-2">Pending Invite Acknowledgments</div>
<div class="text-sm text-yellow-300">${diagnostics.pendingInviteAcks.length} pending</div>
</div>
`;
alerts.push(`<div class="text-yellow-300 text-xs">⏳ ${diagnostics.pendingInviteAcks.length} pending ACKs</div>`);
}
if (diagnostics.consecutiveInviteFailures > 0) {
const severity = diagnostics.consecutiveInviteFailures >= 3 ? 'text-red-300' : 'text-orange-300';
const icon = diagnostics.consecutiveInviteFailures >= 3 ? '🚨' : '⚠️';
alerts.push(`<div class="${severity} text-xs">${icon} ${diagnostics.consecutiveInviteFailures} consecutive failures</div>`);
}
// Connection issues
if (diagnostics.connectionIssues.length > 0) {
alerts.push(`<div class="text-orange-300 text-xs">🔌 ${diagnostics.connectionIssues.length} connection issues</div>`);
}
if (alerts.length > 0) {
contentDiv.innerHTML += `
<div class="bg-orange-900 border border-orange-600 rounded p-3">
<div class="text-sm text-orange-400 mb-2">Connection Issues</div>
<div class="text-sm text-orange-300">${diagnostics.connectionIssues.join('<br>')}</div>
<div class="bg-gray-700 rounded p-2">
<div class="text-xs text-gray-400 mb-1">Issues:</div>
<div class="space-y-1">${alerts.join('')}</div>
</div>
`;
}
// Channel health summary
const healthyInviteChannels = Object.values(diagnostics.inviteChannels).filter(ch => ch.opened).length;
const healthyRequestChannels = Object.values(diagnostics.requestChannels).filter(ch => ch.opened).length;
// Channel health in compact format
const healthyChannels = Object.values(diagnostics.inviteChannels).filter(ch => ch.opened).length;
const totalPeers = diagnostics.connectedPeers;
contentDiv.innerHTML += `
<div class="bg-gray-700 rounded p-3">
<div class="text-sm text-gray-400 mb-2">Channel Health</div>
<div class="grid grid-cols-2 gap-4 text-sm">
<div>
<span class="text-gray-300">Invite Channels:</span>
<span class="ml-2 font-semibold ${healthyInviteChannels === diagnostics.connectedPeers ? 'text-green-400' : 'text-yellow-400'}">
${healthyInviteChannels}/${diagnostics.connectedPeers} healthy
</span>
</div>
<div>
<span class="text-gray-300">Request Channels:</span>
<span class="ml-2 font-semibold ${healthyRequestChannels === diagnostics.connectedPeers ? 'text-green-400' : 'text-yellow-400'}">
${healthyRequestChannels}/${diagnostics.connectedPeers} healthy
</span>
</div>
<div class="bg-gray-700 rounded p-2">
<div class="text-xs text-gray-400 mb-1">Channel Health:</div>
<div class="grid grid-cols-2 gap-2 text-xs">
<div class="text-gray-300">Invite: <span class="${healthyChannels === totalPeers ? 'text-green-400' : 'text-yellow-400'} font-semibold">${healthyChannels}/${totalPeers}</span></div>
<div class="text-gray-300">Request: <span class="${healthyChannels === totalPeers ? 'text-green-400' : 'text-yellow-400'} font-semibold">${healthyChannels}/${totalPeers}</span></div>
</div>
</div>
`;
// Recommendations
// Compact recommendations
if (diagnostics.recommendations.length > 0) {
contentDiv.innerHTML += `
<div class="bg-blue-900 border border-blue-600 rounded p-3">
<div class="text-sm text-blue-400 mb-2">Recommendations</div>
<ul class="text-sm text-blue-300 space-y-1">
<div class="bg-blue-900 border border-blue-600 rounded p-2">
<div class="text-xs text-blue-400 mb-1">💡 Recommendations:</div>
<ul class="text-xs text-blue-300 space-y-0.5">
${diagnostics.recommendations.map(rec => `<li>• ${rec}</li>`).join('')}
</ul>
</div>
@@ -819,8 +998,8 @@ function displayInviteDiagnostics(diagnostics) {
// Add close button
const closeBtn = document.createElement('button');
closeBtn.id = `close-btn-${resultId}`;
closeBtn.className = 'ml-2 px-2 py-1 text-xs bg-gray-500 text-white rounded hover:bg-gray-600';
closeBtn.textContent = 'Close';
closeBtn.className = 'px-1.5 py-0.5 text-xs bg-gray-500 text-white rounded hover:bg-gray-600';
closeBtn.textContent = '×';
closeBtn.addEventListener('click', () => {
closeDiagnosticResult(resultId);
});
@@ -831,9 +1010,6 @@ function displayInviteDiagnostics(diagnostics) {
if (resultsContainer) {
resultsContainer.insertBefore(resultDiv, resultsContainer.firstChild);
}
// Scroll to top of results
resultDiv.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// Setup Enter key handlers for diagnostics forms
@@ -927,6 +1103,9 @@ window.renderDiagnostics = renderDiagnostics;
window.cancelStream = cancelStream;
window.closeDiagnosticResult = closeDiagnosticResult;
window.runInviteDiagnostics = runInviteDiagnostics;
window.cleanDnsPassStorage = cleanDnsPassStorage;
window.hideRestartMessage = hideRestartMessage;
window.hideShutdownMessage = hideShutdownMessage;
// Setup Enter key handlers when DOM is ready
if (document.readyState === 'loading') {