Spaces:
Runtime error
Runtime error
| {% extends "base.html" %} | |
| {% block title %}Server Administration - Outline VPN{% endblock %} | |
| {% block content %} | |
| <div class="dashboard-container"> | |
| {% include 'sidebar.html' %} | |
| <main class="main-content"> | |
| <div class="container"> | |
| <div class="d-flex justify-content-between align-items-center mb-4"> | |
| <h1 class="h3">Server Administration</h1> | |
| <div class="btn-group"> | |
| <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#backupModal"> | |
| <i class="bi bi-download me-2"></i>Backup Configuration | |
| </button> | |
| <button class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#restoreModal"> | |
| <i class="bi bi-upload me-2"></i>Restore | |
| </button> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <!-- Server Configuration --> | |
| <div class="col-lg-8"> | |
| <div class="dashboard-card"> | |
| <h2 class="h5 mb-4">Server Configuration</h2> | |
| <form id="serverConfigForm" action="{{ url_for('update_server_config') }}" method="POST"> | |
| <!-- Network Settings --> | |
| <div class="mb-4"> | |
| <h3 class="h6 mb-3">Network Settings</h3> | |
| <div class="row g-3"> | |
| <div class="col-md-6"> | |
| <label class="form-label">Server Port</label> | |
| <input type="number" class="form-control" name="port" value="{{ config.port }}" required> | |
| </div> | |
| <div class="col-md-6"> | |
| <label class="form-label">Access Method</label> | |
| <select class="form-select" name="access_method"> | |
| <option value="direct" {% if config.access_method == 'direct' %}selected{% endif %}>Direct</option> | |
| <option value="proxy" {% if config.access_method == 'proxy' %}selected{% endif %}>Proxy</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Protocol Settings --> | |
| <div class="mb-4"> | |
| <h3 class="h6 mb-3">Protocol Settings</h3> | |
| <div class="row g-3"> | |
| <div class="col-md-6"> | |
| <label class="form-label">Default Protocol</label> | |
| <select class="form-select" name="default_protocol"> | |
| <option value="shadowsocks" {% if config.default_protocol == 'shadowsocks' %}selected{% endif %}>Shadowsocks</option> | |
| <option value="wireguard" {% if config.default_protocol == 'wireguard' %}selected{% endif %}>WireGuard</option> | |
| <option value="openvpn" {% if config.default_protocol == 'openvpn' %}selected{% endif %}>OpenVPN</option> | |
| </select> | |
| </div> | |
| <div class="col-md-6"> | |
| <label class="form-label">Encryption Method</label> | |
| <select class="form-select" name="encryption_method"> | |
| <option value="aes-256-gcm" {% if config.encryption_method == 'aes-256-gcm' %}selected{% endif %}>AES-256-GCM</option> | |
| <option value="chacha20-poly1305" {% if config.encryption_method == 'chacha20-poly1305' %}selected{% endif %}>ChaCha20-Poly1305</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Security Settings --> | |
| <div class="mb-4"> | |
| <h3 class="h6 mb-3">Security Settings</h3> | |
| <div class="row g-3"> | |
| <div class="col-md-6"> | |
| <label class="form-label">Session Timeout (minutes)</label> | |
| <input type="number" class="form-control" name="session_timeout" value="{{ config.session_timeout }}" required> | |
| </div> | |
| <div class="col-md-6"> | |
| <label class="form-label">Max Failed Attempts</label> | |
| <input type="number" class="form-control" name="max_failed_attempts" value="{{ config.max_failed_attempts }}" required> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="text-end"> | |
| <button type="submit" class="btn btn-primary">Save Configuration</button> | |
| </div> | |
| </form> | |
| </div> | |
| <!-- Audit Log --> | |
| <div class="dashboard-card mt-4"> | |
| <div class="d-flex justify-content-between align-items-center mb-4"> | |
| <h2 class="h5 mb-0">Audit Log</h2> | |
| <div class="btn-group"> | |
| <button class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown"> | |
| Export | |
| </button> | |
| <ul class="dropdown-menu"> | |
| <li><a class="dropdown-item" href="{{ url_for('export_audit_log', format='csv') }}">CSV</a></li> | |
| <li><a class="dropdown-item" href="{{ url_for('export_audit_log', format='json') }}">JSON</a></li> | |
| </ul> | |
| </div> | |
| </div> | |
| <div class="table-responsive"> | |
| <table class="table"> | |
| <thead> | |
| <tr> | |
| <th>Timestamp</th> | |
| <th>User</th> | |
| <th>Action</th> | |
| <th>Details</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {% for log in audit_logs %} | |
| <tr> | |
| <td>{{ log.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</td> | |
| <td>{{ log.username }}</td> | |
| <td>{{ log.action }}</td> | |
| <td>{{ log.details }}</td> | |
| </tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- System Health --> | |
| <div class="col-lg-4"> | |
| <!-- Real-time Monitoring --> | |
| <div class="dashboard-card"> | |
| <h2 class="h5 mb-4">System Health</h2> | |
| <div class="mb-4"> | |
| <label class="form-label d-flex justify-content-between"> | |
| CPU Usage | |
| <span class="text-{{ 'success' if system_health.cpu_usage < 70 else 'warning' if system_health.cpu_usage < 90 else 'danger' }}"> | |
| {{ system_health.cpu_usage }}% | |
| </span> | |
| </label> | |
| <div class="progress"> | |
| <div class="progress-bar bg-{{ 'success' if system_health.cpu_usage < 70 else 'warning' if system_health.cpu_usage < 90 else 'danger' }}" | |
| role="progressbar" style="width: {{ system_health.cpu_usage }}%"></div> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="form-label d-flex justify-content-between"> | |
| Memory Usage | |
| <span class="text-{{ 'success' if system_health.memory_usage < 70 else 'warning' if system_health.memory_usage < 90 else 'danger' }}"> | |
| {{ system_health.memory_usage }}% | |
| </span> | |
| </label> | |
| <div class="progress"> | |
| <div class="progress-bar bg-{{ 'success' if system_health.memory_usage < 70 else 'warning' if system_health.memory_usage < 90 else 'danger' }}" | |
| role="progressbar" style="width: {{ system_health.memory_usage }}%"></div> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="form-label d-flex justify-content-between"> | |
| Disk Usage | |
| <span class="text-{{ 'success' if system_health.disk_usage < 70 else 'warning' if system_health.disk_usage < 90 else 'danger' }}"> | |
| {{ system_health.disk_usage }}% | |
| </span> | |
| </label> | |
| <div class="progress"> | |
| <div class="progress-bar bg-{{ 'success' if system_health.disk_usage < 70 else 'warning' if system_health.disk_usage < 90 else 'danger' }}" | |
| role="progressbar" style="width: {{ system_health.disk_usage }}%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Active Alerts --> | |
| <div class="dashboard-card mt-4"> | |
| <h2 class="h5 mb-4">Active Alerts</h2> | |
| <div class="alerts-list"> | |
| {% for alert in active_alerts %} | |
| <div class="alert alert-{{ alert.severity }} d-flex align-items-center"> | |
| <i class="bi bi-exclamation-triangle me-2"></i> | |
| <div> | |
| <strong>{{ alert.title }}</strong> | |
| <p class="mb-0 small">{{ alert.message }}</p> | |
| <small class="text-muted">{{ alert.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</small> | |
| </div> | |
| </div> | |
| {% endfor %} | |
| {% if not active_alerts %} | |
| <p class="text-muted text-center mb-0">No active alerts</p> | |
| {% endif %} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <!-- Backup Modal --> | |
| <div class="modal fade" id="backupModal" tabindex="-1"> | |
| <div class="modal-dialog"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h5 class="modal-title">Backup Configuration</h5> | |
| <button type="button" class="btn-close" data-bs-dismiss="modal"></button> | |
| </div> | |
| <div class="modal-body"> | |
| <p>This will create a backup of your entire server configuration, including:</p> | |
| <ul> | |
| <li>Server settings</li> | |
| <li>User configurations</li> | |
| <li>Security settings</li> | |
| <li>Protocol configurations</li> | |
| </ul> | |
| <div class="form-check mb-3"> | |
| <input class="form-check-input" type="checkbox" id="includeUserData"> | |
| <label class="form-check-label" for="includeUserData"> | |
| Include user data and statistics | |
| </label> | |
| </div> | |
| </div> | |
| <div class="modal-footer"> | |
| <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> | |
| <button type="button" class="btn btn-primary" onclick="createBackup()">Create Backup</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Restore Modal --> | |
| <div class="modal fade" id="restoreModal" tabindex="-1"> | |
| <div class="modal-dialog"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h5 class="modal-title">Restore Configuration</h5> | |
| <button type="button" class="btn-close" data-bs-dismiss="modal"></button> | |
| </div> | |
| <form action="{{ url_for('restore_config') }}" method="POST" enctype="multipart/form-data"> | |
| <div class="modal-body"> | |
| <div class="mb-3"> | |
| <label class="form-label">Select Backup File</label> | |
| <input type="file" class="form-control" name="backup_file" accept=".zip,.json" required> | |
| </div> | |
| <div class="alert alert-warning"> | |
| <i class="bi bi-exclamation-triangle me-2"></i> | |
| Restoring a backup will overwrite your current configuration. This action cannot be undone. | |
| </div> | |
| </div> | |
| <div class="modal-footer"> | |
| <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> | |
| <button type="submit" class="btn btn-primary">Restore Configuration</button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| {% endblock %} | |
| {% block extra_js %} | |
| <script> | |
| // Real-time system health updates | |
| function updateSystemHealth() { | |
| fetch('/api/system-health') | |
| .then(response => response.json()) | |
| .then(data => { | |
| updateProgressBar('cpu-usage', data.cpu_usage); | |
| updateProgressBar('memory-usage', data.memory_usage); | |
| updateProgressBar('disk-usage', data.disk_usage); | |
| }); | |
| } | |
| function updateProgressBar(id, value) { | |
| const bar = document.getElementById(id); | |
| const label = bar.previousElementSibling; | |
| bar.style.width = value + '%'; | |
| label.textContent = value + '%'; | |
| const colorClass = value < 70 ? 'success' : value < 90 ? 'warning' : 'danger'; | |
| bar.className = `progress-bar bg-${colorClass}`; | |
| } | |
| // Create backup | |
| function createBackup() { | |
| const includeUserData = document.getElementById('includeUserData').checked; | |
| window.location.href = `{{ url_for('create_backup') }}?include_user_data=${includeUserData}`; | |
| } | |
| // Initialize real-time updates | |
| setInterval(updateSystemHealth, 5000); | |
| </script> | |
| {% endblock %} | |