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

555 lines
22 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.

{% extends 'ssh_manager/base.html' %}
{% block title %}Projeler - Hosting Yönetim Paneli{% endblock %}
{% block content %}
<!-- Projeler Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h3 class="mb-1">
<i class="bi bi-folder-fill me-2"></i>Projeler
</h3>
<small class="text-muted">Tüm hosting projeleri ve yönetim işlemleri</small>
</div>
<div class="d-flex gap-2">
<input type="text" id="projectSearch" class="form-control" style="max-width: 250px;" placeholder="Proje ara...">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addProjectModal">
<i class="bi bi-plus-circle"></i> Yeni Proje
</button>
</div>
</div>
<!-- Projeler Tablosu -->
<div class="table-responsive">
<table class="table table-dark table-striped">
<thead>
<tr>
<th>#</th>
<th>Proje Bilgileri</th>
<th>Klasör & Disk</th>
<th>Site Durumu</th>
<th>Son Yedekleme</th>
<th class="actions">İşlemler</th>
</tr>
</thead>
<tbody>
{% for project in projects %}
<tr>
<td>{{ forloop.counter }}</td>
<td style="line-height: 1.3;">
<div class="fw-bold">{{ project.name }}</div>
{% if project.customer %}
<small class="text-info">
<i class="bi bi-person me-1"></i>{{ project.customer.get_display_name }}
</small><br>
{% endif %}
{% if project.ssh_credential %}
<small class="text-muted">
<i class="bi bi-server me-1"></i>{{ project.ssh_credential.hostname }}
</small>
{% else %}
<small class="text-danger">SSH credential yok</small>
{% endif %}
</td>
<td style="line-height: 1.3;">
<div class="fw-bold">{{ project.folder_name }}</div>
{% if project.disk_usage %}
<small class="text-muted">
<i class="bi bi-hdd me-1"></i>{{ project.disk_usage }}
</small>
{% else %}
<small class="text-muted">-</small>
{% endif %}
</td>
<td>
{% if project.url %}
<div class="d-flex align-items-center mb-1">
{% if project.is_site_active %}
<span class="badge bg-success me-2" title="Site aktif">
<i class="bi bi-check-circle"></i> Aktif
</span>
{% elif project.last_site_check %}
<span class="badge bg-danger me-2" title="Site pasif">
<i class="bi bi-x-circle"></i> Pasif
</span>
{% else %}
<span class="badge bg-secondary me-2" title="Kontrol edilmemiş">
<i class="bi bi-question-circle"></i> Bilinmiyor
</span>
{% endif %}
</div>
<a href="{% if not project.url|slice:':4' == 'http' %}http://{% endif %}{{ project.url }}" target="_blank" class="text-decoration-none small" style="color: #4fc3f7;">
<i class="bi bi-globe me-1"></i>{{ project.url }}
</a>
{% else %}
<span class="text-muted">URL tanımlanmamış</span>
{% endif %}
</td>
<td>
{% if project.last_backup %}
<small>{{ project.last_backup|date:"d.m.Y H:i" }}</small>
{% else %}
<span class="text-warning">Yedek alınmamış</span>
{% endif %}
</td>
<td class="actions">
<i class="action-icon edit bi bi-pencil" onclick="editProject({{ project.id }})" title="Düzenle"></i>
<i class="action-icon delete bi bi-trash" onclick="deleteProject({{ project.id }})" title="Sil"></i>
<i class="action-icon backup bi bi-cloud-arrow-up" onclick="backupProject({{ project.id }})" title="Yedekle"></i>
<i class="action-icon logs bi bi-journal-text" onclick="showLogsByProject({{ project.id }})" title="Loglar"></i>
{% if project.url %}
<i class="action-icon site bi bi-globe-americas" onclick="checkSiteStatus({{ project.id }})" title="Site Kontrol"></i>
<i class="action-icon meta bi bi-key" onclick="showMetaKey({{ project.id }})" title="Meta Key"></i>
{% endif %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="6" class="text-center text-muted py-4">
<i class="bi bi-folder" style="font-size: 2rem;"></i>
<div class="mt-2">Henüz proje eklenmemiş</div>
<button class="btn btn-sm btn-outline-primary mt-2" data-bs-toggle="modal" data-bs-target="#addProjectModal">
İlk projeyi ekle
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Proje Ekleme/Düzenleme Modal -->
<div class="modal fade" id="addProjectModal" tabindex="-1" aria-labelledby="addProjectModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<form id="projectForm">
<div class="modal-header">
<h5 class="modal-title" id="addProjectModalLabel">Yeni Proje Ekle</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Kapat"></button>
</div>
<div class="modal-body">
<input type="hidden" id="projectId" name="projectId">
<!-- Proje Bilgileri -->
<div class="mb-4">
<h6 class="text-muted mb-3">
<i class="bi bi-folder me-1"></i>Proje Bilgileri
</h6>
<div class="row g-3">
<div class="col-md-6">
<label for="name" class="form-label">Proje Adı *</label>
<input type="text" class="form-control" id="name" name="name" required placeholder="Proje adı">
</div>
<div class="col-md-6">
<label for="folder_name" class="form-label">Klasör Adı *</label>
<input type="text" class="form-control" id="folder_name" name="folder_name" required placeholder="Sunucudaki klasör adı">
</div>
<div class="col-md-6">
<label for="customer" class="form-label">Müşteri</label>
<select class="form-select" id="customer" name="customer">
<option value="">Müşteri Seçiniz (Opsiyonel)</option>
{% for customer in customers %}
<option value="{{ customer.id }}">{{ customer.get_display_name }} ({{ customer.get_customer_type_display }})</option>
{% endfor %}
</select>
</div>
<div class="col-md-6">
<label for="ssh_credential" class="form-label">Host *</label>
<select class="form-select" id="ssh_credential" name="ssh_credential" required>
<option value="">Host Seçiniz</option>
{% for host in ssh_credentials %}
<option value="{{ host.id }}">{{ host.name|default:host.hostname }}</option>
{% endfor %}
</select>
</div>
</div>
</div>
<!-- Site Bilgileri -->
<div class="mb-3">
<h6 class="text-muted mb-3">
<i class="bi bi-globe me-1"></i>Site Bilgileri
</h6>
<div class="row g-3">
<div class="col-12">
<label for="url" class="form-label">Site URL'i</label>
<input type="url" class="form-control" id="url" name="url" placeholder="https://example.com">
<small class="text-muted">Site aktiflik kontrolü için gerekli</small>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
<button type="submit" class="btn btn-primary">Kaydet</button>
</div>
</form>
</div>
</div>
</div>
<!-- Log Modal -->
<div class="modal fade" id="logsModal" tabindex="-1" aria-labelledby="logsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="logsModalLabel">
<i class="bi bi-journal-text me-2"></i>Proje Logları
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Kapat"></button>
</div>
<div class="modal-body" id="logsContent">
<div class="text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Yükleniyor...</span>
</div>
<p class="mt-2">Loglar yükleniyor...</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" id="clearLogsBtn" onclick="clearProjectLogs()" style="display: none;">
<i class="bi bi-trash"></i> Logları Temizle
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
</div>
</div>
</div>
</div>
<!-- Meta Key Modal -->
<div class="modal fade" id="metaKeyModal" tabindex="-1" aria-labelledby="metaKeyModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="metaKeyModalLabel">
<i class="bi bi-key me-2"></i>Site Doğrulama Meta Key
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Kapat"></button>
</div>
<div class="modal-body" id="metaKeyContent">
<div class="text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Yükleniyor...</span>
</div>
<p class="mt-2">Meta key bilgileri yükleniyor...</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
</div>
</div>
</div>
</div>
<script>
// Tüm JavaScript fonksiyonları buradan project_list.html'den kopyalanacak
// Proje düzenleme fonksiyonu
window.editProject = function(id) {
fetch(`/get-project-details/${id}/`)
.then(r => r.json())
.then(data => {
if (data.success) {
document.getElementById('projectId').value = id;
document.getElementById('name').value = data.project.name;
document.getElementById('folder_name').value = data.project.folder_name;
document.getElementById('url').value = data.project.url || '';
document.getElementById('ssh_credential').value = data.project.ssh_credential_id || '';
document.getElementById('customer').value = data.project.customer_id || '';
document.getElementById('addProjectModalLabel').innerText = 'Proje Düzenle';
const modal = new bootstrap.Modal(document.getElementById('addProjectModal'));
modal.show();
} else {
showToast('❌ Proje bilgisi alınamadı!', 'error');
}
})
.catch(error => {
showToast('❌ Proje bilgisi alınırken hata oluştu!', 'error');
});
}
// Proje Sil
window.deleteProject = function(id) {
if (confirm('Projeyi silmek istediğinize emin misiniz?')) {
fetch(`/delete_project/${id}/`, {
method: 'POST',
headers: { 'X-CSRFToken': getCookie('csrftoken') }
})
.then(r => r.json())
.then(data => {
if (data.success) {
showToast('✅ Proje başarıyla silindi', 'success');
setTimeout(() => location.reload(), 1200);
} else {
showToast(`${data.message}`, 'error');
}
});
}
}
// Proje Yedekle
window.backupProject = function(id) {
if (confirm('Projeyi yedeklemek istiyor musunuz?')) {
showToast('🔄 Yedekleme başlatılıyor...', 'info');
fetch(`/backup-project/${id}/`, {
method: 'POST',
headers: { 'X-CSRFToken': getCookie('csrftoken') }
})
.then(r => r.json())
.then(data => {
if (data.success) {
showToast('✅ Yedekleme başarılı', 'success');
setTimeout(() => {
window.showLogsByProject(id);
}, 800);
} else {
showToast(`${data.message}`, 'error');
}
})
.catch(error => {
showToast('❌ Yedekleme hatası!', 'error');
});
}
}
// Log görüntüleme
window.showLogsByProject = function(projectId) {
window.currentProjectId = projectId;
fetch(`/project/${projectId}/backup-logs/`)
.then(r => r.json())
.then(data => {
let html = '';
let hasLogs = false;
if (data.success && data.logs.length > 0) {
hasLogs = true;
html = '<div class="log-list">';
data.logs.forEach(function(log) {
let statusBadge = log.status ?
'<span class="badge bg-success me-2"><i class="bi bi-check-circle"></i> Başarılı</span>' :
'<span class="badge bg-danger me-2"><i class="bi bi-x-circle"></i> Hata</span>';
let typeIcon = '';
if (log.log_type === 'backup') {
typeIcon = '<i class="bi bi-cloud-arrow-up text-warning me-2"></i>';
} else if (log.log_type === 'command') {
typeIcon = '<i class="bi bi-terminal text-info me-2"></i>';
}
html += `
<div class="log-entry mb-3 p-3" style="background: rgba(255,255,255,0.05); border-radius: 8px; border-left: 3px solid ${log.status ? '#28a745' : '#dc3545'};">
<div class="d-flex justify-content-between align-items-center mb-2">
<div>${statusBadge}${typeIcon}<strong>${log.command}</strong></div>
<small class="text-muted">${log.created_at}</small>
</div>
<div class="log-output" style="font-family: monospace; font-size: 0.9em; color: #bdbdbd;">
${log.output.replace(/\n/g, '<br>')}
</div>
</div>
`;
});
html += '</div>';
} else {
html = `
<div class="text-center py-4">
<i class="bi bi-journal-text" style="font-size: 2rem; color: #6c757d;"></i>
<p class="text-muted mt-2">Bu projeye ait log bulunamadı</p>
</div>
`;
}
document.getElementById('logsContent').innerHTML = html;
const clearBtn = document.getElementById('clearLogsBtn');
clearBtn.style.display = hasLogs ? 'inline-block' : 'none';
const logsModal = new bootstrap.Modal(document.getElementById('logsModal'));
logsModal.show();
});
}
// Site durumu kontrol
window.checkSiteStatus = function(projectId) {
showToast('🔄 Site kontrol ediliyor...', 'info');
fetch(`/project/${projectId}/check-site/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
const statusText = data.status ? '✅ Site Aktif' : '❌ Site Pasif';
showToast(statusText, data.status ? 'success' : 'error');
setTimeout(() => location.reload(), 1500);
} else {
showToast(`${data.message}`, 'error');
}
})
.catch(error => {
showToast('❌ Kontrol hatası!', 'error');
});
}
// Meta key göster
window.showMetaKey = function(projectId) {
fetch(`/project/${projectId}/meta-key/`)
.then(response => response.json())
.then(data => {
if (data.success) {
const content = `
<div class="alert alert-info">
<h6><i class="bi bi-info-circle"></i> Kullanım Talimatları:</h6>
<p>Bu meta tag'ı sitenizin <code>&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="copyToClipboard('${data.meta_key}')">
<i class="bi bi-clipboard"></i>
</button>
</div>
</div>
<div class="mb-3">
<label class="form-label"><strong>HTML Meta Tag:</strong></label>
<div class="input-group">
<textarea class="form-control" rows="2" readonly>${data.meta_tag}</textarea>
<button class="btn btn-outline-secondary" onclick="copyToClipboard('${data.meta_tag}')">
<i class="bi bi-clipboard"></i>
</button>
</div>
</div>
<div class="alert alert-warning">
<small><i class="bi bi-exclamation-triangle"></i> Meta tag'ı ekledikten sonra "Site Kontrol" butonuyla doğrulama yapabilirsiniz.</small>
</div>
`;
document.getElementById('metaKeyContent').innerHTML = content;
const modal = new bootstrap.Modal(document.getElementById('metaKeyModal'));
modal.show();
} else {
showToast(`${data.message}`, 'error');
}
})
.catch(error => {
showToast('❌ Meta key alınırken hata oluştu!', 'error');
});
}
// Clipboard'a kopyala
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
showToast('📋 Panoya kopyalandı!', 'success');
}).catch(err => {
showToast('❌ Kopyalama hatası!', 'error');
});
}
// Log temizleme
function clearProjectLogs() {
if (!window.currentProjectId) {
showToast('Proje ID bulunamadı!', 'error');
return;
}
if (!confirm('Bu projenin tüm loglarını silmek istediğinizden emin misiniz?')) {
return;
}
fetch(`/project/${window.currentProjectId}/clear-logs/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
const modal = bootstrap.Modal.getInstance(document.getElementById('logsModal'));
if (modal) modal.hide();
showToast(`${data.deleted_count} log kaydı silindi`, 'success');
window.currentProjectId = null;
} else {
showToast(`${data.message || 'Log silme işlemi başarısız!'}`, 'error');
}
})
.catch(error => {
showToast('❌ Log silme sırasında bir hata oluştu!', 'error');
});
}
// Proje Form Submit
document.getElementById('projectForm').addEventListener('submit', function(e) {
e.preventDefault();
const id = document.getElementById('projectId').value;
const url = id ? `/update-project/${id}/` : '/project/create/';
const formData = new FormData(this);
fetch(url, {
method: 'POST',
headers: { 'X-CSRFToken': getCookie('csrftoken') },
body: formData
})
.then(r => r.json())
.then(data => {
if (data.success) {
showToast('✅ Proje başarıyla kaydedildi', 'success');
const modal = bootstrap.Modal.getInstance(document.getElementById('addProjectModal'));
if (modal) modal.hide();
setTimeout(() => location.reload(), 1200);
} else {
showToast(`${data.message}`, 'error');
}
});
});
// Proje Arama
document.getElementById('projectSearch').addEventListener('keyup', function() {
const searchTerm = this.value.toLowerCase().trim();
const projectRows = document.querySelectorAll('tbody tr');
if (searchTerm.length < 2) {
projectRows.forEach(row => {
if (row.cells.length > 1) {
row.style.display = '';
}
});
return;
}
projectRows.forEach(row => {
if (row.cells.length > 1) {
const projectInfo = row.cells[1].textContent.toLowerCase();
const folderInfo = row.cells[2].textContent.toLowerCase();
const siteInfo = row.cells[3].textContent.toLowerCase();
if (projectInfo.includes(searchTerm) || folderInfo.includes(searchTerm) || siteInfo.includes(searchTerm)) {
row.style.display = '';
} else {
row.style.display = 'none';
}
}
});
});
// Modal reset
document.getElementById('addProjectModal').addEventListener('hidden.bs.modal', function () {
document.getElementById('projectForm').reset();
document.getElementById('projectId').value = '';
document.getElementById('addProjectModalLabel').textContent = 'Yeni Proje Ekle';
});
</script>
{% endblock %}