1082 lines
42 KiB
HTML
1082 lines
42 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="tr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Hosting Yönetim Paneli</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: #e0e0e0;
|
||
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: #999;
|
||
margin-top: 0.25rem;
|
||
}
|
||
|
||
.sidebar-nav {
|
||
padding: 1.5rem 0;
|
||
}
|
||
|
||
.nav-item {
|
||
display: block;
|
||
padding: 0.75rem 1.5rem;
|
||
color: #ccc;
|
||
text-decoration: none;
|
||
border-left: 3px solid transparent;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.nav-item:hover {
|
||
background: rgba(79, 195, 247, 0.1);
|
||
color: #4fc3f7;
|
||
border-left-color: #4fc3f7;
|
||
}
|
||
|
||
.nav-item.active {
|
||
background: rgba(79, 195, 247, 0.2);
|
||
color: #4fc3f7;
|
||
border-left-color: #4fc3f7;
|
||
}
|
||
|
||
.nav-item i {
|
||
width: 20px;
|
||
margin-right: 0.75rem;
|
||
}
|
||
|
||
/* Main Content */
|
||
.main-content {
|
||
margin-left: 280px;
|
||
padding: 2rem;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.content-section {
|
||
transition: opacity 0.3s ease;
|
||
}
|
||
|
||
.table-dark th, .table-dark td { color: #e0e0e0; }
|
||
.table-dark { background: #23272b; }
|
||
.modal-content { background: #23272b; color: #e0e0e0; }
|
||
.form-control, .form-select { background: #181a1b; color: #e0e0e0; border: 1px solid #444; }
|
||
.form-label { color: #e0e0e0; }
|
||
.actions { min-width: 140px; }
|
||
.btn { padding: 0.25rem 0.5rem; }
|
||
.btn i { font-size: 1.1rem; }
|
||
.action-icon {
|
||
color: #6c757d;
|
||
font-size: 1.2rem;
|
||
margin: 0 0.3rem;
|
||
cursor: pointer;
|
||
transition: color 0.2s ease;
|
||
}
|
||
.action-icon:hover { color: #007bff; }
|
||
.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: #999; }
|
||
#mainToast .toast-body { color: orange; font-weight: bold; }
|
||
|
||
/* 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>
|
||
|
||
<!-- Main Content -->
|
||
<div class="main-content">
|
||
<!-- Toast Container -->
|
||
<div class="position-fixed top-0 end-0 p-3" style="z-index: 1100">
|
||
<!-- Normal Toast -->
|
||
<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>
|
||
|
||
<!-- Progress Toast -->
|
||
<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>
|
||
<h1 class="mb-4" id="pageTitle">Dashboard</h1>
|
||
|
||
<!-- Dashboard Section -->
|
||
<div id="dashboardSection" class="content-section">
|
||
<div class="mb-3">
|
||
<button class="btn btn-primary me-1" data-bs-toggle="modal" data-bs-target="#addHostModal" title="Yeni Host Ekle"><i class="bi bi-hdd-network"></i></button>
|
||
<button class="btn btn-success me-1" data-bs-toggle="modal" data-bs-target="#addProjectModal" title="Yeni Proje Ekle"><i class="bi bi-plus-circle"></i></button>
|
||
<button class="btn btn-secondary" onclick="location.reload()" title="Yenile"><i class="bi bi-arrow-clockwise"></i></button>
|
||
</div>
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<h3>Host Hesapları</h3>
|
||
<button class="btn btn-sm btn-success" id="refreshHostsBtn" title="Host Bilgilerini Yenile">
|
||
<i class="bi bi-arrow-clockwise"></i> Hostları Yenile
|
||
</button>
|
||
</div>
|
||
<table class="table table-dark table-bordered table-striped">
|
||
<thead>
|
||
<tr>
|
||
<th>#</th>
|
||
<th>Hostname</th>
|
||
<th>Kullanıcı</th>
|
||
<th>Port</th>
|
||
<th>Base Path</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>{{ forloop.counter }}</td>
|
||
<td>{{ host.hostname }}</td>
|
||
<td>{{ host.username }}</td>
|
||
<td>{{ host.port }}</td>
|
||
<td>{{ host.base_path }}</td>
|
||
<td>{{ host.disk_usage|default:'-' }}</td>
|
||
<td>{{ host.last_check|date:"d.m.Y H:i" }}</td>
|
||
<td>
|
||
<i class="bi bi-pencil action-icon edit" onclick="editHost({{ host.id }})" title="Düzenle"></i>
|
||
<i class="bi bi-trash action-icon delete" onclick="deleteHost({{ host.id }})" title="Sil"></i>
|
||
</td>
|
||
</tr>
|
||
{% empty %}
|
||
<tr><td colspan="8">Kayıtlı host yok.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<h3>Projeler</h3>
|
||
<input type="text" id="projectSearch" class="form-control" style="max-width: 250px;" placeholder="Proje Ara (en az 3 harf)...">
|
||
</div>
|
||
<table class="table table-dark table-bordered table-striped">
|
||
<thead>
|
||
<tr>
|
||
<th>#</th>
|
||
<th>Proje Adı</th>
|
||
<th>Klasör</th>
|
||
<th>URL</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.2;">
|
||
{{ project.name }}<br>
|
||
{% if project.customer %}
|
||
<small style="font-size: 0.75em; color: #4fc3f7;">👤 {{ project.customer.get_display_name }}</small><br>
|
||
{% endif %}
|
||
{% if project.ssh_credential %}
|
||
{% if project.ssh_credential.hostname %}
|
||
<small style="font-size: 0.8em; color: #bdbdbd;">🖥️ {{ project.ssh_credential.hostname }}</small>
|
||
{% else %}
|
||
<small class="text-danger" style="font-size: 0.8em;">Hostname boş</small>
|
||
{% endif %}
|
||
{% else %}
|
||
<small class="text-danger" style="font-size: 0.8em;">SSH credential yok</small>
|
||
{% endif %}
|
||
</td>
|
||
<td style="line-height: 1.2;">
|
||
{{ project.folder_name }}<br>
|
||
<small style="font-size: 0.8em; color: #bdbdbd;">{{ project.disk_usage|default:'-' }}</small>
|
||
</td>
|
||
<td>
|
||
{% if project.url %}
|
||
<div class="d-flex align-items-center">
|
||
{% if project.is_site_active %}
|
||
<span class="badge bg-success me-2" title="Site aktif">🟢</span>
|
||
{% elif project.last_site_check %}
|
||
<span class="badge bg-danger me-2" title="Site pasif">🔴</span>
|
||
{% else %}
|
||
<span class="badge bg-secondary me-2" title="Kontrol edilmemiş">⚪</span>
|
||
{% endif %}
|
||
<a href="{% if not project.url|slice:':4' == 'http' %}http://{% endif %}{{ project.url }}" target="_blank" class="text-decoration-none" style="color: #4fc3f7;">
|
||
<i class="bi bi-globe me-1"></i>{{ project.url }}
|
||
</a>
|
||
</div>
|
||
{% else %}
|
||
<span class="text-muted">-</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>{{ project.last_backup|date:"d.m.Y H:i" }}</td>
|
||
<td>
|
||
<i class="bi bi-pencil action-icon edit" onclick="editProject({{ project.id }})" title="Düzenle"></i>
|
||
<i class="bi bi-trash action-icon delete" onclick="deleteProject({{ project.id }})" title="Sil"></i>
|
||
<i class="bi bi-cloud-arrow-up action-icon backup" onclick="backupProject({{ project.id }})" title="Yedekle"></i>
|
||
<i class="bi bi-journal-text action-icon logs" onclick="showLogsByProject({{ project.id }})" title="Loglar"></i>
|
||
{% if project.url %}
|
||
<i class="bi bi-globe-americas action-icon site" onclick="checkSiteStatus({{ project.id }})" title="Site Kontrol"></i>
|
||
<i class="bi bi-key action-icon meta" onclick="showMetaKey({{ project.id }})" title="Meta Key"></i>
|
||
{% endif %}
|
||
</td>
|
||
</tr>
|
||
{% empty %}
|
||
<tr><td colspan="6">Kayıtlı proje yok.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div> <!-- dashboardSection kapanış -->
|
||
</div>
|
||
|
||
<!-- Host Ekle Modal -->
|
||
<div class="modal fade" id="addHostModal" tabindex="-1" aria-labelledby="addHostModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content">
|
||
<form id="hostForm">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="addHostModalLabel">Yeni Host 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="hostId" name="hostId">
|
||
<div class="mb-3">
|
||
<label for="hostname" class="form-label">Hostname</label>
|
||
<input type="text" class="form-control" id="hostname" name="hostname" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label for="username" class="form-label">Kullanıcı Adı</label>
|
||
<input type="text" class="form-control" id="username" name="username" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label for="password" class="form-label">Şifre</label>
|
||
<input type="password" class="form-control" id="password" name="password">
|
||
</div>
|
||
<div class="mb-3">
|
||
<label for="port" class="form-label">Port</label>
|
||
<input type="number" class="form-control" id="port" name="port" value="22" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label for="base_path" class="form-label">Base Path</label>
|
||
<input type="text" class="form-control" id="base_path" name="base_path" required>
|
||
</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>
|
||
<!-- Proje Ekle Modal -->
|
||
<div class="modal fade" id="addProjectModal" tabindex="-1" aria-labelledby="addProjectModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog">
|
||
<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">
|
||
<div class="mb-3">
|
||
<label for="name" class="form-label">Proje Adı</label>
|
||
<input type="text" class="form-control" id="name" name="name" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<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="mb-3">
|
||
<label for="folder_name" class="form-label">Klasör Adı</label>
|
||
<input type="text" class="form-control" id="folder_name" name="folder_name" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label for="ssh_credential" class="form-label">Host</label>
|
||
<select class="form-select" id="ssh_credential" name="ssh_credential" required>
|
||
<option value="">Seçiniz</option>
|
||
{% for host in ssh_credentials %}
|
||
<option value="{{ host.id }}">{{ host.hostname }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label for="url" class="form-label">URL</label>
|
||
<input type="text" class="form-control" id="url" name="url">
|
||
</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">Loglar</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Kapat"></button>
|
||
</div>
|
||
<div class="modal-body" id="logsContent">
|
||
Yükleniyor...
|
||
</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">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">
|
||
Yükleniyor...
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||
<script>
|
||
// CSRF Token Al
|
||
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 mesajı fonksiyonu
|
||
function toastMessage(msg) {
|
||
const toastBody = document.getElementById('mainToastBody');
|
||
toastBody.textContent = msg;
|
||
const toastEl = document.getElementById('mainToast');
|
||
const toast = new bootstrap.Toast(toastEl);
|
||
toast.show();
|
||
}
|
||
|
||
// showToast fonksiyonu (mevcut kodlarla uyumluluk için)
|
||
function showToast(message, type = 'info') {
|
||
const toastBody = document.getElementById('mainToastBody');
|
||
toastBody.innerHTML = message;
|
||
|
||
// Toast rengini tipine göre ayarla
|
||
const toastEl = document.getElementById('mainToast');
|
||
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 if (type === 'warning') {
|
||
toastEl.classList.add('text-bg-warning');
|
||
} else {
|
||
toastEl.classList.add('text-bg-dark');
|
||
}
|
||
|
||
const toast = new bootstrap.Toast(toastEl);
|
||
toast.show();
|
||
}
|
||
|
||
// Host Sil
|
||
function deleteHost(id) {
|
||
if (confirm('Host hesabını silmek istediğinize emin misiniz?')) {
|
||
fetch(`/delete_host/${id}/`, {
|
||
method: 'POST',
|
||
headers: { 'X-CSRFToken': getCookie('csrftoken') }
|
||
})
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
toastMessage(data.message);
|
||
if (data.success) setTimeout(() => location.reload(), 1200);
|
||
});
|
||
}
|
||
}
|
||
|
||
// Host Düzenle
|
||
function editHost(id) {
|
||
fetch(`/get_host/${id}/`)
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
document.getElementById('hostId').value = data.host.id;
|
||
document.getElementById('hostname').value = data.host.hostname;
|
||
document.getElementById('username').value = data.host.username;
|
||
document.getElementById('password').value = data.host.password;
|
||
document.getElementById('port').value = data.host.port;
|
||
document.getElementById('base_path').value = data.host.base_path;
|
||
document.getElementById('addHostModalLabel').innerText = 'Host Düzenle';
|
||
// Edit modunda inputlara editing class ekle
|
||
['hostname','username','password','port','base_path'].forEach(function(id) {
|
||
document.getElementById(id).classList.add('editing');
|
||
});
|
||
var modal = new bootstrap.Modal(document.getElementById('addHostModal'));
|
||
modal.show();
|
||
} else {
|
||
toastMessage('Host bilgisi alınamadı!');
|
||
}
|
||
});
|
||
}
|
||
|
||
// Proje düzenleme fonksiyonu - global scope
|
||
window.editProject = function(id) {
|
||
console.log('EditProject called with ID:', id);
|
||
|
||
fetch(`/get-project-details/${id}/`)
|
||
.then(r => {
|
||
console.log('Response received:', r.status);
|
||
return r.json();
|
||
})
|
||
.then(data => {
|
||
console.log('Data received:', data);
|
||
|
||
if (data.success) {
|
||
// Form alanlarını doldur
|
||
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 || '';
|
||
|
||
// Modal başlığını değiştir
|
||
document.getElementById('addProjectModalLabel').innerText = 'Proje Düzenle';
|
||
|
||
// Edit modunda inputlara editing class ekle
|
||
['name','folder_name','url','ssh_credential'].forEach(function(id) {
|
||
const element = document.getElementById(id);
|
||
if (element) {
|
||
element.classList.add('editing');
|
||
}
|
||
});
|
||
|
||
// Modal'ı göster
|
||
console.log('Opening modal...');
|
||
const modalElement = document.getElementById('addProjectModal');
|
||
if (modalElement) {
|
||
const modal = new bootstrap.Modal(modalElement);
|
||
modal.show();
|
||
console.log('Modal shown');
|
||
} else {
|
||
console.error('Modal element not found');
|
||
}
|
||
} else {
|
||
console.error('Project data error:', data.message);
|
||
showToast('❌ Proje bilgisi alınamadı!', 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Fetch error:', error);
|
||
showToast('❌ Proje bilgisi alınırken hata oluştu!', 'error');
|
||
});
|
||
}
|
||
|
||
// Proje Sil - global scope
|
||
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 => {
|
||
toastMessage(data.message);
|
||
if (data.success) {
|
||
setTimeout(() => location.reload(), 1200);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// Proje Yedekle - global scope
|
||
window.backupProject = function(id) {
|
||
if (confirm('Projeyi yedeklemek istiyor musunuz?')) {
|
||
// Progress toast'ı başlat
|
||
showProgressToast('Yedekleme', 'Başlatılıyor...', 10);
|
||
|
||
fetch(`/backup-project/${id}/`, {
|
||
method: 'POST',
|
||
headers: { 'X-CSRFToken': getCookie('csrftoken') }
|
||
})
|
||
.then(r => {
|
||
updateProgress(40, 'İşlem devam ediyor...');
|
||
return r.json();
|
||
})
|
||
.then(data => {
|
||
updateProgress(80, 'Tamamlanıyor...');
|
||
|
||
setTimeout(() => {
|
||
updateProgress(100, 'Bitti!');
|
||
|
||
setTimeout(() => {
|
||
hideProgressToast();
|
||
|
||
if (data.success) {
|
||
showToast('✅ Yedekleme Başarılı', 'success');
|
||
|
||
// Logları göster
|
||
setTimeout(() => {
|
||
window.showLogsByProject(id);
|
||
}, 800);
|
||
} else {
|
||
showToast(`❌ ${data.message}`, 'error');
|
||
}
|
||
}, 300);
|
||
}, 600);
|
||
})
|
||
.catch(error => {
|
||
hideProgressToast();
|
||
console.error('Backup error:', error);
|
||
showToast('❌ Yedekleme Hatası!', 'error');
|
||
});
|
||
}
|
||
}
|
||
|
||
// Log görüntüleme - global scope
|
||
window.showLogsByProject = function(projectId) {
|
||
// Global değişkene proje ID'sini kaydet
|
||
window.currentProjectId = projectId;
|
||
|
||
fetch(`/project/${projectId}/backup-logs/`)
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
let html = '<h5>Proje Logları</h5><ul class="list-group">';
|
||
let hasLogs = false;
|
||
|
||
if (data.success && data.logs.length > 0) {
|
||
hasLogs = true;
|
||
data.logs.forEach(function(log) {
|
||
let statusIcon = log.status ? '<span class="badge bg-success me-2">✔</span>' : '<span class="badge bg-danger me-2">✖</span>';
|
||
|
||
// Log tipine göre ikon ekle
|
||
let typeIcon = '';
|
||
if (log.log_type === 'backup') {
|
||
typeIcon = '<i class="bi bi-cloud-arrow-up text-warning me-1"></i>';
|
||
} else if (log.log_type === 'command') {
|
||
typeIcon = '<i class="bi bi-globe-americas text-info me-1"></i>';
|
||
}
|
||
|
||
html += `<li class="list-group-item bg-dark text-light border-secondary mb-2" style="border-radius:8px;">
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<span>${statusIcon}${typeIcon}<b>${log.command}</b></span>
|
||
<small class="text-muted">${log.created_at}</small>
|
||
</div>
|
||
<div class="mt-1" style="font-size:0.97em; color:#bdbdbd;">${log.output}</div>
|
||
</li>`;
|
||
});
|
||
} else {
|
||
html += '<li class="list-group-item bg-dark text-light">Bu projeye ait log bulunamadı.</li>';
|
||
}
|
||
html += '</ul>';
|
||
document.getElementById('logsContent').innerHTML = html;
|
||
|
||
// Temizle butonunu göster/gizle
|
||
const clearBtn = document.getElementById('clearLogsBtn');
|
||
if (hasLogs) {
|
||
clearBtn.style.display = 'inline-block';
|
||
} else {
|
||
clearBtn.style.display = 'none';
|
||
}
|
||
|
||
var logsModal = new bootstrap.Modal(document.getElementById('logsModal'));
|
||
logsModal.show();
|
||
});
|
||
}
|
||
|
||
// Site durumu kontrol fonksiyonu - global scope
|
||
window.checkSiteStatus = function(projectId) {
|
||
// Progress toast'ı başlat
|
||
showProgressToast('Site Kontrol', 'İşlem başlatılıyor...', 10);
|
||
|
||
fetch(`/project/${projectId}/check-site/`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCookie('csrftoken'),
|
||
'Content-Type': 'application/json'
|
||
}
|
||
})
|
||
.then(response => {
|
||
updateProgress(60, 'Kontrol ediliyor...');
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
updateProgress(90, 'Sonuçlar kaydediliyor...');
|
||
|
||
setTimeout(() => {
|
||
updateProgress(100, 'Tamamlandı!');
|
||
|
||
setTimeout(() => {
|
||
hideProgressToast();
|
||
|
||
if (data.success) {
|
||
const statusText = data.status ? '✅ Site Aktif' : '❌ Site Pasif';
|
||
showToast(statusText, data.status ? 'success' : 'error');
|
||
|
||
// Sayfayı yenile
|
||
setTimeout(() => location.reload(), 1500);
|
||
} else {
|
||
showToast(`❌ ${data.message}`, 'error');
|
||
}
|
||
}, 300);
|
||
}, 500);
|
||
})
|
||
.catch(error => {
|
||
hideProgressToast();
|
||
console.error('Error:', error);
|
||
showToast('❌ Kontrol hatası!', 'error');
|
||
});
|
||
}
|
||
|
||
// Meta key göster fonksiyonu - global scope
|
||
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="copyMetaKey('${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 id="metaTagText">${data.meta_tag}</textarea>
|
||
<button class="btn btn-outline-secondary" onclick="copyMetaTag()">
|
||
<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 => {
|
||
console.error('Error:', error);
|
||
showToast('❌ Meta key alınırken hata oluştu!', 'error');
|
||
});
|
||
}
|
||
|
||
// Progress Toast fonksiyonları
|
||
function showProgressToast(title, message, progress) {
|
||
const progressToast = document.getElementById('progressToast');
|
||
const progressMessage = document.getElementById('progressMessage');
|
||
const progressDetail = document.getElementById('progressDetail');
|
||
const progressBar = document.getElementById('progressBar');
|
||
|
||
progressMessage.textContent = title;
|
||
progressDetail.textContent = message;
|
||
progressBar.style.width = progress + '%';
|
||
|
||
const toast = new bootstrap.Toast(progressToast);
|
||
toast.show();
|
||
}
|
||
|
||
function updateProgress(progress, message) {
|
||
const progressDetail = document.getElementById('progressDetail');
|
||
const progressBar = document.getElementById('progressBar');
|
||
|
||
progressDetail.textContent = message;
|
||
progressBar.style.width = progress + '%';
|
||
}
|
||
|
||
function hideProgressToast() {
|
||
const progressToast = document.getElementById('progressToast');
|
||
const toast = bootstrap.Toast.getInstance(progressToast);
|
||
if (toast) {
|
||
toast.hide();
|
||
}
|
||
}
|
||
|
||
// Clipboard'a kopyala fonksiyonu
|
||
function copyToClipboard(text) {
|
||
navigator.clipboard.writeText(text).then(() => {
|
||
showToast('📋 Panoya kopyalandı!', 'success');
|
||
}).catch(err => {
|
||
console.error('Error copying to clipboard:', err);
|
||
showToast('❌ Kopyalama hatası!', 'error');
|
||
});
|
||
}
|
||
|
||
// Meta key'i kopyala
|
||
function copyMetaKey(metaKey) {
|
||
navigator.clipboard.writeText(metaKey).then(() => {
|
||
showToast('📋 Meta key panoya kopyalandı!', 'success');
|
||
}).catch(err => {
|
||
console.error('Error copying to clipboard:', err);
|
||
showToast('❌ Kopyalama hatası!', 'error');
|
||
});
|
||
}
|
||
|
||
// Meta tag'ı kopyala
|
||
function copyMetaTag() {
|
||
const metaTagText = document.getElementById('metaTagText').value;
|
||
navigator.clipboard.writeText(metaTagText).then(() => {
|
||
showToast('📋 Meta tag panoya kopyalandı!', 'success');
|
||
}).catch(err => {
|
||
console.error('Error copying to clipboard:', err);
|
||
showToast('❌ Kopyalama hatası!', 'error');
|
||
});
|
||
}
|
||
|
||
// Log temizleme fonksiyonu
|
||
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) {
|
||
// Modal'ı kapat
|
||
const modal = bootstrap.Modal.getInstance(document.getElementById('logsModal'));
|
||
if (modal) {
|
||
modal.hide();
|
||
}
|
||
|
||
// Toast mesajı göster
|
||
showToast(`✅ ${data.deleted_count} log kaydı silindi`, 'success');
|
||
|
||
// Global değişkeni temizle
|
||
window.currentProjectId = null;
|
||
} 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');
|
||
});
|
||
}
|
||
|
||
// Proje Form Submit
|
||
const projectForm = document.getElementById('projectForm');
|
||
if (projectForm) {
|
||
projectForm.onsubmit = function(e) {
|
||
e.preventDefault();
|
||
const id = document.getElementById('projectId').value;
|
||
const url = id ? `/update-project/${id}/` : '/project/create/';
|
||
const method = 'POST';
|
||
const formData = new FormData(projectForm);
|
||
fetch(url, {
|
||
method: method,
|
||
headers: { 'X-CSRFToken': getCookie('csrftoken') },
|
||
body: formData
|
||
})
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
toastMessage(data.message);
|
||
if (data.success) {
|
||
const modal = bootstrap.Modal.getInstance(document.getElementById('addProjectModal'));
|
||
if (modal) modal.hide();
|
||
setTimeout(() => location.reload(), 1200);
|
||
}
|
||
});
|
||
};
|
||
}
|
||
|
||
// Proje Ekle modalı açıldığında formu sıfırla
|
||
const addProjectModal = document.getElementById('addProjectModal');
|
||
if (addProjectModal) {
|
||
addProjectModal.addEventListener('show.bs.modal', function (event) {
|
||
if (!document.getElementById('projectId').value) {
|
||
document.getElementById('projectForm').reset();
|
||
document.getElementById('addProjectModalLabel').innerText = 'Yeni Proje Ekle';
|
||
}
|
||
});
|
||
|
||
addProjectModal.addEventListener('hide.bs.modal', function (event) {
|
||
document.getElementById('projectForm').reset();
|
||
document.getElementById('projectId').value = '';
|
||
document.getElementById('addProjectModalLabel').innerText = 'Yeni Proje Ekle';
|
||
['name','folder_name','url','ssh_credential'].forEach(function(id) {
|
||
document.getElementById(id).classList.remove('editing');
|
||
});
|
||
});
|
||
}
|
||
|
||
// Host Form Submit
|
||
const hostForm = document.getElementById('hostForm');
|
||
if (hostForm) {
|
||
hostForm.onsubmit = function(e) {
|
||
e.preventDefault();
|
||
const id = document.getElementById('hostId').value;
|
||
const url = id ? `/update_host/${id}/` : '/create_host/';
|
||
const method = 'POST';
|
||
const formData = new FormData(hostForm);
|
||
fetch(url, {
|
||
method: method,
|
||
headers: { 'X-CSRFToken': getCookie('csrftoken') },
|
||
body: formData
|
||
})
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
toastMessage(data.message);
|
||
if (data.success) {
|
||
const modal = bootstrap.Modal.getInstance(document.getElementById('addHostModal'));
|
||
if (modal) modal.hide();
|
||
setTimeout(() => location.reload(), 1200);
|
||
}
|
||
});
|
||
};
|
||
}
|
||
|
||
// Host Ekle modalı açıldığında formu sıfırla
|
||
const addHostModal = document.getElementById('addHostModal');
|
||
if (addHostModal) {
|
||
addHostModal.addEventListener('show.bs.modal', function (event) {
|
||
// Sadece yeni ekleme için formu sıfırla
|
||
if (!document.getElementById('hostId').value) {
|
||
document.getElementById('hostForm').reset();
|
||
document.getElementById('addHostModalLabel').innerText = 'Yeni Host Ekle';
|
||
}
|
||
});
|
||
|
||
addHostModal.addEventListener('hide.bs.modal', function (event) {
|
||
// Modal kapatılırken editing class'ı kaldır
|
||
['hostname','username','password','port','base_path'].forEach(function(id) {
|
||
document.getElementById(id).classList.remove('editing');
|
||
});
|
||
});
|
||
}
|
||
|
||
// Proje Arama
|
||
const projectSearchInput = document.getElementById('projectSearch');
|
||
if (projectSearchInput) {
|
||
projectSearchInput.addEventListener('keyup', function() {
|
||
const searchTerm = this.value.toLowerCase().trim();
|
||
const projectRows = document.querySelectorAll('table:nth-of-type(2) tbody tr');
|
||
|
||
if (searchTerm.length < 3) {
|
||
projectRows.forEach(row => {
|
||
// Eğer satırda "Kayıtlı proje yok." mesajı varsa gösterme
|
||
if (row.cells.length > 1) {
|
||
row.style.display = '';
|
||
}
|
||
});
|
||
return;
|
||
}
|
||
|
||
projectRows.forEach(row => {
|
||
if (row.cells.length > 1) { // "Kayıtlı proje yok" satırını atla
|
||
const projectName = row.cells[1].textContent.toLowerCase();
|
||
const folderName = row.cells[2].textContent.toLowerCase();
|
||
const projectUrl = row.cells[3].textContent.toLowerCase();
|
||
|
||
if (projectName.includes(searchTerm) || folderName.includes(searchTerm) || projectUrl.includes(searchTerm)) {
|
||
row.style.display = '';
|
||
} else {
|
||
row.style.display = 'none';
|
||
}
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// Host yenile butonu event listener
|
||
const refreshHostsBtn = document.getElementById('refreshHostsBtn');
|
||
if (refreshHostsBtn) {
|
||
refreshHostsBtn.addEventListener('click', function() {
|
||
console.log('Host yenile butonu tıklandı');
|
||
showProgressToast('Host Bilgileri Güncelleniyor', 'Tüm hostların durum ve disk bilgileri kontrol ediliyor...', 0);
|
||
|
||
fetch('/update-hosts-status/', {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCookie('csrftoken'),
|
||
'Content-Type': 'application/json'
|
||
}
|
||
})
|
||
.then(response => {
|
||
console.log('Response status:', response.status);
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
console.log('Response data:', data);
|
||
hideProgressToast();
|
||
|
||
if (data.success) {
|
||
showToast('✅ Tüm host bilgileri güncellendi', 'success');
|
||
setTimeout(() => location.reload(), 1000);
|
||
} else {
|
||
showToast(`❌ ${data.message || 'Host güncelleme işlemi başarısız!'}`, 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
hideProgressToast();
|
||
console.error('Error:', error);
|
||
showToast('❌ Host güncelleme sırasında bir hata oluştu!', 'error');
|
||
});
|
||
});
|
||
}
|
||
|
||
// DOM yüklendikten sonra çalıştır
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
console.log('DOM yüklendi');
|
||
});
|
||
|
||
</script>
|
||
</div> <!-- main-content kapanış -->
|
||
</body>
|
||
</html>
|