feat: Enforce 1 user 1 connection for X-ray and fix active stats speed

This commit is contained in:
infosave2007
2026-01-30 15:04:23 +03:00
parent 2f38cd58a3
commit 47b97d9a76
6 changed files with 94 additions and 3 deletions
+23
View File
@@ -1210,7 +1210,30 @@ class InstallProtocolManager
}
// 2. Modify config
// Ensure policy for 1 user 1 connection
if (!isset($config['policy'])) {
$config['policy'] = ['levels' => ['0' => []]];
}
if (!isset($config['policy']['levels'])) {
$config['policy']['levels'] = ['0' => []];
}
if (!isset($config['policy']['levels']['0'])) {
$config['policy']['levels']['0'] = [];
}
// Enforce limitIp: 1 for user level 0
$config['policy']['levels']['0']['handshake'] = 4;
$config['policy']['levels']['0']['connIdle'] = 300;
$config['policy']['levels']['0']['uplinkOnly'] = 2;
$config['policy']['levels']['0']['downlinkOnly'] = 5;
$config['policy']['levels']['0']['statsUserUplink'] = true;
$config['policy']['levels']['0']['statsUserDownlink'] = true;
$config['policy']['levels']['0']['bufferSize'] = 4;
$config['policy']['levels']['0']['limitIp'] = 1; // Enforce 1 IP per user
// Assuming VLESS structure: inbounds[0] -> settings -> clients
if (!isset($config['inbounds'][0]['settings']['clients'])) {
// Might be different structure? But we stick to standard Amnezia XRay config
if (!isset($config['inbounds'][0]['settings'])) {
+50 -2
View File
@@ -1529,6 +1529,17 @@ class VpnClient
}
try {
// Get previous stats for speed calculation
$pdo = DB::conn();
$stmtPrev = $pdo->prepare('SELECT bytes_sent, bytes_received, last_sync_at, last_handshake FROM vpn_clients WHERE id = ?');
$stmtPrev->execute([$this->clientId]);
$prev = $stmtPrev->fetch();
$prevSent = (int) ($prev['bytes_sent'] ?? 0);
$prevReceived = (int) ($prev['bytes_received'] ?? 0);
$prevSyncAt = $prev['last_sync_at'] ? strtotime($prev['last_sync_at']) : 0;
$prevHandshake = $prev['last_handshake'] ? strtotime($prev['last_handshake']) : 0;
// XRay stats logic
$stats = [];
@@ -1547,6 +1558,14 @@ class VpnClient
if ($identifier) {
$stats = self::getXrayStats($serverData, $identifier);
// Infer online status for XRay: if traffic increased, they are online.
// Update last_handshake to NOW() if activity detected.
if ($stats['bytes_sent'] > $prevSent || $stats['bytes_received'] > $prevReceived) {
$stats['last_handshake'] = time();
} else {
// Keep previous handshake if no new activity
$stats['last_handshake'] = $prevHandshake;
}
}
}
@@ -1555,10 +1574,36 @@ class VpnClient
$stats = self::getClientStatsFromServer($serverData, $this->data['public_key']);
}
$pdo = DB::conn();
// Calculate speeds (bytes per second)
$now = time();
$timeDiff = $now - $prevSyncAt;
$currentSpeed = 0;
$speedUp = 0;
$speedDown = 0;
if ($timeDiff > 0 && $prevSyncAt > 0) {
// Total speed
$bytesDiff = ($stats['bytes_sent'] + $stats['bytes_received']) - ($prevSent + $prevReceived);
if ($bytesDiff > 0) {
$currentSpeed = (int) ($bytesDiff / $timeDiff);
}
// Upload speed
$sentDiff = $stats['bytes_sent'] - $prevSent;
if ($sentDiff > 0) {
$speedUp = (int) ($sentDiff / $timeDiff);
}
// Download speed
$receivedDiff = $stats['bytes_received'] - $prevReceived;
if ($receivedDiff > 0) {
$speedDown = (int) ($receivedDiff / $timeDiff);
}
}
$stmt = $pdo->prepare('
UPDATE vpn_clients
SET bytes_sent = ?, bytes_received = ?, last_handshake = ?, last_sync_at = NOW()
SET bytes_sent = ?, bytes_received = ?, last_handshake = ?, current_speed = ?, speed_up = ?, speed_down = ?, last_sync_at = NOW()
WHERE id = ?
');
@@ -1570,6 +1615,9 @@ class VpnClient
$stats['bytes_sent'],
$stats['bytes_received'],
$lastHandshake,
$currentSpeed,
$speedUp,
$speedDown,
$this->clientId
]);
} catch (Exception $e) {
@@ -0,0 +1 @@
ALTER TABLE vpn_clients ADD COLUMN current_speed BIGINT DEFAULT 0 AFTER traffic_limit;
+3
View File
@@ -0,0 +1,3 @@
ALTER TABLE vpn_clients ADD COLUMN speed_up BIGINT DEFAULT 0 AFTER current_speed;
ALTER TABLE vpn_clients ADD COLUMN speed_down BIGINT DEFAULT 0 AFTER speed_up;
-- We can drop current_speed later or keep it as total
+13
View File
@@ -0,0 +1,13 @@
<?php
require_once __DIR__ . '/inc/Config.php';
Config::load(__DIR__ . '/.env');
require_once __DIR__ . '/inc/DB.php';
try {
$pdo = DB::conn();
$sql = file_get_contents(__DIR__ . '/migrations/053_split_speed.sql');
$pdo->exec($sql);
echo "Migration 053 applied successfully.\n";
} catch (Exception $e) {
echo "Migration failed: " . $e->getMessage() . "\n";
}
+4 -1
View File
@@ -335,7 +335,10 @@
{% endif %}
</td>
<td class="px-6 py-4 text-sm">
<div id="client-speed-{{ client.id }}" class="text-gray-400">-</div>
<div id="client-speed-{{ client.id }}" class="text-gray-600 text-xs">
<span class="text-green-600">↑ {{ (client.speed_up|default(0) / 1024)|number_format(1) }} KB/s</span><br>
<span class="text-blue-600">↓ {{ (client.speed_down|default(0) / 1024)|number_format(1) }} KB/s</span>
</div>
</td>
<td class="px-6 py-4 text-sm">
{% if client.last_handshake %}