Files
GuacPanel/webgui/templates/base.html
T
njomac 50c125e0db 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>
2026-04-26 11:21:44 +02:00

148 lines
5.6 KiB
HTML

<!DOCTYPE html>
<html lang="hr" data-bs-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Guacamole Manager{% endblock %}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/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 { min-height: 100vh; background: #0f1117; }
#sidebar {
width: 230px; min-height: 100vh; background: #161b22;
border-right: 1px solid #30363d; position: fixed; top: 0; left: 0;
display: flex; flex-direction: column; z-index: 100;
}
#sidebar .brand {
padding: 1.25rem 1rem; border-bottom: 1px solid #30363d;
font-weight: 700; font-size: 1rem; color: #58a6ff;
display: flex; align-items: center; gap: .5rem;
}
#sidebar .nav-link {
color: #8b949e; padding: .55rem 1rem; border-radius: 6px;
margin: 2px 8px; display: flex; align-items: center; gap: .6rem;
font-size: .9rem; transition: all .15s;
}
#sidebar .nav-link:hover { background: #21262d; color: #c9d1d9; }
#sidebar .nav-link.active { background: #1f6feb22; color: #58a6ff; }
#sidebar .nav-link i { font-size: 1rem; width: 18px; text-align: center; }
#sidebar .sidebar-footer {
margin-top: auto; padding: 1rem; border-top: 1px solid #30363d; font-size: .8rem;
}
#content { margin-left: 230px; padding: 2rem; }
.status-dot {
width: 10px; height: 10px; border-radius: 50%; display: inline-block;
}
.status-ok { background: #3fb950; box-shadow: 0 0 6px #3fb95066; }
.status-err { background: #f85149; box-shadow: 0 0 6px #f8514966; }
.status-warn { background: #d29922; box-shadow: 0 0 6px #d2992266; }
.card { background: #161b22; border: 1px solid #30363d; }
.card-header { background: #1c2128; border-bottom: 1px solid #30363d; }
.badge-country {
background: #1f6feb33; color: #58a6ff; border: 1px solid #1f6feb66;
font-size: .75rem; padding: 3px 8px; border-radius: 12px; margin: 2px;
display: inline-block;
}
#log-output {
background: #010409; color: #c9d1d9; font-family: monospace;
font-size: .8rem; height: 400px; overflow-y: auto; padding: 1rem;
border-radius: 6px; border: 1px solid #30363d;
}
.toast-container { position: fixed; bottom: 1.5rem; right: 1.5rem; z-index: 9999; }
@media (max-width: 768px) {
#sidebar { width: 100%; min-height: auto; position: relative; }
#content { margin-left: 0; }
}
</style>
</head>
<body>
<!-- Sidebar -->
<div id="sidebar">
<div class="brand">
<i class="bi bi-display"></i> Guacamole
</div>
<nav class="nav flex-column mt-2">
<a class="nav-link {% if request.endpoint == 'dashboard' %}active{% endif %}"
href="{{ url_for('dashboard') }}">
<i class="bi bi-grid-1x2"></i> Dashboard
</a>
<a class="nav-link {% if request.endpoint == 'basicauth' %}active{% endif %}"
href="{{ url_for('basicauth') }}">
<i class="bi bi-person-lock"></i> Basic Auth
</a>
<a class="nav-link {% if request.endpoint == 'geoip' %}active{% endif %}"
href="{{ url_for('geoip') }}">
<i class="bi bi-globe"></i> GeoIP
</a>
<a class="nav-link {% if request.endpoint == 'fail2ban' %}active{% endif %}"
href="{{ url_for('fail2ban') }}">
<i class="bi bi-shield-x"></i> fail2ban
</a>
<a class="nav-link {% if request.endpoint == 'guacamole' %}active{% endif %}"
href="{{ url_for('guacamole') }}">
<i class="bi bi-boxes"></i> Kontejneri
</a>
<a class="nav-link {% if request.endpoint == 'ssl' %}active{% endif %}"
href="{{ url_for('ssl') }}">
<i class="bi bi-lock"></i> SSL
</a>
<a class="nav-link {% if request.endpoint == 'updates' %}active{% endif %}"
href="{{ url_for('updates') }}">
<i class="bi bi-arrow-up-circle"></i> Ažuriranja
</a>
<a class="nav-link {% if request.endpoint == 'backup' %}active{% endif %}"
href="{{ url_for('backup') }}">
<i class="bi bi-archive"></i> Backup
</a>
<a class="nav-link {% if request.endpoint == 'settings' %}active{% endif %}"
href="{{ url_for('settings') }}">
<i class="bi bi-gear"></i> Postavke
</a>
</nav>
<div class="sidebar-footer text-muted">
<a href="{{ url_for('logout') }}" class="text-danger text-decoration-none">
<i class="bi bi-box-arrow-right"></i> Odjava
</a>
</div>
</div>
<!-- Content -->
<div id="content">
{% block content %}{% endblock %}
</div>
<!-- Toast container -->
<div class="toast-container">
<div id="toast" class="toast align-items-center border-0" role="alert">
<div class="d-flex">
<div class="toast-body" id="toast-body"></div>
<button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
const toast = new bootstrap.Toast(document.getElementById('toast'), {delay: 4000});
function showToast(msg, ok=true) {
const el = document.getElementById('toast');
el.className = `toast align-items-center border-0 text-bg-${ok ? 'success' : 'danger'}`;
document.getElementById('toast-body').textContent = msg;
toast.show();
}
async function api(url, body={}) {
const r = await fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body)
});
return r.json();
}
</script>
{% block scripts %}{% endblock %}
</body>
</html>