diff --git a/config/version b/config/version index 39e84545..05822b7e 100644 --- a/config/version +++ b/config/version @@ -1 +1 @@ -2.8.10 \ No newline at end of file +2.8.11 \ No newline at end of file diff --git a/go.mod b/go.mod index 411794af..97e2e38d 100644 --- a/go.mod +++ b/go.mod @@ -5,18 +5,18 @@ go 1.26.0 require ( github.com/gin-contrib/gzip v1.2.5 github.com/gin-contrib/sessions v1.0.4 - github.com/gin-gonic/gin v1.11.0 + github.com/gin-gonic/gin v1.12.0 github.com/go-ldap/ldap/v3 v3.4.12 github.com/goccy/go-json v0.10.5 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 github.com/joho/godotenv v1.5.1 - github.com/mymmrac/telego v1.6.0 + github.com/mymmrac/telego v1.7.0 github.com/nicksnyder/go-i18n/v2 v2.6.1 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/pelletier/go-toml/v2 v2.2.4 github.com/robfig/cron/v3 v3.0.1 - github.com/shirou/gopsutil/v4 v4.26.1 + github.com/shirou/gopsutil/v4 v4.26.2 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/valyala/fasthttp v1.69.0 github.com/xlzd/gotp v0.1.0 @@ -39,7 +39,7 @@ require ( github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/cloudwego/base64x v0.1.6 // indirect - github.com/ebitengine/purego v0.9.1 // indirect + github.com/ebitengine/purego v0.10.0 // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect @@ -60,7 +60,7 @@ require ( github.com/klauspost/compress v1.18.4 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect + github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.34 // indirect github.com/miekg/dns v1.1.72 // indirect @@ -72,29 +72,30 @@ require ( github.com/quic-go/quic-go v0.59.0 // indirect github.com/refraction-networking/utls v1.8.2 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/sagernet/sing v0.7.18 // indirect + github.com/sagernet/sing v0.8.1 // indirect github.com/sagernet/sing-shadowsocks v0.2.9 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fastjson v1.6.7 // indirect + github.com/valyala/fastjson v1.6.10 // indirect github.com/vishvananda/netlink v1.3.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect github.com/xtls/reality v0.0.0-20251116175510-cd53f7d50237 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/arch v0.24.0 // indirect - golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a // indirect + golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect golang.org/x/mod v0.33.0 // indirect - golang.org/x/net v0.50.0 // indirect + golang.org/x/net v0.51.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.42.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect google.golang.org/protobuf v1.36.11 // indirect gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0 // indirect lukechampine.com/blake3 v1.4.1 // indirect diff --git a/go.sum b/go.sum index 7e18ac8d..9b78e860 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gE github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= -github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4= @@ -35,8 +35,8 @@ github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kb github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= -github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= +github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= +github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4= @@ -117,8 +117,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= -github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM= +github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk= @@ -130,8 +130,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mymmrac/telego v1.6.0 h1:Zc8rgyHozvd/7ZgyrigyHdAF9koHYMfilYfyB6wlFC0= -github.com/mymmrac/telego v1.6.0/go.mod h1:xt6ZWA8zi8KmuzryE1ImEdl9JSwjHNpM4yhC7D8hU4Y= +github.com/mymmrac/telego v1.7.0 h1:yRO/l00tFGG4nY66ufUKb4ARqv7qx9+LsjQv/b0NEyo= +github.com/mymmrac/telego v1.7.0/go.mod h1:pdLV346EgVuq7Xrh3kMggeBiazeHhsdEoK0RTEOPXRM= github.com/nicksnyder/go-i18n/v2 v2.6.1 h1:JDEJraFsQE17Dut9HFDHzCoAWGEQJom5s0TRd17NIEQ= github.com/nicksnyder/go-i18n/v2 v2.6.1/go.mod h1:Vee0/9RD3Quc/NmwEjzzD7VTZ+Ir7QbXocrkhOzmUKA= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= @@ -156,12 +156,12 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/sagernet/sing v0.7.18 h1:iZHkaru1/MoHugx3G+9S3WG4owMewKO/KvieE2Pzk4E= -github.com/sagernet/sing v0.7.18/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.8.1 h1:Li+zg4xdiMsvdX4j50TPqmSG8LF/TB9US2qlAN40izU= +github.com/sagernet/sing v0.8.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing-shadowsocks v0.2.9 h1:Paep5zCszRKsEn8587O0MnhFWKJwDW1Y4zOYYlIxMkM= github.com/sagernet/sing-shadowsocks v0.2.9/go.mod h1:TE/Z6401Pi8tgr0nBZcM/xawAI6u3F6TTbz4nH/qw+8= -github.com/shirou/gopsutil/v4 v4.26.1 h1:TOkEyriIXk2HX9d4isZJtbjXbEjf5qyKPAzbzY0JWSo= -github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc= +github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= +github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -187,8 +187,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI= github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw= -github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM= -github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4= +github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= @@ -203,6 +203,8 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE= +go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= @@ -227,12 +229,12 @@ golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y= golang.org/x/arch v0.24.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a h1:ovFr6Z0MNmU7nH8VaX5xqw+05ST2uO1exVfZPVqRC5o= -golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -255,8 +257,8 @@ golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+Z golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/install.sh b/install.sh index 46207777..9d1aeb6b 100644 --- a/install.sh +++ b/install.sh @@ -76,7 +76,7 @@ is_port_in_use() { install_base() { case "${release}" in ubuntu | debian | armbian) - apt-get update && apt-get install -y -q curl tar tzdata socat ca-certificates + apt-get update && apt-get install -y -q cron curl tar tzdata socat ca-certificates ;; fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol) dnf -y update && dnf install -y -q curl tar tzdata socat ca-certificates diff --git a/main.go b/main.go index 8096616c..f8d3357b 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "github.com/mhsanaei/3x-ui/v2/logger" "github.com/mhsanaei/3x-ui/v2/sub" "github.com/mhsanaei/3x-ui/v2/util/crypto" + "github.com/mhsanaei/3x-ui/v2/util/sys" "github.com/mhsanaei/3x-ui/v2/web" "github.com/mhsanaei/3x-ui/v2/web/global" "github.com/mhsanaei/3x-ui/v2/web/service" @@ -70,7 +71,7 @@ func runWebServer() { sigCh := make(chan os.Signal, 1) // Trap shutdown signals - signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM) + signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM, sys.SIGUSR1) for { sig := <-sigCh @@ -108,6 +109,12 @@ func runWebServer() { return } log.Println("Sub server restarted successfully.") + case sys.SIGUSR1: + logger.Info("Received USR1 signal, restarting xray-core...") + err := server.RestartXray() + if err != nil { + logger.Error("Failed to restart xray-core:", err) + } default: // --- FIX FOR TELEGRAM BOT CONFLICT (409) on full shutdown --- diff --git a/util/sys/sys_darwin.go b/util/sys/sys_darwin.go index b635b549..b44d7689 100644 --- a/util/sys/sys_darwin.go +++ b/util/sys/sys_darwin.go @@ -7,11 +7,14 @@ import ( "encoding/binary" "fmt" "sync" + "syscall" "github.com/shirou/gopsutil/v4/net" "golang.org/x/sys/unix" ) +var SIGUSR1 = syscall.SIGUSR1 + func GetTCPCount() (int, error) { stats, err := net.Connections("tcp") if err != nil { diff --git a/util/sys/sys_linux.go b/util/sys/sys_linux.go index 23483b57..5b1b1127 100644 --- a/util/sys/sys_linux.go +++ b/util/sys/sys_linux.go @@ -12,8 +12,11 @@ import ( "strconv" "strings" "sync" + "syscall" ) +var SIGUSR1 = syscall.SIGUSR1 + func getLinesNum(filename string) (int, error) { file, err := os.Open(filename) if err != nil { diff --git a/util/sys/sys_windows.go b/util/sys/sys_windows.go index 186fa4bb..9b6d659f 100644 --- a/util/sys/sys_windows.go +++ b/util/sys/sys_windows.go @@ -12,6 +12,8 @@ import ( "github.com/shirou/gopsutil/v4/net" ) +var SIGUSR1 = syscall.Signal(0) + // GetConnectionCount returns the number of active connections for the specified protocol ("tcp" or "udp"). func GetConnectionCount(proto string) (int, error) { if proto != "tcp" && proto != "udp" { diff --git a/web/assets/js/subscription.js b/web/assets/js/subscription.js index b79d361c..228dcfa0 100644 --- a/web/assets/js/subscription.js +++ b/web/assets/js/subscription.js @@ -144,7 +144,7 @@ return this.app.subUrl; }, happUrl() { - return `happ://add/${encodeURIComponent(this.app.subUrl)}`; + return `happ://add/${this.app.subUrl}`; } }, methods: { diff --git a/web/controller/index.go b/web/controller/index.go index 5f9e1c2c..605f874f 100644 --- a/web/controller/index.go +++ b/web/controller/index.go @@ -4,6 +4,7 @@ import ( "net/http" "text/template" "time" + "fmt" "github.com/mhsanaei/3x-ui/v2/logger" "github.com/mhsanaei/3x-ui/v2/web/service" @@ -71,14 +72,22 @@ func (a *IndexController) login(c *gin.Context) { return } - user := a.userService.CheckUser(form.Username, form.Password, form.TwoFactorCode) + user, checkErr := a.userService.CheckUser(form.Username, form.Password, form.TwoFactorCode) timeStr := time.Now().Format("2006-01-02 15:04:05") safeUser := template.HTMLEscapeString(form.Username) safePass := template.HTMLEscapeString(form.Password) if user == nil { logger.Warningf("wrong username: \"%s\", password: \"%s\", IP: \"%s\"", safeUser, safePass, getRemoteIp(c)) - a.tgbot.UserLoginNotify(safeUser, safePass, getRemoteIp(c), timeStr, 0) + + notifyPass := safePass + + if checkErr != nil && checkErr.Error() == "invalid 2fa code" { + translatedError := a.tgbot.I18nBot("tgbot.messages.2faFailed") + notifyPass = fmt.Sprintf("*** (%s)", translatedError) + } + + a.tgbot.UserLoginNotify(safeUser, notifyPass, getRemoteIp(c), timeStr, 0) pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword")) return } diff --git a/web/html/form/outbound.html b/web/html/form/outbound.html index a2de920a..21bc11fc 100644 --- a/web/html/form/outbound.html +++ b/web/html/form/outbound.html @@ -612,7 +612,7 @@ mKCP Original - + xDNS (Experimental) diff --git a/web/html/form/stream/stream_finalmask.html b/web/html/form/stream/stream_finalmask.html index 35962dfa..0b6418b7 100644 --- a/web/html/form/stream/stream_finalmask.html +++ b/web/html/form/stream/stream_finalmask.html @@ -18,7 +18,7 @@ xICMP (Experimental) - + xDNS (Experimental) diff --git a/web/html/settings/panel/subscription/subpage.html b/web/html/settings/panel/subscription/subpage.html index c59f68ee..794c67c3 100644 --- a/web/html/settings/panel/subscription/subpage.html +++ b/web/html/settings/panel/subscription/subpage.html @@ -206,7 +206,7 @@ NPV Tunnel Happ + @click="open('happ://add/' + app.subUrl)">Happ diff --git a/web/job/ldap_sync_job.go b/web/job/ldap_sync_job.go index a947eb73..7edb8178 100644 --- a/web/job/ldap_sync_job.go +++ b/web/job/ldap_sync_job.go @@ -271,10 +271,7 @@ func (j *LdapSyncJob) deleteClientsNotInLDAP(inboundTag string, ldapEmails map[s // Delete in batches for i := 0; i < len(toDelete); i += batchSize { - end := i + batchSize - if end > len(toDelete) { - end = len(toDelete) - } + end := min(i+batchSize, len(toDelete)) batch := toDelete[i:end] for _, c := range batch { diff --git a/web/locale/locale.go b/web/locale/locale.go index c469911a..73da75b4 100644 --- a/web/locale/locale.go +++ b/web/locale/locale.go @@ -37,7 +37,7 @@ type SettingService interface { // InitLocalizer initializes the internationalization system with embedded translation files. func InitLocalizer(i18nFS embed.FS, settingService SettingService) error { - // set default bundle to english + // set default bundle to English i18nBundle = i18n.NewBundle(language.MustParse("en-US")) i18nBundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) diff --git a/web/service/setting.go b/web/service/setting.go index 447a2325..b27c6c80 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -109,7 +109,7 @@ var defaultValueMap = map[string]string{ // It handles configuration storage, retrieval, and validation for all system settings. type SettingService struct{} -func (s *SettingService) GetDefaultJsonConfig() (any, error) { +func (s *SettingService) GetDefaultJSONConfig() (any, error) { var jsonData any err := json.Unmarshal([]byte(xrayTemplateConfig), &jsonData) if err != nil { @@ -126,7 +126,7 @@ func (s *SettingService) GetAllSetting() (*entity.AllSetting, error) { return nil, err } allSetting := &entity.AllSetting{} - t := reflect.TypeOf(allSetting).Elem() + t := reflect.TypeFor[entity.AllSetting]() v := reflect.ValueOf(allSetting).Elem() fields := reflect_util.GetFields(t) @@ -612,7 +612,7 @@ func (s *SettingService) GetIpLimitEnable() (bool, error) { return (accessLogPath != "none" && accessLogPath != ""), nil } -// LDAP exported getters +// GetLdapEnable returns whether LDAP is enabled. func (s *SettingService) GetLdapEnable() (bool, error) { return s.getBool("ldapEnable") } @@ -699,7 +699,7 @@ func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error { } v := reflect.ValueOf(allSetting).Elem() - t := reflect.TypeOf(allSetting).Elem() + t := reflect.TypeFor[entity.AllSetting]() fields := reflect_util.GetFields(t) errs := make([]error, 0) for _, field := range fields { diff --git a/web/service/tgbot.go b/web/service/tgbot.go index 3ff80b40..6a49f1d3 100644 --- a/web/service/tgbot.go +++ b/web/service/tgbot.go @@ -8,6 +8,7 @@ import ( "encoding/json" "errors" "fmt" + "html" "io" "math/big" "net" @@ -15,6 +16,7 @@ import ( "net/url" "os" "regexp" + "slices" "strconv" "strings" "sync" @@ -651,7 +653,7 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo msg += t.I18nBot("tgbot.commands.help") msg += t.I18nBot("tgbot.commands.pleaseChoose") case "start": - msg += t.I18nBot("tgbot.commands.start", "Firstname=="+message.From.FirstName) + msg += t.I18nBot("tgbot.commands.start", "Firstname=="+html.EscapeString(message.From.FirstName)) if isAdmin { msg += t.I18nBot("tgbot.commands.welcome", "Hostname=="+hostname) } @@ -2718,7 +2720,7 @@ func (t *Tgbot) prepareServerUsageInfo() string { info += t.I18nBot("tgbot.messages.ip", "IP=="+t.I18nBot("tgbot.unknown")) info += "\r\n" } else { - for i := 0; i < len(netInterfaces); i++ { + for i := range netInterfaces { if (netInterfaces[i].Flags & net.FlagUp) != 0 { addrs, _ := netInterfaces[i].Addrs() @@ -2787,29 +2789,29 @@ func (t *Tgbot) UserLoginNotify(username string, password string, ip string, tim // getInboundUsages retrieves and formats inbound usage information. func (t *Tgbot) getInboundUsages() string { - info := "" + var info strings.Builder // get traffic inbounds, err := t.inboundService.GetAllInbounds() if err != nil { logger.Warning("GetAllInbounds run failed:", err) - info += t.I18nBot("tgbot.answers.getInboundsFailed") + info.WriteString(t.I18nBot("tgbot.answers.getInboundsFailed")) } else { // NOTE:If there no any sessions here,need to notify here // TODO:Sub-node push, automatic conversion format for _, inbound := range inbounds { - info += t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark) - info += t.I18nBot("tgbot.messages.port", "Port=="+strconv.Itoa(inbound.Port)) - info += t.I18nBot("tgbot.messages.traffic", "Total=="+common.FormatTraffic((inbound.Up+inbound.Down)), "Upload=="+common.FormatTraffic(inbound.Up), "Download=="+common.FormatTraffic(inbound.Down)) + info.WriteString(t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark)) + info.WriteString(t.I18nBot("tgbot.messages.port", "Port=="+strconv.Itoa(inbound.Port))) + info.WriteString(t.I18nBot("tgbot.messages.traffic", "Total=="+common.FormatTraffic((inbound.Up+inbound.Down)), "Upload=="+common.FormatTraffic(inbound.Up), "Download=="+common.FormatTraffic(inbound.Down))) if inbound.ExpiryTime == 0 { - info += t.I18nBot("tgbot.messages.expire", "Time=="+t.I18nBot("tgbot.unlimited")) + info.WriteString(t.I18nBot("tgbot.messages.expire", "Time=="+t.I18nBot("tgbot.unlimited"))) } else { - info += t.I18nBot("tgbot.messages.expire", "Time=="+time.Unix((inbound.ExpiryTime/1000), 0).Format("2006-01-02 15:04:05")) + info.WriteString(t.I18nBot("tgbot.messages.expire", "Time=="+time.Unix((inbound.ExpiryTime/1000), 0).Format("2006-01-02 15:04:05"))) } - info += "\r\n" + info.WriteString("\r\n") } } - return info + return info.String() } // getInbounds creates an inline keyboard with all inbounds. @@ -3059,12 +3061,9 @@ func (t *Tgbot) clientInfoMsg( status := t.I18nBot("tgbot.offline") isOnline := false if p.IsRunning() { - for _, online := range p.GetOnlineClients() { - if online == traffic.Email { - status = t.I18nBot("tgbot.online") - isOnline = true - break - } + if slices.Contains(p.GetOnlineClients(), traffic.Email) { + status = t.I18nBot("tgbot.online") + isOnline = true } } @@ -3429,11 +3428,11 @@ func (t *Tgbot) searchInbound(chatId int64, remark string) { t.SendMsgToTgbot(chatId, info) if len(inbound.ClientStats) > 0 { - output := "" + var output strings.Builder for _, traffic := range inbound.ClientStats { - output += t.clientInfoMsg(&traffic, true, true, true, true, true, true) + output.WriteString(t.clientInfoMsg(&traffic, true, true, true, true, true, true)) } - t.SendMsgToTgbot(chatId, output) + t.SendMsgToTgbot(chatId, output.String()) } } } diff --git a/web/service/user.go b/web/service/user.go index 1bde69f6..0a2a3f3e 100644 --- a/web/service/user.go +++ b/web/service/user.go @@ -33,7 +33,7 @@ func (s *UserService) GetFirstUser() (*model.User, error) { return user, nil } -func (s *UserService) CheckUser(username string, password string, twoFactorCode string) *model.User { +func (s *UserService) CheckUser(username string, password string, twoFactorCode string) (*model.User, error) { db := database.GetDB() user := &model.User{} @@ -43,17 +43,16 @@ func (s *UserService) CheckUser(username string, password string, twoFactorCode First(user). Error if err == gorm.ErrRecordNotFound { - return nil + return nil, errors.New("invalid credentials") } else if err != nil { logger.Warning("check user err:", err) - return nil + return nil, err } - // If LDAP enabled and local password check fails, attempt LDAP auth if !crypto.CheckPasswordHash(user.Password, password) { ldapEnabled, _ := s.settingService.GetLdapEnable() if !ldapEnabled { - return nil + return nil, errors.New("invalid credentials") } host, _ := s.settingService.GetLdapHost() @@ -77,15 +76,14 @@ func (s *UserService) CheckUser(username string, password string, twoFactorCode } ok, err := ldaputil.AuthenticateUser(cfg, username, password) if err != nil || !ok { - return nil + return nil, errors.New("invalid credentials") } - // On successful LDAP auth, continue 2FA checks below } twoFactorEnable, err := s.settingService.GetTwoFactorEnable() if err != nil { logger.Warning("check two factor err:", err) - return nil + return nil, err } if twoFactorEnable { @@ -93,15 +91,15 @@ func (s *UserService) CheckUser(username string, password string, twoFactorCode if err != nil { logger.Warning("check two factor token err:", err) - return nil + return nil, err } if gotp.NewDefaultTOTP(twoFactorToken).Now() != twoFactorCode { - return nil + return nil, errors.New("invalid 2fa code") } } - return user + return user, nil } func (s *UserService) UpdateUser(id int, username string, password string) error { diff --git a/web/translation/translate.ar_EG.toml b/web/translation/translate.ar_EG.toml index 2ac5d5ce..534adc12 100644 --- a/web/translation/translate.ar_EG.toml +++ b/web/translation/translate.ar_EG.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ حفظت بيانات مستخدم Telegram." "loginSuccess" = "✅ تسجيل الدخول للبانل تم بنجاح.\r\n" "loginFailed" = "❗️فشل محاولة تسجيل الدخول للبانل.\r\n" +"2faFailed" = "فشل 2FA" "report" = "🕰 التقارير المجدولة: {{ .RunTime }}\r\n" "datetime" = "⏰ التاريخ والوقت: {{ .DateTime }}\r\n" "hostname" = "💻 السيرفر: {{ .Hostname }}\r\n" diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index db44c3a4..415ca80d 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ Telegram User saved." "loginSuccess" = "✅ Logged in to the panel successfully.\r\n" "loginFailed" = "❗️Login attempt to the panel failed.\r\n" +"2faFailed" = "2FA Failed" "report" = "🕰 Scheduled Reports: {{ .RunTime }}\r\n" "datetime" = "⏰ Date&Time: {{ .DateTime }}\r\n" "hostname" = "💻 Host: {{ .Hostname }}\r\n" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index 3658749a..98a875bf 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ Usuario de Telegram guardado." "loginSuccess" = "✅ Has iniciado sesión en el panel con éxito.\r\n" "loginFailed" = "❗️ Falló el inicio de sesión en el panel.\r\n" +"2faFailed" = "Error de 2FA" "report" = "🕰 Informes programados: {{ .RunTime }}\r\n" "datetime" = "⏰ Fecha y Hora: {{ .DateTime }}\r\n" "hostname" = "💻 Nombre del Host: {{ .Hostname }}\r\n" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index 4ce06e37..2d2e5b1c 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ کاربر تلگرام ذخیره شد." "loginSuccess" = "✅ با موفقیت به پنل وارد شدید.\r\n" "loginFailed" = "❗️ ورود به پنل ناموفق‌بود \r\n" +"2faFailed" = "خطای 2FA" "report" = "🕰 گزارشات‌زمان‌بندی‌شده: {{ .RunTime }}\r\n" "datetime" = "⏰ تاریخ‌وزمان: {{ .DateTime }}\r\n" "hostname" = "💻 نام‌میزبان: {{ .Hostname }}\r\n" diff --git a/web/translation/translate.id_ID.toml b/web/translation/translate.id_ID.toml index 3e3d5d0e..1db9f85c 100644 --- a/web/translation/translate.id_ID.toml +++ b/web/translation/translate.id_ID.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ Pengguna Telegram tersimpan." "loginSuccess" = "✅ Berhasil masuk ke panel.\r\n" "loginFailed" = "❗️ Gagal masuk ke panel.\r\n" +"2faFailed" = "2FA Gagal" "report" = "🕰 Laporan Terjadwal: {{ .RunTime }}\r\n" "datetime" = "⏰ Tanggal & Waktu: {{ .DateTime }}\r\n" "hostname" = "💻 Host: {{ .Hostname }}\r\n" diff --git a/web/translation/translate.ja_JP.toml b/web/translation/translate.ja_JP.toml index ada2bbe9..46a6327d 100644 --- a/web/translation/translate.ja_JP.toml +++ b/web/translation/translate.ja_JP.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ Telegramユーザーが保存されました。" "loginSuccess" = "✅ パネルに正常にログインしました。\r\n" "loginFailed" = "❗️ パネルのログインに失敗しました。\r\n" +"2faFailed" = "2FAエラー" "report" = "🕰 定期報告:{{ .RunTime }}\r\n" "datetime" = "⏰ 日時:{{ .DateTime }}\r\n" "hostname" = "💻 ホスト名:{{ .Hostname }}\r\n" diff --git a/web/translation/translate.pt_BR.toml b/web/translation/translate.pt_BR.toml index c0cc20bc..6249cdeb 100644 --- a/web/translation/translate.pt_BR.toml +++ b/web/translation/translate.pt_BR.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ Usuário do Telegram salvo." "loginSuccess" = "✅ Conectado ao painel com sucesso.\r\n" "loginFailed" = "❗️Tentativa de login no painel falhou.\r\n" +"2faFailed" = "Falha no 2FA" "report" = "🕰 Relatórios agendados: {{ .RunTime }}\r\n" "datetime" = "⏰ Data&Hora: {{ .DateTime }}\r\n" "hostname" = "💻 Host: {{ .Hostname }}\r\n" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index 13006e53..2d2bfb70 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ Пользователь Telegram сохранен." "loginSuccess" = "✅ Успешный вход в панель.\r\n" "loginFailed" = "❗️ Ошибка входа в панель.\r\n" +"2faFailed" = "Ошибка 2FA" "report" = "🕰 Запланированные отчеты: {{ .RunTime }}\r\n" "datetime" = "⏰ Дата и время: {{ .DateTime }}\r\n" "hostname" = "💻 Имя хоста: {{ .Hostname }}\r\n" diff --git a/web/translation/translate.tr_TR.toml b/web/translation/translate.tr_TR.toml index f40603c2..7aa10bd8 100644 --- a/web/translation/translate.tr_TR.toml +++ b/web/translation/translate.tr_TR.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ Telegram Kullanıcısı kaydedildi." "loginSuccess" = "✅ Panele başarıyla giriş yapıldı.\r\n" "loginFailed" = "❗️Panele giriş denemesi başarısız oldu.\r\n" +"2faFailed" = "2FA Hatası" "report" = "🕰 Planlanmış Raporlar: {{ .RunTime }}\r\n" "datetime" = "⏰ Tarih&Zaman: {{ .DateTime }}\r\n" "hostname" = "💻 Sunucu: {{ .Hostname }}\r\n" diff --git a/web/translation/translate.uk_UA.toml b/web/translation/translate.uk_UA.toml index b22a272f..eb1cac15 100644 --- a/web/translation/translate.uk_UA.toml +++ b/web/translation/translate.uk_UA.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ Користувача Telegram збережено." "loginSuccess" = "✅ Успішно ввійшли в панель\r\n" "loginFailed" = "❗️ Помилка входу в панель.\r\n" +"2faFailed" = "Помилка 2FA" "report" = "🕰 Заплановані звіти: {{ .RunTime }}\r\n" "datetime" = "⏰ Дата й час: {{ .DateTime }}\r\n" "hostname" = "💻 Хост: {{ .Hostname }}\r\n" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index 1fbcf6e4..4cd8a6c8 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ Người dùng Telegram đã được lưu." "loginSuccess" = "✅ Đăng nhập thành công vào bảng điều khiển.\r\n" "loginFailed" = "❗️ Đăng nhập vào bảng điều khiển thất bại.\r\n" +"2faFailed" = "Lỗi 2FA" "report" = "🕰 Báo cáo định kỳ: {{ .RunTime }}\r\n" "datetime" = "⏰ Ngày-Giờ: {{ .DateTime }}\r\n" "hostname" = "💻 Tên máy chủ: {{ .Hostname }}\r\n" diff --git a/web/translation/translate.zh_CN.toml b/web/translation/translate.zh_CN.toml index 44c175ea..721d0c24 100644 --- a/web/translation/translate.zh_CN.toml +++ b/web/translation/translate.zh_CN.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ 电报用户已保存。" "loginSuccess" = "✅ 成功登录到面板。\r\n" "loginFailed" = "❗️ 面板登录失败。\r\n" +"2faFailed" = "2FA 失败" "report" = "🕰 定时报告:{{ .RunTime }}\r\n" "datetime" = "⏰ 日期时间:{{ .DateTime }}\r\n" "hostname" = "💻 主机名:{{ .Hostname }}\r\n" diff --git a/web/translation/translate.zh_TW.toml b/web/translation/translate.zh_TW.toml index a1caf321..04148eca 100644 --- a/web/translation/translate.zh_TW.toml +++ b/web/translation/translate.zh_TW.toml @@ -669,6 +669,7 @@ "userSaved" = "✅ 電報使用者已儲存。" "loginSuccess" = "✅ 成功登入到面板。\r\n" "loginFailed" = "❗️ 面板登入失敗。\r\n" +"2faFailed" = "2FA 失敗" "report" = "🕰 定時報告:{{ .RunTime }}\r\n" "datetime" = "⏰ 日期時間:{{ .DateTime }}\r\n" "hostname" = "💻 主機名:{{ .Hostname }}\r\n" diff --git a/web/web.go b/web/web.go index 300572a3..60934048 100644 --- a/web/web.go +++ b/web/web.go @@ -200,7 +200,7 @@ func (s *Server) initRouter() (*gin.Engine, error) { if err != nil { return nil, err } - engine.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{basePath + "panel/api/"}))) + engine.Use(gzip.Gzip(gzip.DefaultCompression)) assetsBasePath := basePath + "assets/" store := cookie.NewStore(secret) @@ -490,3 +490,7 @@ func (s *Server) GetCron() *cron.Cron { func (s *Server) GetWSHub() any { return s.wsHub } + +func (s *Server) RestartXray() error { + return s.xrayService.RestartXray(true) +} diff --git a/x-ui.rc b/x-ui.rc index 1323d76a..cfb54211 100644 --- a/x-ui.rc +++ b/x-ui.rc @@ -10,4 +10,9 @@ depend() { } start_pre(){ cd /usr/local/x-ui +} +reload() { + ebegin "Reloading ${RC_SVCNAME}" + kill -USR1 $pidfile + eend $? } \ No newline at end of file diff --git a/x-ui.service.arch b/x-ui.service.arch index b6e01141..9060fbf6 100644 --- a/x-ui.service.arch +++ b/x-ui.service.arch @@ -9,6 +9,7 @@ Environment="XRAY_VMESS_AEAD_FORCED=false" Type=simple WorkingDirectory=/usr/lib/x-ui/ ExecStart=/usr/lib/x-ui/x-ui +ExecReload=kill -USR1 $MAINPID Restart=on-failure RestartSec=5s diff --git a/x-ui.service.debian b/x-ui.service.debian index 037f88bb..4902c5fe 100644 --- a/x-ui.service.debian +++ b/x-ui.service.debian @@ -9,6 +9,7 @@ Environment="XRAY_VMESS_AEAD_FORCED=false" Type=simple WorkingDirectory=/usr/local/x-ui/ ExecStart=/usr/local/x-ui/x-ui +ExecReload=kill -USR1 $MAINPID Restart=on-failure RestartSec=5s diff --git a/x-ui.service.rhel b/x-ui.service.rhel index 30652d52..696a9c12 100644 --- a/x-ui.service.rhel +++ b/x-ui.service.rhel @@ -9,6 +9,7 @@ Environment="XRAY_VMESS_AEAD_FORCED=false" Type=simple WorkingDirectory=/usr/local/x-ui/ ExecStart=/usr/local/x-ui/x-ui +ExecReload=kill -USR1 $MAINPID Restart=on-failure RestartSec=5s diff --git a/x-ui.sh b/x-ui.sh index 2bd125ab..0a2b818b 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -408,6 +408,16 @@ restart() { fi } +restart_xray() { + systemctl reload x-ui + LOGI "xray-core Restart signal sent successfully, Please check the log information to confirm whether xray restarted successfully" + sleep 2 + show_xray_status + if [[ $# == 0 ]]; then + before_show_menu + fi +} + status() { if [[ $release == "alpine" ]]; then rc-service x-ui status @@ -421,7 +431,7 @@ status() { enable() { if [[ $release == "alpine" ]]; then - rc-update add x-ui + rc-update add x-ui default else systemctl enable x-ui fi @@ -2154,6 +2164,7 @@ show_usage() { │ ${blue}x-ui start${plain} - Start │ │ ${blue}x-ui stop${plain} - Stop │ │ ${blue}x-ui restart${plain} - Restart │ +| ${blue}x-ui restart-xray${plain} - Restart Xray │ │ ${blue}x-ui status${plain} - Current Status │ │ ${blue}x-ui settings${plain} - Current Settings │ │ ${blue}x-ui enable${plain} - Enable Autostart on OS Startup │ @@ -2189,25 +2200,26 @@ show_menu() { │ ${green}11.${plain} Start │ │ ${green}12.${plain} Stop │ │ ${green}13.${plain} Restart │ -│ ${green}14.${plain} Check Status │ -│ ${green}15.${plain} Logs Management │ +| ${green}14.${plain} Restart Xray │ +│ ${green}15.${plain} Check Status │ +│ ${green}16.${plain} Logs Management │ │────────────────────────────────────────────────│ -│ ${green}16.${plain} Enable Autostart │ -│ ${green}17.${plain} Disable Autostart │ +│ ${green}17.${plain} Enable Autostart │ +│ ${green}18.${plain} Disable Autostart │ │────────────────────────────────────────────────│ -│ ${green}18.${plain} SSL Certificate Management │ -│ ${green}19.${plain} Cloudflare SSL Certificate │ -│ ${green}20.${plain} IP Limit Management │ -│ ${green}21.${plain} Firewall Management │ -│ ${green}22.${plain} SSH Port Forwarding Management │ +│ ${green}19.${plain} SSL Certificate Management │ +│ ${green}20.${plain} Cloudflare SSL Certificate │ +│ ${green}21.${plain} IP Limit Management │ +│ ${green}22.${plain} Firewall Management │ +│ ${green}23.${plain} SSH Port Forwarding Management │ │────────────────────────────────────────────────│ -│ ${green}23.${plain} Enable BBR │ -│ ${green}24.${plain} Update Geo Files │ -│ ${green}25.${plain} Speedtest by Ookla │ +│ ${green}24.${plain} Enable BBR │ +│ ${green}25.${plain} Update Geo Files │ +│ ${green}26.${plain} Speedtest by Ookla │ ╚────────────────────────────────────────────────╝ " show_status - echo && read -rp "Please enter your selection [0-25]: " num + echo && read -rp "Please enter your selection [0-26]: " num case "${num}" in 0) @@ -2253,43 +2265,46 @@ show_menu() { check_install && restart ;; 14) - check_install && status + check_install && restart_xray ;; 15) - check_install && show_log + check_install && status ;; 16) - check_install && enable + check_install && show_log ;; 17) - check_install && disable + check_install && enable ;; 18) - ssl_cert_issue_main + check_install && disable ;; 19) - ssl_cert_issue_CF + ssl_cert_issue_main ;; 20) - iplimit_main + ssl_cert_issue_CF ;; 21) - firewall_menu + iplimit_main ;; 22) - SSH_port_forwarding + firewall_menu ;; 23) - bbr_menu + SSH_port_forwarding ;; 24) - update_geo + bbr_menu ;; 25) + update_geo + ;; + 26) run_speedtest ;; *) - LOGE "Please enter the correct number [0-25]" + LOGE "Please enter the correct number [0-26]" ;; esac } @@ -2305,6 +2320,9 @@ if [[ $# > 0 ]]; then "restart") check_install 0 && restart 0 ;; + "restart-xray") + check_install 0 && restart_xray 0 + ;; "status") check_install 0 && status 0 ;;