11
index.js
11
index.js
@@ -72,6 +72,7 @@ app.get('/tail/api/files', (req, res) => {
|
||||
app.get('/tail/api/files/:endpoint', (req, res) => {
|
||||
const endpoint = path.normalize(req.params.endpoint)
|
||||
const file = path.join(directory, endpoint)
|
||||
const isReconnect = req.query.reconnect === 'true'
|
||||
|
||||
// Security check - ensure file is within the directory
|
||||
if (!file.startsWith(path.resolve(directory))) {
|
||||
@@ -95,14 +96,20 @@ app.get('/tail/api/files/:endpoint', (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
// For reconnections, start from current end, for new connections start from 'end'
|
||||
const beginAt = isReconnect ? fs.statSync(file).size : 'end'
|
||||
|
||||
const stream = ts.createReadStream(file, {
|
||||
beginAt: 'end',
|
||||
beginAt: beginAt,
|
||||
endOnError: true,
|
||||
detectTruncate: true
|
||||
})
|
||||
|
||||
// Send initial message
|
||||
res.write(Buffer.from(`data: Tailing ${endpoint}...\n\n`))
|
||||
const initialMessage = isReconnect
|
||||
? `Reconnected to ${endpoint}...`
|
||||
: `Tailing ${endpoint}...`
|
||||
res.write(Buffer.from(`data: ${initialMessage}\n\n`))
|
||||
|
||||
// Handle stream events
|
||||
stream.on('error', error => {
|
||||
|
||||
@@ -571,6 +571,32 @@ input:checked + .slider:before {
|
||||
background: rgba(6, 182, 212, 0.1);
|
||||
}
|
||||
|
||||
.log-line.reconnect-message {
|
||||
border-left-color: var(--warning-color);
|
||||
background: rgba(245, 158, 11, 0.15);
|
||||
color: var(--warning-color);
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
margin: var(--spacing-sm) 0;
|
||||
}
|
||||
|
||||
.log-line.reconnect-message i {
|
||||
margin-right: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.log-line.reconnect-success {
|
||||
border-left-color: var(--success-color);
|
||||
background: rgba(16, 185, 129, 0.15);
|
||||
color: var(--success-color);
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
margin: var(--spacing-sm) 0;
|
||||
}
|
||||
|
||||
.log-line.reconnect-success i {
|
||||
margin-right: var(--spacing-xs);
|
||||
}
|
||||
|
||||
/* Loading states */
|
||||
.loading {
|
||||
display: flex;
|
||||
|
||||
@@ -234,7 +234,7 @@ class LogTailMonitor {
|
||||
/**
|
||||
* Start tailing a specific file
|
||||
*/
|
||||
async startTailing(filename) {
|
||||
async startTailing(filename, isReconnect = false) {
|
||||
this.showLoading('Connecting to ' + filename + '...');
|
||||
|
||||
// Stop current connection
|
||||
@@ -254,17 +254,28 @@ class LogTailMonitor {
|
||||
}
|
||||
});
|
||||
|
||||
// Clear existing logs
|
||||
// Clear existing logs only if it's not a reconnection
|
||||
if (!isReconnect) {
|
||||
this.clearLogs();
|
||||
} else {
|
||||
// Add a reconnection indicator to the existing logs
|
||||
this.addReconnectionMessage();
|
||||
}
|
||||
|
||||
try {
|
||||
// Start SSE connection
|
||||
this.eventSource = new EventSource(`api/files/${encodeURIComponent(filename)}`);
|
||||
// Start SSE connection with reconnect parameter if needed
|
||||
const url = `api/files/${encodeURIComponent(filename)}${isReconnect ? '?reconnect=true' : ''}`;
|
||||
this.eventSource = new EventSource(url);
|
||||
|
||||
this.eventSource.onopen = () => {
|
||||
this.hideLoading();
|
||||
this.setConnectionStatus(true);
|
||||
console.log('SSE connection established');
|
||||
|
||||
// If this is a reconnection, add a success message
|
||||
if (isReconnect) {
|
||||
this.addReconnectionSuccessMessage();
|
||||
}
|
||||
};
|
||||
|
||||
this.eventSource.onmessage = (event) => {
|
||||
@@ -283,11 +294,11 @@ class LogTailMonitor {
|
||||
console.log('SSE connection closed');
|
||||
}
|
||||
|
||||
// Auto-reconnect after delay
|
||||
// Auto-reconnect after delay, but don't clear logs
|
||||
setTimeout(() => {
|
||||
if (this.currentFile && this.eventSource.readyState === EventSource.CLOSED) {
|
||||
console.log('Attempting to reconnect...');
|
||||
this.startTailing(this.currentFile);
|
||||
this.startTailing(this.currentFile, true); // true = isReconnect
|
||||
}
|
||||
}, 5000);
|
||||
};
|
||||
@@ -568,6 +579,36 @@ class LogTailMonitor {
|
||||
this.elements.loadingOverlay.classList.remove('visible');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a reconnection message to the log
|
||||
*/
|
||||
addReconnectionMessage() {
|
||||
const reconnectLine = document.createElement('div');
|
||||
reconnectLine.className = 'log-line reconnect-message';
|
||||
reconnectLine.innerHTML = '<i class="fas fa-plug"></i> Reconnecting to log stream...';
|
||||
|
||||
this.elements.logOutput.appendChild(reconnectLine);
|
||||
|
||||
if (this.autoScroll) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a successful reconnection message to the log
|
||||
*/
|
||||
addReconnectionSuccessMessage() {
|
||||
const successLine = document.createElement('div');
|
||||
successLine.className = 'log-line reconnect-success';
|
||||
successLine.innerHTML = '<i class="fas fa-check-circle"></i> Successfully reconnected - continuing log stream...';
|
||||
|
||||
this.elements.logOutput.appendChild(successLine);
|
||||
|
||||
if (this.autoScroll) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape HTML to prevent XSS
|
||||
*/
|
||||
@@ -580,15 +621,18 @@ class LogTailMonitor {
|
||||
|
||||
// Initialize the application when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new LogTailMonitor();
|
||||
window.logMonitor = new LogTailMonitor();
|
||||
});
|
||||
|
||||
// Handle page visibility changes
|
||||
// Handle page visibility changes - attempt reconnection instead of reload
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
// Refresh connection when page becomes visible
|
||||
if (document.visibilityState === 'visible' && window.logMonitor) {
|
||||
// Try to reconnect to current file if connection was lost
|
||||
if (window.logMonitor.currentFile && (!window.logMonitor.eventSource || window.logMonitor.eventSource.readyState === EventSource.CLOSED)) {
|
||||
console.log('Page became visible, attempting to reconnect...');
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
window.logMonitor.startTailing(window.logMonitor.currentFile, true);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Referencia en una nueva incidencia
Block a user