diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d68ea808..a53ccdc0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,6 +18,7 @@ on: - 'go.mod' - 'go.sum' - 'x-ui.service.debian' + - 'x-ui.service.arch' - 'x-ui.service.rhel' jobs: @@ -80,6 +81,7 @@ jobs: mkdir x-ui cp xui-release x-ui/ cp x-ui.service.debian x-ui/ + cp x-ui.service.arch x-ui/ cp x-ui.service.rhel x-ui/ cp x-ui.sh x-ui/ mv x-ui/xui-release x-ui/x-ui @@ -223,4 +225,4 @@ jobs: file: x-ui-windows-amd64.zip asset_name: x-ui-windows-amd64.zip overwrite: true - prerelease: true \ No newline at end of file + prerelease: true diff --git a/install.sh b/install.sh index d8e95e22..89ddf2a0 100644 --- a/install.sh +++ b/install.sh @@ -818,6 +818,15 @@ install_x-ui() { fi fi ;; + arch | manjaro | parch) + if [ -f "x-ui.service.arch" ]; then + echo -e "${green}Found x-ui.service.arch in extracted files, installing...${plain}" + cp -f x-ui.service.arch ${xui_service}/x-ui.service >/dev/null 2>&1 + if [[ $? -eq 0 ]]; then + service_installed=true + fi + fi + ;; *) if [ -f "x-ui.service.rhel" ]; then echo -e "${green}Found x-ui.service.rhel in extracted files, installing...${plain}" @@ -837,6 +846,9 @@ install_x-ui() { ubuntu | debian | armbian) curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.debian >/dev/null 2>&1 ;; + arch | manjaro | parch) + curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.arch >/dev/null 2>&1 + ;; *) curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.rhel >/dev/null 2>&1 ;; diff --git a/update.sh b/update.sh index 91c37c37..58206984 100755 --- a/update.sh +++ b/update.sh @@ -737,6 +737,7 @@ update_x-ui() { rm ${xui_folder} -f >/dev/null 2>&1 rm ${xui_folder}/x-ui.service -f >/dev/null 2>&1 rm ${xui_folder}/x-ui.service.debian -f >/dev/null 2>&1 + rm ${xui_folder}/x-ui.service.arch -f >/dev/null 2>&1 rm ${xui_folder}/x-ui.service.rhel -f >/dev/null 2>&1 rm ${xui_folder}/x-ui -f >/dev/null 2>&1 rm ${xui_folder}/x-ui.sh -f >/dev/null 2>&1 @@ -819,6 +820,15 @@ update_x-ui() { fi fi ;; + arch | manjaro | parch) + if [ -f "x-ui.service.arch" ]; then + echo -e "${green}Installing arch-like systemd unit...${plain}" + cp -f x-ui.service.arch ${xui_service}/x-ui.service >/dev/null 2>&1 + if [[ $? -eq 0 ]]; then + service_installed=true + fi + fi + ;; *) if [ -f "x-ui.service.rhel" ]; then echo -e "${green}Installing rhel-like systemd unit...${plain}" @@ -837,6 +847,9 @@ update_x-ui() { ubuntu | debian | armbian) ${curl_bin} -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.debian >/dev/null 2>&1 ;; + arch | manjaro | parch) + ${curl_bin} -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.arch >/dev/null 2>&1 + ;; *) ${curl_bin} -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.rhel >/dev/null 2>&1 ;; diff --git a/web/assets/js/websocket.js b/web/assets/js/websocket.js index 5b8a3948..ccafef87 100644 --- a/web/assets/js/websocket.js +++ b/web/assets/js/websocket.js @@ -14,10 +14,12 @@ class WebSocketClient { } connect() { - if (this.ws && this.ws.readyState === WebSocket.OPEN) { + if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { return; } + this.shouldReconnect = true; + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; // Ensure basePath ends with '/' for proper URL construction let basePath = this.basePath || ''; @@ -97,7 +99,10 @@ class WebSocketClient { if (!this.listeners.has(event)) { this.listeners.set(event, []); } - this.listeners.get(event).push(callback); + const callbacks = this.listeners.get(event); + if (!callbacks.includes(callback)) { + callbacks.push(callback); + } } off(event, callback) { diff --git a/web/html/inbounds.html b/web/html/inbounds.html index 0156f9bd..f6f2b852 100644 --- a/web/html/inbounds.html +++ b/web/html/inbounds.html @@ -1618,7 +1618,6 @@ if (payload && Array.isArray(payload)) { // Use setInbounds to properly convert to DBInbound objects with methods this.setInbounds(payload); - this.searchInbounds(this.searchKey); } }); @@ -1630,14 +1629,31 @@ // Update online clients list in real-time if (payload && Array.isArray(payload.onlineClients)) { - this.onlineClients = payload.onlineClients; - // Recalculate client counts to update online status - this.dbInbounds.forEach(dbInbound => { - const inbound = this.inbounds.find(ib => ib.id === dbInbound.id); - if (inbound && this.clientCount[dbInbound.id]) { - this.clientCount[dbInbound.id] = this.getClientCounts(dbInbound, inbound); + const nextOnlineClients = payload.onlineClients; + let onlineChanged = this.onlineClients.length !== nextOnlineClients.length; + if (!onlineChanged) { + const prevSet = new Set(this.onlineClients); + for (const email of nextOnlineClients) { + if (!prevSet.has(email)) { + onlineChanged = true; + break; + } } - }); + } + this.onlineClients = nextOnlineClients; + if (onlineChanged) { + // Recalculate client counts to update online status + this.dbInbounds.forEach(dbInbound => { + const inbound = this.inbounds.find(ib => ib.id === dbInbound.id); + if (inbound && this.clientCount[dbInbound.id]) { + this.clientCount[dbInbound.id] = this.getClientCounts(dbInbound, inbound); + } + }); + + if (this.enableFilter) { + this.filterInbounds(); + } + } } // Update last online map in real-time diff --git a/web/html/settings/panel/subscription/subpage.html b/web/html/settings/panel/subscription/subpage.html index 222352ff..c59f68ee 100644 --- a/web/html/settings/panel/subscription/subpage.html +++ b/web/html/settings/panel/subscription/subpage.html @@ -5,6 +5,43 @@ + {{ template "page/head_end" .}} {{ template "page/body_start" .}} @@ -138,27 +175,12 @@ style="margin-bottom: -10px; position: relative; z-index: 2; box-shadow: 0 2px 4px rgba(0,0,0,0.2);"> [[ linkName(link, idx) ]] -
+
+
diff --git a/web/service/xray.go b/web/service/xray.go index 1c96d33c..d26dd1f6 100644 --- a/web/service/xray.go +++ b/web/service/xray.go @@ -44,6 +44,9 @@ func (s *XrayService) GetXrayErr() error { } err := p.GetErr() + if err == nil { + return nil + } if runtime.GOOS == "windows" && err.Error() == "exit status 1" { // exit status 1 on Windows means that Xray process was killed diff --git a/x-ui.service.arch b/x-ui.service.arch new file mode 100644 index 00000000..b6e01141 --- /dev/null +++ b/x-ui.service.arch @@ -0,0 +1,16 @@ +[Unit] +Description=x-ui Service +After=network.target +Wants=network.target + +[Service] +EnvironmentFile=-/etc/conf.d/x-ui +Environment="XRAY_VMESS_AEAD_FORCED=false" +Type=simple +WorkingDirectory=/usr/lib/x-ui/ +ExecStart=/usr/lib/x-ui/x-ui +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target diff --git a/x-ui.sh b/x-ui.sh index 07aaddc6..42dbb601 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -229,9 +229,9 @@ reset_user() { read -rp "Do you want to disable currently configured two-factor authentication? (y/n): " twoFactorConfirm if [[ $twoFactorConfirm != "y" && $twoFactorConfirm != "Y" ]]; then - ${xui_folder}/x-ui setting -username ${config_account} -password ${config_password} -resetTwoFactor false >/dev/null 2>&1 + ${xui_folder}/x-ui setting -username "${config_account}" -password "${config_password}" -resetTwoFactor false >/dev/null 2>&1 else - ${xui_folder}/x-ui setting -username ${config_account} -password ${config_password} -resetTwoFactor true >/dev/null 2>&1 + ${xui_folder}/x-ui setting -username "${config_account}" -password "${config_password}" -resetTwoFactor true >/dev/null 2>&1 echo -e "Two factor authentication has been disabled." fi @@ -530,20 +530,27 @@ bbr_menu() { disable_bbr() { - if ! grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf || ! grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then + if [[ $(sysctl -n net.ipv4.tcp_congestion_control) != "bbr" ]] || [[ ! $(sysctl -n net.core.default_qdisc) =~ ^(fq|cake)$ ]]; then echo -e "${yellow}BBR is not currently enabled.${plain}" before_show_menu fi - # Replace BBR with CUBIC configurations - sed -i 's/net.core.default_qdisc=fq/net.core.default_qdisc=pfifo_fast/' /etc/sysctl.conf - sed -i 's/net.ipv4.tcp_congestion_control=bbr/net.ipv4.tcp_congestion_control=cubic/' /etc/sysctl.conf + if [ -f "/etc/sysctl.d/99-bbr-x-ui.conf" ]; then + old_settings=$(head -1 /etc/sysctl.d/99-bbr-x-ui.conf | tr -d '#') + sysctl -w net.core.default_qdisc="${old_settings%:*}" + sysctl -w net.ipv4.tcp_congestion_control="${old_settings#*:}" + rm /etc/sysctl.d/99-bbr-x-ui.conf + sysctl --system + else + # Replace BBR with CUBIC configurations + if [ -f "/etc/sysctl.conf" ]; then + sed -i 's/net.core.default_qdisc=fq/net.core.default_qdisc=pfifo_fast/' /etc/sysctl.conf + sed -i 's/net.ipv4.tcp_congestion_control=bbr/net.ipv4.tcp_congestion_control=cubic/' /etc/sysctl.conf + sysctl -p + fi + fi - # Apply changes - sysctl -p - - # Verify that BBR is replaced with CUBIC - if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "cubic" ]]; then + if [[ $(sysctl -n net.ipv4.tcp_congestion_control) != "bbr" ]]; then echo -e "${green}BBR has been replaced with CUBIC successfully.${plain}" else echo -e "${red}Failed to replace BBR with CUBIC. Please check your system configuration.${plain}" @@ -551,20 +558,34 @@ disable_bbr() { } enable_bbr() { - if grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf && grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then + if [[ $(sysctl -n net.ipv4.tcp_congestion_control) == "bbr" ]] && [[ $(sysctl -n net.core.default_qdisc) =~ ^(fq|cake)$ ]]; then echo -e "${green}BBR is already enabled!${plain}" before_show_menu fi # Enable BBR - echo "net.core.default_qdisc=fq" | tee -a /etc/sysctl.conf - echo "net.ipv4.tcp_congestion_control=bbr" | tee -a /etc/sysctl.conf - - # Apply changes - sysctl -p + if [ -d "/etc/sysctl.d/" ]; then + { + echo "#$(sysctl -n net.core.default_qdisc):$(sysctl -n net.ipv4.tcp_congestion_control)" + echo "net.core.default_qdisc = fq" + echo "net.ipv4.tcp_congestion_control = bbr" + } > "/etc/sysctl.d/99-bbr-x-ui.conf" + if [ -f "/etc/sysctl.conf" ]; then + # Backup old settings from sysctl.conf, if any + sed -i 's/^net.core.default_qdisc/# &/' /etc/sysctl.conf + sed -i 's/^net.ipv4.tcp_congestion_control/# &/' /etc/sysctl.conf + fi + sysctl --system + else + sed -i '/net.core.default_qdisc/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf + echo "net.core.default_qdisc=fq" | tee -a /etc/sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr" | tee -a /etc/sysctl.conf + sysctl -p + fi # Verify that BBR is enabled - if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then + if [[ $(sysctl -n net.ipv4.tcp_congestion_control) == "bbr" ]]; then echo -e "${green}BBR has been enabled successfully.${plain}" else echo -e "${red}Failed to enable BBR. Please check your system configuration.${plain}"