mirror of
https://github.com/plexguide/Huntarr.io.git
synced 2026-04-20 11:26:52 -04:00
748 lines
29 KiB
JavaScript
748 lines
29 KiB
JavaScript
/**
|
|
* Backup and Restore functionality for Huntarr
|
|
* Handles database backup creation, restoration, and management
|
|
*/
|
|
|
|
const BackupRestore = {
|
|
initialized: false,
|
|
backupSettings: {
|
|
frequency: 3,
|
|
retention: 3
|
|
},
|
|
|
|
initialize: function() {
|
|
if (this.initialized) {
|
|
console.log('[BackupRestore] Already initialized');
|
|
return;
|
|
}
|
|
|
|
console.log('[BackupRestore] Initializing backup/restore functionality');
|
|
|
|
this.bindEvents();
|
|
this.loadSettings();
|
|
this.loadBackupList();
|
|
this.updateNextBackupTime();
|
|
|
|
this.initialized = true;
|
|
console.log('[BackupRestore] Initialization complete');
|
|
},
|
|
|
|
bindEvents: function() {
|
|
// Backup frequency change
|
|
const frequencyInput = document.getElementById('backup-frequency');
|
|
if (frequencyInput) {
|
|
frequencyInput.addEventListener('change', () => {
|
|
this.backupSettings.frequency = parseInt(frequencyInput.value) || 3;
|
|
this.saveSettings();
|
|
this.updateNextBackupTime();
|
|
});
|
|
}
|
|
|
|
// Backup retention change
|
|
const retentionInput = document.getElementById('backup-retention');
|
|
if (retentionInput) {
|
|
retentionInput.addEventListener('change', () => {
|
|
this.backupSettings.retention = parseInt(retentionInput.value) || 3;
|
|
this.saveSettings();
|
|
});
|
|
}
|
|
|
|
// Create manual backup
|
|
const createBackupBtn = document.getElementById('create-backup-btn');
|
|
if (createBackupBtn) {
|
|
createBackupBtn.addEventListener('click', () => {
|
|
this.createManualBackup();
|
|
});
|
|
}
|
|
|
|
// Restore backup selection
|
|
const restoreSelect = document.getElementById('restore-backup-select');
|
|
if (restoreSelect) {
|
|
restoreSelect.addEventListener('change', () => {
|
|
this.handleRestoreSelection();
|
|
});
|
|
}
|
|
|
|
// Restore confirmation input
|
|
const restoreConfirmation = document.getElementById('restore-confirmation');
|
|
if (restoreConfirmation) {
|
|
restoreConfirmation.addEventListener('input', () => {
|
|
this.validateRestoreConfirmation();
|
|
});
|
|
}
|
|
|
|
// Restore button
|
|
const restoreBtn = document.getElementById('restore-backup-btn');
|
|
if (restoreBtn) {
|
|
restoreBtn.addEventListener('click', () => {
|
|
this.restoreBackup();
|
|
});
|
|
}
|
|
|
|
// Delete confirmation input
|
|
const deleteConfirmation = document.getElementById('delete-confirmation');
|
|
if (deleteConfirmation) {
|
|
deleteConfirmation.addEventListener('input', () => {
|
|
this.validateDeleteConfirmation();
|
|
});
|
|
}
|
|
|
|
// Delete database button
|
|
const deleteBtn = document.getElementById('delete-database-btn');
|
|
if (deleteBtn) {
|
|
deleteBtn.addEventListener('click', () => {
|
|
this.deleteDatabase();
|
|
});
|
|
}
|
|
|
|
// Download backup selection
|
|
const downloadSelect = document.getElementById('download-backup-select');
|
|
if (downloadSelect) {
|
|
downloadSelect.addEventListener('change', () => {
|
|
this.handleDownloadSelection();
|
|
});
|
|
}
|
|
|
|
// Download backup button
|
|
const downloadBtn = document.getElementById('download-backup-btn');
|
|
if (downloadBtn) {
|
|
downloadBtn.addEventListener('click', () => {
|
|
this.downloadBackup();
|
|
});
|
|
}
|
|
|
|
// Upload backup file input
|
|
const uploadFileInput = document.getElementById('upload-backup-file');
|
|
if (uploadFileInput) {
|
|
uploadFileInput.addEventListener('change', () => {
|
|
this.handleUploadFileSelection();
|
|
});
|
|
}
|
|
|
|
// Upload confirmation input
|
|
const uploadConfirmation = document.getElementById('upload-confirmation');
|
|
if (uploadConfirmation) {
|
|
uploadConfirmation.addEventListener('input', () => {
|
|
this.validateUploadConfirmation();
|
|
});
|
|
}
|
|
|
|
// Upload button
|
|
const uploadBtn = document.getElementById('upload-backup-btn');
|
|
if (uploadBtn) {
|
|
uploadBtn.addEventListener('click', () => {
|
|
this.uploadBackup();
|
|
});
|
|
}
|
|
},
|
|
|
|
loadSettings: function() {
|
|
console.log('[BackupRestore] Loading backup settings');
|
|
|
|
fetch('./api/backup/settings')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
this.backupSettings = {
|
|
frequency: data.settings.frequency || 3,
|
|
retention: data.settings.retention || 3
|
|
};
|
|
|
|
// Update UI
|
|
const frequencyInput = document.getElementById('backup-frequency');
|
|
const retentionInput = document.getElementById('backup-retention');
|
|
|
|
if (frequencyInput) frequencyInput.value = this.backupSettings.frequency;
|
|
if (retentionInput) retentionInput.value = this.backupSettings.retention;
|
|
|
|
this.updateNextBackupTime();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('[BackupRestore] Error loading settings:', error);
|
|
this.showError('Failed to load backup settings');
|
|
});
|
|
},
|
|
|
|
saveSettings: function() {
|
|
console.log('[BackupRestore] Saving backup settings', this.backupSettings);
|
|
|
|
fetch('./api/backup/settings', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(this.backupSettings)
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
console.log('[BackupRestore] Settings saved successfully');
|
|
this.showSuccess('Backup settings saved');
|
|
} else {
|
|
throw new Error(data.error || 'Failed to save settings');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('[BackupRestore] Error saving settings:', error);
|
|
this.showError('Failed to save backup settings');
|
|
});
|
|
},
|
|
|
|
loadBackupList: function() {
|
|
console.log('[BackupRestore] Loading backup list');
|
|
|
|
const listContainer = document.getElementById('backup-list-container');
|
|
const restoreSelect = document.getElementById('restore-backup-select');
|
|
|
|
if (listContainer) {
|
|
listContainer.innerHTML = '<div class="backup-list-loading"><i class="fas fa-spinner fa-spin"></i> Loading backup list...</div>';
|
|
}
|
|
|
|
if (restoreSelect) {
|
|
restoreSelect.innerHTML = '<option value="">Loading available backups...</option>';
|
|
}
|
|
|
|
fetch('./api/backup/list')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
this.renderBackupList(data.backups);
|
|
this.populateRestoreSelect(data.backups);
|
|
} else {
|
|
throw new Error(data.error || 'Failed to load backups');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('[BackupRestore] Error loading backup list:', error);
|
|
if (listContainer) {
|
|
listContainer.innerHTML = '<div class="backup-list-loading">Error loading backup list</div>';
|
|
}
|
|
if (restoreSelect) {
|
|
restoreSelect.innerHTML = '<option value="">Error loading backups</option>';
|
|
}
|
|
});
|
|
},
|
|
|
|
renderBackupList: function(backups) {
|
|
const listContainer = document.getElementById('backup-list-container');
|
|
if (!listContainer) return;
|
|
|
|
if (!backups || backups.length === 0) {
|
|
listContainer.innerHTML = '<div class="backup-list-loading">No backups available</div>';
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
backups.forEach(backup => {
|
|
const date = new Date(backup.timestamp);
|
|
const formattedDate = date.toLocaleString();
|
|
const size = this.formatFileSize(backup.size);
|
|
|
|
// Ensure backup ID is properly escaped for HTML attributes
|
|
const escapedId = backup.id.replace(/'/g, "\\'");
|
|
|
|
html += `
|
|
<div class="backup-item" data-backup-id="${escapedId}">
|
|
<div class="backup-info">
|
|
<div class="backup-name">${backup.name}</div>
|
|
<div class="backup-details">
|
|
Created: ${formattedDate} | Size: ${size} | Type: ${backup.type || 'Manual'}
|
|
</div>
|
|
</div>
|
|
<div class="backup-actions">
|
|
<button class="delete-backup-btn" onclick="BackupRestore.deleteBackup('${escapedId}')">
|
|
<i class="fas fa-trash"></i> Delete
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
listContainer.innerHTML = html;
|
|
},
|
|
|
|
populateRestoreSelect: function(backups) {
|
|
const restoreSelect = document.getElementById('restore-backup-select');
|
|
const downloadSelect = document.getElementById('download-backup-select');
|
|
if (!restoreSelect || !downloadSelect) return;
|
|
|
|
if (!backups || backups.length === 0) {
|
|
restoreSelect.innerHTML = '<option value="">No backups available</option>';
|
|
downloadSelect.innerHTML = '<option value="">No backups available</option>';
|
|
return;
|
|
}
|
|
|
|
let html = '<option value="">Select a backup to restore...</option>';
|
|
let downloadHtml = '<option value="">Select a backup to download...</option>';
|
|
backups.forEach(backup => {
|
|
const date = new Date(backup.timestamp);
|
|
const formattedDate = date.toLocaleString();
|
|
const size = this.formatFileSize(backup.size);
|
|
|
|
html += `<option value="${backup.id}">${backup.name} - ${formattedDate} (${size})</option>`;
|
|
downloadHtml += `<option value="${backup.id}">${backup.name} - ${formattedDate} (${size})</option>`;
|
|
});
|
|
|
|
restoreSelect.innerHTML = html;
|
|
downloadSelect.innerHTML = downloadHtml;
|
|
},
|
|
|
|
updateNextBackupTime: function() {
|
|
const nextBackupElement = document.getElementById('next-backup-time');
|
|
if (!nextBackupElement) return;
|
|
|
|
fetch('./api/backup/next-scheduled')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success && data.next_backup) {
|
|
const nextDate = new Date(data.next_backup);
|
|
nextBackupElement.innerHTML = `<i class="fas fa-clock"></i> ${nextDate.toLocaleString()}`;
|
|
} else {
|
|
nextBackupElement.innerHTML = '<i class="fas fa-clock"></i> Not scheduled';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('[BackupRestore] Error getting next backup time:', error);
|
|
nextBackupElement.innerHTML = '<i class="fas fa-exclamation-triangle"></i> Error loading schedule';
|
|
});
|
|
},
|
|
|
|
createManualBackup: function() {
|
|
console.log('[BackupRestore] Creating manual backup');
|
|
|
|
const createBtn = document.getElementById('create-backup-btn');
|
|
const progressContainer = document.getElementById('backup-progress');
|
|
const progressFill = document.querySelector('.progress-fill');
|
|
const progressText = document.querySelector('.progress-text');
|
|
|
|
if (createBtn) createBtn.disabled = true;
|
|
if (progressContainer) progressContainer.style.display = 'block';
|
|
if (progressText) progressText.textContent = 'Creating backup...';
|
|
|
|
// Animate progress bar
|
|
let progress = 0;
|
|
const progressInterval = setInterval(() => {
|
|
progress += Math.random() * 15;
|
|
if (progress > 90) progress = 90;
|
|
if (progressFill) progressFill.style.width = progress + '%';
|
|
}, 200);
|
|
|
|
fetch('./api/backup/create', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
type: 'manual'
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
clearInterval(progressInterval);
|
|
|
|
if (data.success) {
|
|
if (progressFill) progressFill.style.width = '100%';
|
|
if (progressText) progressText.textContent = 'Backup created successfully!';
|
|
|
|
setTimeout(() => {
|
|
if (progressContainer) progressContainer.style.display = 'none';
|
|
if (progressFill) progressFill.style.width = '0%';
|
|
}, 2000);
|
|
|
|
this.showSuccess(`Backup created successfully: ${data.backup_name}`);
|
|
this.loadBackupList(); // Refresh the list
|
|
} else {
|
|
throw new Error(data.error || 'Failed to create backup');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
clearInterval(progressInterval);
|
|
console.error('[BackupRestore] Error creating backup:', error);
|
|
|
|
if (progressContainer) progressContainer.style.display = 'none';
|
|
if (progressFill) progressFill.style.width = '0%';
|
|
|
|
this.showError('Failed to create backup: ' + error.message);
|
|
})
|
|
.finally(() => {
|
|
if (createBtn) createBtn.disabled = false;
|
|
});
|
|
},
|
|
|
|
handleRestoreSelection: function() {
|
|
const restoreSelect = document.getElementById('restore-backup-select');
|
|
const confirmationGroup = document.getElementById('restore-confirmation-group');
|
|
const actionGroup = document.getElementById('restore-action-group');
|
|
|
|
if (!restoreSelect) return;
|
|
|
|
if (restoreSelect.value) {
|
|
if (confirmationGroup) confirmationGroup.style.display = 'block';
|
|
if (actionGroup) actionGroup.style.display = 'block';
|
|
} else {
|
|
if (confirmationGroup) confirmationGroup.style.display = 'none';
|
|
if (actionGroup) actionGroup.style.display = 'none';
|
|
}
|
|
|
|
this.validateRestoreConfirmation();
|
|
},
|
|
|
|
validateRestoreConfirmation: function() {
|
|
const confirmationInput = document.getElementById('restore-confirmation');
|
|
const restoreBtn = document.getElementById('restore-backup-btn');
|
|
|
|
if (!confirmationInput || !restoreBtn) return;
|
|
|
|
const isValid = confirmationInput.value.toUpperCase() === 'RESTORE';
|
|
restoreBtn.disabled = !isValid;
|
|
},
|
|
|
|
restoreBackup: function() {
|
|
const restoreSelect = document.getElementById('restore-backup-select');
|
|
const confirmationInput = document.getElementById('restore-confirmation');
|
|
|
|
if (!restoreSelect || !confirmationInput) return;
|
|
|
|
const backupId = restoreSelect.value;
|
|
const confirmation = confirmationInput.value.toUpperCase();
|
|
|
|
if (!backupId || confirmation !== 'RESTORE') {
|
|
this.showError('Please select a backup and type RESTORE to confirm');
|
|
return;
|
|
}
|
|
|
|
var self = this;
|
|
var doRestore = function() {
|
|
console.log('[BackupRestore] Restoring backup:', backupId);
|
|
const restoreBtn = document.getElementById('restore-backup-btn');
|
|
if (restoreBtn) {
|
|
restoreBtn.disabled = true;
|
|
restoreBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Restoring...';
|
|
}
|
|
fetch('./api/backup/restore', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
backup_id: backupId
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
self.showSuccess('Database restored successfully! Reloading in 3 seconds...');
|
|
setTimeout(() => {
|
|
window.location.reload();
|
|
}, 3000);
|
|
} else {
|
|
throw new Error(data.error || 'Failed to restore backup');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('[BackupRestore] Error restoring backup:', error);
|
|
self.showError('Failed to restore backup: ' + error.message);
|
|
})
|
|
.finally(() => {
|
|
if (restoreBtn) {
|
|
restoreBtn.disabled = false;
|
|
restoreBtn.innerHTML = '<i class="fas fa-exclamation-triangle"></i> Restore Database';
|
|
}
|
|
});
|
|
};
|
|
if (window.HuntarrConfirm && window.HuntarrConfirm.show) {
|
|
window.HuntarrConfirm.show({ title: 'Restore Database', message: 'This will permanently overwrite your current database. Are you absolutely sure?', confirmLabel: 'Restore', onConfirm: doRestore });
|
|
} else {
|
|
if (!confirm('This will permanently overwrite your current database. Are you absolutely sure?')) return;
|
|
doRestore();
|
|
}
|
|
},
|
|
|
|
validateDeleteConfirmation: function() {
|
|
const confirmationInput = document.getElementById('delete-confirmation');
|
|
const actionGroup = document.getElementById('delete-action-group');
|
|
const deleteBtn = document.getElementById('delete-database-btn');
|
|
|
|
if (!confirmationInput || !actionGroup || !deleteBtn) return;
|
|
|
|
const isValid = confirmationInput.value.toLowerCase() === 'huntarr';
|
|
|
|
if (isValid) {
|
|
actionGroup.style.display = 'block';
|
|
deleteBtn.disabled = false;
|
|
} else {
|
|
actionGroup.style.display = 'none';
|
|
deleteBtn.disabled = true;
|
|
}
|
|
},
|
|
|
|
deleteDatabase: function() {
|
|
const confirmationInput = document.getElementById('delete-confirmation');
|
|
|
|
if (!confirmationInput || confirmationInput.value.toLowerCase() !== 'huntarr') {
|
|
this.showError('Please type "huntarr" to confirm database deletion');
|
|
return;
|
|
}
|
|
|
|
var self = this;
|
|
var doDelete = function() {
|
|
console.log('[BackupRestore] Deleting database');
|
|
const deleteBtn = document.getElementById('delete-database-btn');
|
|
if (deleteBtn) {
|
|
deleteBtn.disabled = true;
|
|
deleteBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Deleting...';
|
|
}
|
|
fetch('./api/backup/delete-database', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
self.showSuccess('Database deleted successfully! Redirecting to setup...');
|
|
setTimeout(() => {
|
|
window.location.href = './setup';
|
|
}, 3000);
|
|
} else {
|
|
throw new Error(data.error || 'Failed to delete database');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('[BackupRestore] Error deleting database:', error);
|
|
self.showError('Failed to delete database: ' + error.message);
|
|
})
|
|
.finally(() => {
|
|
if (deleteBtn) {
|
|
deleteBtn.disabled = false;
|
|
deleteBtn.innerHTML = '<i class="fas fa-trash-alt"></i> Delete Database';
|
|
}
|
|
});
|
|
};
|
|
if (window.HuntarrConfirm && window.HuntarrConfirm.show) {
|
|
window.HuntarrConfirm.show({ title: 'Delete Database', message: 'This will PERMANENTLY DELETE your entire Huntarr database. This action CANNOT be undone. Are you absolutely sure?', confirmLabel: 'Delete', onConfirm: doDelete });
|
|
} else {
|
|
if (!confirm('This will PERMANENTLY DELETE your entire Huntarr database. This action CANNOT be undone. Are you absolutely sure?')) return;
|
|
doDelete();
|
|
}
|
|
},
|
|
|
|
deleteBackup: function(backupId) {
|
|
var self = this;
|
|
var doDelete = function() {
|
|
console.log('[BackupRestore] Deleting backup:', backupId);
|
|
console.log('[BackupRestore] Backup ID type:', typeof backupId);
|
|
console.log('[BackupRestore] Backup ID length:', backupId ? backupId.length : 0);
|
|
|
|
// Add extra validation for backupId
|
|
if (!backupId || typeof backupId !== 'string') {
|
|
self.showError('Invalid backup ID provided for deletion');
|
|
return;
|
|
}
|
|
|
|
// Additional debugging - check if the backupId contains special characters
|
|
console.log('[BackupRestore] Backup ID raw:', backupId);
|
|
console.log('[BackupRestore] Backup ID escaped:', encodeURIComponent(backupId));
|
|
|
|
fetch('./api/backup/delete', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
backup_id: backupId
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
if (data.success) {
|
|
self.showSuccess('Backup deleted successfully');
|
|
self.loadBackupList(); // Refresh the list
|
|
} else {
|
|
throw new Error(data.error || 'Failed to delete backup');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('[BackupRestore] Error deleting backup:', error);
|
|
self.showError('Failed to delete backup: ' + error.message);
|
|
});
|
|
};
|
|
if (window.HuntarrConfirm && window.HuntarrConfirm.show) {
|
|
window.HuntarrConfirm.show({ title: 'Delete Backup', message: 'Are you sure you want to delete this backup? This action cannot be undone.', confirmLabel: 'Delete', onConfirm: doDelete });
|
|
} else {
|
|
if (!confirm('Are you sure you want to delete this backup? This action cannot be undone.')) return;
|
|
doDelete();
|
|
}
|
|
},
|
|
|
|
formatFileSize: function(bytes) {
|
|
if (bytes === 0) return '0 Bytes';
|
|
|
|
const k = 1024;
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
},
|
|
|
|
showSuccess: function(message) {
|
|
if (window.huntarrUI && window.huntarrUI.showNotification) {
|
|
window.huntarrUI.showNotification(message, 'success');
|
|
} else {
|
|
alert(message);
|
|
}
|
|
},
|
|
|
|
showError: function(message) {
|
|
if (window.huntarrUI && window.huntarrUI.showNotification) {
|
|
window.huntarrUI.showNotification(message, 'error');
|
|
} else {
|
|
alert(message);
|
|
}
|
|
},
|
|
|
|
// Download backup functions
|
|
handleDownloadSelection: function() {
|
|
const downloadSelect = document.getElementById('download-backup-select');
|
|
const downloadBtn = document.getElementById('download-backup-btn');
|
|
|
|
if (!downloadSelect || !downloadBtn) return;
|
|
|
|
if (downloadSelect.value) {
|
|
downloadBtn.disabled = false;
|
|
} else {
|
|
downloadBtn.disabled = true;
|
|
}
|
|
},
|
|
|
|
downloadBackup: function() {
|
|
const downloadSelect = document.getElementById('download-backup-select');
|
|
|
|
if (!downloadSelect) return;
|
|
|
|
const backupId = downloadSelect.value;
|
|
|
|
if (!backupId) {
|
|
this.showError('Please select a backup to download');
|
|
return;
|
|
}
|
|
|
|
console.log('[BackupRestore] Downloading backup:', backupId);
|
|
|
|
// Create a temporary link and trigger download
|
|
const downloadUrl = `./api/backup/download/${backupId}`;
|
|
const link = document.createElement('a');
|
|
link.href = downloadUrl;
|
|
link.download = `${backupId}.zip`;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
|
|
this.showSuccess('Download started');
|
|
},
|
|
|
|
// Upload backup functions
|
|
handleUploadFileSelection: function() {
|
|
const uploadFileInput = document.getElementById('upload-backup-file');
|
|
const confirmationGroup = document.getElementById('upload-confirmation-group');
|
|
const actionGroup = document.getElementById('upload-action-group');
|
|
|
|
if (!uploadFileInput) return;
|
|
|
|
if (uploadFileInput.files.length > 0) {
|
|
if (confirmationGroup) confirmationGroup.style.display = 'block';
|
|
if (actionGroup) actionGroup.style.display = 'block';
|
|
} else {
|
|
if (confirmationGroup) confirmationGroup.style.display = 'none';
|
|
if (actionGroup) actionGroup.style.display = 'none';
|
|
}
|
|
|
|
this.validateUploadConfirmation();
|
|
},
|
|
|
|
validateUploadConfirmation: function() {
|
|
const confirmationInput = document.getElementById('upload-confirmation');
|
|
const uploadBtn = document.getElementById('upload-backup-btn');
|
|
|
|
if (!confirmationInput || !uploadBtn) return;
|
|
|
|
const isValid = confirmationInput.value.toUpperCase() === 'UPLOAD';
|
|
uploadBtn.disabled = !isValid;
|
|
},
|
|
|
|
uploadBackup: function() {
|
|
const uploadFileInput = document.getElementById('upload-backup-file');
|
|
const confirmationInput = document.getElementById('upload-confirmation');
|
|
|
|
if (!uploadFileInput || !confirmationInput) return;
|
|
|
|
const file = uploadFileInput.files[0];
|
|
const confirmation = confirmationInput.value.toUpperCase();
|
|
|
|
if (!file) {
|
|
this.showError('Please select a backup file to upload');
|
|
return;
|
|
}
|
|
|
|
if (confirmation !== 'UPLOAD') {
|
|
this.showError('Please type UPLOAD to confirm');
|
|
return;
|
|
}
|
|
|
|
var self = this;
|
|
var doUpload = function() {
|
|
console.log('[BackupRestore] Uploading backup:', file.name);
|
|
const uploadBtn = document.getElementById('upload-backup-btn');
|
|
if (uploadBtn) {
|
|
uploadBtn.disabled = true;
|
|
uploadBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Uploading and restoring...';
|
|
}
|
|
const formData = new FormData();
|
|
formData.append('backup_file', file);
|
|
fetch('./api/backup/upload', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
self.showSuccess('Backup uploaded and restored successfully! Reloading in 3 seconds...');
|
|
setTimeout(() => {
|
|
window.location.reload();
|
|
}, 3000);
|
|
} else {
|
|
throw new Error(data.error || 'Failed to upload backup');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('[BackupRestore] Error uploading backup:', error);
|
|
self.showError('Failed to upload backup: ' + error.message);
|
|
})
|
|
.finally(() => {
|
|
if (uploadBtn) {
|
|
uploadBtn.disabled = false;
|
|
uploadBtn.innerHTML = '<i class="fas fa-exclamation-triangle"></i> Upload and Restore Backup';
|
|
}
|
|
});
|
|
};
|
|
if (window.HuntarrConfirm && window.HuntarrConfirm.show) {
|
|
window.HuntarrConfirm.show({ title: 'Upload and Restore', message: 'This will permanently overwrite your current database with the uploaded backup. Are you absolutely sure?', confirmLabel: 'Upload', onConfirm: doUpload });
|
|
} else {
|
|
if (!confirm('This will permanently overwrite your current database with the uploaded backup. Are you absolutely sure?')) return;
|
|
doUpload();
|
|
}
|
|
}
|
|
};
|
|
|
|
// Initialize when DOM is ready
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Don't auto-initialize - let the main UI handle it
|
|
console.log('[BackupRestore] Module loaded');
|
|
}); |