diff --git a/.github/update.log b/.github/update.log index 2d1582a76c..40dad360ce 100644 --- a/.github/update.log +++ b/.github/update.log @@ -722,3 +722,4 @@ Update On Tue Jul 30 20:33:50 CEST 2024 Update On Wed Jul 31 20:30:50 CEST 2024 Update On Thu Aug 1 20:31:07 CEST 2024 Update On Fri Aug 2 20:31:28 CEST 2024 +Update On Sat Aug 3 20:32:05 CEST 2024 diff --git a/clash-meta/.github/workflows/build.yml b/clash-meta/.github/workflows/build.yml index 9b8e000a12..b936a57fea 100644 --- a/clash-meta/.github/workflows/build.yml +++ b/clash-meta/.github/workflows/build.yml @@ -155,6 +155,7 @@ jobs: echo "BUILDTIME=$(date)" >> $GITHUB_ENV echo "CGO_ENABLED=0" >> $GITHUB_ENV echo "BUILDTAG=-extldflags --static" >> $GITHUB_ENV + echo "GOTOOLCHAIN=local" >> $GITHUB_ENV - name: Setup NDK if: ${{ matrix.jobs.goos == 'android' }} diff --git a/clash-meta/go.mod b/clash-meta/go.mod index e18f1e554d..57d6eea7ff 100644 --- a/clash-meta/go.mod +++ b/clash-meta/go.mod @@ -7,20 +7,20 @@ require ( github.com/bahlo/generic-list-go v0.2.0 github.com/cilium/ebpf v0.12.3 github.com/coreos/go-iptables v0.7.0 - github.com/dlclark/regexp2 v1.11.0 - github.com/go-chi/chi/v5 v5.0.14 + github.com/dlclark/regexp2 v1.11.2 + github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.2.0 - github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6 + github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9 github.com/klauspost/compress v1.17.9 github.com/klauspost/cpuid/v2 v2.2.8 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2 github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 - github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e + github.com/metacubex/quic-go v0.45.1-0.20240803003931-60a15f6efd94 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.7 @@ -34,7 +34,7 @@ require ( github.com/mroth/weightedrand/v2 v2.1.0 github.com/openacid/low v0.1.21 github.com/oschwald/maxminddb-golang v1.12.0 - github.com/puzpuzpuz/xsync/v3 v3.2.0 + github.com/puzpuzpuz/xsync/v3 v3.4.0 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a @@ -42,7 +42,7 @@ require ( github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e - github.com/samber/lo v1.39.0 + github.com/samber/lo v1.46.0 github.com/shirou/gopsutil/v3 v3.24.5 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 @@ -50,9 +50,9 @@ require ( gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.24.0 - golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 - golang.org/x/net v0.26.0 + golang.org/x/crypto v0.25.0 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 + golang.org/x/net v0.27.0 golang.org/x/sync v0.7.0 golang.org/x/sys v0.22.0 google.golang.org/protobuf v1.34.2 @@ -107,10 +107,10 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/mod v0.18.0 // indirect + golang.org/x/mod v0.19.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.22.0 // indirect + golang.org/x/tools v0.23.0 // indirect ) replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 diff --git a/clash-meta/go.sum b/clash-meta/go.sum index dbc4eafce3..50dac8d203 100644 --- a/clash-meta/go.sum +++ b/clash-meta/go.sum @@ -26,8 +26,8 @@ github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFE 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/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= -github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.2 h1:/u628IuisSTwri5/UKloiIsH8+qF2Pu7xEQX+yIKg68= +github.com/dlclark/regexp2 v1.11.2/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= @@ -42,8 +42,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= -github.com/go-chi/chi/v5 v5.0.14 h1:PyEwo2Vudraa0x/Wl6eDRRW2NXBvekgfxyydcM0WGE0= -github.com/go-chi/chi/v5 v5.0.14/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= @@ -75,8 +75,8 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6 h1:dh8D8FksyMhD64mRMbUhZHWYJfNoNMCxfVq6eexleMw= -github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= +github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9 h1:LZJWucZz7ztCqY6Jsu7N9g124iJ2kt/O62j3+UchZFg= +github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= @@ -103,8 +103,8 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e h1:bLYn3GuRvWDcBDAkIv5kUYIhzHwafDVq635BuybnKqI= -github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= +github.com/metacubex/quic-go v0.45.1-0.20240803003931-60a15f6efd94 h1:wlhwgxRzPLH8Ce0VME35iD2sr7jY2gFrL299/T4C2Sg= +github.com/metacubex/quic-go v0.45.1-0.20240803003931-60a15f6efd94/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 h1:YG/JkwGPbca5rUtEMHIu8ZuqzR7BSVm1iqY8hNoMeMA= @@ -149,8 +149,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/puzpuzpuz/xsync/v3 v3.2.0 h1:9AzuUeF88YC5bK8u2vEG1Fpvu4wgpM1wfPIExfaaDxQ= -github.com/puzpuzpuz/xsync/v3 v3.2.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4= +github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= @@ -172,8 +172,8 @@ github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxe github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= -github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= -github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/samber/lo v1.46.0 h1:w8G+oaCPgz1PoCJztqymCFaKwXt+5cCXn51uPxExFfQ= +github.com/samber/lo v1.46.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -226,18 +226,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -259,15 +259,15 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= diff --git a/clash-nyanpasu/.eslintignore b/clash-nyanpasu/.eslintignore new file mode 100644 index 0000000000..f8092e0cd6 --- /dev/null +++ b/clash-nyanpasu/.eslintignore @@ -0,0 +1 @@ +frontend/nyanpasu/auto-imports.d.ts diff --git a/clash-nyanpasu/.eslintrc.cjs b/clash-nyanpasu/.eslintrc.cjs index a205a94e39..ccbfd73cd9 100644 --- a/clash-nyanpasu/.eslintrc.cjs +++ b/clash-nyanpasu/.eslintrc.cjs @@ -18,6 +18,7 @@ module.exports = { "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off", "@typescript-eslint/no-unused-vars": "warn", "@typescript-eslint/no-explicit-any": "warn", + "react/jsx-no-undef": "off", "react/react-in-jsx-scope": "off", "@typescript-eslint/no-namespace": "off", "react-compiler/react-compiler": "error", diff --git a/clash-nyanpasu/.prettierignore b/clash-nyanpasu/.prettierignore index c3f863e3b9..8d07b831e1 100644 --- a/clash-nyanpasu/.prettierignore +++ b/clash-nyanpasu/.prettierignore @@ -7,3 +7,4 @@ pnpm-lock.yaml *.lock *.wxs frontend/nyanpasu/src/router.ts +frontend/nyanpasu/auto-imports.d.ts diff --git a/clash-nyanpasu/backend/Cargo.lock b/clash-nyanpasu/backend/Cargo.lock index 22397d1cb4..d38ceae91b 100644 --- a/clash-nyanpasu/backend/Cargo.lock +++ b/clash-nyanpasu/backend/Cargo.lock @@ -642,6 +642,15 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.69.4" @@ -1193,10 +1202,13 @@ dependencies = [ "async-trait", "atomic_enum", "auto-launch", + "axum", "backon", "base64 0.22.1", + "bincode", "boa_engine", "boa_utils", + "bytes", "chrono", "clap", "cocoa 0.25.0", @@ -1223,6 +1235,7 @@ dependencies = [ "indexmap 2.3.0", "log", "md-5", + "mime", "mlua", "nanoid", "num_cpus", @@ -1249,6 +1262,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml 0.9.34+deprecated", + "sha2 0.10.8", "simd-json", "single-instance", "sysinfo", @@ -3892,7 +3906,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -5727,9 +5741,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -7160,12 +7174,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" dependencies = [ "cfg-if", "fastrand 2.1.0", + "once_cell", "rustix 0.38.34", "windows-sys 0.52.0", ] diff --git a/clash-nyanpasu/backend/tauri/Cargo.toml b/clash-nyanpasu/backend/tauri/Cargo.toml index e4267e8f1c..5a93de6168 100644 --- a/clash-nyanpasu/backend/tauri/Cargo.toml +++ b/clash-nyanpasu/backend/tauri/Cargo.toml @@ -59,6 +59,10 @@ tauri = { version = "1.5.4", features = [ ] } window-vibrancy = { version = "0.5.0" } window-shadows = { version = "0.2.2" } +axum = "0.7" +mime = "0.3" +bincode = "1" +bytes = { version = "1", features = ["serde"] } wry = { version = "0.24.6" } semver = "1.0" zip = "2.0.0" @@ -126,7 +130,7 @@ mlua = { version = "0.9", features = [ "vendored", ] } enumflags2 = "0.7" - +sha2 = "0.10" [target.'cfg(target_os = "macos")'.dependencies] cocoa = "0.25.0" objc = "0.2.7" diff --git a/clash-nyanpasu/backend/tauri/src/config/nyanpasu/mod.rs b/clash-nyanpasu/backend/tauri/src/config/nyanpasu/mod.rs index dbe16cfa51..16c0b6a415 100644 --- a/clash-nyanpasu/backend/tauri/src/config/nyanpasu/mod.rs +++ b/clash-nyanpasu/backend/tauri/src/config/nyanpasu/mod.rs @@ -2,6 +2,7 @@ use crate::utils::{dirs, help}; use anyhow::Result; // use log::LevelFilter; use enumflags2::bitflags; +use semver::Op; use serde::{Deserialize, Serialize}; mod clash_strategy; pub mod logging; @@ -99,6 +100,25 @@ pub enum ProxiesSelectorMode { Submenu, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)] +#[serde(rename_all = "snake_case")] +pub enum TunStack { + System, + #[default] + Gvisor, + Mixed, +} + +impl AsRef for TunStack { + fn as_ref(&self) -> &str { + match self { + TunStack::System => "system", + TunStack::Gvisor => "gvisor", + TunStack::Mixed => "mixed", + } + } +} + /// ### `verge.yaml` schema #[derive(Default, Debug, Clone, Deserialize, Serialize)] pub struct IVerge { @@ -210,6 +230,12 @@ pub struct IVerge { /// 是否启用代理托盘选择 pub clash_tray_selector: Option, + + pub always_on_top: Option, + + /// Tun 堆栈选择 + /// TODO: 弃用此字段,转移到 clash config 里 + pub tun_stack: Option, } #[derive(Default, Debug, Clone, Deserialize, Serialize)] @@ -305,6 +331,7 @@ impl IVerge { enable_auto_check_update: Some(true), clash_tray_selector: Some(ProxiesSelectorMode::default()), enable_service_mode: Some(false), + always_on_top: Some(false), ..Self::default() } } @@ -364,5 +391,7 @@ impl IVerge { patch!(window_size_state); patch!(clash_strategy); patch!(clash_tray_selector); + patch!(tun_stack); + patch!(always_on_top); } } diff --git a/clash-nyanpasu/backend/tauri/src/core/service/mod.rs b/clash-nyanpasu/backend/tauri/src/core/service/mod.rs index 0a0dc25c0a..47b4a92bda 100644 --- a/clash-nyanpasu/backend/tauri/src/core/service/mod.rs +++ b/clash-nyanpasu/backend/tauri/src/core/service/mod.rs @@ -27,9 +27,7 @@ pub async fn init_service() { .. }) = control::status().await { - if !enable_service { - control::stop_service().await.unwrap(); - } else { + if enable_service { ipc::spawn_health_check() } } diff --git a/clash-nyanpasu/backend/tauri/src/enhance/tun.rs b/clash-nyanpasu/backend/tauri/src/enhance/tun.rs index 5bfd17f6ef..19e1643ffb 100644 --- a/clash-nyanpasu/backend/tauri/src/enhance/tun.rs +++ b/clash-nyanpasu/backend/tauri/src/enhance/tun.rs @@ -1,6 +1,9 @@ use serde_yaml::{Mapping, Value}; -use crate::config::{nyanpasu::ClashCore, Config}; +use crate::config::{ + nyanpasu::{ClashCore, TunStack}, + Config, +}; macro_rules! revise { ($map: expr, $key: expr, $val: expr) => { @@ -43,7 +46,17 @@ pub fn use_tun(mut config: Mapping, enable: bool) -> Mapping { if core == ClashCore::ClashRs { append!(tun_val, "device-id", "dev://utun1989"); } else { - append!(tun_val, "stack", "gvisor"); + let mut tun_stack = { + *Config::verge() + .latest() + .tun_stack + .as_ref() + .unwrap_or(&TunStack::default()) + }; + if core == ClashCore::ClashPremium && tun_stack == TunStack::Mixed { + tun_stack = TunStack::Gvisor; + } + append!(tun_val, "stack", AsRef::::as_ref(&tun_stack)); append!(tun_val, "dns-hijack", vec!["any:53"]); append!(tun_val, "auto-route", true); append!(tun_val, "auto-detect-interface", true); diff --git a/clash-nyanpasu/backend/tauri/src/feat.rs b/clash-nyanpasu/backend/tauri/src/feat.rs index 311db24061..3c8f44db47 100644 --- a/clash-nyanpasu/backend/tauri/src/feat.rs +++ b/clash-nyanpasu/backend/tauri/src/feat.rs @@ -6,7 +6,7 @@ //! use crate::{ config::*, - core::*, + core::{service::ipc::get_ipc_state, *}, log_err, utils::{self, help::get_clash_external_port, resolve}, }; @@ -283,21 +283,15 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> { let enable_tray_selector = patch.clash_tray_selector; let res = || async move { - #[cfg(target_os = "windows")] - { - let service_mode = patch.enable_service_mode; + let service_mode = patch.enable_service_mode; + let ipc_state = get_ipc_state(); + if service_mode.is_some() && ipc_state.is_connected() { + log::debug!(target: "app", "change service mode to {}", service_mode.unwrap()); - if service_mode.is_some() { - log::debug!(target: "app", "change service mode to {}", service_mode.unwrap()); - - Config::generate().await?; - CoreManager::global().run_core().await?; - } else if tun_mode.is_some() { - update_core_config().await?; - } + Config::generate().await?; + CoreManager::global().run_core().await?; } - #[cfg(not(target_os = "windows"))] if tun_mode.is_some() { update_core_config().await?; } diff --git a/clash-nyanpasu/backend/tauri/src/ipc.rs b/clash-nyanpasu/backend/tauri/src/ipc.rs index 6bf7fe6629..543c1c4e6f 100644 --- a/clash-nyanpasu/backend/tauri/src/ipc.rs +++ b/clash-nyanpasu/backend/tauri/src/ipc.rs @@ -15,6 +15,7 @@ use anyhow::{Context, Result}; use chrono::Local; use indexmap::IndexMap; use log::debug; +use profile::item_type::ProfileItemType; use serde_yaml::Mapping; use std::collections::VecDeque; use sysproxy::Sysproxy; @@ -102,10 +103,36 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult { /// 修改某个profile item的 #[tauri::command] -pub fn patch_profile(index: String, profile: ProfileItem) -> CmdResult { +pub async fn patch_profile(index: String, profile: ProfileItem) -> CmdResult { tracing::debug!("patch profile: {index} with {profile:?}"); - wrap_err!(Config::profiles().data().patch_item(index, profile))?; + wrap_err!(Config::profiles().data().patch_item(index.clone(), profile))?; ProfilesJobGuard::global().lock().refresh(); + let need_update = { + let profiles = Config::profiles(); + let profiles = profiles.latest(); + match (&profiles.chain, &profiles.current) { + (Some(chains), _) if chains.contains(&index) => true, + (_, Some(current_chain)) if current_chain == &index => true, + (_, Some(current_chain)) => match profiles.get_item(current_chain) { + Ok(item) => item + .chains + .as_ref() + .map_or(false, |chain| chain.contains(&index)), + Err(_) => false, + }, + _ => false, + } + }; + if need_update { + match CoreManager::global().update_config().await { + Ok(_) => { + handle::Handle::refresh_clash(); + } + Err(err) => { + log::error!(target: "app", "{err}"); + } + } + } Ok(()) } @@ -131,7 +158,14 @@ pub fn read_profile_file(index: String) -> CmdResult { let profiles = Config::profiles(); let profiles = profiles.latest(); let item = wrap_err!(profiles.get_item(&index))?; - let data = wrap_err!(item.read_file())?; + let data = match item.r#type.as_ref().unwrap_or(&ProfileItemType::Local) { + ProfileItemType::Local | ProfileItemType::Remote => { + let raw = wrap_err!(item.read_file())?; + let data = wrap_err!(serde_yaml::from_str::(&raw))?; + wrap_err!(serde_yaml::to_string(&data).context("failed to convert yaml to string"))? + } + _ => wrap_err!(item.read_file())?, + }; Ok(data) } @@ -450,6 +484,11 @@ pub fn restart_application(app_handle: tauri::AppHandle) -> CmdResult { Ok(()) } +#[tauri::command] +pub fn get_server_port() -> CmdResult { + Ok(*crate::server::SERVER_PORT) +} + #[cfg(not(windows))] #[tauri::command] pub async fn set_custom_app_dir(_path: String) -> CmdResult { @@ -488,17 +527,56 @@ pub mod service { #[tauri::command] pub async fn start_service() -> CmdResult { - wrap_err!(service::control::start_service().await) + let res = wrap_err!(service::control::start_service().await); + let enabled_service = { + *crate::config::Config::verge() + .latest() + .enable_service_mode + .as_ref() + .unwrap_or(&false) + }; + if enabled_service { + if let Err(e) = crate::core::CoreManager::global().run_core().await { + log::error!(target: "app", "{e}"); + } + } + res } #[tauri::command] pub async fn stop_service() -> CmdResult { - wrap_err!(service::control::stop_service().await) + let res = wrap_err!(service::control::stop_service().await); + let enabled_service = { + *crate::config::Config::verge() + .latest() + .enable_service_mode + .as_ref() + .unwrap_or(&false) + }; + if enabled_service { + if let Err(e) = crate::core::CoreManager::global().run_core().await { + log::error!(target: "app", "{e}"); + } + } + res } #[tauri::command] pub async fn restart_service() -> CmdResult { - wrap_err!(service::control::restart_service().await) + let res = wrap_err!(service::control::restart_service().await); + let enabled_service = { + *crate::config::Config::verge() + .latest() + .enable_service_mode + .as_ref() + .unwrap_or(&false) + }; + if enabled_service { + if let Err(e) = crate::core::CoreManager::global().run_core().await { + log::error!(target: "app", "{e}"); + } + } + res } } diff --git a/clash-nyanpasu/backend/tauri/src/main.rs b/clash-nyanpasu/backend/tauri/src/main.rs index 7c611b65bb..e572bc07a3 100644 --- a/clash-nyanpasu/backend/tauri/src/main.rs +++ b/clash-nyanpasu/backend/tauri/src/main.rs @@ -19,6 +19,7 @@ mod core; mod enhance; mod feat; mod ipc; +mod server; mod utils; use crate::{ config::Config, @@ -67,6 +68,16 @@ fn main() -> std::io::Result<()> { #[cfg(not(feature = "verge-dev"))] tauri_plugin_deep_link::prepare("moe.elaina.clash.nyanpasu"); + // Custom scheme check + #[cfg(not(target_os = "macos"))] + // on macos the plugin handles this (macos doesn't use cli args for the url) + let custom_schema = match std::env::args().nth(1) { + Some(url) => url::Url::parse(&url).ok(), + None => None, + }; + #[cfg(target_os = "macos")] + let custom_schema: Option = None; + // 单例检测 let single_instance_result = utils::init::check_singleton(); @@ -77,14 +88,16 @@ fn main() -> std::io::Result<()> { }; rust_i18n::set_locale(locale); - if let Err(e) = init::run_pending_migrations() { - utils::dialog::panic_dialog( - &format!( - "Failed to finish migration event: {}\nYou can see the detailed information at migration.log in your local data dir.\nYou're supposed to submit it as the attachment of new issue.", - e, - ) - ); - std::process::exit(1); + if single_instance_result.is_ok() { + if let Err(e) = init::run_pending_migrations() { + utils::dialog::panic_dialog( + &format!( + "Failed to finish migration event: {}\nYou can see the detailed information at migration.log in your local data dir.\nYou're supposed to submit it as the attachment of new issue.", + e, + ) + ); + std::process::exit(1); + } } crate::log_err!(init::init_config()); @@ -101,7 +114,9 @@ fn main() -> std::io::Result<()> { rust_i18n::set_locale(verge.as_str()); // show a dialog to print the single instance error - let _singleton = single_instance_result.unwrap(); // hold the guard until the end of the program + if custom_schema.is_none() { + let _singleton = single_instance_result.unwrap(); // hold the guard until the end of the program + } #[allow(unused_mut)] let mut builder = tauri::Builder::default() @@ -112,7 +127,7 @@ fn main() -> std::io::Result<()> { let handle = app.handle().clone(); // For start new app from schema #[cfg(not(target_os = "macos"))] - if let Some(url) = std::env::args().nth(1) { + if let Some(url) = custom_schema { log::info!(target: "app", "started with schema"); if Config::verge().data().enable_silent_start.unwrap_or(true) { resolve::create_window(&handle.clone()); @@ -128,7 +143,7 @@ fn main() -> std::io::Result<()> { .unwrap(); }); } - + // This operation should terminate the app if app is called by custom scheme and this instance is not the primary instance log_err!(tauri_plugin_deep_link::register( &["clash-nyanpasu", "clash"], move |request| { @@ -137,6 +152,13 @@ fn main() -> std::io::Result<()> { handle.emit_all("scheme-request-received", request).unwrap(); } )); + std::thread::spawn(move || { + nyanpasu_utils::runtime::block_on(async move { + server::run(*server::SERVER_PORT) + .await + .expect("failed to start server"); + }); + }); Ok(()) }) .on_system_tray_event(core::tray::Tray::on_system_tray_event) @@ -202,6 +224,7 @@ fn main() -> std::io::Result<()> { ipc::update_proxy_provider, ipc::restart_application, ipc::collect_envs, + ipc::get_server_port, ]); #[cfg(target_os = "macos")] diff --git a/clash-nyanpasu/backend/tauri/src/server/mod.rs b/clash-nyanpasu/backend/tauri/src/server/mod.rs new file mode 100644 index 0000000000..09117954b6 --- /dev/null +++ b/clash-nyanpasu/backend/tauri/src/server/mod.rs @@ -0,0 +1,92 @@ +pub(crate) use crate::utils::candy::get_reqwest_client; +use anyhow::{anyhow, Result}; +use axum::{ + body::Body, + extract::Query, + http::{Response, StatusCode}, + routing::get, + Router, +}; +use base64::{prelude::BASE64_STANDARD, Engine}; +use bytes::Bytes; +use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::borrow::Cow; +use tracing_attributes::instrument; +use url::Url; +pub static SERVER_PORT: Lazy = Lazy::new(|| port_scanner::request_open_port().unwrap()); + +#[derive(Deserialize)] +struct CacheIcon { + /// should be encoded as base64 + url: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +struct CacheFile<'n> { + mime: Cow<'n, str>, + bytes: Bytes, +} + +async fn cache_icon_inner<'n>(url: &str) -> Result> { + let url = BASE64_STANDARD.decode(url)?; + let url = String::from_utf8_lossy(&url); + let url = Url::parse(&url)?; + // get filename + let hash = Sha256::digest(url.as_str().as_bytes()); + let cache_dir = crate::utils::dirs::cache_dir()?.join("icons"); + if !cache_dir.exists() { + std::fs::create_dir_all(&cache_dir)?; + } + let cache_file = cache_dir.join(format!("{:x}.bin", hash)); + if cache_file.exists() { + let cache_file = tokio::fs::read(cache_file).await?; + let cache_file: CacheFile = bincode::deserialize(&cache_file)?; + return Ok(cache_file); + } + let client = get_reqwest_client()?; + let response = client.get(url).send().await?.error_for_status()?; + let mime = response + .headers() + .get("content-type") + .ok_or(anyhow!("no content-type"))? + .to_str()? + .to_string(); + + let bytes = response.bytes().await?; + let data = CacheFile { + mime: Cow::Owned(mime), + bytes, + }; + tokio::fs::write(cache_file, bincode::serialize(&data)?).await?; + Ok(data) +} + +async fn cache_icon(query: Query) -> Response { + match cache_icon_inner(&query.url).await { + Ok(data) => { + let mut response = Response::new(Body::from(data.bytes)); + response + .headers_mut() + .insert("content-type", data.mime.parse().unwrap()); + response + } + Err(e) => { + let mut response = Response::new(Body::from(e.to_string())); + *response.status_mut() = StatusCode::BAD_REQUEST; + response + } + } +} + +#[instrument] +pub async fn run(port: u16) -> std::io::Result<()> { + let app = Router::new().route("/cache/icon", get(cache_icon)); + let listener = tokio::net::TcpListener::bind(format!("127.0.0.1:{port}")).await?; + tracing::debug!( + "internal http server listening on {}", + listener.local_addr()? + ); + axum::serve(listener, app).await +} diff --git a/clash-nyanpasu/backend/tauri/src/utils/dirs.rs b/clash-nyanpasu/backend/tauri/src/utils/dirs.rs index d6601acdf1..0559f55a07 100644 --- a/clash-nyanpasu/backend/tauri/src/utils/dirs.rs +++ b/clash-nyanpasu/backend/tauri/src/utils/dirs.rs @@ -194,18 +194,18 @@ pub fn app_resources_dir() -> Result { } /// Cache dir, it safe to clean up -pub fn cache_dir() -> Result { - let mut dir = dirs::cache_dir() - .ok_or(anyhow::anyhow!("failed to get the cache dir"))? - .join(APP_DIR_PLACEHOLDER.as_ref()); - if cfg!(windows) { - dir.push("cache"); - } - if !dir.exists() { - fs::create_dir_all(&dir)?; - } - Ok(dir) -} +// pub fn cache_dir() -> Result { +// let mut dir = dirs::cache_dir() +// .ok_or(anyhow::anyhow!("failed to get the cache dir"))? +// .join(APP_DIR_PLACEHOLDER.as_ref()); +// if cfg!(windows) { +// dir.push("cache"); +// } +// if !dir.exists() { +// fs::create_dir_all(&dir)?; +// } +// Ok(dir) +// } /// App install dir, sidecars should placed here pub fn app_install_dir() -> Result { @@ -247,6 +247,10 @@ pub fn clash_pid_path() -> Result { Ok(app_data_dir()?.join("clash.pid")) } +pub fn cache_dir() -> Result { + Ok(app_data_dir()?.join("cache")) +} + #[cfg(windows)] #[deprecated( since = "1.6.0", diff --git a/clash-nyanpasu/backend/tauri/src/utils/resolve.rs b/clash-nyanpasu/backend/tauri/src/utils/resolve.rs index e9d00ed6b7..3eb974ea3c 100644 --- a/clash-nyanpasu/backend/tauri/src/utils/resolve.rs +++ b/clash-nyanpasu/backend/tauri/src/utils/resolve.rs @@ -150,6 +150,14 @@ pub fn create_window(app_handle: &AppHandle) { return; } + let always_on_top = { + *Config::verge() + .latest() + .always_on_top + .as_ref() + .unwrap_or(&false) + }; + let mut builder = tauri::window::WindowBuilder::new( app_handle, "main".to_string(), @@ -157,7 +165,9 @@ pub fn create_window(app_handle: &AppHandle) { ) .title("Clash Nyanpasu") .fullscreen(false) + .always_on_top(always_on_top) .min_inner_size(600.0, 520.0); + let win_state = &Config::verge().latest().window_size_state.clone(); match win_state { Some(_) => { diff --git a/clash-nyanpasu/frontend/interface/service/tauri.ts b/clash-nyanpasu/frontend/interface/service/tauri.ts index 3f73824f0e..271c91935e 100644 --- a/clash-nyanpasu/frontend/interface/service/tauri.ts +++ b/clash-nyanpasu/frontend/interface/service/tauri.ts @@ -203,3 +203,11 @@ export const save_window_size_state = async () => { export const collect_envs = async () => { return await invoke("collect_envs"); }; + +export const getRuntimeYaml = async () => { + return await invoke("get_runtime_yaml"); +}; + +export const getServerPort = async () => { + return await invoke("get_server_port"); +}; diff --git a/clash-nyanpasu/frontend/interface/service/types.ts b/clash-nyanpasu/frontend/interface/service/types.ts index d35e6183ae..1258e439ad 100644 --- a/clash-nyanpasu/frontend/interface/service/types.ts +++ b/clash-nyanpasu/frontend/interface/service/types.ts @@ -45,6 +45,8 @@ export interface VergeConfig { clash_strategy?: { external_controller_port_strategy: "fixed" | "random" | "allow_fallback"; }; + tun_stack?: "system" | "gvisor" | "mixed"; + always_on_top?: boolean; } export interface ClashInfo { diff --git a/clash-nyanpasu/frontend/nyanpasu/auto-imports.d.ts b/clash-nyanpasu/frontend/nyanpasu/auto-imports.d.ts new file mode 100644 index 0000000000..0ce674aa0b --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/auto-imports.d.ts @@ -0,0 +1,9 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +export {} +declare global { + const IconMdiTextBoxCheckOutline: (typeof import("~icons/mdi/text-box-check-outline.jsx"))["default"] +} diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json index a7ce49bb52..57cdb4823f 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -48,6 +48,7 @@ "devDependencies": { "@emotion/babel-plugin": "11.12.0", "@emotion/react": "11.13.0", + "@iconify/json": "2.2.233", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", "@vitejs/plugin-react": "4.3.1", @@ -56,6 +57,8 @@ "sass": "1.77.8", "shiki": "1.12.1", "tailwindcss-textshadow": "2.1.3", + "unplugin-auto-import": "0.18.2", + "unplugin-icons": "0.19.1", "vite": "5.3.5", "vite-plugin-monaco-editor": "1.1.3", "vite-plugin-sass-dts": "1.3.25", diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/layout/layout-control.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/layout/layout-control.tsx index 914c4cbe4b..711b84bcdb 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/components/layout/layout-control.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/layout/layout-control.tsx @@ -1,3 +1,4 @@ +import { useMemoizedFn } from "ahooks"; import { debounce } from "lodash-es"; import { useEffect, useState } from "react"; import { classNames } from "@/utils"; @@ -7,9 +8,11 @@ import { CropSquareRounded, FilterNoneRounded, HorizontalRuleRounded, + PushPin, + PushPinOutlined, } from "@mui/icons-material"; import { alpha, Button, ButtonProps, useTheme } from "@mui/material"; -import { save_window_size_state } from "@nyanpasu/interface"; +import { save_window_size_state, useNyanpasu } from "@nyanpasu/interface"; import { platform, type Platform } from "@tauri-apps/api/os"; import { appWindow } from "@tauri-apps/api/window"; @@ -29,6 +32,7 @@ const CtrlButton = (props: ButtonProps) => { }; export const LayoutControl = ({ className }: { className?: string }) => { + const { nyanpasuConfig, setNyanpasuConfig } = useNyanpasu(); const [isMaximized, setIsMaximized] = useState(false); const [platfrom, setPlatform] = useState("win32"); @@ -46,6 +50,12 @@ export const LayoutControl = ({ className }: { className?: string }) => { } }; + const toggleAlwaysOnTop = useMemoizedFn(async () => { + const isAlwaysOnTop = !!nyanpasuConfig?.always_on_top; + await setNyanpasuConfig({ always_on_top: !isAlwaysOnTop }); + await appWindow.setAlwaysOnTop(!isAlwaysOnTop); + }); + useEffect(() => { // Update the maximized state updateMaximized(); @@ -67,6 +77,17 @@ export const LayoutControl = ({ className }: { className?: string }) => { return (
+ + {nyanpasuConfig?.always_on_top ? ( + + ) : ( + + )} + + appWindow.minimize()}> diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/profiles/runtime-config-diff-dialog.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/profiles/runtime-config-diff-dialog.tsx new file mode 100644 index 0000000000..ababcf8408 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/profiles/runtime-config-diff-dialog.tsx @@ -0,0 +1,83 @@ +import { useAtomValue } from "jotai"; +import { useEffect, useRef } from "react"; +import { useTranslation } from "react-i18next"; +import useSWR from "swr"; +import { monaco } from "@/services/monaco"; +import { themeMode } from "@/store"; +import { getRuntimeYaml, useClash } from "@nyanpasu/interface"; +import { BaseDialog } from "@nyanpasu/ui"; + +export type RuntimeConfigDiffDialogProps = { + open: boolean; + onClose: () => void; +}; + +export default function RuntimeConfigDiffDialog({ + open, + onClose, +}: RuntimeConfigDiffDialogProps) { + const { t } = useTranslation(); + const { getProfiles, getProfileFile } = useClash(); + const currentProfileUid = getProfiles.data?.current; + const mode = useAtomValue(themeMode); + const { + data: runtimeConfig, + isLoading: isLoadingRuntimeConfig, + error: errorRuntimeConfig, + } = useSWR(open ? "/getRuntimeConfigYaml" : null, getRuntimeYaml); + const { + data: profileConfig, + isLoading: isLoadingProfileConfig, + error: errorProfileConfig, + } = useSWR( + open ? `/readProfileFile?uid=${currentProfileUid}` : null, + async (key) => { + const url = new URL(key, window.location.origin); + return await getProfileFile(url.searchParams.get("uid")!); + }, + { + revalidateOnFocus: true, + refreshInterval: 0, + }, + ); + const monacoRef = useRef(null); + const editorRef = useRef(null); + const domRef = useRef(null); + useEffect(() => { + if (open && runtimeConfig && profileConfig) { + console.log("init monaco"); + const run = async () => { + const { monaco } = await import("@/services/monaco"); + monacoRef.current = monaco; + console.log(domRef.current); + editorRef.current = monaco.editor.createDiffEditor(domRef.current!, { + theme: mode === "light" ? "vs" : "vs-dark", + minimap: { enabled: false }, + automaticLayout: true, + readOnly: true, + }); + editorRef.current.setModel({ + original: monaco.editor.createModel(profileConfig, "yaml"), + modified: monaco.editor.createModel(runtimeConfig, "yaml"), + }); + }; + run().catch(console.error); + } + return () => { + monacoRef.current = null; + editorRef.current?.dispose(); + }; + }, [mode, open, runtimeConfig, profileConfig]); + console.log(currentProfileUid); + if (!currentProfileUid) { + return null; + } + + return ( + +
+
+
+ + ); +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/profiles/script-dialog.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/profiles/script-dialog.tsx index 8862ac9e36..08aebfaba2 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/components/profiles/script-dialog.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/profiles/script-dialog.tsx @@ -78,7 +78,7 @@ export const ScriptDialog = ({ desc: "", }); } - }, [isEdit]); + }, [form, isEdit, profile]); const [openMonaco, setOpenMonaco] = useState(false); diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/proxies/group-list.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/proxies/group-list.tsx index 1494e80dac..e332c7164b 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/components/proxies/group-list.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/proxies/group-list.tsx @@ -1,5 +1,6 @@ import { useAtom } from "jotai"; -import { memo } from "react"; +import { memo, useMemo } from "react"; +import useSWR from "swr"; import { Virtualizer } from "virtua"; import { proxyGroupAtom } from "@/store"; import { @@ -9,16 +10,30 @@ import { ListItemIcon, ListItemText, } from "@mui/material"; -import { useClashCore } from "@nyanpasu/interface"; +import { getServerPort, useClashCore } from "@nyanpasu/interface"; const IconRender = memo(function IconRender({ icon }: { icon: string }) { + const { + data: serverPort, + isLoading, + error, + } = useSWR("/getServerPort", getServerPort); const src = icon.trim().startsWith(" { + if (!src.startsWith("http")) { + return src; + } + return `http://localhost:${serverPort}/cache/icon?url=${btoa(src)}`; + }, [src, serverPort]); + console.log(serverPort, isLoading, error); + if (isLoading || error) { + return null; + } return ( - + ); }); diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/setting/setting-clash-base.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/setting/setting-clash-base.tsx index d067a04bfc..ab2985b4a7 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/components/setting/setting-clash-base.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/setting/setting-clash-base.tsx @@ -1,8 +1,10 @@ +import { useMemo } from "react"; import { useTranslation } from "react-i18next"; +import { useCoreType } from "@/hooks/use-store"; import getSystem from "@/utils/get-system"; import { message } from "@/utils/notification"; import { Button, List, ListItem, ListItemText } from "@mui/material"; -import { pullupUWPTool } from "@nyanpasu/interface"; +import { pullupUWPTool, useNyanpasu, VergeConfig } from "@nyanpasu/interface"; import { BaseCard, MenuItem, SwitchItem } from "@nyanpasu/ui"; import { clash } from "./modules"; @@ -13,8 +15,8 @@ const isWIN = getSystem() === "windows"; export const SettingClashBase = () => { const { t } = useTranslation(); - // const [coreType] = useCoreType(); - + const [coreType] = useCoreType(); + const { nyanpasuConfig, setNyanpasuConfig } = useNyanpasu(); const clickUWP = async () => { try { await pullupUWPTool(); @@ -26,6 +28,23 @@ export const SettingClashBase = () => { } }; + const tunStackOptions = useMemo(() => { + const options: { + [key: string]: string; + } = { + system: "System", + gvisor: "gVisor", + mixed: "Mixed", + }; + if (coreType === "clash") { + delete options.mixed; + } + return options; + }, [coreType]); + const tunStackSelected = useMemo(() => { + const stack = nyanpasuConfig?.tun_stack || "gvisor"; + return stack in tunStackOptions ? stack : "gvisor"; + }, [nyanpasuConfig?.tun_stack, tunStackOptions]); return ( @@ -36,6 +55,22 @@ export const SettingClashBase = () => { + {coreType !== "clash-rs" && ( + { + const payload = { + tun_stack: value as NonNullable, + } as Partial; + if (nyanpasuConfig?.enable_tun_mode) { + payload.enable_tun_mode = true; // just to reload clash config + } + setNyanpasuConfig(payload); + }} + /> + )} { }); const handleUpdateCore = useLockFn( - async (core: Required["clash_core"]) => { + async (core: Required["clash_core"]) => { try { loading.mask = true; diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/_app.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/_app.tsx index d4c0652ce9..93c867db37 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/pages/_app.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/_app.tsx @@ -32,7 +32,14 @@ export default function App() { const isDrawer = useMemo(() => Boolean(column === 1), [column]); return ( - + diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/profiles.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/profiles.tsx index 2fbc5d7480..0cc40ce747 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/pages/profiles.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/profiles.tsx @@ -1,4 +1,5 @@ import { useAtom } from "jotai"; +import { useState } from "react"; import { useTranslation } from "react-i18next"; import { atomChainsSelected, @@ -8,10 +9,11 @@ import NewProfileButton from "@/components/profiles/new-profile-button"; import ProfileItem from "@/components/profiles/profile-item"; import ProfileSide from "@/components/profiles/profile-side"; import { QuickImport } from "@/components/profiles/quick-import"; +import RuntimeConfigDiffDialog from "@/components/profiles/runtime-config-diff-dialog"; import { filterProfiles } from "@/components/profiles/utils"; import { Public } from "@mui/icons-material"; import Masonry from "@mui/lab/Masonry"; -import { Button } from "@mui/material"; +import { Button, IconButton } from "@mui/material"; import { Profile, useClash } from "@nyanpasu/interface"; import { SidePage } from "@nyanpasu/ui"; @@ -46,12 +48,25 @@ export const ProfilePage = () => { setGlobalChain(false); }; + const [runtimeConfigViewerOpen, setRuntimeConfigViewerOpen] = useState(false); + console.log(runtimeConfigViewerOpen); return ( + setRuntimeConfigViewerOpen(false)} + /> + { + setRuntimeConfigViewerOpen(true); + }} + > + +