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,79 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Backup{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="mb-0"><i class="bi bi-archive me-2"></i>Backup</h4>
|
||||
<button class="btn btn-primary" onclick="createBackup(this)">
|
||||
<i class="bi bi-plus-circle"></i> Napravi backup
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info small mb-4">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
Backup uključuje: Guacamole konfiguraciju, PostgreSQL bazu, nginx konfig,
|
||||
Basic Auth korisnike, fail2ban pravila i SSL certifikate.
|
||||
Sprema se u <code>/root/</code> (čitljivo samo root-u).
|
||||
</div>
|
||||
|
||||
<!-- Lista backupa -->
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span class="fw-semibold">Dostupni backupi</span>
|
||||
<span class="badge bg-secondary">{{ backups|length }}</span>
|
||||
</div>
|
||||
{% if backups %}
|
||||
<div class="list-group list-group-flush">
|
||||
{% for b in backups %}
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center"
|
||||
style="background:transparent; border-color:#30363d">
|
||||
<div>
|
||||
<i class="bi bi-file-zip me-2 text-muted"></i>
|
||||
<span class="font-monospace small">{{ b }}</span>
|
||||
</div>
|
||||
<button class="btn btn-outline-danger btn-sm" onclick="deleteBackup('{{ b }}')">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card-body text-muted">
|
||||
<i class="bi bi-inbox me-1"></i> Nema dostupnih backupa
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Progress -->
|
||||
<div id="backup-progress" class="card mt-4 d-none">
|
||||
<div class="card-body d-flex align-items-center gap-3">
|
||||
<div class="spinner-border spinner-border-sm text-primary"></div>
|
||||
<span>Kreiranje backupa, molim pričekajte...</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
async function createBackup(btn) {
|
||||
const orig = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Backup...';
|
||||
document.getElementById('backup-progress').classList.remove('d-none');
|
||||
|
||||
const r = await api('/api/backup/create');
|
||||
showToast(r.msg, r.ok);
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = orig;
|
||||
document.getElementById('backup-progress').classList.add('d-none');
|
||||
if (r.ok) setTimeout(() => location.reload(), 1500);
|
||||
}
|
||||
|
||||
async function deleteBackup(name) {
|
||||
if (!confirm(`Obrisati backup "${name}"?`)) return;
|
||||
const r = await api('/api/backup/delete', {name});
|
||||
showToast(r.msg, r.ok);
|
||||
if (r.ok) setTimeout(() => location.reload(), 800);
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user