feat: Enforce 1 user 1 connection for X-ray and fix active stats speed
This commit is contained in:
@@ -1210,7 +1210,30 @@ class InstallProtocolManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Modify config
|
// 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
|
// Assuming VLESS structure: inbounds[0] -> settings -> clients
|
||||||
|
|
||||||
if (!isset($config['inbounds'][0]['settings']['clients'])) {
|
if (!isset($config['inbounds'][0]['settings']['clients'])) {
|
||||||
// Might be different structure? But we stick to standard Amnezia XRay config
|
// Might be different structure? But we stick to standard Amnezia XRay config
|
||||||
if (!isset($config['inbounds'][0]['settings'])) {
|
if (!isset($config['inbounds'][0]['settings'])) {
|
||||||
|
|||||||
+50
-2
@@ -1529,6 +1529,17 @@ class VpnClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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
|
// XRay stats logic
|
||||||
$stats = [];
|
$stats = [];
|
||||||
|
|
||||||
@@ -1547,6 +1558,14 @@ class VpnClient
|
|||||||
|
|
||||||
if ($identifier) {
|
if ($identifier) {
|
||||||
$stats = self::getXrayStats($serverData, $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']);
|
$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('
|
$stmt = $pdo->prepare('
|
||||||
UPDATE vpn_clients
|
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 = ?
|
WHERE id = ?
|
||||||
');
|
');
|
||||||
|
|
||||||
@@ -1570,6 +1615,9 @@ class VpnClient
|
|||||||
$stats['bytes_sent'],
|
$stats['bytes_sent'],
|
||||||
$stats['bytes_received'],
|
$stats['bytes_received'],
|
||||||
$lastHandshake,
|
$lastHandshake,
|
||||||
|
$currentSpeed,
|
||||||
|
$speedUp,
|
||||||
|
$speedDown,
|
||||||
$this->clientId
|
$this->clientId
|
||||||
]);
|
]);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE vpn_clients ADD COLUMN current_speed BIGINT DEFAULT 0 AFTER traffic_limit;
|
||||||
@@ -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
|
||||||
@@ -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";
|
||||||
|
}
|
||||||
@@ -335,7 +335,10 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 text-sm">
|
<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>
|
||||||
<td class="px-6 py-4 text-sm">
|
<td class="px-6 py-4 text-sm">
|
||||||
{% if client.last_handshake %}
|
{% if client.last_handshake %}
|
||||||
|
|||||||
Reference in New Issue
Block a user