Files
hostpanel/templates/ssh_manager/yedeklemeler.html
ilkeral 342f1314c7 yeni
2025-07-21 13:49:36 +03:00

427 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends 'ssh_manager/base.html' %}
{% block title %}Yedeklemeler - Hosting Yönetim Paneli{% endblock %}
{% block content %}
<!-- Yedeklemeler Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h3 class="mb-1">
<i class="bi bi-cloud-arrow-up me-2"></i>Yedeklemeler
</h3>
<small class="text-muted">Proje yedekleme işlemleri ve S3 yönetimi</small>
</div>
<div class="d-flex gap-2">
<button class="btn btn-info" onclick="refreshBackupStatus()">
<i class="bi bi-arrow-clockwise"></i> Durumu Yenile
</button>
<button class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#backupModal">
<i class="bi bi-cloud-arrow-up"></i> Yeni Yedekleme
</button>
<button class="btn btn-success" onclick="startAllBackups()">
<i class="bi bi-cloud-arrow-up-fill"></i> Tümünü Yedekle
</button>
</div>
</div>
<!-- Yedekleme İstatistikleri -->
<div class="row g-4 mb-4">
<div class="col-md-3">
<div class="card">
<div class="card-body text-center">
<div class="icon-box bg-info bg-opacity-10 mx-auto mb-3">
<i class="bi bi-cloud-arrow-up text-info"></i>
</div>
<h4 class="mb-1">{{ total_backups|default:0 }}</h4>
<p class="text-muted mb-0">Toplam Yedekleme</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body text-center">
<div class="icon-box bg-success bg-opacity-10 mx-auto mb-3">
<i class="bi bi-check-circle text-success"></i>
</div>
<h4 class="mb-1">{{ successful_backups|default:0 }}</h4>
<p class="text-muted mb-0">Başarılı</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body text-center">
<div class="icon-box bg-danger bg-opacity-10 mx-auto mb-3">
<i class="bi bi-x-circle text-danger"></i>
</div>
<h4 class="mb-1">{{ failed_backups|default:0 }}</h4>
<p class="text-muted mb-0">Başarısız</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body text-center">
<div class="icon-box bg-warning bg-opacity-10 mx-auto mb-3">
<i class="bi bi-clock text-warning"></i>
</div>
<h4 class="mb-1">
{% if backup_logs %}
{{ backup_logs.0.created_at|date:"d.m H:i" }}
{% else %}
-
{% endif %}
</h4>
<p class="text-muted mb-0">Son Yedekleme</p>
</div>
</div>
</div>
</div>
<!-- Yedekleme Geçmişi -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="bi bi-clock-history me-2"></i>Yedekleme Geçmişi
</h5>
</div>
<div class="card-body">
{% if backup_logs %}
<div class="table-responsive">
<table class="table table-dark table-striped">
<thead>
<tr>
<th>Tarih</th>
<th>Proje</th>
<th>Host</th>
<th>Durum</th>
<th>Detay</th>
<th class="actions">İşlemler</th>
</tr>
</thead>
<tbody>
{% for log in backup_logs %}
<tr>
<td>
<span>{{ log.created_at|date:"d.m.Y" }}</span><br>
<small class="text-muted">{{ log.created_at|date:"H:i:s" }}</small>
</td>
<td>
{% if log.ssh_credential %}
<strong>{{ log.ssh_credential.name }}</strong>
{% else %}
<span class="text-muted">Genel</span>
{% endif %}
</td>
<td>
{% if log.ssh_credential %}
<small>{{ log.ssh_credential.hostname }}</small>
{% else %}
<small class="text-muted">-</small>
{% endif %}
</td>
<td>
<span class="badge {% if log.status == 'success' %}bg-success{% elif log.status == 'error' %}bg-danger{% else %}bg-warning{% endif %} fs-6">
{% if log.status == 'success' %}
<i class="bi bi-check-circle me-1"></i>Başarılı
{% elif log.status == 'error' %}
<i class="bi bi-x-circle me-1"></i>Hata
{% else %}
<i class="bi bi-clock me-1"></i>Bekliyor
{% endif %}
</span>
</td>
<td>
{% if log.output %}
<button class="btn btn-sm btn-outline-info" onclick="showLogDetail('{{ log.output|escapejs }}', '{{ log.created_at|date:"d.m.Y H:i" }}')">
<i class="bi bi-eye"></i> Detay
</button>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td>
<div class="btn-group" role="group">
{% if log.ssh_credential %}
<button class="btn btn-sm btn-outline-warning" onclick="retryBackup({{ log.ssh_credential.id }})" title="Tekrar Dene">
<i class="bi bi-arrow-clockwise"></i>
</button>
{% endif %}
<button class="btn btn-sm btn-outline-danger" onclick="deleteLog({{ log.id }})" title="Kaydı Sil">
<i class="bi bi-trash"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-cloud-arrow-up" style="font-size: 3rem; color: #6c757d;"></i>
<h5 class="mt-3 text-muted">Henüz yedekleme yapılmamış</h5>
<p class="text-muted">İlk yedeklemenizi başlatmak için yukarıdaki butonu kullanın</p>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#backupModal">
<i class="bi bi-cloud-arrow-up"></i> İlk Yedeklemeyi Başlat
</button>
</div>
{% endif %}
</div>
</div>
<!-- Yedekleme Modal -->
<div class="modal fade" id="backupModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark border-secondary">
<div class="modal-header border-secondary">
<h5 class="modal-title">
<i class="bi bi-cloud-arrow-up me-2"></i>Yeni Yedekleme
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="backupForm">
{% csrf_token %}
<div class="mb-3">
<label for="backupProject" class="form-label">Proje Seçin</label>
<select class="form-select bg-dark text-white border-secondary" id="backupProject" name="project_id" required>
<option value="">Proje seçin...</option>
{% load ssh_manager_tags %}
{% get_projects as projects %}
{% for project in projects %}
<option value="{{ project.id }}">{{ project.name }} ({{ project.ssh_credential.hostname }})</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="backupType" class="form-label">Yedekleme Türü</label>
<select class="form-select bg-dark text-white border-secondary" id="backupType" name="backup_type">
<option value="full">Tam Yedekleme</option>
<option value="files">Sadece Dosyalar</option>
<option value="database">Sadece Veritabanı</option>
</select>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="compressBackup" name="compress" checked>
<label class="form-check-label" for="compressBackup">
Sıkıştırılmış yedekleme (.tar.gz)
</label>
</div>
</div>
<div class="mb-3">
<label for="backupNote" class="form-label">Not (Opsiyonel)</label>
<textarea class="form-control bg-dark text-white border-secondary" id="backupNote" name="note" rows="3" placeholder="Yedekleme hakkında not..."></textarea>
</div>
</form>
</div>
<div class="modal-footer border-secondary">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">İptal</button>
<button type="button" class="btn btn-warning" onclick="startBackup()">
<i class="bi bi-cloud-arrow-up"></i> Yedeklemeyi Başlat
</button>
</div>
</div>
</div>
</div>
<!-- Log Detay Modal -->
<div class="modal fade" id="logDetailModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content bg-dark border-secondary">
<div class="modal-header border-secondary">
<h5 class="modal-title">
<i class="bi bi-file-text me-2"></i>Yedekleme Detayı
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="logDetailContent" style="font-family: monospace; background: #1a1d23; padding: 1rem; border-radius: 0.375rem; max-height: 400px; overflow-y: auto;">
</div>
</div>
<div class="modal-footer border-secondary">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
</div>
</div>
</div>
</div>
<style>
.icon-box {
width: 60px;
height: 60px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.icon-box i {
font-size: 1.5rem;
}
.card {
background: #23272b;
border: 1px solid #444;
}
.card-header {
background: #1a1d23;
border-bottom: 1px solid #444;
}
.table-dark {
--bs-table-bg: #23272b;
}
.badge {
font-size: 0.8rem;
}
</style>
<script>
// Yedekleme durumunu yenile
function refreshBackupStatus() {
showToast('Yedekleme durumu kontrol ediliyor...', 'info');
setTimeout(() => location.reload(), 1000);
}
// Tüm projeleri yedekle
function startAllBackups() {
if (!confirm('Tüm projelerin yedeklenmesi uzun sürebilir. Devam etmek istediğinizden emin misiniz?')) {
return;
}
showToast('Toplu yedekleme başlatılıyor...', 'info');
fetch('/backup-all-projects/', {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast(data.message, 'success');
setTimeout(() => location.reload(), 2000);
} else {
showToast(data.message || 'Toplu yedekleme başlatılamadı', 'error');
}
})
.catch(error => {
console.error('Toplu yedekleme hatası:', error);
showToast('Toplu yedekleme hatası', 'error');
});
}
// Tek proje yedekleme başlat
function startBackup() {
const form = document.getElementById('backupForm');
const formData = new FormData(form);
if (!formData.get('project_id')) {
showToast('Lütfen bir proje seçin', 'error');
return;
}
showToast('Yedekleme başlatılıyor...', 'info');
fetch('/start-backup/', {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken')
},
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast(data.message, 'success');
bootstrap.Modal.getInstance(document.getElementById('backupModal')).hide();
setTimeout(() => location.reload(), 2000);
} else {
showToast(data.message || 'Yedekleme başlatılamadı', 'error');
}
})
.catch(error => {
console.error('Yedekleme hatası:', error);
showToast('Yedekleme hatası', 'error');
});
}
// Yedeklemeyi tekrar dene
function retryBackup(projectId) {
if (!confirm('Bu projenin yedeklenmesini tekrar denemek istediğinizden emin misiniz?')) {
return;
}
showToast('Yedekleme tekrar deneniyor...', 'info');
fetch('/retry-backup/', {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
project_id: projectId
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast(data.message, 'success');
setTimeout(() => location.reload(), 2000);
} else {
showToast(data.message || 'Yedekleme tekrar denenemedi', 'error');
}
})
.catch(error => {
console.error('Yedekleme tekrar deneme hatası:', error);
showToast('Yedekleme tekrar deneme hatası', 'error');
});
}
// Log detayını göster
function showLogDetail(logOutput, logDate) {
document.getElementById('logDetailContent').innerHTML = logOutput.replace(/\n/g, '<br>');
document.querySelector('#logDetailModal .modal-title').innerHTML =
'<i class="bi bi-file-text me-2"></i>Yedekleme Detayı - ' + logDate;
const modal = new bootstrap.Modal(document.getElementById('logDetailModal'));
modal.show();
}
// Log kaydını sil
function deleteLog(logId) {
if (!confirm('Bu log kaydını silmek istediğinizden emin misiniz?')) {
return;
}
fetch(`/delete-log/${logId}/`, {
method: 'DELETE',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast('Log kaydı silindi', 'success');
setTimeout(() => location.reload(), 1000);
} else {
showToast(data.message || 'Log kaydı silinemedi', 'error');
}
})
.catch(error => {
console.error('Log silme hatası:', error);
showToast('Log silme hatası', 'error');
});
}
</script>
{% endblock %}