@@ -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-x s 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-4 00">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-7 00 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-6 00 rounded p-3 ">
<div class="text-sm text-o range -400 mb-2 ">Connection Issues</div>
<div class="text-sm text-orange-300 "> ${ diagnostics . connectionIssues . join ( '<br> ' ) } </div>
<div class="bg-gray-7 00 rounded p-2 ">
<div class="text-x s text-g ray -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-x s text-gray-400 mb-1 ">Channel Health: </div>
<div class="grid grid-cols-2 gap-2 text-x s">
<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>
` ;
// R ecommendations
// Compact r ecommendations
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-x s text-blue-400 mb-1 ">💡 Recommendations: </div>
<ul class="text-x s 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' ) {