feat: Add real-time online client status updates for servers
This commit is contained in:
@@ -2401,6 +2401,40 @@ Router::get('/api/servers/{id}/clients', function ($params) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// API: Get online clients for a server (real-time)
|
||||||
|
Router::get('/api/servers/{id}/online', function ($params) {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$user = authenticateRequest();
|
||||||
|
if (!$user) {
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode(['error' => 'Unauthorized']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$serverId = (int) $params['id'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$server = new VpnServer($serverId);
|
||||||
|
$serverData = $server->getData();
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if ($serverData['user_id'] != $user['id'] && $user['role'] !== 'admin') {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['error' => 'Forbidden']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../inc/ServerMonitoring.php';
|
||||||
|
$onlineLogins = ServerMonitoring::getOnlineClientsForServer($serverData);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'online' => $onlineLogins]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// API: List server protocols
|
// API: List server protocols
|
||||||
Router::get('/api/servers/{id}/protocols', function ($params) {
|
Router::get('/api/servers/{id}/protocols', function ($params) {
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
|
|||||||
@@ -276,13 +276,13 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4">{{ client.client_ip }}</td>
|
<td class="px-6 py-4">{{ client.client_ip }}</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4" data-client-name="{{ client.name }}" data-client-status="{{ client.status }}">
|
||||||
{% if client.name in online_logins %}
|
{% if client.name in online_logins %}
|
||||||
<span class="px-2 py-1 bg-green-100 text-green-800 rounded text-xs"><i class="fas fa-wifi mr-1"></i>Online</span>
|
<span class="online-badge px-2 py-1 bg-green-100 text-green-800 rounded text-xs"><i class="fas fa-wifi mr-1"></i>Online</span>
|
||||||
{% elseif client.status == 'active' %}
|
{% elseif client.status == 'active' %}
|
||||||
<span class="px-2 py-1 bg-gray-100 text-gray-600 rounded text-xs">{{ t('status.active') }}</span>
|
<span class="status-badge px-2 py-1 bg-gray-100 text-gray-600 rounded text-xs">{{ t('status.active') }}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="px-2 py-1 bg-red-100 text-red-800 rounded text-xs">{{ t('status.disabled') }}</span>
|
<span class="status-badge px-2 py-1 bg-red-100 text-red-800 rounded text-xs">{{ t('status.disabled') }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 text-sm">
|
<td class="px-6 py-4 text-sm">
|
||||||
@@ -977,6 +977,38 @@ if (document.querySelector('[id^="client-speed-"]')) {
|
|||||||
updateClientSpeeds();
|
updateClientSpeeds();
|
||||||
setInterval(updateClientSpeeds, 30000);
|
setInterval(updateClientSpeeds, 30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Real-time online status updates
|
||||||
|
async function updateOnlineStatus() {
|
||||||
|
const serverId = {{ server.id }};
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/servers/${serverId}/online`, {
|
||||||
|
credentials: 'same-origin'
|
||||||
|
});
|
||||||
|
if (!response.ok) return;
|
||||||
|
const data = await response.json();
|
||||||
|
if (!data.success) return;
|
||||||
|
|
||||||
|
const onlineSet = new Set(data.online);
|
||||||
|
document.querySelectorAll('td[data-client-name]').forEach(cell => {
|
||||||
|
const clientName = cell.dataset.clientName;
|
||||||
|
const clientStatus = cell.dataset.clientStatus;
|
||||||
|
|
||||||
|
if (onlineSet.has(clientName)) {
|
||||||
|
cell.innerHTML = '<span class="online-badge px-2 py-1 bg-green-100 text-green-800 rounded text-xs"><i class="fas fa-wifi mr-1"></i>Online</span>';
|
||||||
|
} else if (clientStatus === 'active') {
|
||||||
|
cell.innerHTML = '<span class="status-badge px-2 py-1 bg-gray-100 text-gray-600 rounded text-xs">{{ t("status.active") }}</span>';
|
||||||
|
} else {
|
||||||
|
cell.innerHTML = '<span class="status-badge px-2 py-1 bg-red-100 text-red-800 rounded text-xs">{{ t("status.disabled") }}</span>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to update online status:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll every 5 seconds
|
||||||
|
setInterval(updateOnlineStatus, 5000);
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user