first commit
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Verbundene Clients - {{ server.name }} - mGuard VPN{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<nav aria-label="breadcrumb" class="mb-3">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/vpn-servers">VPN-Server</a></li>
|
||||
<li class="breadcrumb-item"><a href="/vpn-servers/{{ server.id }}">{{ server.name }}</a></li>
|
||||
<li class="breadcrumb-item active">Clients</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>
|
||||
<i class="bi bi-people"></i> Verbundene Clients
|
||||
<span class="badge bg-primary">{{ clients|length }}</span>
|
||||
</h1>
|
||||
<a href="/vpn-servers/{{ server.id }}" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Zurück zum Server
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{% if clients %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Common Name</th>
|
||||
<th>Echte Adresse</th>
|
||||
<th>Empfangen</th>
|
||||
<th>Gesendet</th>
|
||||
<th>Verbunden seit</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for client in clients %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ client.common_name }}</strong>
|
||||
</td>
|
||||
<td><code>{{ client.real_address }}</code></td>
|
||||
<td>{{ (client.bytes_received / 1024 / 1024)|round(2) }} MB</td>
|
||||
<td>{{ (client.bytes_sent / 1024 / 1024)|round(2) }} MB</td>
|
||||
<td>{{ client.connected_since }}</td>
|
||||
<td>
|
||||
<form action="/vpn-servers/{{ server.id }}/disconnect/{{ client.common_name }}" method="post"
|
||||
onsubmit="return confirm('Client wirklich trennen?');">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-x-circle"></i> Trennen
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-5 text-muted">
|
||||
<i class="bi bi-people" style="font-size: 3rem;"></i>
|
||||
<p class="mt-3">Keine Clients verbunden</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,260 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ server.name }} - VPN-Server - mGuard VPN{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<nav aria-label="breadcrumb" class="mb-3">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/vpn-servers">VPN-Server</a></li>
|
||||
<li class="breadcrumb-item active">{{ server.name }}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>
|
||||
{% if server.status.value == 'running' %}
|
||||
<span class="status-indicator online"></span>
|
||||
{% else %}
|
||||
<span class="status-indicator offline"></span>
|
||||
{% endif %}
|
||||
{{ server.name }}
|
||||
{% if server.is_primary %}
|
||||
<span class="badge bg-primary">Primär</span>
|
||||
{% endif %}
|
||||
{% if server.protocol.value == 'udp' %}
|
||||
<span class="badge badge-udp">UDP</span>
|
||||
{% else %}
|
||||
<span class="badge badge-tcp">TCP</span>
|
||||
{% endif %}
|
||||
</h1>
|
||||
<div>
|
||||
<a href="/vpn-servers/{{ server.id }}/clients" class="btn btn-outline-primary">
|
||||
<i class="bi bi-people"></i> Clients ({{ status.connected_clients or 0 }})
|
||||
</a>
|
||||
<a href="/vpn-servers/{{ server.id }}/edit" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-pencil"></i> Bearbeiten
|
||||
</a>
|
||||
<form action="/vpn-servers/{{ server.id }}/delete" method="post" class="d-inline"
|
||||
onsubmit="return confirm('VPN-Server wirklich löschen?');">
|
||||
<button type="submit" class="btn btn-outline-danger">
|
||||
<i class="bi bi-trash"></i> Löschen
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Status Card -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="bi bi-activity"></i> Status</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-5">Status</dt>
|
||||
<dd class="col-sm-7">
|
||||
{% if server.status.value == 'running' %}
|
||||
<span class="badge bg-success">Läuft</span>
|
||||
{% elif server.status.value == 'stopped' %}
|
||||
<span class="badge bg-secondary">Gestoppt</span>
|
||||
{% elif server.status.value == 'starting' %}
|
||||
<span class="badge bg-warning">Startet...</span>
|
||||
{% elif server.status.value == 'error' %}
|
||||
<span class="badge bg-danger">Fehler</span>
|
||||
{% else %}
|
||||
<span class="badge bg-light text-dark">{{ server.status.value }}</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-5">Verbundene Clients</dt>
|
||||
<dd class="col-sm-7">{{ status.connected_clients or 0 }}</dd>
|
||||
|
||||
<dt class="col-sm-5">Letzte Prüfung</dt>
|
||||
<dd class="col-sm-7">
|
||||
{{ server.last_status_check.strftime('%d.%m.%Y %H:%M') if server.last_status_check else '-' }}
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-5">Container</dt>
|
||||
<dd class="col-sm-7"><code>{{ server.docker_container_name or '-' }}</code></dd>
|
||||
|
||||
<dt class="col-sm-5">Management-Port</dt>
|
||||
<dd class="col-sm-7">{{ server.management_port }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Network Card -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="bi bi-hdd-network"></i> Netzwerk</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-5">Adresse</dt>
|
||||
<dd class="col-sm-7"><code>{{ server.hostname }}:{{ server.port }}</code></dd>
|
||||
|
||||
<dt class="col-sm-5">Protokoll</dt>
|
||||
<dd class="col-sm-7">{{ server.protocol.value.upper() }}</dd>
|
||||
|
||||
<dt class="col-sm-5">VPN-Netzwerk</dt>
|
||||
<dd class="col-sm-7">{{ server.vpn_network }}/{{ server.vpn_netmask }}</dd>
|
||||
|
||||
<dt class="col-sm-5">Max Clients</dt>
|
||||
<dd class="col-sm-7">{{ server.max_clients }}</dd>
|
||||
|
||||
<dt class="col-sm-5">Keepalive</dt>
|
||||
<dd class="col-sm-7">{{ server.keepalive_interval }}s / {{ server.keepalive_timeout }}s</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Security Card -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="bi bi-shield-lock"></i> Sicherheit</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-5">CA</dt>
|
||||
<dd class="col-sm-7">
|
||||
<a href="/ca/{{ server.certificate_authority.id }}">
|
||||
{{ server.certificate_authority.name }}
|
||||
</a>
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-5">Cipher</dt>
|
||||
<dd class="col-sm-7">{{ server.cipher.value }}</dd>
|
||||
|
||||
<dt class="col-sm-5">Auth</dt>
|
||||
<dd class="col-sm-7">{{ server.auth.value }}</dd>
|
||||
|
||||
<dt class="col-sm-5">TLS Version</dt>
|
||||
<dd class="col-sm-7">>= {{ server.tls_version_min }}</dd>
|
||||
|
||||
<dt class="col-sm-5">TLS-Auth</dt>
|
||||
<dd class="col-sm-7">
|
||||
{% if server.tls_auth_enabled %}
|
||||
<span class="badge bg-success">Aktiviert</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Deaktiviert</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-5">Kompression</dt>
|
||||
<dd class="col-sm-7">{{ server.compression.value }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Profiles Card -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="bi bi-person-vcard"></i> VPN-Profile</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if server.vpn_profiles %}
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for profile in server.vpn_profiles[:10] %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center px-0">
|
||||
<span>
|
||||
<a href="/gateways/{{ profile.gateway_id }}/profiles/{{ profile.id }}">
|
||||
{{ profile.gateway.name }} - {{ profile.name }}
|
||||
</a>
|
||||
</span>
|
||||
{% if profile.status.value == 'active' %}
|
||||
<span class="badge bg-success">Aktiv</span>
|
||||
{% elif profile.status.value == 'provisioned' %}
|
||||
<span class="badge bg-info">Provisioniert</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ profile.status.value }}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if server.vpn_profiles|length > 10 %}
|
||||
<p class="text-muted small mt-2 mb-0">... und {{ server.vpn_profiles|length - 10 }} weitere</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="text-muted mb-0">Keine Profile verwenden diesen Server.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Server Log -->
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-terminal"></i> Server-Log</h5>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="refreshLog()">
|
||||
<i class="bi bi-arrow-clockwise"></i> Aktualisieren
|
||||
</button>
|
||||
<a href="/api/internal/vpn-servers/{{ server.id }}/logs/raw?lines=500"
|
||||
class="btn btn-sm btn-outline-primary" target="_blank">
|
||||
<i class="bi bi-download"></i> Download
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="server-log" class="bg-dark text-light p-3"
|
||||
style="max-height: 400px; overflow-y: auto; font-family: monospace; font-size: 12px;">
|
||||
<div class="text-center py-4">
|
||||
<div class="spinner-border spinner-border-sm text-light" role="status">
|
||||
<span class="visually-hidden">Laden...</span>
|
||||
</div>
|
||||
<span class="ms-2">Log wird geladen...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
async function refreshLog() {
|
||||
const container = document.getElementById('server-log');
|
||||
try {
|
||||
const response = await fetch('/api/internal/vpn-servers/{{ server.id }}/logs?lines=100');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.lines && data.lines.length > 0) {
|
||||
container.innerHTML = data.lines.map(line => {
|
||||
// Color-code log levels
|
||||
let lineClass = '';
|
||||
if (line.includes('ERROR') || line.includes('error')) lineClass = 'text-danger';
|
||||
else if (line.includes('WARN') || line.includes('warning')) lineClass = 'text-warning';
|
||||
else if (line.includes('INFO')) lineClass = 'text-info';
|
||||
return `<div class="${lineClass}">${escapeHtml(line)}</div>`;
|
||||
}).join('');
|
||||
container.scrollTop = container.scrollHeight;
|
||||
} else {
|
||||
container.innerHTML = '<div class="text-muted">Keine Log-Einträge vorhanden</div>';
|
||||
}
|
||||
} catch (error) {
|
||||
container.innerHTML = `<div class="text-danger">Fehler beim Laden: ${error.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// Load log on page load
|
||||
document.addEventListener('DOMContentLoaded', refreshLog);
|
||||
|
||||
// Auto-refresh every 10 seconds
|
||||
setInterval(refreshLog, 10000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,204 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{% if server %}VPN-Server bearbeiten{% else %}Neuer VPN-Server{% endif %} - mGuard VPN{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">
|
||||
{% if server %}
|
||||
<i class="bi bi-pencil"></i> VPN-Server bearbeiten
|
||||
{% else %}
|
||||
<i class="bi bi-plus-lg"></i> Neuer VPN-Server
|
||||
{% endif %}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{% if server %}/vpn-servers/{{ server.id }}/edit{% else %}/vpn-servers/new{% endif %}" method="post">
|
||||
|
||||
<!-- Basic Settings -->
|
||||
<h6 class="text-muted mb-3">Grundeinstellungen</h6>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Name *</label>
|
||||
<input type="text" name="name" class="form-control" required
|
||||
value="{{ server.name if server else '' }}"
|
||||
placeholder="z.B. Produktion UDP">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Certificate Authority *</label>
|
||||
{% if server %}
|
||||
<input type="text" class="form-control" disabled
|
||||
value="{{ server.certificate_authority.name }}">
|
||||
<input type="hidden" name="ca_id" value="{{ server.ca_id }}">
|
||||
{% else %}
|
||||
<select name="ca_id" class="form-select" required>
|
||||
<option value="">-- CA auswählen --</option>
|
||||
{% for ca in cas %}
|
||||
<option value="{{ ca.id }}">{{ ca.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Beschreibung</label>
|
||||
<textarea name="description" class="form-control" rows="2"
|
||||
placeholder="Optionale Beschreibung">{{ server.description if server else '' }}</textarea>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h6 class="text-muted mb-3">Netzwerk</h6>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Hostname/IP *</label>
|
||||
<input type="text" name="hostname" class="form-control" required
|
||||
value="{{ server.hostname if server else '' }}"
|
||||
placeholder="vpn.meinefirma.de">
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label">Port *</label>
|
||||
<input type="number" name="port" class="form-control" required
|
||||
value="{{ server.port if server else 1194 }}"
|
||||
min="1" max="65535">
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label">Protokoll *</label>
|
||||
<select name="protocol" class="form-select">
|
||||
<option value="udp" {% if not server or server.protocol.value == 'udp' %}selected{% endif %}>UDP</option>
|
||||
<option value="tcp" {% if server and server.protocol.value == 'tcp' %}selected{% endif %}>TCP</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">VPN-Netzwerk</label>
|
||||
<input type="text" name="vpn_network" class="form-control"
|
||||
value="{{ server.vpn_network if server else '10.8.0.0' }}"
|
||||
placeholder="10.8.0.0">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Netzmaske</label>
|
||||
<input type="text" name="vpn_netmask" class="form-control"
|
||||
value="{{ server.vpn_netmask if server else '255.255.255.0' }}"
|
||||
placeholder="255.255.255.0">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h6 class="text-muted mb-3">Sicherheit</h6>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">Cipher</label>
|
||||
<select name="cipher" class="form-select">
|
||||
{% for c in ciphers %}
|
||||
<option value="{{ c.value }}" {% if server and server.cipher.value == c.value %}selected{% elif not server and c.value == 'AES-256-GCM' %}selected{% endif %}>
|
||||
{{ c.value }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">Auth</label>
|
||||
<select name="auth" class="form-select">
|
||||
{% for a in auth_methods %}
|
||||
<option value="{{ a.value }}" {% if server and server.auth.value == a.value %}selected{% elif not server and a.value == 'SHA256' %}selected{% endif %}>
|
||||
{{ a.value }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">TLS Version min</label>
|
||||
<select name="tls_version_min" class="form-select">
|
||||
<option value="1.2" {% if not server or server.tls_version_min == '1.2' %}selected{% endif %}>1.2</option>
|
||||
<option value="1.3" {% if server and server.tls_version_min == '1.3' %}selected{% endif %}>1.3</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h6 class="text-muted mb-3">Performance</h6>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label">Max Clients</label>
|
||||
<input type="number" name="max_clients" class="form-control"
|
||||
value="{{ server.max_clients if server else 100 }}"
|
||||
min="1" max="1000">
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label">Keepalive (s)</label>
|
||||
<input type="number" name="keepalive_interval" class="form-control"
|
||||
value="{{ server.keepalive_interval if server else 10 }}"
|
||||
min="1" max="60">
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label">Timeout (s)</label>
|
||||
<input type="number" name="keepalive_timeout" class="form-control"
|
||||
value="{{ server.keepalive_timeout if server else 60 }}"
|
||||
min="10" max="300">
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label">Kompression</label>
|
||||
<select name="compression" class="form-select">
|
||||
{% for co in compression_options %}
|
||||
<option value="{{ co.value }}" {% if server and server.compression.value == co.value %}selected{% elif not server and co.value == 'none' %}selected{% endif %}>
|
||||
{{ co.value }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h6 class="text-muted mb-3">Docker</h6>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Management-Port</label>
|
||||
<input type="number" name="management_port" class="form-control"
|
||||
value="{{ server.management_port if server else 7505 }}"
|
||||
min="1" max="65535">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3 d-flex align-items-end">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" name="is_primary" class="form-check-input" id="isPrimary"
|
||||
{% if server and server.is_primary %}checked{% endif %}>
|
||||
<label class="form-check-label" for="isPrimary">Primärer Server</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if server %}
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3 d-flex align-items-end">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" name="is_active" class="form-check-input" id="isActive"
|
||||
{% if server.is_active %}checked{% endif %}>
|
||||
<label class="form-check-label" for="isActive">Server aktiv</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-check-lg"></i>
|
||||
{% if server %}Speichern{% else %}VPN-Server erstellen{% endif %}
|
||||
</button>
|
||||
<a href="/vpn-servers" class="btn btn-outline-secondary">Abbrechen</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,93 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}VPN-Server - mGuard VPN{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1><i class="bi bi-server"></i> VPN-Server</h1>
|
||||
<a href="/vpn-servers/new" class="btn btn-primary">
|
||||
<i class="bi bi-plus-lg"></i> Neuer VPN-Server
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
{% for server in servers %}
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<div class="card h-100 {% if server.is_primary %}border-primary{% endif %}">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
{% if server.status.value == 'running' %}
|
||||
<span class="status-indicator online"></span>
|
||||
{% else %}
|
||||
<span class="status-indicator offline"></span>
|
||||
{% endif %}
|
||||
{{ server.name }}
|
||||
</span>
|
||||
<div>
|
||||
{% if server.is_primary %}
|
||||
<span class="badge bg-primary">Primär</span>
|
||||
{% endif %}
|
||||
{% if server.protocol.value == 'udp' %}
|
||||
<span class="badge badge-udp">UDP</span>
|
||||
{% else %}
|
||||
<span class="badge badge-tcp">TCP</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small">{{ server.description or 'Keine Beschreibung' }}</p>
|
||||
|
||||
<!-- Connection Info -->
|
||||
<div class="mb-3">
|
||||
<code>{{ server.hostname }}:{{ server.port }}</code>
|
||||
</div>
|
||||
|
||||
<!-- Status -->
|
||||
<div class="mb-2">
|
||||
{% if server.status.value == 'running' %}
|
||||
<span class="badge bg-success">Läuft</span>
|
||||
<span class="badge bg-light text-dark">{{ server.connected_clients }} Clients</span>
|
||||
{% elif server.status.value == 'stopped' %}
|
||||
<span class="badge bg-secondary">Gestoppt</span>
|
||||
{% elif server.status.value == 'starting' %}
|
||||
<span class="badge bg-warning">Startet...</span>
|
||||
{% elif server.status.value == 'error' %}
|
||||
<span class="badge bg-danger">Fehler</span>
|
||||
{% else %}
|
||||
<span class="badge bg-light text-dark">{{ server.status.value }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Details -->
|
||||
<ul class="list-unstyled small mb-0">
|
||||
<li><strong>Netzwerk:</strong> {{ server.vpn_network }}/{{ server.vpn_netmask }}</li>
|
||||
<li><strong>Cipher:</strong> {{ server.cipher.value }}</li>
|
||||
<li><strong>CA:</strong> {{ server.certificate_authority.name if server.certificate_authority else '-' }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="/vpn-servers/{{ server.id }}" class="btn btn-sm btn-primary">
|
||||
<i class="bi bi-eye"></i> Details
|
||||
</a>
|
||||
<a href="/vpn-servers/{{ server.id }}/clients" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-people"></i> Clients
|
||||
</a>
|
||||
<a href="/vpn-servers/{{ server.id }}/edit" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
Keine VPN-Server vorhanden.
|
||||
<a href="/vpn-servers/new">Erstellen Sie einen neuen VPN-Server</a>.
|
||||
<br>
|
||||
<small class="text-muted">Hinweis: Sie benötigen zuerst eine <a href="/ca">Certificate Authority</a>.</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user