338 lines
12 KiB
JavaScript
338 lines
12 KiB
JavaScript
|
|
/**
|
||
|
|
* Proxmox Task Management - Generelle Funktionen für Task-Überwachung
|
||
|
|
*/
|
||
|
|
|
||
|
|
try {
|
||
|
|
console.log('Proxmox Task Manager will start with ', taskMonitorOptions);
|
||
|
|
}catch(e){
|
||
|
|
console.log('Proxmox Task Manager will start without options');
|
||
|
|
const taskMonitorOptions = {};
|
||
|
|
}
|
||
|
|
|
||
|
|
class ProxmoxTaskManager {
|
||
|
|
constructor(options = taskMonitorOptions || {}) {
|
||
|
|
this.checkInterval = options.checkInterval || 2000; // 1 second default
|
||
|
|
this.maxTimeout = options.maxTimeout || 50000; // 30 seconds default
|
||
|
|
this.taskStatusUrl = options.taskStatusUrl || '/api/task-status/';
|
||
|
|
this.activeMonitors = new Map(); // Track active monitoring processes
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Startet die Überwachung einer Proxmox Task
|
||
|
|
* @param {string} taskId - Die Proxmox Task ID
|
||
|
|
* @param {Object} callbacks - Callback-Funktionen für verschiedene Events
|
||
|
|
* @param {function} callbacks.onProgress - Wird bei jedem Status-Update aufgerufen
|
||
|
|
* @param {function} callbacks.onSuccess - Wird bei erfolgreichem Abschluss aufgerufen
|
||
|
|
* @param {function} callbacks.onError - Wird bei Fehlern aufgerufen
|
||
|
|
* @param {function} callbacks.onTimeout - Wird bei Timeout aufgerufen
|
||
|
|
* @param {Object} context - Zusätzlicher Kontext, der an Callbacks weitergegeben wird
|
||
|
|
*/
|
||
|
|
monitorTask(taskId, callbacks = {}, context = {}) {
|
||
|
|
// Stoppe eventuell bereits laufende Überwachung für diese Task
|
||
|
|
this.stopMonitoring(taskId);
|
||
|
|
|
||
|
|
const monitor = {
|
||
|
|
taskId: taskId,
|
||
|
|
callbacks: callbacks,
|
||
|
|
context: context,
|
||
|
|
interval: null,
|
||
|
|
timeout: null,
|
||
|
|
startTime: Date.now()
|
||
|
|
};
|
||
|
|
|
||
|
|
// Starte Intervall für Status-Checks
|
||
|
|
monitor.interval = setInterval(() => {
|
||
|
|
this._checkTaskStatus(monitor);
|
||
|
|
}, this.checkInterval);
|
||
|
|
|
||
|
|
// Safety timeout
|
||
|
|
monitor.timeout = setTimeout(() => {
|
||
|
|
this._handleTimeout(monitor);
|
||
|
|
}, this.maxTimeout);
|
||
|
|
|
||
|
|
// Speichere Monitor für spätere Referenz
|
||
|
|
this.activeMonitors.set(taskId, monitor);
|
||
|
|
|
||
|
|
// Ersten Check sofort ausführen
|
||
|
|
this._checkTaskStatus(monitor);
|
||
|
|
|
||
|
|
return taskId; // Return taskId for reference
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Stoppt die Überwachung einer Task
|
||
|
|
* @param {string} taskId - Die Task ID
|
||
|
|
*/
|
||
|
|
stopMonitoring(taskId) {
|
||
|
|
const monitor = this.activeMonitors.get(taskId);
|
||
|
|
if (monitor) {
|
||
|
|
if (monitor.interval) clearInterval(monitor.interval);
|
||
|
|
if (monitor.timeout) clearTimeout(monitor.timeout);
|
||
|
|
this.activeMonitors.delete(taskId);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Stoppt alle aktiven Überwachungen
|
||
|
|
*/
|
||
|
|
stopAllMonitoring() {
|
||
|
|
for (const [taskId] of this.activeMonitors) {
|
||
|
|
this.stopMonitoring(taskId);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Prüft den Status einer Task
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
_checkTaskStatus(monitor) {
|
||
|
|
$.get(this.taskStatusUrl, { 'task_id': monitor.taskId })
|
||
|
|
.done((data) => {
|
||
|
|
if (data.status === 'success') {
|
||
|
|
const taskStatus = data.task_status;
|
||
|
|
const progress = data.progress || 0;
|
||
|
|
|
||
|
|
// Progress callback
|
||
|
|
if (monitor.callbacks.onProgress) {
|
||
|
|
monitor.callbacks.onProgress(taskStatus, progress, data, monitor.context);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if task is completed
|
||
|
|
if (taskStatus === 'OK' || taskStatus === 'completed') {
|
||
|
|
this._handleSuccess(monitor, data);
|
||
|
|
} else if (taskStatus === 'stopped' || taskStatus === 'error') {
|
||
|
|
this._handleError(monitor, data);
|
||
|
|
}
|
||
|
|
// If still running, continue monitoring
|
||
|
|
} else {
|
||
|
|
this._handleError(monitor, data);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.fail((xhr) => {
|
||
|
|
let errorMsg = 'Network error';
|
||
|
|
try {
|
||
|
|
const response = JSON.parse(xhr.responseText);
|
||
|
|
errorMsg = response.message || errorMsg;
|
||
|
|
} catch (e) {}
|
||
|
|
|
||
|
|
this._handleError(monitor, { message: errorMsg });
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Behandelt erfolgreichen Task-Abschluss
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
_handleSuccess(monitor, data) {
|
||
|
|
this.stopMonitoring(monitor.taskId);
|
||
|
|
if (monitor.callbacks.onSuccess) {
|
||
|
|
monitor.callbacks.onSuccess(data, monitor.context);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Behandelt Task-Fehler
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
_handleError(monitor, data) {
|
||
|
|
this.stopMonitoring(monitor.taskId);
|
||
|
|
if (monitor.callbacks.onError) {
|
||
|
|
monitor.callbacks.onError(data, monitor.context);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Behandelt Timeout
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
_handleTimeout(monitor) {
|
||
|
|
this.stopMonitoring(monitor.taskId);
|
||
|
|
if (monitor.callbacks.onTimeout) {
|
||
|
|
monitor.callbacks.onTimeout(monitor.context);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Gibt die Anzahl aktiver Überwachungen zurück
|
||
|
|
*/
|
||
|
|
getActiveMonitorCount() {
|
||
|
|
return this.activeMonitors.size;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Gibt alle aktiven Task IDs zurück
|
||
|
|
*/
|
||
|
|
getActiveTaskIds() {
|
||
|
|
return Array.from(this.activeMonitors.keys());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Standard Proxmox Task Manager Instanz
|
||
|
|
* Kann global verwendet werden
|
||
|
|
*/
|
||
|
|
const proxmoxTaskManager = new ProxmoxTaskManager();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Hilfsfunktion für einfache Task-Überwachung mit Standard-Callbacks
|
||
|
|
* @param {string} taskId - Die Task ID
|
||
|
|
* @param {Object} element - DOM Element das visuell aktualisiert werden soll
|
||
|
|
* @param {string} action - Aktion die ausgeführt wird (für Logging)
|
||
|
|
* @param {function} onComplete - Optional: Custom completion callback
|
||
|
|
*/
|
||
|
|
function monitorProxmoxTask(taskId, element, action, onComplete = null) {
|
||
|
|
const $element = $(element);
|
||
|
|
|
||
|
|
return proxmoxTaskManager.monitorTask(taskId, {
|
||
|
|
onProgress: (status, progress, data, context) => {
|
||
|
|
console.log(`Task ${taskId} progress: ${status} (${progress}%)`);
|
||
|
|
|
||
|
|
// Update tooltip with progress
|
||
|
|
if (progress > 0) {
|
||
|
|
$element.attr('title', `${action}: ${progress}%`);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onSuccess: (data, context) => {
|
||
|
|
console.log(`Task ${taskId} completed successfully`);
|
||
|
|
$element.removeClass('pending error');
|
||
|
|
|
||
|
|
if (onComplete) {
|
||
|
|
onComplete(true, data, context);
|
||
|
|
} else {
|
||
|
|
// Standard success handling
|
||
|
|
$element.attr('title', `${action} completed successfully`);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onError: (data, context) => {
|
||
|
|
console.error(`Task ${taskId} failed:`, data.message);
|
||
|
|
$element.removeClass('pending').addClass('error');
|
||
|
|
$element.attr('title', `${action} failed: ${data.message}`);
|
||
|
|
|
||
|
|
if (onComplete) {
|
||
|
|
onComplete(false, data, context);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onTimeout: (context) => {
|
||
|
|
console.warn(`Task ${taskId} monitoring timed out`);
|
||
|
|
$element.removeClass('pending').addClass('error');
|
||
|
|
$element.attr('title', `${action} timed out`);
|
||
|
|
|
||
|
|
if (onComplete) {
|
||
|
|
onComplete(false, { message: 'Timeout' }, context);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
element: element,
|
||
|
|
action: action,
|
||
|
|
taskId: taskId
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Erweiterte Task-Überwachung mit Live-Log-Container (für Container-Erstellung etc.)
|
||
|
|
* @param {string} taskId - Die Task ID
|
||
|
|
* @param {string} logContainerId - ID des Log-Containers
|
||
|
|
* @param {function} onComplete - Callback bei Completion
|
||
|
|
*/
|
||
|
|
function monitorProxmoxTaskWithLiveLog(taskId, logContainerId, onComplete = null) {
|
||
|
|
const logContainer = document.getElementById(logContainerId);
|
||
|
|
const logContent = logContainer?.querySelector('.live-log-content');
|
||
|
|
|
||
|
|
if (!logContainer || !logContent) {
|
||
|
|
console.error('Log container not found:', logContainerId);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Clear existing content
|
||
|
|
logContent.innerHTML = '<div class="loading">Monitoring task...</div>';
|
||
|
|
|
||
|
|
return proxmoxTaskManager.monitorTask(taskId, {
|
||
|
|
onProgress: (status, progress, data, context) => {
|
||
|
|
const timestamp = new Date().toLocaleTimeString();
|
||
|
|
const progressText = progress > 0 ? ` (${progress}%)` : '';
|
||
|
|
|
||
|
|
const logEntry = document.createElement('div');
|
||
|
|
logEntry.className = 'log-entry new';
|
||
|
|
logEntry.innerHTML = `
|
||
|
|
<span class="log-timestamp">${timestamp}</span>
|
||
|
|
<span class="log-level info">INFO</span>
|
||
|
|
<span class="log-message">Task ${status}${progressText}</span>
|
||
|
|
`;
|
||
|
|
|
||
|
|
// Remove loading message if present
|
||
|
|
const loading = logContent.querySelector('.loading');
|
||
|
|
if (loading) loading.remove();
|
||
|
|
|
||
|
|
logContent.appendChild(logEntry);
|
||
|
|
logContent.scrollTop = logContent.scrollHeight;
|
||
|
|
|
||
|
|
// Remove 'new' class after animation
|
||
|
|
setTimeout(() => logEntry.classList.remove('new'), 500);
|
||
|
|
},
|
||
|
|
|
||
|
|
onSuccess: (data, context) => {
|
||
|
|
const timestamp = new Date().toLocaleTimeString();
|
||
|
|
const logEntry = document.createElement('div');
|
||
|
|
logEntry.className = 'log-entry new';
|
||
|
|
logEntry.innerHTML = `
|
||
|
|
<span class="log-timestamp">${timestamp}</span>
|
||
|
|
<span class="log-level success">✓</span>
|
||
|
|
<span class="log-message">Task completed successfully</span>
|
||
|
|
`;
|
||
|
|
|
||
|
|
logContent.appendChild(logEntry);
|
||
|
|
logContent.scrollTop = logContent.scrollHeight;
|
||
|
|
|
||
|
|
if (onComplete) {
|
||
|
|
setTimeout(() => onComplete(true, data, context), 1000);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onError: (data, context) => {
|
||
|
|
const timestamp = new Date().toLocaleTimeString();
|
||
|
|
const logEntry = document.createElement('div');
|
||
|
|
logEntry.className = 'log-entry new';
|
||
|
|
logEntry.innerHTML = `
|
||
|
|
<span class="log-timestamp">${timestamp}</span>
|
||
|
|
<span class="log-level error">✗</span>
|
||
|
|
<span class="log-message">Task failed: ${data.message}</span>
|
||
|
|
`;
|
||
|
|
|
||
|
|
logContent.appendChild(logEntry);
|
||
|
|
logContent.scrollTop = logContent.scrollHeight;
|
||
|
|
|
||
|
|
if (onComplete) {
|
||
|
|
setTimeout(() => onComplete(false, data, context), 1000);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onTimeout: (context) => {
|
||
|
|
const timestamp = new Date().toLocaleTimeString();
|
||
|
|
const logEntry = document.createElement('div');
|
||
|
|
logEntry.className = 'log-entry new';
|
||
|
|
logEntry.innerHTML = `
|
||
|
|
<span class="log-timestamp">${timestamp}</span>
|
||
|
|
<span class="log-level warning">⚠</span>
|
||
|
|
<span class="log-message">Task monitoring timed out</span>
|
||
|
|
`;
|
||
|
|
|
||
|
|
logContent.appendChild(logEntry);
|
||
|
|
logContent.scrollTop = logContent.scrollHeight;
|
||
|
|
|
||
|
|
if (onComplete) {
|
||
|
|
setTimeout(() => onComplete(false, { message: 'Timeout' }, context), 1000);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
taskId: taskId,
|
||
|
|
logContainerId: logContainerId
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Cleanup bei Seitenwechsel
|
||
|
|
$(window).on('beforeunload', function() {
|
||
|
|
proxmoxTaskManager.stopAllMonitoring();
|
||
|
|
});
|