Add project files

This commit is contained in:
infosave2007
2025-11-07 13:34:06 +03:00
parent 3402b19f2c
commit a33af60f2d
41 changed files with 8128 additions and 0 deletions
+134
View File
@@ -0,0 +1,134 @@
{% extends "layout.twig" %}
{% block title %}{{ server.name }}{% endblock %}
{% block content %}
<div class="max-w-7xl mx-auto px-4 py-8">
<div class="mb-6"><h1 class="text-3xl font-bold">{{ server.name }}</h1><p class="text-gray-600">{{ server.host }}</p></div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div class="bg-white rounded shadow p-6">
<h3 class="font-bold mb-4">Server Info</h3>
<dl class="space-y-2">
<div><dt class="text-sm text-gray-600">Status</dt><dd><span class="px-2 py-1 bg-green-100 text-green-800 rounded text-sm">{{ server.status }}</span></dd></div>
<div><dt class="text-sm text-gray-600">VPN Port</dt><dd>{{ server.vpn_port }}</dd></div>
<div><dt class="text-sm text-gray-600">Subnet</dt><dd>{{ server.vpn_subnet }}</dd></div>
</dl>
</div>
<div class="bg-white rounded shadow p-6">
<h3 class="font-bold mb-4">Create Client</h3>
<form method="POST" action="/servers/{{ server.id }}/clients/create" class="flex gap-2" id="createClientForm">
<input name="name" placeholder="Client name" required class="flex-1 px-3 py-2 border rounded" id="clientName">
<button type="submit" class="gradient-bg text-white px-4 py-2 rounded" id="createClientBtn">
<span id="createClientText">Create</span>
<i class="fas fa-spinner fa-spin" id="createClientSpinner" style="display:none;"></i>
</button>
</form>
</div>
</div>
<div class="bg-white rounded shadow">
<div class="px-6 py-4 border-b flex justify-between items-center">
<h3 class="font-bold">Clients ({{ clients|length }})</h3>
<button onclick="syncAllStats({{ server.id }})" class="text-purple-600 hover:text-purple-800 text-sm">
<i class="fas fa-sync-alt"></i> Sync Stats
</button>
</div>
{% if clients|length > 0 %}
<table class="w-full">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Name</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">IP</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Status</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Traffic</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Last Seen</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Actions</th>
</tr>
</thead>
<tbody>
{% for client in clients %}
<tr class="border-t">
<td class="px-6 py-4">{{ client.name }}</td>
<td class="px-6 py-4">{{ client.client_ip }}</td>
<td class="px-6 py-4">
{% if client.status == 'active' %}
<span class="px-2 py-1 bg-green-100 text-green-800 rounded text-xs">Active</span>
{% else %}
<span class="px-2 py-1 bg-red-100 text-red-800 rounded text-xs">Disabled</span>
{% endif %}
</td>
<td class="px-6 py-4 text-sm">
<div class="text-gray-600">
{{ (client.bytes_sent|default(0) / 1024 / 1024)|number_format(2) }} MB
</div>
<div class="text-gray-600">
{{ (client.bytes_received|default(0) / 1024 / 1024)|number_format(2) }} MB
</div>
</td>
<td class="px-6 py-4 text-sm">
{% if client.last_handshake %}
<span class="text-gray-600">{{ client.last_handshake }}</span>
{% else %}
<span class="text-gray-400">Never</span>
{% endif %}
</td>
<td class="px-6 py-4">
<a href="/clients/{{ client.id }}" class="text-purple-600 hover:text-purple-800 mr-2">View</a>
{% if client.status == 'active' %}
<form method="POST" action="/clients/{{ client.id }}/revoke" style="display:inline;">
<button type="submit" class="text-orange-600 hover:text-orange-800 mr-2" onclick="return confirm('Revoke access for this client?')">Revoke</button>
</form>
{% else %}
<form method="POST" action="/clients/{{ client.id }}/restore" style="display:inline;">
<button type="submit" class="text-green-600 hover:text-green-800 mr-2">Restore</button>
</form>
{% endif %}
<form method="POST" action="/clients/{{ client.id }}/delete" style="display:inline;">
<button type="submit" class="text-red-600 hover:text-red-800" onclick="return confirm('Delete this client permanently?')">Delete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="p-12 text-center text-gray-500">No clients yet</div>
{% endif %}
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('createClientForm');
if (form) {
form.addEventListener('submit', function(e) {
const btn = document.getElementById('createClientBtn');
const text = document.getElementById('createClientText');
const spinner = document.getElementById('createClientSpinner');
// Show spinner and disable button
btn.disabled = true;
text.style.display = 'none';
spinner.style.display = 'inline-block';
// Form will submit normally
});
}
});
async function syncAllStats(serverId) {
try {
const response = await fetch(`/servers/${serverId}/sync-stats`, {
method: 'POST'
});
const data = await response.json();
if (data.success) {
location.reload();
} else {
alert('Failed to sync stats: ' + (data.error || 'Unknown error'));
}
} catch (error) {
alert('Error: ' + error.message);
}
}
</script>
{% endblock %}