881 lines
37 KiB
HTML
881 lines
37 KiB
HTML
{% 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">
|
||
<select id="statusFilter" class="form-select" style="max-width: 180px;">
|
||
<option value="">Tüm Projeler</option>
|
||
<option value="active">🟢 Aktif Projeler</option>
|
||
<option value="inactive">🔴 Pasif Projeler</option>
|
||
<option value="unknown">⚫ Bilinmeyen Durumlar</option>
|
||
<option value="no-url">🔘 URL'siz Projeler</option>
|
||
</select>
|
||
<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>
|
||
</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>Host Yenileme</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.host_renewal_date %}
|
||
{% if project.host_renewal_date <= today %}
|
||
<small class="text-danger fw-bold">{{ project.host_renewal_date|date:"d.m.Y" }}</small>
|
||
<br><span class="badge bg-danger">Süresi Dolmuş</span>
|
||
{% elif project.host_renewal_date <= warning_date %}
|
||
<small>{{ project.host_renewal_date|date:"d.m.Y" }}</small>
|
||
<br><span class="badge bg-warning">Yaklaşıyor</span>
|
||
{% else %}
|
||
<small>{{ project.host_renewal_date|date:"d.m.Y" }}</small>
|
||
{% endif %}
|
||
{% else %}
|
||
<span class="text-muted">Tarih yok</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="ProjectManager.editProject({{ project.id }})" title="Düzenle"></i>
|
||
<i class="action-icon delete bi bi-trash" onclick="ProjectManager.deleteProject({{ project.id }})" title="Sil"></i>
|
||
<i class="action-icon renewal bi bi-calendar-plus" onclick="ProjectManager.renewHost({{ project.id }})" title="Domain Expiration Tarihi Sorgula ve Host Yenile"></i>
|
||
<i class="action-icon backup bi bi-cloud-arrow-up" onclick="ProjectManager.backupProject({{ project.id }})" title="Yedekle"></i>
|
||
<i class="action-icon logs bi bi-journal-text" onclick="ProjectManager.showLogsByProject({{ project.id }})" title="Loglar"></i>
|
||
{% if project.url %}
|
||
<i class="action-icon site bi bi-globe-americas" onclick="ProjectManager.checkSiteStatus({{ project.id }})" title="Site Kontrol"></i>
|
||
<i class="action-icon meta bi bi-key" onclick="ProjectManager.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">
|
||
<input type="hidden" id="projectId" name="id" value="">
|
||
<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-md-8">
|
||
<label for="url" class="form-label">Site URL'i</label>
|
||
<input type="text" class="form-control" id="url" name="url" placeholder="https://example.com">
|
||
<small class="text-muted">Site aktiflik kontrolü için gerekli</small>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<label for="host_renewal_date" class="form-label">Host Yenileme Tarihi</label>
|
||
<input type="date" class="form-control" id="host_renewal_date" name="host_renewal_date">
|
||
<small class="text-muted">Hosting paketi yenileme tarihi</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="ProjectManager.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 bg-dark text-light">
|
||
<div class="modal-header border-secondary">
|
||
<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 btn-close-white" 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 border-secondary">
|
||
<button type="button" class="btn btn-outline-light" data-bs-dismiss="modal">Kapat</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Version: 2025-08-08-v4-{{ "now"|date:"YmdHis" }} - Fixed function definitions
|
||
// Ensure DOM is loaded before running scripts
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
console.log('DOM loaded, initializing project management functions...');
|
||
|
||
// CSRF Token almak için utility fonksiyon
|
||
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 bildirim fonksiyonu
|
||
function showToast(message, type = 'info', duration = 3000) {
|
||
// Toast container oluştur (eğer yoksa)
|
||
let toastContainer = document.getElementById('toast-container');
|
||
if (!toastContainer) {
|
||
toastContainer = document.createElement('div');
|
||
toastContainer.id = 'toast-container';
|
||
toastContainer.style.cssText = `
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
z-index: 9999;
|
||
max-width: 350px;
|
||
`;
|
||
document.body.appendChild(toastContainer);
|
||
}
|
||
|
||
// Toast elementi oluştur
|
||
const toast = document.createElement('div');
|
||
const bgClass = type === 'success' ? 'bg-success' :
|
||
type === 'error' ? 'bg-danger' :
|
||
type === 'warning' ? 'bg-warning' : 'bg-info';
|
||
|
||
toast.className = `alert ${bgClass} text-white mb-2`;
|
||
toast.style.cssText = `
|
||
opacity: 0;
|
||
transform: translateX(100%);
|
||
transition: all 0.3s ease;
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||
`;
|
||
toast.innerHTML = message;
|
||
|
||
// Toast'u container'a ekle
|
||
toastContainer.appendChild(toast);
|
||
|
||
// Animasyonla göster
|
||
setTimeout(() => {
|
||
toast.style.opacity = '1';
|
||
toast.style.transform = 'translateX(0)';
|
||
}, 100);
|
||
|
||
// Belirtilen süre sonra kaldır
|
||
setTimeout(() => {
|
||
toast.style.opacity = '0';
|
||
toast.style.transform = 'translateX(100%)';
|
||
setTimeout(() => {
|
||
if (toast.parentNode) {
|
||
toast.parentNode.removeChild(toast);
|
||
}
|
||
}, 300);
|
||
}, duration);
|
||
}
|
||
|
||
// ============= PROJE YÖNETİM FONKSİYONLARI =============
|
||
|
||
// Proje düzenleme fonksiyonu
|
||
function editProject(id) {
|
||
console.log('editProject called with ID:', id);
|
||
fetch(`/get-project-details/${id}/`)
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
console.log('Project details response:', 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;
|
||
|
||
// URL değerini ayarla - mevcut URL'yi olduğu gibi göster
|
||
const urlValue = data.project.url || '';
|
||
document.getElementById('url').value = urlValue;
|
||
|
||
// Host yenileme tarihini ayarla
|
||
const renewalDate = data.project.host_renewal_date || '';
|
||
document.getElementById('host_renewal_date').value = renewalDate;
|
||
|
||
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 => {
|
||
console.error('editProject error:', error);
|
||
showToast('❌ Proje bilgisi alınırken hata oluştu!', 'error');
|
||
});
|
||
}
|
||
function editProject(id) {
|
||
console.log('editProject called with ID:', id);
|
||
fetch(`/get-project-details/${id}/`)
|
||
.then(r => r.json())
|
||
.then data => {
|
||
console.log('Project details response:', 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;
|
||
|
||
// URL değerini ayarla - mevcut URL'yi olduğu gibi göster
|
||
const urlValue = data.project.url || '';
|
||
document.getElementById('url').value = urlValue;
|
||
|
||
// Host yenileme tarihini ayarla
|
||
const renewalDate = data.project.host_renewal_date || '';
|
||
document.getElementById('host_renewal_date').value = renewalDate;
|
||
|
||
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 => {
|
||
console.error('editProject error:', error);
|
||
showToast('❌ Proje bilgisi alınırken hata oluştu!', 'error');
|
||
});
|
||
}
|
||
|
||
// Proje Sil
|
||
function deleteProject(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
|
||
function backupProject(id) {
|
||
showToast('🔄 Proje yedekleniyor...', 'info');
|
||
|
||
fetch(`/backup-project/${id}/`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCookie('csrftoken'),
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast('✅ Proje başarıyla yedeklendi!', 'success');
|
||
} else {
|
||
showToast(`❌ ${data.message}`, 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Backup error:', error);
|
||
showToast('❌ Yedekleme sırasında hata oluştu!', 'error');
|
||
});
|
||
}
|
||
|
||
// Log görüntüleme
|
||
function showLogsByProject(id) {
|
||
// Modal'ı aç
|
||
const logsModal = new bootstrap.Modal(document.getElementById('logsModal'));
|
||
logsModal.show();
|
||
|
||
// Loading durumunu göster
|
||
document.getElementById('logsContent').innerHTML = `
|
||
<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>
|
||
`;
|
||
|
||
// Clear logs butonunu gizle
|
||
document.getElementById('clearLogsBtn').style.display = 'none';
|
||
|
||
fetch(`/project/${id}/backup-logs/`)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
console.log('Logs:', data.logs);
|
||
|
||
if (data.logs.length > 0) {
|
||
let logsHtml = '<div class="logs-container">';
|
||
|
||
data.logs.forEach(log => {
|
||
const statusIcon = log.status === 'success' ?
|
||
'<i class="bi bi-check-circle-fill text-success me-2"></i>' :
|
||
'<i class="bi bi-x-circle-fill text-danger me-2"></i>';
|
||
|
||
const logTypeIcon = log.log_type === 'backup' ?
|
||
'<i class="bi bi-cloud-arrow-up me-1"></i>' :
|
||
'<i class="bi bi-terminal me-1"></i>';
|
||
|
||
logsHtml += `
|
||
<div class="card mb-3 ${log.status === 'success' ? 'border-success' : 'border-danger'}">
|
||
<div class="card-header d-flex justify-content-between align-items-center py-2">
|
||
<div>
|
||
${statusIcon}
|
||
${logTypeIcon}
|
||
<small class="text-muted">${log.created_at}</small>
|
||
</div>
|
||
<span class="badge ${log.status === 'success' ? 'bg-success' : 'bg-danger'}">${log.status}</span>
|
||
</div>
|
||
<div class="card-body py-2">
|
||
<p class="mb-2"><strong>Komut:</strong></p>
|
||
<code class="d-block bg-dark text-light p-2 rounded mb-2">${log.command}</code>
|
||
|
||
<p class="mb-2"><strong>Çıktı:</strong></p>
|
||
<pre class="bg-light p-2 rounded text-dark" style="max-height: 200px; overflow-y: auto; font-size: 0.85rem;">${log.output}</pre>
|
||
</div>
|
||
</div>
|
||
`;
|
||
});
|
||
|
||
logsHtml += '</div>';
|
||
document.getElementById('logsContent').innerHTML = logsHtml;
|
||
|
||
// Clear logs butonunu göster
|
||
document.getElementById('clearLogsBtn').style.display = 'inline-block';
|
||
} else {
|
||
document.getElementById('logsContent').innerHTML = `
|
||
<div class="text-center text-muted py-4">
|
||
<i class="bi bi-journal-text" style="font-size: 3rem;"></i>
|
||
<h5 class="mt-3">Bu proje için henüz log kaydı bulunmuyor</h5>
|
||
<p class="text-muted">Yedekleme veya site kontrol işlemleri yapıldığında loglar burada görünecektir.</p>
|
||
</div>
|
||
`;
|
||
}
|
||
} else {
|
||
document.getElementById('logsContent').innerHTML = `
|
||
<div class="alert alert-danger">
|
||
<i class="bi bi-exclamation-triangle me-2"></i>
|
||
${data.message}
|
||
</div>
|
||
`;
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Logs error:', error);
|
||
document.getElementById('logsContent').innerHTML = `
|
||
<div class="alert alert-danger">
|
||
<i class="bi bi-exclamation-triangle me-2"></i>
|
||
Loglar yüklenirken hata oluştu!
|
||
</div>
|
||
`;
|
||
});
|
||
}
|
||
|
||
// Site durumu kontrol
|
||
function checkSiteStatus(id) {
|
||
showToast('🔄 Site durumu kontrol ediliyor...', 'info');
|
||
|
||
fetch(`/project/${id}/check-site/`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCookie('csrftoken'),
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast('✅ Site kontrol tamamlandı!', 'success');
|
||
setTimeout(() => window.location.reload(), 1500);
|
||
} else {
|
||
showToast(`❌ ${data.message}`, 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Site check error:', error);
|
||
showToast('❌ Site kontrol sırasında hata oluştu!', 'error');
|
||
});
|
||
}
|
||
|
||
// Meta Key Göster
|
||
function showMetaKey(id) {
|
||
// Modal'ı aç
|
||
const metaKeyModal = new bootstrap.Modal(document.getElementById('metaKeyModal'));
|
||
metaKeyModal.show();
|
||
|
||
// Loading durumunu göster
|
||
document.getElementById('metaKeyContent').innerHTML = `
|
||
<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>
|
||
`;
|
||
|
||
fetch(`/project/${id}/meta-key/`)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
console.log('Meta key:', data);
|
||
|
||
let metaKeyHtml = `
|
||
<div class="mb-4">
|
||
<h6 class="text-warning mb-3">
|
||
<i class="bi bi-info-circle me-2"></i>Site Doğrulama Talimatları
|
||
</h6>
|
||
<div class="alert alert-info">
|
||
<p class="mb-2">Sitenizin doğrulanması için aşağıdaki meta tag'i HTML'inizin <head> bölümüne ekleyin:</p>
|
||
<ol class="mb-0">
|
||
<li>Site yönetici paneline giriş yapın</li>
|
||
<li>Tema düzenleyicisini açın (header.php veya index.html)</li>
|
||
<li>Aşağıdaki meta tag'i <head> bölümüne ekleyin</li>
|
||
<li>Değişiklikleri kaydedin</li>
|
||
<li>"Site Doğrula" butonuna tıklayın</li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-4">
|
||
<label class="form-label text-warning fw-bold">Meta Verification Tag:</label>
|
||
<div class="input-group">
|
||
<input type="text" class="form-control bg-dark text-light border-secondary"
|
||
value='${data.meta_key}' readonly id="metaKeyInput">
|
||
<button class="btn btn-outline-info" type="button" onclick="ProjectManager.copyToClipboard('${data.meta_key}')">>
|
||
<i class="bi bi-clipboard"></i> Kopyala
|
||
</button>
|
||
</div>
|
||
<small class="text-muted">Bu meta tag'i sitenizin <head> bölümüne ekleyin</small>
|
||
</div>
|
||
|
||
<div class="mb-4">
|
||
<h6 class="text-warning mb-3">
|
||
<i class="bi bi-check-circle me-2"></i>Doğrulama İşlemi
|
||
</h6>
|
||
<div class="d-grid">
|
||
<button class="btn btn-success btn-lg" onclick="ProjectManager.verifySite(${id})">>
|
||
<i class="bi bi-shield-check me-2"></i>Site Doğrula
|
||
</button>
|
||
</div>
|
||
<small class="text-muted d-block text-center mt-2">
|
||
Meta tag'i ekledikten sonra bu butona tıklayın
|
||
</small>
|
||
</div>
|
||
|
||
<div class="alert alert-warning">
|
||
<i class="bi bi-exclamation-triangle me-2"></i>
|
||
<strong>Önemli:</strong> Meta tag'i eklemeden önce doğrulama yapmayın.
|
||
Tag eklendikten sonra 2-3 dakika bekleyip doğrulama yapın.
|
||
</div>
|
||
`;
|
||
|
||
document.getElementById('metaKeyContent').innerHTML = metaKeyHtml;
|
||
} else {
|
||
document.getElementById('metaKeyContent').innerHTML = `
|
||
<div class="alert alert-danger">
|
||
<i class="bi bi-exclamation-triangle me-2"></i>
|
||
${data.message}
|
||
</div>
|
||
`;
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Meta key error:', error);
|
||
document.getElementById('metaKeyContent').innerHTML = `
|
||
<div class="alert alert-danger">
|
||
<i class="bi bi-exclamation-triangle me-2"></i>
|
||
Meta key alınırken hata oluştu!
|
||
</div>
|
||
`;
|
||
});
|
||
}
|
||
|
||
// Clipboard'a kopyala
|
||
function copyToClipboard(text) {
|
||
navigator.clipboard.writeText(text).then(() => {
|
||
showToast('✅ Panoya kopyalandı!', 'success');
|
||
}).catch(err => {
|
||
console.error('Copy error:', err);
|
||
showToast('❌ Kopyalama hatası!', 'error');
|
||
});
|
||
}
|
||
|
||
// Site doğrula
|
||
function verifySite(projectId) {
|
||
showToast('🔄 Site doğrulama tamamlandı!', 'success');
|
||
|
||
fetch(`/project/${projectId}/check-site/`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCookie('csrftoken'),
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast('✅ Site doğrulama tamamlandı!', 'success');
|
||
} else {
|
||
showToast(`❌ ${data.message}`, 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Site doğrulama hatası:', error);
|
||
showToast('❌ Site doğrulama hatası!', 'error');
|
||
});
|
||
}
|
||
|
||
// Proje loglarını temizle
|
||
function clearProjectLogs() {
|
||
if (confirm('Tüm proje loglarını temizlemek istediğinize emin misiniz?')) {
|
||
showToast('🔄 Loglar temizleniyor...', 'info');
|
||
|
||
fetch('/logs/clear/', {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCookie('csrftoken'),
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast('✅ Loglar temizlendi!', 'success');
|
||
setTimeout(() => window.location.reload(), 1500);
|
||
} else {
|
||
showToast(`❌ ${data.message}`, 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Clear logs error:', error);
|
||
showToast('❌ Log temizleme hatası!', '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');
|
||
}
|
||
});
|
||
});
|
||
|
||
// Durum Filtresi
|
||
document.getElementById('statusFilter').addEventListener('change', function() {
|
||
const filterValue = this.value;
|
||
const projectRows = document.querySelectorAll('tbody tr');
|
||
|
||
projectRows.forEach(row => {
|
||
if (row.cells.length > 1) {
|
||
const siteStatusCell = row.cells[3]; // Site Durumu sütunu
|
||
let shouldShow = false;
|
||
|
||
if (filterValue === '') {
|
||
// Tüm projeler
|
||
shouldShow = true;
|
||
} else if (filterValue === 'active') {
|
||
// Aktif projeler - success badge'i olanlar
|
||
shouldShow = siteStatusCell.querySelector('.badge.bg-success') !== null;
|
||
} else if (filterValue === 'inactive') {
|
||
// Pasif projeler - danger badge'i olanlar
|
||
shouldShow = siteStatusCell.querySelector('.badge.bg-danger') !== null;
|
||
} else if (filterValue === 'unknown') {
|
||
// Bilinmeyen durum - secondary badge'i olanlar
|
||
shouldShow = siteStatusCell.querySelector('.badge.bg-secondary') !== null;
|
||
} else if (filterValue === 'no-url') {
|
||
// URL'siz projeler - "URL tanımlanmamış" yazısı olanlar
|
||
shouldShow = siteStatusCell.textContent.includes('URL tanımlanmamış');
|
||
}
|
||
|
||
row.style.display = shouldShow ? '' : 'none';
|
||
}
|
||
});
|
||
|
||
// Filtreleme sonucunda gösterilen proje sayısını güncelle
|
||
updateProjectCount();
|
||
});
|
||
|
||
// Proje sayısını güncelleme fonksiyonu
|
||
function updateProjectCount() {
|
||
const visibleRows = document.querySelectorAll('tbody tr[style=""], tbody tr:not([style])');
|
||
const totalRows = document.querySelectorAll('tbody tr').length;
|
||
|
||
// Eğer empty state row varsa onu sayma
|
||
const actualVisibleRows = Array.from(visibleRows).filter(row => row.cells.length > 1);
|
||
const actualTotalRows = Array.from(document.querySelectorAll('tbody tr')).filter(row => row.cells.length > 1);
|
||
|
||
// Başlığı güncelle
|
||
const headerText = document.querySelector('h3');
|
||
const originalText = headerText.textContent.split(' (')[0]; // Mevcut sayı bilgisini temizle
|
||
|
||
if (actualTotalRows.length > 0) {
|
||
if (actualVisibleRows.length !== actualTotalRows.length) {
|
||
headerText.innerHTML = `<i class="bi bi-folder-fill me-2"></i>Projeler (${actualVisibleRows.length}/${actualTotalRows.length})`;
|
||
} else {
|
||
headerText.innerHTML = `<i class="bi bi-folder-fill me-2"></i>Projeler (${actualTotalRows.length})`;
|
||
}
|
||
} else {
|
||
headerText.innerHTML = `<i class="bi bi-folder-fill me-2"></i>Projeler`;
|
||
}
|
||
}
|
||
|
||
// Proje Arama (güncellenmiş versiyon - filtreleme ile uyumlu)
|
||
document.getElementById('projectSearch').addEventListener('keyup', function() {
|
||
const searchTerm = this.value.toLowerCase().trim();
|
||
const statusFilter = document.getElementById('statusFilter').value;
|
||
const projectRows = document.querySelectorAll('tbody tr');
|
||
|
||
projectRows.forEach(row => {
|
||
if (row.cells.length > 1) {
|
||
let shouldShow = true;
|
||
|
||
// Önce arama terimine göre kontrol et
|
||
if (searchTerm.length >= 2) {
|
||
const projectInfo = row.cells[1].textContent.toLowerCase();
|
||
const folderInfo = row.cells[2].textContent.toLowerCase();
|
||
const siteInfo = row.cells[3].textContent.toLowerCase();
|
||
|
||
shouldShow = projectInfo.includes(searchTerm) ||
|
||
folderInfo.includes(searchTerm) ||
|
||
siteInfo.includes(searchTerm);
|
||
}
|
||
|
||
// Sonra durum filtresine göre kontrol et
|
||
if (shouldShow && statusFilter !== '') {
|
||
const siteStatusCell = row.cells[3];
|
||
|
||
if (statusFilter === 'active') {
|
||
shouldShow = siteStatusCell.querySelector('.badge.bg-success') !== null;
|
||
} else if (statusFilter === 'inactive') {
|
||
shouldShow = siteStatusCell.querySelector('.badge.bg-danger') !== null;
|
||
} else if (statusFilter === 'unknown') {
|
||
shouldShow = siteStatusCell.querySelector('.badge.bg-secondary') !== null;
|
||
} else if (statusFilter === 'no-url') {
|
||
shouldShow = siteStatusCell.textContent.includes('URL tanımlanmamış');
|
||
}
|
||
}
|
||
|
||
row.style.display = shouldShow ? '' : 'none';
|
||
}
|
||
});
|
||
|
||
// Proje sayısını güncelle
|
||
updateProjectCount();
|
||
});
|
||
|
||
// Proje Arama (eski kod - kaldırıldı)
|
||
|
||
// 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';
|
||
});
|
||
|
||
// Sayfa yüklendiğinde proje sayısını göster
|
||
updateProjectCount();
|
||
|
||
}); // DOMContentLoaded end
|
||
|
||
</script>
|
||
{% endblock %}
|