Dateien
Hetzner-Backup/v2_adminpanel/templates/backups_new.html
2025-07-03 20:38:33 +00:00

297 Zeilen
12 KiB
HTML

{% extends "base.html" %}
{% block title %}Backup-Verwaltung{% endblock %}
{% block extra_css %}
<style>
.status-success { color: #28a745; }
.status-failed { color: #dc3545; }
.status-in_progress { color: #ffc107; }
.backup-actions { white-space: nowrap; }
.github-indicator {
display: inline-block;
margin-left: 10px;
}
.github-indicator.uploaded { color: #28a745; }
.github-indicator.not-uploaded { color: #6c757d; }
.backup-type-badge {
margin-left: 5px;
}
.recent-backups {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
}
.backup-card {
transition: transform 0.2s;
}
.backup-card:hover {
transform: translateY(-2px);
}
</style>
{% endblock %}
{% block content %}
<div class="container py-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>💾 Backup-Verwaltung</h2>
<div>
<span class="text-muted">Automatische Backups: Täglich um 03:00 Uhr</span>
</div>
</div>
<!-- Letzte 3 GitHub-Backups prominent anzeigen -->
<div class="recent-backups">
<h4 class="mb-3">🌟 Letzte GitHub-Backups</h4>
<div class="row">
{% set github_uploaded_backups = backups | selectattr('github_uploaded', 'eq', True) | selectattr('status', 'eq', 'success') | list %}
{% for backup in github_uploaded_backups[:3] %}
<div class="col-md-4 mb-3">
<div class="card backup-card h-100">
<div class="card-body">
<h6 class="card-title">
{% if backup.is_server_backup %}
🖥️ Server-Backup
{% else %}
💾 Datenbank-Backup
{% endif %}
</h6>
<p class="mb-1"><small>{{ backup.created_at.strftime('%d.%m.%Y %H:%M') }}</small></p>
<p class="mb-1"><small>{{ (backup.filesize / 1024 / 1024)|round(2) }} MB</small></p>
<div class="mt-2">
<button class="btn btn-sm btn-primary" onclick="downloadFromGitHub({{ backup.id }})">
<i class="fas fa-download"></i> Download
</button>
{% if backup.is_server_backup %}
<button class="btn btn-sm btn-warning" onclick="restoreServer({{ backup.id }})">
<i class="fas fa-undo"></i> Restore
</button>
{% else %}
<button class="btn btn-sm btn-warning" onclick="showRestoreModal({{ backup.id }})">
<i class="fas fa-undo"></i> Restore
</button>
{% endif %}
</div>
</div>
</div>
</div>
{% else %}
<div class="col-12">
<p class="text-muted text-center">Noch keine GitHub-Backups vorhanden</p>
</div>
{% endfor %}
</div>
</div>
<!-- Backup-Info -->
<div class="row mb-4">
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">📊 Backup-Statistiken</h5>
{% set total_backups = backups | selectattr('github_uploaded', 'eq', True) | list | length %}
{% set db_backups = backups | selectattr('github_uploaded', 'eq', True) | selectattr('is_server_backup', 'eq', False) | list | length %}
{% set server_backups = backups | selectattr('github_uploaded', 'eq', True) | selectattr('is_server_backup', 'eq', True) | list | length %}
<p class="mb-1"><strong>Gesamt:</strong> {{ total_backups }} Backups</p>
<p class="mb-1"><strong>Datenbank:</strong> {{ db_backups }}</p>
<p class="mb-0"><strong>Server:</strong> {{ server_backups }}</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">💿 Speicherplatz</h5>
{% set local_size = backups | selectattr('local_deleted', 'eq', False) | selectattr('file_exists', 'eq', True) | sum(attribute='filesize') %}
<p class="mb-1"><strong>Lokal:</strong> {{ (local_size / 1024 / 1024)|round(2) }} MB</p>
<p class="mb-0"><small class="text-muted">GitHub: Unbegrenzt</small></p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">🔧 Backup erstellen</h5>
<div class="d-grid gap-2">
<button class="btn btn-primary" onclick="createBackup('database')">
💾 Datenbank-Backup
</button>
<button class="btn btn-success" onclick="createBackup('server')">
🖥️ Server-Backup
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Alle GitHub Backups -->
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="fab fa-github"></i> Alle GitHub-Backups</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Typ</th>
<th>Erstellt</th>
<th>Größe</th>
<th>Status</th>
<th>Erstellt von</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for backup in github_uploaded_backups %}
<tr>
<td>
{% if backup.is_server_backup %}
<span class="badge bg-success">🖥️ Server</span>
{% else %}
<span class="badge bg-primary">💾 DB</span>
{% endif %}
</td>
<td>{{ backup.created_at.strftime('%d.%m.%Y %H:%M:%S') }}</td>
<td>{{ (backup.filesize / 1024 / 1024)|round(2) }} MB</td>
<td>
<span class="status-{{ backup.status }}">
{% if backup.status == 'success' %}✅{% else %}❌{% endif %}
{{ backup.status }}
</span>
</td>
<td>{{ backup.created_by or 'system' }}</td>
<td class="backup-actions">
<button class="btn btn-sm btn-primary" onclick="downloadFromGitHub({{ backup.id }})" title="Von GitHub herunterladen">
<i class="fas fa-download"></i>
</button>
{% if backup.is_server_backup %}
<button class="btn btn-sm btn-warning" onclick="restoreServer({{ backup.id }})" title="Server wiederherstellen">
<i class="fas fa-undo"></i>
</button>
{% else %}
<button class="btn btn-sm btn-warning" onclick="showRestoreModal({{ backup.id }})" title="Datenbank wiederherstellen">
<i class="fas fa-database"></i>
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Restore Modal für Datenbank -->
<div class="modal fade" id="restoreModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Backup wiederherstellen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>⚠️ <strong>Warnung:</strong> Alle aktuellen Daten werden überschrieben!</p>
<div class="mb-3">
<label class="form-label">Verschlüsselungsschlüssel (falls benötigt):</label>
<input type="password" class="form-control" id="encryptionKey" placeholder="Leer lassen für Standard-Schlüssel">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="button" class="btn btn-danger" onclick="confirmRestore()">Wiederherstellen</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
let selectedBackupId = null;
function createBackup(type) {
const btn = event.target;
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Erstelle...';
fetch('/admin/backup/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: type,
push_to_github: true,
delete_local: true // Automatisch lokal löschen
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Backup wurde erfolgreich erstellt und zu GitHub hochgeladen!');
location.reload();
} else {
alert('Fehler: ' + data.error);
btn.disabled = false;
btn.innerHTML = type === 'database' ? '💾 Datenbank-Backup' : '🖥️ Server-Backup';
}
})
.catch(error => {
alert('Fehler beim Erstellen des Backups: ' + error);
btn.disabled = false;
btn.innerHTML = type === 'database' ? '💾 Datenbank-Backup' : '🖥️ Server-Backup';
});
}
function downloadFromGitHub(backupId) {
window.location.href = `/admin/backup/download/${backupId}?from_github=true`;
}
function showRestoreModal(backupId) {
selectedBackupId = backupId;
const modal = new bootstrap.Modal(document.getElementById('restoreModal'));
modal.show();
}
function confirmRestore() {
const encryptionKey = document.getElementById('encryptionKey').value;
fetch(`/admin/backup/restore/${selectedBackupId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
encryption_key: encryptionKey
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Backup wurde erfolgreich wiederhergestellt!');
location.reload();
} else {
alert('Fehler beim Wiederherstellen: ' + data.error);
}
});
bootstrap.Modal.getInstance(document.getElementById('restoreModal')).hide();
}
function restoreServer(backupId) {
if (!confirm('⚠️ WARNUNG: Dies wird den kompletten Server-Zustand wiederherstellen!\n\nAlle aktuellen Konfigurationen, Datenbanken und Docker-Volumes werden überschrieben.\n\nSind Sie sicher?')) {
return;
}
alert('Server-Restore muss derzeit manuell über SSH durchgeführt werden:\n\n' +
'1. SSH zum Server verbinden\n' +
'2. Backup von GitHub herunterladen\n' +
'3. ./restore_full_backup.sh <backup_name> ausführen');
}
</script>
{% endblock %}