yeni
This commit is contained in:
506
templates/ssh_manager/base.html
Normal file
506
templates/ssh_manager/base.html
Normal file
@ -0,0 +1,506 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="tr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}Hosting Yönetim Paneli{% endblock %}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
|
||||
<style>
|
||||
body {
|
||||
background: #181a1b;
|
||||
color: #f0f0f0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
/* Sidebar Styles */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 280px;
|
||||
height: 100vh;
|
||||
background: linear-gradient(180deg, #1a1d23 0%, #23272b 100%);
|
||||
border-right: 1px solid #333;
|
||||
z-index: 1000;
|
||||
box-shadow: 2px 0 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem 1.5rem;
|
||||
border-bottom: 1px solid #333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar-logo {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
color: #4fc3f7;
|
||||
margin-bottom: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.sidebar-subtitle {
|
||||
font-size: 0.9rem;
|
||||
color: #c0c0c0;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
color: #e8e8e8;
|
||||
text-decoration: none;
|
||||
border-left: 3px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: rgba(79, 195, 247, 0.1);
|
||||
color: #ffffff;
|
||||
border-left-color: #4fc3f7;
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: rgba(79, 195, 247, 0.2);
|
||||
color: #ffffff;
|
||||
border-left-color: #4fc3f7;
|
||||
}
|
||||
|
||||
.nav-item i {
|
||||
width: 20px;
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
/* Dropdown menu styles */
|
||||
.nav-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 100%;
|
||||
width: 100%;
|
||||
background: #1a1d23;
|
||||
border-left: 3px solid #4fc3f7;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.nav-dropdown.active .nav-dropdown-content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav-dropdown-item {
|
||||
display: block;
|
||||
padding: 0.5rem 1.5rem 0.5rem 3rem;
|
||||
color: #e0e0e0;
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.3s ease;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.nav-dropdown-item:hover {
|
||||
background: rgba(79, 195, 247, 0.1);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.nav-dropdown-item.active {
|
||||
background: rgba(79, 195, 247, 0.2);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.nav-dropdown-toggle::after {
|
||||
content: '\F282';
|
||||
font-family: 'bootstrap-icons';
|
||||
float: right;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-dropdown.active .nav-dropdown-toggle::after {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.table-dark th, .table-dark td { color: #f0f0f0; }
|
||||
.table-dark { background: #23272b; }
|
||||
.modal-content { background: #23272b; color: #f0f0f0; }
|
||||
.form-control, .form-select { background: #181a1b; color: #f0f0f0; border: 1px solid #444; }
|
||||
.form-label { color: #f0f0f0; }
|
||||
.actions { min-width: 140px; }
|
||||
.btn { padding: 0.25rem 0.5rem; }
|
||||
.btn i { font-size: 1.1rem; }
|
||||
.action-icon {
|
||||
color: #8c8c8c;
|
||||
font-size: 1.2rem;
|
||||
margin: 0 0.3rem;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
.action-icon:hover { color: #4fc3f7; }
|
||||
.action-icon.edit:hover { color: #17a2b8; }
|
||||
.action-icon.delete:hover { color: #dc3545; }
|
||||
.action-icon.backup:hover { color: #ffc107; }
|
||||
.action-icon.logs:hover { color: #6c757d; }
|
||||
.action-icon.site:hover { color: #28a745; }
|
||||
.action-icon.meta:hover { color: #007bff; }
|
||||
.table-striped > tbody > tr:nth-of-type(odd) { background-color: #23272b; }
|
||||
.table-striped > tbody > tr:nth-of-type(even) { background-color: #181a1b; }
|
||||
.badge.bg-success, .badge.bg-danger { font-size: 0.9em; }
|
||||
.form-control.editing:focus { background:rgb(108, 106, 106) !important; color:rgb(176, 181, 185) !important; border-color: #bdbdbd !important; }
|
||||
#projectSearch::placeholder { color: #bbb; }
|
||||
#mainToast .toast-body { color: #ffffff; font-weight: bold; }
|
||||
|
||||
.log-row {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.log-row:hover {
|
||||
background-color: rgba(79, 195, 247, 0.1) !important;
|
||||
}
|
||||
|
||||
.log-status-success {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.log-status-error {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.log-type-backup {
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
.log-type-command {
|
||||
color: #17a2b8;
|
||||
}
|
||||
|
||||
/* Main content text improvements */
|
||||
.main-content h1, .main-content h2, .main-content h3, .main-content h4, .main-content h5, .main-content h6 {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.main-content p, .main-content span, .main-content div {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.main-content .text-muted {
|
||||
color: #b0b0b0 !important;
|
||||
}
|
||||
|
||||
/* Form controls and inputs */
|
||||
.form-control::placeholder {
|
||||
color: #bbb !important;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
background-color: #2a2d31 !important;
|
||||
color: #ffffff !important;
|
||||
border-color: #4fc3f7 !important;
|
||||
box-shadow: 0 0 0 0.2rem rgba(79, 195, 247, 0.25) !important;
|
||||
}
|
||||
|
||||
/* Button text improvements */
|
||||
.btn-primary {
|
||||
background-color: #4fc3f7;
|
||||
border-color: #4fc3f7;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: #6c757d;
|
||||
border-color: #6c757d;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* Table improvements */
|
||||
.table th {
|
||||
color: #ffffff !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.table td {
|
||||
color: #f0f0f0 !important;
|
||||
}
|
||||
|
||||
/* Modal improvements */
|
||||
.modal-header {
|
||||
border-bottom: 1px solid #444;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-top: 1px solid #444;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Toast improvements */
|
||||
#mainToastBody {
|
||||
color: #ffffff !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Customer Form Improvements */
|
||||
.modal-lg {
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.modal-body h6 {
|
||||
color: #4fc3f7;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid rgba(79, 195, 247, 0.3);
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-check-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-check-input:checked {
|
||||
background-color: #4fc3f7;
|
||||
border-color: #4fc3f7;
|
||||
}
|
||||
|
||||
.row.g-3 {
|
||||
--bs-gutter-x: 1.5rem;
|
||||
--bs-gutter-y: 1rem;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #4fc3f7;
|
||||
box-shadow: 0 0 0 0.2rem rgba(79, 195, 247, 0.25);
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #b0b0b0 !important;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
position: relative;
|
||||
}
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Sidebar -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<div class="sidebar-logo">
|
||||
<i class="bi bi-server"></i>
|
||||
HostPanel
|
||||
</div>
|
||||
<div class="sidebar-subtitle">Hosting Yönetim Sistemi</div>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav">
|
||||
<a href="{% url 'dashboard' %}" class="nav-item {% if request.resolver_match.url_name == 'project_list' or request.resolver_match.url_name == 'dashboard' %}active{% endif %}">
|
||||
<i class="bi bi-speedometer2"></i>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="{% url 'musteriler' %}" class="nav-item {% if request.resolver_match.url_name == 'musteriler' %}active{% endif %}">
|
||||
<i class="bi bi-people"></i>
|
||||
Müşteriler
|
||||
</a>
|
||||
<a href="{% url 'host_yonetimi' %}" class="nav-item {% if request.resolver_match.url_name == 'host_yonetimi' %}active{% endif %}">
|
||||
<i class="bi bi-hdd-network"></i>
|
||||
Host Yönetimi
|
||||
</a>
|
||||
<a href="{% url 'projeler' %}" class="nav-item {% if request.resolver_match.url_name == 'projeler' %}active{% endif %}">
|
||||
<i class="bi bi-folder-fill"></i>
|
||||
Projeler
|
||||
</a>
|
||||
<a href="{% url 'yedeklemeler' %}" class="nav-item {% if request.resolver_match.url_name == 'yedeklemeler' %}active{% endif %}">
|
||||
<i class="bi bi-cloud-arrow-up"></i>
|
||||
Yedeklemeler
|
||||
</a>
|
||||
<a href="{% url 'islem_gecmisi' %}" class="nav-item {% if request.resolver_match.url_name == 'islem_gecmisi' %}active{% endif %}">
|
||||
<i class="bi bi-clock-history"></i>
|
||||
İşlem Geçmişi
|
||||
</a>
|
||||
<a href="{% url 'ayarlar' %}" class="nav-item {% if request.resolver_match.url_name == 'ayarlar' %}active{% endif %}">
|
||||
<i class="bi bi-gear"></i>
|
||||
Ayarlar
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Toast Container -->
|
||||
<div class="position-fixed top-0 end-0 p-3" style="z-index: 1100">
|
||||
<div id="mainToast" class="toast align-items-center text-bg-dark border-0" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="4000">
|
||||
<div class="d-flex">
|
||||
<div class="toast-body" id="mainToastBody">
|
||||
Mesaj
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Kapat"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="progressToast" class="toast align-items-center text-bg-info border-0" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="false">
|
||||
<div class="toast-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span id="progressMessage">İşlem devam ediyor...</span>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Kapat"></button>
|
||||
</div>
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" id="progressBar" role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
<small class="text-muted" id="progressDetail">Başlatılıyor...</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<h1 class="mb-4" style="color: #ffffff;">{% block page_title %}{{ page_title|default:"Dashboard" }}{% endblock %}</h1>
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
function showToast(message, type = 'info') {
|
||||
const toast = document.getElementById('mainToast');
|
||||
const toastBody = document.getElementById('mainToastBody');
|
||||
|
||||
toastBody.textContent = message;
|
||||
|
||||
// Remove existing classes
|
||||
toast.classList.remove('text-bg-dark', 'text-bg-success', 'text-bg-danger', 'text-bg-warning', 'text-bg-info');
|
||||
|
||||
// Add appropriate class based on type
|
||||
switch(type) {
|
||||
case 'success':
|
||||
toast.classList.add('text-bg-success');
|
||||
break;
|
||||
case 'error':
|
||||
toast.classList.add('text-bg-danger');
|
||||
break;
|
||||
case 'warning':
|
||||
toast.classList.add('text-bg-warning');
|
||||
break;
|
||||
default:
|
||||
toast.classList.add('text-bg-info');
|
||||
}
|
||||
|
||||
const bsToast = new bootstrap.Toast(toast);
|
||||
bsToast.show();
|
||||
}
|
||||
|
||||
// Dropdown toggle fonksiyonu
|
||||
function toggleDropdown(element) {
|
||||
const dropdown = element.closest('.nav-dropdown');
|
||||
const allDropdowns = document.querySelectorAll('.nav-dropdown');
|
||||
|
||||
// Diğer dropdown'ları kapat
|
||||
allDropdowns.forEach(dd => {
|
||||
if (dd !== dropdown) {
|
||||
dd.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Bu dropdown'ı aç/kapat
|
||||
dropdown.classList.toggle('active');
|
||||
}
|
||||
|
||||
// Yeni müşteri modal'ını aç
|
||||
function openNewCustomerModal(type) {
|
||||
// Eğer müşteri sayfasındaysak modal'ı doğrudan aç
|
||||
if (window.location.pathname.includes('/musteriler/')) {
|
||||
if (typeof resetCustomerForm === 'function') {
|
||||
resetCustomerForm();
|
||||
if (type === 'corporate') {
|
||||
document.getElementById('typeCorporate').checked = true;
|
||||
document.getElementById('corporateFields').style.display = 'block';
|
||||
document.getElementById('individualFields').style.display = 'none';
|
||||
document.getElementById('surname').required = false;
|
||||
document.getElementById('company_name').required = true;
|
||||
} else {
|
||||
document.getElementById('typeIndividual').checked = true;
|
||||
document.getElementById('individualFields').style.display = 'block';
|
||||
document.getElementById('corporateFields').style.display = 'none';
|
||||
document.getElementById('surname').required = true;
|
||||
document.getElementById('company_name').required = false;
|
||||
}
|
||||
const modal = new bootstrap.Modal(document.getElementById('customerModal'));
|
||||
modal.show();
|
||||
}
|
||||
} else {
|
||||
// Müşteri sayfasına yönlendir
|
||||
window.location.href = '/musteriler/?new=' + type;
|
||||
}
|
||||
}
|
||||
|
||||
// Sayfa dışına tıklanırsa dropdown'ları kapat
|
||||
document.addEventListener('click', function(event) {
|
||||
if (!event.target.closest('.nav-dropdown')) {
|
||||
document.querySelectorAll('.nav-dropdown').forEach(dd => {
|
||||
dd.classList.remove('active');
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
445
templates/ssh_manager/dashboard.html
Normal file
445
templates/ssh_manager/dashboard.html
Normal file
@ -0,0 +1,445 @@
|
||||
{% 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 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) {
|
||||
showToast(data.message, 'success');
|
||||
setTimeout(() => location.reload(), 2000);
|
||||
} else {
|
||||
showToast(data.message, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('Site 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 %}
|
||||
359
templates/ssh_manager/host_yonetimi.html
Normal file
359
templates/ssh_manager/host_yonetimi.html
Normal file
@ -0,0 +1,359 @@
|
||||
{% extends 'ssh_manager/base.html' %}
|
||||
|
||||
{% block title %}Host Yönetimi - Hosting Yönetim Paneli{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h3 class="mb-1">Host Yönetimi</h3>
|
||||
<small class="text-muted">SSH bağlantı bilgileri ve sunucu durumları</small>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-success" onclick="refreshAllHosts()">
|
||||
<i class="bi bi-arrow-clockwise"></i> Tümünü Yenile
|
||||
</button>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#hostModal" onclick="resetHostForm()">
|
||||
<i class="bi bi-plus-circle"></i> Yeni Host
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Host Tablosu -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Host Adı</th>
|
||||
<th>IP/Domain</th>
|
||||
<th>Port</th>
|
||||
<th>Kullanıcı</th>
|
||||
<th>Durum</th>
|
||||
<th>Disk Kullanımı</th>
|
||||
<th>Son Kontrol</th>
|
||||
<th class="actions">İşlemler</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for host in ssh_credentials %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ host.name }}</strong>
|
||||
{% if host.is_default %}
|
||||
<span class="badge bg-info ms-1">Varsayılan</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ host.hostname }}</td>
|
||||
<td>{{ host.port }}</td>
|
||||
<td>{{ host.username }}</td>
|
||||
<td>
|
||||
<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' %}
|
||||
<i class="bi bi-check-circle"></i> Bağlı
|
||||
{% elif host.connection_status == 'failed' %}
|
||||
<i class="bi bi-x-circle"></i> Hata
|
||||
{% else %}
|
||||
<i class="bi bi-question-circle"></i> Bilinmiyor
|
||||
{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
{% if host.disk_usage %}
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="progress me-2" style="width: 80px; height: 8px;">
|
||||
<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>
|
||||
<small>{{ host.disk_usage }}%</small>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted">-</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if host.last_checked %}
|
||||
<small class="text-muted">{{ host.last_checked|date:"d.m.Y H:i" }}</small>
|
||||
{% else %}
|
||||
<span class="text-muted">Hiçbir zaman</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="actions">
|
||||
<i class="action-icon bi bi-arrow-clockwise" title="Bağlantı Testi" onclick="testConnection({{ host.id }})"></i>
|
||||
<i class="action-icon edit bi bi-pencil" title="Düzenle" onclick="editHost({{ host.id }})"></i>
|
||||
<i class="action-icon delete bi bi-trash" title="Sil" onclick="deleteHost({{ host.id }})"></i>
|
||||
<i class="action-icon logs bi bi-list-ul" title="Loglar" onclick="viewHostLogs({{ host.id }})"></i>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="8" class="text-center text-muted py-4">
|
||||
<i class="bi bi-server" style="font-size: 2rem;"></i>
|
||||
<div class="mt-2">Henüz host tanımlanmamış</div>
|
||||
<button class="btn btn-sm btn-outline-primary mt-2" data-bs-toggle="modal" data-bs-target="#hostModal" onclick="resetHostForm()">
|
||||
İlk host'u ekle
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Host Ekleme/Düzenleme Modal -->
|
||||
<div class="modal fade" id="hostModal" tabindex="-1" aria-labelledby="hostModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<form id="hostForm">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="hostModalLabel">Yeni Host Ekle</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="hostId" name="hostId">
|
||||
|
||||
<!-- Bağlantı Bilgileri -->
|
||||
<div class="mb-4">
|
||||
<h6 class="text-muted mb-3">
|
||||
<i class="bi bi-server me-1"></i>Bağlantı Bilgileri
|
||||
</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="hostName" class="form-label">Host Adı *</label>
|
||||
<input type="text" class="form-control" id="hostName" name="name" required placeholder="Sunucu adı">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="hostname" class="form-label">IP/Domain *</label>
|
||||
<input type="text" class="form-control" id="hostname" name="hostname" required placeholder="192.168.1.100 veya example.com">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="port" class="form-label">Port</label>
|
||||
<input type="number" class="form-control" id="port" name="port" value="22" min="1" max="65535">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="username" class="form-label">Kullanıcı Adı *</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required placeholder="root">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kimlik Doğrulama -->
|
||||
<div class="mb-4">
|
||||
<h6 class="text-muted mb-3">
|
||||
<i class="bi bi-key me-1"></i>Kimlik Doğrulama
|
||||
</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label for="password" class="form-label">Şifre</label>
|
||||
<input type="password" class="form-control" id="password" name="password" placeholder="SSH şifresi">
|
||||
<small class="text-muted">Güvenlik için şifre şifrelenmiş olarak saklanır</small>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="basePath" class="form-label">Temel Dizin</label>
|
||||
<input type="text" class="form-control" id="basePath" name="base_path" placeholder="/var/www" value="/var/www">
|
||||
<small class="text-muted">Projelerin bulunduğu ana dizin</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ayarlar -->
|
||||
<div class="mb-3">
|
||||
<h6 class="text-muted mb-3">
|
||||
<i class="bi bi-gear me-1"></i>Ayarlar
|
||||
</h6>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="isDefault" name="is_default">
|
||||
<label class="form-check-label" for="isDefault">
|
||||
Varsayılan host olarak ayarla
|
||||
</label>
|
||||
<small class="form-text text-muted d-block">Yeni projeler için otomatik olarak bu host kullanılacak</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
|
||||
<button type="button" class="btn btn-info me-2" onclick="testHostConnection()">
|
||||
<i class="bi bi-wifi"></i> Bağlantı Testi
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">Kaydet</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Host bağlantı testi
|
||||
function testConnection(hostId) {
|
||||
showToast('Bağlantı test ediliyor...', 'info');
|
||||
|
||||
fetch(`/test-host-connection/${hostId}/`, {
|
||||
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(), 1000);
|
||||
} else {
|
||||
showToast(data.message, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('Bağlantı testi sırasında hata oluştu', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Tüm hostları yenile
|
||||
function refreshAllHosts() {
|
||||
showToast('Tüm hostlar 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(data.message, 'success');
|
||||
setTimeout(() => location.reload(), 2000);
|
||||
} else {
|
||||
showToast(data.message, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('Host yenileme sırasında hata oluştu', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Host formu sıfırla
|
||||
function resetHostForm() {
|
||||
document.getElementById('hostForm').reset();
|
||||
document.getElementById('hostId').value = '';
|
||||
document.getElementById('hostModalLabel').textContent = 'Yeni Host Ekle';
|
||||
document.getElementById('port').value = '22';
|
||||
document.getElementById('basePath').value = '/var/www';
|
||||
}
|
||||
|
||||
// Host düzenle
|
||||
function editHost(hostId) {
|
||||
fetch(`/get-host-details/${hostId}/`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const host = data.host;
|
||||
|
||||
document.getElementById('hostId').value = host.id;
|
||||
document.getElementById('hostName').value = host.name;
|
||||
document.getElementById('hostname').value = host.hostname;
|
||||
document.getElementById('port').value = host.port;
|
||||
document.getElementById('username').value = host.username;
|
||||
document.getElementById('basePath').value = host.base_path || '/var/www';
|
||||
document.getElementById('isDefault').checked = host.is_default;
|
||||
|
||||
document.getElementById('hostModalLabel').textContent = 'Host Düzenle';
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('hostModal'));
|
||||
modal.show();
|
||||
} else {
|
||||
showToast('Host bilgileri alınamadı', 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('Host bilgileri alınırken hata oluştu', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Host sil
|
||||
function deleteHost(hostId) {
|
||||
if (confirm('Bu host\'u silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.')) {
|
||||
fetch(`/delete-host/${hostId}/`, {
|
||||
method: 'DELETE',
|
||||
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(), 1000);
|
||||
} else {
|
||||
showToast(data.message, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('Host silme sırasında hata oluştu', 'error');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Host logları görüntüle
|
||||
function viewHostLogs(hostId) {
|
||||
// Host spesifik logları göstermek için logs sayfasına yönlendir
|
||||
window.location.href = `/islem-gecmisi/?host=${hostId}`;
|
||||
}
|
||||
|
||||
// Modal içinde bağlantı testi
|
||||
function testHostConnection() {
|
||||
const formData = new FormData(document.getElementById('hostForm'));
|
||||
|
||||
showToast('Bağlantı test ediliyor...', 'info');
|
||||
|
||||
fetch('/test-host-connection-form/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast(data.message, 'success');
|
||||
} else {
|
||||
showToast(data.message, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('Bağlantı testi sırasında hata oluştu', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Host form submit
|
||||
document.getElementById('hostForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(this);
|
||||
const hostId = document.getElementById('hostId').value;
|
||||
const url = hostId ? `/update-host/${hostId}/` : '/create-host/';
|
||||
|
||||
fetch(url, {
|
||||
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('hostModal')).hide();
|
||||
setTimeout(() => location.reload(), 1000);
|
||||
} else {
|
||||
showToast(data.message, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('Host kaydederken hata oluştu', 'error');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
140
templates/ssh_manager/islem_gecmisi.html
Normal file
140
templates/ssh_manager/islem_gecmisi.html
Normal file
@ -0,0 +1,140 @@
|
||||
{% extends 'ssh_manager/base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
.log-table tbody td {
|
||||
font-size: 0.85rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
.log-table .log-type-backup {
|
||||
color: #ffc107;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.log-table .log-type-command {
|
||||
color: #17a2b8;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
</style>
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h3>İşlem Geçmişi</h3>
|
||||
<small class="text-muted">Tüm sistem işlemleri ve logları</small>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<input type="text" id="logSearch" class="form-control" style="max-width: 250px;" placeholder="Proje adına göre ara...">
|
||||
<button class="btn btn-danger btn-sm" onclick="clearAllLogs()" title="Tüm Logları Temizle">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-bordered table-striped log-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40px;">#</th>
|
||||
<th style="width: 130px;">Tarih</th>
|
||||
<th style="width: 120px;">Tip</th>
|
||||
<th style="width: 150px;">Proje</th>
|
||||
<th>İşlem</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for log in logs %}
|
||||
<tr class="log-row" data-project="{{ log.command|lower }}">
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{ log.created_at|date:"d.m.Y H:i" }}</td>
|
||||
<td>
|
||||
{% if log.log_type == 'backup' %}
|
||||
<span class="log-type-backup">💾 Yedekleme</span>
|
||||
{% else %}
|
||||
<span class="log-type-command">⚙️ Komut</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if 'Proje:' in log.output %}
|
||||
{% with project_name=log.output|cut:'Proje: ' %}
|
||||
{{ project_name|truncatechars:25 }}
|
||||
{% endwith %}
|
||||
{% elif 'Proje:' in log.command %}
|
||||
{% if ')' in log.command %}
|
||||
{% with project_part=log.command|cut:'(Proje: '|cut:')' %}
|
||||
{{ project_part|truncatechars:25 }}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
{% with project_part=log.command|cut:'Proje: ' %}
|
||||
{{ project_part|truncatechars:25 }}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% elif log.log_type == 'backup' and 'Backup:' in log.command %}
|
||||
{% with folder_name=log.command|cut:'Backup: ' %}
|
||||
{{ folder_name|truncatechars:25 }}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
Sistem
|
||||
{% endif %}
|
||||
</td>
|
||||
<td title="{{ log.output }}">{{ log.command|default:log.output|truncatechars:100 }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted">
|
||||
<i class="bi bi-inbox" style="font-size: 3rem; opacity: 0.3;"></i>
|
||||
<p class="mt-2">Henüz işlem geçmişi bulunmuyor</p>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Log arama işlevselliği
|
||||
document.getElementById('logSearch').addEventListener('keyup', function() {
|
||||
const searchTerm = this.value.toLowerCase().trim();
|
||||
const logRows = document.querySelectorAll('.log-row');
|
||||
|
||||
logRows.forEach(row => {
|
||||
const projectName = row.getAttribute('data-project') || '';
|
||||
const command = row.cells[4].textContent.toLowerCase(); // İşlem kolonu artık 4. indekste
|
||||
|
||||
if (searchTerm === '' ||
|
||||
projectName.includes(searchTerm) ||
|
||||
command.includes(searchTerm)) {
|
||||
row.style.display = '';
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Tüm logları temizle
|
||||
function clearAllLogs() {
|
||||
if (!confirm('Tüm işlem geçmişini silmek istediğinizden emin misiniz?\nBu işlem geri alınamaz.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/logs/clear/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken'),
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast(`✅ ${data.deleted_count} log kaydı silindi`, 'success');
|
||||
setTimeout(() => location.reload(), 1000);
|
||||
} else {
|
||||
showToast(`❌ ${data.message || 'Log silme işlemi başarısız!'}`, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showToast('❌ Log silme sırasında bir hata oluştu!', 'error');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
467
templates/ssh_manager/musteriler.html
Normal file
467
templates/ssh_manager/musteriler.html
Normal file
@ -0,0 +1,467 @@
|
||||
{% extends 'ssh_manager/base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
.customer-card {
|
||||
background: #23272b;
|
||||
border: 1px solid #333;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.customer-card:hover {
|
||||
border-color: #4fc3f7;
|
||||
box-shadow: 0 2px 8px rgba(79, 195, 247, 0.1);
|
||||
}
|
||||
.customer-type-badge {
|
||||
font-size: 0.8rem;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.customer-projects {
|
||||
font-size: 0.9rem;
|
||||
color: #bbb;
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h3>Müşteri Yönetimi
|
||||
{% if filter_type == 'individual' %}
|
||||
<small class="text-muted">- Bireysel Müşteriler</small>
|
||||
{% elif filter_type == 'corporate' %}
|
||||
<small class="text-muted">- Kurumsal Müşteriler</small>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<small class="text-muted">Bireysel ve kurumsal müşteri profilleri</small>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<input type="text" id="customerSearch" class="form-control" style="max-width: 250px;" placeholder="Müşteri ara...">
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#customerModal" onclick="resetCustomerForm()">
|
||||
<i class="bi bi-plus-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
{% for customer in customers %}
|
||||
<div class="col-md-6 col-lg-4 customer-item" data-customer="{{ customer.get_display_name|lower }}">
|
||||
<div class="customer-card">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div>
|
||||
<h5 class="mb-1">{{ customer.get_display_name }}</h5>
|
||||
<span class="customer-type-badge badge {% if customer.customer_type == 'corporate' %}bg-info{% else %}bg-success{% endif %}">
|
||||
{% if customer.customer_type == 'corporate' %}Kurumsal{% else %}Bireysel{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-sm btn-outline-info" onclick="editCustomer({{ customer.id }})" title="Düzenle">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="deleteCustomer({{ customer.id }})" title="Sil">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="customer-info">
|
||||
{% if customer.email %}
|
||||
<div class="mb-1">
|
||||
<i class="bi bi-envelope me-1"></i>
|
||||
<small>{{ customer.email }}</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if customer.phone %}
|
||||
<div class="mb-1">
|
||||
<i class="bi bi-telephone me-1"></i>
|
||||
<small>{{ customer.phone }}</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if customer.customer_type == 'corporate' and customer.tax_number %}
|
||||
<div class="mb-1">
|
||||
<i class="bi bi-building me-1"></i>
|
||||
<small>Vergi No: {{ customer.tax_number }}</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="customer-projects mt-3">
|
||||
<i class="bi bi-folder me-1"></i>
|
||||
<small>{{ customer.project_set.count }} proje</small>
|
||||
{% if customer.notes %}
|
||||
<div class="mt-2">
|
||||
<i class="bi bi-chat-text me-1"></i>
|
||||
<small class="text-muted">{{ customer.notes|truncatechars:50 }}</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="col-12">
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="bi bi-people" style="font-size: 3rem; opacity: 0.3;"></i>
|
||||
<p class="mt-2">Henüz müşteri kaydı bulunmuyor</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Müşteri Modal -->
|
||||
<div class="modal fade" id="customerModal" tabindex="-1" aria-labelledby="customerModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<form id="customerForm">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="customerModalLabel">Yeni Müşteri Ekle</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="customerId" name="customerId">
|
||||
|
||||
<!-- Müşteri Tipi Seçimi -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-bold">Müşteri Tipi</label>
|
||||
<div class="d-flex gap-4 mt-2">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="customer_type" id="typeIndividual" value="individual" checked>
|
||||
<label class="form-check-label" for="typeIndividual">
|
||||
<i class="bi bi-person me-1"></i>Bireysel
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="customer_type" id="typeCorporate" value="corporate">
|
||||
<label class="form-check-label" for="typeCorporate">
|
||||
<i class="bi bi-building me-1"></i>Kurumsal
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bireysel Müşteri Alanları -->
|
||||
<div id="individualFields">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="name" class="form-label">Ad *</label>
|
||||
<input type="text" class="form-control" id="name" name="name">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="surname" class="form-label">Soyad *</label>
|
||||
<input type="text" class="form-control" id="surname" name="surname">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="tc_number" class="form-label">TC Kimlik No</label>
|
||||
<input type="text" class="form-control" id="tc_number" name="tc_number" maxlength="11" placeholder="11 haneli TC kimlik numarası">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="birth_date" class="form-label">Doğum Tarihi</label>
|
||||
<input type="date" class="form-control" id="birth_date" name="birth_date">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kurumsal Müşteri Alanları -->
|
||||
<div id="corporateFields" style="display: none;">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="company_name" class="form-label">Şirket Adı *</label>
|
||||
<input type="text" class="form-control" id="company_name" name="company_name">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="authorized_person" class="form-label">Yetkili Kişi</label>
|
||||
<input type="text" class="form-control" id="authorized_person" name="authorized_person">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="tax_number" class="form-label">Vergi No</label>
|
||||
<input type="text" class="form-control" id="tax_number" name="tax_number" placeholder="10 haneli vergi numarası">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="tax_office" class="form-label">Vergi Dairesi</label>
|
||||
<input type="text" class="form-control" id="tax_office" name="tax_office">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- İletişim Bilgileri -->
|
||||
<div class="mt-4">
|
||||
<h6 class="text-muted mb-3">
|
||||
<i class="bi bi-telephone me-1"></i>İletişim Bilgileri
|
||||
</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="email" class="form-label">E-posta *</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required placeholder="ornek@email.com">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="phone" class="form-label">Telefon</label>
|
||||
<input type="tel" class="form-control" id="phone" name="phone" placeholder="0555 123 45 67">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="address" class="form-label">Adres</label>
|
||||
<textarea class="form-control" id="address" name="address" rows="2" placeholder="Tam adres bilgisi..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ek Bilgiler -->
|
||||
<div class="mt-4">
|
||||
<h6 class="text-muted mb-3">
|
||||
<i class="bi bi-chat-text me-1"></i>Ek Bilgiler
|
||||
</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label for="notes" class="form-label">Notlar</label>
|
||||
<textarea class="form-control" id="notes" name="notes" rows="3" placeholder="Müşteri hakkında notlar, özel istekler vb."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
|
||||
<button type="submit" class="btn btn-primary">Kaydet</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Müşteri tipi değiştiğinde alanları göster/gizle
|
||||
document.querySelectorAll('input[name="customer_type"]').forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
const individualFields = document.getElementById('individualFields');
|
||||
const corporateFields = document.getElementById('corporateFields');
|
||||
|
||||
if (this.value === 'individual') {
|
||||
individualFields.style.display = 'block';
|
||||
corporateFields.style.display = 'none';
|
||||
document.getElementById('name').required = true;
|
||||
document.getElementById('surname').required = true;
|
||||
document.getElementById('company_name').required = false;
|
||||
} else {
|
||||
individualFields.style.display = 'none';
|
||||
corporateFields.style.display = 'block';
|
||||
document.getElementById('name').required = false;
|
||||
document.getElementById('surname').required = false;
|
||||
document.getElementById('company_name').required = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Müşteri arama
|
||||
document.getElementById('customerSearch').addEventListener('keyup', function() {
|
||||
const searchTerm = this.value.toLowerCase().trim();
|
||||
const customerItems = document.querySelectorAll('.customer-item');
|
||||
|
||||
customerItems.forEach(item => {
|
||||
const customerName = item.getAttribute('data-customer') || '';
|
||||
if (searchTerm === '' || customerName.includes(searchTerm)) {
|
||||
item.style.display = '';
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Form reset
|
||||
function resetCustomerForm() {
|
||||
document.getElementById('customerForm').reset();
|
||||
document.getElementById('customerId').value = '';
|
||||
document.getElementById('customerModalLabel').textContent = 'Yeni Müşteri Ekle';
|
||||
document.getElementById('typeIndividual').checked = true;
|
||||
document.getElementById('individualFields').style.display = 'block';
|
||||
document.getElementById('corporateFields').style.display = 'none';
|
||||
document.getElementById('name').required = true;
|
||||
document.getElementById('surname').required = true;
|
||||
document.getElementById('company_name').required = false;
|
||||
}
|
||||
|
||||
// Müşteri düzenle
|
||||
function editCustomer(id) {
|
||||
fetch(`/get-customer-details/${id}/`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const customer = data.customer;
|
||||
|
||||
document.getElementById('customerId').value = id;
|
||||
document.getElementById('customerModalLabel').textContent = 'Müşteri Düzenle';
|
||||
|
||||
// Müşteri tipini seç
|
||||
if (customer.customer_type === 'corporate') {
|
||||
document.getElementById('typeCorporate').checked = true;
|
||||
document.getElementById('corporateFields').style.display = 'block';
|
||||
document.getElementById('individualFields').style.display = 'none';
|
||||
document.getElementById('name').required = false;
|
||||
document.getElementById('surname').required = false;
|
||||
document.getElementById('company_name').required = true;
|
||||
} else {
|
||||
document.getElementById('typeIndividual').checked = true;
|
||||
document.getElementById('individualFields').style.display = 'block';
|
||||
document.getElementById('corporateFields').style.display = 'none';
|
||||
document.getElementById('name').required = true;
|
||||
document.getElementById('surname').required = true;
|
||||
document.getElementById('company_name').required = false;
|
||||
}
|
||||
|
||||
// Form alanlarını doldur
|
||||
document.getElementById('name').value = customer.name || '';
|
||||
document.getElementById('surname').value = customer.surname || '';
|
||||
document.getElementById('email').value = customer.email || '';
|
||||
document.getElementById('phone').value = customer.phone || '';
|
||||
document.getElementById('address').value = customer.address || '';
|
||||
document.getElementById('notes').value = customer.notes || '';
|
||||
document.getElementById('tc_number').value = customer.tc_number || '';
|
||||
document.getElementById('birth_date').value = customer.birth_date || '';
|
||||
document.getElementById('company_name').value = customer.company_name || '';
|
||||
document.getElementById('authorized_person').value = customer.authorized_person || '';
|
||||
document.getElementById('tax_number').value = customer.tax_number || '';
|
||||
document.getElementById('tax_office').value = customer.tax_office || '';
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('customerModal'));
|
||||
modal.show();
|
||||
} else {
|
||||
showToast('❌ Müşteri bilgisi alınamadı!', 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showToast('❌ Müşteri bilgisi alınırken hata oluştu!', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Müşteri sil
|
||||
function deleteCustomer(id) {
|
||||
if (confirm('Müşteriyi silmek istediğinizden emin misiniz?\nBu işlem müşteriye ait tüm projeleri de etkileyebilir.')) {
|
||||
fetch(`/musteri/${id}/delete/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken'),
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast('✅ Müşteri başarıyla silindi', 'success');
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
showToast(`❌ ${data.message}`, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showToast('❌ Müşteri silme sırasında hata oluştu!', 'error');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Form submit
|
||||
document.getElementById('customerForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const id = document.getElementById('customerId').value;
|
||||
const url = id ? `/update-customer/${id}/` : '/musteri/create/';
|
||||
const formData = new FormData(this);
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast('✅ Müşteri başarıyla kaydedildi', 'success');
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('customerModal'));
|
||||
if (modal) modal.hide();
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
showToast(`❌ ${data.message}`, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showToast('❌ Müşteri kaydedilirken hata oluştu!', 'error');
|
||||
});
|
||||
});
|
||||
|
||||
// Cookie helper
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
// Toast helper
|
||||
function showToast(message, type = 'info') {
|
||||
const toastBody = document.getElementById('mainToastBody');
|
||||
const toastEl = document.getElementById('mainToast');
|
||||
|
||||
toastBody.textContent = message;
|
||||
|
||||
// Toast rengini ayarla
|
||||
toastEl.className = 'toast align-items-center border-0';
|
||||
if (type === 'success') {
|
||||
toastEl.classList.add('text-bg-success');
|
||||
} else if (type === 'error') {
|
||||
toastEl.classList.add('text-bg-danger');
|
||||
} else {
|
||||
toastEl.classList.add('text-bg-info');
|
||||
}
|
||||
|
||||
const toast = new bootstrap.Toast(toastEl);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
// Sayfa yüklendiğinde URL parametrelerini kontrol et
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const newCustomerType = urlParams.get('new');
|
||||
|
||||
if (newCustomerType && (newCustomerType === 'individual' || newCustomerType === 'corporate')) {
|
||||
resetCustomerForm();
|
||||
|
||||
if (newCustomerType === 'corporate') {
|
||||
document.getElementById('typeCorporate').checked = true;
|
||||
document.getElementById('corporateFields').style.display = 'block';
|
||||
document.getElementById('individualFields').style.display = 'none';
|
||||
document.getElementById('name').required = false;
|
||||
document.getElementById('surname').required = false;
|
||||
document.getElementById('company_name').required = true;
|
||||
} else {
|
||||
document.getElementById('typeIndividual').checked = true;
|
||||
document.getElementById('individualFields').style.display = 'block';
|
||||
document.getElementById('corporateFields').style.display = 'none';
|
||||
document.getElementById('name').required = true;
|
||||
document.getElementById('surname').required = true;
|
||||
document.getElementById('company_name').required = false;
|
||||
}
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('customerModal'));
|
||||
modal.show();
|
||||
|
||||
// URL'yi temizle
|
||||
window.history.replaceState({}, document.title, window.location.pathname);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
1081
templates/ssh_manager/project_list.html
Normal file
1081
templates/ssh_manager/project_list.html
Normal file
File diff suppressed because it is too large
Load Diff
554
templates/ssh_manager/projeler.html
Normal file
554
templates/ssh_manager/projeler.html
Normal file
@ -0,0 +1,554 @@
|
||||
{% extends 'ssh_manager/base.html' %}
|
||||
|
||||
{% block title %}Projeler - Hosting Yönetim Paneli{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Projeler Header -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h3 class="mb-1">
|
||||
<i class="bi bi-folder-fill me-2"></i>Projeler
|
||||
</h3>
|
||||
<small class="text-muted">Tüm hosting projeleri ve yönetim işlemleri</small>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<input type="text" id="projectSearch" class="form-control" style="max-width: 250px;" placeholder="Proje ara...">
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addProjectModal">
|
||||
<i class="bi bi-plus-circle"></i> Yeni Proje
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Projeler Tablosu -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Proje Bilgileri</th>
|
||||
<th>Klasör & Disk</th>
|
||||
<th>Site Durumu</th>
|
||||
<th>Son Yedekleme</th>
|
||||
<th class="actions">İşlemler</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for project in projects %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td style="line-height: 1.3;">
|
||||
<div class="fw-bold">{{ project.name }}</div>
|
||||
{% if project.customer %}
|
||||
<small class="text-info">
|
||||
<i class="bi bi-person me-1"></i>{{ project.customer.get_display_name }}
|
||||
</small><br>
|
||||
{% endif %}
|
||||
{% if project.ssh_credential %}
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-server me-1"></i>{{ project.ssh_credential.hostname }}
|
||||
</small>
|
||||
{% else %}
|
||||
<small class="text-danger">SSH credential yok</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="line-height: 1.3;">
|
||||
<div class="fw-bold">{{ project.folder_name }}</div>
|
||||
{% if project.disk_usage %}
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-hdd me-1"></i>{{ project.disk_usage }}
|
||||
</small>
|
||||
{% else %}
|
||||
<small class="text-muted">-</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if project.url %}
|
||||
<div class="d-flex align-items-center mb-1">
|
||||
{% if project.is_site_active %}
|
||||
<span class="badge bg-success me-2" title="Site aktif">
|
||||
<i class="bi bi-check-circle"></i> Aktif
|
||||
</span>
|
||||
{% elif project.last_site_check %}
|
||||
<span class="badge bg-danger me-2" title="Site pasif">
|
||||
<i class="bi bi-x-circle"></i> Pasif
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary me-2" title="Kontrol edilmemiş">
|
||||
<i class="bi bi-question-circle"></i> Bilinmiyor
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<a href="{% if not project.url|slice:':4' == 'http' %}http://{% endif %}{{ project.url }}" target="_blank" class="text-decoration-none small" style="color: #4fc3f7;">
|
||||
<i class="bi bi-globe me-1"></i>{{ project.url }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="text-muted">URL tanımlanmamış</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if project.last_backup %}
|
||||
<small>{{ project.last_backup|date:"d.m.Y H:i" }}</small>
|
||||
{% else %}
|
||||
<span class="text-warning">Yedek alınmamış</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="actions">
|
||||
<i class="action-icon edit bi bi-pencil" onclick="editProject({{ project.id }})" title="Düzenle"></i>
|
||||
<i class="action-icon delete bi bi-trash" onclick="deleteProject({{ project.id }})" title="Sil"></i>
|
||||
<i class="action-icon backup bi bi-cloud-arrow-up" onclick="backupProject({{ project.id }})" title="Yedekle"></i>
|
||||
<i class="action-icon logs bi bi-journal-text" onclick="showLogsByProject({{ project.id }})" title="Loglar"></i>
|
||||
{% if project.url %}
|
||||
<i class="action-icon site bi bi-globe-americas" onclick="checkSiteStatus({{ project.id }})" title="Site Kontrol"></i>
|
||||
<i class="action-icon meta bi bi-key" onclick="showMetaKey({{ project.id }})" title="Meta Key"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="6" class="text-center text-muted py-4">
|
||||
<i class="bi bi-folder" style="font-size: 2rem;"></i>
|
||||
<div class="mt-2">Henüz proje eklenmemiş</div>
|
||||
<button class="btn btn-sm btn-outline-primary mt-2" data-bs-toggle="modal" data-bs-target="#addProjectModal">
|
||||
İlk projeyi ekle
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Proje Ekleme/Düzenleme Modal -->
|
||||
<div class="modal fade" id="addProjectModal" tabindex="-1" aria-labelledby="addProjectModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<form id="projectForm">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addProjectModalLabel">Yeni Proje Ekle</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Kapat"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="projectId" name="projectId">
|
||||
|
||||
<!-- Proje Bilgileri -->
|
||||
<div class="mb-4">
|
||||
<h6 class="text-muted mb-3">
|
||||
<i class="bi bi-folder me-1"></i>Proje Bilgileri
|
||||
</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="name" class="form-label">Proje Adı *</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required placeholder="Proje adı">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="folder_name" class="form-label">Klasör Adı *</label>
|
||||
<input type="text" class="form-control" id="folder_name" name="folder_name" required placeholder="Sunucudaki klasör adı">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="customer" class="form-label">Müşteri</label>
|
||||
<select class="form-select" id="customer" name="customer">
|
||||
<option value="">Müşteri Seçiniz (Opsiyonel)</option>
|
||||
{% for customer in customers %}
|
||||
<option value="{{ customer.id }}">{{ customer.get_display_name }} ({{ customer.get_customer_type_display }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="ssh_credential" class="form-label">Host *</label>
|
||||
<select class="form-select" id="ssh_credential" name="ssh_credential" required>
|
||||
<option value="">Host Seçiniz</option>
|
||||
{% for host in ssh_credentials %}
|
||||
<option value="{{ host.id }}">{{ host.name|default:host.hostname }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Site Bilgileri -->
|
||||
<div class="mb-3">
|
||||
<h6 class="text-muted mb-3">
|
||||
<i class="bi bi-globe me-1"></i>Site Bilgileri
|
||||
</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label for="url" class="form-label">Site URL'i</label>
|
||||
<input type="url" class="form-control" id="url" name="url" placeholder="https://example.com">
|
||||
<small class="text-muted">Site aktiflik kontrolü için gerekli</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
|
||||
<button type="submit" class="btn btn-primary">Kaydet</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Log Modal -->
|
||||
<div class="modal fade" id="logsModal" tabindex="-1" aria-labelledby="logsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="logsModalLabel">
|
||||
<i class="bi bi-journal-text me-2"></i>Proje Logları
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Kapat"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="logsContent">
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Yükleniyor...</span>
|
||||
</div>
|
||||
<p class="mt-2">Loglar yükleniyor...</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" id="clearLogsBtn" onclick="clearProjectLogs()" style="display: none;">
|
||||
<i class="bi bi-trash"></i> Logları Temizle
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Meta Key Modal -->
|
||||
<div class="modal fade" id="metaKeyModal" tabindex="-1" aria-labelledby="metaKeyModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="metaKeyModalLabel">
|
||||
<i class="bi bi-key me-2"></i>Site Doğrulama Meta Key
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Kapat"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="metaKeyContent">
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Yükleniyor...</span>
|
||||
</div>
|
||||
<p class="mt-2">Meta key bilgileri yükleniyor...</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Tüm JavaScript fonksiyonları buradan project_list.html'den kopyalanacak
|
||||
// Proje düzenleme fonksiyonu
|
||||
window.editProject = function(id) {
|
||||
fetch(`/get-project-details/${id}/`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
document.getElementById('projectId').value = id;
|
||||
document.getElementById('name').value = data.project.name;
|
||||
document.getElementById('folder_name').value = data.project.folder_name;
|
||||
document.getElementById('url').value = data.project.url || '';
|
||||
document.getElementById('ssh_credential').value = data.project.ssh_credential_id || '';
|
||||
document.getElementById('customer').value = data.project.customer_id || '';
|
||||
|
||||
document.getElementById('addProjectModalLabel').innerText = 'Proje Düzenle';
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('addProjectModal'));
|
||||
modal.show();
|
||||
} else {
|
||||
showToast('❌ Proje bilgisi alınamadı!', 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('❌ Proje bilgisi alınırken hata oluştu!', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Proje Sil
|
||||
window.deleteProject = function(id) {
|
||||
if (confirm('Projeyi silmek istediğinize emin misiniz?')) {
|
||||
fetch(`/delete_project/${id}/`, {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRFToken': getCookie('csrftoken') }
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast('✅ Proje başarıyla silindi', 'success');
|
||||
setTimeout(() => location.reload(), 1200);
|
||||
} else {
|
||||
showToast(`❌ ${data.message}`, 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Proje Yedekle
|
||||
window.backupProject = function(id) {
|
||||
if (confirm('Projeyi yedeklemek istiyor musunuz?')) {
|
||||
showToast('🔄 Yedekleme başlatılıyor...', 'info');
|
||||
|
||||
fetch(`/backup-project/${id}/`, {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRFToken': getCookie('csrftoken') }
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast('✅ Yedekleme başarılı', 'success');
|
||||
setTimeout(() => {
|
||||
window.showLogsByProject(id);
|
||||
}, 800);
|
||||
} else {
|
||||
showToast(`❌ ${data.message}`, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('❌ Yedekleme hatası!', 'error');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Log görüntüleme
|
||||
window.showLogsByProject = function(projectId) {
|
||||
window.currentProjectId = projectId;
|
||||
|
||||
fetch(`/project/${projectId}/backup-logs/`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
let html = '';
|
||||
let hasLogs = false;
|
||||
|
||||
if (data.success && data.logs.length > 0) {
|
||||
hasLogs = true;
|
||||
html = '<div class="log-list">';
|
||||
data.logs.forEach(function(log) {
|
||||
let statusBadge = log.status ?
|
||||
'<span class="badge bg-success me-2"><i class="bi bi-check-circle"></i> Başarılı</span>' :
|
||||
'<span class="badge bg-danger me-2"><i class="bi bi-x-circle"></i> Hata</span>';
|
||||
|
||||
let typeIcon = '';
|
||||
if (log.log_type === 'backup') {
|
||||
typeIcon = '<i class="bi bi-cloud-arrow-up text-warning me-2"></i>';
|
||||
} else if (log.log_type === 'command') {
|
||||
typeIcon = '<i class="bi bi-terminal text-info me-2"></i>';
|
||||
}
|
||||
|
||||
html += `
|
||||
<div class="log-entry mb-3 p-3" style="background: rgba(255,255,255,0.05); border-radius: 8px; border-left: 3px solid ${log.status ? '#28a745' : '#dc3545'};">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div>${statusBadge}${typeIcon}<strong>${log.command}</strong></div>
|
||||
<small class="text-muted">${log.created_at}</small>
|
||||
</div>
|
||||
<div class="log-output" style="font-family: monospace; font-size: 0.9em; color: #bdbdbd;">
|
||||
${log.output.replace(/\n/g, '<br>')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
html += '</div>';
|
||||
} else {
|
||||
html = `
|
||||
<div class="text-center py-4">
|
||||
<i class="bi bi-journal-text" style="font-size: 2rem; color: #6c757d;"></i>
|
||||
<p class="text-muted mt-2">Bu projeye ait log bulunamadı</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
document.getElementById('logsContent').innerHTML = html;
|
||||
|
||||
const clearBtn = document.getElementById('clearLogsBtn');
|
||||
clearBtn.style.display = hasLogs ? 'inline-block' : 'none';
|
||||
|
||||
const logsModal = new bootstrap.Modal(document.getElementById('logsModal'));
|
||||
logsModal.show();
|
||||
});
|
||||
}
|
||||
|
||||
// Site durumu kontrol
|
||||
window.checkSiteStatus = function(projectId) {
|
||||
showToast('🔄 Site kontrol ediliyor...', 'info');
|
||||
|
||||
fetch(`/project/${projectId}/check-site/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken'),
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const statusText = data.status ? '✅ Site Aktif' : '❌ Site Pasif';
|
||||
showToast(statusText, data.status ? 'success' : 'error');
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
showToast(`❌ ${data.message}`, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('❌ Kontrol hatası!', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Meta key göster
|
||||
window.showMetaKey = function(projectId) {
|
||||
fetch(`/project/${projectId}/meta-key/`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const content = `
|
||||
<div class="alert alert-info">
|
||||
<h6><i class="bi bi-info-circle"></i> Kullanım Talimatları:</h6>
|
||||
<p>Bu meta tag'ı sitenizin <code><head></code> bölümüne ekleyin:</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><strong>Meta Key:</strong></label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" value="${data.meta_key}" readonly>
|
||||
<button class="btn btn-outline-secondary" onclick="copyToClipboard('${data.meta_key}')">
|
||||
<i class="bi bi-clipboard"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><strong>HTML Meta Tag:</strong></label>
|
||||
<div class="input-group">
|
||||
<textarea class="form-control" rows="2" readonly>${data.meta_tag}</textarea>
|
||||
<button class="btn btn-outline-secondary" onclick="copyToClipboard('${data.meta_tag}')">
|
||||
<i class="bi bi-clipboard"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<small><i class="bi bi-exclamation-triangle"></i> Meta tag'ı ekledikten sonra "Site Kontrol" butonuyla doğrulama yapabilirsiniz.</small>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.getElementById('metaKeyContent').innerHTML = content;
|
||||
const modal = new bootstrap.Modal(document.getElementById('metaKeyModal'));
|
||||
modal.show();
|
||||
} else {
|
||||
showToast(`❌ ${data.message}`, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('❌ Meta key alınırken hata oluştu!', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Clipboard'a kopyala
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
showToast('📋 Panoya kopyalandı!', 'success');
|
||||
}).catch(err => {
|
||||
showToast('❌ Kopyalama hatası!', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Log temizleme
|
||||
function clearProjectLogs() {
|
||||
if (!window.currentProjectId) {
|
||||
showToast('Proje ID bulunamadı!', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm('Bu projenin tüm loglarını silmek istediğinizden emin misiniz?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`/project/${window.currentProjectId}/clear-logs/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken'),
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('logsModal'));
|
||||
if (modal) modal.hide();
|
||||
|
||||
showToast(`✅ ${data.deleted_count} log kaydı silindi`, 'success');
|
||||
window.currentProjectId = null;
|
||||
} else {
|
||||
showToast(`❌ ${data.message || 'Log silme işlemi başarısız!'}`, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('❌ Log silme sırasında bir hata oluştu!', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Proje Form Submit
|
||||
document.getElementById('projectForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const id = document.getElementById('projectId').value;
|
||||
const url = id ? `/update-project/${id}/` : '/project/create/';
|
||||
const formData = new FormData(this);
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRFToken': getCookie('csrftoken') },
|
||||
body: formData
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast('✅ Proje başarıyla kaydedildi', 'success');
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('addProjectModal'));
|
||||
if (modal) modal.hide();
|
||||
setTimeout(() => location.reload(), 1200);
|
||||
} else {
|
||||
showToast(`❌ ${data.message}`, 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Proje Arama
|
||||
document.getElementById('projectSearch').addEventListener('keyup', function() {
|
||||
const searchTerm = this.value.toLowerCase().trim();
|
||||
const projectRows = document.querySelectorAll('tbody tr');
|
||||
|
||||
if (searchTerm.length < 2) {
|
||||
projectRows.forEach(row => {
|
||||
if (row.cells.length > 1) {
|
||||
row.style.display = '';
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
projectRows.forEach(row => {
|
||||
if (row.cells.length > 1) {
|
||||
const projectInfo = row.cells[1].textContent.toLowerCase();
|
||||
const folderInfo = row.cells[2].textContent.toLowerCase();
|
||||
const siteInfo = row.cells[3].textContent.toLowerCase();
|
||||
|
||||
if (projectInfo.includes(searchTerm) || folderInfo.includes(searchTerm) || siteInfo.includes(searchTerm)) {
|
||||
row.style.display = '';
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Modal reset
|
||||
document.getElementById('addProjectModal').addEventListener('hidden.bs.modal', function () {
|
||||
document.getElementById('projectForm').reset();
|
||||
document.getElementById('projectId').value = '';
|
||||
document.getElementById('addProjectModalLabel').textContent = 'Yeni Proje Ekle';
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
426
templates/ssh_manager/yedeklemeler.html
Normal file
426
templates/ssh_manager/yedeklemeler.html
Normal file
@ -0,0 +1,426 @@
|
||||
{% 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 %}
|
||||
Reference in New Issue
Block a user