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

1082 lines
42 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!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>&lt;head&gt;</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ıı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ıı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>