From fdbb18c9df60cbfd00ae99d093b47903b76ecc95 Mon Sep 17 00:00:00 2001 From: infosave2007 Date: Sat, 24 Jan 2026 14:50:55 +0300 Subject: [PATCH] fix(qr): Pass raw VLESS URI in last_config without JSON wrapper --- inc/QrUtil.php | 4 ++-- inc/VpnClient.php | 45 +++++++++++++++++++++++++++++---------------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/inc/QrUtil.php b/inc/QrUtil.php index 39b4f44..c1d86ab 100644 --- a/inc/QrUtil.php +++ b/inc/QrUtil.php @@ -439,8 +439,8 @@ class QrUtil [ 'xray' => [ 'isThirdPartyConfig' => true, - // Wrap the raw VLESS URI in a "config" field inside last_config - 'last_config' => json_encode(['config' => $rawConfig], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), + // Pass raw VLESS URI directly, without JSON wrapper + 'last_config' => $rawConfig, 'port' => (string) $port, 'transport_proto' => 'tcp' ], diff --git a/inc/VpnClient.php b/inc/VpnClient.php index be9c515..eff2eb2 100644 --- a/inc/VpnClient.php +++ b/inc/VpnClient.php @@ -264,8 +264,7 @@ class VpnClient if (is_array($clients) && !empty($clients)) { $cid = $clients[0]['id'] ?? null; if (is_string($cid) && $cid !== '' && empty($vars['client_id'])) { - // $vars['client_id'] = $cid; - // DO NOT reuse existing ID. We want to create a NEW client. + $vars['client_id'] = $cid; } } $stream = $inbounds[0]['streamSettings'] ?? []; @@ -389,10 +388,7 @@ class VpnClient } // Ensure client_id (UUID) for X-Ray - if (stripos($slug, 'xray') !== false || stripos($slug, 'vless') !== false) { - // Force clear any pre-existing ID from extras - unset($vars['client_id']); - + if (empty($vars['client_id']) && (stripos($slug, 'xray') !== false || stripos($slug, 'vless') !== false)) { $data = random_bytes(16); $data[6] = chr(ord($data[6]) & 0x0f | 0x40); $data[8] = chr(ord($data[8]) & 0x3f | 0x80); @@ -404,16 +400,12 @@ class VpnClient // We pass generic options. InstallProtocolManager will handle specific logic for 'add_client' phase. // For xray-vless it uses builtin fallback in runScript. try { - error_log("DEBUG: Attempting to add client via IPM. Slug: " . ($protoRow['slug'] ?? 'N/A') . ", ClientUUID: " . ($vars['client_id'] ?? 'N/A')); require_once __DIR__ . '/InstallProtocolManager.php'; InstallProtocolManager::addClient($server, $protoRow, $vars); - error_log("DEBUG: IPM::addClient returned successfully"); } catch (Exception $e) { - error_log("DEBUG: Failed to add client to server: " . $e->getMessage()); + error_log("Failed to add client to server: " . $e->getMessage()); throw $e; } - } else { - error_log("DEBUG: protoRow is empty, cannot call IPM::addClient"); } $config = $protoRow ? ProtocolService::generateProtocolOutput($protoRow, $vars) : ''; @@ -967,11 +959,32 @@ class VpnClient try { // Check for X-Ray VLESS - // Check for X-Ray VLESS, VMess, Shadowsocks (Standard URI schemes) - if (strpos($config, 'vless://') === 0 || strpos($config, 'vmess://') === 0 || strpos($config, 'ss://') === 0) { - // Generate a standard QR code containing just the URI string. - // This is compatible with Amnezia VPB, v2rayNG, and other standard clients. - return QrUtil::pngBase64($config); + if (strpos($config, 'vless://') === 0) { + // Parse VLESS URI + $parsed = parse_url($config); + // Allow missing user (UUID) and port for partial configs + if ($parsed && isset($parsed['host'])) { + $host = $parsed['host']; + $port = isset($parsed['port']) ? (int) $parsed['port'] : 443; + $clientId = $parsed['user'] ?? ''; + $fragment = $parsed['fragment'] ?? ''; + + parse_str($parsed['query'] ?? '', $query); + + $reality = null; + if (($query['security'] ?? '') === 'reality') { + $reality = [ + 'publicKey' => $query['pbk'] ?? '', + 'serverName' => $query['sni'] ?? '', + 'shortId' => $query['sid'] ?? '', + 'fingerprint' => $query['fp'] ?? 'chrome' + ]; + } + + // Use QrUtil to encode correct X-Ray payload + $payloadXray = QrUtil::encodeXrayPayload($host, $port, $clientId, $fragment, $reality, $config); + return QrUtil::pngBase64($payloadXray); + } } // Fallback for WireGuard / default