459 lines
17 KiB
HTML
459 lines
17 KiB
HTML
{% extends 'ssh_manager/base.html' %}
|
||
|
||
{% block title %}Dashboard - Hosting Yönetim Paneli{% endblock %}
|
||
|
||
{% block content %}
|
||
<!-- Dashboard Header -->
|
||
<div class="row mb-4">
|
||
<div class="col-12">
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<div>
|
||
<h2 class="mb-1">
|
||
<i class="bi bi-speedometer2 me-2"></i>Dashboard
|
||
</h2>
|
||
<p class="text-muted mb-0">Hosting yönetim sistemi genel görünümü</p>
|
||
</div>
|
||
<div class="text-end">
|
||
<small class="text-muted">Son güncelleme: {{ "now"|date:"d.m.Y H:i" }}</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Dashboard Cards -->
|
||
<div class="row g-4 mb-4">
|
||
<!-- Toplam Projeler -->
|
||
<div class="col-xl-3 col-md-6">
|
||
<div class="card dashboard-card h-100">
|
||
<div class="card-body">
|
||
<div class="d-flex align-items-center">
|
||
<div class="icon-box bg-primary bg-opacity-10">
|
||
<i class="bi bi-folder-fill text-primary"></i>
|
||
</div>
|
||
<div class="ms-3">
|
||
<h3 class="mb-0">{{ projects.count }}</h3>
|
||
<p class="text-muted mb-0">Toplam Proje</p>
|
||
</div>
|
||
</div>
|
||
<div class="mt-3">
|
||
<a href="{% url 'projeler' %}" class="btn btn-sm btn-outline-primary">
|
||
<i class="bi bi-arrow-right"></i> Projeleri Görüntüle
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Aktif Siteler -->
|
||
<div class="col-xl-3 col-md-6">
|
||
<div class="card dashboard-card h-100">
|
||
<div class="card-body">
|
||
<div class="d-flex align-items-center">
|
||
<div class="icon-box bg-success bg-opacity-10">
|
||
<i class="bi bi-globe text-success"></i>
|
||
</div>
|
||
<div class="ms-3">
|
||
<h3 class="mb-0">{{ active_sites_count }}</h3>
|
||
<p class="text-muted mb-0">Aktif Site</p>
|
||
</div>
|
||
</div>
|
||
<div class="mt-3">
|
||
<button class="btn btn-sm btn-outline-success" onclick="checkAllSites()">
|
||
<i class="bi bi-arrow-clockwise"></i> Tümünü Kontrol Et
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Toplam Müşteriler -->
|
||
<div class="col-xl-3 col-md-6">
|
||
<div class="card dashboard-card h-100">
|
||
<div class="card-body">
|
||
<div class="d-flex align-items-center">
|
||
<div class="icon-box bg-info bg-opacity-10">
|
||
<i class="bi bi-people text-info"></i>
|
||
</div>
|
||
<div class="ms-3">
|
||
<h3 class="mb-0">{{ customers.count }}</h3>
|
||
<p class="text-muted mb-0">Toplam Müşteri</p>
|
||
</div>
|
||
</div>
|
||
<div class="mt-3">
|
||
<a href="{% url 'musteriler' %}" class="btn btn-sm btn-outline-info">
|
||
<i class="bi bi-arrow-right"></i> Müşterileri Görüntüle
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Host Durumu -->
|
||
<div class="col-xl-3 col-md-6">
|
||
<div class="card dashboard-card h-100">
|
||
<div class="card-body">
|
||
<div class="d-flex align-items-center">
|
||
<div class="icon-box bg-warning bg-opacity-10">
|
||
<i class="bi bi-server text-warning"></i>
|
||
</div>
|
||
<div class="ms-3">
|
||
<h3 class="mb-0">{{ online_hosts_count }}/{{ ssh_credentials.count }}</h3>
|
||
<p class="text-muted mb-0">Çevrimiçi Host</p>
|
||
</div>
|
||
</div>
|
||
<div class="mt-3 d-flex gap-2">
|
||
<button class="btn btn-sm btn-outline-success" onclick="refreshAllHosts()" title="Tüm host durumlarını kontrol et">
|
||
<i class="bi bi-arrow-clockwise"></i> Kontrol Et
|
||
</button>
|
||
<a href="{% url 'host_yonetimi' %}" class="btn btn-sm btn-outline-warning">
|
||
<i class="bi bi-gear"></i> Yönet
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Charts Row -->
|
||
<div class="row g-4 mb-4">
|
||
<!-- Son İşlemler -->
|
||
<div class="col-lg-8">
|
||
<div class="card h-100">
|
||
<div class="card-header">
|
||
<h5 class="card-title mb-0">
|
||
<i class="bi bi-clock-history me-2"></i>Son İşlemler
|
||
</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
{% if recent_logs %}
|
||
<div class="table-responsive">
|
||
<table class="table table-dark table-sm">
|
||
<thead>
|
||
<tr>
|
||
<th>Tarih</th>
|
||
<th>İşlem</th>
|
||
<th>Proje</th>
|
||
<th>Durum</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for log in recent_logs|slice:":10" %}
|
||
<tr>
|
||
<td>
|
||
<small>{{ log.created_at|date:"d.m H:i" }}</small>
|
||
</td>
|
||
<td>
|
||
<i class="bi {% if log.log_type == 'backup' %}bi-cloud-arrow-up text-warning{% elif log.log_type == 'command' %}bi-terminal text-info{% else %}bi-gear text-secondary{% endif %}"></i>
|
||
{{ log.get_log_type_display }}
|
||
</td>
|
||
<td>
|
||
{% if log.ssh_credential %}
|
||
<small>{{ log.ssh_credential.name }}</small>
|
||
{% else %}
|
||
<small class="text-muted">-</small>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
<span class="badge {% if log.status == 'success' %}bg-success{% else %}bg-danger{% endif %} fs-6">
|
||
{{ log.get_status_display }}
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="text-center mt-3">
|
||
<a href="{% url 'islem_gecmisi' %}" class="btn btn-sm btn-outline-primary">
|
||
<i class="bi bi-list-ul"></i> Tüm İşlem Geçmişi
|
||
</a>
|
||
</div>
|
||
{% else %}
|
||
<div class="text-center py-4">
|
||
<i class="bi bi-clock-history" style="font-size: 2rem; color: #6c757d;"></i>
|
||
<p class="text-muted mt-2">Henüz işlem geçmişi yok</p>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Sistem Durumu -->
|
||
<div class="col-lg-4">
|
||
<div class="card h-100">
|
||
<div class="card-header">
|
||
<h5 class="card-title mb-0">
|
||
<i class="bi bi-activity me-2"></i>Sistem Durumu
|
||
</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
<!-- Host Durumları -->
|
||
<div class="mb-4">
|
||
<h6 class="text-muted mb-3">Host Durumları</h6>
|
||
{% for host in ssh_credentials|slice:":5" %}
|
||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||
<span class="small">{{ host.name }}</span>
|
||
<span class="badge {% if host.connection_status == 'connected' %}bg-success{% elif host.connection_status == 'failed' %}bg-danger{% else %}bg-secondary{% endif %}">
|
||
{% if host.connection_status == 'connected' %}Bağlı{% elif host.connection_status == 'failed' %}Hata{% else %}Bilinmiyor{% endif %}
|
||
</span>
|
||
</div>
|
||
{% empty %}
|
||
<p class="text-muted small">Host tanımlanmamış</p>
|
||
{% endfor %}
|
||
</div>
|
||
|
||
<!-- Disk Kullanımı -->
|
||
<div class="mb-4">
|
||
<h6 class="text-muted mb-3">Disk Kullanımı</h6>
|
||
{% for host in ssh_credentials %}
|
||
{% if host.disk_usage %}
|
||
<div class="mb-3">
|
||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||
<small>{{ host.name }}</small>
|
||
<small>{{ host.disk_usage }}%</small>
|
||
</div>
|
||
<div class="progress" style="height: 6px;">
|
||
<div class="progress-bar {% if host.disk_usage > 80 %}bg-danger{% elif host.disk_usage > 60 %}bg-warning{% else %}bg-success{% endif %}"
|
||
style="width: {{ host.disk_usage }}%"></div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
{% endfor %}
|
||
</div>
|
||
|
||
<!-- Hızlı İşlemler -->
|
||
<div>
|
||
<h6 class="text-muted mb-3">Hızlı İşlemler</h6>
|
||
<div class="d-grid gap-2">
|
||
<button class="btn btn-sm btn-outline-primary" onclick="refreshAllData()">
|
||
<i class="bi bi-arrow-clockwise"></i> Tüm Verileri Yenile
|
||
</button>
|
||
<a href="{% url 'yedeklemeler' %}" class="btn btn-sm btn-outline-warning">
|
||
<i class="bi bi-cloud-arrow-up"></i> Yedekleme Başlat
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Son Yedeklemeler -->
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h5 class="card-title mb-0">
|
||
<i class="bi bi-cloud-arrow-up me-2"></i>Son Yedeklemeler
|
||
</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
{% if recent_backups %}
|
||
<div class="row">
|
||
{% for project in recent_backups|slice:":6" %}
|
||
<div class="col-lg-4 col-md-6 mb-3">
|
||
<div class="backup-item">
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<div>
|
||
<h6 class="mb-1">{{ project.name }}</h6>
|
||
<small class="text-muted">
|
||
{% if project.last_backup %}
|
||
{{ project.last_backup|date:"d.m.Y H:i" }}
|
||
{% else %}
|
||
Yedek alınmamış
|
||
{% endif %}
|
||
</small>
|
||
</div>
|
||
<div>
|
||
{% if project.last_backup %}
|
||
<span class="badge bg-success">Tamam</span>
|
||
{% else %}
|
||
<span class="badge bg-warning">Bekliyor</span>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
<div class="text-center mt-3">
|
||
<a href="{% url 'yedeklemeler' %}" class="btn btn-outline-primary">
|
||
<i class="bi bi-cloud-arrow-up"></i> Tüm Yedeklemeler
|
||
</a>
|
||
</div>
|
||
{% else %}
|
||
<div class="text-center py-4">
|
||
<i class="bi bi-cloud-arrow-up" style="font-size: 2rem; color: #6c757d;"></i>
|
||
<p class="text-muted mt-2">Henüz yedekleme yapılmamış</p>
|
||
<button class="btn btn-primary" onclick="startBackup()">
|
||
<i class="bi bi-cloud-arrow-up"></i> İlk Yedeklemeyi Başlat
|
||
</button>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
.dashboard-card {
|
||
border: none;
|
||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.5);
|
||
transition: transform 0.2s ease-in-out;
|
||
}
|
||
|
||
.dashboard-card:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.7);
|
||
}
|
||
|
||
.icon-box {
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.icon-box i {
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.backup-item {
|
||
padding: 1rem;
|
||
background: rgba(255, 255, 255, 0.05);
|
||
border-radius: 8px;
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.card {
|
||
background: #23272b;
|
||
border: 1px solid #444;
|
||
}
|
||
|
||
.card-header {
|
||
background: #1a1d23;
|
||
border-bottom: 1px solid #444;
|
||
}
|
||
|
||
.progress {
|
||
background-color: rgba(255, 255, 255, 0.1);
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
// Tüm host durumlarını kontrol et
|
||
function refreshAllHosts() {
|
||
showToast('Host durumları kontrol ediliyor...', 'info');
|
||
|
||
fetch('/refresh-all-hosts/', {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCookie('csrftoken'),
|
||
'Content-Type': 'application/json'
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast('Host durumları başarıyla güncellendi', 'success');
|
||
setTimeout(() => location.reload(), 1000);
|
||
} else {
|
||
showToast('Host kontrol hatası: ' + (data.message || 'Bilinmeyen hata'), 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Host kontrol hatası:', error);
|
||
showToast('Host kontrol hatası', 'error');
|
||
});
|
||
}
|
||
|
||
// Tüm siteleri kontrol et
|
||
function checkAllSites() {
|
||
showToast('Tüm siteler ve disk kullanımları kontrol ediliyor...', 'info');
|
||
|
||
fetch('/check-all-sites/', {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCookie('csrftoken'),
|
||
'Content-Type': 'application/json'
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
// Sonuçları göster
|
||
const activeCount = data.results.filter(r => r.status).length;
|
||
const totalCount = data.results.length;
|
||
|
||
// Özet mesaj oluştur
|
||
const summaryMessage = `
|
||
<div>
|
||
<div><strong>Site Kontrolü Tamamlandı</strong></div>
|
||
<div class="mt-2">${activeCount}/${totalCount} site aktif</div>
|
||
<div class="small text-muted mt-1">Disk kullanımları güncellendi</div>
|
||
</div>
|
||
`;
|
||
|
||
showToast(summaryMessage, 'success', 5000);
|
||
setTimeout(() => location.reload(), 2000);
|
||
} else {
|
||
showToast(data.message, 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
showToast('Site ve disk kontrolü hatası', 'error');
|
||
});
|
||
}
|
||
|
||
// Tüm verileri yenile
|
||
function refreshAllData() {
|
||
showToast('Veriler yenileniyor...', 'info');
|
||
|
||
fetch('/refresh-all-hosts/', {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCookie('csrftoken'),
|
||
'Content-Type': 'application/json'
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast('Veriler başarıyla yenilendi', 'success');
|
||
setTimeout(() => location.reload(), 1000);
|
||
} else {
|
||
showToast('Veri yenileme hatası', 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
showToast('Veri yenileme hatası', 'error');
|
||
});
|
||
}
|
||
|
||
// Yedekleme başlat
|
||
function startBackup() {
|
||
showToast('Yedekleme işlemi başlatılıyor...', 'info');
|
||
// Yedekleme sayfasına yönlendir
|
||
window.location.href = '{% url "yedeklemeler" %}';
|
||
}
|
||
|
||
// Sayfa yüklendiğinde otomatik host kontrol (opsiyonel)
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Son host kontrolünden 10 dakika geçtiyse otomatik kontrol et
|
||
const lastCheck = localStorage.getItem('lastHostCheck');
|
||
const now = new Date().getTime();
|
||
|
||
if (!lastCheck || (now - parseInt(lastCheck)) > 10 * 60 * 1000) { // 10 dakika
|
||
// Sessizce host durumlarını kontrol et (sadece ilk açılışta)
|
||
setTimeout(() => {
|
||
console.log('Otomatik host kontrolü başlatılıyor...');
|
||
refreshAllHosts();
|
||
localStorage.setItem('lastHostCheck', now.toString());
|
||
}, 2000); // 2 saniye sonra başlat
|
||
}
|
||
});
|
||
</script>
|
||
{% endblock %}
|