117 lines
3.6 KiB
JavaScript
117 lines
3.6 KiB
JavaScript
|
|
/**
|
||
|
|
* Live Status Monitor for Container Creation
|
||
|
|
* Provides real-time updates similar to Proxmox task viewer
|
||
|
|
*/
|
||
|
|
|
||
|
|
class LiveStatusMonitor {
|
||
|
|
constructor(taskId, logContainer) {
|
||
|
|
this.taskId = taskId;
|
||
|
|
this.logContainer = logContainer;
|
||
|
|
this.pollInterval = 1000; // Poll every second
|
||
|
|
|
||
|
|
this.init();
|
||
|
|
}
|
||
|
|
|
||
|
|
init() {
|
||
|
|
console.log(`Initializing live status for task: ${this.taskId}`);
|
||
|
|
|
||
|
|
// Initial request with error handling
|
||
|
|
$.get(`/manager/task/${this.taskId}/status/`)
|
||
|
|
.done((data) => {
|
||
|
|
console.log('Initial status data received:', data);
|
||
|
|
this.updateDisplay(data);
|
||
|
|
|
||
|
|
// Start polling only after successful initial request
|
||
|
|
this.startPolling();
|
||
|
|
})
|
||
|
|
.fail((xhr, status, error) => {
|
||
|
|
console.error('Failed to get initial status:', error, xhr.responseText);
|
||
|
|
this.showError(`Failed to connect to live status: ${error}`);
|
||
|
|
|
||
|
|
// Still try to start polling in case it's a temporary issue
|
||
|
|
setTimeout(() => {
|
||
|
|
this.startPolling();
|
||
|
|
}, 2000);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
startPolling() {
|
||
|
|
setInterval(() => {
|
||
|
|
$.get(`/manager/task/${this.taskId}/status/`)
|
||
|
|
.done((data) => {
|
||
|
|
this.updateDisplay(data);
|
||
|
|
})
|
||
|
|
.fail((xhr, status, error) => {
|
||
|
|
console.error('Polling error:', error);
|
||
|
|
// Don't show error for every polling failure
|
||
|
|
});
|
||
|
|
}, this.pollInterval);
|
||
|
|
}
|
||
|
|
|
||
|
|
showError(message) {
|
||
|
|
const logContent = this.logContainer.querySelector('.live-log-content');
|
||
|
|
if (logContent) {
|
||
|
|
logContent.innerHTML = `<div class="error" style="color: red; padding: 10px;">${message}</div>`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
updateDisplay(data) {
|
||
|
|
const logContent = this.logContainer.querySelector('.live-log-content');
|
||
|
|
if (!logContent) return;
|
||
|
|
|
||
|
|
const logs = data.logs || {};
|
||
|
|
const steps = logs.steps || [];
|
||
|
|
|
||
|
|
if (steps.length === 0) {
|
||
|
|
logContent.innerHTML = '<div class="loading">Waiting for status updates...</div>';
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Track existing entries to highlight new ones
|
||
|
|
const existingCount = logContent.querySelectorAll('.log-entry').length;
|
||
|
|
|
||
|
|
logContent.innerHTML = '';
|
||
|
|
|
||
|
|
steps.forEach((step, index) => {
|
||
|
|
const entry = this.createLogEntry(step);
|
||
|
|
if (index >= existingCount) {
|
||
|
|
entry.classList.add('new');
|
||
|
|
}
|
||
|
|
logContent.appendChild(entry);
|
||
|
|
});
|
||
|
|
|
||
|
|
// Auto-scroll to bottom
|
||
|
|
logContent.scrollTop = logContent.scrollHeight;
|
||
|
|
}
|
||
|
|
|
||
|
|
createLogEntry(step) {
|
||
|
|
const entry = document.createElement('div');
|
||
|
|
entry.className = 'log-entry';
|
||
|
|
|
||
|
|
const timestamp = new Date(step.timestamp).toLocaleTimeString();
|
||
|
|
const message = step.message || '';
|
||
|
|
|
||
|
|
entry.innerHTML = `
|
||
|
|
<div class="log-timestamp">${timestamp}</div>
|
||
|
|
<div class="log-message">${this.escapeHtml(message)}</div>
|
||
|
|
`;
|
||
|
|
|
||
|
|
return entry;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
escapeHtml(text) {
|
||
|
|
const div = document.createElement('div');
|
||
|
|
div.textContent = text;
|
||
|
|
return div.innerHTML;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let liveStatus = null;
|
||
|
|
|
||
|
|
function initLiveStatus(taskId, containerId) {
|
||
|
|
const container = document.getElementById(containerId);
|
||
|
|
if (container && taskId) {
|
||
|
|
liveStatus = new LiveStatusMonitor(taskId, container);
|
||
|
|
}
|
||
|
|
}
|