Initial commit — GuacPanel v1.0
All-in-one installer za Apache Guacamole s Nginx reverse proxy, fail2ban, SSL (Let's Encrypt / Self-signed), GeoIP blokiranjem i Flask web GUI za upravljanje. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Ažuriranja{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h4 class="mb-4"><i class="bi bi-arrow-up-circle me-2"></i>Ažuriranja</h4>
|
||||
|
||||
<!-- Trenutne verzije -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header fw-semibold"><i class="bi bi-info-circle me-1"></i> Trenutne verzije</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
{% for name, ver in versions.items() %}
|
||||
<div class="col-md-4">
|
||||
<div class="p-3 rounded" style="background:#0d1117; border:1px solid #30363d">
|
||||
<div class="small text-muted text-uppercase mb-1">{{ name }}</div>
|
||||
<div class="fw-semibold font-monospace">{{ ver }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Akcije ažuriranja -->
|
||||
<div class="row g-4">
|
||||
|
||||
<!-- Guacamole -->
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header fw-semibold">
|
||||
<i class="bi bi-display me-1"></i> Guacamole
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small">
|
||||
Preuzima najnovije Docker image-e za Guacamole, guacd i PostgreSQL
|
||||
te restarta kontejnere. Konfiguracija i podaci se čuvaju.
|
||||
</p>
|
||||
<button class="btn btn-primary" onclick="startUpdate('guacamole', this)">
|
||||
<i class="bi bi-cloud-download"></i> Ažuriraj Guacamole
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nginx -->
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header fw-semibold">
|
||||
<i class="bi bi-server me-1"></i> Nginx
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small">
|
||||
Ažurira nginx paket putem sistemskog package managera
|
||||
({{ pkg }}) i automatski reloada konfiguraciju.
|
||||
</p>
|
||||
<button class="btn btn-primary" onclick="startUpdate('nginx', this)">
|
||||
<i class="bi bi-arrow-up-circle"></i> Ažuriraj Nginx
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- fail2ban -->
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header fw-semibold">
|
||||
<i class="bi bi-shield me-1"></i> fail2ban
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small">
|
||||
Ažurira fail2ban paket i restarta servis.
|
||||
Jail konfiguracije ostaju netaknute.
|
||||
</p>
|
||||
<button class="btn btn-primary" onclick="updateFail2ban(this)">
|
||||
<i class="bi bi-arrow-up-circle"></i> Ažuriraj fail2ban
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sistem -->
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header fw-semibold">
|
||||
<i class="bi bi-hdd me-1"></i> Sistemske nadogradnje
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small">
|
||||
Ažurira sve instalirane sistemske pakete putem <code>{{ pkg }}</code>.
|
||||
Može potrajati nekoliko minuta.
|
||||
</p>
|
||||
<div class="alert alert-warning py-2 small mb-3">
|
||||
<i class="bi bi-exclamation-triangle me-1"></i>
|
||||
Nakon sistemske nadogradnje može biti potreban restart servera.
|
||||
</div>
|
||||
<button class="btn btn-warning" onclick="startUpdate('system', this)">
|
||||
<i class="bi bi-hdd-rack"></i> Ažuriraj sistem
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Output konzola -->
|
||||
<div id="output-panel" class="card mt-4 d-none">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span class="fw-semibold">
|
||||
<i class="bi bi-terminal me-1"></i>
|
||||
Output: <span id="output-title">—</span>
|
||||
</span>
|
||||
<div>
|
||||
<span id="output-status" class="badge bg-warning me-2">U tijeku...</span>
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="clearOutput()">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="log-output"></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let activeSource = null;
|
||||
|
||||
function startUpdate(type, btn) {
|
||||
const titles = {
|
||||
guacamole: 'Guacamole',
|
||||
nginx: 'Nginx',
|
||||
system: 'Sistemske nadogradnje'
|
||||
};
|
||||
|
||||
if (activeSource) {
|
||||
activeSource.close();
|
||||
activeSource = null;
|
||||
}
|
||||
|
||||
document.getElementById('output-panel').classList.remove('d-none');
|
||||
document.getElementById('output-title').textContent = titles[type] || type;
|
||||
document.getElementById('output-status').textContent = 'U tijeku...';
|
||||
document.getElementById('output-status').className = 'badge bg-warning me-2';
|
||||
|
||||
const out = document.getElementById('log-output');
|
||||
out.innerHTML = '';
|
||||
|
||||
const origHtml = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Ažuriranje...';
|
||||
|
||||
activeSource = new EventSource(`/api/update/${type}`);
|
||||
activeSource.onmessage = e => {
|
||||
const data = JSON.parse(e.data);
|
||||
if (data === '__DONE__') {
|
||||
activeSource.close();
|
||||
activeSource = null;
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = origHtml;
|
||||
document.getElementById('output-status').textContent = 'Završeno';
|
||||
document.getElementById('output-status').className = 'badge bg-success me-2';
|
||||
return;
|
||||
}
|
||||
const line = document.createElement('div');
|
||||
line.textContent = data;
|
||||
if (data.startsWith('✓')) line.style.color = '#3fb950';
|
||||
out.appendChild(line);
|
||||
out.scrollTop = out.scrollHeight;
|
||||
};
|
||||
activeSource.onerror = () => {
|
||||
activeSource.close();
|
||||
activeSource = null;
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = origHtml;
|
||||
document.getElementById('output-status').textContent = 'Greška';
|
||||
document.getElementById('output-status').className = 'badge bg-danger me-2';
|
||||
};
|
||||
}
|
||||
|
||||
async function updateFail2ban(btn) {
|
||||
const orig = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Ažuriranje...';
|
||||
|
||||
document.getElementById('output-panel').classList.remove('d-none');
|
||||
document.getElementById('output-title').textContent = 'fail2ban';
|
||||
document.getElementById('output-status').textContent = 'U tijeku...';
|
||||
document.getElementById('output-status').className = 'badge bg-warning me-2';
|
||||
|
||||
const r = await api('/api/update/fail2ban');
|
||||
const out = document.getElementById('log-output');
|
||||
out.innerHTML = '';
|
||||
const line = document.createElement('div');
|
||||
line.textContent = r.msg;
|
||||
out.appendChild(line);
|
||||
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = orig;
|
||||
document.getElementById('output-status').textContent = r.ok ? 'Završeno' : 'Greška';
|
||||
document.getElementById('output-status').className = `badge ${r.ok ? 'bg-success' : 'bg-danger'} me-2`;
|
||||
showToast(r.ok ? 'fail2ban ažuriran' : r.msg, r.ok);
|
||||
}
|
||||
|
||||
function clearOutput() {
|
||||
document.getElementById('log-output').innerHTML = '';
|
||||
document.getElementById('output-panel').classList.add('d-none');
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user