829 lines
33 KiB
HTML
829 lines
33 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Configuration Backup{% endblock %}
|
|
|
|
{% block content %}
|
|
<div id="content" class="app-content">
|
|
<div class="container">
|
|
<ul class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item active">Configuration Backup</li>
|
|
</ul>
|
|
|
|
<div class="row align-items-center mb-3">
|
|
<div class="col">
|
|
<h1 class="page-header">Configuration Backup</h1>
|
|
<p class="text-muted">Create and manage system configuration backups</p>
|
|
</div>
|
|
<div class="col-auto">
|
|
<button type="button" class="btn btn-primary" onclick="createBackup()">
|
|
<i class="fa fa-plus me-2"></i>Create Backup
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Backup Actions -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-body text-center">
|
|
<i class="fa fa-download fa-3x text-primary mb-3"></i>
|
|
<h5>Create Backup</h5>
|
|
<p class="text-muted">Generate a complete configuration backup</p>
|
|
<button class="btn btn-primary" onclick="showCreateBackupModal()">
|
|
<i class="fa fa-plus me-2"></i>New Backup
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-body text-center">
|
|
<i class="fa fa-upload fa-3x text-success mb-3"></i>
|
|
<h5>Restore Backup</h5>
|
|
<p class="text-muted">Restore from an existing backup file</p>
|
|
<button class="btn btn-success" onclick="showRestoreBackupModal()">
|
|
<i class="fa fa-upload me-2"></i>Restore
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-body text-center">
|
|
<i class="fa fa-clock fa-3x text-info mb-3"></i>
|
|
<h5>Scheduled Backups</h5>
|
|
<p class="text-muted">Configure automatic backup schedules</p>
|
|
<button class="btn btn-info" onclick="showScheduleModal()">
|
|
<i class="fa fa-calendar me-2"></i>Schedule
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Backup List -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Backup History</h4>
|
|
<div class="card-tools">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="refreshBackupList()">
|
|
<i class="fa fa-sync me-1"></i>Refresh
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table id="backupTable" class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Backup Name</th>
|
|
<th>Type</th>
|
|
<th>Size</th>
|
|
<th>Created</th>
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="backupTableBody">
|
|
<tr>
|
|
<td colspan="6" class="text-center text-muted">
|
|
<i class="fa fa-spinner fa-spin me-2"></i>Loading backups...
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Create Backup Modal -->
|
|
<div class="modal fade" id="createBackupModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Create Configuration Backup</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="createBackupForm">
|
|
<div class="modal-body">
|
|
{% csrf_token %}
|
|
<div class="mb-3">
|
|
<label class="form-label">Backup Name</label>
|
|
<input type="text" name="backup_name" class="form-control" placeholder="e.g., Pre-upgrade backup" required>
|
|
<div class="form-text">A descriptive name for this backup</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Backup Type</label>
|
|
<select name="backup_type" class="form-select" required>
|
|
<option value="full">Full Configuration</option>
|
|
<option value="settings">Settings Only</option>
|
|
<option value="database">Database Schema</option>
|
|
<option value="files">Configuration Files</option>
|
|
<option value="custom">Custom Selection</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="customBackupOptions" style="display: none;">
|
|
<label class="form-label">Include Components</label>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="include_settings" checked>
|
|
<label class="form-check-label">Django Settings</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="include_database" checked>
|
|
<label class="form-check-label">Database Configuration</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="include_media">
|
|
<label class="form-check-label">Media Files</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="include_static">
|
|
<label class="form-check-label">Static Files</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="include_logs">
|
|
<label class="form-check-label">Log Files</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="include_certificates">
|
|
<label class="form-check-label">SSL Certificates</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Compression</label>
|
|
<select name="compression" class="form-select">
|
|
<option value="gzip">GZip (Recommended)</option>
|
|
<option value="zip">ZIP</option>
|
|
<option value="none">No Compression</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="encrypt_backup">
|
|
<label class="form-check-label">Encrypt Backup</label>
|
|
</div>
|
|
<div class="form-text">Recommended for sensitive configurations</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Description (Optional)</label>
|
|
<textarea name="description" class="form-control" rows="3" placeholder="Additional notes about this backup..."></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fa fa-download me-2"></i>Create Backup
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Restore Backup Modal -->
|
|
<div class="modal fade" id="restoreBackupModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Restore Configuration Backup</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="restoreBackupForm">
|
|
<div class="modal-body">
|
|
{% csrf_token %}
|
|
<div class="mb-3">
|
|
<label class="form-label">Restore Method</label>
|
|
<select id="restoreMethod" name="restore_method" class="form-select" required>
|
|
<option value="existing">From Existing Backup</option>
|
|
<option value="upload">Upload Backup File</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="existingBackupSection">
|
|
<div class="mb-3">
|
|
<label class="form-label">Select Backup</label>
|
|
<select name="backup_id" class="form-select" id="existingBackupSelect">
|
|
<option value="">Loading backups...</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="uploadBackupSection" style="display: none;">
|
|
<div class="mb-3">
|
|
<label class="form-label">Backup File</label>
|
|
<input type="file" name="backup_file" class="form-control" accept=".gz,.zip,.tar">
|
|
<div class="form-text">Supported formats: .gz, .zip, .tar</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Restore Options</label>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="create_backup_before_restore" checked>
|
|
<label class="form-check-label">Create backup before restore</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="restart_services">
|
|
<label class="form-check-label">Restart services after restore</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="validate_config" checked>
|
|
<label class="form-check-label">Validate configuration</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alert alert-warning">
|
|
<i class="fa fa-exclamation-triangle me-2"></i>
|
|
<strong>Warning:</strong> Restoring a backup will overwrite current configuration.
|
|
Make sure to create a backup of current settings first.
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-warning">
|
|
<i class="fa fa-upload me-2"></i>Restore Backup
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Schedule Backup Modal -->
|
|
<div class="modal fade" id="scheduleBackupModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Schedule Automatic Backups</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="scheduleBackupForm">
|
|
<div class="modal-body">
|
|
{% csrf_token %}
|
|
<div class="mb-3">
|
|
<label class="form-label">Schedule Name</label>
|
|
<input type="text" name="schedule_name" class="form-control" placeholder="e.g., Daily Configuration Backup" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Frequency</label>
|
|
<select name="frequency" class="form-select" required>
|
|
<option value="daily">Daily</option>
|
|
<option value="weekly">Weekly</option>
|
|
<option value="monthly">Monthly</option>
|
|
<option value="custom">Custom (Cron)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="customCronSection" style="display: none;">
|
|
<div class="mb-3">
|
|
<label class="form-label">Cron Expression</label>
|
|
<input type="text" name="cron_expression" class="form-control" placeholder="0 2 * * *">
|
|
<div class="form-text">Example: "0 2 * * *" for daily at 2 AM</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Backup Type</label>
|
|
<select name="scheduled_backup_type" class="form-select" required>
|
|
<option value="full">Full Configuration</option>
|
|
<option value="settings">Settings Only</option>
|
|
<option value="database">Database Schema</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Retention Policy</label>
|
|
<select name="retention_days" class="form-select">
|
|
<option value="7">Keep for 7 days</option>
|
|
<option value="30">Keep for 30 days</option>
|
|
<option value="90">Keep for 90 days</option>
|
|
<option value="365">Keep for 1 year</option>
|
|
<option value="0">Keep forever</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="email_notification" checked>
|
|
<label class="form-check-label">Email notification on completion</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="email_on_failure" checked>
|
|
<label class="form-check-label">Email notification on failure</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-info">
|
|
<i class="fa fa-calendar me-2"></i>Create Schedule
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Backup Progress Modal -->
|
|
<div class="modal fade" id="backupProgressModal" tabindex="-1" data-bs-backdrop="static">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Backup Progress</h5>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between">
|
|
<span id="progressLabel">Initializing...</span>
|
|
<span id="progressPercent">0%</span>
|
|
</div>
|
|
<div class="progress">
|
|
<div id="progressBar" class="progress-bar" style="width: 0%"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="progressLog" class="bg-light p-3" style="height: 200px; overflow-y: auto; font-family: monospace; font-size: 0.875rem;">
|
|
<!-- Progress messages will appear here -->
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" id="cancelBackupBtn" class="btn btn-secondary">Cancel</button>
|
|
<button type="button" id="closeProgressBtn" class="btn btn-primary" style="display: none;">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Backup Details Modal -->
|
|
<div class="modal fade" id="backupDetailsModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Backup Details</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="backupDetailsContent">
|
|
<!-- Backup details will be loaded here -->
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
<button type="button" class="btn btn-primary" onclick="downloadBackup()">
|
|
<i class="fa fa-download me-2"></i>Download
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
var currentBackupId = null;
|
|
|
|
$(document).ready(function() {
|
|
loadBackupList();
|
|
setupEventHandlers();
|
|
});
|
|
|
|
function setupEventHandlers() {
|
|
// Form submissions
|
|
$('#createBackupForm').submit(function(e) {
|
|
e.preventDefault();
|
|
createBackup();
|
|
});
|
|
|
|
$('#restoreBackupForm').submit(function(e) {
|
|
e.preventDefault();
|
|
restoreBackup();
|
|
});
|
|
|
|
$('#scheduleBackupForm').submit(function(e) {
|
|
e.preventDefault();
|
|
scheduleBackup();
|
|
});
|
|
|
|
// Backup type change
|
|
$('select[name="backup_type"]').change(function() {
|
|
if ($(this).val() === 'custom') {
|
|
$('#customBackupOptions').show();
|
|
} else {
|
|
$('#customBackupOptions').hide();
|
|
}
|
|
});
|
|
|
|
// Restore method change
|
|
$('#restoreMethod').change(function() {
|
|
if ($(this).val() === 'upload') {
|
|
$('#existingBackupSection').hide();
|
|
$('#uploadBackupSection').show();
|
|
} else {
|
|
$('#existingBackupSection').show();
|
|
$('#uploadBackupSection').hide();
|
|
}
|
|
});
|
|
|
|
// Schedule frequency change
|
|
$('select[name="frequency"]').change(function() {
|
|
if ($(this).val() === 'custom') {
|
|
$('#customCronSection').show();
|
|
} else {
|
|
$('#customCronSection').hide();
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadBackupList() {
|
|
$.get('{% url "core:get_backup_list" %}', function(data) {
|
|
renderBackupList(data.backups);
|
|
populateBackupSelects(data.backups);
|
|
}).fail(function() {
|
|
$('#backupTableBody').html('<tr><td colspan="6" class="text-center text-danger">Failed to load backups</td></tr>');
|
|
});
|
|
}
|
|
|
|
function renderBackupList(backups) {
|
|
var html = '';
|
|
|
|
if (backups.length === 0) {
|
|
html = '<tr><td colspan="6" class="text-center text-muted">No backups found</td></tr>';
|
|
} else {
|
|
backups.forEach(function(backup) {
|
|
html += '<tr>' +
|
|
'<td>' +
|
|
'<strong>' + backup.name + '</strong><br>' +
|
|
'<small class="text-muted">' + (backup.description || 'No description') + '</small>' +
|
|
'</td>' +
|
|
'<td><span class="badge bg-' + getTypeColor(backup.type) + '">' + backup.type + '</span></td>' +
|
|
'<td>' + formatFileSize(backup.size) + '</td>' +
|
|
'<td>' + formatDate(backup.created_at) + '</td>' +
|
|
'<td><span class="badge bg-' + getStatusColor(backup.status) + '">' + backup.status + '</span></td>' +
|
|
'<td>' +
|
|
'<div class="btn-group btn-group-sm">' +
|
|
'<button class="btn btn-outline-primary" onclick="viewBackupDetails(\'' + backup.id + '\')" title="View Details">' +
|
|
'<i class="fa fa-eye"></i>' +
|
|
'</button>' +
|
|
'<button class="btn btn-outline-success" onclick="downloadBackup(\'' + backup.id + '\')" title="Download">' +
|
|
'<i class="fa fa-download"></i>' +
|
|
'</button>' +
|
|
'<button class="btn btn-outline-warning" onclick="restoreFromBackup(\'' + backup.id + '\')" title="Restore">' +
|
|
'<i class="fa fa-upload"></i>' +
|
|
'</button>' +
|
|
'<button class="btn btn-outline-danger" onclick="deleteBackup(\'' + backup.id + '\')" title="Delete">' +
|
|
'<i class="fa fa-trash"></i>' +
|
|
'</button>' +
|
|
'</div>' +
|
|
'</td>' +
|
|
'</tr>';
|
|
});
|
|
}
|
|
|
|
$('#backupTableBody').html(html);
|
|
}
|
|
|
|
function populateBackupSelects(backups) {
|
|
var options = '<option value="">Select a backup...</option>';
|
|
backups.forEach(function(backup) {
|
|
if (backup.status === 'completed') {
|
|
options += '<option value="' + backup.id + '">' + backup.name + ' (' + formatDate(backup.created_at) + ')</option>';
|
|
}
|
|
});
|
|
$('#existingBackupSelect').html(options);
|
|
}
|
|
|
|
function getTypeColor(type) {
|
|
switch (type) {
|
|
case 'full': return 'primary';
|
|
case 'settings': return 'info';
|
|
case 'database': return 'warning';
|
|
case 'files': return 'secondary';
|
|
default: return 'light';
|
|
}
|
|
}
|
|
|
|
function getStatusColor(status) {
|
|
switch (status) {
|
|
case 'completed': return 'success';
|
|
case 'failed': return 'danger';
|
|
case 'in_progress': return 'warning';
|
|
case 'pending': return 'secondary';
|
|
default: return 'light';
|
|
}
|
|
}
|
|
|
|
function formatFileSize(bytes) {
|
|
if (bytes === 0) return '0 Bytes';
|
|
var k = 1024;
|
|
var sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
var i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
}
|
|
|
|
function formatDate(dateString) {
|
|
return new Date(dateString).toLocaleString();
|
|
}
|
|
|
|
function showCreateBackupModal() {
|
|
$('#createBackupModal').modal('show');
|
|
}
|
|
|
|
function showRestoreBackupModal() {
|
|
$('#restoreBackupModal').modal('show');
|
|
}
|
|
|
|
function showScheduleModal() {
|
|
$('#scheduleBackupModal').modal('show');
|
|
}
|
|
|
|
function createBackup() {
|
|
var formData = new FormData($('#createBackupForm')[0]);
|
|
|
|
$('#createBackupModal').modal('hide');
|
|
showBackupProgress();
|
|
|
|
$.post('{% url "core:create_backup" %}', formData, function(data) {
|
|
if (data.success) {
|
|
currentBackupId = data.backup_id;
|
|
monitorBackupProgress(data.backup_id);
|
|
} else {
|
|
hideBackupProgress();
|
|
toastr.error('Failed to start backup: ' + data.error);
|
|
}
|
|
}).fail(function() {
|
|
hideBackupProgress();
|
|
toastr.error('Failed to start backup');
|
|
});
|
|
}
|
|
|
|
function restoreBackup() {
|
|
if (!confirm('Are you sure you want to restore this backup? This will overwrite current configuration.')) {
|
|
return;
|
|
}
|
|
|
|
var formData = new FormData($('#restoreBackupForm')[0]);
|
|
|
|
$('#restoreBackupModal').modal('hide');
|
|
showBackupProgress('Restoring backup...');
|
|
|
|
$.post('{% url "core:restore_backup" %}', formData, function(data) {
|
|
if (data.success) {
|
|
monitorRestoreProgress(data.restore_id);
|
|
} else {
|
|
hideBackupProgress();
|
|
toastr.error('Failed to start restore: ' + data.error);
|
|
}
|
|
}).fail(function() {
|
|
hideBackupProgress();
|
|
toastr.error('Failed to start restore');
|
|
});
|
|
}
|
|
|
|
function scheduleBackup() {
|
|
var formData = new FormData($('#scheduleBackupForm')[0]);
|
|
|
|
$.post('{% url "core:schedule_backup" %}', formData, function(data) {
|
|
if (data.success) {
|
|
$('#scheduleBackupModal').modal('hide');
|
|
toastr.success('Backup schedule created successfully');
|
|
} else {
|
|
toastr.error('Failed to create schedule: ' + data.error);
|
|
}
|
|
}).fail(function() {
|
|
toastr.error('Failed to create schedule');
|
|
});
|
|
}
|
|
|
|
function showBackupProgress(title = 'Creating backup...') {
|
|
$('#backupProgressModal .modal-title').text(title);
|
|
$('#progressLabel').text('Initializing...');
|
|
$('#progressPercent').text('0%');
|
|
$('#progressBar').css('width', '0%');
|
|
$('#progressLog').html('');
|
|
$('#cancelBackupBtn').show();
|
|
$('#closeProgressBtn').hide();
|
|
$('#backupProgressModal').modal('show');
|
|
}
|
|
|
|
function hideBackupProgress() {
|
|
$('#backupProgressModal').modal('hide');
|
|
}
|
|
|
|
function monitorBackupProgress(backupId) {
|
|
var checkProgress = function() {
|
|
$.get('{% url "core:get_backup_progress" %}', {backup_id: backupId}, function(data) {
|
|
updateProgress(data.progress, data.message, data.log);
|
|
|
|
if (data.status === 'completed') {
|
|
$('#progressLabel').text('Backup completed successfully');
|
|
$('#cancelBackupBtn').hide();
|
|
$('#closeProgressBtn').show();
|
|
toastr.success('Backup created successfully');
|
|
loadBackupList();
|
|
} else if (data.status === 'failed') {
|
|
$('#progressLabel').text('Backup failed');
|
|
$('#cancelBackupBtn').hide();
|
|
$('#closeProgressBtn').show();
|
|
toastr.error('Backup failed: ' + data.error);
|
|
} else {
|
|
setTimeout(checkProgress, 2000);
|
|
}
|
|
}).fail(function() {
|
|
$('#progressLabel').text('Failed to get progress');
|
|
$('#cancelBackupBtn').hide();
|
|
$('#closeProgressBtn').show();
|
|
});
|
|
};
|
|
|
|
checkProgress();
|
|
}
|
|
|
|
function monitorRestoreProgress(restoreId) {
|
|
var checkProgress = function() {
|
|
$.get('{% url "core:get_restore_progress" %}', {restore_id: restoreId}, function(data) {
|
|
updateProgress(data.progress, data.message, data.log);
|
|
|
|
if (data.status === 'completed') {
|
|
$('#progressLabel').text('Restore completed successfully');
|
|
$('#cancelBackupBtn').hide();
|
|
$('#closeProgressBtn').show();
|
|
toastr.success('Configuration restored successfully');
|
|
} else if (data.status === 'failed') {
|
|
$('#progressLabel').text('Restore failed');
|
|
$('#cancelBackupBtn').hide();
|
|
$('#closeProgressBtn').show();
|
|
toastr.error('Restore failed: ' + data.error);
|
|
} else {
|
|
setTimeout(checkProgress, 2000);
|
|
}
|
|
}).fail(function() {
|
|
$('#progressLabel').text('Failed to get progress');
|
|
$('#cancelBackupBtn').hide();
|
|
$('#closeProgressBtn').show();
|
|
});
|
|
};
|
|
|
|
checkProgress();
|
|
}
|
|
|
|
function updateProgress(progress, message, log) {
|
|
$('#progressPercent').text(progress + '%');
|
|
$('#progressBar').css('width', progress + '%');
|
|
$('#progressLabel').text(message);
|
|
|
|
if (log) {
|
|
var logHtml = $('#progressLog').html();
|
|
logHtml += '<div>' + new Date().toLocaleTimeString() + ': ' + log + '</div>';
|
|
$('#progressLog').html(logHtml);
|
|
$('#progressLog').scrollTop($('#progressLog')[0].scrollHeight);
|
|
}
|
|
}
|
|
|
|
function viewBackupDetails(backupId) {
|
|
$.get('{% url "core:get_backup_details" %}', {backup_id: backupId}, function(data) {
|
|
var detailsHtml = '<div class="row">' +
|
|
'<div class="col-md-6">' +
|
|
'<h6>Backup Information</h6>' +
|
|
'<table class="table table-sm">' +
|
|
'<tr><td><strong>Name:</strong></td><td>' + data.name + '</td></tr>' +
|
|
'<tr><td><strong>Type:</strong></td><td><span class="badge bg-' + getTypeColor(data.type) + '">' + data.type + '</span></td></tr>' +
|
|
'<tr><td><strong>Size:</strong></td><td>' + formatFileSize(data.size) + '</td></tr>' +
|
|
'<tr><td><strong>Created:</strong></td><td>' + formatDate(data.created_at) + '</td></tr>' +
|
|
'<tr><td><strong>Status:</strong></td><td><span class="badge bg-' + getStatusColor(data.status) + '">' + data.status + '</span></td></tr>' +
|
|
'</table>' +
|
|
'</div>' +
|
|
'<div class="col-md-6">' +
|
|
'<h6>Configuration</h6>' +
|
|
'<table class="table table-sm">' +
|
|
'<tr><td><strong>Compression:</strong></td><td>' + (data.compression || 'None') + '</td></tr>' +
|
|
'<tr><td><strong>Encrypted:</strong></td><td>' + (data.encrypted ? 'Yes' : 'No') + '</td></tr>' +
|
|
'<tr><td><strong>Components:</strong></td><td>' + (data.components || []).join(', ') + '</td></tr>' +
|
|
'</table>' +
|
|
'</div>' +
|
|
'</div>';
|
|
|
|
if (data.description) {
|
|
detailsHtml += '<hr><h6>Description</h6><p>' + data.description + '</p>';
|
|
}
|
|
|
|
if (data.log) {
|
|
detailsHtml += '<hr><h6>Backup Log</h6><pre class="bg-light p-3" style="max-height: 200px; overflow-y: auto;">' + data.log + '</pre>';
|
|
}
|
|
|
|
$('#backupDetailsContent').html(detailsHtml);
|
|
currentBackupId = backupId;
|
|
$('#backupDetailsModal').modal('show');
|
|
});
|
|
}
|
|
|
|
function downloadBackup(backupId = null) {
|
|
var id = backupId || currentBackupId;
|
|
if (id) {
|
|
window.open('{% url "core:download_backup" %}?backup_id=' + id, '_blank');
|
|
}
|
|
}
|
|
|
|
function restoreFromBackup(backupId) {
|
|
$('#existingBackupSelect').val(backupId);
|
|
$('#restoreMethod').val('existing');
|
|
$('#existingBackupSection').show();
|
|
$('#uploadBackupSection').hide();
|
|
showRestoreBackupModal();
|
|
}
|
|
|
|
function deleteBackup(backupId) {
|
|
if (confirm('Are you sure you want to delete this backup? This action cannot be undone.')) {
|
|
$.post('{% url "core:delete_backup" %}', {backup_id: backupId}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Backup deleted successfully');
|
|
loadBackupList();
|
|
} else {
|
|
toastr.error('Failed to delete backup: ' + data.error);
|
|
}
|
|
}).fail(function() {
|
|
toastr.error('Failed to delete backup');
|
|
});
|
|
}
|
|
}
|
|
|
|
function refreshBackupList() {
|
|
loadBackupList();
|
|
toastr.success('Backup list refreshed');
|
|
}
|
|
|
|
// Close progress modal handlers
|
|
$('#closeProgressBtn').click(function() {
|
|
hideBackupProgress();
|
|
});
|
|
|
|
$('#cancelBackupBtn').click(function() {
|
|
if (currentBackupId && confirm('Are you sure you want to cancel the backup?')) {
|
|
$.post('{% url "core:cancel_backup" %}', {backup_id: currentBackupId}, function(data) {
|
|
hideBackupProgress();
|
|
if (data.success) {
|
|
toastr.info('Backup cancelled');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.card-tools {
|
|
margin-left: auto;
|
|
}
|
|
|
|
.progress {
|
|
height: 20px;
|
|
}
|
|
|
|
.progress-bar {
|
|
transition: width 0.3s ease;
|
|
}
|
|
|
|
#progressLog {
|
|
font-size: 0.875rem;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
#progressLog div {
|
|
margin-bottom: 2px;
|
|
}
|
|
|
|
.btn-group-sm .btn {
|
|
padding: 0.25rem 0.5rem;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.table td {
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.badge {
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
pre {
|
|
font-size: 0.875rem;
|
|
line-height: 1.4;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|