7c9136152b
- Added a new PHP script for collecting server metrics every 30 seconds. - Created a ServerMonitoring class to handle metrics collection for CPU, RAM, Disk, and Network. - Introduced database tables for storing server and client metrics. - Updated server view template to display real-time metrics using Chart.js. - Added translations for monitoring UI elements. - Created a new monitoring template for detailed server metrics visualization. - Implemented client speed tracking and display in the monitoring UI.
179 lines
8.4 KiB
Twig
179 lines
8.4 KiB
Twig
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{% block title %}{{ app_name }}{% endblock %}</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
{% block styles %}{% endblock %}
|
|
<style>
|
|
.gradient-bg {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
}
|
|
.mobile-menu {
|
|
display: none;
|
|
}
|
|
.mobile-menu.active {
|
|
display: block;
|
|
}
|
|
.language-dropdown {
|
|
display: none;
|
|
}
|
|
.language-dropdown.active {
|
|
display: block;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-gray-50">
|
|
{% if user %}
|
|
<!-- Navigation -->
|
|
<nav class="gradient-bg shadow-lg">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div class="flex justify-between h-16">
|
|
<div class="flex">
|
|
<div class="flex-shrink-0 flex items-center">
|
|
<i class="fas fa-shield-alt text-white text-2xl mr-2"></i>
|
|
<span class="text-white text-xl font-bold">{{ app_name }}</span>
|
|
</div>
|
|
<!-- Desktop Menu -->
|
|
<div class="hidden md:ml-6 md:flex md:space-x-8">
|
|
<a href="/dashboard" class="text-white hover:text-gray-200 inline-flex items-center px-1 pt-1 text-sm font-medium">
|
|
<i class="fas fa-tachometer-alt mr-2"></i>{{ t('menu.dashboard') }}
|
|
</a>
|
|
<a href="/servers" class="text-white hover:text-gray-200 inline-flex items-center px-1 pt-1 text-sm font-medium">
|
|
<i class="fas fa-server mr-2"></i>{{ t('menu.servers') }}
|
|
</a>
|
|
{% if user.role == 'admin' %}
|
|
<a href="/settings" class="text-white hover:text-gray-200 inline-flex items-center px-1 pt-1 text-sm font-medium">
|
|
<i class="fas fa-cog mr-2"></i>{{ t('menu.settings') }}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<!-- Language Selector -->
|
|
<div class="relative mr-4">
|
|
<button onclick="toggleLanguageDropdown()" class="text-white hover:text-gray-200 flex items-center focus:outline-none">
|
|
{% set currentLang = current_language %}
|
|
{% for lang in languages %}
|
|
{% if lang.code == currentLang %}
|
|
<span class="mr-2">{{ getFlag(lang.code) }}</span>
|
|
{% endif %}
|
|
{% endfor %}
|
|
<i class="fas fa-chevron-down ml-1 text-xs"></i>
|
|
</button>
|
|
<div id="languageDropdown" class="language-dropdown absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg z-50">
|
|
{% for lang in languages %}
|
|
<form method="POST" action="/language/change" class="block">
|
|
<input type="hidden" name="language" value="{{ lang.code }}">
|
|
<input type="hidden" name="redirect" value="{{ current_uri }}">
|
|
<button type="submit" class="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 flex items-center
|
|
{% if lang.code == current_language %}bg-purple-50 font-medium{% endif %}">
|
|
<span class="mr-2">{{ getFlag(lang.code) }}</span>
|
|
<span>{{ lang.name }}</span>
|
|
{% if lang.code == current_language %}
|
|
<i class="fas fa-check text-purple-600 ml-auto"></i>
|
|
{% endif %}
|
|
</button>
|
|
</form>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- User Menu (Desktop) -->
|
|
<div class="hidden md:flex items-center">
|
|
<span class="text-white mr-4">
|
|
<i class="fas fa-user mr-1"></i>{{ user.name }}
|
|
</span>
|
|
<a href="/logout" class="text-white hover:text-gray-200">
|
|
<i class="fas fa-sign-out-alt mr-1"></i>{{ t('menu.logout') }}
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Mobile menu button -->
|
|
<div class="md:hidden flex items-center ml-4">
|
|
<button onclick="toggleMobileMenu()" type="button" class="text-white hover:text-gray-200 focus:outline-none">
|
|
<i class="fas fa-bars text-xl"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mobile Menu -->
|
|
<div id="mobileMenu" class="mobile-menu md:hidden">
|
|
<div class="px-2 pt-2 pb-3 space-y-1">
|
|
<a href="/dashboard" class="text-white hover:bg-purple-700 block px-3 py-2 rounded-md text-base font-medium">
|
|
<i class="fas fa-tachometer-alt mr-2"></i>{{ t('menu.dashboard') }}
|
|
</a>
|
|
<a href="/servers" class="text-white hover:bg-purple-700 block px-3 py-2 rounded-md text-base font-medium">
|
|
<i class="fas fa-server mr-2"></i>{{ t('menu.servers') }}
|
|
</a>
|
|
{% if user.role == 'admin' %}
|
|
<a href="/settings" class="text-white hover:bg-purple-700 block px-3 py-2 rounded-md text-base font-medium">
|
|
<i class="fas fa-cog mr-2"></i>{{ t('menu.settings') }}
|
|
</a>
|
|
{% endif %}
|
|
<div class="border-t border-purple-500 my-2"></div>
|
|
<div class="text-white px-3 py-2">
|
|
<i class="fas fa-user mr-1"></i>{{ user.name }}
|
|
</div>
|
|
<a href="/logout" class="text-white hover:bg-purple-700 block px-3 py-2 rounded-md text-base font-medium">
|
|
<i class="fas fa-sign-out-alt mr-1"></i>{{ t('menu.logout') }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
{% endif %}
|
|
|
|
<!-- Main Content -->
|
|
<main class="{% if user %}py-10{% endif %}">
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
|
|
<!-- Footer -->
|
|
<footer class="bg-white border-t border-gray-200 mt-auto">
|
|
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
|
<p class="text-center text-gray-500 text-sm">
|
|
{{ app_name }} © 2025 | Open Source VPN Management Panel
|
|
</p>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
function toggleMobileMenu() {
|
|
const menu = document.getElementById('mobileMenu');
|
|
menu.classList.toggle('active');
|
|
}
|
|
|
|
function toggleLanguageDropdown() {
|
|
const dropdown = document.getElementById('languageDropdown');
|
|
dropdown.classList.toggle('active');
|
|
}
|
|
|
|
// Close dropdown when clicking outside
|
|
document.addEventListener('click', function(event) {
|
|
const languageButton = event.target.closest('button[onclick="toggleLanguageDropdown()"]');
|
|
const dropdown = document.getElementById('languageDropdown');
|
|
const isInsideDropdown = dropdown && dropdown.contains(event.target);
|
|
|
|
// Don't close if clicking the button or inside dropdown (except submit buttons)
|
|
if (!languageButton && !isInsideDropdown && dropdown) {
|
|
dropdown.classList.remove('active');
|
|
}
|
|
});
|
|
|
|
// Ensure dropdown stays open when form is being submitted
|
|
document.addEventListener('submit', function(event) {
|
|
if (event.target.closest('#languageDropdown')) {
|
|
// Form will submit normally, dropdown will close on page reload
|
|
}
|
|
});
|
|
</script>
|
|
|
|
{% block scripts %}{% endblock %}
|
|
</body>
|
|
</html>
|