diff --git a/.github/update.log b/.github/update.log index 574cde8abf..5fd2a32927 100644 --- a/.github/update.log +++ b/.github/update.log @@ -1092,3 +1092,4 @@ Update On Wed Aug 13 20:43:46 CEST 2025 Update On Thu Aug 14 20:40:52 CEST 2025 Update On Fri Aug 15 20:40:48 CEST 2025 Update On Sat Aug 16 20:36:37 CEST 2025 +Update On Sun Aug 17 20:39:03 CEST 2025 diff --git a/clash-meta/adapter/outbound/vless.go b/clash-meta/adapter/outbound/vless.go index b26ddb07f2..3db99e1feb 100644 --- a/clash-meta/adapter/outbound/vless.go +++ b/clash-meta/adapter/outbound/vless.go @@ -3,7 +3,6 @@ package outbound import ( "context" "crypto/tls" - "errors" "fmt" "net" "net/http" @@ -457,11 +456,6 @@ func NewVless(option VlessOption) (*Vless, error) { if err != nil { return nil, err } - if v.encryption != nil { - if option.Flow != "" { - return nil, errors.New(`vless "encryption" doesn't support "flow" yet`) - } - } v.realityConfig, err = v.option.RealityOpts.Parse() if err != nil { diff --git a/clash-meta/go.mod b/clash-meta/go.mod index 66c1556fc5..1845c53242 100644 --- a/clash-meta/go.mod +++ b/clash-meta/go.mod @@ -31,7 +31,7 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.6 github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 github.com/metacubex/sing-tun v0.4.7 - github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d + github.com/metacubex/sing-vmess v0.2.4-0.20250817075824-5e05f123ccdd github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/smux v0.0.0-20250503055512-501391591dee github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 diff --git a/clash-meta/go.sum b/clash-meta/go.sum index 3c2d3e3fb4..90fcddcc18 100644 --- a/clash-meta/go.sum +++ b/clash-meta/go.sum @@ -131,8 +131,8 @@ github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MY github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E= github.com/metacubex/sing-tun v0.4.7 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778= github.com/metacubex/sing-tun v0.4.7/go.mod h1:xHecZRwBnKWe6zG9amAK9cXf91lF6blgjBqm+VvOrmU= -github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d h1:koSVAQiYqU/qtJ527e+wbsEPvTaM39HW75LSvh04IT0= -github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= +github.com/metacubex/sing-vmess v0.2.4-0.20250817075824-5e05f123ccdd h1:VfD6UxKPAg7u9rPyxl18lQkpE9s8dZq0u2cPAgQShWs= +github.com/metacubex/sing-vmess v0.2.4-0.20250817075824-5e05f123ccdd/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80= github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113awp7P6odM2okB5s60HUyF0FMqKmo= diff --git a/clash-meta/listener/inbound/vless_test.go b/clash-meta/listener/inbound/vless_test.go index e344aa2e88..c110228db3 100644 --- a/clash-meta/listener/inbound/vless_test.go +++ b/clash-meta/listener/inbound/vless_test.go @@ -102,6 +102,11 @@ func TestInboundVless_Encryption(t *testing.T) { Encryption: "8min-vless-mlkem768client-" + clientBase64, } testInboundVless(t, inboundOptions, outboundOptions) + t.Run("xtls-rprx-vision", func(t *testing.T) { + outboundOptions := outboundOptions + outboundOptions.Flow = "xtls-rprx-vision" + testInboundVless(t, inboundOptions, outboundOptions) + }) }) t.Run("-xored-", func(t *testing.T) { inboundOptions := inbound.VlessOption{ diff --git a/clash-meta/listener/sing_vless/server.go b/clash-meta/listener/sing_vless/server.go index 0e729cab99..d386ef670b 100644 --- a/clash-meta/listener/sing_vless/server.go +++ b/clash-meta/listener/sing_vless/server.go @@ -43,6 +43,22 @@ func init() { } return true, tlsConn.NetConn(), reflect.TypeOf(tlsConn.Conn).Elem(), unsafe.Pointer(tlsConn.Conn) }) + + vless.RegisterTLS(func(conn net.Conn) (loaded bool, netConn net.Conn, reflectType reflect.Type, reflectPointer unsafe.Pointer) { + tlsConn, loaded := common.Cast[*encryption.ClientConn](conn) + if !loaded { + return + } + return true, tlsConn.Conn, reflect.TypeOf(tlsConn).Elem(), unsafe.Pointer(tlsConn) + }) + + vless.RegisterTLS(func(conn net.Conn) (loaded bool, netConn net.Conn, reflectType reflect.Type, reflectPointer unsafe.Pointer) { + tlsConn, loaded := common.Cast[*encryption.ServerConn](conn) + if !loaded { + return + } + return true, tlsConn.Conn, reflect.TypeOf(tlsConn).Elem(), unsafe.Pointer(tlsConn) + }) } type Listener struct { diff --git a/clash-meta/transport/vless/vision/vision.go b/clash-meta/transport/vless/vision/vision.go index 570a569c34..32634f0ce8 100644 --- a/clash-meta/transport/vless/vision/vision.go +++ b/clash-meta/transport/vless/vision/vision.go @@ -12,6 +12,7 @@ import ( N "github.com/metacubex/mihomo/common/net" tlsC "github.com/metacubex/mihomo/component/tls" + "github.com/metacubex/mihomo/transport/vless/encryption" "github.com/gofrs/uuid/v5" "github.com/metacubex/sing/common" @@ -58,13 +59,27 @@ func NewConn(conn connWithUpstream, userUUID *uuid.UUID) (*Conn, error) { t = reflect.TypeOf(underlying.Conn).Elem() //log.Debugln("t:%v", t) p = unsafe.Pointer(underlying.Conn) + case *encryption.ClientConn: + //log.Debugln("type *encryption.ClientConn") + c.Conn = underlying.Conn + c.tlsConn = underlying + t = reflect.TypeOf(underlying).Elem() + p = unsafe.Pointer(underlying) + case *encryption.ServerConn: + //log.Debugln("type *encryption.ServerConn") + c.Conn = underlying.Conn + c.tlsConn = underlying + t = reflect.TypeOf(underlying).Elem() + p = unsafe.Pointer(underlying) default: return nil, fmt.Errorf(`failed to use vision, maybe "security" is not "tls" or "utls"`) } - i, _ := t.FieldByName("input") - r, _ := t.FieldByName("rawInput") - c.input = (*bytes.Reader)(unsafe.Add(p, i.Offset)) - c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset)) + if i, ok := t.FieldByName("input"); ok { + c.input = (*bytes.Reader)(unsafe.Add(p, i.Offset)) + } + if r, ok := t.FieldByName("rawInput"); ok { + c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset)) + } return c, nil } diff --git a/clash-nyanpasu/.github/workflows/ci.yml b/clash-nyanpasu/.github/workflows/ci.yml index a8760f0207..1bcc2a7b62 100644 --- a/clash-nyanpasu/.github/workflows/ci.yml +++ b/clash-nyanpasu/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: - os: windows-latest runs-on: ${{ matrix.targets.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Rust run: | rustup toolchain install stable --profile minimal --no-self-update @@ -121,7 +121,7 @@ jobs: runs-on: ${{ matrix.targets.os }} needs: lint steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Tauri dependencies if: startsWith(matrix.targets.os, 'ubuntu-') run: >- @@ -208,7 +208,7 @@ jobs: # the steps our job runs **in order** steps: # checkout the code on the workflow runner - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 # install system dependencies that Tauri needs to compile on Linux. # note the extra dependencies for `tauri-driver` to run which are: `webkit2gtk-driver` and `xvfb` diff --git a/clash-nyanpasu/.github/workflows/daily.yml b/clash-nyanpasu/.github/workflows/daily.yml index f6a6f109b2..0ad880bed3 100644 --- a/clash-nyanpasu/.github/workflows/daily.yml +++ b/clash-nyanpasu/.github/workflows/daily.yml @@ -12,7 +12,7 @@ jobs: if: startsWith(github.repository, 'libnyanpasu') steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Node uses: actions/setup-node@v4 with: @@ -51,7 +51,7 @@ jobs: if: startsWith(github.repository, 'libnyanpasu') steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: dev - name: Install Node diff --git a/clash-nyanpasu/.github/workflows/deps-build-linux.yaml b/clash-nyanpasu/.github/workflows/deps-build-linux.yaml index 09f8c4d78f..d31487f37a 100644 --- a/clash-nyanpasu/.github/workflows/deps-build-linux.yaml +++ b/clash-nyanpasu/.github/workflows/deps-build-linux.yaml @@ -51,7 +51,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Rust stable run: | diff --git a/clash-nyanpasu/.github/workflows/deps-build-macos.yaml b/clash-nyanpasu/.github/workflows/deps-build-macos.yaml index 4c1e07561d..181964f579 100644 --- a/clash-nyanpasu/.github/workflows/deps-build-macos.yaml +++ b/clash-nyanpasu/.github/workflows/deps-build-macos.yaml @@ -45,7 +45,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - uses: maxim-lobanov/setup-xcode@v1 with: diff --git a/clash-nyanpasu/.github/workflows/deps-build-windows-nsis.yaml b/clash-nyanpasu/.github/workflows/deps-build-windows-nsis.yaml index 00d2b3e25d..a3a958b314 100644 --- a/clash-nyanpasu/.github/workflows/deps-build-windows-nsis.yaml +++ b/clash-nyanpasu/.github/workflows/deps-build-windows-nsis.yaml @@ -73,7 +73,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Rust stable run: | diff --git a/clash-nyanpasu/.github/workflows/deps-create-updater.yaml b/clash-nyanpasu/.github/workflows/deps-create-updater.yaml index 1f52b3a55b..9cd6020b3c 100644 --- a/clash-nyanpasu/.github/workflows/deps-create-updater.yaml +++ b/clash-nyanpasu/.github/workflows/deps-create-updater.yaml @@ -36,7 +36,7 @@ jobs: contents: write steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: ${{ github.ref }} # blocked by https://github.com/actions/checkout/issues/1467 diff --git a/clash-nyanpasu/.github/workflows/deps-delete-releases.yaml b/clash-nyanpasu/.github/workflows/deps-delete-releases.yaml index 8e77b627b2..1d7436fb70 100644 --- a/clash-nyanpasu/.github/workflows/deps-delete-releases.yaml +++ b/clash-nyanpasu/.github/workflows/deps-delete-releases.yaml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Delete current release assets uses: mknejp/delete-release-assets@v1 diff --git a/clash-nyanpasu/.github/workflows/deps-message-telegram.yaml b/clash-nyanpasu/.github/workflows/deps-message-telegram.yaml index 9361e102ba..000ee550ad 100644 --- a/clash-nyanpasu/.github/workflows/deps-message-telegram.yaml +++ b/clash-nyanpasu/.github/workflows/deps-message-telegram.yaml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - uses: actions/setup-node@v4 with: diff --git a/clash-nyanpasu/.github/workflows/deps-update-tag.yaml b/clash-nyanpasu/.github/workflows/deps-update-tag.yaml index c872904fa7..dede8fe586 100644 --- a/clash-nyanpasu/.github/workflows/deps-update-tag.yaml +++ b/clash-nyanpasu/.github/workflows/deps-update-tag.yaml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set Env run: | diff --git a/clash-nyanpasu/.github/workflows/macos-aarch64.yaml b/clash-nyanpasu/.github/workflows/macos-aarch64.yaml index 6a83963430..c1e765121c 100644 --- a/clash-nyanpasu/.github/workflows/macos-aarch64.yaml +++ b/clash-nyanpasu/.github/workflows/macos-aarch64.yaml @@ -11,7 +11,7 @@ jobs: runs-on: macos-14 steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: install Rust stable run: | diff --git a/clash-nyanpasu/.github/workflows/publish.yml b/clash-nyanpasu/.github/workflows/publish.yml index afbd440500..4e0146b44d 100644 --- a/clash-nyanpasu/.github/workflows/publish.yml +++ b/clash-nyanpasu/.github/workflows/publish.yml @@ -24,14 +24,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 - name: Prepare Node uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - uses: pnpm/action-setup@v4 name: Install pnpm with: diff --git a/clash-nyanpasu/backend/Cargo.lock b/clash-nyanpasu/backend/Cargo.lock index 6fe85e116c..2df5c2fab9 100644 --- a/clash-nyanpasu/backend/Cargo.lock +++ b/clash-nyanpasu/backend/Cargo.lock @@ -20,9 +20,9 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "accesskit" @@ -51,7 +51,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bfae7c152994a31dc7d99b8eeac7784a919f71d1b306f4b83217e110fd3824c" dependencies = [ "accesskit", - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -62,7 +62,7 @@ checksum = "692dd318ff8a7a0ffda67271c4bd10cf32249656f4e49390db0b26ca92b095f2" dependencies = [ "accesskit", "accesskit_consumer", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -80,7 +80,7 @@ dependencies = [ "async-executor", "async-task", "atspi", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "futures-util", "serde", "zbus", @@ -94,7 +94,7 @@ checksum = "70a042b62c9c05bf7b616f015515c17d2813f3ba89978d6f4fc369735d60700a" dependencies = [ "accesskit", "accesskit_consumer", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "static_assertions", "windows 0.61.3", "windows-core 0.61.2", @@ -217,7 +217,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.9.1", + "bitflags 2.9.2", "cc", "cesu8", "jni", @@ -226,7 +226,7 @@ dependencies = [ "log", "ndk", "ndk-context", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", "thiserror 1.0.69", ] @@ -273,9 +273,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -303,35 +303,35 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" dependencies = [ "derive_arbitrary", ] @@ -345,7 +345,7 @@ dependencies = [ "clipboard-win", "image", "log", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-core-graphics", @@ -371,7 +371,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -392,6 +392,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading 0.8.8", +] + [[package]] name = "ashpd" version = "0.11.0" @@ -419,7 +428,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -457,7 +466,7 @@ dependencies = [ "async-task", "concurrent-queue", "fastrand 2.3.0", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "pin-project-lite", "slab", ] @@ -470,35 +479,34 @@ checksum = "09f7e37c0ed80b2a977691c47dae8625cfb21e205827106c64f7c588766b2e50" dependencies = [ "async-lock", "blocking", - "futures-lite 2.6.0", + "futures-lite 2.6.1", ] [[package]] name = "async-io" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ "async-lock", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "parking", - "polling 3.8.0", + "polling 3.10.0", "rustix 1.0.8", "slab", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "async-lock" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "event-listener-strategy", "pin-project-lite", ] @@ -511,14 +519,14 @@ checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ "async-io", "blocking", - "futures-lite 2.6.0", + "futures-lite 2.6.1", ] [[package]] name = "async-process" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" dependencies = [ "async-channel 2.5.0", "async-io", @@ -527,10 +535,9 @@ dependencies = [ "async-task", "blocking", "cfg-if", - "event-listener 5.4.0", - "futures-lite 2.6.0", + "event-listener 5.4.1", + "futures-lite 2.6.1", "rustix 1.0.8", - "tracing", ] [[package]] @@ -541,14 +548,14 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "async-signal" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" dependencies = [ "async-io", "async-lock", @@ -559,7 +566,7 @@ dependencies = [ "rustix 1.0.8", "signal-hook-registry", "slab", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -570,13 +577,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -625,7 +632,7 @@ checksum = "99e1aca718ea7b89985790c94aad72d77533063fe00bc497bb79a7c2dae6a661" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -663,7 +670,7 @@ checksum = "4193d51303d8332304056ae0004714256b46b6635a5c556109b319c0d3784938" dependencies = [ "atspi-common", "atspi-proxies", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "zbus", ] @@ -684,7 +691,7 @@ version = "0.5.0" source = "git+https://github.com/libnyanpasu/auto-launch.git#729d5429dd689067047489af4a0a32f7013854c8" dependencies = [ "dirs 5.0.1", - "thiserror 2.0.12", + "thiserror 2.0.15", "windows-registry 0.3.0", "windows-result 0.2.0", ] @@ -711,9 +718,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" +checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" dependencies = [ "arrayvec", ] @@ -859,7 +866,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cexpr", "clang-sys", "itertools 0.12.1", @@ -872,7 +879,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.104", + "syn 2.0.106", "which 4.4.2", ] @@ -905,9 +912,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" dependencies = [ "serde", ] @@ -918,6 +925,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "block-buffer" version = "0.9.0" @@ -951,7 +964,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" dependencies = [ - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] @@ -963,7 +976,7 @@ dependencies = [ "async-channel 2.5.0", "async-task", "futures-io", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "piper", ] @@ -973,7 +986,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c340fe0f0b267787095cbe35240c6786ff19da63ec7b69367ba338eace8169b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "boa_interner", "boa_macros", "boa_string", @@ -989,7 +1002,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f620c3f06f51e65c0504ddf04978be1b814ac6586f0b45f6019801ab5efd37f9" dependencies = [ "arrayvec", - "bitflags 2.9.1", + "bitflags 2.9.2", "boa_ast", "boa_gc", "boa_interner", @@ -1001,7 +1014,7 @@ dependencies = [ "cfg-if", "dashmap", "fast-float2", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "icu_normalizer 1.5.0", "indexmap 2.10.0", "intrusive-collections", @@ -1023,7 +1036,7 @@ dependencies = [ "static_assertions", "tap", "thin-vec", - "thiserror 2.0.12", + "thiserror 2.0.15", "time", ] @@ -1036,7 +1049,7 @@ dependencies = [ "boa_macros", "boa_profiler", "boa_string", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "thin-vec", ] @@ -1048,7 +1061,7 @@ checksum = "42407a3b724cfaecde8f7d4af566df4b56af32a2f11f0956f5570bb974e7f749" dependencies = [ "boa_gc", "boa_macros", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "indexmap 2.10.0", "once_cell", "phf 0.11.3", @@ -1064,7 +1077,7 @@ checksum = "9fd3f870829131332587f607a7ff909f1af5fc523fd1b192db55fbbdf52e8d3c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -1074,7 +1087,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cc142dac798cdc6e2dbccfddeb50f36d2523bb977a976e19bdb3ae19b740804" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "boa_ast", "boa_interner", "boa_macros", @@ -1135,9 +1148,9 @@ dependencies = [ [[package]] name = "brotli" -version = "8.0.1" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1181,22 +1194,22 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1245,7 +1258,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cairo-sys-rs", "glib", "libc", @@ -1270,9 +1283,9 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "log", - "polling 3.8.0", + "polling 3.10.0", "rustix 0.38.44", "slab", "thiserror 1.0.69", @@ -1292,9 +1305,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" +checksum = "5d07aa9a93b00c76f71bc35d598bed923f6d4f3a9ca5c24b7737ae1a292841c0" dependencies = [ "serde", ] @@ -1319,17 +1332,17 @@ dependencies = [ "semver 1.0.26", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] name = "cargo_toml" -version = "0.22.1" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" dependencies = [ "serde", - "toml 0.8.23", + "toml 0.9.5", ] [[package]] @@ -1349,9 +1362,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.30" +version = "1.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" dependencies = [ "jobserver", "libc", @@ -1453,9 +1466,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.42" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", "clap_derive", @@ -1463,9 +1476,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstream", "anstyle", @@ -1475,14 +1488,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1550,7 +1563,7 @@ dependencies = [ "nyanpasu-ipc", "nyanpasu-macro", "nyanpasu-utils", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-foundation 0.3.1", "once_cell", @@ -1588,7 +1601,7 @@ dependencies = [ "specta", "specta-typescript", "strum 0.27.2", - "sysinfo", + "sysinfo 0.36.1", "sysproxy", "tauri", "tauri-build", @@ -1605,7 +1618,7 @@ dependencies = [ "tauri-specta", "tempfile", "test-log", - "thiserror 2.0.12", + "thiserror 2.0.15", "time", "timeago", "tokio", @@ -1648,7 +1661,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -1714,7 +1727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d" dependencies = [ "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1818,7 +1831,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.10.1", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -1842,7 +1855,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.10.1", "libc", ] @@ -1995,7 +2008,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2005,7 +2018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2020,24 +2033,24 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.48" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2d5c8f48d9c0c23250e52b55e82a6ab4fdba6650c931f5a0a57a43abda812b" +checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc" dependencies = [ "curl-sys", "libc", "openssl-probe", "openssl-sys", "schannel", - "socket2 0.5.10", + "socket2 0.6.0", "windows-sys 0.59.0", ] [[package]] name = "curl-sys" -version = "0.4.82+curl-8.14.1" +version = "0.4.83+curl-8.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4d63638b5ec65f1a4ae945287b3fd035be4554bbaf211901159c9a2a74fb5be" +checksum = "5830daf304027db10c82632a464879d46a3f7c4ba17a31592657ad16c719b483" dependencies = [ "cc", "libc", @@ -2076,7 +2089,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2087,7 +2100,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2147,7 +2160,7 @@ dependencies = [ "concat-idents", "cron_clock", "dashmap", - "event-listener 5.4.0", + "event-listener 5.4.1", "futures", "log", "lru", @@ -2172,13 +2185,13 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2199,7 +2212,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2209,7 +2222,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2222,7 +2235,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2315,17 +2328,17 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users 0.5.0", + "redox_users 0.5.2", "windows-sys 0.60.2", ] [[package]] name = "dirs-utils" version = "0.1.0" -source = "git+https://github.com/libnyanpasu/nyanpasu-utils.git#b667b551a38076c483aa9aef432e9fc6a425ea4c" +source = "git+https://github.com/libnyanpasu/nyanpasu-utils.git#dc10823ed5249e837b3005ab71fec0abc7b53f2c" dependencies = [ "dirs 6.0.0", - "thiserror 2.0.12", + "thiserror 2.0.15", "tracing", "windows 0.61.3", ] @@ -2342,10 +2355,10 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.6.1", "libc", - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] @@ -2356,14 +2369,14 @@ checksum = "0e8ffedfe3971acf2350b12253a7c8845822a28c1fd07fe4feb2d03cffa3b24f" dependencies = [ "fxhash", "log", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-core-graphics", "objc2-foundation 0.3.1", "scopeguard", "smithay-client-toolkit", - "thiserror 2.0.12", + "thiserror 2.0.15", "widestring 1.2.0", "windows 0.59.0", "xcb", @@ -2377,7 +2390,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2409,7 +2422,7 @@ checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2477,9 +2490,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ecolor" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a631732d995184114016fab22fc7e3faf73d6841c2d7650395fe251fbcd9285" +checksum = "b6a7fc3172c2ef56966b2ce4f84177e159804c40b9a84de8861558ce4a59f422" dependencies = [ "bytemuck", "emath", @@ -2487,9 +2500,9 @@ dependencies = [ [[package]] name = "eframe" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790ccfbb3dd556588342463454b2b2b13909e5fdce5bc2a1432a8aa69c8b7a" +checksum = "34037a80dc03a4147e1684bff4e4fdab2b1408d715d7b78470cd3179258964b9" dependencies = [ "ahash", "bytemuck", @@ -2523,13 +2536,13 @@ dependencies = [ [[package]] name = "egui" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8470210c95a42cc985d9ffebfd5067eea55bdb1c3f7611484907db9639675e28" +checksum = "49e2be082f77715496b4a39fdc6f5dc7491fefe2833111781b8697ea6ee919a7" dependencies = [ "accesskit", "ahash", - "bitflags 2.9.1", + "bitflags 2.9.2", "emath", "epaint", "log", @@ -2541,9 +2554,9 @@ dependencies = [ [[package]] name = "egui-wgpu" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14de9942d8b9e99e2d830403c208ab1a6e052e925a7456a4f6f66d567d90de1d" +checksum = "64c7277a171ec1b711080ddb3b0bfa1b3aa9358834d5386d39e83fbc16d61212" dependencies = [ "ahash", "bytemuck", @@ -2561,9 +2574,9 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c490804a035cec9c826082894a3e1ecf4198accd3817deb10f7919108ebafab0" +checksum = "fe6d8b0f8d6de4d43e794e343f03bacc3908aada931f0ed6fd7041871388a590" dependencies = [ "accesskit_winit", "ahash", @@ -2581,9 +2594,9 @@ dependencies = [ [[package]] name = "egui_extras" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f791a5937f518249016b276b3639ad2aa3824048b6f2161ec2b431ab325880a" +checksum = "8ae8f23013328beb6be7ab29c75807142e8e1c7951643780a813e54cceaa9929" dependencies = [ "ahash", "egui", @@ -2598,9 +2611,9 @@ dependencies = [ [[package]] name = "egui_glow" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d44f3fd4fdc5f960c9e9ef7327c26647edc3141abf96102980647129d49358e6" +checksum = "0ab645760288e42eab70283a5cccf44509a6f43b554351855d3c73594bfe3c23" dependencies = [ "ahash", "bytemuck", @@ -2636,9 +2649,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "emath" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f057b141e7e46340c321400be74b793543b1b213036f0f989c35d35957c32e" +checksum = "935df67dc48fdeef132f2f7ada156ddc79e021344dd42c17f066b956bb88dde3" dependencies = [ "bytemuck", ] @@ -2652,7 +2665,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.9.2", + "toml 0.9.5", "vswhom", "winreg 0.55.0", ] @@ -2707,7 +2720,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2728,7 +2741,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2760,9 +2773,9 @@ dependencies = [ [[package]] name = "epaint" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94cca02195f0552c17cabdc02f39aa9ab6fbd815dac60ab1cd3d5b0aa6f9551c" +checksum = "b66fc0a5a9d322917de9bd3ac7d426ca8aa3127fbf1e76fae5b6b25e051e06a3" dependencies = [ "ab_glyph", "ahash", @@ -2778,9 +2791,9 @@ dependencies = [ [[package]] name = "epaint_default_fonts" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8495e11ed527dff39663b8c36b6c2b2799d7e4287fb90556e455d72eca0b4d3" +checksum = "4f6cf8ce0fb817000aa24f5e630bda904a353536bd430b83ebc1dceee95b4a3a" [[package]] name = "equator" @@ -2799,7 +2812,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2834,6 +2847,15 @@ version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" +[[package]] +name = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -2842,9 +2864,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -2857,7 +2879,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "pin-project-lite", ] @@ -3060,7 +3082,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3126,15 +3148,15 @@ dependencies = [ [[package]] name = "futures-buffered" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe940397c8b744b9c2c974791c2c08bca2c3242ce0290393249e98f215a00472" +checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" dependencies = [ "cordyceps", "diatomic-waker", "futures-core", "pin-project-lite", - "spin", + "spin 0.10.0", ] [[package]] @@ -3156,7 +3178,7 @@ dependencies = [ "fixedbitset 0.5.7", "futures-buffered", "futures-core", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "pin-project", "slab", "smallvec", @@ -3202,9 +3224,9 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand 2.3.0", "futures-core", @@ -3221,7 +3243,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3509,7 +3531,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "futures-channel", "futures-core", "futures-executor", @@ -3537,7 +3559,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3552,9 +3574,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "global-hotkey" @@ -3564,11 +3586,11 @@ checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7" dependencies = [ "crossbeam-channel", "keyboard-types", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "once_cell", "serde", - "thiserror 2.0.12", + "thiserror 2.0.15", "windows-sys 0.59.0", "x11rb", "xkeysym", @@ -3628,7 +3650,7 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12124de845cacfebedff80e877bb37b5b75c34c5a4c89e47e1cdd67fb6041325" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg_aliases", "cgl", "dispatch2", @@ -3636,7 +3658,7 @@ dependencies = [ "glutin_glx_sys", "glutin_wgl_sys", "libloading 0.8.8", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-foundation 0.3.1", @@ -3699,6 +3721,57 @@ dependencies = [ "system-deps", ] +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.9.2", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.9.2", +] + +[[package]] +name = "gpu-allocator" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +dependencies = [ + "log", + "presser", + "thiserror 1.0.69", + "windows 0.58.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.9.2", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.9.2", +] + [[package]] name = "gtk" version = "0.18.2" @@ -3748,14 +3821,14 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "h2" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -3787,7 +3860,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2c385c6df70fd180bbb673d93039dbd2cd34e41d782600bdf6e1ca7bce39aa" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.5", "serde", ] @@ -3828,9 +3901,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", @@ -3847,7 +3920,7 @@ dependencies = [ "hash32", "rustc_version", "serde", - "spin", + "spin 0.9.8", "stable_deref_trait", ] @@ -4033,9 +4106,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64 0.22.1", "bytes", @@ -4049,7 +4122,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.0", "system-configuration", "tokio", "tower-service", @@ -4113,7 +4186,7 @@ dependencies = [ "potential_utf", "yoke 0.8.0", "zerofrom", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -4126,7 +4199,7 @@ dependencies = [ "litemap 0.8.0", "tinystr 0.8.1", "writeable 0.6.1", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -4192,7 +4265,7 @@ dependencies = [ "icu_properties 2.0.1", "icu_provider 2.0.0", "smallvec", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -4235,7 +4308,7 @@ dependencies = [ "icu_provider 2.0.0", "potential_utf", "zerotrie", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -4281,7 +4354,7 @@ dependencies = [ "yoke 0.8.0", "zerofrom", "zerotrie", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -4292,7 +4365,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4401,7 +4474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "serde", ] @@ -4426,7 +4499,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "inotify-sys", "libc", ] @@ -4464,7 +4537,7 @@ version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb6250a98af259a26fd5a4a6081fccea9ac116e4c3178acf4aeb86d32d2b7715" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cc", "handlebars", "lazy_static", @@ -4482,7 +4555,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4511,20 +4584,20 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg-if", "libc", ] [[package]] name = "ipc-channel" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1c98b70019c830a1fc39cecfe1f60ff99c4122f0a189697c810c90ec545c14" +checksum = "1700f6b8b9f00cdd675f32fbb3a5be882213140dfe045805273221ca266c43f8" dependencies = [ "bincode 1.3.3", "crossbeam-channel", @@ -4776,11 +4849,22 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "serde", "unicode-segmentation", ] +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading 0.8.8", + "pkg-config", +] + [[package]] name = "khronos_api" version = "3.1.0" @@ -4834,11 +4918,12 @@ dependencies = [ [[package]] name = "kurbo" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" dependencies = [ "arrayvec", + "euclid", "smallvec", ] @@ -4886,9 +4971,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libfuzzer-sys" @@ -4917,7 +5002,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -4938,13 +5023,13 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.4" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "libc", - "redox_syscall 0.5.13", + "redox_syscall 0.5.17", ] [[package]] @@ -4994,9 +5079,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "litrs" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "lock_api" @@ -5042,7 +5127,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -5104,11 +5189,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "119c8490084af61b44c9eda9d626475847a186737c0378c85e32d77c33a01cd4" dependencies = [ "cc", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-foundation 0.3.1", "time", ] +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "markup5ever" version = "0.14.1" @@ -5131,7 +5225,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5223,6 +5317,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metal" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" +dependencies = [ + "bitflags 2.9.2", + "block", + "core-graphics-types 0.1.3", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + [[package]] name = "mime" version = "0.3.17" @@ -5277,9 +5386,9 @@ dependencies = [ [[package]] name = "mlua" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de25fc513588ac1273aa8c6dc0fffee6d32c12f38dc75f5cdc74547121a107ef" +checksum = "ab2fea92b2adabd51808311b101551d6e3f8602b65e9fae51f7ad5b3d500f4cd" dependencies = [ "bstr", "either", @@ -5296,9 +5405,9 @@ dependencies = [ [[package]] name = "mlua-sys" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcdf7c9e260ca82aaa32ac11148941952b856bb8c69aa5a9e65962f21fcb8637" +checksum = "3d4dc9cfc5a7698899802e97480617d9726f7da78c910db989d4d0fd4991d900" dependencies = [ "cc", "cfg-if", @@ -5309,23 +5418,23 @@ dependencies = [ [[package]] name = "muda" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58b89bf91c19bf036347f1ab85a81c560f08c0667c8601bece664d860a600988" +checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" dependencies = [ "crossbeam-channel", "dpi", "gtk", "keyboard-types", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-foundation 0.3.1", "once_cell", "png", "serde", - "thiserror 2.0.12", - "windows-sys 0.59.0", + "thiserror 2.0.15", + "windows-sys 0.60.2", ] [[package]] @@ -5336,19 +5445,20 @@ checksum = "2b977c445f26e49757f9aca3631c3b8b836942cb278d69a92e7b80d3b24da632" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg_aliases", "codespan-reporting", "half", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "hexf-parse", "indexmap 2.10.0", "log", "num-traits", "once_cell", "rustc-hash 1.1.0", + "spirv", "strum 0.26.3", - "thiserror 2.0.12", + "thiserror 2.0.15", "unicode-ident", ] @@ -5384,10 +5494,10 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "jni-sys", "log", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", "raw-window-handle", "thiserror 1.0.69", @@ -5399,6 +5509,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -5411,7 +5530,7 @@ dependencies = [ [[package]] name = "network-utils" version = "0.1.0" -source = "git+https://github.com/libnyanpasu/nyanpasu-utils.git#b667b551a38076c483aa9aef432e9fc6a425ea4c" +source = "git+https://github.com/libnyanpasu/nyanpasu-utils.git#dc10823ed5249e837b3005ab71fec0abc7b53f2c" dependencies = [ "log", "tempfile", @@ -5455,7 +5574,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg-if", "libc", ] @@ -5466,7 +5585,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg-if", "cfg_aliases", "libc", @@ -5532,7 +5651,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "fsevent-sys", "inotify", "kqueue", @@ -5563,7 +5682,7 @@ version = "4.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6442248665a5aa2514e794af3b39661a8e73033b1cc5e59899e1276117ee4400" dependencies = [ - "futures-lite 2.6.0", + "futures-lite 2.6.1", "log", "mac-notification-sys", "serde", @@ -5632,7 +5751,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5694,7 +5813,7 @@ dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5718,7 +5837,7 @@ dependencies = [ "humansize", "image", "ipc-channel", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-foundation 0.3.1", "parking_lot", @@ -5730,8 +5849,8 @@ dependencies = [ [[package]] name = "nyanpasu-ipc" -version = "1.3.1" -source = "git+https://github.com/libnyanpasu/nyanpasu-service.git#cbdb7174883fbf62d576a654792aa8eb25f1287f" +version = "1.4.1" +source = "git+https://github.com/libnyanpasu/nyanpasu-service.git#e1d2a1a9bb6f0d3a44572516fdc614ebc275b68d" dependencies = [ "anyhow", "axum", @@ -5749,7 +5868,7 @@ dependencies = [ "serde_json", "simd-json", "specta", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", "tokio-util", "tracing", @@ -5763,13 +5882,13 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "nyanpasu-utils" version = "0.1.0" -source = "git+https://github.com/libnyanpasu/nyanpasu-utils.git#b667b551a38076c483aa9aef432e9fc6a425ea4c" +source = "git+https://github.com/libnyanpasu/nyanpasu-utils.git#dc10823ed5249e837b3005ab71fec0abc7b53f2c" dependencies = [ "camino", "constcat", @@ -5785,13 +5904,22 @@ dependencies = [ "serde", "shared_child", "specta", - "sysinfo", - "thiserror 2.0.12", + "sysinfo 0.37.0", + "thiserror 2.0.15", "tokio", "tracing", "tracing-attributes", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + [[package]] name = "objc-sys" version = "0.3.5" @@ -5810,9 +5938,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" dependencies = [ "objc2-encode", "objc2-exception-helper", @@ -5824,7 +5952,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -5840,10 +5968,10 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.6.1", "libc", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-cloud-kit 0.3.1", "objc2-core-data 0.3.1", "objc2-core-foundation", @@ -5859,7 +5987,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -5872,8 +6000,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.2", + "objc2 0.6.2", "objc2-foundation 0.3.1", ] @@ -5894,7 +6022,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -5906,8 +6034,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.2", + "objc2 0.6.2", "objc2-foundation 0.3.1", ] @@ -5917,11 +6045,11 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.6.1", "dispatch2", "libc", - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] @@ -5930,11 +6058,11 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.6.1", "dispatch2", "libc", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-core-foundation", "objc2-io-surface", "objc2-metal 0.3.1", @@ -5958,7 +6086,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e" dependencies = [ - "objc2 0.6.1", + "objc2 0.6.2", "objc2-foundation 0.3.1", ] @@ -5995,7 +6123,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.5.1", "dispatch", "libc", @@ -6008,10 +6136,10 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.6.1", "libc", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-core-foundation", ] @@ -6031,8 +6159,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.2", + "objc2 0.6.2", "objc2-core-foundation", ] @@ -6054,7 +6182,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -6066,8 +6194,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f246c183239540aab1782457b35ab2040d4259175bd1d0c58e46ada7b47a874" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.2", + "objc2 0.6.2", "objc2-foundation 0.3.1", ] @@ -6077,8 +6205,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26bb88504b5a050dbba515d2414607bf5e57dd56b107bc5f0351197a3e7bdc5d" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.2", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-foundation 0.3.1", ] @@ -6089,7 +6217,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -6102,8 +6230,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.2", + "objc2 0.6.2", "objc2-foundation 0.3.1", ] @@ -6123,7 +6251,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit 0.2.2", @@ -6144,8 +6272,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.2", + "objc2 0.6.2", "objc2-core-foundation", "objc2-foundation 0.3.1", ] @@ -6167,7 +6295,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -6180,9 +6308,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91672909de8b1ce1c2252e95bbee8c1649c9ad9d14b9248b3d7b4c47903c47ad" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.6.1", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-foundation 0.3.1", @@ -6239,7 +6367,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg-if", "foreign-types 0.3.2", "libc", @@ -6256,7 +6384,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6267,9 +6395,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.1+3.5.1" +version = "300.5.2+3.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "735230c832b28c000e3bc117119e6466a663ec73506bc0a9907ea4187508e42a" +checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4" dependencies = [ "cc", ] @@ -6311,6 +6439,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -6324,7 +6461,7 @@ dependencies = [ [[package]] name = "os-utils" version = "0.1.0" -source = "git+https://github.com/libnyanpasu/nyanpasu-utils.git#b667b551a38076c483aa9aef432e9fc6a425ea4c" +source = "git+https://github.com/libnyanpasu/nyanpasu-utils.git#dc10823ed5249e837b3005ab71fec0abc7b53f2c" dependencies = [ "nix 0.30.1", "shared_child", @@ -6359,12 +6496,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" dependencies = [ - "objc2 0.6.1", + "objc2 0.6.2", "objc2-foundation 0.3.1", "objc2-osa-kit", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -6375,9 +6512,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "owned_ttf_parser" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" dependencies = [ "ttf-parser", ] @@ -6398,7 +6535,7 @@ dependencies = [ "owo-colors", "oxc-miette-derive", "textwrap", - "thiserror 2.0.12", + "thiserror 2.0.15", "unicode-width", ] @@ -6410,29 +6547,29 @@ checksum = "a6eabb57f935b454fbe0552ea0abaaf9eb0019b5fa05a7bbe7efd5bd8c765085" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "oxc_allocator" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c67f2b817263a72b6cc5b46e32467bc4077496f88315ce08c1796647fed84d4" +checksum = "b37f326db7599f4b4d10b56f696a959d949f5cef8538ecae7adcc3c161aa192c" dependencies = [ "allocator-api2", "bumpalo", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "oxc_data_structures", "rustc-hash 2.1.1", ] [[package]] name = "oxc_ast" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989b81258ce151e23adee6f1393f7c0fb7e10e3a6c6c001f71bbaff4081c72a4" +checksum = "4259700fbdd926114ade8041ed68dbb83917acaca4669526b1ab29ae79f79e4d" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "oxc_allocator", "oxc_ast_macros", "oxc_data_structures", @@ -6444,21 +6581,21 @@ dependencies = [ [[package]] name = "oxc_ast_macros" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff7e8e42eea621e6fa7072a480fa04ae4dfe73f9a958822c066cc7f1eba57e82" +checksum = "60269747adf5090dfa21a3ff408b7dd62627282e7e85030b08bec55dce07972f" dependencies = [ "phf 0.12.1", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "oxc_ast_visit" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0a968790ef8968de08d813a2fe1a15739930fb1c171fddeebf3545d802ea216" +checksum = "b27076752c60c46fc37639ff3042549437827fdc5d22641a7b592fb7bbcb5d7c" dependencies = [ "oxc_allocator", "oxc_ast", @@ -6468,18 +6605,15 @@ dependencies = [ [[package]] name = "oxc_data_structures" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c58a40be09dbb289150046a39da2fb130eb5b8ff1f013f2963e880e1af261c" -dependencies = [ - "rustversion", -] +checksum = "8b99bd27751e6e99cdc0e9e8b6a2d88d18bd6cdb3516aa516466080b2b4c3d13" [[package]] name = "oxc_diagnostics" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702bde62d95587518aec86247457830f189242f814fba8389beb6e8c1585c444" +checksum = "1963cb534cda91da652b9b6c35eeb1f099aef273457268fe43f7f6e7e3a82d40" dependencies = [ "cow-utils", "oxc-miette", @@ -6488,9 +6622,9 @@ dependencies = [ [[package]] name = "oxc_ecmascript" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4dab5abe97e790fe1b631a7eacb9a1cbecbe2dad5b984ef11fcb9115b376ab" +checksum = "2aaa81649df64a361d55a0356ada772edd53a3a4f7f695f987528a401ba65431" dependencies = [ "cow-utils", "num-bigint", @@ -6503,9 +6637,9 @@ dependencies = [ [[package]] name = "oxc_estree" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a453daacd90f168554015dd49467038ab6894045805f861950c1d0a8e6a5e" +checksum = "6dc17babfecb9b93e5df7f52ea2d01990b93a129d8b9185fc5a9e8a22b46d1a3" [[package]] name = "oxc_index" @@ -6515,11 +6649,11 @@ checksum = "2fa07b0cfa997730afed43705766ef27792873fdf5215b1391949fec678d2392" [[package]] name = "oxc_parser" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70253728183a5a92d1ba964d9952201e0269ebd07c4917c4553283d564225bab" +checksum = "26b6b70cd5b179a34289003d84d4de9abb920ddcc25f70ff10cddf6f2e5d97ed" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cow-utils", "memchr", "num-bigint", @@ -6538,11 +6672,11 @@ dependencies = [ [[package]] name = "oxc_regular_expression" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5bb2ed6646bb7815126e0f36787c349e13561fed2c05f95b27a1f82dd066c4" +checksum = "61ddf077833348e25ea39a0b95b3490abafa02daa87bbd8260e60d64ff3f2c3b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "oxc_allocator", "oxc_ast_macros", "oxc_diagnostics", @@ -6554,9 +6688,9 @@ dependencies = [ [[package]] name = "oxc_span" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b315080fee7613168e0e08d3280efccdc12fea1efe5c0285e3a2d6265660f02" +checksum = "6f8740fd6737f52f14a14c28434d144e8491816812f7401e6a889bbaa595c074" dependencies = [ "compact_str", "oxc-miette", @@ -6567,11 +6701,11 @@ dependencies = [ [[package]] name = "oxc_syntax" -version = "0.81.0" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d9d80d47d2eee5c1e6dbcdf1841b42afc4f1cd1f63701fc346162616dac52f" +checksum = "3e460636ae51b185fa38507402ee11c6d7b5998fe392aa64c1b3c2c9f5dbd611" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cow-utils", "dragonbox_ecma", "nonmax", @@ -6637,7 +6771,7 @@ dependencies = [ "cfg-if", "libc", "petgraph", - "redox_syscall 0.5.13", + "redox_syscall 0.5.17", "smallvec", "thread-id", "windows-targets 0.52.6", @@ -6688,7 +6822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", - "thiserror 2.0.12", + "thiserror 2.0.15", "ucd-trie", ] @@ -6712,7 +6846,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6860,7 +6994,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "unicase", ] @@ -6874,7 +7008,7 @@ dependencies = [ "phf_shared 0.12.1", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6937,7 +7071,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6977,7 +7111,7 @@ checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", "indexmap 2.10.0", - "quick-xml 0.38.0", + "quick-xml 0.38.1", "serde", "time", ] @@ -7013,17 +7147,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.8.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", "rustix 1.0.8", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -7063,7 +7196,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" dependencies = [ - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -7087,6 +7220,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + [[package]] name = "pretty_assertions" version = "1.4.1" @@ -7099,12 +7238,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.35" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" dependencies = [ "proc-macro2", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -7167,9 +7306,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -7190,7 +7329,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -7238,9 +7377,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.0" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8927b0664f5c5a98265138b7e3f90aa19a6b21353182469ace36d4ac527b7b1b" +checksum = "9845d9dccf565065824e69f9f235fafba1587031eda353c1f1561cd6a6be78f4" dependencies = [ "memchr", ] @@ -7259,7 +7398,7 @@ dependencies = [ "rustc-hash 2.1.1", "rustls", "socket2 0.5.10", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", "tracing", "web-time", @@ -7280,7 +7419,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror 2.0.15", "tinyvec", "tracing", "web-time", @@ -7425,6 +7564,12 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "range-alloc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" + [[package]] name = "rav1e" version = "0.7.1" @@ -7483,9 +7628,9 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -7493,9 +7638,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -7509,9 +7654,9 @@ checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" [[package]] name = "redb" -version = "2.6.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b38b05028f398f08bea4691640503ec25fcb60b82fb61ce1f8fd1f4fccd3f7" +checksum = "225e8bf881033e020ed87e9f10fc6254cf3ebab8d440e6fbb4c7b34bec2a0543" dependencies = [ "libc", ] @@ -7527,11 +7672,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] @@ -7547,13 +7692,13 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -7573,7 +7718,7 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -7626,7 +7771,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "145bb27393fe455dd64d6cbc8d059adfa392590a45eadf079c01b11857e7b010" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.5", "memchr", ] @@ -7638,9 +7783,9 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64 0.22.1", "bytes", @@ -7714,7 +7859,7 @@ dependencies = [ "gtk-sys", "js-sys", "log", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-foundation 0.3.1", @@ -7799,7 +7944,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -7827,9 +7972,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -7858,7 +8003,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "errno", "libc", "linux-raw-sys 0.4.15", @@ -7871,7 +8016,7 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "errno", "libc", "linux-raw-sys 0.9.4", @@ -7880,9 +8025,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.29" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "log", "once_cell", @@ -7916,9 +8061,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rustybuzz" @@ -7926,7 +8071,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "bytemuck", "core_maths", "log", @@ -8016,7 +8161,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8056,7 +8201,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -8135,9 +8280,9 @@ dependencies = [ [[package]] name = "serde-untagged" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e" +checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3" dependencies = [ "erased-serde", "serde", @@ -8150,7 +8295,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" dependencies = [ - "ordered-float", + "ordered-float 2.10.1", "serde", ] @@ -8162,7 +8307,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8173,7 +8318,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8207,7 +8352,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8269,7 +8414,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8434,9 +8579,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -8513,9 +8658,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slotmap" @@ -8555,7 +8700,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "calloop", "calloop-wayland-source", "cursor-icon", @@ -8599,7 +8744,7 @@ dependencies = [ "async-net", "async-process", "blocking", - "futures-lite 2.6.0", + "futures-lite 2.6.1", ] [[package]] @@ -8647,7 +8792,7 @@ dependencies = [ "objc2-foundation 0.2.2", "objc2-quartz-core 0.2.2", "raw-window-handle", - "redox_syscall 0.5.13", + "redox_syscall 0.5.17", "wasm-bindgen", "web-sys", "windows-sys 0.59.0", @@ -8705,7 +8850,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8738,6 +8883,21 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.9.2", +] + [[package]] name = "sptr" version = "0.3.2" @@ -8811,7 +8971,7 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.27.1", + "strum_macros 0.27.2", ] [[package]] @@ -8824,20 +8984,19 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "rustversion", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8880,9 +9039,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -8906,7 +9065,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -8932,6 +9091,20 @@ dependencies = [ "windows 0.61.3", ] +[[package]] +name = "sysinfo" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows 0.61.3", +] + [[package]] name = "sysproxy" version = "0.3.0" @@ -8952,7 +9125,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -8986,7 +9159,7 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49c380ca75a231b87b6c9dd86948f035012e7171d1a7c40a9c2890489a7ffd8a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation 0.10.1", "core-graphics 0.24.0", "crossbeam-channel", @@ -9002,8 +9175,8 @@ dependencies = [ "log", "ndk", "ndk-context", - "ndk-sys", - "objc2 0.6.1", + "ndk-sys 0.6.0+11769913", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-foundation 0.3.1", "once_cell", @@ -9027,7 +9200,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -9055,9 +9228,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.6.2" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "124e129c9c0faa6bec792c5948c89e86c90094133b0b9044df0ce5f0a8efaa0d" +checksum = "352a4bc7bf6c25f5624227e3641adf475a6535707451b09bb83271df8b7a6ac7" dependencies = [ "anyhow", "bytes", @@ -9075,7 +9248,7 @@ dependencies = [ "log", "mime", "muda", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-foundation 0.3.1", "objc2-ui-kit 0.3.1", @@ -9094,7 +9267,7 @@ dependencies = [ "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", "tray-icon", "url", @@ -9107,9 +9280,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f025c389d3adb83114bec704da973142e82fc6ec799c7c750c5e21cefaec83" +checksum = "182d688496c06bf08ea896459bf483eb29cdff35c1c4c115fb14053514303064" dependencies = [ "anyhow", "cargo_toml", @@ -9129,9 +9302,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df493a1075a241065bc865ed5ef8d0fbc1e76c7afdc0bf0eccfaa7d4f0e406" +checksum = "b54a99a6cd8e01abcfa61508177e6096a4fe2681efecee9214e962f2f073ae4a" dependencies = [ "base64 0.22.1", "brotli", @@ -9145,9 +9318,9 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", - "syn 2.0.104", + "syn 2.0.106", "tauri-utils", - "thiserror 2.0.12", + "thiserror 2.0.15", "time", "url", "uuid", @@ -9156,23 +9329,23 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f237fbea5866fa5f2a60a21bea807a2d6e0379db070d89c3a10ac0f2d4649bbc" +checksum = "7945b14dc45e23532f2ded6e120170bbdd4af5ceaa45784a6b33d250fbce3f9e" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "tauri-codegen", "tauri-utils", ] [[package]] name = "tauri-plugin" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9a0bd00bf1930ad1a604d08b0eb6b2a9c1822686d65d7f4731a7723b8901d3" +checksum = "5bd5c1e56990c70a906ef67a9851bbdba9136d26075ee9a2b19c8b46986b3e02" dependencies = [ "anyhow", "glob", @@ -9197,7 +9370,7 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -9207,7 +9380,7 @@ dependencies = [ "dirs 6.0.0", "interprocess", "log", - "objc2 0.6.1", + "objc2 0.6.2", "once_cell", "tauri-utils", "tokio", @@ -9217,9 +9390,9 @@ dependencies = [ [[package]] name = "tauri-plugin-dialog" -version = "2.3.0" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aefb14219b492afb30b12647b5b1247cadd2c0603467310c36e0f7ae1698c28" +checksum = "37e5858cc7b455a73ab4ea2ebc08b5be33682c00ff1bf4cad5537d4fb62499d9" dependencies = [ "log", "raw-window-handle", @@ -9229,15 +9402,15 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-plugin-fs", - "thiserror 2.0.12", + "thiserror 2.0.15", "url", ] [[package]] name = "tauri-plugin-fs" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c341290d31991dbca38b31d412c73dfbdb070bb11536784f19dd2211d13b778f" +checksum = "8c6ef84ee2f2094ce093e55106d90d763ba343fad57566992962e8f76d113f99" dependencies = [ "anyhow", "dunce", @@ -9250,7 +9423,7 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-utils", - "thiserror 2.0.12", + "thiserror 2.0.15", "toml 0.8.23", "url", ] @@ -9267,7 +9440,7 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -9284,7 +9457,7 @@ dependencies = [ "serde_repr", "tauri", "tauri-plugin", - "thiserror 2.0.12", + "thiserror 2.0.15", "time", "url", ] @@ -9304,7 +9477,7 @@ dependencies = [ "sys-locale", "tauri", "tauri-plugin", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -9334,7 +9507,7 @@ dependencies = [ "shared_child", "tauri", "tauri-plugin", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", ] @@ -9362,7 +9535,7 @@ dependencies = [ "tauri", "tauri-plugin", "tempfile", - "thiserror 2.0.12", + "thiserror 2.0.15", "time", "tokio", "url", @@ -9372,37 +9545,37 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7bb73d1bceac06c20b3f755b2c8a2cb13b20b50083084a8cf3700daf397ba4" +checksum = "2b1cc885be806ea15ff7b0eb47098a7b16323d9228876afda329e34e2d6c4676" dependencies = [ "cookie", "dpi", "gtk", "http 1.3.1", "jni", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-ui-kit 0.3.1", "raw-window-handle", "serde", "serde_json", "tauri-utils", - "thiserror 2.0.12", + "thiserror 2.0.15", "url", "windows 0.61.3", ] [[package]] name = "tauri-runtime-wry" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "902b5aa9035e16f342eb64f8bf06ccdc2808e411a2525ed1d07672fa4e780bad" +checksum = "fe653a2fbbef19fe898efc774bc52c8742576342a33d3d028c189b57eb1d2439" dependencies = [ "gtk", "http 1.3.1", "jni", "log", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-foundation 0.3.1", "once_cell", @@ -9432,7 +9605,7 @@ dependencies = [ "specta-typescript", "tauri", "tauri-specta-macros", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -9444,14 +9617,14 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "tauri-utils" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41743bbbeb96c3a100d234e5a0b60a46d5aa068f266160862c7afdbf828ca02e" +checksum = "9330c15cabfe1d9f213478c9e8ec2b0c76dab26bb6f314b8ad1c8a568c1d186e" dependencies = [ "anyhow", "brotli", @@ -9477,7 +9650,7 @@ dependencies = [ "serde_json", "serde_with", "swift-rs", - "thiserror 2.0.12", + "thiserror 2.0.15", "toml 0.8.23", "url", "urlpattern", @@ -9487,13 +9660,12 @@ dependencies = [ [[package]] name = "tauri-winres" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d321dbc6f998d825ab3f0d62673e810c861aac2d0de2cc2c395328f1d113b4" +checksum = "fd21509dd1fa9bd355dc29894a6ff10635880732396aa38c0066c1e6c1ab8074" dependencies = [ "embed-resource", - "indexmap 2.10.0", - "toml 0.8.23", + "toml 0.9.5", ] [[package]] @@ -9503,7 +9675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" dependencies = [ "quick-xml 0.37.5", - "thiserror 2.0.12", + "thiserror 2.0.15", "windows 0.61.3", "windows-version", ] @@ -9581,7 +9753,7 @@ dependencies = [ "memmem", "num-derive 0.3.3", "num-traits", - "ordered-float", + "ordered-float 2.10.1", "regex", "semver 0.11.0", "sha2 0.9.9", @@ -9614,7 +9786,7 @@ checksum = "451b374529930d7601b1eef8d32bc79ae870b6079b069401709c2a8bf9e75f36" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -9645,11 +9817,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.15", ] [[package]] @@ -9660,18 +9832,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -9791,7 +9963,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -9838,7 +10010,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -9896,7 +10068,7 @@ dependencies = [ "futures-io", "futures-sink", "futures-util", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "pin-project-lite", "slab", "tokio", @@ -9916,9 +10088,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ "indexmap 2.10.0", "serde", @@ -9985,9 +10157,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" dependencies = [ "winnow 0.7.12", ] @@ -10026,7 +10198,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "bytes", "futures-util", "http 1.3.1", @@ -10083,7 +10255,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -10161,15 +10333,15 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da75ec677957aa21f6e0b361df0daab972f13a5bee3606de0638fd4ee1c666a" +checksum = "a0d92153331e7d02ec09137538996a7786fe679c629c279e82a6be762b7e6fe2" dependencies = [ "crossbeam-channel", "dirs 6.0.0", "libappindicator", "muda", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-core-graphics", @@ -10177,17 +10349,16 @@ dependencies = [ "once_cell", "png", "serde", - "thiserror 2.0.12", + "thiserror 2.0.15", "windows-sys 0.59.0", ] [[package]] name = "tree_magic_mini" -version = "3.1.6" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac5e8971f245c3389a5a76e648bfc80803ae066a1243a75db0064d7c1129d63" +checksum = "f943391d896cdfe8eec03a04d7110332d445be7df856db382dd96a730667562c" dependencies = [ - "fnv", "memchr", "nom 7.1.3", "once_cell", @@ -10233,7 +10404,7 @@ dependencies = [ "log", "rand 0.9.2", "sha1", - "thiserror 2.0.12", + "thiserror 2.0.15", "utf-8", ] @@ -10250,7 +10421,7 @@ dependencies = [ "log", "rand 0.9.2", "sha1", - "thiserror 2.0.12", + "thiserror 2.0.15", "utf-8", ] @@ -10522,9 +10693,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" dependencies = [ "getrandom 0.3.3", "js-sys", @@ -10692,7 +10863,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -10727,7 +10898,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -10756,13 +10927,13 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.44", + "rustix 1.0.8", "scoped-tls", "smallvec", "wayland-sys", @@ -10770,12 +10941,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.1", - "rustix 0.38.44", + "bitflags 2.9.2", + "rustix 1.0.8", "wayland-backend", "wayland-scanner", ] @@ -10786,29 +10957,29 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" +checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" dependencies = [ - "rustix 0.38.44", + "rustix 1.0.8", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.32.8" +version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "wayland-backend", "wayland-client", "wayland-scanner", @@ -10816,11 +10987,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" +checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -10829,11 +11000,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" +checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -10842,9 +11013,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" dependencies = [ "proc-macro2", "quick-xml 0.37.5", @@ -10853,9 +11024,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" dependencies = [ "dlib", "log", @@ -10893,7 +11064,7 @@ dependencies = [ "jni", "log", "ndk-context", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-foundation 0.3.1", "url", "web-sys", @@ -10983,7 +11154,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -10992,7 +11163,7 @@ version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" dependencies = [ - "thiserror 2.0.12", + "thiserror 2.0.15", "windows 0.61.3", "windows-core 0.61.2", ] @@ -11010,12 +11181,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec8fb398f119472be4d80bc3647339f56eb63b2a331f6a3d16e25d8144197dd9" dependencies = [ "arrayvec", - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg_aliases", "document-features", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "js-sys", "log", + "naga", "parking_lot", "portable-atomic", "profiling", @@ -11023,6 +11195,7 @@ dependencies = [ "smallvec", "static_assertions", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", "wgpu-core", "wgpu-hal", @@ -11038,10 +11211,10 @@ dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.9.1", + "bitflags 2.9.2", "cfg_aliases", "document-features", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "indexmap 2.10.0", "log", "naga", @@ -11052,12 +11225,32 @@ dependencies = [ "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.15", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", "wgpu-core-deps-windows-linux-android", "wgpu-hal", "wgpu-types", ] +[[package]] +name = "wgpu-core-deps-apple" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd488b3239b6b7b185c3b045c39ca6bf8af34467a4c5de4e0b1a564135d093d" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09ad7aceb3818e52539acc679f049d3475775586f3f4e311c30165cf2c00445" +dependencies = [ + "wgpu-hal", +] + [[package]] name = "wgpu-core-deps-windows-linux-android" version = "25.0.0" @@ -11073,17 +11266,45 @@ version = "25.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f968767fe4d3d33747bbd1473ccd55bf0f6451f55d733b5597e67b5deab4ad17" dependencies = [ - "bitflags 2.9.1", + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.9.2", + "block", + "bytemuck", + "cfg-if", "cfg_aliases", + "core-graphics-types 0.1.3", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.15.5", + "js-sys", + "khronos-egl", + "libc", "libloading 0.8.8", "log", + "metal", "naga", + "ndk-sys 0.5.0+25.2.9519653", + "objc", + "ordered-float 4.6.0", "parking_lot", "portable-atomic", + "profiling", + "range-alloc", "raw-window-handle", "renderdoc-sys", - "thiserror 2.0.12", + "smallvec", + "thiserror 2.0.15", + "wasm-bindgen", + "web-sys", "wgpu-types", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] @@ -11092,11 +11313,11 @@ version = "25.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aa49460c2a8ee8edba3fca54325540d904dd85b2e086ada762767e17d06e8bc" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "bytemuck", "js-sys", "log", - "thiserror 2.0.12", + "thiserror 2.0.15", "web-sys", ] @@ -11137,11 +11358,11 @@ dependencies = [ [[package]] name = "whoami" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" dependencies = [ - "redox_syscall 0.5.13", + "libredox", "wasite", "web-sys", ] @@ -11195,7 +11416,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-foundation 0.3.1", @@ -11231,7 +11452,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" dependencies = [ "windows-core 0.59.0", - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -11288,7 +11509,7 @@ dependencies = [ "windows-interface 0.59.1", "windows-result 0.3.4", "windows-strings 0.3.1", - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -11323,7 +11544,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -11334,7 +11555,7 @@ checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -11345,7 +11566,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -11356,7 +11577,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -11367,7 +11588,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -11505,7 +11726,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -11556,10 +11777,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -11770,14 +11992,14 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winit" -version = "0.30.11" +version = "0.30.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4409c10174df8779dc29a4788cac85ed84024ccbc1743b776b21a520ee1aaf4" +checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.1", + "bitflags 2.9.2", "block2 0.5.1", "bytemuck", "calloop", @@ -11870,7 +12092,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] @@ -11884,7 +12106,7 @@ dependencies = [ "os_pipe", "rustix 0.38.44", "tempfile", - "thiserror 2.0.12", + "thiserror 2.0.15", "tree_magic_mini", "wayland-backend", "wayland-client", @@ -11931,7 +12153,7 @@ dependencies = [ "kuchikiki", "libc", "ndk", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-foundation 0.3.1", @@ -11943,7 +12165,7 @@ dependencies = [ "sha2 0.10.9", "soup3", "tao-macros", - "thiserror 2.0.12", + "thiserror 2.0.15", "url", "webkit2gtk", "webkit2gtk-sys", @@ -12008,9 +12230,9 @@ dependencies = [ [[package]] name = "xcb" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e2f212bb1a92cd8caac8051b829a6582ede155ccb60b5d5908b81b100952be" +checksum = "f07c123b796139bfe0603e654eaf08e132e52387ba95b252c78bad3640ba37ea" dependencies = [ "bitflags 1.3.2", "libc", @@ -12035,7 +12257,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "dlib", "log", "once_cell", @@ -12107,7 +12329,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -12119,7 +12341,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -12139,9 +12361,9 @@ dependencies = [ "async-trait", "blocking", "enumflags2", - "event-listener 5.4.0", + "event-listener 5.4.1", "futures-core", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "hex", "nix 0.30.1", "ordered-stream", @@ -12175,7 +12397,7 @@ checksum = "dc6821851fa840b708b4cbbaf6241868cabc85a2dc22f426361b0292bfc0b836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "zbus-lockstep", "zbus_xml", "zvariant", @@ -12190,7 +12412,7 @@ dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "zbus_names", "zvariant", "zvariant_utils", @@ -12238,7 +12460,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -12258,7 +12480,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -12279,7 +12501,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -12306,9 +12528,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke 0.8.0", "zerofrom", @@ -12323,7 +12545,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -12334,7 +12556,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -12448,9 +12670,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a" +checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" dependencies = [ "zune-core", ] @@ -12479,7 +12701,7 @@ dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "zvariant_utils", ] @@ -12493,6 +12715,6 @@ dependencies = [ "quote", "serde", "static_assertions", - "syn 2.0.104", + "syn 2.0.106", "winnow 0.7.12", ] diff --git a/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/audit.yml b/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/audit.yml index 6ac151b9a9..16843062da 100644 --- a/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/audit.yml +++ b/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/audit.yml @@ -20,7 +20,7 @@ jobs: audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions-rs/audit-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/format.yml b/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/format.yml index 6f7db438b6..786036e525 100644 --- a/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/format.yml +++ b/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/format.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt diff --git a/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/lint.yml b/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/lint.yml index 26f18d1060..57d93ee7e3 100644 --- a/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/lint.yml +++ b/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/lint.yml @@ -19,7 +19,7 @@ jobs: runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable with: components: clippy diff --git a/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/release.yml b/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/release.yml index ba3a45a50f..f10c2b8161 100644 --- a/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/release.yml +++ b/clash-nyanpasu/backend/tauri-plugin-deep-link/.github/workflows/release.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 diff --git a/clash-nyanpasu/backend/tauri/Cargo.toml b/clash-nyanpasu/backend/tauri/Cargo.toml index 0438a472e2..d01093406f 100644 --- a/clash-nyanpasu/backend/tauri/Cargo.toml +++ b/clash-nyanpasu/backend/tauri/Cargo.toml @@ -148,7 +148,7 @@ notify-debouncer-full = "0.6.0" notify = "8.0.0" # Database -redb = "2.0.0" +redb = "3.0.0" # Logging & Tracing log = "0.4.20" @@ -172,12 +172,12 @@ display-info = "0.5.0" # should be removed after upgrading to tauri v2 # OXC (The Oxidation Compiler) # We use it to parse and transpile the old script profile to esm based script profile -oxc_parser = "0.81" -oxc_allocator = "0.81" -oxc_span = "0.81" -oxc_ast = "0.81" -oxc_syntax = "0.81" -oxc_ast_visit = "0.81" +oxc_parser = "0.82" +oxc_allocator = "0.82" +oxc_span = "0.82" +oxc_ast = "0.82" +oxc_syntax = "0.82" +oxc_ast_visit = "0.82" # Lua Integration mlua = { version = "0.11", features = [ diff --git a/clash-nyanpasu/backend/tauri/gen/schemas/acl-manifests.json b/clash-nyanpasu/backend/tauri/gen/schemas/acl-manifests.json index 77f1baf776..b116065255 100644 --- a/clash-nyanpasu/backend/tauri/gen/schemas/acl-manifests.json +++ b/clash-nyanpasu/backend/tauri/gen/schemas/acl-manifests.json @@ -1 +1 @@ -{"clipboard-manager":{"default_permission":{"identifier":"default","description":"No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n","permissions":[]},"permissions":{"allow-clear":{"identifier":"allow-clear","description":"Enables the clear command without any pre-configured scope.","commands":{"allow":["clear"],"deny":[]}},"allow-read-image":{"identifier":"allow-read-image","description":"Enables the read_image command without any pre-configured scope.","commands":{"allow":["read_image"],"deny":[]}},"allow-read-text":{"identifier":"allow-read-text","description":"Enables the read_text command without any pre-configured scope.","commands":{"allow":["read_text"],"deny":[]}},"allow-write-html":{"identifier":"allow-write-html","description":"Enables the write_html command without any pre-configured scope.","commands":{"allow":["write_html"],"deny":[]}},"allow-write-image":{"identifier":"allow-write-image","description":"Enables the write_image command without any pre-configured scope.","commands":{"allow":["write_image"],"deny":[]}},"allow-write-text":{"identifier":"allow-write-text","description":"Enables the write_text command without any pre-configured scope.","commands":{"allow":["write_text"],"deny":[]}},"deny-clear":{"identifier":"deny-clear","description":"Denies the clear command without any pre-configured scope.","commands":{"allow":[],"deny":["clear"]}},"deny-read-image":{"identifier":"deny-read-image","description":"Denies the read_image command without any pre-configured scope.","commands":{"allow":[],"deny":["read_image"]}},"deny-read-text":{"identifier":"deny-read-text","description":"Denies the read_text command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text"]}},"deny-write-html":{"identifier":"deny-write-html","description":"Denies the write_html command without any pre-configured scope.","commands":{"allow":[],"deny":["write_html"]}},"deny-write-image":{"identifier":"deny-write-image","description":"Denies the write_image command without any pre-configured scope.","commands":{"allow":[],"deny":["write_image"]}},"deny-write-text":{"identifier":"deny-write-text","description":"Denies the write_text command without any pre-configured scope.","commands":{"allow":[],"deny":["write_text"]}}},"permission_sets":{},"global_scope_schema":null},"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"dialog":{"default_permission":{"identifier":"default","description":"This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n","permissions":["allow-ask","allow-confirm","allow-message","allow-save","allow-open"]},"permissions":{"allow-ask":{"identifier":"allow-ask","description":"Enables the ask command without any pre-configured scope.","commands":{"allow":["ask"],"deny":[]}},"allow-confirm":{"identifier":"allow-confirm","description":"Enables the confirm command without any pre-configured scope.","commands":{"allow":["confirm"],"deny":[]}},"allow-message":{"identifier":"allow-message","description":"Enables the message command without any pre-configured scope.","commands":{"allow":["message"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-save":{"identifier":"allow-save","description":"Enables the save command without any pre-configured scope.","commands":{"allow":["save"],"deny":[]}},"deny-ask":{"identifier":"deny-ask","description":"Denies the ask command without any pre-configured scope.","commands":{"allow":[],"deny":["ask"]}},"deny-confirm":{"identifier":"deny-confirm","description":"Denies the confirm command without any pre-configured scope.","commands":{"allow":[],"deny":["confirm"]}},"deny-message":{"identifier":"deny-message","description":"Denies the message command without any pre-configured scope.","commands":{"allow":[],"deny":["message"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-save":{"identifier":"deny-save","description":"Denies the save command without any pre-configured scope.","commands":{"allow":[],"deny":["save"]}}},"permission_sets":{},"global_scope_schema":null},"fs":{"default_permission":{"identifier":"default","description":"This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n","permissions":["create-app-specific-dirs","read-app-specific-dirs-recursive","deny-default"]},"permissions":{"allow-copy-file":{"identifier":"allow-copy-file","description":"Enables the copy_file command without any pre-configured scope.","commands":{"allow":["copy_file"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-exists":{"identifier":"allow-exists","description":"Enables the exists command without any pre-configured scope.","commands":{"allow":["exists"],"deny":[]}},"allow-fstat":{"identifier":"allow-fstat","description":"Enables the fstat command without any pre-configured scope.","commands":{"allow":["fstat"],"deny":[]}},"allow-ftruncate":{"identifier":"allow-ftruncate","description":"Enables the ftruncate command without any pre-configured scope.","commands":{"allow":["ftruncate"],"deny":[]}},"allow-lstat":{"identifier":"allow-lstat","description":"Enables the lstat command without any pre-configured scope.","commands":{"allow":["lstat"],"deny":[]}},"allow-mkdir":{"identifier":"allow-mkdir","description":"Enables the mkdir command without any pre-configured scope.","commands":{"allow":["mkdir"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-read":{"identifier":"allow-read","description":"Enables the read command without any pre-configured scope.","commands":{"allow":["read"],"deny":[]}},"allow-read-dir":{"identifier":"allow-read-dir","description":"Enables the read_dir command without any pre-configured scope.","commands":{"allow":["read_dir"],"deny":[]}},"allow-read-file":{"identifier":"allow-read-file","description":"Enables the read_file command without any pre-configured scope.","commands":{"allow":["read_file"],"deny":[]}},"allow-read-text-file":{"identifier":"allow-read-text-file","description":"Enables the read_text_file command without any pre-configured scope.","commands":{"allow":["read_text_file"],"deny":[]}},"allow-read-text-file-lines":{"identifier":"allow-read-text-file-lines","description":"Enables the read_text_file_lines command without any pre-configured scope.","commands":{"allow":["read_text_file_lines","read_text_file_lines_next"],"deny":[]}},"allow-read-text-file-lines-next":{"identifier":"allow-read-text-file-lines-next","description":"Enables the read_text_file_lines_next command without any pre-configured scope.","commands":{"allow":["read_text_file_lines_next"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-rename":{"identifier":"allow-rename","description":"Enables the rename command without any pre-configured scope.","commands":{"allow":["rename"],"deny":[]}},"allow-seek":{"identifier":"allow-seek","description":"Enables the seek command without any pre-configured scope.","commands":{"allow":["seek"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"allow-stat":{"identifier":"allow-stat","description":"Enables the stat command without any pre-configured scope.","commands":{"allow":["stat"],"deny":[]}},"allow-truncate":{"identifier":"allow-truncate","description":"Enables the truncate command without any pre-configured scope.","commands":{"allow":["truncate"],"deny":[]}},"allow-unwatch":{"identifier":"allow-unwatch","description":"Enables the unwatch command without any pre-configured scope.","commands":{"allow":["unwatch"],"deny":[]}},"allow-watch":{"identifier":"allow-watch","description":"Enables the watch command without any pre-configured scope.","commands":{"allow":["watch"],"deny":[]}},"allow-write":{"identifier":"allow-write","description":"Enables the write command without any pre-configured scope.","commands":{"allow":["write"],"deny":[]}},"allow-write-file":{"identifier":"allow-write-file","description":"Enables the write_file command without any pre-configured scope.","commands":{"allow":["write_file","open","write"],"deny":[]}},"allow-write-text-file":{"identifier":"allow-write-text-file","description":"Enables the write_text_file command without any pre-configured scope.","commands":{"allow":["write_text_file"],"deny":[]}},"create-app-specific-dirs":{"identifier":"create-app-specific-dirs","description":"This permissions allows to create the application specific directories.\n","commands":{"allow":["mkdir","scope-app-index"],"deny":[]}},"deny-copy-file":{"identifier":"deny-copy-file","description":"Denies the copy_file command without any pre-configured scope.","commands":{"allow":[],"deny":["copy_file"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-exists":{"identifier":"deny-exists","description":"Denies the exists command without any pre-configured scope.","commands":{"allow":[],"deny":["exists"]}},"deny-fstat":{"identifier":"deny-fstat","description":"Denies the fstat command without any pre-configured scope.","commands":{"allow":[],"deny":["fstat"]}},"deny-ftruncate":{"identifier":"deny-ftruncate","description":"Denies the ftruncate command without any pre-configured scope.","commands":{"allow":[],"deny":["ftruncate"]}},"deny-lstat":{"identifier":"deny-lstat","description":"Denies the lstat command without any pre-configured scope.","commands":{"allow":[],"deny":["lstat"]}},"deny-mkdir":{"identifier":"deny-mkdir","description":"Denies the mkdir command without any pre-configured scope.","commands":{"allow":[],"deny":["mkdir"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-read":{"identifier":"deny-read","description":"Denies the read command without any pre-configured scope.","commands":{"allow":[],"deny":["read"]}},"deny-read-dir":{"identifier":"deny-read-dir","description":"Denies the read_dir command without any pre-configured scope.","commands":{"allow":[],"deny":["read_dir"]}},"deny-read-file":{"identifier":"deny-read-file","description":"Denies the read_file command without any pre-configured scope.","commands":{"allow":[],"deny":["read_file"]}},"deny-read-text-file":{"identifier":"deny-read-text-file","description":"Denies the read_text_file command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file"]}},"deny-read-text-file-lines":{"identifier":"deny-read-text-file-lines","description":"Denies the read_text_file_lines command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file_lines"]}},"deny-read-text-file-lines-next":{"identifier":"deny-read-text-file-lines-next","description":"Denies the read_text_file_lines_next command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file_lines_next"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-rename":{"identifier":"deny-rename","description":"Denies the rename command without any pre-configured scope.","commands":{"allow":[],"deny":["rename"]}},"deny-seek":{"identifier":"deny-seek","description":"Denies the seek command without any pre-configured scope.","commands":{"allow":[],"deny":["seek"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}},"deny-stat":{"identifier":"deny-stat","description":"Denies the stat command without any pre-configured scope.","commands":{"allow":[],"deny":["stat"]}},"deny-truncate":{"identifier":"deny-truncate","description":"Denies the truncate command without any pre-configured scope.","commands":{"allow":[],"deny":["truncate"]}},"deny-unwatch":{"identifier":"deny-unwatch","description":"Denies the unwatch command without any pre-configured scope.","commands":{"allow":[],"deny":["unwatch"]}},"deny-watch":{"identifier":"deny-watch","description":"Denies the watch command without any pre-configured scope.","commands":{"allow":[],"deny":["watch"]}},"deny-webview-data-linux":{"identifier":"deny-webview-data-linux","description":"This denies read access to the\n`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.","commands":{"allow":[],"deny":[]}},"deny-webview-data-windows":{"identifier":"deny-webview-data-windows","description":"This denies read access to the\n`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.","commands":{"allow":[],"deny":[]}},"deny-write":{"identifier":"deny-write","description":"Denies the write command without any pre-configured scope.","commands":{"allow":[],"deny":["write"]}},"deny-write-file":{"identifier":"deny-write-file","description":"Denies the write_file command without any pre-configured scope.","commands":{"allow":[],"deny":["write_file"]}},"deny-write-text-file":{"identifier":"deny-write-text-file","description":"Denies the write_text_file command without any pre-configured scope.","commands":{"allow":[],"deny":["write_text_file"]}},"read-all":{"identifier":"read-all","description":"This enables all read related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","read_file","read","open","read_text_file","read_text_file_lines","read_text_file_lines_next","seek","stat","lstat","fstat","exists","watch","unwatch"],"deny":[]}},"read-app-specific-dirs-recursive":{"identifier":"read-app-specific-dirs-recursive","description":"This permission allows recursive read functionality on the application\nspecific base directories. \n","commands":{"allow":["read_dir","read_file","read_text_file","read_text_file_lines","read_text_file_lines_next","exists","scope-app-recursive"],"deny":[]}},"read-dirs":{"identifier":"read-dirs","description":"This enables directory read and file metadata related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","stat","lstat","fstat","exists"],"deny":[]}},"read-files":{"identifier":"read-files","description":"This enables file read related commands without any pre-configured accessible paths.","commands":{"allow":["read_file","read","open","read_text_file","read_text_file_lines","read_text_file_lines_next","seek","stat","lstat","fstat","exists"],"deny":[]}},"read-meta":{"identifier":"read-meta","description":"This enables all index or metadata related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","stat","lstat","fstat","exists","size"],"deny":[]}},"scope":{"identifier":"scope","description":"An empty permission you can use to modify the global scope.","commands":{"allow":[],"deny":[]}},"scope-app":{"identifier":"scope-app","description":"This scope permits access to all files and list content of top level directories in the application folders.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/*"},{"path":"$APPDATA"},{"path":"$APPDATA/*"},{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/*"},{"path":"$APPCACHE"},{"path":"$APPCACHE/*"},{"path":"$APPLOG"},{"path":"$APPLOG/*"}]}},"scope-app-index":{"identifier":"scope-app-index","description":"This scope permits to list all files and folders in the application directories.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPDATA"},{"path":"$APPLOCALDATA"},{"path":"$APPCACHE"},{"path":"$APPLOG"}]}},"scope-app-recursive":{"identifier":"scope-app-recursive","description":"This scope permits recursive access to the complete application folders, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/**"},{"path":"$APPDATA"},{"path":"$APPDATA/**"},{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/**"},{"path":"$APPCACHE"},{"path":"$APPCACHE/**"},{"path":"$APPLOG"},{"path":"$APPLOG/**"}]}},"scope-appcache":{"identifier":"scope-appcache","description":"This scope permits access to all files and list content of top level directories in the `$APPCACHE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"},{"path":"$APPCACHE/*"}]}},"scope-appcache-index":{"identifier":"scope-appcache-index","description":"This scope permits to list all files and folders in the `$APPCACHE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"}]}},"scope-appcache-recursive":{"identifier":"scope-appcache-recursive","description":"This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"},{"path":"$APPCACHE/**"}]}},"scope-appconfig":{"identifier":"scope-appconfig","description":"This scope permits access to all files and list content of top level directories in the `$APPCONFIG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/*"}]}},"scope-appconfig-index":{"identifier":"scope-appconfig-index","description":"This scope permits to list all files and folders in the `$APPCONFIG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"}]}},"scope-appconfig-recursive":{"identifier":"scope-appconfig-recursive","description":"This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/**"}]}},"scope-appdata":{"identifier":"scope-appdata","description":"This scope permits access to all files and list content of top level directories in the `$APPDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"},{"path":"$APPDATA/*"}]}},"scope-appdata-index":{"identifier":"scope-appdata-index","description":"This scope permits to list all files and folders in the `$APPDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"}]}},"scope-appdata-recursive":{"identifier":"scope-appdata-recursive","description":"This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]}},"scope-applocaldata":{"identifier":"scope-applocaldata","description":"This scope permits access to all files and list content of top level directories in the `$APPLOCALDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/*"}]}},"scope-applocaldata-index":{"identifier":"scope-applocaldata-index","description":"This scope permits to list all files and folders in the `$APPLOCALDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"}]}},"scope-applocaldata-recursive":{"identifier":"scope-applocaldata-recursive","description":"This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/**"}]}},"scope-applog":{"identifier":"scope-applog","description":"This scope permits access to all files and list content of top level directories in the `$APPLOG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"},{"path":"$APPLOG/*"}]}},"scope-applog-index":{"identifier":"scope-applog-index","description":"This scope permits to list all files and folders in the `$APPLOG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"}]}},"scope-applog-recursive":{"identifier":"scope-applog-recursive","description":"This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"},{"path":"$APPLOG/**"}]}},"scope-audio":{"identifier":"scope-audio","description":"This scope permits access to all files and list content of top level directories in the `$AUDIO` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"},{"path":"$AUDIO/*"}]}},"scope-audio-index":{"identifier":"scope-audio-index","description":"This scope permits to list all files and folders in the `$AUDIO`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"}]}},"scope-audio-recursive":{"identifier":"scope-audio-recursive","description":"This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"},{"path":"$AUDIO/**"}]}},"scope-cache":{"identifier":"scope-cache","description":"This scope permits access to all files and list content of top level directories in the `$CACHE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"},{"path":"$CACHE/*"}]}},"scope-cache-index":{"identifier":"scope-cache-index","description":"This scope permits to list all files and folders in the `$CACHE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"}]}},"scope-cache-recursive":{"identifier":"scope-cache-recursive","description":"This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"},{"path":"$CACHE/**"}]}},"scope-config":{"identifier":"scope-config","description":"This scope permits access to all files and list content of top level directories in the `$CONFIG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"},{"path":"$CONFIG/*"}]}},"scope-config-index":{"identifier":"scope-config-index","description":"This scope permits to list all files and folders in the `$CONFIG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"}]}},"scope-config-recursive":{"identifier":"scope-config-recursive","description":"This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"},{"path":"$CONFIG/**"}]}},"scope-data":{"identifier":"scope-data","description":"This scope permits access to all files and list content of top level directories in the `$DATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"},{"path":"$DATA/*"}]}},"scope-data-index":{"identifier":"scope-data-index","description":"This scope permits to list all files and folders in the `$DATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"}]}},"scope-data-recursive":{"identifier":"scope-data-recursive","description":"This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"},{"path":"$DATA/**"}]}},"scope-desktop":{"identifier":"scope-desktop","description":"This scope permits access to all files and list content of top level directories in the `$DESKTOP` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"},{"path":"$DESKTOP/*"}]}},"scope-desktop-index":{"identifier":"scope-desktop-index","description":"This scope permits to list all files and folders in the `$DESKTOP`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"}]}},"scope-desktop-recursive":{"identifier":"scope-desktop-recursive","description":"This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"},{"path":"$DESKTOP/**"}]}},"scope-document":{"identifier":"scope-document","description":"This scope permits access to all files and list content of top level directories in the `$DOCUMENT` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"},{"path":"$DOCUMENT/*"}]}},"scope-document-index":{"identifier":"scope-document-index","description":"This scope permits to list all files and folders in the `$DOCUMENT`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"}]}},"scope-document-recursive":{"identifier":"scope-document-recursive","description":"This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"},{"path":"$DOCUMENT/**"}]}},"scope-download":{"identifier":"scope-download","description":"This scope permits access to all files and list content of top level directories in the `$DOWNLOAD` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"},{"path":"$DOWNLOAD/*"}]}},"scope-download-index":{"identifier":"scope-download-index","description":"This scope permits to list all files and folders in the `$DOWNLOAD`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"}]}},"scope-download-recursive":{"identifier":"scope-download-recursive","description":"This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"},{"path":"$DOWNLOAD/**"}]}},"scope-exe":{"identifier":"scope-exe","description":"This scope permits access to all files and list content of top level directories in the `$EXE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"},{"path":"$EXE/*"}]}},"scope-exe-index":{"identifier":"scope-exe-index","description":"This scope permits to list all files and folders in the `$EXE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"}]}},"scope-exe-recursive":{"identifier":"scope-exe-recursive","description":"This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"},{"path":"$EXE/**"}]}},"scope-font":{"identifier":"scope-font","description":"This scope permits access to all files and list content of top level directories in the `$FONT` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"},{"path":"$FONT/*"}]}},"scope-font-index":{"identifier":"scope-font-index","description":"This scope permits to list all files and folders in the `$FONT`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"}]}},"scope-font-recursive":{"identifier":"scope-font-recursive","description":"This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"},{"path":"$FONT/**"}]}},"scope-home":{"identifier":"scope-home","description":"This scope permits access to all files and list content of top level directories in the `$HOME` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"},{"path":"$HOME/*"}]}},"scope-home-index":{"identifier":"scope-home-index","description":"This scope permits to list all files and folders in the `$HOME`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"}]}},"scope-home-recursive":{"identifier":"scope-home-recursive","description":"This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"},{"path":"$HOME/**"}]}},"scope-localdata":{"identifier":"scope-localdata","description":"This scope permits access to all files and list content of top level directories in the `$LOCALDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"},{"path":"$LOCALDATA/*"}]}},"scope-localdata-index":{"identifier":"scope-localdata-index","description":"This scope permits to list all files and folders in the `$LOCALDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"}]}},"scope-localdata-recursive":{"identifier":"scope-localdata-recursive","description":"This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"},{"path":"$LOCALDATA/**"}]}},"scope-log":{"identifier":"scope-log","description":"This scope permits access to all files and list content of top level directories in the `$LOG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"},{"path":"$LOG/*"}]}},"scope-log-index":{"identifier":"scope-log-index","description":"This scope permits to list all files and folders in the `$LOG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"}]}},"scope-log-recursive":{"identifier":"scope-log-recursive","description":"This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"},{"path":"$LOG/**"}]}},"scope-picture":{"identifier":"scope-picture","description":"This scope permits access to all files and list content of top level directories in the `$PICTURE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"},{"path":"$PICTURE/*"}]}},"scope-picture-index":{"identifier":"scope-picture-index","description":"This scope permits to list all files and folders in the `$PICTURE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"}]}},"scope-picture-recursive":{"identifier":"scope-picture-recursive","description":"This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"},{"path":"$PICTURE/**"}]}},"scope-public":{"identifier":"scope-public","description":"This scope permits access to all files and list content of top level directories in the `$PUBLIC` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"},{"path":"$PUBLIC/*"}]}},"scope-public-index":{"identifier":"scope-public-index","description":"This scope permits to list all files and folders in the `$PUBLIC`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"}]}},"scope-public-recursive":{"identifier":"scope-public-recursive","description":"This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"},{"path":"$PUBLIC/**"}]}},"scope-resource":{"identifier":"scope-resource","description":"This scope permits access to all files and list content of top level directories in the `$RESOURCE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"},{"path":"$RESOURCE/*"}]}},"scope-resource-index":{"identifier":"scope-resource-index","description":"This scope permits to list all files and folders in the `$RESOURCE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"}]}},"scope-resource-recursive":{"identifier":"scope-resource-recursive","description":"This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"},{"path":"$RESOURCE/**"}]}},"scope-runtime":{"identifier":"scope-runtime","description":"This scope permits access to all files and list content of top level directories in the `$RUNTIME` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"},{"path":"$RUNTIME/*"}]}},"scope-runtime-index":{"identifier":"scope-runtime-index","description":"This scope permits to list all files and folders in the `$RUNTIME`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"}]}},"scope-runtime-recursive":{"identifier":"scope-runtime-recursive","description":"This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"},{"path":"$RUNTIME/**"}]}},"scope-temp":{"identifier":"scope-temp","description":"This scope permits access to all files and list content of top level directories in the `$TEMP` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"},{"path":"$TEMP/*"}]}},"scope-temp-index":{"identifier":"scope-temp-index","description":"This scope permits to list all files and folders in the `$TEMP`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"}]}},"scope-temp-recursive":{"identifier":"scope-temp-recursive","description":"This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"},{"path":"$TEMP/**"}]}},"scope-template":{"identifier":"scope-template","description":"This scope permits access to all files and list content of top level directories in the `$TEMPLATE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"},{"path":"$TEMPLATE/*"}]}},"scope-template-index":{"identifier":"scope-template-index","description":"This scope permits to list all files and folders in the `$TEMPLATE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"}]}},"scope-template-recursive":{"identifier":"scope-template-recursive","description":"This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"},{"path":"$TEMPLATE/**"}]}},"scope-video":{"identifier":"scope-video","description":"This scope permits access to all files and list content of top level directories in the `$VIDEO` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"},{"path":"$VIDEO/*"}]}},"scope-video-index":{"identifier":"scope-video-index","description":"This scope permits to list all files and folders in the `$VIDEO`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"}]}},"scope-video-recursive":{"identifier":"scope-video-recursive","description":"This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"},{"path":"$VIDEO/**"}]}},"write-all":{"identifier":"write-all","description":"This enables all write related commands without any pre-configured accessible paths.","commands":{"allow":["mkdir","create","copy_file","remove","rename","truncate","ftruncate","write","write_file","write_text_file"],"deny":[]}},"write-files":{"identifier":"write-files","description":"This enables all file write related commands without any pre-configured accessible paths.","commands":{"allow":["create","copy_file","remove","rename","truncate","ftruncate","write","write_file","write_text_file"],"deny":[]}}},"permission_sets":{"allow-app-meta":{"identifier":"allow-app-meta","description":"This allows non-recursive read access to metadata of the application folders, including file listing and statistics.","permissions":["read-meta","scope-app-index"]},"allow-app-meta-recursive":{"identifier":"allow-app-meta-recursive","description":"This allows full recursive read access to metadata of the application folders, including file listing and statistics.","permissions":["read-meta","scope-app-recursive"]},"allow-app-read":{"identifier":"allow-app-read","description":"This allows non-recursive read access to the application folders.","permissions":["read-all","scope-app"]},"allow-app-read-recursive":{"identifier":"allow-app-read-recursive","description":"This allows full recursive read access to the complete application folders, files and subdirectories.","permissions":["read-all","scope-app-recursive"]},"allow-app-write":{"identifier":"allow-app-write","description":"This allows non-recursive write access to the application folders.","permissions":["write-all","scope-app"]},"allow-app-write-recursive":{"identifier":"allow-app-write-recursive","description":"This allows full recursive write access to the complete application folders, files and subdirectories.","permissions":["write-all","scope-app-recursive"]},"allow-appcache-meta":{"identifier":"allow-appcache-meta","description":"This allows non-recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-appcache-index"]},"allow-appcache-meta-recursive":{"identifier":"allow-appcache-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-appcache-recursive"]},"allow-appcache-read":{"identifier":"allow-appcache-read","description":"This allows non-recursive read access to the `$APPCACHE` folder.","permissions":["read-all","scope-appcache"]},"allow-appcache-read-recursive":{"identifier":"allow-appcache-read-recursive","description":"This allows full recursive read access to the complete `$APPCACHE` folder, files and subdirectories.","permissions":["read-all","scope-appcache-recursive"]},"allow-appcache-write":{"identifier":"allow-appcache-write","description":"This allows non-recursive write access to the `$APPCACHE` folder.","permissions":["write-all","scope-appcache"]},"allow-appcache-write-recursive":{"identifier":"allow-appcache-write-recursive","description":"This allows full recursive write access to the complete `$APPCACHE` folder, files and subdirectories.","permissions":["write-all","scope-appcache-recursive"]},"allow-appconfig-meta":{"identifier":"allow-appconfig-meta","description":"This allows non-recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-appconfig-index"]},"allow-appconfig-meta-recursive":{"identifier":"allow-appconfig-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-appconfig-recursive"]},"allow-appconfig-read":{"identifier":"allow-appconfig-read","description":"This allows non-recursive read access to the `$APPCONFIG` folder.","permissions":["read-all","scope-appconfig"]},"allow-appconfig-read-recursive":{"identifier":"allow-appconfig-read-recursive","description":"This allows full recursive read access to the complete `$APPCONFIG` folder, files and subdirectories.","permissions":["read-all","scope-appconfig-recursive"]},"allow-appconfig-write":{"identifier":"allow-appconfig-write","description":"This allows non-recursive write access to the `$APPCONFIG` folder.","permissions":["write-all","scope-appconfig"]},"allow-appconfig-write-recursive":{"identifier":"allow-appconfig-write-recursive","description":"This allows full recursive write access to the complete `$APPCONFIG` folder, files and subdirectories.","permissions":["write-all","scope-appconfig-recursive"]},"allow-appdata-meta":{"identifier":"allow-appdata-meta","description":"This allows non-recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-appdata-index"]},"allow-appdata-meta-recursive":{"identifier":"allow-appdata-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-appdata-recursive"]},"allow-appdata-read":{"identifier":"allow-appdata-read","description":"This allows non-recursive read access to the `$APPDATA` folder.","permissions":["read-all","scope-appdata"]},"allow-appdata-read-recursive":{"identifier":"allow-appdata-read-recursive","description":"This allows full recursive read access to the complete `$APPDATA` folder, files and subdirectories.","permissions":["read-all","scope-appdata-recursive"]},"allow-appdata-write":{"identifier":"allow-appdata-write","description":"This allows non-recursive write access to the `$APPDATA` folder.","permissions":["write-all","scope-appdata"]},"allow-appdata-write-recursive":{"identifier":"allow-appdata-write-recursive","description":"This allows full recursive write access to the complete `$APPDATA` folder, files and subdirectories.","permissions":["write-all","scope-appdata-recursive"]},"allow-applocaldata-meta":{"identifier":"allow-applocaldata-meta","description":"This allows non-recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-applocaldata-index"]},"allow-applocaldata-meta-recursive":{"identifier":"allow-applocaldata-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-applocaldata-recursive"]},"allow-applocaldata-read":{"identifier":"allow-applocaldata-read","description":"This allows non-recursive read access to the `$APPLOCALDATA` folder.","permissions":["read-all","scope-applocaldata"]},"allow-applocaldata-read-recursive":{"identifier":"allow-applocaldata-read-recursive","description":"This allows full recursive read access to the complete `$APPLOCALDATA` folder, files and subdirectories.","permissions":["read-all","scope-applocaldata-recursive"]},"allow-applocaldata-write":{"identifier":"allow-applocaldata-write","description":"This allows non-recursive write access to the `$APPLOCALDATA` folder.","permissions":["write-all","scope-applocaldata"]},"allow-applocaldata-write-recursive":{"identifier":"allow-applocaldata-write-recursive","description":"This allows full recursive write access to the complete `$APPLOCALDATA` folder, files and subdirectories.","permissions":["write-all","scope-applocaldata-recursive"]},"allow-applog-meta":{"identifier":"allow-applog-meta","description":"This allows non-recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.","permissions":["read-meta","scope-applog-index"]},"allow-applog-meta-recursive":{"identifier":"allow-applog-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.","permissions":["read-meta","scope-applog-recursive"]},"allow-applog-read":{"identifier":"allow-applog-read","description":"This allows non-recursive read access to the `$APPLOG` folder.","permissions":["read-all","scope-applog"]},"allow-applog-read-recursive":{"identifier":"allow-applog-read-recursive","description":"This allows full recursive read access to the complete `$APPLOG` folder, files and subdirectories.","permissions":["read-all","scope-applog-recursive"]},"allow-applog-write":{"identifier":"allow-applog-write","description":"This allows non-recursive write access to the `$APPLOG` folder.","permissions":["write-all","scope-applog"]},"allow-applog-write-recursive":{"identifier":"allow-applog-write-recursive","description":"This allows full recursive write access to the complete `$APPLOG` folder, files and subdirectories.","permissions":["write-all","scope-applog-recursive"]},"allow-audio-meta":{"identifier":"allow-audio-meta","description":"This allows non-recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.","permissions":["read-meta","scope-audio-index"]},"allow-audio-meta-recursive":{"identifier":"allow-audio-meta-recursive","description":"This allows full recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.","permissions":["read-meta","scope-audio-recursive"]},"allow-audio-read":{"identifier":"allow-audio-read","description":"This allows non-recursive read access to the `$AUDIO` folder.","permissions":["read-all","scope-audio"]},"allow-audio-read-recursive":{"identifier":"allow-audio-read-recursive","description":"This allows full recursive read access to the complete `$AUDIO` folder, files and subdirectories.","permissions":["read-all","scope-audio-recursive"]},"allow-audio-write":{"identifier":"allow-audio-write","description":"This allows non-recursive write access to the `$AUDIO` folder.","permissions":["write-all","scope-audio"]},"allow-audio-write-recursive":{"identifier":"allow-audio-write-recursive","description":"This allows full recursive write access to the complete `$AUDIO` folder, files and subdirectories.","permissions":["write-all","scope-audio-recursive"]},"allow-cache-meta":{"identifier":"allow-cache-meta","description":"This allows non-recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-cache-index"]},"allow-cache-meta-recursive":{"identifier":"allow-cache-meta-recursive","description":"This allows full recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-cache-recursive"]},"allow-cache-read":{"identifier":"allow-cache-read","description":"This allows non-recursive read access to the `$CACHE` folder.","permissions":["read-all","scope-cache"]},"allow-cache-read-recursive":{"identifier":"allow-cache-read-recursive","description":"This allows full recursive read access to the complete `$CACHE` folder, files and subdirectories.","permissions":["read-all","scope-cache-recursive"]},"allow-cache-write":{"identifier":"allow-cache-write","description":"This allows non-recursive write access to the `$CACHE` folder.","permissions":["write-all","scope-cache"]},"allow-cache-write-recursive":{"identifier":"allow-cache-write-recursive","description":"This allows full recursive write access to the complete `$CACHE` folder, files and subdirectories.","permissions":["write-all","scope-cache-recursive"]},"allow-config-meta":{"identifier":"allow-config-meta","description":"This allows non-recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-config-index"]},"allow-config-meta-recursive":{"identifier":"allow-config-meta-recursive","description":"This allows full recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-config-recursive"]},"allow-config-read":{"identifier":"allow-config-read","description":"This allows non-recursive read access to the `$CONFIG` folder.","permissions":["read-all","scope-config"]},"allow-config-read-recursive":{"identifier":"allow-config-read-recursive","description":"This allows full recursive read access to the complete `$CONFIG` folder, files and subdirectories.","permissions":["read-all","scope-config-recursive"]},"allow-config-write":{"identifier":"allow-config-write","description":"This allows non-recursive write access to the `$CONFIG` folder.","permissions":["write-all","scope-config"]},"allow-config-write-recursive":{"identifier":"allow-config-write-recursive","description":"This allows full recursive write access to the complete `$CONFIG` folder, files and subdirectories.","permissions":["write-all","scope-config-recursive"]},"allow-data-meta":{"identifier":"allow-data-meta","description":"This allows non-recursive read access to metadata of the `$DATA` folder, including file listing and statistics.","permissions":["read-meta","scope-data-index"]},"allow-data-meta-recursive":{"identifier":"allow-data-meta-recursive","description":"This allows full recursive read access to metadata of the `$DATA` folder, including file listing and statistics.","permissions":["read-meta","scope-data-recursive"]},"allow-data-read":{"identifier":"allow-data-read","description":"This allows non-recursive read access to the `$DATA` folder.","permissions":["read-all","scope-data"]},"allow-data-read-recursive":{"identifier":"allow-data-read-recursive","description":"This allows full recursive read access to the complete `$DATA` folder, files and subdirectories.","permissions":["read-all","scope-data-recursive"]},"allow-data-write":{"identifier":"allow-data-write","description":"This allows non-recursive write access to the `$DATA` folder.","permissions":["write-all","scope-data"]},"allow-data-write-recursive":{"identifier":"allow-data-write-recursive","description":"This allows full recursive write access to the complete `$DATA` folder, files and subdirectories.","permissions":["write-all","scope-data-recursive"]},"allow-desktop-meta":{"identifier":"allow-desktop-meta","description":"This allows non-recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.","permissions":["read-meta","scope-desktop-index"]},"allow-desktop-meta-recursive":{"identifier":"allow-desktop-meta-recursive","description":"This allows full recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.","permissions":["read-meta","scope-desktop-recursive"]},"allow-desktop-read":{"identifier":"allow-desktop-read","description":"This allows non-recursive read access to the `$DESKTOP` folder.","permissions":["read-all","scope-desktop"]},"allow-desktop-read-recursive":{"identifier":"allow-desktop-read-recursive","description":"This allows full recursive read access to the complete `$DESKTOP` folder, files and subdirectories.","permissions":["read-all","scope-desktop-recursive"]},"allow-desktop-write":{"identifier":"allow-desktop-write","description":"This allows non-recursive write access to the `$DESKTOP` folder.","permissions":["write-all","scope-desktop"]},"allow-desktop-write-recursive":{"identifier":"allow-desktop-write-recursive","description":"This allows full recursive write access to the complete `$DESKTOP` folder, files and subdirectories.","permissions":["write-all","scope-desktop-recursive"]},"allow-document-meta":{"identifier":"allow-document-meta","description":"This allows non-recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.","permissions":["read-meta","scope-document-index"]},"allow-document-meta-recursive":{"identifier":"allow-document-meta-recursive","description":"This allows full recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.","permissions":["read-meta","scope-document-recursive"]},"allow-document-read":{"identifier":"allow-document-read","description":"This allows non-recursive read access to the `$DOCUMENT` folder.","permissions":["read-all","scope-document"]},"allow-document-read-recursive":{"identifier":"allow-document-read-recursive","description":"This allows full recursive read access to the complete `$DOCUMENT` folder, files and subdirectories.","permissions":["read-all","scope-document-recursive"]},"allow-document-write":{"identifier":"allow-document-write","description":"This allows non-recursive write access to the `$DOCUMENT` folder.","permissions":["write-all","scope-document"]},"allow-document-write-recursive":{"identifier":"allow-document-write-recursive","description":"This allows full recursive write access to the complete `$DOCUMENT` folder, files and subdirectories.","permissions":["write-all","scope-document-recursive"]},"allow-download-meta":{"identifier":"allow-download-meta","description":"This allows non-recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.","permissions":["read-meta","scope-download-index"]},"allow-download-meta-recursive":{"identifier":"allow-download-meta-recursive","description":"This allows full recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.","permissions":["read-meta","scope-download-recursive"]},"allow-download-read":{"identifier":"allow-download-read","description":"This allows non-recursive read access to the `$DOWNLOAD` folder.","permissions":["read-all","scope-download"]},"allow-download-read-recursive":{"identifier":"allow-download-read-recursive","description":"This allows full recursive read access to the complete `$DOWNLOAD` folder, files and subdirectories.","permissions":["read-all","scope-download-recursive"]},"allow-download-write":{"identifier":"allow-download-write","description":"This allows non-recursive write access to the `$DOWNLOAD` folder.","permissions":["write-all","scope-download"]},"allow-download-write-recursive":{"identifier":"allow-download-write-recursive","description":"This allows full recursive write access to the complete `$DOWNLOAD` folder, files and subdirectories.","permissions":["write-all","scope-download-recursive"]},"allow-exe-meta":{"identifier":"allow-exe-meta","description":"This allows non-recursive read access to metadata of the `$EXE` folder, including file listing and statistics.","permissions":["read-meta","scope-exe-index"]},"allow-exe-meta-recursive":{"identifier":"allow-exe-meta-recursive","description":"This allows full recursive read access to metadata of the `$EXE` folder, including file listing and statistics.","permissions":["read-meta","scope-exe-recursive"]},"allow-exe-read":{"identifier":"allow-exe-read","description":"This allows non-recursive read access to the `$EXE` folder.","permissions":["read-all","scope-exe"]},"allow-exe-read-recursive":{"identifier":"allow-exe-read-recursive","description":"This allows full recursive read access to the complete `$EXE` folder, files and subdirectories.","permissions":["read-all","scope-exe-recursive"]},"allow-exe-write":{"identifier":"allow-exe-write","description":"This allows non-recursive write access to the `$EXE` folder.","permissions":["write-all","scope-exe"]},"allow-exe-write-recursive":{"identifier":"allow-exe-write-recursive","description":"This allows full recursive write access to the complete `$EXE` folder, files and subdirectories.","permissions":["write-all","scope-exe-recursive"]},"allow-font-meta":{"identifier":"allow-font-meta","description":"This allows non-recursive read access to metadata of the `$FONT` folder, including file listing and statistics.","permissions":["read-meta","scope-font-index"]},"allow-font-meta-recursive":{"identifier":"allow-font-meta-recursive","description":"This allows full recursive read access to metadata of the `$FONT` folder, including file listing and statistics.","permissions":["read-meta","scope-font-recursive"]},"allow-font-read":{"identifier":"allow-font-read","description":"This allows non-recursive read access to the `$FONT` folder.","permissions":["read-all","scope-font"]},"allow-font-read-recursive":{"identifier":"allow-font-read-recursive","description":"This allows full recursive read access to the complete `$FONT` folder, files and subdirectories.","permissions":["read-all","scope-font-recursive"]},"allow-font-write":{"identifier":"allow-font-write","description":"This allows non-recursive write access to the `$FONT` folder.","permissions":["write-all","scope-font"]},"allow-font-write-recursive":{"identifier":"allow-font-write-recursive","description":"This allows full recursive write access to the complete `$FONT` folder, files and subdirectories.","permissions":["write-all","scope-font-recursive"]},"allow-home-meta":{"identifier":"allow-home-meta","description":"This allows non-recursive read access to metadata of the `$HOME` folder, including file listing and statistics.","permissions":["read-meta","scope-home-index"]},"allow-home-meta-recursive":{"identifier":"allow-home-meta-recursive","description":"This allows full recursive read access to metadata of the `$HOME` folder, including file listing and statistics.","permissions":["read-meta","scope-home-recursive"]},"allow-home-read":{"identifier":"allow-home-read","description":"This allows non-recursive read access to the `$HOME` folder.","permissions":["read-all","scope-home"]},"allow-home-read-recursive":{"identifier":"allow-home-read-recursive","description":"This allows full recursive read access to the complete `$HOME` folder, files and subdirectories.","permissions":["read-all","scope-home-recursive"]},"allow-home-write":{"identifier":"allow-home-write","description":"This allows non-recursive write access to the `$HOME` folder.","permissions":["write-all","scope-home"]},"allow-home-write-recursive":{"identifier":"allow-home-write-recursive","description":"This allows full recursive write access to the complete `$HOME` folder, files and subdirectories.","permissions":["write-all","scope-home-recursive"]},"allow-localdata-meta":{"identifier":"allow-localdata-meta","description":"This allows non-recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-localdata-index"]},"allow-localdata-meta-recursive":{"identifier":"allow-localdata-meta-recursive","description":"This allows full recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-localdata-recursive"]},"allow-localdata-read":{"identifier":"allow-localdata-read","description":"This allows non-recursive read access to the `$LOCALDATA` folder.","permissions":["read-all","scope-localdata"]},"allow-localdata-read-recursive":{"identifier":"allow-localdata-read-recursive","description":"This allows full recursive read access to the complete `$LOCALDATA` folder, files and subdirectories.","permissions":["read-all","scope-localdata-recursive"]},"allow-localdata-write":{"identifier":"allow-localdata-write","description":"This allows non-recursive write access to the `$LOCALDATA` folder.","permissions":["write-all","scope-localdata"]},"allow-localdata-write-recursive":{"identifier":"allow-localdata-write-recursive","description":"This allows full recursive write access to the complete `$LOCALDATA` folder, files and subdirectories.","permissions":["write-all","scope-localdata-recursive"]},"allow-log-meta":{"identifier":"allow-log-meta","description":"This allows non-recursive read access to metadata of the `$LOG` folder, including file listing and statistics.","permissions":["read-meta","scope-log-index"]},"allow-log-meta-recursive":{"identifier":"allow-log-meta-recursive","description":"This allows full recursive read access to metadata of the `$LOG` folder, including file listing and statistics.","permissions":["read-meta","scope-log-recursive"]},"allow-log-read":{"identifier":"allow-log-read","description":"This allows non-recursive read access to the `$LOG` folder.","permissions":["read-all","scope-log"]},"allow-log-read-recursive":{"identifier":"allow-log-read-recursive","description":"This allows full recursive read access to the complete `$LOG` folder, files and subdirectories.","permissions":["read-all","scope-log-recursive"]},"allow-log-write":{"identifier":"allow-log-write","description":"This allows non-recursive write access to the `$LOG` folder.","permissions":["write-all","scope-log"]},"allow-log-write-recursive":{"identifier":"allow-log-write-recursive","description":"This allows full recursive write access to the complete `$LOG` folder, files and subdirectories.","permissions":["write-all","scope-log-recursive"]},"allow-picture-meta":{"identifier":"allow-picture-meta","description":"This allows non-recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.","permissions":["read-meta","scope-picture-index"]},"allow-picture-meta-recursive":{"identifier":"allow-picture-meta-recursive","description":"This allows full recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.","permissions":["read-meta","scope-picture-recursive"]},"allow-picture-read":{"identifier":"allow-picture-read","description":"This allows non-recursive read access to the `$PICTURE` folder.","permissions":["read-all","scope-picture"]},"allow-picture-read-recursive":{"identifier":"allow-picture-read-recursive","description":"This allows full recursive read access to the complete `$PICTURE` folder, files and subdirectories.","permissions":["read-all","scope-picture-recursive"]},"allow-picture-write":{"identifier":"allow-picture-write","description":"This allows non-recursive write access to the `$PICTURE` folder.","permissions":["write-all","scope-picture"]},"allow-picture-write-recursive":{"identifier":"allow-picture-write-recursive","description":"This allows full recursive write access to the complete `$PICTURE` folder, files and subdirectories.","permissions":["write-all","scope-picture-recursive"]},"allow-public-meta":{"identifier":"allow-public-meta","description":"This allows non-recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.","permissions":["read-meta","scope-public-index"]},"allow-public-meta-recursive":{"identifier":"allow-public-meta-recursive","description":"This allows full recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.","permissions":["read-meta","scope-public-recursive"]},"allow-public-read":{"identifier":"allow-public-read","description":"This allows non-recursive read access to the `$PUBLIC` folder.","permissions":["read-all","scope-public"]},"allow-public-read-recursive":{"identifier":"allow-public-read-recursive","description":"This allows full recursive read access to the complete `$PUBLIC` folder, files and subdirectories.","permissions":["read-all","scope-public-recursive"]},"allow-public-write":{"identifier":"allow-public-write","description":"This allows non-recursive write access to the `$PUBLIC` folder.","permissions":["write-all","scope-public"]},"allow-public-write-recursive":{"identifier":"allow-public-write-recursive","description":"This allows full recursive write access to the complete `$PUBLIC` folder, files and subdirectories.","permissions":["write-all","scope-public-recursive"]},"allow-resource-meta":{"identifier":"allow-resource-meta","description":"This allows non-recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.","permissions":["read-meta","scope-resource-index"]},"allow-resource-meta-recursive":{"identifier":"allow-resource-meta-recursive","description":"This allows full recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.","permissions":["read-meta","scope-resource-recursive"]},"allow-resource-read":{"identifier":"allow-resource-read","description":"This allows non-recursive read access to the `$RESOURCE` folder.","permissions":["read-all","scope-resource"]},"allow-resource-read-recursive":{"identifier":"allow-resource-read-recursive","description":"This allows full recursive read access to the complete `$RESOURCE` folder, files and subdirectories.","permissions":["read-all","scope-resource-recursive"]},"allow-resource-write":{"identifier":"allow-resource-write","description":"This allows non-recursive write access to the `$RESOURCE` folder.","permissions":["write-all","scope-resource"]},"allow-resource-write-recursive":{"identifier":"allow-resource-write-recursive","description":"This allows full recursive write access to the complete `$RESOURCE` folder, files and subdirectories.","permissions":["write-all","scope-resource-recursive"]},"allow-runtime-meta":{"identifier":"allow-runtime-meta","description":"This allows non-recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.","permissions":["read-meta","scope-runtime-index"]},"allow-runtime-meta-recursive":{"identifier":"allow-runtime-meta-recursive","description":"This allows full recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.","permissions":["read-meta","scope-runtime-recursive"]},"allow-runtime-read":{"identifier":"allow-runtime-read","description":"This allows non-recursive read access to the `$RUNTIME` folder.","permissions":["read-all","scope-runtime"]},"allow-runtime-read-recursive":{"identifier":"allow-runtime-read-recursive","description":"This allows full recursive read access to the complete `$RUNTIME` folder, files and subdirectories.","permissions":["read-all","scope-runtime-recursive"]},"allow-runtime-write":{"identifier":"allow-runtime-write","description":"This allows non-recursive write access to the `$RUNTIME` folder.","permissions":["write-all","scope-runtime"]},"allow-runtime-write-recursive":{"identifier":"allow-runtime-write-recursive","description":"This allows full recursive write access to the complete `$RUNTIME` folder, files and subdirectories.","permissions":["write-all","scope-runtime-recursive"]},"allow-temp-meta":{"identifier":"allow-temp-meta","description":"This allows non-recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.","permissions":["read-meta","scope-temp-index"]},"allow-temp-meta-recursive":{"identifier":"allow-temp-meta-recursive","description":"This allows full recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.","permissions":["read-meta","scope-temp-recursive"]},"allow-temp-read":{"identifier":"allow-temp-read","description":"This allows non-recursive read access to the `$TEMP` folder.","permissions":["read-all","scope-temp"]},"allow-temp-read-recursive":{"identifier":"allow-temp-read-recursive","description":"This allows full recursive read access to the complete `$TEMP` folder, files and subdirectories.","permissions":["read-all","scope-temp-recursive"]},"allow-temp-write":{"identifier":"allow-temp-write","description":"This allows non-recursive write access to the `$TEMP` folder.","permissions":["write-all","scope-temp"]},"allow-temp-write-recursive":{"identifier":"allow-temp-write-recursive","description":"This allows full recursive write access to the complete `$TEMP` folder, files and subdirectories.","permissions":["write-all","scope-temp-recursive"]},"allow-template-meta":{"identifier":"allow-template-meta","description":"This allows non-recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.","permissions":["read-meta","scope-template-index"]},"allow-template-meta-recursive":{"identifier":"allow-template-meta-recursive","description":"This allows full recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.","permissions":["read-meta","scope-template-recursive"]},"allow-template-read":{"identifier":"allow-template-read","description":"This allows non-recursive read access to the `$TEMPLATE` folder.","permissions":["read-all","scope-template"]},"allow-template-read-recursive":{"identifier":"allow-template-read-recursive","description":"This allows full recursive read access to the complete `$TEMPLATE` folder, files and subdirectories.","permissions":["read-all","scope-template-recursive"]},"allow-template-write":{"identifier":"allow-template-write","description":"This allows non-recursive write access to the `$TEMPLATE` folder.","permissions":["write-all","scope-template"]},"allow-template-write-recursive":{"identifier":"allow-template-write-recursive","description":"This allows full recursive write access to the complete `$TEMPLATE` folder, files and subdirectories.","permissions":["write-all","scope-template-recursive"]},"allow-video-meta":{"identifier":"allow-video-meta","description":"This allows non-recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.","permissions":["read-meta","scope-video-index"]},"allow-video-meta-recursive":{"identifier":"allow-video-meta-recursive","description":"This allows full recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.","permissions":["read-meta","scope-video-recursive"]},"allow-video-read":{"identifier":"allow-video-read","description":"This allows non-recursive read access to the `$VIDEO` folder.","permissions":["read-all","scope-video"]},"allow-video-read-recursive":{"identifier":"allow-video-read-recursive","description":"This allows full recursive read access to the complete `$VIDEO` folder, files and subdirectories.","permissions":["read-all","scope-video-recursive"]},"allow-video-write":{"identifier":"allow-video-write","description":"This allows non-recursive write access to the `$VIDEO` folder.","permissions":["write-all","scope-video"]},"allow-video-write-recursive":{"identifier":"allow-video-write-recursive","description":"This allows full recursive write access to the complete `$VIDEO` folder, files and subdirectories.","permissions":["write-all","scope-video-recursive"]},"deny-default":{"identifier":"deny-default","description":"This denies access to dangerous Tauri relevant files and folders by default.","permissions":["deny-webview-data-linux","deny-webview-data-windows"]}},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","title":"FsScopeEntry","description":"FS scope entry.","anyOf":[{"description":"A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},{"type":"object","required":["path"],"properties":{"path":{"description":"A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"}}}]}},"global-shortcut":{"default_permission":{"identifier":"default","description":"No features are enabled by default, as we believe\nthe shortcuts can be inherently dangerous and it is\napplication specific if specific shortcuts should be\nregistered or unregistered.\n","permissions":[]},"permissions":{"allow-is-registered":{"identifier":"allow-is-registered","description":"Enables the is_registered command without any pre-configured scope.","commands":{"allow":["is_registered"],"deny":[]}},"allow-register":{"identifier":"allow-register","description":"Enables the register command without any pre-configured scope.","commands":{"allow":["register"],"deny":[]}},"allow-register-all":{"identifier":"allow-register-all","description":"Enables the register_all command without any pre-configured scope.","commands":{"allow":["register_all"],"deny":[]}},"allow-unregister":{"identifier":"allow-unregister","description":"Enables the unregister command without any pre-configured scope.","commands":{"allow":["unregister"],"deny":[]}},"allow-unregister-all":{"identifier":"allow-unregister-all","description":"Enables the unregister_all command without any pre-configured scope.","commands":{"allow":["unregister_all"],"deny":[]}},"deny-is-registered":{"identifier":"deny-is-registered","description":"Denies the is_registered command without any pre-configured scope.","commands":{"allow":[],"deny":["is_registered"]}},"deny-register":{"identifier":"deny-register","description":"Denies the register command without any pre-configured scope.","commands":{"allow":[],"deny":["register"]}},"deny-register-all":{"identifier":"deny-register-all","description":"Denies the register_all command without any pre-configured scope.","commands":{"allow":[],"deny":["register_all"]}},"deny-unregister":{"identifier":"deny-unregister","description":"Denies the unregister command without any pre-configured scope.","commands":{"allow":[],"deny":["unregister"]}},"deny-unregister-all":{"identifier":"deny-unregister-all","description":"Denies the unregister_all command without any pre-configured scope.","commands":{"allow":[],"deny":["unregister_all"]}}},"permission_sets":{},"global_scope_schema":null},"notification":{"default_permission":{"identifier":"default","description":"This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n","permissions":["allow-is-permission-granted","allow-request-permission","allow-notify","allow-register-action-types","allow-register-listener","allow-cancel","allow-get-pending","allow-remove-active","allow-get-active","allow-check-permissions","allow-show","allow-batch","allow-list-channels","allow-delete-channel","allow-create-channel","allow-permission-state"]},"permissions":{"allow-batch":{"identifier":"allow-batch","description":"Enables the batch command without any pre-configured scope.","commands":{"allow":["batch"],"deny":[]}},"allow-cancel":{"identifier":"allow-cancel","description":"Enables the cancel command without any pre-configured scope.","commands":{"allow":["cancel"],"deny":[]}},"allow-check-permissions":{"identifier":"allow-check-permissions","description":"Enables the check_permissions command without any pre-configured scope.","commands":{"allow":["check_permissions"],"deny":[]}},"allow-create-channel":{"identifier":"allow-create-channel","description":"Enables the create_channel command without any pre-configured scope.","commands":{"allow":["create_channel"],"deny":[]}},"allow-delete-channel":{"identifier":"allow-delete-channel","description":"Enables the delete_channel command without any pre-configured scope.","commands":{"allow":["delete_channel"],"deny":[]}},"allow-get-active":{"identifier":"allow-get-active","description":"Enables the get_active command without any pre-configured scope.","commands":{"allow":["get_active"],"deny":[]}},"allow-get-pending":{"identifier":"allow-get-pending","description":"Enables the get_pending command without any pre-configured scope.","commands":{"allow":["get_pending"],"deny":[]}},"allow-is-permission-granted":{"identifier":"allow-is-permission-granted","description":"Enables the is_permission_granted command without any pre-configured scope.","commands":{"allow":["is_permission_granted"],"deny":[]}},"allow-list-channels":{"identifier":"allow-list-channels","description":"Enables the list_channels command without any pre-configured scope.","commands":{"allow":["list_channels"],"deny":[]}},"allow-notify":{"identifier":"allow-notify","description":"Enables the notify command without any pre-configured scope.","commands":{"allow":["notify"],"deny":[]}},"allow-permission-state":{"identifier":"allow-permission-state","description":"Enables the permission_state command without any pre-configured scope.","commands":{"allow":["permission_state"],"deny":[]}},"allow-register-action-types":{"identifier":"allow-register-action-types","description":"Enables the register_action_types command without any pre-configured scope.","commands":{"allow":["register_action_types"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-active":{"identifier":"allow-remove-active","description":"Enables the remove_active command without any pre-configured scope.","commands":{"allow":["remove_active"],"deny":[]}},"allow-request-permission":{"identifier":"allow-request-permission","description":"Enables the request_permission command without any pre-configured scope.","commands":{"allow":["request_permission"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"deny-batch":{"identifier":"deny-batch","description":"Denies the batch command without any pre-configured scope.","commands":{"allow":[],"deny":["batch"]}},"deny-cancel":{"identifier":"deny-cancel","description":"Denies the cancel command without any pre-configured scope.","commands":{"allow":[],"deny":["cancel"]}},"deny-check-permissions":{"identifier":"deny-check-permissions","description":"Denies the check_permissions command without any pre-configured scope.","commands":{"allow":[],"deny":["check_permissions"]}},"deny-create-channel":{"identifier":"deny-create-channel","description":"Denies the create_channel command without any pre-configured scope.","commands":{"allow":[],"deny":["create_channel"]}},"deny-delete-channel":{"identifier":"deny-delete-channel","description":"Denies the delete_channel command without any pre-configured scope.","commands":{"allow":[],"deny":["delete_channel"]}},"deny-get-active":{"identifier":"deny-get-active","description":"Denies the get_active command without any pre-configured scope.","commands":{"allow":[],"deny":["get_active"]}},"deny-get-pending":{"identifier":"deny-get-pending","description":"Denies the get_pending command without any pre-configured scope.","commands":{"allow":[],"deny":["get_pending"]}},"deny-is-permission-granted":{"identifier":"deny-is-permission-granted","description":"Denies the is_permission_granted command without any pre-configured scope.","commands":{"allow":[],"deny":["is_permission_granted"]}},"deny-list-channels":{"identifier":"deny-list-channels","description":"Denies the list_channels command without any pre-configured scope.","commands":{"allow":[],"deny":["list_channels"]}},"deny-notify":{"identifier":"deny-notify","description":"Denies the notify command without any pre-configured scope.","commands":{"allow":[],"deny":["notify"]}},"deny-permission-state":{"identifier":"deny-permission-state","description":"Denies the permission_state command without any pre-configured scope.","commands":{"allow":[],"deny":["permission_state"]}},"deny-register-action-types":{"identifier":"deny-register-action-types","description":"Denies the register_action_types command without any pre-configured scope.","commands":{"allow":[],"deny":["register_action_types"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-active":{"identifier":"deny-remove-active","description":"Denies the remove_active command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_active"]}},"deny-request-permission":{"identifier":"deny-request-permission","description":"Denies the request_permission command without any pre-configured scope.","commands":{"allow":[],"deny":["request_permission"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}}},"permission_sets":{},"global_scope_schema":null},"os":{"default_permission":{"identifier":"default","description":"This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n","permissions":["allow-arch","allow-exe-extension","allow-family","allow-locale","allow-os-type","allow-platform","allow-version"]},"permissions":{"allow-arch":{"identifier":"allow-arch","description":"Enables the arch command without any pre-configured scope.","commands":{"allow":["arch"],"deny":[]}},"allow-exe-extension":{"identifier":"allow-exe-extension","description":"Enables the exe_extension command without any pre-configured scope.","commands":{"allow":["exe_extension"],"deny":[]}},"allow-family":{"identifier":"allow-family","description":"Enables the family command without any pre-configured scope.","commands":{"allow":["family"],"deny":[]}},"allow-hostname":{"identifier":"allow-hostname","description":"Enables the hostname command without any pre-configured scope.","commands":{"allow":["hostname"],"deny":[]}},"allow-locale":{"identifier":"allow-locale","description":"Enables the locale command without any pre-configured scope.","commands":{"allow":["locale"],"deny":[]}},"allow-os-type":{"identifier":"allow-os-type","description":"Enables the os_type command without any pre-configured scope.","commands":{"allow":["os_type"],"deny":[]}},"allow-platform":{"identifier":"allow-platform","description":"Enables the platform command without any pre-configured scope.","commands":{"allow":["platform"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-arch":{"identifier":"deny-arch","description":"Denies the arch command without any pre-configured scope.","commands":{"allow":[],"deny":["arch"]}},"deny-exe-extension":{"identifier":"deny-exe-extension","description":"Denies the exe_extension command without any pre-configured scope.","commands":{"allow":[],"deny":["exe_extension"]}},"deny-family":{"identifier":"deny-family","description":"Denies the family command without any pre-configured scope.","commands":{"allow":[],"deny":["family"]}},"deny-hostname":{"identifier":"deny-hostname","description":"Denies the hostname command without any pre-configured scope.","commands":{"allow":[],"deny":["hostname"]}},"deny-locale":{"identifier":"deny-locale","description":"Denies the locale command without any pre-configured scope.","commands":{"allow":[],"deny":["locale"]}},"deny-os-type":{"identifier":"deny-os-type","description":"Denies the os_type command without any pre-configured scope.","commands":{"allow":[],"deny":["os_type"]}},"deny-platform":{"identifier":"deny-platform","description":"Denies the platform command without any pre-configured scope.","commands":{"allow":[],"deny":["platform"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"process":{"default_permission":{"identifier":"default","description":"This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n","permissions":["allow-exit","allow-restart"]},"permissions":{"allow-exit":{"identifier":"allow-exit","description":"Enables the exit command without any pre-configured scope.","commands":{"allow":["exit"],"deny":[]}},"allow-restart":{"identifier":"allow-restart","description":"Enables the restart command without any pre-configured scope.","commands":{"allow":["restart"],"deny":[]}},"deny-exit":{"identifier":"deny-exit","description":"Denies the exit command without any pre-configured scope.","commands":{"allow":[],"deny":["exit"]}},"deny-restart":{"identifier":"deny-restart","description":"Denies the restart command without any pre-configured scope.","commands":{"allow":[],"deny":["restart"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ShellScopeEntry","description":"Shell scope entry.","anyOf":[{"type":"object","required":["cmd","name"],"properties":{"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"args":{"description":"The allowed arguments for the command execution.","allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}]}},"additionalProperties":false},{"type":"object","required":["name","sidecar"],"properties":{"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"args":{"description":"The allowed arguments for the command execution.","allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}]},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"additionalProperties":false}],"definitions":{"ShellScopeEntryAllowedArgs":{"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.","anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","type":"array","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"}}]},"ShellScopeEntryAllowedArg":{"description":"A command argument allowed to be executed by the webview API.","anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"description":"A variable that is set while calling the command from the webview API.","type":"object","required":["validator"],"properties":{"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"},"raw":{"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","default":false,"type":"boolean"}},"additionalProperties":false}]}}}},"updater":{"default_permission":{"identifier":"default","description":"This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n","permissions":["allow-check","allow-download","allow-install","allow-download-and-install"]},"permissions":{"allow-check":{"identifier":"allow-check","description":"Enables the check command without any pre-configured scope.","commands":{"allow":["check"],"deny":[]}},"allow-download":{"identifier":"allow-download","description":"Enables the download command without any pre-configured scope.","commands":{"allow":["download"],"deny":[]}},"allow-download-and-install":{"identifier":"allow-download-and-install","description":"Enables the download_and_install command without any pre-configured scope.","commands":{"allow":["download_and_install"],"deny":[]}},"allow-install":{"identifier":"allow-install","description":"Enables the install command without any pre-configured scope.","commands":{"allow":["install"],"deny":[]}},"deny-check":{"identifier":"deny-check","description":"Denies the check command without any pre-configured scope.","commands":{"allow":[],"deny":["check"]}},"deny-download":{"identifier":"deny-download","description":"Denies the download command without any pre-configured scope.","commands":{"allow":[],"deny":["download"]}},"deny-download-and-install":{"identifier":"deny-download-and-install","description":"Denies the download_and_install command without any pre-configured scope.","commands":{"allow":[],"deny":["download_and_install"]}},"deny-install":{"identifier":"deny-install","description":"Denies the install command without any pre-configured scope.","commands":{"allow":[],"deny":["install"]}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file +{"clipboard-manager":{"default_permission":{"identifier":"default","description":"No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n","permissions":[]},"permissions":{"allow-clear":{"identifier":"allow-clear","description":"Enables the clear command without any pre-configured scope.","commands":{"allow":["clear"],"deny":[]}},"allow-read-image":{"identifier":"allow-read-image","description":"Enables the read_image command without any pre-configured scope.","commands":{"allow":["read_image"],"deny":[]}},"allow-read-text":{"identifier":"allow-read-text","description":"Enables the read_text command without any pre-configured scope.","commands":{"allow":["read_text"],"deny":[]}},"allow-write-html":{"identifier":"allow-write-html","description":"Enables the write_html command without any pre-configured scope.","commands":{"allow":["write_html"],"deny":[]}},"allow-write-image":{"identifier":"allow-write-image","description":"Enables the write_image command without any pre-configured scope.","commands":{"allow":["write_image"],"deny":[]}},"allow-write-text":{"identifier":"allow-write-text","description":"Enables the write_text command without any pre-configured scope.","commands":{"allow":["write_text"],"deny":[]}},"deny-clear":{"identifier":"deny-clear","description":"Denies the clear command without any pre-configured scope.","commands":{"allow":[],"deny":["clear"]}},"deny-read-image":{"identifier":"deny-read-image","description":"Denies the read_image command without any pre-configured scope.","commands":{"allow":[],"deny":["read_image"]}},"deny-read-text":{"identifier":"deny-read-text","description":"Denies the read_text command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text"]}},"deny-write-html":{"identifier":"deny-write-html","description":"Denies the write_html command without any pre-configured scope.","commands":{"allow":[],"deny":["write_html"]}},"deny-write-image":{"identifier":"deny-write-image","description":"Denies the write_image command without any pre-configured scope.","commands":{"allow":[],"deny":["write_image"]}},"deny-write-text":{"identifier":"deny-write-text","description":"Denies the write_text command without any pre-configured scope.","commands":{"allow":[],"deny":["write_text"]}}},"permission_sets":{},"global_scope_schema":null},"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"dialog":{"default_permission":{"identifier":"default","description":"This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n","permissions":["allow-ask","allow-confirm","allow-message","allow-save","allow-open"]},"permissions":{"allow-ask":{"identifier":"allow-ask","description":"Enables the ask command without any pre-configured scope.","commands":{"allow":["ask"],"deny":[]}},"allow-confirm":{"identifier":"allow-confirm","description":"Enables the confirm command without any pre-configured scope.","commands":{"allow":["confirm"],"deny":[]}},"allow-message":{"identifier":"allow-message","description":"Enables the message command without any pre-configured scope.","commands":{"allow":["message"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-save":{"identifier":"allow-save","description":"Enables the save command without any pre-configured scope.","commands":{"allow":["save"],"deny":[]}},"deny-ask":{"identifier":"deny-ask","description":"Denies the ask command without any pre-configured scope.","commands":{"allow":[],"deny":["ask"]}},"deny-confirm":{"identifier":"deny-confirm","description":"Denies the confirm command without any pre-configured scope.","commands":{"allow":[],"deny":["confirm"]}},"deny-message":{"identifier":"deny-message","description":"Denies the message command without any pre-configured scope.","commands":{"allow":[],"deny":["message"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-save":{"identifier":"deny-save","description":"Denies the save command without any pre-configured scope.","commands":{"allow":[],"deny":["save"]}}},"permission_sets":{},"global_scope_schema":null},"fs":{"default_permission":{"identifier":"default","description":"This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n","permissions":["create-app-specific-dirs","read-app-specific-dirs-recursive","deny-default"]},"permissions":{"allow-copy-file":{"identifier":"allow-copy-file","description":"Enables the copy_file command without any pre-configured scope.","commands":{"allow":["copy_file"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-exists":{"identifier":"allow-exists","description":"Enables the exists command without any pre-configured scope.","commands":{"allow":["exists"],"deny":[]}},"allow-fstat":{"identifier":"allow-fstat","description":"Enables the fstat command without any pre-configured scope.","commands":{"allow":["fstat"],"deny":[]}},"allow-ftruncate":{"identifier":"allow-ftruncate","description":"Enables the ftruncate command without any pre-configured scope.","commands":{"allow":["ftruncate"],"deny":[]}},"allow-lstat":{"identifier":"allow-lstat","description":"Enables the lstat command without any pre-configured scope.","commands":{"allow":["lstat"],"deny":[]}},"allow-mkdir":{"identifier":"allow-mkdir","description":"Enables the mkdir command without any pre-configured scope.","commands":{"allow":["mkdir"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-read":{"identifier":"allow-read","description":"Enables the read command without any pre-configured scope.","commands":{"allow":["read"],"deny":[]}},"allow-read-dir":{"identifier":"allow-read-dir","description":"Enables the read_dir command without any pre-configured scope.","commands":{"allow":["read_dir"],"deny":[]}},"allow-read-file":{"identifier":"allow-read-file","description":"Enables the read_file command without any pre-configured scope.","commands":{"allow":["read_file"],"deny":[]}},"allow-read-text-file":{"identifier":"allow-read-text-file","description":"Enables the read_text_file command without any pre-configured scope.","commands":{"allow":["read_text_file"],"deny":[]}},"allow-read-text-file-lines":{"identifier":"allow-read-text-file-lines","description":"Enables the read_text_file_lines command without any pre-configured scope.","commands":{"allow":["read_text_file_lines","read_text_file_lines_next"],"deny":[]}},"allow-read-text-file-lines-next":{"identifier":"allow-read-text-file-lines-next","description":"Enables the read_text_file_lines_next command without any pre-configured scope.","commands":{"allow":["read_text_file_lines_next"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-rename":{"identifier":"allow-rename","description":"Enables the rename command without any pre-configured scope.","commands":{"allow":["rename"],"deny":[]}},"allow-seek":{"identifier":"allow-seek","description":"Enables the seek command without any pre-configured scope.","commands":{"allow":["seek"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"allow-stat":{"identifier":"allow-stat","description":"Enables the stat command without any pre-configured scope.","commands":{"allow":["stat"],"deny":[]}},"allow-truncate":{"identifier":"allow-truncate","description":"Enables the truncate command without any pre-configured scope.","commands":{"allow":["truncate"],"deny":[]}},"allow-unwatch":{"identifier":"allow-unwatch","description":"Enables the unwatch command without any pre-configured scope.","commands":{"allow":["unwatch"],"deny":[]}},"allow-watch":{"identifier":"allow-watch","description":"Enables the watch command without any pre-configured scope.","commands":{"allow":["watch"],"deny":[]}},"allow-write":{"identifier":"allow-write","description":"Enables the write command without any pre-configured scope.","commands":{"allow":["write"],"deny":[]}},"allow-write-file":{"identifier":"allow-write-file","description":"Enables the write_file command without any pre-configured scope.","commands":{"allow":["write_file","open","write"],"deny":[]}},"allow-write-text-file":{"identifier":"allow-write-text-file","description":"Enables the write_text_file command without any pre-configured scope.","commands":{"allow":["write_text_file"],"deny":[]}},"create-app-specific-dirs":{"identifier":"create-app-specific-dirs","description":"This permissions allows to create the application specific directories.\n","commands":{"allow":["mkdir","scope-app-index"],"deny":[]}},"deny-copy-file":{"identifier":"deny-copy-file","description":"Denies the copy_file command without any pre-configured scope.","commands":{"allow":[],"deny":["copy_file"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-exists":{"identifier":"deny-exists","description":"Denies the exists command without any pre-configured scope.","commands":{"allow":[],"deny":["exists"]}},"deny-fstat":{"identifier":"deny-fstat","description":"Denies the fstat command without any pre-configured scope.","commands":{"allow":[],"deny":["fstat"]}},"deny-ftruncate":{"identifier":"deny-ftruncate","description":"Denies the ftruncate command without any pre-configured scope.","commands":{"allow":[],"deny":["ftruncate"]}},"deny-lstat":{"identifier":"deny-lstat","description":"Denies the lstat command without any pre-configured scope.","commands":{"allow":[],"deny":["lstat"]}},"deny-mkdir":{"identifier":"deny-mkdir","description":"Denies the mkdir command without any pre-configured scope.","commands":{"allow":[],"deny":["mkdir"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-read":{"identifier":"deny-read","description":"Denies the read command without any pre-configured scope.","commands":{"allow":[],"deny":["read"]}},"deny-read-dir":{"identifier":"deny-read-dir","description":"Denies the read_dir command without any pre-configured scope.","commands":{"allow":[],"deny":["read_dir"]}},"deny-read-file":{"identifier":"deny-read-file","description":"Denies the read_file command without any pre-configured scope.","commands":{"allow":[],"deny":["read_file"]}},"deny-read-text-file":{"identifier":"deny-read-text-file","description":"Denies the read_text_file command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file"]}},"deny-read-text-file-lines":{"identifier":"deny-read-text-file-lines","description":"Denies the read_text_file_lines command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file_lines"]}},"deny-read-text-file-lines-next":{"identifier":"deny-read-text-file-lines-next","description":"Denies the read_text_file_lines_next command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file_lines_next"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-rename":{"identifier":"deny-rename","description":"Denies the rename command without any pre-configured scope.","commands":{"allow":[],"deny":["rename"]}},"deny-seek":{"identifier":"deny-seek","description":"Denies the seek command without any pre-configured scope.","commands":{"allow":[],"deny":["seek"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}},"deny-stat":{"identifier":"deny-stat","description":"Denies the stat command without any pre-configured scope.","commands":{"allow":[],"deny":["stat"]}},"deny-truncate":{"identifier":"deny-truncate","description":"Denies the truncate command without any pre-configured scope.","commands":{"allow":[],"deny":["truncate"]}},"deny-unwatch":{"identifier":"deny-unwatch","description":"Denies the unwatch command without any pre-configured scope.","commands":{"allow":[],"deny":["unwatch"]}},"deny-watch":{"identifier":"deny-watch","description":"Denies the watch command without any pre-configured scope.","commands":{"allow":[],"deny":["watch"]}},"deny-webview-data-linux":{"identifier":"deny-webview-data-linux","description":"This denies read access to the\n`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.","commands":{"allow":[],"deny":[]}},"deny-webview-data-windows":{"identifier":"deny-webview-data-windows","description":"This denies read access to the\n`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.","commands":{"allow":[],"deny":[]}},"deny-write":{"identifier":"deny-write","description":"Denies the write command without any pre-configured scope.","commands":{"allow":[],"deny":["write"]}},"deny-write-file":{"identifier":"deny-write-file","description":"Denies the write_file command without any pre-configured scope.","commands":{"allow":[],"deny":["write_file"]}},"deny-write-text-file":{"identifier":"deny-write-text-file","description":"Denies the write_text_file command without any pre-configured scope.","commands":{"allow":[],"deny":["write_text_file"]}},"read-all":{"identifier":"read-all","description":"This enables all read related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","read_file","read","open","read_text_file","read_text_file_lines","read_text_file_lines_next","seek","stat","lstat","fstat","exists","watch","unwatch"],"deny":[]}},"read-app-specific-dirs-recursive":{"identifier":"read-app-specific-dirs-recursive","description":"This permission allows recursive read functionality on the application\nspecific base directories. \n","commands":{"allow":["read_dir","read_file","read_text_file","read_text_file_lines","read_text_file_lines_next","exists","scope-app-recursive"],"deny":[]}},"read-dirs":{"identifier":"read-dirs","description":"This enables directory read and file metadata related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","stat","lstat","fstat","exists"],"deny":[]}},"read-files":{"identifier":"read-files","description":"This enables file read related commands without any pre-configured accessible paths.","commands":{"allow":["read_file","read","open","read_text_file","read_text_file_lines","read_text_file_lines_next","seek","stat","lstat","fstat","exists"],"deny":[]}},"read-meta":{"identifier":"read-meta","description":"This enables all index or metadata related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","stat","lstat","fstat","exists","size"],"deny":[]}},"scope":{"identifier":"scope","description":"An empty permission you can use to modify the global scope.","commands":{"allow":[],"deny":[]}},"scope-app":{"identifier":"scope-app","description":"This scope permits access to all files and list content of top level directories in the application folders.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/*"},{"path":"$APPDATA"},{"path":"$APPDATA/*"},{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/*"},{"path":"$APPCACHE"},{"path":"$APPCACHE/*"},{"path":"$APPLOG"},{"path":"$APPLOG/*"}]}},"scope-app-index":{"identifier":"scope-app-index","description":"This scope permits to list all files and folders in the application directories.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPDATA"},{"path":"$APPLOCALDATA"},{"path":"$APPCACHE"},{"path":"$APPLOG"}]}},"scope-app-recursive":{"identifier":"scope-app-recursive","description":"This scope permits recursive access to the complete application folders, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/**"},{"path":"$APPDATA"},{"path":"$APPDATA/**"},{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/**"},{"path":"$APPCACHE"},{"path":"$APPCACHE/**"},{"path":"$APPLOG"},{"path":"$APPLOG/**"}]}},"scope-appcache":{"identifier":"scope-appcache","description":"This scope permits access to all files and list content of top level directories in the `$APPCACHE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"},{"path":"$APPCACHE/*"}]}},"scope-appcache-index":{"identifier":"scope-appcache-index","description":"This scope permits to list all files and folders in the `$APPCACHE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"}]}},"scope-appcache-recursive":{"identifier":"scope-appcache-recursive","description":"This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"},{"path":"$APPCACHE/**"}]}},"scope-appconfig":{"identifier":"scope-appconfig","description":"This scope permits access to all files and list content of top level directories in the `$APPCONFIG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/*"}]}},"scope-appconfig-index":{"identifier":"scope-appconfig-index","description":"This scope permits to list all files and folders in the `$APPCONFIG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"}]}},"scope-appconfig-recursive":{"identifier":"scope-appconfig-recursive","description":"This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/**"}]}},"scope-appdata":{"identifier":"scope-appdata","description":"This scope permits access to all files and list content of top level directories in the `$APPDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"},{"path":"$APPDATA/*"}]}},"scope-appdata-index":{"identifier":"scope-appdata-index","description":"This scope permits to list all files and folders in the `$APPDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"}]}},"scope-appdata-recursive":{"identifier":"scope-appdata-recursive","description":"This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]}},"scope-applocaldata":{"identifier":"scope-applocaldata","description":"This scope permits access to all files and list content of top level directories in the `$APPLOCALDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/*"}]}},"scope-applocaldata-index":{"identifier":"scope-applocaldata-index","description":"This scope permits to list all files and folders in the `$APPLOCALDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"}]}},"scope-applocaldata-recursive":{"identifier":"scope-applocaldata-recursive","description":"This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/**"}]}},"scope-applog":{"identifier":"scope-applog","description":"This scope permits access to all files and list content of top level directories in the `$APPLOG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"},{"path":"$APPLOG/*"}]}},"scope-applog-index":{"identifier":"scope-applog-index","description":"This scope permits to list all files and folders in the `$APPLOG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"}]}},"scope-applog-recursive":{"identifier":"scope-applog-recursive","description":"This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"},{"path":"$APPLOG/**"}]}},"scope-audio":{"identifier":"scope-audio","description":"This scope permits access to all files and list content of top level directories in the `$AUDIO` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"},{"path":"$AUDIO/*"}]}},"scope-audio-index":{"identifier":"scope-audio-index","description":"This scope permits to list all files and folders in the `$AUDIO`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"}]}},"scope-audio-recursive":{"identifier":"scope-audio-recursive","description":"This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"},{"path":"$AUDIO/**"}]}},"scope-cache":{"identifier":"scope-cache","description":"This scope permits access to all files and list content of top level directories in the `$CACHE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"},{"path":"$CACHE/*"}]}},"scope-cache-index":{"identifier":"scope-cache-index","description":"This scope permits to list all files and folders in the `$CACHE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"}]}},"scope-cache-recursive":{"identifier":"scope-cache-recursive","description":"This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"},{"path":"$CACHE/**"}]}},"scope-config":{"identifier":"scope-config","description":"This scope permits access to all files and list content of top level directories in the `$CONFIG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"},{"path":"$CONFIG/*"}]}},"scope-config-index":{"identifier":"scope-config-index","description":"This scope permits to list all files and folders in the `$CONFIG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"}]}},"scope-config-recursive":{"identifier":"scope-config-recursive","description":"This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"},{"path":"$CONFIG/**"}]}},"scope-data":{"identifier":"scope-data","description":"This scope permits access to all files and list content of top level directories in the `$DATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"},{"path":"$DATA/*"}]}},"scope-data-index":{"identifier":"scope-data-index","description":"This scope permits to list all files and folders in the `$DATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"}]}},"scope-data-recursive":{"identifier":"scope-data-recursive","description":"This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"},{"path":"$DATA/**"}]}},"scope-desktop":{"identifier":"scope-desktop","description":"This scope permits access to all files and list content of top level directories in the `$DESKTOP` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"},{"path":"$DESKTOP/*"}]}},"scope-desktop-index":{"identifier":"scope-desktop-index","description":"This scope permits to list all files and folders in the `$DESKTOP`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"}]}},"scope-desktop-recursive":{"identifier":"scope-desktop-recursive","description":"This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"},{"path":"$DESKTOP/**"}]}},"scope-document":{"identifier":"scope-document","description":"This scope permits access to all files and list content of top level directories in the `$DOCUMENT` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"},{"path":"$DOCUMENT/*"}]}},"scope-document-index":{"identifier":"scope-document-index","description":"This scope permits to list all files and folders in the `$DOCUMENT`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"}]}},"scope-document-recursive":{"identifier":"scope-document-recursive","description":"This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"},{"path":"$DOCUMENT/**"}]}},"scope-download":{"identifier":"scope-download","description":"This scope permits access to all files and list content of top level directories in the `$DOWNLOAD` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"},{"path":"$DOWNLOAD/*"}]}},"scope-download-index":{"identifier":"scope-download-index","description":"This scope permits to list all files and folders in the `$DOWNLOAD`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"}]}},"scope-download-recursive":{"identifier":"scope-download-recursive","description":"This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"},{"path":"$DOWNLOAD/**"}]}},"scope-exe":{"identifier":"scope-exe","description":"This scope permits access to all files and list content of top level directories in the `$EXE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"},{"path":"$EXE/*"}]}},"scope-exe-index":{"identifier":"scope-exe-index","description":"This scope permits to list all files and folders in the `$EXE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"}]}},"scope-exe-recursive":{"identifier":"scope-exe-recursive","description":"This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"},{"path":"$EXE/**"}]}},"scope-font":{"identifier":"scope-font","description":"This scope permits access to all files and list content of top level directories in the `$FONT` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"},{"path":"$FONT/*"}]}},"scope-font-index":{"identifier":"scope-font-index","description":"This scope permits to list all files and folders in the `$FONT`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"}]}},"scope-font-recursive":{"identifier":"scope-font-recursive","description":"This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"},{"path":"$FONT/**"}]}},"scope-home":{"identifier":"scope-home","description":"This scope permits access to all files and list content of top level directories in the `$HOME` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"},{"path":"$HOME/*"}]}},"scope-home-index":{"identifier":"scope-home-index","description":"This scope permits to list all files and folders in the `$HOME`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"}]}},"scope-home-recursive":{"identifier":"scope-home-recursive","description":"This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"},{"path":"$HOME/**"}]}},"scope-localdata":{"identifier":"scope-localdata","description":"This scope permits access to all files and list content of top level directories in the `$LOCALDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"},{"path":"$LOCALDATA/*"}]}},"scope-localdata-index":{"identifier":"scope-localdata-index","description":"This scope permits to list all files and folders in the `$LOCALDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"}]}},"scope-localdata-recursive":{"identifier":"scope-localdata-recursive","description":"This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"},{"path":"$LOCALDATA/**"}]}},"scope-log":{"identifier":"scope-log","description":"This scope permits access to all files and list content of top level directories in the `$LOG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"},{"path":"$LOG/*"}]}},"scope-log-index":{"identifier":"scope-log-index","description":"This scope permits to list all files and folders in the `$LOG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"}]}},"scope-log-recursive":{"identifier":"scope-log-recursive","description":"This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"},{"path":"$LOG/**"}]}},"scope-picture":{"identifier":"scope-picture","description":"This scope permits access to all files and list content of top level directories in the `$PICTURE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"},{"path":"$PICTURE/*"}]}},"scope-picture-index":{"identifier":"scope-picture-index","description":"This scope permits to list all files and folders in the `$PICTURE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"}]}},"scope-picture-recursive":{"identifier":"scope-picture-recursive","description":"This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"},{"path":"$PICTURE/**"}]}},"scope-public":{"identifier":"scope-public","description":"This scope permits access to all files and list content of top level directories in the `$PUBLIC` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"},{"path":"$PUBLIC/*"}]}},"scope-public-index":{"identifier":"scope-public-index","description":"This scope permits to list all files and folders in the `$PUBLIC`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"}]}},"scope-public-recursive":{"identifier":"scope-public-recursive","description":"This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"},{"path":"$PUBLIC/**"}]}},"scope-resource":{"identifier":"scope-resource","description":"This scope permits access to all files and list content of top level directories in the `$RESOURCE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"},{"path":"$RESOURCE/*"}]}},"scope-resource-index":{"identifier":"scope-resource-index","description":"This scope permits to list all files and folders in the `$RESOURCE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"}]}},"scope-resource-recursive":{"identifier":"scope-resource-recursive","description":"This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"},{"path":"$RESOURCE/**"}]}},"scope-runtime":{"identifier":"scope-runtime","description":"This scope permits access to all files and list content of top level directories in the `$RUNTIME` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"},{"path":"$RUNTIME/*"}]}},"scope-runtime-index":{"identifier":"scope-runtime-index","description":"This scope permits to list all files and folders in the `$RUNTIME`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"}]}},"scope-runtime-recursive":{"identifier":"scope-runtime-recursive","description":"This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"},{"path":"$RUNTIME/**"}]}},"scope-temp":{"identifier":"scope-temp","description":"This scope permits access to all files and list content of top level directories in the `$TEMP` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"},{"path":"$TEMP/*"}]}},"scope-temp-index":{"identifier":"scope-temp-index","description":"This scope permits to list all files and folders in the `$TEMP`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"}]}},"scope-temp-recursive":{"identifier":"scope-temp-recursive","description":"This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"},{"path":"$TEMP/**"}]}},"scope-template":{"identifier":"scope-template","description":"This scope permits access to all files and list content of top level directories in the `$TEMPLATE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"},{"path":"$TEMPLATE/*"}]}},"scope-template-index":{"identifier":"scope-template-index","description":"This scope permits to list all files and folders in the `$TEMPLATE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"}]}},"scope-template-recursive":{"identifier":"scope-template-recursive","description":"This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"},{"path":"$TEMPLATE/**"}]}},"scope-video":{"identifier":"scope-video","description":"This scope permits access to all files and list content of top level directories in the `$VIDEO` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"},{"path":"$VIDEO/*"}]}},"scope-video-index":{"identifier":"scope-video-index","description":"This scope permits to list all files and folders in the `$VIDEO`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"}]}},"scope-video-recursive":{"identifier":"scope-video-recursive","description":"This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"},{"path":"$VIDEO/**"}]}},"write-all":{"identifier":"write-all","description":"This enables all write related commands without any pre-configured accessible paths.","commands":{"allow":["mkdir","create","copy_file","remove","rename","truncate","ftruncate","write","write_file","write_text_file"],"deny":[]}},"write-files":{"identifier":"write-files","description":"This enables all file write related commands without any pre-configured accessible paths.","commands":{"allow":["create","copy_file","remove","rename","truncate","ftruncate","write","write_file","write_text_file"],"deny":[]}}},"permission_sets":{"allow-app-meta":{"identifier":"allow-app-meta","description":"This allows non-recursive read access to metadata of the application folders, including file listing and statistics.","permissions":["read-meta","scope-app-index"]},"allow-app-meta-recursive":{"identifier":"allow-app-meta-recursive","description":"This allows full recursive read access to metadata of the application folders, including file listing and statistics.","permissions":["read-meta","scope-app-recursive"]},"allow-app-read":{"identifier":"allow-app-read","description":"This allows non-recursive read access to the application folders.","permissions":["read-all","scope-app"]},"allow-app-read-recursive":{"identifier":"allow-app-read-recursive","description":"This allows full recursive read access to the complete application folders, files and subdirectories.","permissions":["read-all","scope-app-recursive"]},"allow-app-write":{"identifier":"allow-app-write","description":"This allows non-recursive write access to the application folders.","permissions":["write-all","scope-app"]},"allow-app-write-recursive":{"identifier":"allow-app-write-recursive","description":"This allows full recursive write access to the complete application folders, files and subdirectories.","permissions":["write-all","scope-app-recursive"]},"allow-appcache-meta":{"identifier":"allow-appcache-meta","description":"This allows non-recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-appcache-index"]},"allow-appcache-meta-recursive":{"identifier":"allow-appcache-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-appcache-recursive"]},"allow-appcache-read":{"identifier":"allow-appcache-read","description":"This allows non-recursive read access to the `$APPCACHE` folder.","permissions":["read-all","scope-appcache"]},"allow-appcache-read-recursive":{"identifier":"allow-appcache-read-recursive","description":"This allows full recursive read access to the complete `$APPCACHE` folder, files and subdirectories.","permissions":["read-all","scope-appcache-recursive"]},"allow-appcache-write":{"identifier":"allow-appcache-write","description":"This allows non-recursive write access to the `$APPCACHE` folder.","permissions":["write-all","scope-appcache"]},"allow-appcache-write-recursive":{"identifier":"allow-appcache-write-recursive","description":"This allows full recursive write access to the complete `$APPCACHE` folder, files and subdirectories.","permissions":["write-all","scope-appcache-recursive"]},"allow-appconfig-meta":{"identifier":"allow-appconfig-meta","description":"This allows non-recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-appconfig-index"]},"allow-appconfig-meta-recursive":{"identifier":"allow-appconfig-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-appconfig-recursive"]},"allow-appconfig-read":{"identifier":"allow-appconfig-read","description":"This allows non-recursive read access to the `$APPCONFIG` folder.","permissions":["read-all","scope-appconfig"]},"allow-appconfig-read-recursive":{"identifier":"allow-appconfig-read-recursive","description":"This allows full recursive read access to the complete `$APPCONFIG` folder, files and subdirectories.","permissions":["read-all","scope-appconfig-recursive"]},"allow-appconfig-write":{"identifier":"allow-appconfig-write","description":"This allows non-recursive write access to the `$APPCONFIG` folder.","permissions":["write-all","scope-appconfig"]},"allow-appconfig-write-recursive":{"identifier":"allow-appconfig-write-recursive","description":"This allows full recursive write access to the complete `$APPCONFIG` folder, files and subdirectories.","permissions":["write-all","scope-appconfig-recursive"]},"allow-appdata-meta":{"identifier":"allow-appdata-meta","description":"This allows non-recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-appdata-index"]},"allow-appdata-meta-recursive":{"identifier":"allow-appdata-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-appdata-recursive"]},"allow-appdata-read":{"identifier":"allow-appdata-read","description":"This allows non-recursive read access to the `$APPDATA` folder.","permissions":["read-all","scope-appdata"]},"allow-appdata-read-recursive":{"identifier":"allow-appdata-read-recursive","description":"This allows full recursive read access to the complete `$APPDATA` folder, files and subdirectories.","permissions":["read-all","scope-appdata-recursive"]},"allow-appdata-write":{"identifier":"allow-appdata-write","description":"This allows non-recursive write access to the `$APPDATA` folder.","permissions":["write-all","scope-appdata"]},"allow-appdata-write-recursive":{"identifier":"allow-appdata-write-recursive","description":"This allows full recursive write access to the complete `$APPDATA` folder, files and subdirectories.","permissions":["write-all","scope-appdata-recursive"]},"allow-applocaldata-meta":{"identifier":"allow-applocaldata-meta","description":"This allows non-recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-applocaldata-index"]},"allow-applocaldata-meta-recursive":{"identifier":"allow-applocaldata-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-applocaldata-recursive"]},"allow-applocaldata-read":{"identifier":"allow-applocaldata-read","description":"This allows non-recursive read access to the `$APPLOCALDATA` folder.","permissions":["read-all","scope-applocaldata"]},"allow-applocaldata-read-recursive":{"identifier":"allow-applocaldata-read-recursive","description":"This allows full recursive read access to the complete `$APPLOCALDATA` folder, files and subdirectories.","permissions":["read-all","scope-applocaldata-recursive"]},"allow-applocaldata-write":{"identifier":"allow-applocaldata-write","description":"This allows non-recursive write access to the `$APPLOCALDATA` folder.","permissions":["write-all","scope-applocaldata"]},"allow-applocaldata-write-recursive":{"identifier":"allow-applocaldata-write-recursive","description":"This allows full recursive write access to the complete `$APPLOCALDATA` folder, files and subdirectories.","permissions":["write-all","scope-applocaldata-recursive"]},"allow-applog-meta":{"identifier":"allow-applog-meta","description":"This allows non-recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.","permissions":["read-meta","scope-applog-index"]},"allow-applog-meta-recursive":{"identifier":"allow-applog-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.","permissions":["read-meta","scope-applog-recursive"]},"allow-applog-read":{"identifier":"allow-applog-read","description":"This allows non-recursive read access to the `$APPLOG` folder.","permissions":["read-all","scope-applog"]},"allow-applog-read-recursive":{"identifier":"allow-applog-read-recursive","description":"This allows full recursive read access to the complete `$APPLOG` folder, files and subdirectories.","permissions":["read-all","scope-applog-recursive"]},"allow-applog-write":{"identifier":"allow-applog-write","description":"This allows non-recursive write access to the `$APPLOG` folder.","permissions":["write-all","scope-applog"]},"allow-applog-write-recursive":{"identifier":"allow-applog-write-recursive","description":"This allows full recursive write access to the complete `$APPLOG` folder, files and subdirectories.","permissions":["write-all","scope-applog-recursive"]},"allow-audio-meta":{"identifier":"allow-audio-meta","description":"This allows non-recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.","permissions":["read-meta","scope-audio-index"]},"allow-audio-meta-recursive":{"identifier":"allow-audio-meta-recursive","description":"This allows full recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.","permissions":["read-meta","scope-audio-recursive"]},"allow-audio-read":{"identifier":"allow-audio-read","description":"This allows non-recursive read access to the `$AUDIO` folder.","permissions":["read-all","scope-audio"]},"allow-audio-read-recursive":{"identifier":"allow-audio-read-recursive","description":"This allows full recursive read access to the complete `$AUDIO` folder, files and subdirectories.","permissions":["read-all","scope-audio-recursive"]},"allow-audio-write":{"identifier":"allow-audio-write","description":"This allows non-recursive write access to the `$AUDIO` folder.","permissions":["write-all","scope-audio"]},"allow-audio-write-recursive":{"identifier":"allow-audio-write-recursive","description":"This allows full recursive write access to the complete `$AUDIO` folder, files and subdirectories.","permissions":["write-all","scope-audio-recursive"]},"allow-cache-meta":{"identifier":"allow-cache-meta","description":"This allows non-recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-cache-index"]},"allow-cache-meta-recursive":{"identifier":"allow-cache-meta-recursive","description":"This allows full recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-cache-recursive"]},"allow-cache-read":{"identifier":"allow-cache-read","description":"This allows non-recursive read access to the `$CACHE` folder.","permissions":["read-all","scope-cache"]},"allow-cache-read-recursive":{"identifier":"allow-cache-read-recursive","description":"This allows full recursive read access to the complete `$CACHE` folder, files and subdirectories.","permissions":["read-all","scope-cache-recursive"]},"allow-cache-write":{"identifier":"allow-cache-write","description":"This allows non-recursive write access to the `$CACHE` folder.","permissions":["write-all","scope-cache"]},"allow-cache-write-recursive":{"identifier":"allow-cache-write-recursive","description":"This allows full recursive write access to the complete `$CACHE` folder, files and subdirectories.","permissions":["write-all","scope-cache-recursive"]},"allow-config-meta":{"identifier":"allow-config-meta","description":"This allows non-recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-config-index"]},"allow-config-meta-recursive":{"identifier":"allow-config-meta-recursive","description":"This allows full recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-config-recursive"]},"allow-config-read":{"identifier":"allow-config-read","description":"This allows non-recursive read access to the `$CONFIG` folder.","permissions":["read-all","scope-config"]},"allow-config-read-recursive":{"identifier":"allow-config-read-recursive","description":"This allows full recursive read access to the complete `$CONFIG` folder, files and subdirectories.","permissions":["read-all","scope-config-recursive"]},"allow-config-write":{"identifier":"allow-config-write","description":"This allows non-recursive write access to the `$CONFIG` folder.","permissions":["write-all","scope-config"]},"allow-config-write-recursive":{"identifier":"allow-config-write-recursive","description":"This allows full recursive write access to the complete `$CONFIG` folder, files and subdirectories.","permissions":["write-all","scope-config-recursive"]},"allow-data-meta":{"identifier":"allow-data-meta","description":"This allows non-recursive read access to metadata of the `$DATA` folder, including file listing and statistics.","permissions":["read-meta","scope-data-index"]},"allow-data-meta-recursive":{"identifier":"allow-data-meta-recursive","description":"This allows full recursive read access to metadata of the `$DATA` folder, including file listing and statistics.","permissions":["read-meta","scope-data-recursive"]},"allow-data-read":{"identifier":"allow-data-read","description":"This allows non-recursive read access to the `$DATA` folder.","permissions":["read-all","scope-data"]},"allow-data-read-recursive":{"identifier":"allow-data-read-recursive","description":"This allows full recursive read access to the complete `$DATA` folder, files and subdirectories.","permissions":["read-all","scope-data-recursive"]},"allow-data-write":{"identifier":"allow-data-write","description":"This allows non-recursive write access to the `$DATA` folder.","permissions":["write-all","scope-data"]},"allow-data-write-recursive":{"identifier":"allow-data-write-recursive","description":"This allows full recursive write access to the complete `$DATA` folder, files and subdirectories.","permissions":["write-all","scope-data-recursive"]},"allow-desktop-meta":{"identifier":"allow-desktop-meta","description":"This allows non-recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.","permissions":["read-meta","scope-desktop-index"]},"allow-desktop-meta-recursive":{"identifier":"allow-desktop-meta-recursive","description":"This allows full recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.","permissions":["read-meta","scope-desktop-recursive"]},"allow-desktop-read":{"identifier":"allow-desktop-read","description":"This allows non-recursive read access to the `$DESKTOP` folder.","permissions":["read-all","scope-desktop"]},"allow-desktop-read-recursive":{"identifier":"allow-desktop-read-recursive","description":"This allows full recursive read access to the complete `$DESKTOP` folder, files and subdirectories.","permissions":["read-all","scope-desktop-recursive"]},"allow-desktop-write":{"identifier":"allow-desktop-write","description":"This allows non-recursive write access to the `$DESKTOP` folder.","permissions":["write-all","scope-desktop"]},"allow-desktop-write-recursive":{"identifier":"allow-desktop-write-recursive","description":"This allows full recursive write access to the complete `$DESKTOP` folder, files and subdirectories.","permissions":["write-all","scope-desktop-recursive"]},"allow-document-meta":{"identifier":"allow-document-meta","description":"This allows non-recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.","permissions":["read-meta","scope-document-index"]},"allow-document-meta-recursive":{"identifier":"allow-document-meta-recursive","description":"This allows full recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.","permissions":["read-meta","scope-document-recursive"]},"allow-document-read":{"identifier":"allow-document-read","description":"This allows non-recursive read access to the `$DOCUMENT` folder.","permissions":["read-all","scope-document"]},"allow-document-read-recursive":{"identifier":"allow-document-read-recursive","description":"This allows full recursive read access to the complete `$DOCUMENT` folder, files and subdirectories.","permissions":["read-all","scope-document-recursive"]},"allow-document-write":{"identifier":"allow-document-write","description":"This allows non-recursive write access to the `$DOCUMENT` folder.","permissions":["write-all","scope-document"]},"allow-document-write-recursive":{"identifier":"allow-document-write-recursive","description":"This allows full recursive write access to the complete `$DOCUMENT` folder, files and subdirectories.","permissions":["write-all","scope-document-recursive"]},"allow-download-meta":{"identifier":"allow-download-meta","description":"This allows non-recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.","permissions":["read-meta","scope-download-index"]},"allow-download-meta-recursive":{"identifier":"allow-download-meta-recursive","description":"This allows full recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.","permissions":["read-meta","scope-download-recursive"]},"allow-download-read":{"identifier":"allow-download-read","description":"This allows non-recursive read access to the `$DOWNLOAD` folder.","permissions":["read-all","scope-download"]},"allow-download-read-recursive":{"identifier":"allow-download-read-recursive","description":"This allows full recursive read access to the complete `$DOWNLOAD` folder, files and subdirectories.","permissions":["read-all","scope-download-recursive"]},"allow-download-write":{"identifier":"allow-download-write","description":"This allows non-recursive write access to the `$DOWNLOAD` folder.","permissions":["write-all","scope-download"]},"allow-download-write-recursive":{"identifier":"allow-download-write-recursive","description":"This allows full recursive write access to the complete `$DOWNLOAD` folder, files and subdirectories.","permissions":["write-all","scope-download-recursive"]},"allow-exe-meta":{"identifier":"allow-exe-meta","description":"This allows non-recursive read access to metadata of the `$EXE` folder, including file listing and statistics.","permissions":["read-meta","scope-exe-index"]},"allow-exe-meta-recursive":{"identifier":"allow-exe-meta-recursive","description":"This allows full recursive read access to metadata of the `$EXE` folder, including file listing and statistics.","permissions":["read-meta","scope-exe-recursive"]},"allow-exe-read":{"identifier":"allow-exe-read","description":"This allows non-recursive read access to the `$EXE` folder.","permissions":["read-all","scope-exe"]},"allow-exe-read-recursive":{"identifier":"allow-exe-read-recursive","description":"This allows full recursive read access to the complete `$EXE` folder, files and subdirectories.","permissions":["read-all","scope-exe-recursive"]},"allow-exe-write":{"identifier":"allow-exe-write","description":"This allows non-recursive write access to the `$EXE` folder.","permissions":["write-all","scope-exe"]},"allow-exe-write-recursive":{"identifier":"allow-exe-write-recursive","description":"This allows full recursive write access to the complete `$EXE` folder, files and subdirectories.","permissions":["write-all","scope-exe-recursive"]},"allow-font-meta":{"identifier":"allow-font-meta","description":"This allows non-recursive read access to metadata of the `$FONT` folder, including file listing and statistics.","permissions":["read-meta","scope-font-index"]},"allow-font-meta-recursive":{"identifier":"allow-font-meta-recursive","description":"This allows full recursive read access to metadata of the `$FONT` folder, including file listing and statistics.","permissions":["read-meta","scope-font-recursive"]},"allow-font-read":{"identifier":"allow-font-read","description":"This allows non-recursive read access to the `$FONT` folder.","permissions":["read-all","scope-font"]},"allow-font-read-recursive":{"identifier":"allow-font-read-recursive","description":"This allows full recursive read access to the complete `$FONT` folder, files and subdirectories.","permissions":["read-all","scope-font-recursive"]},"allow-font-write":{"identifier":"allow-font-write","description":"This allows non-recursive write access to the `$FONT` folder.","permissions":["write-all","scope-font"]},"allow-font-write-recursive":{"identifier":"allow-font-write-recursive","description":"This allows full recursive write access to the complete `$FONT` folder, files and subdirectories.","permissions":["write-all","scope-font-recursive"]},"allow-home-meta":{"identifier":"allow-home-meta","description":"This allows non-recursive read access to metadata of the `$HOME` folder, including file listing and statistics.","permissions":["read-meta","scope-home-index"]},"allow-home-meta-recursive":{"identifier":"allow-home-meta-recursive","description":"This allows full recursive read access to metadata of the `$HOME` folder, including file listing and statistics.","permissions":["read-meta","scope-home-recursive"]},"allow-home-read":{"identifier":"allow-home-read","description":"This allows non-recursive read access to the `$HOME` folder.","permissions":["read-all","scope-home"]},"allow-home-read-recursive":{"identifier":"allow-home-read-recursive","description":"This allows full recursive read access to the complete `$HOME` folder, files and subdirectories.","permissions":["read-all","scope-home-recursive"]},"allow-home-write":{"identifier":"allow-home-write","description":"This allows non-recursive write access to the `$HOME` folder.","permissions":["write-all","scope-home"]},"allow-home-write-recursive":{"identifier":"allow-home-write-recursive","description":"This allows full recursive write access to the complete `$HOME` folder, files and subdirectories.","permissions":["write-all","scope-home-recursive"]},"allow-localdata-meta":{"identifier":"allow-localdata-meta","description":"This allows non-recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-localdata-index"]},"allow-localdata-meta-recursive":{"identifier":"allow-localdata-meta-recursive","description":"This allows full recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-localdata-recursive"]},"allow-localdata-read":{"identifier":"allow-localdata-read","description":"This allows non-recursive read access to the `$LOCALDATA` folder.","permissions":["read-all","scope-localdata"]},"allow-localdata-read-recursive":{"identifier":"allow-localdata-read-recursive","description":"This allows full recursive read access to the complete `$LOCALDATA` folder, files and subdirectories.","permissions":["read-all","scope-localdata-recursive"]},"allow-localdata-write":{"identifier":"allow-localdata-write","description":"This allows non-recursive write access to the `$LOCALDATA` folder.","permissions":["write-all","scope-localdata"]},"allow-localdata-write-recursive":{"identifier":"allow-localdata-write-recursive","description":"This allows full recursive write access to the complete `$LOCALDATA` folder, files and subdirectories.","permissions":["write-all","scope-localdata-recursive"]},"allow-log-meta":{"identifier":"allow-log-meta","description":"This allows non-recursive read access to metadata of the `$LOG` folder, including file listing and statistics.","permissions":["read-meta","scope-log-index"]},"allow-log-meta-recursive":{"identifier":"allow-log-meta-recursive","description":"This allows full recursive read access to metadata of the `$LOG` folder, including file listing and statistics.","permissions":["read-meta","scope-log-recursive"]},"allow-log-read":{"identifier":"allow-log-read","description":"This allows non-recursive read access to the `$LOG` folder.","permissions":["read-all","scope-log"]},"allow-log-read-recursive":{"identifier":"allow-log-read-recursive","description":"This allows full recursive read access to the complete `$LOG` folder, files and subdirectories.","permissions":["read-all","scope-log-recursive"]},"allow-log-write":{"identifier":"allow-log-write","description":"This allows non-recursive write access to the `$LOG` folder.","permissions":["write-all","scope-log"]},"allow-log-write-recursive":{"identifier":"allow-log-write-recursive","description":"This allows full recursive write access to the complete `$LOG` folder, files and subdirectories.","permissions":["write-all","scope-log-recursive"]},"allow-picture-meta":{"identifier":"allow-picture-meta","description":"This allows non-recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.","permissions":["read-meta","scope-picture-index"]},"allow-picture-meta-recursive":{"identifier":"allow-picture-meta-recursive","description":"This allows full recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.","permissions":["read-meta","scope-picture-recursive"]},"allow-picture-read":{"identifier":"allow-picture-read","description":"This allows non-recursive read access to the `$PICTURE` folder.","permissions":["read-all","scope-picture"]},"allow-picture-read-recursive":{"identifier":"allow-picture-read-recursive","description":"This allows full recursive read access to the complete `$PICTURE` folder, files and subdirectories.","permissions":["read-all","scope-picture-recursive"]},"allow-picture-write":{"identifier":"allow-picture-write","description":"This allows non-recursive write access to the `$PICTURE` folder.","permissions":["write-all","scope-picture"]},"allow-picture-write-recursive":{"identifier":"allow-picture-write-recursive","description":"This allows full recursive write access to the complete `$PICTURE` folder, files and subdirectories.","permissions":["write-all","scope-picture-recursive"]},"allow-public-meta":{"identifier":"allow-public-meta","description":"This allows non-recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.","permissions":["read-meta","scope-public-index"]},"allow-public-meta-recursive":{"identifier":"allow-public-meta-recursive","description":"This allows full recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.","permissions":["read-meta","scope-public-recursive"]},"allow-public-read":{"identifier":"allow-public-read","description":"This allows non-recursive read access to the `$PUBLIC` folder.","permissions":["read-all","scope-public"]},"allow-public-read-recursive":{"identifier":"allow-public-read-recursive","description":"This allows full recursive read access to the complete `$PUBLIC` folder, files and subdirectories.","permissions":["read-all","scope-public-recursive"]},"allow-public-write":{"identifier":"allow-public-write","description":"This allows non-recursive write access to the `$PUBLIC` folder.","permissions":["write-all","scope-public"]},"allow-public-write-recursive":{"identifier":"allow-public-write-recursive","description":"This allows full recursive write access to the complete `$PUBLIC` folder, files and subdirectories.","permissions":["write-all","scope-public-recursive"]},"allow-resource-meta":{"identifier":"allow-resource-meta","description":"This allows non-recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.","permissions":["read-meta","scope-resource-index"]},"allow-resource-meta-recursive":{"identifier":"allow-resource-meta-recursive","description":"This allows full recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.","permissions":["read-meta","scope-resource-recursive"]},"allow-resource-read":{"identifier":"allow-resource-read","description":"This allows non-recursive read access to the `$RESOURCE` folder.","permissions":["read-all","scope-resource"]},"allow-resource-read-recursive":{"identifier":"allow-resource-read-recursive","description":"This allows full recursive read access to the complete `$RESOURCE` folder, files and subdirectories.","permissions":["read-all","scope-resource-recursive"]},"allow-resource-write":{"identifier":"allow-resource-write","description":"This allows non-recursive write access to the `$RESOURCE` folder.","permissions":["write-all","scope-resource"]},"allow-resource-write-recursive":{"identifier":"allow-resource-write-recursive","description":"This allows full recursive write access to the complete `$RESOURCE` folder, files and subdirectories.","permissions":["write-all","scope-resource-recursive"]},"allow-runtime-meta":{"identifier":"allow-runtime-meta","description":"This allows non-recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.","permissions":["read-meta","scope-runtime-index"]},"allow-runtime-meta-recursive":{"identifier":"allow-runtime-meta-recursive","description":"This allows full recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.","permissions":["read-meta","scope-runtime-recursive"]},"allow-runtime-read":{"identifier":"allow-runtime-read","description":"This allows non-recursive read access to the `$RUNTIME` folder.","permissions":["read-all","scope-runtime"]},"allow-runtime-read-recursive":{"identifier":"allow-runtime-read-recursive","description":"This allows full recursive read access to the complete `$RUNTIME` folder, files and subdirectories.","permissions":["read-all","scope-runtime-recursive"]},"allow-runtime-write":{"identifier":"allow-runtime-write","description":"This allows non-recursive write access to the `$RUNTIME` folder.","permissions":["write-all","scope-runtime"]},"allow-runtime-write-recursive":{"identifier":"allow-runtime-write-recursive","description":"This allows full recursive write access to the complete `$RUNTIME` folder, files and subdirectories.","permissions":["write-all","scope-runtime-recursive"]},"allow-temp-meta":{"identifier":"allow-temp-meta","description":"This allows non-recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.","permissions":["read-meta","scope-temp-index"]},"allow-temp-meta-recursive":{"identifier":"allow-temp-meta-recursive","description":"This allows full recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.","permissions":["read-meta","scope-temp-recursive"]},"allow-temp-read":{"identifier":"allow-temp-read","description":"This allows non-recursive read access to the `$TEMP` folder.","permissions":["read-all","scope-temp"]},"allow-temp-read-recursive":{"identifier":"allow-temp-read-recursive","description":"This allows full recursive read access to the complete `$TEMP` folder, files and subdirectories.","permissions":["read-all","scope-temp-recursive"]},"allow-temp-write":{"identifier":"allow-temp-write","description":"This allows non-recursive write access to the `$TEMP` folder.","permissions":["write-all","scope-temp"]},"allow-temp-write-recursive":{"identifier":"allow-temp-write-recursive","description":"This allows full recursive write access to the complete `$TEMP` folder, files and subdirectories.","permissions":["write-all","scope-temp-recursive"]},"allow-template-meta":{"identifier":"allow-template-meta","description":"This allows non-recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.","permissions":["read-meta","scope-template-index"]},"allow-template-meta-recursive":{"identifier":"allow-template-meta-recursive","description":"This allows full recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.","permissions":["read-meta","scope-template-recursive"]},"allow-template-read":{"identifier":"allow-template-read","description":"This allows non-recursive read access to the `$TEMPLATE` folder.","permissions":["read-all","scope-template"]},"allow-template-read-recursive":{"identifier":"allow-template-read-recursive","description":"This allows full recursive read access to the complete `$TEMPLATE` folder, files and subdirectories.","permissions":["read-all","scope-template-recursive"]},"allow-template-write":{"identifier":"allow-template-write","description":"This allows non-recursive write access to the `$TEMPLATE` folder.","permissions":["write-all","scope-template"]},"allow-template-write-recursive":{"identifier":"allow-template-write-recursive","description":"This allows full recursive write access to the complete `$TEMPLATE` folder, files and subdirectories.","permissions":["write-all","scope-template-recursive"]},"allow-video-meta":{"identifier":"allow-video-meta","description":"This allows non-recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.","permissions":["read-meta","scope-video-index"]},"allow-video-meta-recursive":{"identifier":"allow-video-meta-recursive","description":"This allows full recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.","permissions":["read-meta","scope-video-recursive"]},"allow-video-read":{"identifier":"allow-video-read","description":"This allows non-recursive read access to the `$VIDEO` folder.","permissions":["read-all","scope-video"]},"allow-video-read-recursive":{"identifier":"allow-video-read-recursive","description":"This allows full recursive read access to the complete `$VIDEO` folder, files and subdirectories.","permissions":["read-all","scope-video-recursive"]},"allow-video-write":{"identifier":"allow-video-write","description":"This allows non-recursive write access to the `$VIDEO` folder.","permissions":["write-all","scope-video"]},"allow-video-write-recursive":{"identifier":"allow-video-write-recursive","description":"This allows full recursive write access to the complete `$VIDEO` folder, files and subdirectories.","permissions":["write-all","scope-video-recursive"]},"deny-default":{"identifier":"deny-default","description":"This denies access to dangerous Tauri relevant files and folders by default.","permissions":["deny-webview-data-linux","deny-webview-data-windows"]}},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","title":"FsScopeEntry","description":"FS scope entry.","anyOf":[{"description":"A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},{"type":"object","required":["path"],"properties":{"path":{"description":"A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"}}}]}},"global-shortcut":{"default_permission":{"identifier":"default","description":"No features are enabled by default, as we believe\nthe shortcuts can be inherently dangerous and it is\napplication specific if specific shortcuts should be\nregistered or unregistered.\n","permissions":[]},"permissions":{"allow-is-registered":{"identifier":"allow-is-registered","description":"Enables the is_registered command without any pre-configured scope.","commands":{"allow":["is_registered"],"deny":[]}},"allow-register":{"identifier":"allow-register","description":"Enables the register command without any pre-configured scope.","commands":{"allow":["register"],"deny":[]}},"allow-register-all":{"identifier":"allow-register-all","description":"Enables the register_all command without any pre-configured scope.","commands":{"allow":["register_all"],"deny":[]}},"allow-unregister":{"identifier":"allow-unregister","description":"Enables the unregister command without any pre-configured scope.","commands":{"allow":["unregister"],"deny":[]}},"allow-unregister-all":{"identifier":"allow-unregister-all","description":"Enables the unregister_all command without any pre-configured scope.","commands":{"allow":["unregister_all"],"deny":[]}},"deny-is-registered":{"identifier":"deny-is-registered","description":"Denies the is_registered command without any pre-configured scope.","commands":{"allow":[],"deny":["is_registered"]}},"deny-register":{"identifier":"deny-register","description":"Denies the register command without any pre-configured scope.","commands":{"allow":[],"deny":["register"]}},"deny-register-all":{"identifier":"deny-register-all","description":"Denies the register_all command without any pre-configured scope.","commands":{"allow":[],"deny":["register_all"]}},"deny-unregister":{"identifier":"deny-unregister","description":"Denies the unregister command without any pre-configured scope.","commands":{"allow":[],"deny":["unregister"]}},"deny-unregister-all":{"identifier":"deny-unregister-all","description":"Denies the unregister_all command without any pre-configured scope.","commands":{"allow":[],"deny":["unregister_all"]}}},"permission_sets":{},"global_scope_schema":null},"notification":{"default_permission":{"identifier":"default","description":"This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n","permissions":["allow-is-permission-granted","allow-request-permission","allow-notify","allow-register-action-types","allow-register-listener","allow-cancel","allow-get-pending","allow-remove-active","allow-get-active","allow-check-permissions","allow-show","allow-batch","allow-list-channels","allow-delete-channel","allow-create-channel","allow-permission-state"]},"permissions":{"allow-batch":{"identifier":"allow-batch","description":"Enables the batch command without any pre-configured scope.","commands":{"allow":["batch"],"deny":[]}},"allow-cancel":{"identifier":"allow-cancel","description":"Enables the cancel command without any pre-configured scope.","commands":{"allow":["cancel"],"deny":[]}},"allow-check-permissions":{"identifier":"allow-check-permissions","description":"Enables the check_permissions command without any pre-configured scope.","commands":{"allow":["check_permissions"],"deny":[]}},"allow-create-channel":{"identifier":"allow-create-channel","description":"Enables the create_channel command without any pre-configured scope.","commands":{"allow":["create_channel"],"deny":[]}},"allow-delete-channel":{"identifier":"allow-delete-channel","description":"Enables the delete_channel command without any pre-configured scope.","commands":{"allow":["delete_channel"],"deny":[]}},"allow-get-active":{"identifier":"allow-get-active","description":"Enables the get_active command without any pre-configured scope.","commands":{"allow":["get_active"],"deny":[]}},"allow-get-pending":{"identifier":"allow-get-pending","description":"Enables the get_pending command without any pre-configured scope.","commands":{"allow":["get_pending"],"deny":[]}},"allow-is-permission-granted":{"identifier":"allow-is-permission-granted","description":"Enables the is_permission_granted command without any pre-configured scope.","commands":{"allow":["is_permission_granted"],"deny":[]}},"allow-list-channels":{"identifier":"allow-list-channels","description":"Enables the list_channels command without any pre-configured scope.","commands":{"allow":["list_channels"],"deny":[]}},"allow-notify":{"identifier":"allow-notify","description":"Enables the notify command without any pre-configured scope.","commands":{"allow":["notify"],"deny":[]}},"allow-permission-state":{"identifier":"allow-permission-state","description":"Enables the permission_state command without any pre-configured scope.","commands":{"allow":["permission_state"],"deny":[]}},"allow-register-action-types":{"identifier":"allow-register-action-types","description":"Enables the register_action_types command without any pre-configured scope.","commands":{"allow":["register_action_types"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-active":{"identifier":"allow-remove-active","description":"Enables the remove_active command without any pre-configured scope.","commands":{"allow":["remove_active"],"deny":[]}},"allow-request-permission":{"identifier":"allow-request-permission","description":"Enables the request_permission command without any pre-configured scope.","commands":{"allow":["request_permission"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"deny-batch":{"identifier":"deny-batch","description":"Denies the batch command without any pre-configured scope.","commands":{"allow":[],"deny":["batch"]}},"deny-cancel":{"identifier":"deny-cancel","description":"Denies the cancel command without any pre-configured scope.","commands":{"allow":[],"deny":["cancel"]}},"deny-check-permissions":{"identifier":"deny-check-permissions","description":"Denies the check_permissions command without any pre-configured scope.","commands":{"allow":[],"deny":["check_permissions"]}},"deny-create-channel":{"identifier":"deny-create-channel","description":"Denies the create_channel command without any pre-configured scope.","commands":{"allow":[],"deny":["create_channel"]}},"deny-delete-channel":{"identifier":"deny-delete-channel","description":"Denies the delete_channel command without any pre-configured scope.","commands":{"allow":[],"deny":["delete_channel"]}},"deny-get-active":{"identifier":"deny-get-active","description":"Denies the get_active command without any pre-configured scope.","commands":{"allow":[],"deny":["get_active"]}},"deny-get-pending":{"identifier":"deny-get-pending","description":"Denies the get_pending command without any pre-configured scope.","commands":{"allow":[],"deny":["get_pending"]}},"deny-is-permission-granted":{"identifier":"deny-is-permission-granted","description":"Denies the is_permission_granted command without any pre-configured scope.","commands":{"allow":[],"deny":["is_permission_granted"]}},"deny-list-channels":{"identifier":"deny-list-channels","description":"Denies the list_channels command without any pre-configured scope.","commands":{"allow":[],"deny":["list_channels"]}},"deny-notify":{"identifier":"deny-notify","description":"Denies the notify command without any pre-configured scope.","commands":{"allow":[],"deny":["notify"]}},"deny-permission-state":{"identifier":"deny-permission-state","description":"Denies the permission_state command without any pre-configured scope.","commands":{"allow":[],"deny":["permission_state"]}},"deny-register-action-types":{"identifier":"deny-register-action-types","description":"Denies the register_action_types command without any pre-configured scope.","commands":{"allow":[],"deny":["register_action_types"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-active":{"identifier":"deny-remove-active","description":"Denies the remove_active command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_active"]}},"deny-request-permission":{"identifier":"deny-request-permission","description":"Denies the request_permission command without any pre-configured scope.","commands":{"allow":[],"deny":["request_permission"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}}},"permission_sets":{},"global_scope_schema":null},"os":{"default_permission":{"identifier":"default","description":"This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n","permissions":["allow-arch","allow-exe-extension","allow-family","allow-locale","allow-os-type","allow-platform","allow-version"]},"permissions":{"allow-arch":{"identifier":"allow-arch","description":"Enables the arch command without any pre-configured scope.","commands":{"allow":["arch"],"deny":[]}},"allow-exe-extension":{"identifier":"allow-exe-extension","description":"Enables the exe_extension command without any pre-configured scope.","commands":{"allow":["exe_extension"],"deny":[]}},"allow-family":{"identifier":"allow-family","description":"Enables the family command without any pre-configured scope.","commands":{"allow":["family"],"deny":[]}},"allow-hostname":{"identifier":"allow-hostname","description":"Enables the hostname command without any pre-configured scope.","commands":{"allow":["hostname"],"deny":[]}},"allow-locale":{"identifier":"allow-locale","description":"Enables the locale command without any pre-configured scope.","commands":{"allow":["locale"],"deny":[]}},"allow-os-type":{"identifier":"allow-os-type","description":"Enables the os_type command without any pre-configured scope.","commands":{"allow":["os_type"],"deny":[]}},"allow-platform":{"identifier":"allow-platform","description":"Enables the platform command without any pre-configured scope.","commands":{"allow":["platform"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-arch":{"identifier":"deny-arch","description":"Denies the arch command without any pre-configured scope.","commands":{"allow":[],"deny":["arch"]}},"deny-exe-extension":{"identifier":"deny-exe-extension","description":"Denies the exe_extension command without any pre-configured scope.","commands":{"allow":[],"deny":["exe_extension"]}},"deny-family":{"identifier":"deny-family","description":"Denies the family command without any pre-configured scope.","commands":{"allow":[],"deny":["family"]}},"deny-hostname":{"identifier":"deny-hostname","description":"Denies the hostname command without any pre-configured scope.","commands":{"allow":[],"deny":["hostname"]}},"deny-locale":{"identifier":"deny-locale","description":"Denies the locale command without any pre-configured scope.","commands":{"allow":[],"deny":["locale"]}},"deny-os-type":{"identifier":"deny-os-type","description":"Denies the os_type command without any pre-configured scope.","commands":{"allow":[],"deny":["os_type"]}},"deny-platform":{"identifier":"deny-platform","description":"Denies the platform command without any pre-configured scope.","commands":{"allow":[],"deny":["platform"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"process":{"default_permission":{"identifier":"default","description":"This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n","permissions":["allow-exit","allow-restart"]},"permissions":{"allow-exit":{"identifier":"allow-exit","description":"Enables the exit command without any pre-configured scope.","commands":{"allow":["exit"],"deny":[]}},"allow-restart":{"identifier":"allow-restart","description":"Enables the restart command without any pre-configured scope.","commands":{"allow":["restart"],"deny":[]}},"deny-exit":{"identifier":"deny-exit","description":"Denies the exit command without any pre-configured scope.","commands":{"allow":[],"deny":["exit"]}},"deny-restart":{"identifier":"deny-restart","description":"Denies the restart command without any pre-configured scope.","commands":{"allow":[],"deny":["restart"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ShellScopeEntry","description":"Shell scope entry.","anyOf":[{"type":"object","required":["cmd","name"],"properties":{"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"args":{"description":"The allowed arguments for the command execution.","allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}]}},"additionalProperties":false},{"type":"object","required":["name","sidecar"],"properties":{"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"args":{"description":"The allowed arguments for the command execution.","allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}]},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"additionalProperties":false}],"definitions":{"ShellScopeEntryAllowedArgs":{"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.","anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","type":"array","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"}}]},"ShellScopeEntryAllowedArg":{"description":"A command argument allowed to be executed by the webview API.","anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"description":"A variable that is set while calling the command from the webview API.","type":"object","required":["validator"],"properties":{"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"},"raw":{"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","default":false,"type":"boolean"}},"additionalProperties":false}]}}}},"updater":{"default_permission":{"identifier":"default","description":"This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n","permissions":["allow-check","allow-download","allow-install","allow-download-and-install"]},"permissions":{"allow-check":{"identifier":"allow-check","description":"Enables the check command without any pre-configured scope.","commands":{"allow":["check"],"deny":[]}},"allow-download":{"identifier":"allow-download","description":"Enables the download command without any pre-configured scope.","commands":{"allow":["download"],"deny":[]}},"allow-download-and-install":{"identifier":"allow-download-and-install","description":"Enables the download_and_install command without any pre-configured scope.","commands":{"allow":["download_and_install"],"deny":[]}},"allow-install":{"identifier":"allow-install","description":"Enables the install command without any pre-configured scope.","commands":{"allow":["install"],"deny":[]}},"deny-check":{"identifier":"deny-check","description":"Denies the check command without any pre-configured scope.","commands":{"allow":[],"deny":["check"]}},"deny-download":{"identifier":"deny-download","description":"Denies the download command without any pre-configured scope.","commands":{"allow":[],"deny":["download"]}},"deny-download-and-install":{"identifier":"deny-download-and-install","description":"Denies the download_and_install command without any pre-configured scope.","commands":{"allow":[],"deny":["download_and_install"]}},"deny-install":{"identifier":"deny-install","description":"Denies the install command without any pre-configured scope.","commands":{"allow":[],"deny":["install"]}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file diff --git a/clash-nyanpasu/backend/tauri/gen/schemas/desktop-schema.json b/clash-nyanpasu/backend/tauri/gen/schemas/desktop-schema.json index d2bd384d20..2e1c51771a 100644 --- a/clash-nyanpasu/backend/tauri/gen/schemas/desktop-schema.json +++ b/clash-nyanpasu/backend/tauri/gen/schemas/desktop-schema.json @@ -2271,10 +2271,10 @@ "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" }, { - "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`", + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`", "type": "string", "const": "core:app:default", - "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`" + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`" }, { "description": "Enables the app_hide command without any pre-configured scope.", @@ -2288,6 +2288,12 @@ "const": "core:app:allow-app-show", "markdownDescription": "Enables the app_show command without any pre-configured scope." }, + { + "description": "Enables the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-bundle-type", + "markdownDescription": "Enables the bundle_type command without any pre-configured scope." + }, { "description": "Enables the default_window_icon command without any pre-configured scope.", "type": "string", @@ -2354,6 +2360,12 @@ "const": "core:app:deny-app-show", "markdownDescription": "Denies the app_show command without any pre-configured scope." }, + { + "description": "Denies the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-bundle-type", + "markdownDescription": "Denies the bundle_type command without any pre-configured scope." + }, { "description": "Denies the default_window_icon command without any pre-configured scope.", "type": "string", diff --git a/clash-nyanpasu/backend/tauri/gen/schemas/windows-schema.json b/clash-nyanpasu/backend/tauri/gen/schemas/windows-schema.json index d2bd384d20..2e1c51771a 100644 --- a/clash-nyanpasu/backend/tauri/gen/schemas/windows-schema.json +++ b/clash-nyanpasu/backend/tauri/gen/schemas/windows-schema.json @@ -2271,10 +2271,10 @@ "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" }, { - "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`", + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`", "type": "string", "const": "core:app:default", - "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`" + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`" }, { "description": "Enables the app_hide command without any pre-configured scope.", @@ -2288,6 +2288,12 @@ "const": "core:app:allow-app-show", "markdownDescription": "Enables the app_show command without any pre-configured scope." }, + { + "description": "Enables the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-bundle-type", + "markdownDescription": "Enables the bundle_type command without any pre-configured scope." + }, { "description": "Enables the default_window_icon command without any pre-configured scope.", "type": "string", @@ -2354,6 +2360,12 @@ "const": "core:app:deny-app-show", "markdownDescription": "Denies the app_show command without any pre-configured scope." }, + { + "description": "Denies the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-bundle-type", + "markdownDescription": "Denies the bundle_type command without any pre-configured scope." + }, { "description": "Denies the default_window_icon command without any pre-configured scope.", "type": "string", diff --git a/clash-nyanpasu/backend/tauri/src/config/profile/item/remote.rs b/clash-nyanpasu/backend/tauri/src/config/profile/item/remote.rs index 47fcd4c529..a1fc023a90 100644 --- a/clash-nyanpasu/backend/tauri/src/config/profile/item/remote.rs +++ b/clash-nyanpasu/backend/tauri/src/config/profile/item/remote.rs @@ -180,36 +180,10 @@ async fn subscribe_url( None => None, }; - // parse the Content-Disposition - let filename = match header - .get("content-disposition") - .or(header.get("Content-Disposition")) - { - Some(value) => { - tracing::debug!("Content-Disposition: {:?}", value); - - let filename = format!("{value:?}"); - let filename = filename.trim_matches('"'); - match help::parse_str::(filename, "filename*") { - Some(filename) => { - let iter = percent_encoding::percent_decode(filename.as_bytes()); - let filename = iter.decode_utf8().unwrap_or_default(); - filename - .split("''") - .last() - .map(|s| s.trim_matches('"').to_string()) - } - None => match help::parse_str::(filename, "filename") { - Some(filename) => { - let filename = filename.trim_matches('"'); - Some(filename.to_string()) - } - None => None, - }, - } - } - None => None, - }; + // Try to parse filename from headers + // `Profile-Title` -> `Content-Disposition` + let filename = utils::parse_profile_title_header(resp.headers()) + .or_else(|| utils::parse_filename_from_content_disposition(resp.headers())); // parse the profile-update-interval let opts = match header @@ -376,8 +350,10 @@ impl RemoteProfileBuilder { let mut subscription = subscribe_url(&url, &options).await?; let extra = subscription.info; - if self.shared.get_name().is_none() && subscription.filename.is_some() { - self.shared.name(subscription.filename.take().unwrap()); + if self.shared.get_name().is_none() { + if let Some(filename) = subscription.filename.take() { + self.shared.name(filename); + } } if self.option.get_update_interval().is_none() && subscription.opts.is_some() { self.option @@ -470,3 +446,57 @@ impl RemoteProfileOptions { options } } + +mod utils { + use base64::{Engine, engine::general_purpose}; + use reqwest::header::{self, HeaderMap}; + + /// parse profile title from headers + pub fn parse_profile_title_header(headers: &HeaderMap) -> Option { + headers + .get("profile-title") + .and_then(|v| v.to_str().ok()) + .and_then(|v| { + if v.starts_with("base64:") { + let encoded = v.trim_start_matches("base64:"); + general_purpose::STANDARD + .decode(encoded) + .ok() + .and_then(|bytes| String::from_utf8(bytes).ok()) + } else { + Some(v.to_string()) + } + }) + } + + pub fn parse_filename_from_content_disposition(headers: &HeaderMap) -> Option { + let filename = crate::utils::help::parse_str::( + headers + .get(header::CONTENT_DISPOSITION) + .and_then(|v| v.to_str().ok()) + .unwrap_or(""), + "filename", + )?; + tracing::debug!("Content-Disposition: {:?}", filename); + + let filename = format!("{filename:?}"); + let filename = filename.trim_matches('"'); + match crate::utils::help::parse_str::(filename, "filename*") { + Some(filename) => { + let iter = percent_encoding::percent_decode(filename.as_bytes()); + let filename = iter.decode_utf8().unwrap_or_default(); + filename + .split("''") + .last() + .map(|s| s.trim_matches('"').to_string()) + } + None => match crate::utils::help::parse_str::(filename, "filename") { + Some(filename) => { + let filename = filename.trim_matches('"'); + Some(filename.to_string()) + } + None => None, + }, + } + } +} diff --git a/clash-nyanpasu/backend/tauri/src/core/storage.rs b/clash-nyanpasu/backend/tauri/src/core/storage.rs index 90a5d9735a..09031478e1 100644 --- a/clash-nyanpasu/backend/tauri/src/core/storage.rs +++ b/clash-nyanpasu/backend/tauri/src/core/storage.rs @@ -1,12 +1,14 @@ use crate::{log_err, utils::dirs}; use anyhow::Context; -use redb::TableDefinition; +use redb::{ReadableDatabase, TableDefinition}; use serde::{Serialize, de::DeserializeOwned}; use std::{fs, ops::Deref, result::Result as StdResult, sync::Arc}; use tauri::{Emitter, Manager}; #[derive(Debug, thiserror::Error)] pub enum StorageOperationError { + #[error("failed to open database: {0}")] + OpenDatabase(#[from] redb::DatabaseError), #[error("internal redb error: {0}")] Redb(#[from] redb::Error), #[error("internal redb table error: {0}")] @@ -61,19 +63,36 @@ pub trait WebStorage { } impl StorageInner { + fn create_and_init_database(path: &std::path::Path) -> Result { + let db = redb::Database::create(path)?; + // Create table + let write_txn = db.begin_write()?; + write_txn.open_table(NYANPASU_TABLE)?; + write_txn.commit()?; + Ok(db) + } + pub fn try_new(path: &std::path::Path) -> Result { - let instance: redb::Database = if path.exists() && !path.is_dir() { - redb::Database::open(path).unwrap() + let metadata = fs::metadata(path).ok(); + let instance: redb::Database = if metadata.as_ref().is_some_and(|m| m.is_file()) { + match redb::Database::open(path) { + Ok(db) => db, + // In redb v3 upgrading point, we only store the task history, and frontend persist state, + // such as memorized router, which is NOT very valuable to make us keep two redb versions, + // intended to support upgrade database formats. + Err(redb::DatabaseError::UpgradeRequired(ver)) => { + tracing::error!("database upgrade required {ver:?}, removing..."); + fs::remove_file(path).unwrap(); + Self::create_and_init_database(path)? + } + Err(e) => return Err(e.into()), + } } else { - if path.exists() && path.is_dir() { + // Remove previous rocksdb files + if metadata.is_some_and(|m| m.is_dir()) { fs::remove_dir_all(path).unwrap(); } - let db = redb::Database::create(path).unwrap(); - // Create table - let write_txn = db.begin_write().unwrap(); - write_txn.open_table(NYANPASU_TABLE).unwrap(); - write_txn.commit().unwrap(); - db + Self::create_and_init_database(path)? }; Ok(Self { instance, diff --git a/clash-nyanpasu/backend/tauri/src/core/tasks/storage.rs b/clash-nyanpasu/backend/tauri/src/core/tasks/storage.rs index ec0360a689..a07bab7e4b 100644 --- a/clash-nyanpasu/backend/tauri/src/core/tasks/storage.rs +++ b/clash-nyanpasu/backend/tauri/src/core/tasks/storage.rs @@ -9,7 +9,7 @@ use crate::core::{ tasks::task::Task, }; use log::debug; -use redb::ReadableTable; +use redb::{ReadableDatabase, ReadableTable}; use std::{collections::HashSet, str}; pub struct TaskStorage { diff --git a/clash-nyanpasu/backend/tauri/src/enhance/script/js.rs b/clash-nyanpasu/backend/tauri/src/enhance/script/js.rs index 2f9e82ec65..91cd8471ad 100644 --- a/clash-nyanpasu/backend/tauri/src/enhance/script/js.rs +++ b/clash-nyanpasu/backend/tauri/src/enhance/script/js.rs @@ -291,10 +291,18 @@ mod utils { use std::borrow::Cow; + #[derive(Debug)] + // TODO: support fn params check and support typescript type erase + struct DefaultExport { + span: Span, + is_function: bool, + } + #[derive(Debug, Default)] struct FunctionVisitor<'n> { exported_name: Vec>, declared_functions: Vec<(Cow<'n, str>, Cow<'n, Span>)>, + default_export: Option, } impl<'n> Visit<'n> for FunctionVisitor<'n> { @@ -323,8 +331,23 @@ mod utils { } walk_function(self, it, flags); } + + // Visit export default declaration to save the default export + fn visit_export_default_declaration( + &mut self, + it: &oxc_ast::ast::ExportDefaultDeclaration<'n>, + ) { + self.default_export = Some(DefaultExport { + is_function: matches!( + it.declaration, + oxc_ast::ast::ExportDefaultDeclarationKind::FunctionDeclaration(_) + ), + span: it.span, + }); + } } + /// This is a tool function to wrap the script if it is not a ESM script. pub fn wrap_script_if_not_esm(script: &str) -> Result, anyhow::Error> { let allocator = Allocator::default(); let source_type = SourceType::default().with_module(true); @@ -341,10 +364,13 @@ mod utils { } return Err(anyhow::anyhow!("parse error: {}", errors)); } - // eprintln!("result: {:#?}", result.program); + #[cfg(test)] + eprintln!("result: {:#?}", result.program); let mut visitor = FunctionVisitor::default(); visitor.visit_program(&result.program); - if visitor.exported_name.iter().any(|s| s.contains("default")) { + #[cfg(test)] + eprintln!("visitor: {:#?}", visitor); + if visitor.default_export.is_some() { return Ok(Cow::Borrowed(script)); } // check whether `function main` exists diff --git a/clash-nyanpasu/backend/tauri/src/utils/help.rs b/clash-nyanpasu/backend/tauri/src/utils/help.rs index 83a307ff85..0ab142f55e 100644 --- a/clash-nyanpasu/backend/tauri/src/utils/help.rs +++ b/clash-nyanpasu/backend/tauri/src/utils/help.rs @@ -21,6 +21,8 @@ use tracing::{debug, warn}; use tracing_attributes::instrument; use crate::trace_err; +use base64::{Engine, engine::general_purpose}; +use reqwest::header::HeaderMap; /// read data from yaml as struct T pub fn read_yaml(path: &PathBuf) -> Result { @@ -84,6 +86,7 @@ pub fn get_uid(prefix: &str) -> String { /// parse the string /// xxx=123123; => 123123 + pub fn parse_str(target: &str, key: &str) -> Option { target.split(';').map(str::trim).find_map(|s| { let mut parts = s.splitn(2, '='); diff --git a/clash-nyanpasu/frontend/interface/package.json b/clash-nyanpasu/frontend/interface/package.json index c8c433a002..ca4ad3d6b3 100644 --- a/clash-nyanpasu/frontend/interface/package.json +++ b/clash-nyanpasu/frontend/interface/package.json @@ -13,7 +13,7 @@ "dependencies": { "@tanstack/react-query": "5.84.2", "@tauri-apps/api": "2.6.0", - "ahooks": "3.9.0", + "ahooks": "3.9.1", "dayjs": "1.11.13", "lodash-es": "4.17.21", "ofetch": "1.4.1", diff --git a/clash-nyanpasu/frontend/interface/src/ipc/use-clash-proxies.ts b/clash-nyanpasu/frontend/interface/src/ipc/use-clash-proxies.ts index ecdb640370..95720e8a92 100644 --- a/clash-nyanpasu/frontend/interface/src/ipc/use-clash-proxies.ts +++ b/clash-nyanpasu/frontend/interface/src/ipc/use-clash-proxies.ts @@ -88,7 +88,9 @@ export const useClashProxies = () => { }) // Apply helper functions to groups and global - const groups = result.groups.map(createGroupWithHelpers) + const groups = result.groups + .filter((g) => !g.hidden) + .map(createGroupWithHelpers) const global = createGroupWithHelpers(result.global) // merge the results & type validation diff --git a/clash-nyanpasu/frontend/nyanpasu/auto-imports.d.ts b/clash-nyanpasu/frontend/nyanpasu/auto-imports.d.ts index 1ad2271ea4..9d2400790b 100644 --- a/clash-nyanpasu/frontend/nyanpasu/auto-imports.d.ts +++ b/clash-nyanpasu/frontend/nyanpasu/auto-imports.d.ts @@ -6,5 +6,5 @@ // biome-ignore lint: disable 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 422452a616..c7d723efdf 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -26,7 +26,7 @@ "@tanstack/router-zod-adapter": "1.81.5", "@tauri-apps/api": "2.6.0", "@types/json-schema": "7.0.15", - "ahooks": "3.9.0", + "ahooks": "3.9.1", "allotment": "1.20.4", "country-code-emoji": "2.3.0", "country-emoji": "1.5.6", @@ -56,12 +56,12 @@ "@csstools/normalize.css": "12.1.1", "@emotion/babel-plugin": "11.13.5", "@emotion/react": "11.14.0", - "@iconify/json": "2.2.373", + "@iconify/json": "2.2.375", "@monaco-editor/react": "4.7.0", "@tanstack/react-query": "5.84.2", - "@tanstack/react-router": "1.131.14", - "@tanstack/react-router-devtools": "1.131.14", - "@tanstack/router-plugin": "1.131.15", + "@tanstack/react-router": "1.131.22", + "@tanstack/react-router-devtools": "1.131.22", + "@tanstack/router-plugin": "1.131.22", "@tauri-apps/plugin-clipboard-manager": "2.3.0", "@tauri-apps/plugin-dialog": "2.3.0", "@tauri-apps/plugin-fs": "2.4.0", @@ -74,8 +74,8 @@ "@types/react-dom": "19.1.7", "@types/validator": "13.15.2", "@vitejs/plugin-legacy": "7.2.1", - "@vitejs/plugin-react": "4.7.0", - "@vitejs/plugin-react-swc": "3.11.0", + "@vitejs/plugin-react": "5.0.0", + "@vitejs/plugin-react-swc": "4.0.0", "change-case": "5.4.4", "clsx": "2.1.1", "core-js": "3.45.0", @@ -85,7 +85,7 @@ "nanoid": "5.1.5", "sass-embedded": "1.90.0", "shiki": "2.5.0", - "unplugin-auto-import": "19.3.0", + "unplugin-auto-import": "20.0.0", "unplugin-icons": "22.2.0", "validator": "13.15.15", "vite": "7.1.2", diff --git a/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/index.scss b/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/index.scss index 15ded366ad..ba8f1d2e97 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/index.scss +++ b/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/index.scss @@ -1,4 +1,4 @@ -@import './fonts.scss'; +@use './fonts.scss'; body { margin: 0; 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 cf099c475a..49d94d8e79 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/components/proxies/group-list.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/proxies/group-list.tsx @@ -67,7 +67,7 @@ export const GroupList = ({ } return data.groups.filter((group) => { - const filterMatches = + return ( !deferredProxiesFilter || group.name .toLowerCase() @@ -78,7 +78,7 @@ export const GroupList = ({ .includes(deferredProxiesFilter.toLowerCase()) }) || false - return !(group.hidden ?? false) && filterMatches + ) }) }, [data?.groups, deferredProxiesFilter]) diff --git a/clash-nyanpasu/frontend/nyanpasu/src/services/i18n.ts b/clash-nyanpasu/frontend/nyanpasu/src/services/i18n.ts index 39f7593722..c877e3eea1 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/services/i18n.ts +++ b/clash-nyanpasu/frontend/nyanpasu/src/services/i18n.ts @@ -1,9 +1,9 @@ import i18n from 'i18next' import { initReactI18next } from 'react-i18next' -import en from '@/locales/en.json' -import ru from '@/locales/ru.json' -import zhCN from '@/locales/zh-CN.json' -import zhTW from '@/locales/zh-TW.json' +import en from '../locales/en.json' +import ru from '../locales/ru.json' +import zhCN from '../locales/zh-CN.json' +import zhTW from '../locales/zh-TW.json' const resources = { en: { translation: en }, diff --git a/clash-nyanpasu/frontend/ui/package.json b/clash-nyanpasu/frontend/ui/package.json index a39a7430d0..f2f893b99b 100644 --- a/clash-nyanpasu/frontend/ui/package.json +++ b/clash-nyanpasu/frontend/ui/package.json @@ -20,8 +20,8 @@ "@tauri-apps/api": "2.6.0", "@types/d3": "7.4.3", "@types/react": "19.1.10", - "@vitejs/plugin-react": "4.7.0", - "ahooks": "3.9.0", + "@vitejs/plugin-react": "5.0.0", + "ahooks": "3.9.1", "d3": "7.9.0", "framer-motion": "12.23.12", "react": "19.1.1", diff --git a/clash-nyanpasu/pnpm-lock.yaml b/clash-nyanpasu/pnpm-lock.yaml index 1c6c4e8f2e..553c815fc6 100644 --- a/clash-nyanpasu/pnpm-lock.yaml +++ b/clash-nyanpasu/pnpm-lock.yaml @@ -182,8 +182,8 @@ importers: specifier: 2.6.0 version: 2.6.0 ahooks: - specifier: 3.9.0 - version: 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: 3.9.1 + version: 3.9.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) dayjs: specifier: 1.11.13 version: 1.11.13 @@ -250,7 +250,7 @@ importers: version: 4.1.11 '@tanstack/router-zod-adapter': specifier: 1.81.5 - version: 1.81.5(@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17) + version: 1.81.5(@tanstack/react-router@1.131.22(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17) '@tauri-apps/api': specifier: 2.6.0 version: 2.6.0 @@ -258,8 +258,8 @@ importers: specifier: 7.0.15 version: 7.0.15 ahooks: - specifier: 3.9.0 - version: 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: 3.9.1 + version: 3.9.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) allotment: specifier: 1.20.4 version: 1.20.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -343,8 +343,8 @@ importers: specifier: 11.14.0 version: 11.14.0(@types/react@19.1.10)(react@19.1.1) '@iconify/json': - specifier: 2.2.373 - version: 2.2.373 + specifier: 2.2.375 + version: 2.2.375 '@monaco-editor/react': specifier: 4.7.0 version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -352,14 +352,14 @@ importers: specifier: 5.84.2 version: 5.84.2(react@19.1.1) '@tanstack/react-router': - specifier: 1.131.14 - version: 1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: 1.131.22 + version: 1.131.22(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@tanstack/react-router-devtools': - specifier: 1.131.14 - version: 1.131.14(@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.14)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3) + specifier: 1.131.22 + version: 1.131.22(@tanstack/react-router@1.131.22(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.22)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3) '@tanstack/router-plugin': - specifier: 1.131.15 - version: 1.131.15(@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) + specifier: 1.131.22 + version: 1.131.22(@tanstack/react-router@1.131.22(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) '@tauri-apps/plugin-clipboard-manager': specifier: 2.3.0 version: 2.3.0 @@ -397,11 +397,11 @@ importers: specifier: 7.2.1 version: 7.2.1(terser@5.36.0)(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) '@vitejs/plugin-react': - specifier: 4.7.0 - version: 4.7.0(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) + specifier: 5.0.0 + version: 5.0.0(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) '@vitejs/plugin-react-swc': - specifier: 3.11.0 - version: 3.11.0(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) + specifier: 4.0.0 + version: 4.0.0(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) change-case: specifier: 5.4.4 version: 5.4.4 @@ -430,8 +430,8 @@ importers: specifier: 2.5.0 version: 2.5.0 unplugin-auto-import: - specifier: 19.3.0 - version: 19.3.0 + specifier: 20.0.0 + version: 20.0.0 unplugin-icons: specifier: 22.2.0 version: 22.2.0(@svgr/core@8.1.0(typescript@5.9.2)) @@ -487,11 +487,11 @@ importers: specifier: 19.1.10 version: 19.1.10 '@vitejs/plugin-react': - specifier: 4.7.0 - version: 4.7.0(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) + specifier: 5.0.0 + version: 5.0.0(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1)) ahooks: - specifier: 3.9.0 - version: 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: 3.9.1 + version: 3.9.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) d3: specifier: 7.9.0 version: 7.9.0 @@ -1759,8 +1759,8 @@ packages: prettier-plugin-ember-template-tag: optional: true - '@iconify/json@2.2.373': - resolution: {integrity: sha512-ZgDwDLgbzf/nEyDoSdC75A1XA0L7Q4rfz0ZKGiq/ZWTztrlM7ASBvjKb6/dhs+gtvepnOc5CLUrBcDUYKZblLg==} + '@iconify/json@2.2.375': + resolution: {integrity: sha512-SexUHUFDGXWZbRIJKg63opwAA/qVquYYi3H4hOdyWwmzuEyjdfzpvkBwEA/BekJX+M3mBOxu6uRj+E89hJbFvw==} '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -2513,8 +2513,8 @@ packages: '@types/react': optional: true - '@rolldown/pluginutils@1.0.0-beta.27': - resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rolldown/pluginutils@1.0.0-beta.30': + resolution: {integrity: sha512-whXaSoNUFiyDAjkUF8OBpOm77Szdbk5lGNqFe6CbVbJFrhCCPinCbRA3NjawwlNHla1No7xvXXh+CpSxnPfUEw==} '@rollup/pluginutils@4.2.1': resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} @@ -2762,68 +2762,68 @@ packages: peerDependencies: '@svgr/core': '*' - '@swc/core-darwin-arm64@1.13.0': - resolution: {integrity: sha512-SkmR9u7MHDu2X8hf7SjZTmsAfQTmel0mi+TJ7AGtufLwGySv6pwQfJ/CIJpcPxYENVqDJAFnDrHaKV8mgA6kxQ==} + '@swc/core-darwin-arm64@1.13.3': + resolution: {integrity: sha512-ux0Ws4pSpBTqbDS9GlVP354MekB1DwYlbxXU3VhnDr4GBcCOimpocx62x7cFJkSpEBF8bmX8+/TTCGKh4PbyXw==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.13.0': - resolution: {integrity: sha512-15/SyDjXRtFJ09fYHBXUXrj4tpiSpCkjgsF1z3/sSpHH1POWpQUQzxmFyomPQVZ/SsDqP18WGH09Vph4Qriuiw==} + '@swc/core-darwin-x64@1.13.3': + resolution: {integrity: sha512-p0X6yhxmNUOMZrbeZ3ZNsPige8lSlSe1llllXvpCLkKKxN/k5vZt1sULoq6Nj4eQ7KeHQVm81/+AwKZyf/e0TA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.13.0': - resolution: {integrity: sha512-AHauVHZQEJI/dCZQg6VYNNQ6HROz8dSOnCSheXzzBw1DGWo77BlcxRP0fF0jaAXM9WNqtCUOY1HiJ9ohkAE61Q==} + '@swc/core-linux-arm-gnueabihf@1.13.3': + resolution: {integrity: sha512-OmDoiexL2fVWvQTCtoh0xHMyEkZweQAlh4dRyvl8ugqIPEVARSYtaj55TBMUJIP44mSUOJ5tytjzhn2KFxFcBA==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.13.0': - resolution: {integrity: sha512-qyZmBZF7asF6954/x7yn6R7Bzd45KRG05rK2atIF9J3MTa8az7vubP1Q3BWmmss1j8699DELpbuoJucGuhsNXw==} + '@swc/core-linux-arm64-gnu@1.13.3': + resolution: {integrity: sha512-STfKku3QfnuUj6k3g9ld4vwhtgCGYIFQmsGPPgT9MK/dI3Lwnpe5Gs5t1inoUIoGNP8sIOLlBB4HV4MmBjQuhw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.13.0': - resolution: {integrity: sha512-whskQCOUlLQT7MjnronpHmyHegBka5ig9JkQvecbqhWzRfdwN+c2xTJs3kQsWy2Vc2f1hcL3D8hGIwY5TwPxMQ==} + '@swc/core-linux-arm64-musl@1.13.3': + resolution: {integrity: sha512-bc+CXYlFc1t8pv9yZJGus372ldzOVscBl7encUBlU1m/Sig0+NDJLz6cXXRcFyl6ABNOApWeR4Yl7iUWx6C8og==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.13.0': - resolution: {integrity: sha512-51n4P4nv6rblXyH3zCEktvmR9uSAZ7+zbfeby0sxbj8LS/IKuVd7iCwD5dwMj4CxG9Fs+HgjN73dLQF/OerHhg==} + '@swc/core-linux-x64-gnu@1.13.3': + resolution: {integrity: sha512-dFXoa0TEhohrKcxn/54YKs1iwNeW6tUkHJgXW33H381SvjKFUV53WR231jh1sWVJETjA3vsAwxKwR23s7UCmUA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.13.0': - resolution: {integrity: sha512-VMqelgvnXs27eQyhDf1S2O2MxSdchIH7c1tkxODRtu9eotcAeniNNgqqLjZ5ML0MGeRk/WpbsAY/GWi7eSpiHw==} + '@swc/core-linux-x64-musl@1.13.3': + resolution: {integrity: sha512-ieyjisLB+ldexiE/yD8uomaZuZIbTc8tjquYln9Quh5ykOBY7LpJJYBWvWtm1g3pHv6AXlBI8Jay7Fffb6aLfA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.13.0': - resolution: {integrity: sha512-NLJmseWJngWeENgat+O/WB4ptNxtx2X4OfPnSG5a/A4sxcn2E4jq91OPvbeUQwDkH+ZQWKXmbXFzt7Nn661QYA==} + '@swc/core-win32-arm64-msvc@1.13.3': + resolution: {integrity: sha512-elTQpnaX5vESSbhCEgcwXjpMsnUbqqHfEpB7ewpkAsLzKEXZaK67ihSRYAuAx6ewRQTo7DS5iTT6X5aQD3MzMw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.13.0': - resolution: {integrity: sha512-UBfwrp0xW37KQGTA08mwrCLIm1ZKy6pXK8IVwou7BvhMgrItRNweTGyUrCnvDLUfyYFuJCmzcEaJ3NudtctD6g==} + '@swc/core-win32-ia32-msvc@1.13.3': + resolution: {integrity: sha512-nvehQVEOdI1BleJpuUgPLrclJ0TzbEMc+MarXDmmiRFwEUGqj+pnfkTSb7RZyS1puU74IXdK/YhTirHurtbI9w==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.13.0': - resolution: {integrity: sha512-BAB1P7Z/y2EENsfsPytPnjIyBVRZN2WULY+s3ozW4QkGmYHde6XXG28n0ABTHhcIOmmR2VzM+uaW1x48laSimw==} + '@swc/core-win32-x64-msvc@1.13.3': + resolution: {integrity: sha512-A+JSKGkRbPLVV2Kwx8TaDAV0yXIXm/gc8m98hSkVDGlPBBmydgzNdWy3X7HTUBM7IDk7YlWE7w2+RUGjdgpTmg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.13.0': - resolution: {integrity: sha512-7Fh16ZH/Rj3Di720if+sw9BictD4N5kbTpsyDC+URXhvsZ7qRt1lH7PaeIQYyJJQHwFhoKpwwGxfGU9SHgPLdw==} + '@swc/core@1.13.3': + resolution: {integrity: sha512-ZaDETVWnm6FE0fc+c2UE8MHYVS3Fe91o5vkmGfgwGXFbxYvAjKSqxM/j4cRc9T7VZNSJjriXq58XkfCp3Y6f+w==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -2945,16 +2945,16 @@ packages: peerDependencies: react: ^18 || ^19 - '@tanstack/react-router-devtools@1.131.14': - resolution: {integrity: sha512-V3C7spd6SG+cShttwFxTXCg0EHyemVeGEgAXPr9AQLKehkCIa6R5WgpUCLxirhkfPBHrwA638q6um0eIODUNqA==} + '@tanstack/react-router-devtools@1.131.22': + resolution: {integrity: sha512-1IEN2wyb3wM3FjyOy6WX1SGAXmofqerAqaZWEXwQ6NcOOa+r46q3MYza/MOymghchEWA2Y7RkXruqcBPa9vu6g==} engines: {node: '>=12'} peerDependencies: - '@tanstack/react-router': ^1.131.14 + '@tanstack/react-router': ^1.131.22 react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-router@1.131.14': - resolution: {integrity: sha512-AA/BKIW6m3ivOrGnCGQKucNuxdPApTgkbVUSMIBx5sLBn/bA5f9JIBgEuJQBkeHYx2JZi/PVq4N6E7USWYC2Ag==} + '@tanstack/react-router@1.131.22': + resolution: {integrity: sha512-gLCAVIglv25MRXMcYQ6XR7FTN93qAR7P3ITMaejDtWofAcqVYJWs3ou1VaXXlOHklEPNgvTa8rcuAD6GQtRzFA==} engines: {node: '>=12'} peerDependencies: react: '>=18.0.0 || >=19.0.0' @@ -2979,15 +2979,15 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/router-core@1.131.14': - resolution: {integrity: sha512-07kOQsaEFs1wVNaz854cWpxwc8EMRsl5jNy8RJwDUckHGxgeDK67J6CAhoKMiR8cXCTQ4OqZKE8W7W+iFg8B5A==} + '@tanstack/router-core@1.131.22': + resolution: {integrity: sha512-lOmX1fzEriOlxJzrkg+QT2897pscJmFrmp4Tz2uEejaNS/dwC0B9wQ8EwSgE9vwmD3OFwaNNjWkvAB37NVYj+g==} engines: {node: '>=12'} - '@tanstack/router-devtools-core@1.131.14': - resolution: {integrity: sha512-MV4PNtGUkcgr/qRBm+6LtHu1rf1+CieTZpEuXNxm3A7zNbDLeIk3aCmjHrsmCgKDE3aj0/q3CWB9LoJZ9lMysw==} + '@tanstack/router-devtools-core@1.131.22': + resolution: {integrity: sha512-YbQFAE2BtGXbW5mLEDteiKLmArtLxqOKolJSDNMjaLjX4HQAJKRn1uRs5iNJpgCBBy4KK1AW1bdbPQdAgBgTWA==} engines: {node: '>=12'} peerDependencies: - '@tanstack/router-core': ^1.131.14 + '@tanstack/router-core': ^1.131.22 csstype: ^3.0.10 solid-js: '>=1.9.5' tiny-invariant: ^1.3.3 @@ -2995,16 +2995,16 @@ packages: csstype: optional: true - '@tanstack/router-generator@1.131.14': - resolution: {integrity: sha512-KyNpWD+6Wtbk7Vm16S9LrGfrEJQihjU8WkK7UnkSFyXnfDti1Rbxv9ftIKj4DZqFKKaw3QCFXwTj2fd0DEsvcA==} + '@tanstack/router-generator@1.131.22': + resolution: {integrity: sha512-iFlxngM+c4MaIyKIaqBx0F89tp8mjuAjVSylE2sE3sekF4Irh9pfAHqNZYMFgou4aZR9BPHvcU6cuDO7mQBHjg==} engines: {node: '>=12'} - '@tanstack/router-plugin@1.131.15': - resolution: {integrity: sha512-acjC9qxsGir6f/autNSXIiJXvtBB+RLseG0u3PeI54EpnqvKT7uTxaQijANyn6Pqi7chc0VzF+OqXI8DBjo/sg==} + '@tanstack/router-plugin@1.131.22': + resolution: {integrity: sha512-JyHI0r1CvfgpG7XFwdEr9+i6Bql3E6s27lbYS2qYX2cjZkv9Vte95jGJ0Qk1XseNx97qGWYT5eVpfmqMz+1+dQ==} engines: {node: '>=12'} peerDependencies: '@rsbuild/core': '>=1.0.2' - '@tanstack/react-router': ^1.131.14 + '@tanstack/react-router': ^1.131.22 vite: '>=5.0.0 || >=6.0.0' vite-plugin-solid: ^2.11.2 webpack: '>=5.92.0' @@ -3608,14 +3608,15 @@ packages: terser: ^5.16.0 vite: ^7.0.0 - '@vitejs/plugin-react-swc@3.11.0': - resolution: {integrity: sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==} + '@vitejs/plugin-react-swc@4.0.0': + resolution: {integrity: sha512-4A1dThI578v07mpG4M+ziNn6lmlMlhtVCheL+2WLvClnLvEULi8rpAZThn2oEKn3GtFXFTOeko6eLRhx2V2kgA==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^4 || ^5 || ^6 || ^7 - '@vitejs/plugin-react@4.7.0': - resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} - engines: {node: ^14.18.0 || >=16.0.0} + '@vitejs/plugin-react@5.0.0': + resolution: {integrity: sha512-Jx9JfsTa05bYkS9xo0hkofp2dCmp1blrKjw9JONs5BTHOvJCgLbaPSuZLGSVJW6u2qe0tc4eevY0+gSNNi0YCw==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -3665,11 +3666,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} - engines: {node: '>=0.4.0'} - hasBin: true - acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -3683,9 +3679,9 @@ packages: resolution: {integrity: sha512-JVzqkCNRT+VfqzzgPWDPnwvDheSAUdiMUn3NoLXpDJF5lRqeJqyC9iGsAxIOAW+mzIdq+uP1TvcX6bMtrH0agg==} engines: {node: '>= 14'} - ahooks@3.9.0: - resolution: {integrity: sha512-r20/C38aFyU3Zqp3620gkdLnxmQhnmWORB3eGGTDlM4i/fOc0GUvM+f2oleMzEu7b3+pHXyzz+FB6ojxsUdYdw==} - engines: {node: '>=8.0.0'} + ahooks@3.9.1: + resolution: {integrity: sha512-5HaldTF43CywmC9qsVqXjbszimAKXXtPKQp0NUtelml0BM3x8i2rq/XNcvjQnw2w7AxbhFQSPDYTdbr1CkbRfg==} + engines: {node: '>=20'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -4170,6 +4166,9 @@ packages: confbox@0.2.1: resolution: {integrity: sha512-hkT3yDPFbs95mNCy1+7qNKC6Pro+/ibzYxtM2iqEigpf0sVw+bg4Zh9/snjsBcf990vfIsg5+1U7VyiyBb3etg==} + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + configstore@3.1.5: resolution: {integrity: sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==} engines: {node: '>=4'} @@ -5048,6 +5047,9 @@ packages: exsolve@1.0.4: resolution: {integrity: sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==} + exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + ext@1.7.0: resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} @@ -6837,6 +6839,9 @@ packages: pkg-types@2.1.0: resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==} + pkg-types@2.2.0: + resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -7960,10 +7965,6 @@ packages: tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} - tinyglobby@0.2.13: - resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} @@ -8155,8 +8156,8 @@ packages: unified@11.0.4: resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} - unimport@4.2.0: - resolution: {integrity: sha512-mYVtA0nmzrysnYnyb3ALMbByJ+Maosee2+WyE0puXl+Xm2bUwPorPaaeZt0ETfuroPOtG8jj1g/qeFZ6buFnag==} + unimport@5.2.0: + resolution: {integrity: sha512-bTuAMMOOqIAyjV4i4UH7P07pO+EsVxmhOzQ2YJ290J6mkLUdozNhb5I/YoOEheeNADC03ent3Qj07X0fWfUpmw==} engines: {node: '>=18.12.0'} unique-string@1.0.0: @@ -8198,11 +8199,11 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - unplugin-auto-import@19.3.0: - resolution: {integrity: sha512-iIi0u4Gq2uGkAOGqlPJOAMI8vocvjh1clGTfSK4SOrJKrt+tirrixo/FjgBwXQNNdS7ofcr7OxzmOb/RjWxeEQ==} + unplugin-auto-import@20.0.0: + resolution: {integrity: sha512-YAqD07gQa5PpGlhLFQ9rWDtGa42nBR2oNMudnX7G5P8XmkQx3EPL7sKNQroTdt5ehwks55NHy0dzxwwA3xtqQg==} engines: {node: '>=14'} peerDependencies: - '@nuxt/kit': ^3.2.2 + '@nuxt/kit': ^4.0.0 '@vueuse/core': '*' peerDependenciesMeta: '@nuxt/kit': @@ -9967,7 +9968,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@iconify/json@2.2.373': + '@iconify/json@2.2.375': dependencies: '@iconify/types': 2.0.0 pathe: 1.1.2 @@ -10675,7 +10676,7 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 - '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rolldown/pluginutils@1.0.0-beta.30': {} '@rollup/pluginutils@4.2.1': dependencies: @@ -10913,51 +10914,51 @@ snapshots: transitivePeerDependencies: - supports-color - '@swc/core-darwin-arm64@1.13.0': + '@swc/core-darwin-arm64@1.13.3': optional: true - '@swc/core-darwin-x64@1.13.0': + '@swc/core-darwin-x64@1.13.3': optional: true - '@swc/core-linux-arm-gnueabihf@1.13.0': + '@swc/core-linux-arm-gnueabihf@1.13.3': optional: true - '@swc/core-linux-arm64-gnu@1.13.0': + '@swc/core-linux-arm64-gnu@1.13.3': optional: true - '@swc/core-linux-arm64-musl@1.13.0': + '@swc/core-linux-arm64-musl@1.13.3': optional: true - '@swc/core-linux-x64-gnu@1.13.0': + '@swc/core-linux-x64-gnu@1.13.3': optional: true - '@swc/core-linux-x64-musl@1.13.0': + '@swc/core-linux-x64-musl@1.13.3': optional: true - '@swc/core-win32-arm64-msvc@1.13.0': + '@swc/core-win32-arm64-msvc@1.13.3': optional: true - '@swc/core-win32-ia32-msvc@1.13.0': + '@swc/core-win32-ia32-msvc@1.13.3': optional: true - '@swc/core-win32-x64-msvc@1.13.0': + '@swc/core-win32-x64-msvc@1.13.3': optional: true - '@swc/core@1.13.0': + '@swc/core@1.13.3': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.23 optionalDependencies: - '@swc/core-darwin-arm64': 1.13.0 - '@swc/core-darwin-x64': 1.13.0 - '@swc/core-linux-arm-gnueabihf': 1.13.0 - '@swc/core-linux-arm64-gnu': 1.13.0 - '@swc/core-linux-arm64-musl': 1.13.0 - '@swc/core-linux-x64-gnu': 1.13.0 - '@swc/core-linux-x64-musl': 1.13.0 - '@swc/core-win32-arm64-msvc': 1.13.0 - '@swc/core-win32-ia32-msvc': 1.13.0 - '@swc/core-win32-x64-msvc': 1.13.0 + '@swc/core-darwin-arm64': 1.13.3 + '@swc/core-darwin-x64': 1.13.3 + '@swc/core-linux-arm-gnueabihf': 1.13.3 + '@swc/core-linux-arm64-gnu': 1.13.3 + '@swc/core-linux-arm64-musl': 1.13.3 + '@swc/core-linux-x64-gnu': 1.13.3 + '@swc/core-linux-x64-musl': 1.13.3 + '@swc/core-win32-arm64-msvc': 1.13.3 + '@swc/core-win32-ia32-msvc': 1.13.3 + '@swc/core-win32-x64-msvc': 1.13.3 '@swc/counter@0.1.3': {} @@ -11054,10 +11055,10 @@ snapshots: '@tanstack/query-core': 5.83.1 react: 19.1.1 - '@tanstack/react-router-devtools@1.131.14(@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.14)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3)': + '@tanstack/react-router-devtools@1.131.22(@tanstack/react-router@1.131.22(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.22)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.5)(tiny-invariant@1.3.3)': dependencies: - '@tanstack/react-router': 1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@tanstack/router-devtools-core': 1.131.14(@tanstack/router-core@1.131.14)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3) + '@tanstack/react-router': 1.131.22(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/router-devtools-core': 1.131.22(@tanstack/router-core@1.131.22)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) transitivePeerDependencies: @@ -11066,11 +11067,11 @@ snapshots: - solid-js - tiny-invariant - '@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@tanstack/react-router@1.131.22(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@tanstack/history': 1.131.2 '@tanstack/react-store': 0.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@tanstack/router-core': 1.131.14 + '@tanstack/router-core': 1.131.22 isbot: 5.1.28 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) @@ -11096,7 +11097,7 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - '@tanstack/router-core@1.131.14': + '@tanstack/router-core@1.131.22': dependencies: '@tanstack/history': 1.131.2 '@tanstack/store': 0.7.0 @@ -11106,9 +11107,9 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/router-devtools-core@1.131.14(@tanstack/router-core@1.131.14)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)': + '@tanstack/router-devtools-core@1.131.22(@tanstack/router-core@1.131.22)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)': dependencies: - '@tanstack/router-core': 1.131.14 + '@tanstack/router-core': 1.131.22 clsx: 2.1.1 goober: 2.1.16(csstype@3.1.3) solid-js: 1.9.5 @@ -11116,9 +11117,9 @@ snapshots: optionalDependencies: csstype: 3.1.3 - '@tanstack/router-generator@1.131.14': + '@tanstack/router-generator@1.131.22': dependencies: - '@tanstack/router-core': 1.131.14 + '@tanstack/router-core': 1.131.22 '@tanstack/router-utils': 1.131.2 '@tanstack/virtual-file-routes': 1.131.2 prettier: 3.6.2 @@ -11129,7 +11130,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.131.15(@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1))': + '@tanstack/router-plugin@1.131.22(@tanstack/react-router@1.131.22(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) @@ -11137,8 +11138,8 @@ snapshots: '@babel/template': 7.27.2 '@babel/traverse': 7.28.0 '@babel/types': 7.28.1 - '@tanstack/router-core': 1.131.14 - '@tanstack/router-generator': 1.131.14 + '@tanstack/router-core': 1.131.22 + '@tanstack/router-generator': 1.131.22 '@tanstack/router-utils': 1.131.2 '@tanstack/virtual-file-routes': 1.131.2 babel-dead-code-elimination: 1.0.10 @@ -11146,7 +11147,7 @@ snapshots: unplugin: 2.3.5 zod: 3.25.76 optionalDependencies: - '@tanstack/react-router': 1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/react-router': 1.131.22(react-dom@19.1.1(react@19.1.1))(react@19.1.1) vite: 7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -11162,9 +11163,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17)': + '@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.131.22(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(zod@4.0.17)': dependencies: - '@tanstack/react-router': 1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/react-router': 1.131.22(react-dom@19.1.1(react@19.1.1))(react@19.1.1) zod: 4.0.17 '@tanstack/store@0.7.0': {} @@ -11778,20 +11779,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-react-swc@3.11.0(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1))': + '@vitejs/plugin-react-swc@4.0.0(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1))': dependencies: - '@rolldown/pluginutils': 1.0.0-beta.27 - '@swc/core': 1.13.0 + '@rolldown/pluginutils': 1.0.0-beta.30 + '@swc/core': 1.13.3 vite: 7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react@4.7.0(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1))': + '@vitejs/plugin-react@5.0.0(vite@7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0) '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.0) - '@rolldown/pluginutils': 1.0.0-beta.27 + '@rolldown/pluginutils': 1.0.0-beta.30 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 vite: 7.1.2(@types/node@22.17.2)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.20.4)(yaml@2.8.1) @@ -11860,8 +11861,6 @@ snapshots: acorn@8.14.0: {} - acorn@8.14.1: {} - acorn@8.15.0: {} adm-zip@0.5.16: {} @@ -11872,9 +11871,9 @@ snapshots: transitivePeerDependencies: - supports-color - ahooks@3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + ahooks@3.9.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.2 dayjs: 1.11.13 intersection-observer: 0.12.2 js-cookie: 3.0.5 @@ -12112,7 +12111,7 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.2 cosmiconfig: 7.1.0 resolve: 1.22.8 @@ -12420,6 +12419,8 @@ snapshots: confbox@0.2.1: {} + confbox@0.2.2: {} + configstore@3.1.5: dependencies: dot-prop: 4.2.1 @@ -13568,6 +13569,8 @@ snapshots: exsolve@1.0.4: {} + exsolve@1.0.7: {} + ext@1.7.0: dependencies: type: 2.7.2 @@ -15542,6 +15545,12 @@ snapshots: exsolve: 1.0.4 pathe: 2.0.3 + pkg-types@2.2.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 + possible-typed-array-names@1.0.0: {} postcss-html@1.8.0: @@ -16730,11 +16739,6 @@ snapshots: tinyexec@1.0.1: {} - tinyglobby@0.2.13: - dependencies: - fdir: 6.4.6(picomatch@4.0.3) - picomatch: 4.0.3 - tinyglobby@0.2.14: dependencies: fdir: 6.4.6(picomatch@4.0.3) @@ -16964,20 +16968,20 @@ snapshots: trough: 2.2.0 vfile: 6.0.1 - unimport@4.2.0: + unimport@5.2.0: dependencies: - acorn: 8.14.1 + acorn: 8.15.0 escape-string-regexp: 5.0.0 estree-walker: 3.0.3 local-pkg: 1.1.1 magic-string: 0.30.17 mlly: 1.7.4 pathe: 2.0.3 - picomatch: 4.0.2 - pkg-types: 2.1.0 + picomatch: 4.0.3 + pkg-types: 2.2.0 scule: 1.3.0 strip-literal: 3.0.0 - tinyglobby: 0.2.13 + tinyglobby: 0.2.14 unplugin: 2.3.5 unplugin-utils: 0.2.4 @@ -17023,12 +17027,12 @@ snapshots: universalify@2.0.1: {} - unplugin-auto-import@19.3.0: + unplugin-auto-import@20.0.0: dependencies: local-pkg: 1.1.1 magic-string: 0.30.17 - picomatch: 4.0.2 - unimport: 4.2.0 + picomatch: 4.0.3 + unimport: 5.2.0 unplugin: 2.3.5 unplugin-utils: 0.2.4 @@ -17047,7 +17051,7 @@ snapshots: unplugin-utils@0.2.4: dependencies: pathe: 2.0.3 - picomatch: 4.0.2 + picomatch: 4.0.3 unplugin@2.3.5: dependencies: diff --git a/lede/package/boot/uboot-rockchip/Makefile b/lede/package/boot/uboot-rockchip/Makefile index 7e8a3a912f..d2c1dfaaa9 100644 --- a/lede/package/boot/uboot-rockchip/Makefile +++ b/lede/package/boot/uboot-rockchip/Makefile @@ -209,7 +209,8 @@ define U-Boot/radxa-e20c-rk3528 $(U-Boot/rk3528/Default) NAME:=Radxa E20C BUILD_DEVICES:= \ - radxa_e20c + radxa_e20c \ + radxa_e24c endef # RK3566 boards @@ -357,7 +358,8 @@ define U-Boot/generic-rk3588 $(U-Boot/rk3588/Default) NAME:=Generic RK3588 BUILD_DEVICES:= \ - hinlink_owl-h88k \ + radxa_e52c \ + radxa_e54c \ seewo_srcm3588-io \ seewo_srcm3588-sw endef diff --git a/lede/package/boot/uboot-rockchip/patches/202-rockchip-Add-initial-RK3582-support.patch b/lede/package/boot/uboot-rockchip/patches/202-rockchip-Add-initial-RK3582-support.patch new file mode 100644 index 0000000000..3619a86cb7 --- /dev/null +++ b/lede/package/boot/uboot-rockchip/patches/202-rockchip-Add-initial-RK3582-support.patch @@ -0,0 +1,262 @@ +From: Jonas Karlman +Date: Mon, 4 Aug 2025 18:16:30 +0000 +Subject: [PATCH v2 1/3] rockchip: Add initial RK3582 support + +The RK3582 SoC is a variant of the RK3588S with some IP blocks disabled. +What blocks are disabled/non-working is indicated by ip-state in OTP. + +This add initial support for RK3582 by using ft_system_setup() to mark +any cpu and/or gpu node with status=fail as indicated by ip-state. + +This apply same policy as vendor U-Boot for RK3582, i.e. two big cpu +cores and the gpu is always failed/disabled. + +Enable Kconfig option OF_SYSTEM_SETUP in board defconfig to make use of +the required DT fixups for RK3582 board variants. + +Signed-off-by: Jonas Karlman +--- + arch/arm/mach-rockchip/rk3588/rk3588.c | 221 +++++++++++++++++++++++++ + 1 file changed, 221 insertions(+) + +--- a/arch/arm/mach-rockchip/rk3588/rk3588.c ++++ b/arch/arm/mach-rockchip/rk3588/rk3588.c +@@ -7,6 +7,7 @@ + #define LOG_CATEGORY LOGC_ARCH + + #include ++#include + #include + #include + #include +@@ -192,6 +193,16 @@ int arch_cpu_init(void) + + #define RK3588_OTP_CPU_CODE_OFFSET 0x02 + #define RK3588_OTP_SPECIFICATION_OFFSET 0x06 ++#define RK3588_OTP_IP_STATE_OFFSET 0x1d ++ ++#define FAIL_CPU_CLUSTER0 GENMASK(3, 0) ++#define FAIL_CPU_CLUSTER1 GENMASK(5, 4) ++#define FAIL_CPU_CLUSTER2 GENMASK(7, 6) ++#define FAIL_GPU GENMASK(4, 1) ++#define FAIL_RKVDEC0 BIT(6) ++#define FAIL_RKVDEC1 BIT(7) ++#define FAIL_RKVENC0 BIT(0) ++#define FAIL_RKVENC1 BIT(2) + + int checkboard(void) + { +@@ -237,3 +248,213 @@ int checkboard(void) + + return 0; + } ++ ++static int fdt_path_del_node(void *fdt, const char *path) ++{ ++ int nodeoffset; ++ ++ nodeoffset = fdt_path_offset(fdt, path); ++ if (nodeoffset < 0) ++ return nodeoffset; ++ ++ return fdt_del_node(fdt, nodeoffset); ++} ++ ++static int fdt_path_set_name(void *fdt, const char *path, const char *name) ++{ ++ int nodeoffset; ++ ++ nodeoffset = fdt_path_offset(fdt, path); ++ if (nodeoffset < 0) ++ return nodeoffset; ++ ++ return fdt_set_name(fdt, nodeoffset, name); ++} ++ ++/* ++ * RK3582 is a variant of the RK3588S with some IP blocks disabled. What blocks ++ * are disabled/non-working is indicated by ip-state in OTP. ft_system_setup() ++ * is used to mark any cpu and/or gpu node with status=fail as indicated by ++ * ip-state. Apply same policy as vendor U-Boot for RK3582, i.e. two big cpu ++ * cores and the gpu is always failed/disabled. Enable OF_SYSTEM_SETUP to make ++ * use of the required DT fixups for RK3582 board variants. ++ */ ++int ft_system_setup(void *blob, struct bd_info *bd) ++{ ++ static const char * const cpu_node_names[] = { ++ "cpu@0", "cpu@100", "cpu@200", "cpu@300", ++ "cpu@400", "cpu@500", "cpu@600", "cpu@700", ++ }; ++ int parent, node, i, comp_len, len, ret; ++ bool cluster1_removed = false; ++ u8 cpu_code[2], ip_state[3]; ++ struct udevice *dev; ++ char soc_comp[16]; ++ const char *comp; ++ void *data; ++ ++ if (!IS_ENABLED(CONFIG_OF_SYSTEM_SETUP)) ++ return 0; ++ ++ if (!IS_ENABLED(CONFIG_ROCKCHIP_OTP) || !CONFIG_IS_ENABLED(MISC)) ++ return -ENOSYS; ++ ++ ret = uclass_get_device_by_driver(UCLASS_MISC, ++ DM_DRIVER_GET(rockchip_otp), &dev); ++ if (ret) { ++ log_debug("Could not find otp device, ret=%d\n", ret); ++ return ret; ++ } ++ ++ /* cpu-code: SoC model, e.g. 0x35 0x82 or 0x35 0x88 */ ++ ret = misc_read(dev, RK3588_OTP_CPU_CODE_OFFSET, cpu_code, 2); ++ if (ret < 0) { ++ log_debug("Could not read cpu-code, ret=%d\n", ret); ++ return ret; ++ } ++ ++ log_debug("cpu-code: %02x %02x\n", cpu_code[0], cpu_code[1]); ++ ++ /* only fail devices on rk3582/rk3583 */ ++ if (!(cpu_code[0] == 0x35 && cpu_code[1] == 0x82) && ++ !(cpu_code[0] == 0x35 && cpu_code[1] == 0x83)) ++ return 0; ++ ++ ret = misc_read(dev, RK3588_OTP_IP_STATE_OFFSET, &ip_state, 3); ++ if (ret < 0) { ++ log_err("Could not read ip-state, ret=%d\n", ret); ++ return ret; ++ } ++ ++ log_debug("ip-state: %02x %02x %02x (otp)\n", ++ ip_state[0], ip_state[1], ip_state[2]); ++ ++ /* policy: fail entire big core cluster when one or more core is bad */ ++ if (ip_state[0] & FAIL_CPU_CLUSTER1) ++ ip_state[0] |= FAIL_CPU_CLUSTER1; ++ if (ip_state[0] & FAIL_CPU_CLUSTER2) ++ ip_state[0] |= FAIL_CPU_CLUSTER2; ++ ++ /* policy: always fail one big core cluster on rk3582/rk3583 */ ++ if (!(ip_state[0] & (FAIL_CPU_CLUSTER1 | FAIL_CPU_CLUSTER2))) ++ ip_state[0] |= FAIL_CPU_CLUSTER2; ++ ++ if (cpu_code[0] == 0x35 && cpu_code[1] == 0x82) { ++ /* policy: always fail gpu on rk3582 */ ++ ip_state[1] |= FAIL_GPU; ++ ++ /* policy: always fail rkvdec on rk3582 */ ++ ip_state[1] |= FAIL_RKVDEC0 | FAIL_RKVDEC1; ++ } else if (cpu_code[0] == 0x35 && cpu_code[1] == 0x83) { ++ /* policy: always fail one rkvdec core on rk3583 */ ++ if (!(ip_state[1] & (FAIL_RKVDEC0 | FAIL_RKVDEC1))) ++ ip_state[1] |= FAIL_RKVDEC1; ++ } ++ ++ /* policy: always fail one rkvenc core on rk3582/rk3583 */ ++ if (!(ip_state[2] & (FAIL_RKVENC0 | FAIL_RKVENC1))) ++ ip_state[2] |= FAIL_RKVENC1; ++ ++ log_debug("ip-state: %02x %02x %02x (policy)\n", ++ ip_state[0], ip_state[1], ip_state[2]); ++ ++ /* cpu cluster1: ip_state[0]: bit4~5 */ ++ if ((ip_state[0] & FAIL_CPU_CLUSTER1) == FAIL_CPU_CLUSTER1) { ++ log_debug("remove cpu-map cluster1\n"); ++ fdt_path_del_node(blob, "/cpus/cpu-map/cluster1"); ++ cluster1_removed = true; ++ } ++ ++ /* cpu cluster2: ip_state[0]: bit6~7 */ ++ if ((ip_state[0] & FAIL_CPU_CLUSTER2) == FAIL_CPU_CLUSTER2) { ++ log_debug("remove cpu-map cluster2\n"); ++ fdt_path_del_node(blob, "/cpus/cpu-map/cluster2"); ++ } else if (cluster1_removed) { ++ /* cluster nodes must be named in a continuous series */ ++ log_debug("rename cpu-map cluster2\n"); ++ fdt_path_set_name(blob, "/cpus/cpu-map/cluster2", "cluster1"); ++ } ++ ++ /* gpu: ip_state[1]: bit1~4 */ ++ if (ip_state[1] & FAIL_GPU) { ++ log_debug("fail gpu\n"); ++ fdt_status_fail_by_pathf(blob, "/gpu@fb000000"); ++ } ++ ++ /* rkvdec: ip_state[1]: bit6,7 */ ++ if (ip_state[1] & FAIL_RKVDEC0) { ++ log_debug("fail rkvdec0\n"); ++ fdt_status_fail_by_pathf(blob, "/video-codec@fdc38000"); ++ fdt_status_fail_by_pathf(blob, "/iommu@fdc38700"); ++ } ++ if (ip_state[1] & FAIL_RKVDEC1) { ++ log_debug("fail rkvdec1\n"); ++ fdt_status_fail_by_pathf(blob, "/video-codec@fdc40000"); ++ fdt_status_fail_by_pathf(blob, "/iommu@fdc40700"); ++ } ++ ++ /* rkvenc: ip_state[2]: bit0,2 */ ++ if (ip_state[2] & FAIL_RKVENC0) { ++ log_debug("fail rkvenc0\n"); ++ fdt_status_fail_by_pathf(blob, "/video-codec@fdbd0000"); ++ fdt_status_fail_by_pathf(blob, "/iommu@fdbdf000"); ++ } ++ if (ip_state[2] & FAIL_RKVENC1) { ++ log_debug("fail rkvenc1\n"); ++ fdt_status_fail_by_pathf(blob, "/video-codec@fdbe0000"); ++ fdt_status_fail_by_pathf(blob, "/iommu@fdbef000"); ++ } ++ ++ parent = fdt_path_offset(blob, "/cpus"); ++ if (parent < 0) { ++ log_err("Could not find /cpus, parent=%d\n", parent); ++ return parent; ++ } ++ ++ /* cpu: ip_state[0]: bit0~7 */ ++ for (i = 0; i < 8; i++) { ++ /* fail any bad cpu core */ ++ if (!(ip_state[0] & BIT(i))) ++ continue; ++ ++ node = fdt_subnode_offset(blob, parent, cpu_node_names[i]); ++ if (node >= 0) { ++ log_debug("fail cpu %s\n", cpu_node_names[i]); ++ fdt_status_fail(blob, node); ++ } else { ++ log_err("Could not find %s, node=%d\n", ++ cpu_node_names[i], node); ++ return node; ++ } ++ } ++ ++ node = fdt_path_offset(blob, "/"); ++ if (node < 0) { ++ log_err("Could not find /, node=%d\n", node); ++ return node; ++ } ++ ++ snprintf(soc_comp, sizeof(soc_comp), "rockchip,rk35%x", cpu_code[1]); ++ ++ for (i = 0, comp_len = 0; ++ (comp = fdt_stringlist_get(blob, node, "compatible", i, &len)); ++ i++) { ++ /* stop at soc compatible */ ++ if (!strcmp(comp, soc_comp) || ++ !strcmp(comp, "rockchip,rk3588s") || ++ !strcmp(comp, "rockchip,rk3588")) ++ break; ++ ++ log_debug("compatible[%d]: %s\n", i, comp); ++ comp_len += len + 1; ++ } ++ ++ /* truncate to only include board compatible */ ++ fdt_setprop_placeholder(blob, node, "compatible", comp_len, &data); ++ ++ /* append soc compatible */ ++ fdt_appendprop_string(blob, node, "compatible", soc_comp); ++ fdt_appendprop_string(blob, node, "compatible", "rockchip,rk3588s"); ++ ++ return 0; ++} diff --git a/lede/package/boot/uboot-rockchip/patches/203-rockchip-rk3588-generic-Enable-support-for.patch b/lede/package/boot/uboot-rockchip/patches/203-rockchip-rk3588-generic-Enable-support-for.patch new file mode 100644 index 0000000000..fb884006ba --- /dev/null +++ b/lede/package/boot/uboot-rockchip/patches/203-rockchip-rk3588-generic-Enable-support-for.patch @@ -0,0 +1,80 @@ +From: Jonas Karlman +Date: Mon, 4 Aug 2025 18:16:31 +0000 +Subject: [PATCH v2 2/3] rockchip: rk3588-generic: Enable support for RK3582 + +Add Kconfig option OF_SYSTEM_SETUP=y to support booting boards with a +RK3582 SoC. CPU and GPU cores are failed based on ip-state and policy. + +Tested on a ROCK 5C Lite v1.1: + + cpu-code: 35 82 + ip-state: 10 00 00 (otp) + ip-state: 30 de 04 (policy) + remove cpu-map cluster1 + rename cpu-map cluster2 + fail gpu + fail rkvdec0 + fail rkvdec1 + fail rkvenc1 + fail cpu cpu@400 + fail cpu cpu@500 + +and on a Radxa E52C: + + cpu-code: 35 82 + ip-state: 00 04 00 (otp) + ip-state: c0 de 04 (policy) + remove cpu-map cluster2 + fail gpu + fail rkvdec0 + fail rkvdec1 + fail rkvenc1 + fail cpu cpu@600 + fail cpu cpu@700 + +Signed-off-by: Jonas Karlman +--- + arch/arm/dts/rk3588-generic.dts | 4 ++-- + configs/generic-rk3588_defconfig | 1 + + doc/board/rockchip/rockchip.rst | 2 +- + 3 files changed, 4 insertions(+), 3 deletions(-) + +--- a/arch/arm/dts/rk3588-generic.dts ++++ b/arch/arm/dts/rk3588-generic.dts +@@ -1,13 +1,13 @@ + // SPDX-License-Identifier: (GPL-2.0+ OR MIT) + /* +- * Minimal generic DT for RK3588S/RK3588 with eMMC, SD-card and USB OTG enabled ++ * Minimal generic DT for RK3582/RK3588S/RK3588 with eMMC, SD-card and USB OTG enabled + */ + + /dts-v1/; + #include "rk3588s.dtsi" + + / { +- model = "Generic RK3588S/RK3588"; ++ model = "Generic RK3582/RK3588S/RK3588"; + compatible = "rockchip,rk3588"; + + aliases { +--- a/configs/generic-rk3588_defconfig ++++ b/configs/generic-rk3588_defconfig +@@ -16,6 +16,7 @@ CONFIG_SPL_FIT_SIGNATURE=y + CONFIG_SPL_LOAD_FIT=y + # CONFIG_BOOTMETH_VBE is not set + CONFIG_LEGACY_IMAGE_FORMAT=y ++CONFIG_OF_SYSTEM_SETUP=y + CONFIG_DEFAULT_FDT_FILE="rockchip/rk3588-generic.dtb" + # CONFIG_DISPLAY_CPUINFO is not set + CONFIG_SPL_MAX_SIZE=0x40000 +--- a/doc/board/rockchip/rockchip.rst ++++ b/doc/board/rockchip/rockchip.rst +@@ -145,7 +145,7 @@ List of mainline supported Rockchip boar + - FriendlyElec NanoPC-T6 (nanopc-t6-rk3588) + - FriendlyElec NanoPi R6C (nanopi-r6c-rk3588s) + - FriendlyElec NanoPi R6S (nanopi-r6s-rk3588s) +- - Generic RK3588S/RK3588 (generic-rk3588) ++ - Generic RK3582/RK3588S/RK3588 (generic-rk3588) + - Hardkernel ODROID-M2 (odroid-m2-rk3588s) + - Indiedroid Nova (nova-rk3588s) + - Khadas Edge2 (khadas-edge2-rk3588s) diff --git a/lede/package/boot/uboot-rockchip/patches/210-rockchip-rk3588-disable-policy-for-rk3582.patch b/lede/package/boot/uboot-rockchip/patches/210-rockchip-rk3588-disable-policy-for-rk3582.patch new file mode 100644 index 0000000000..7b54f7e23b --- /dev/null +++ b/lede/package/boot/uboot-rockchip/patches/210-rockchip-rk3588-disable-policy-for-rk3582.patch @@ -0,0 +1,18 @@ +--- a/arch/arm/mach-rockchip/rk3588/rk3588.c ++++ b/arch/arm/mach-rockchip/rk3588/rk3588.c +@@ -335,6 +335,7 @@ int ft_system_setup(void *blob, struct b + if (ip_state[0] & FAIL_CPU_CLUSTER2) + ip_state[0] |= FAIL_CPU_CLUSTER2; + ++#if 0 + /* policy: always fail one big core cluster on rk3582/rk3583 */ + if (!(ip_state[0] & (FAIL_CPU_CLUSTER1 | FAIL_CPU_CLUSTER2))) + ip_state[0] |= FAIL_CPU_CLUSTER2; +@@ -354,6 +355,7 @@ int ft_system_setup(void *blob, struct b + /* policy: always fail one rkvenc core on rk3582/rk3583 */ + if (!(ip_state[2] & (FAIL_RKVENC0 | FAIL_RKVENC1))) + ip_state[2] |= FAIL_RKVENC1; ++#endif + + log_debug("ip-state: %02x %02x %02x (policy)\n", + ip_state[0], ip_state[1], ip_state[2]); diff --git a/lede/target/linux/rockchip/patches-6.12/104-net-dsa-realtek-fixes-reset-controller.patch b/lede/target/linux/rockchip/patches-6.12/104-net-dsa-realtek-fixes-reset-controller.patch new file mode 100644 index 0000000000..55cfe6d6d1 --- /dev/null +++ b/lede/target/linux/rockchip/patches-6.12/104-net-dsa-realtek-fixes-reset-controller.patch @@ -0,0 +1,11 @@ +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -184,7 +184,7 @@ rtl83xx_probe(struct device *dev, + "realtek,disable-leds"); + + /* TODO: if power is software controlled, set up any regulators here */ +- priv->reset_ctl = devm_reset_control_get_optional(dev, NULL); ++ priv->reset_ctl = devm_reset_control_get_optional(dev, "switch"); + if (IS_ERR(priv->reset_ctl)) + return dev_err_cast_probe(dev, priv->reset_ctl, + "failed to get reset control\n"); diff --git a/lede/target/linux/rockchip/patches-6.12/700-ethernet-stmmac-Add-property-to-disable-VLAN-hw-filter.patch b/lede/target/linux/rockchip/patches-6.12/700-ethernet-stmmac-Add-property-to-disable-VLAN-hw-filter.patch new file mode 100644 index 0000000000..665f86eae0 --- /dev/null +++ b/lede/target/linux/rockchip/patches-6.12/700-ethernet-stmmac-Add-property-to-disable-VLAN-hw-filter.patch @@ -0,0 +1,48 @@ +From 54ad65e581e180eaf81306334b1da3fa0ee7e3f5 Mon Sep 17 00:00:00 2001 +From: jensen +Date: Thu, 17 Nov 2022 18:35:43 +0800 +Subject: [PATCH] ethernet: stmmac: Add property to disable VLAN hw filter + +Since adding VLAN in promisc mode not supported, which makes unable to +setup bridge, add a configurable option to disable it. + +Signed-off-by: jensen +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- + drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 3 +++ + include/linux/stmmac.h | 1 + + 3 files changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -7691,7 +7691,7 @@ int stmmac_dvr_probe(struct device *device, + ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; + priv->hw->hw_vlan_en = true; + } +- if (priv->dma_cap.vlhash) { ++ if (priv->plat->vlhash_en && priv->dma_cap.vlhash) { + ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + ndev->features |= NETIF_F_HW_VLAN_STAG_FILTER; + } +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -596,6 +596,9 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) + "force_sf_dma_mode is ignored if force_thresh_dma_mode is set.\n"); + } + ++ /* To disable VLAN tag filter */ ++ plat->vlhash_en = !of_property_read_bool(np, "snps,no-vlhash"); ++ + of_property_read_u32(np, "snps,ps-speed", &plat->mac_port_sel_speed); + + plat->axi = stmmac_axi_setup(pdev); +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -263,6 +263,7 @@ struct plat_stmmacenet_data { + int mac_port_sel_speed; + int has_xgmac; + u8 vlan_fail_q; ++ bool vlhash_en; + unsigned int eee_usecs_rate; + struct pci_dev *pdev; + int int_snapshot_num; diff --git a/lede/target/linux/rockchip/patches-6.6/700-ethernet-stmmac-Add-property-to-disable-VLAN-hw-filter.patch b/lede/target/linux/rockchip/patches-6.6/700-ethernet-stmmac-Add-property-to-disable-VLAN-hw-filter.patch new file mode 100644 index 0000000000..125be9901e --- /dev/null +++ b/lede/target/linux/rockchip/patches-6.6/700-ethernet-stmmac-Add-property-to-disable-VLAN-hw-filter.patch @@ -0,0 +1,48 @@ +From 54ad65e581e180eaf81306334b1da3fa0ee7e3f5 Mon Sep 17 00:00:00 2001 +From: jensen +Date: Thu, 17 Nov 2022 18:35:43 +0800 +Subject: [PATCH] ethernet: stmmac: Add property to disable VLAN hw filter + +Since adding VLAN in promisc mode not supported, which makes unable to +setup bridge, add a configurable option to disable it. + +Signed-off-by: jensen +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- + drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 3 +++ + include/linux/stmmac.h | 1 + + 3 files changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -7577,7 +7577,7 @@ int stmmac_dvr_probe(struct device *device, + #ifdef STMMAC_VLAN_TAG_USED + /* Both mac100 and gmac support receive VLAN tag detection */ + ndev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX; +- if (priv->dma_cap.vlhash) { ++ if (priv->plat->vlhash_en && priv->dma_cap.vlhash) { + ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + ndev->features |= NETIF_F_HW_VLAN_STAG_FILTER; + } +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -596,6 +596,9 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) + "force_sf_dma_mode is ignored if force_thresh_dma_mode is set.\n"); + } + ++ /* To disable VLAN tag filter */ ++ plat->vlhash_en = !of_property_read_bool(np, "snps,no-vlhash"); ++ + of_property_read_u32(np, "snps,ps-speed", &plat->mac_port_sel_speed); + + plat->axi = stmmac_axi_setup(pdev); +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -300,6 +300,7 @@ struct plat_stmmacenet_data { + int mac_port_sel_speed; + int has_xgmac; + u8 vlan_fail_q; ++ bool vlhash_en; + unsigned int eee_usecs_rate; + struct pci_dev *pdev; + int int_snapshot_num; diff --git a/mieru/Makefile b/mieru/Makefile index f15e27bf5e..c24f3fa599 100644 --- a/mieru/Makefile +++ b/mieru/Makefile @@ -32,7 +32,7 @@ PROJECT_NAME=$(shell basename "${ROOT}") # - pkg/version/current.go # # Use `tools/bump_version.sh` script to change all those files at one shot. -VERSION="3.18.0" +VERSION="3.19.0" # Build binaries and installation packages. .PHONY: build diff --git a/mieru/build/package/mieru/amd64/debian/DEBIAN/control b/mieru/build/package/mieru/amd64/debian/DEBIAN/control index eaf38c09ac..9790f652d0 100755 --- a/mieru/build/package/mieru/amd64/debian/DEBIAN/control +++ b/mieru/build/package/mieru/amd64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mieru -Version: 3.18.0 +Version: 3.19.0 Section: net Priority: optional Architecture: amd64 diff --git a/mieru/build/package/mieru/amd64/rpm/mieru.spec b/mieru/build/package/mieru/amd64/rpm/mieru.spec index 5b3966e408..c777874b8c 100644 --- a/mieru/build/package/mieru/amd64/rpm/mieru.spec +++ b/mieru/build/package/mieru/amd64/rpm/mieru.spec @@ -1,5 +1,5 @@ Name: mieru -Version: 3.18.0 +Version: 3.19.0 Release: 1%{?dist} Summary: Mieru proxy client License: GPLv3+ diff --git a/mieru/build/package/mieru/arm64/debian/DEBIAN/control b/mieru/build/package/mieru/arm64/debian/DEBIAN/control index 6672f65241..15af3b8631 100755 --- a/mieru/build/package/mieru/arm64/debian/DEBIAN/control +++ b/mieru/build/package/mieru/arm64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mieru -Version: 3.18.0 +Version: 3.19.0 Section: net Priority: optional Architecture: arm64 diff --git a/mieru/build/package/mieru/arm64/rpm/mieru.spec b/mieru/build/package/mieru/arm64/rpm/mieru.spec index 5b3966e408..c777874b8c 100644 --- a/mieru/build/package/mieru/arm64/rpm/mieru.spec +++ b/mieru/build/package/mieru/arm64/rpm/mieru.spec @@ -1,5 +1,5 @@ Name: mieru -Version: 3.18.0 +Version: 3.19.0 Release: 1%{?dist} Summary: Mieru proxy client License: GPLv3+ diff --git a/mieru/build/package/mita/amd64/debian/DEBIAN/control b/mieru/build/package/mita/amd64/debian/DEBIAN/control index 791594c112..4cc528d629 100755 --- a/mieru/build/package/mita/amd64/debian/DEBIAN/control +++ b/mieru/build/package/mita/amd64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mita -Version: 3.18.0 +Version: 3.19.0 Section: net Priority: optional Architecture: amd64 diff --git a/mieru/build/package/mita/amd64/rpm/mita.spec b/mieru/build/package/mita/amd64/rpm/mita.spec index 3f69efdc35..4fe7372751 100644 --- a/mieru/build/package/mita/amd64/rpm/mita.spec +++ b/mieru/build/package/mita/amd64/rpm/mita.spec @@ -1,5 +1,5 @@ Name: mita -Version: 3.18.0 +Version: 3.19.0 Release: 1%{?dist} Summary: Mieru proxy server License: GPLv3+ diff --git a/mieru/build/package/mita/arm64/debian/DEBIAN/control b/mieru/build/package/mita/arm64/debian/DEBIAN/control index 31a84db354..f8f8861a02 100755 --- a/mieru/build/package/mita/arm64/debian/DEBIAN/control +++ b/mieru/build/package/mita/arm64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mita -Version: 3.18.0 +Version: 3.19.0 Section: net Priority: optional Architecture: arm64 diff --git a/mieru/build/package/mita/arm64/rpm/mita.spec b/mieru/build/package/mita/arm64/rpm/mita.spec index 0102e93ed6..9f8679d2d5 100644 --- a/mieru/build/package/mita/arm64/rpm/mita.spec +++ b/mieru/build/package/mita/arm64/rpm/mita.spec @@ -1,5 +1,5 @@ Name: mita -Version: 3.18.0 +Version: 3.19.0 Release: 1%{?dist} Summary: Mieru proxy server License: GPLv3+ diff --git a/mieru/docs/server-install.md b/mieru/docs/server-install.md index 84fa43b997..b7d5617437 100644 --- a/mieru/docs/server-install.md +++ b/mieru/docs/server-install.md @@ -18,32 +18,32 @@ Or you can manually install and configure proxy server using the steps below. ```sh # Debian / Ubuntu - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita_3.18.0_amd64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.0/mita_3.19.0_amd64.deb # Debian / Ubuntu - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita_3.18.0_arm64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.0/mita_3.19.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita-3.18.0-1.x86_64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.0/mita-3.19.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita-3.18.0-1.aarch64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.0/mita-3.19.0-1.aarch64.rpm ``` ## Install mita package ```sh # Debian / Ubuntu - X86_64 -sudo dpkg -i mita_3.18.0_amd64.deb +sudo dpkg -i mita_3.19.0_amd64.deb # Debian / Ubuntu - ARM 64 -sudo dpkg -i mita_3.18.0_arm64.deb +sudo dpkg -i mita_3.19.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -sudo rpm -Uvh --force mita-3.18.0-1.x86_64.rpm +sudo rpm -Uvh --force mita-3.19.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -sudo rpm -Uvh --force mita-3.18.0-1.aarch64.rpm +sudo rpm -Uvh --force mita-3.19.0-1.aarch64.rpm ``` Those instructions can also be used to upgrade the version of mita software package. diff --git a/mieru/docs/server-install.zh_CN.md b/mieru/docs/server-install.zh_CN.md index cb4cd136c6..b2758c8f25 100644 --- a/mieru/docs/server-install.zh_CN.md +++ b/mieru/docs/server-install.zh_CN.md @@ -18,32 +18,32 @@ sudo python3 setup.py --lang=zh ```sh # Debian / Ubuntu - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita_3.18.0_amd64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.0/mita_3.19.0_amd64.deb # Debian / Ubuntu - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita_3.18.0_arm64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.0/mita_3.19.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita-3.18.0-1.x86_64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.0/mita-3.19.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.18.0/mita-3.18.0-1.aarch64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.0/mita-3.19.0-1.aarch64.rpm ``` ## 安装 mita 软件包 ```sh # Debian / Ubuntu - X86_64 -sudo dpkg -i mita_3.18.0_amd64.deb +sudo dpkg -i mita_3.19.0_amd64.deb # Debian / Ubuntu - ARM 64 -sudo dpkg -i mita_3.18.0_arm64.deb +sudo dpkg -i mita_3.19.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -sudo rpm -Uvh --force mita-3.18.0-1.x86_64.rpm +sudo rpm -Uvh --force mita-3.19.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -sudo rpm -Uvh --force mita-3.18.0-1.aarch64.rpm +sudo rpm -Uvh --force mita-3.19.0-1.aarch64.rpm ``` 上述指令也可以用来升级 mita 软件包的版本。 diff --git a/mieru/pkg/protocol/session.go b/mieru/pkg/protocol/session.go index ccc1c0a566..5d87e56924 100644 --- a/mieru/pkg/protocol/session.go +++ b/mieru/pkg/protocol/session.go @@ -38,9 +38,16 @@ import ( ) const ( - segmentTreeCapacity = 4096 + // Buffer some segments before blocking the underlay input loop. segmentChanCapacity = 1024 + // Maximum number of segments in a queue or buffer. + segmentTreeCapacity = 4096 + + // Use a very large value for receive queue, because application may be lazy + // and only read received segments on demand. + segmentTreeRecvQueueCapacity = 1024 * 1024 + minWindowSize = 16 maxWindowSize = segmentTreeCapacity @@ -162,7 +169,7 @@ func NewSession(id uint32, isClient bool, mtu int, users map[string]*appctlpb.Us sendQueue: newSegmentTree(segmentTreeCapacity), sendBuf: newSegmentTree(segmentTreeCapacity), recvBuf: newSegmentTree(segmentTreeCapacity), - recvQueue: newSegmentTree(segmentTreeCapacity), + recvQueue: newSegmentTree(segmentTreeRecvQueueCapacity), recvChan: make(chan *segment, segmentChanCapacity), lastRXTime: time.Now(), lastTXTime: time.Now(), @@ -725,6 +732,11 @@ func (s *Session) runOutputOncePacket() { if s.sendQueue.Len() > 0 { s.oLock.Lock() for { + if s.sendBuf.Remaining() <= 1 { + s.oLock.Unlock() + break + } + seg, deleted := s.sendQueue.DeleteMinIf(func(iter *segment) bool { return s.sendAlgorithm.CanSend(bytesInFlight, int64(packetOverhead+len(iter.payload))) }) @@ -890,8 +902,15 @@ func (s *Session) inputData(seg *segment) error { switch s.conn.TransportProtocol() { case common.StreamTransport: // Deliver the segment directly to recvQueue. + for { + if s.recvQueue.Remaining() <= 1 { + time.Sleep(backPressureDelay) + } else { + break + } + } if !s.recvQueue.Insert(seg) { - return fmt.Errorf("insert %v to receive queue failed", seg) + return fmt.Errorf("inputData() failed: insert %v to receive queue failed", seg) } case common.PacketTransport: // Delete all previous acknowledged segments from sendBuf. @@ -928,12 +947,23 @@ func (s *Session) inputData(seg *segment) error { } // Deliver the segment to recvBuf. + for { + if s.recvBuf.Remaining() <= 1 { + time.Sleep(backPressureDelay) + } else { + break + } + } if !s.recvBuf.Insert(seg) { - return fmt.Errorf("insert %v to receive buffer failed", seg) + return fmt.Errorf("inputData() failed: insert %v to receive buffer failed", seg) } // Move recvBuf to recvQueue. for { + if s.recvQueue.Remaining() <= 1 { + break + } + seg3, deleted := s.recvBuf.DeleteMinIf(func(iter *segment) bool { seq, _ := iter.Seq() return seq <= s.nextRecv @@ -944,7 +974,7 @@ func (s *Session) inputData(seg *segment) error { seq, _ := seg3.Seq() if seq == s.nextRecv { if !s.recvQueue.Insert(seg3) { - return fmt.Errorf("insert %v to receive queue failed", seg3) + return fmt.Errorf("inputData() failed: insert %v to receive queue failed", seg3) } s.nextRecv++ das, ok := seg3.metadata.(*dataAckStruct) @@ -992,7 +1022,7 @@ func (s *Session) inputData(seg *segment) error { } if !s.sendQueue.Insert(seg4) { s.oLock.Unlock() - return fmt.Errorf("insert %v to send queue failed", seg4) + return fmt.Errorf("inputData() failed: insert %v to send queue failed", seg4) } else { s.oLock.Unlock() s.forwardStateTo(sessionEstablished) diff --git a/mieru/pkg/version/current.go b/mieru/pkg/version/current.go index d015b427f4..1d6cafae6b 100644 --- a/mieru/pkg/version/current.go +++ b/mieru/pkg/version/current.go @@ -16,5 +16,5 @@ package version const ( - AppVersion = "3.18.0" + AppVersion = "3.19.0" ) diff --git a/mihomo/adapter/outbound/vless.go b/mihomo/adapter/outbound/vless.go index b26ddb07f2..3db99e1feb 100644 --- a/mihomo/adapter/outbound/vless.go +++ b/mihomo/adapter/outbound/vless.go @@ -3,7 +3,6 @@ package outbound import ( "context" "crypto/tls" - "errors" "fmt" "net" "net/http" @@ -457,11 +456,6 @@ func NewVless(option VlessOption) (*Vless, error) { if err != nil { return nil, err } - if v.encryption != nil { - if option.Flow != "" { - return nil, errors.New(`vless "encryption" doesn't support "flow" yet`) - } - } v.realityConfig, err = v.option.RealityOpts.Parse() if err != nil { diff --git a/mihomo/go.mod b/mihomo/go.mod index 66c1556fc5..1845c53242 100644 --- a/mihomo/go.mod +++ b/mihomo/go.mod @@ -31,7 +31,7 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.6 github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 github.com/metacubex/sing-tun v0.4.7 - github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d + github.com/metacubex/sing-vmess v0.2.4-0.20250817075824-5e05f123ccdd github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/smux v0.0.0-20250503055512-501391591dee github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 diff --git a/mihomo/go.sum b/mihomo/go.sum index 3c2d3e3fb4..90fcddcc18 100644 --- a/mihomo/go.sum +++ b/mihomo/go.sum @@ -131,8 +131,8 @@ github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MY github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E= github.com/metacubex/sing-tun v0.4.7 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778= github.com/metacubex/sing-tun v0.4.7/go.mod h1:xHecZRwBnKWe6zG9amAK9cXf91lF6blgjBqm+VvOrmU= -github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d h1:koSVAQiYqU/qtJ527e+wbsEPvTaM39HW75LSvh04IT0= -github.com/metacubex/sing-vmess v0.2.4-0.20250731011226-ea28d589924d/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= +github.com/metacubex/sing-vmess v0.2.4-0.20250817075824-5e05f123ccdd h1:VfD6UxKPAg7u9rPyxl18lQkpE9s8dZq0u2cPAgQShWs= +github.com/metacubex/sing-vmess v0.2.4-0.20250817075824-5e05f123ccdd/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80= github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113awp7P6odM2okB5s60HUyF0FMqKmo= diff --git a/mihomo/listener/inbound/vless_test.go b/mihomo/listener/inbound/vless_test.go index e344aa2e88..c110228db3 100644 --- a/mihomo/listener/inbound/vless_test.go +++ b/mihomo/listener/inbound/vless_test.go @@ -102,6 +102,11 @@ func TestInboundVless_Encryption(t *testing.T) { Encryption: "8min-vless-mlkem768client-" + clientBase64, } testInboundVless(t, inboundOptions, outboundOptions) + t.Run("xtls-rprx-vision", func(t *testing.T) { + outboundOptions := outboundOptions + outboundOptions.Flow = "xtls-rprx-vision" + testInboundVless(t, inboundOptions, outboundOptions) + }) }) t.Run("-xored-", func(t *testing.T) { inboundOptions := inbound.VlessOption{ diff --git a/mihomo/listener/sing_vless/server.go b/mihomo/listener/sing_vless/server.go index 0e729cab99..d386ef670b 100644 --- a/mihomo/listener/sing_vless/server.go +++ b/mihomo/listener/sing_vless/server.go @@ -43,6 +43,22 @@ func init() { } return true, tlsConn.NetConn(), reflect.TypeOf(tlsConn.Conn).Elem(), unsafe.Pointer(tlsConn.Conn) }) + + vless.RegisterTLS(func(conn net.Conn) (loaded bool, netConn net.Conn, reflectType reflect.Type, reflectPointer unsafe.Pointer) { + tlsConn, loaded := common.Cast[*encryption.ClientConn](conn) + if !loaded { + return + } + return true, tlsConn.Conn, reflect.TypeOf(tlsConn).Elem(), unsafe.Pointer(tlsConn) + }) + + vless.RegisterTLS(func(conn net.Conn) (loaded bool, netConn net.Conn, reflectType reflect.Type, reflectPointer unsafe.Pointer) { + tlsConn, loaded := common.Cast[*encryption.ServerConn](conn) + if !loaded { + return + } + return true, tlsConn.Conn, reflect.TypeOf(tlsConn).Elem(), unsafe.Pointer(tlsConn) + }) } type Listener struct { diff --git a/mihomo/transport/vless/vision/vision.go b/mihomo/transport/vless/vision/vision.go index 570a569c34..32634f0ce8 100644 --- a/mihomo/transport/vless/vision/vision.go +++ b/mihomo/transport/vless/vision/vision.go @@ -12,6 +12,7 @@ import ( N "github.com/metacubex/mihomo/common/net" tlsC "github.com/metacubex/mihomo/component/tls" + "github.com/metacubex/mihomo/transport/vless/encryption" "github.com/gofrs/uuid/v5" "github.com/metacubex/sing/common" @@ -58,13 +59,27 @@ func NewConn(conn connWithUpstream, userUUID *uuid.UUID) (*Conn, error) { t = reflect.TypeOf(underlying.Conn).Elem() //log.Debugln("t:%v", t) p = unsafe.Pointer(underlying.Conn) + case *encryption.ClientConn: + //log.Debugln("type *encryption.ClientConn") + c.Conn = underlying.Conn + c.tlsConn = underlying + t = reflect.TypeOf(underlying).Elem() + p = unsafe.Pointer(underlying) + case *encryption.ServerConn: + //log.Debugln("type *encryption.ServerConn") + c.Conn = underlying.Conn + c.tlsConn = underlying + t = reflect.TypeOf(underlying).Elem() + p = unsafe.Pointer(underlying) default: return nil, fmt.Errorf(`failed to use vision, maybe "security" is not "tls" or "utls"`) } - i, _ := t.FieldByName("input") - r, _ := t.FieldByName("rawInput") - c.input = (*bytes.Reader)(unsafe.Add(p, i.Offset)) - c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset)) + if i, ok := t.FieldByName("input"); ok { + c.input = (*bytes.Reader)(unsafe.Add(p, i.Offset)) + } + if r, ok := t.FieldByName("rawInput"); ok { + c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset)) + } return c, nil } diff --git a/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/app.sh b/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/app.sh index 9a39ed75f7..110b3e75a4 100755 --- a/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/app.sh +++ b/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/app.sh @@ -389,6 +389,15 @@ eval_cache_var() { [ -s "$TMP_PATH/var" ] && eval $(cat "$TMP_PATH/var") } +has_1_65535() { + local val="$1" + val=${val//:/-} + case ",$val," in + *,1-65535,*) return 0 ;; + *) return 1 ;; + esac +} + run_ipt2socks() { local flag proto tcp_tproxy local_port socks_address socks_port socks_username socks_password log_file local _extra_param="" @@ -1836,7 +1845,7 @@ acl_app() { } tcp_no_redir_ports=${tcp_no_redir_ports:-${TCP_NO_REDIR_PORTS}} udp_no_redir_ports=${udp_no_redir_ports:-${UDP_NO_REDIR_PORTS}} - if [ "$tcp_no_redir_ports" == "1:65535" ] && [ "$udp_no_redir_ports" == "1:65535" ]; then + if has_1_65535 "$tcp_no_redir_ports" && has_1_65535 "$udp_no_redir_ports"; then unset use_global_config unset tcp_node unset udp_node @@ -2030,7 +2039,7 @@ acl_app() { else echolog " - 全局节点未启用,跳过【${remarks}】" fi - elif [ "$udp_node" = "tcp" ]; then + elif [ "$udp_node" = "tcp" ] || [ "$udp_node" = "$tcp_node" ]; then udp_node=$(get_cache_var "ACL_${sid}_tcp_node") udp_port=$(get_cache_var "ACL_${sid}_tcp_redir_port") set_cache_var "ACL_${sid}_udp_node" "${udp_node}" diff --git a/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/iptables.sh b/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/iptables.sh index 406180ba57..4208a9b75e 100755 --- a/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/iptables.sh +++ b/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/iptables.sh @@ -341,7 +341,7 @@ load_acl() { [ -n "${is_tproxy}" ] && ipt_tmp=$ipt_m [ "$tcp_no_redir_ports" != "disable" ] && { - if [ "$tcp_no_redir_ports" != "1:65535" ]; then + if ! has_1_65535 "$tcp_no_redir_ports"; then [ "$_ipv4" != "1" ] && add_port_rules "$ip6t_m -A PSW $(comment "$remarks") ${_ipt_source} -p tcp" $tcp_no_redir_ports "-j RETURN" 2>/dev/null add_port_rules "$ipt_tmp -A PSW $(comment "$remarks") ${_ipt_source} -p tcp" $tcp_no_redir_ports "-j RETURN" echolog " - ${msg}不代理 TCP 端口[${tcp_no_redir_ports}]" @@ -353,7 +353,7 @@ load_acl() { } [ "$udp_no_redir_ports" != "disable" ] && { - if [ "$udp_no_redir_ports" != "1:65535" ]; then + if ! has_1_65535 "$udp_no_redir_ports"; then [ "$_ipv4" != "1" ] && add_port_rules "$ip6t_m -A PSW $(comment "$remarks") ${_ipt_source} -p udp" $udp_no_redir_ports "-j RETURN" 2>/dev/null add_port_rules "$ipt_m -A PSW $(comment "$remarks") ${_ipt_source} -p udp" $udp_no_redir_ports "-j RETURN" echolog " - ${msg}不代理 UDP 端口[${udp_no_redir_ports}]" @@ -552,7 +552,7 @@ load_acl() { [ "$TCP_NO_REDIR_PORTS" != "disable" ] && { add_port_rules "$ip6t_m -A PSW $(comment "默认") -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN" add_port_rules "$ipt_tmp -A PSW $(comment "默认") -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN" - if [ "$TCP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]" else unset TCP_PROXY_MODE @@ -563,7 +563,7 @@ load_acl() { [ "$UDP_NO_REDIR_PORTS" != "disable" ] && { add_port_rules "$ip6t_m -A PSW $(comment "默认") -p udp" $UDP_NO_REDIR_PORTS "-j RETURN" add_port_rules "$ipt_m -A PSW $(comment "默认") -p udp" $UDP_NO_REDIR_PORTS "-j RETURN" - if [ "$UDP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]" else unset UDP_PROXY_MODE @@ -1100,7 +1100,7 @@ add_firewall_rule() { [ "$TCP_NO_REDIR_PORTS" != "disable" ] && { add_port_rules "$ipt_tmp -A PSW_OUTPUT -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN" add_port_rules "$ip6t_m -A PSW_OUTPUT -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN" - if [ "$TCP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]" else unset LOCALHOST_TCP_PROXY_MODE @@ -1111,7 +1111,7 @@ add_firewall_rule() { [ "$UDP_NO_REDIR_PORTS" != "disable" ] && { add_port_rules "$ipt_m -A PSW_OUTPUT -p udp" $UDP_NO_REDIR_PORTS "-j RETURN" add_port_rules "$ip6t_m -A PSW_OUTPUT -p udp" $UDP_NO_REDIR_PORTS "-j RETURN" - if [ "$UDP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]" else unset LOCALHOST_UDP_PROXY_MODE diff --git a/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/nftables.sh b/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/nftables.sh index 14ebd2043a..01c35afe5c 100755 --- a/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/nftables.sh +++ b/openwrt-passwall/luci-app-passwall/root/usr/share/passwall/nftables.sh @@ -356,7 +356,7 @@ load_acl() { msg="【$remarks】,${msg}" [ "$tcp_no_redir_ports" != "disable" ] && { - if [ "$tcp_no_redir_ports" != "1:65535" ]; then + if ! has_1_65535 "$tcp_no_redir_ports"; then nft "add rule $NFTABLE_NAME $nft_prerouting_chain ${_ipt_source} ip protocol tcp $(factor $tcp_no_redir_ports "tcp dport") counter return comment \"$remarks\"" [ "$_ipv4" != "1" ] && nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 ${_ipt_source} meta l4proto tcp $(factor $tcp_no_redir_ports "tcp dport") counter return comment \"$remarks\"" echolog " - ${msg}不代理 TCP 端口[${tcp_no_redir_ports}]" @@ -368,7 +368,7 @@ load_acl() { } [ "$udp_no_redir_ports" != "disable" ] && { - if [ "$udp_no_redir_ports" != "1:65535" ]; then + if ! has_1_65535 "$udp_no_redir_ports"; then nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol udp ${_ipt_source} $(factor $udp_no_redir_ports "udp dport") counter return comment \"$remarks\"" [ "$_ipv4" != "1" ] && nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto udp ${_ipt_source} $(factor $udp_no_redir_ports "udp dport") counter return comment \"$remarks\"" 2>/dev/null echolog " - ${msg}不代理 UDP 端口[${udp_no_redir_ports}]" @@ -571,7 +571,7 @@ load_acl() { [ "$TCP_NO_REDIR_PORTS" != "disable" ] && { nft "add rule $NFTABLE_NAME $nft_prerouting_chain ip protocol tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return comment \"默认\"" nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return comment \"默认\"" - if [ "$TCP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]" else unset TCP_PROXY_MODE @@ -582,7 +582,7 @@ load_acl() { [ "$UDP_NO_REDIR_PORTS" != "disable" ] && { nft "add $NFTABLE_NAME PSW_MANGLE ip protocol udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return comment \"默认\"" nft "add $NFTABLE_NAME PSW_MANGLE_V6 counter meta l4proto udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return comment \"默认\"" - if [ "$UDP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]" else unset UDP_PROXY_MODE @@ -1125,7 +1125,7 @@ add_firewall_rule() { [ "$TCP_NO_REDIR_PORTS" != "disable" ] && { nft "add rule $NFTABLE_NAME $nft_output_chain ip protocol tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return" nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta l4proto tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return" - if [ "$TCP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]" else unset LOCALHOST_TCP_PROXY_MODE @@ -1136,7 +1136,7 @@ add_firewall_rule() { [ "$UDP_NO_REDIR_PORTS" != "disable" ] && { nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip protocol udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return" nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta l4proto udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return" - if [ "$UDP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]" else unset LOCALHOST_UDP_PROXY_MODE diff --git a/shadowsocks-rust/Cargo.lock b/shadowsocks-rust/Cargo.lock index 025e1e74ca..55379a695b 100644 --- a/shadowsocks-rust/Cargo.lock +++ b/shadowsocks-rust/Cargo.lock @@ -356,9 +356,9 @@ dependencies = [ [[package]] name = "brotli" -version = "8.0.1" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -579,9 +579,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.44" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1f056bae57e3e54c3375c41ff79619ddd13460a17d7438712bd0d83fda4ff8" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", ] @@ -800,7 +800,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" dependencies = [ - "thiserror 2.0.14", + "thiserror 2.0.15", ] [[package]] @@ -1403,7 +1403,7 @@ dependencies = [ "ring", "rustls", "serde", - "thiserror 2.0.14", + "thiserror 2.0.15", "tinyvec", "tokio", "tokio-rustls", @@ -1431,7 +1431,7 @@ dependencies = [ "rustls", "serde", "smallvec", - "thiserror 2.0.14", + "thiserror 2.0.15", "tokio", "tokio-rustls", "tracing", @@ -2449,7 +2449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", - "thiserror 2.0.14", + "thiserror 2.0.15", "ucd-trie", ] @@ -2668,7 +2668,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.5.10", - "thiserror 2.0.14", + "thiserror 2.0.15", "tokio", "tracing", "web-time", @@ -2689,7 +2689,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.14", + "thiserror 2.0.15", "tinyvec", "tracing", "web-time", @@ -2806,7 +2806,7 @@ checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.14", + "thiserror 2.0.15", ] [[package]] @@ -2855,9 +2855,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64", "bytes", @@ -3320,7 +3320,7 @@ dependencies = [ "shadowsocks-crypto", "socket2 0.6.0", "spin", - "thiserror 2.0.14", + "thiserror 2.0.15", "tokio", "tokio-tfo", "trait-variant", @@ -3387,7 +3387,7 @@ dependencies = [ "snmalloc-rs", "sysexits", "tcmalloc", - "thiserror 2.0.14", + "thiserror 2.0.15", "time", "tokio", "tracing", @@ -3436,7 +3436,7 @@ dependencies = [ "smoltcp", "socket2 0.6.0", "spin", - "thiserror 2.0.14", + "thiserror 2.0.15", "tokio", "tokio-native-tls", "tokio-rustls", @@ -3488,9 +3488,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "sm4" @@ -3727,11 +3727,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.14" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" +checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" dependencies = [ - "thiserror-impl 2.0.14", + "thiserror-impl 2.0.15", ] [[package]] @@ -3747,9 +3747,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.14" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" +checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" dependencies = [ "proc-macro2", "quote", @@ -4053,7 +4053,7 @@ dependencies = [ "libc", "log", "nix", - "thiserror 2.0.14", + "thiserror 2.0.15", "tokio", "tokio-util", "windows-sys 0.60.2", @@ -4737,7 +4737,7 @@ dependencies = [ "futures", "libloading", "log", - "thiserror 2.0.14", + "thiserror 2.0.15", "windows-sys 0.60.2", "winreg 0.55.0", ] diff --git a/shadowsocks-rust/Dockerfile b/shadowsocks-rust/Dockerfile index 6459b5130b..182915bea3 100644 --- a/shadowsocks-rust/Dockerfile +++ b/shadowsocks-rust/Dockerfile @@ -1,9 +1,9 @@ -FROM --platform=$BUILDPLATFORM rust:alpine3.20 AS builder +FROM --platform=$BUILDPLATFORM rust:1.89.0-alpine3.22 AS builder ARG TARGETARCH RUN set -x \ - && apk add --no-cache build-base cmake llvm15-dev clang15-libclang clang15 rust-bindgen + && apk add --no-cache musl-dev WORKDIR /root/shadowsocks-rust @@ -36,7 +36,6 @@ RUN case "$TARGETARCH" in \ && PATH="/root/$MUSL-cross/bin:$PATH" \ && CC=/root/$MUSL-cross/bin/$MUSL-gcc \ && echo "CC=$CC" \ - && rustup override set stable \ && rustup target add "$RUST_TARGET" \ && RUSTFLAGS="-C linker=$CC" CC=$CC cargo build --target "$RUST_TARGET" --release --features "full" \ && mv target/$RUST_TARGET/release/ss* target/release/ diff --git a/small/luci-app-passwall/root/usr/share/passwall/app.sh b/small/luci-app-passwall/root/usr/share/passwall/app.sh index 9a39ed75f7..110b3e75a4 100755 --- a/small/luci-app-passwall/root/usr/share/passwall/app.sh +++ b/small/luci-app-passwall/root/usr/share/passwall/app.sh @@ -389,6 +389,15 @@ eval_cache_var() { [ -s "$TMP_PATH/var" ] && eval $(cat "$TMP_PATH/var") } +has_1_65535() { + local val="$1" + val=${val//:/-} + case ",$val," in + *,1-65535,*) return 0 ;; + *) return 1 ;; + esac +} + run_ipt2socks() { local flag proto tcp_tproxy local_port socks_address socks_port socks_username socks_password log_file local _extra_param="" @@ -1836,7 +1845,7 @@ acl_app() { } tcp_no_redir_ports=${tcp_no_redir_ports:-${TCP_NO_REDIR_PORTS}} udp_no_redir_ports=${udp_no_redir_ports:-${UDP_NO_REDIR_PORTS}} - if [ "$tcp_no_redir_ports" == "1:65535" ] && [ "$udp_no_redir_ports" == "1:65535" ]; then + if has_1_65535 "$tcp_no_redir_ports" && has_1_65535 "$udp_no_redir_ports"; then unset use_global_config unset tcp_node unset udp_node @@ -2030,7 +2039,7 @@ acl_app() { else echolog " - 全局节点未启用,跳过【${remarks}】" fi - elif [ "$udp_node" = "tcp" ]; then + elif [ "$udp_node" = "tcp" ] || [ "$udp_node" = "$tcp_node" ]; then udp_node=$(get_cache_var "ACL_${sid}_tcp_node") udp_port=$(get_cache_var "ACL_${sid}_tcp_redir_port") set_cache_var "ACL_${sid}_udp_node" "${udp_node}" diff --git a/small/luci-app-passwall/root/usr/share/passwall/iptables.sh b/small/luci-app-passwall/root/usr/share/passwall/iptables.sh index 406180ba57..4208a9b75e 100755 --- a/small/luci-app-passwall/root/usr/share/passwall/iptables.sh +++ b/small/luci-app-passwall/root/usr/share/passwall/iptables.sh @@ -341,7 +341,7 @@ load_acl() { [ -n "${is_tproxy}" ] && ipt_tmp=$ipt_m [ "$tcp_no_redir_ports" != "disable" ] && { - if [ "$tcp_no_redir_ports" != "1:65535" ]; then + if ! has_1_65535 "$tcp_no_redir_ports"; then [ "$_ipv4" != "1" ] && add_port_rules "$ip6t_m -A PSW $(comment "$remarks") ${_ipt_source} -p tcp" $tcp_no_redir_ports "-j RETURN" 2>/dev/null add_port_rules "$ipt_tmp -A PSW $(comment "$remarks") ${_ipt_source} -p tcp" $tcp_no_redir_ports "-j RETURN" echolog " - ${msg}不代理 TCP 端口[${tcp_no_redir_ports}]" @@ -353,7 +353,7 @@ load_acl() { } [ "$udp_no_redir_ports" != "disable" ] && { - if [ "$udp_no_redir_ports" != "1:65535" ]; then + if ! has_1_65535 "$udp_no_redir_ports"; then [ "$_ipv4" != "1" ] && add_port_rules "$ip6t_m -A PSW $(comment "$remarks") ${_ipt_source} -p udp" $udp_no_redir_ports "-j RETURN" 2>/dev/null add_port_rules "$ipt_m -A PSW $(comment "$remarks") ${_ipt_source} -p udp" $udp_no_redir_ports "-j RETURN" echolog " - ${msg}不代理 UDP 端口[${udp_no_redir_ports}]" @@ -552,7 +552,7 @@ load_acl() { [ "$TCP_NO_REDIR_PORTS" != "disable" ] && { add_port_rules "$ip6t_m -A PSW $(comment "默认") -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN" add_port_rules "$ipt_tmp -A PSW $(comment "默认") -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN" - if [ "$TCP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]" else unset TCP_PROXY_MODE @@ -563,7 +563,7 @@ load_acl() { [ "$UDP_NO_REDIR_PORTS" != "disable" ] && { add_port_rules "$ip6t_m -A PSW $(comment "默认") -p udp" $UDP_NO_REDIR_PORTS "-j RETURN" add_port_rules "$ipt_m -A PSW $(comment "默认") -p udp" $UDP_NO_REDIR_PORTS "-j RETURN" - if [ "$UDP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]" else unset UDP_PROXY_MODE @@ -1100,7 +1100,7 @@ add_firewall_rule() { [ "$TCP_NO_REDIR_PORTS" != "disable" ] && { add_port_rules "$ipt_tmp -A PSW_OUTPUT -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN" add_port_rules "$ip6t_m -A PSW_OUTPUT -p tcp" $TCP_NO_REDIR_PORTS "-j RETURN" - if [ "$TCP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]" else unset LOCALHOST_TCP_PROXY_MODE @@ -1111,7 +1111,7 @@ add_firewall_rule() { [ "$UDP_NO_REDIR_PORTS" != "disable" ] && { add_port_rules "$ipt_m -A PSW_OUTPUT -p udp" $UDP_NO_REDIR_PORTS "-j RETURN" add_port_rules "$ip6t_m -A PSW_OUTPUT -p udp" $UDP_NO_REDIR_PORTS "-j RETURN" - if [ "$UDP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]" else unset LOCALHOST_UDP_PROXY_MODE diff --git a/small/luci-app-passwall/root/usr/share/passwall/nftables.sh b/small/luci-app-passwall/root/usr/share/passwall/nftables.sh index 14ebd2043a..01c35afe5c 100755 --- a/small/luci-app-passwall/root/usr/share/passwall/nftables.sh +++ b/small/luci-app-passwall/root/usr/share/passwall/nftables.sh @@ -356,7 +356,7 @@ load_acl() { msg="【$remarks】,${msg}" [ "$tcp_no_redir_ports" != "disable" ] && { - if [ "$tcp_no_redir_ports" != "1:65535" ]; then + if ! has_1_65535 "$tcp_no_redir_ports"; then nft "add rule $NFTABLE_NAME $nft_prerouting_chain ${_ipt_source} ip protocol tcp $(factor $tcp_no_redir_ports "tcp dport") counter return comment \"$remarks\"" [ "$_ipv4" != "1" ] && nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 ${_ipt_source} meta l4proto tcp $(factor $tcp_no_redir_ports "tcp dport") counter return comment \"$remarks\"" echolog " - ${msg}不代理 TCP 端口[${tcp_no_redir_ports}]" @@ -368,7 +368,7 @@ load_acl() { } [ "$udp_no_redir_ports" != "disable" ] && { - if [ "$udp_no_redir_ports" != "1:65535" ]; then + if ! has_1_65535 "$udp_no_redir_ports"; then nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol udp ${_ipt_source} $(factor $udp_no_redir_ports "udp dport") counter return comment \"$remarks\"" [ "$_ipv4" != "1" ] && nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto udp ${_ipt_source} $(factor $udp_no_redir_ports "udp dport") counter return comment \"$remarks\"" 2>/dev/null echolog " - ${msg}不代理 UDP 端口[${udp_no_redir_ports}]" @@ -571,7 +571,7 @@ load_acl() { [ "$TCP_NO_REDIR_PORTS" != "disable" ] && { nft "add rule $NFTABLE_NAME $nft_prerouting_chain ip protocol tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return comment \"默认\"" nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return comment \"默认\"" - if [ "$TCP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]" else unset TCP_PROXY_MODE @@ -582,7 +582,7 @@ load_acl() { [ "$UDP_NO_REDIR_PORTS" != "disable" ] && { nft "add $NFTABLE_NAME PSW_MANGLE ip protocol udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return comment \"默认\"" nft "add $NFTABLE_NAME PSW_MANGLE_V6 counter meta l4proto udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return comment \"默认\"" - if [ "$UDP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]" else unset UDP_PROXY_MODE @@ -1125,7 +1125,7 @@ add_firewall_rule() { [ "$TCP_NO_REDIR_PORTS" != "disable" ] && { nft "add rule $NFTABLE_NAME $nft_output_chain ip protocol tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return" nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta l4proto tcp $(factor $TCP_NO_REDIR_PORTS "tcp dport") counter return" - if [ "$TCP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$TCP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 TCP 端口[${TCP_NO_REDIR_PORTS}]" else unset LOCALHOST_TCP_PROXY_MODE @@ -1136,7 +1136,7 @@ add_firewall_rule() { [ "$UDP_NO_REDIR_PORTS" != "disable" ] && { nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip protocol udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return" nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta l4proto udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return" - if [ "$UDP_NO_REDIR_PORTS" != "1:65535" ]; then + if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then echolog " - ${msg}不代理 UDP 端口[${UDP_NO_REDIR_PORTS}]" else unset LOCALHOST_UDP_PROXY_MODE diff --git a/v2rayn/v2rayN/AmazTool/UpgradeApp.cs b/v2rayn/v2rayN/AmazTool/UpgradeApp.cs index a4eb288c53..caa269f4c3 100644 --- a/v2rayn/v2rayN/AmazTool/UpgradeApp.cs +++ b/v2rayn/v2rayN/AmazTool/UpgradeApp.cs @@ -79,15 +79,7 @@ internal class UpgradeApp continue; } - try - { - entry.ExtractToFile(entryOutputPath, true); - } - catch - { - Thread.Sleep(1000); - entry.ExtractToFile(entryOutputPath, true); - } + TryExtractToFile(entry, entryOutputPath); Console.WriteLine(entryOutputPath); } @@ -113,4 +105,24 @@ internal class UpgradeApp Utils.StartV2RayN(); } + + private static bool TryExtractToFile(ZipArchiveEntry entry, string outputPath) + { + var retryCount = 5; + var delayMs = 1000; + + for (var i = 1; i <= retryCount; i++) + { + try + { + entry.ExtractToFile(outputPath, true); + return true; + } + catch + { + Thread.Sleep(delayMs * i); + } + } + return false; + } } diff --git a/v2rayn/v2rayN/Directory.Build.props b/v2rayn/v2rayN/Directory.Build.props index 001b32fee1..c7d69e206d 100644 --- a/v2rayn/v2rayN/Directory.Build.props +++ b/v2rayn/v2rayN/Directory.Build.props @@ -1,7 +1,7 @@ - 7.14.0 + 7.14.1 diff --git a/v2rayn/v2rayN/ServiceLib/Common/QRCodeHelper.cs b/v2rayn/v2rayN/ServiceLib/Common/QRCodeUtils.cs similarity index 98% rename from v2rayn/v2rayN/ServiceLib/Common/QRCodeHelper.cs rename to v2rayn/v2rayN/ServiceLib/Common/QRCodeUtils.cs index 9938eadd1b..3d3dc90b13 100644 --- a/v2rayn/v2rayN/ServiceLib/Common/QRCodeHelper.cs +++ b/v2rayn/v2rayN/ServiceLib/Common/QRCodeUtils.cs @@ -4,7 +4,7 @@ using ZXing.SkiaSharp; namespace ServiceLib.Common; -public class QRCodeHelper +public class QRCodeUtils { public static byte[]? GenQRCode(string? url) { diff --git a/v2rayn/v2rayN/ServiceLib/Global.cs b/v2rayn/v2rayN/ServiceLib/Global.cs index e3be7004c4..615c0d3820 100644 --- a/v2rayn/v2rayN/ServiceLib/Global.cs +++ b/v2rayn/v2rayN/ServiceLib/Global.cs @@ -305,13 +305,6 @@ public class Global "" ]; - public static readonly List DomainMatchers = - [ - "linear", - "mph", - "" - ]; - public static readonly List Fingerprints = [ "chrome", diff --git a/v2rayn/v2rayN/ServiceLib/GlobalUsings.cs b/v2rayn/v2rayN/ServiceLib/GlobalUsings.cs index a4bf3ccde5..9a78c73b4a 100644 --- a/v2rayn/v2rayN/ServiceLib/GlobalUsings.cs +++ b/v2rayn/v2rayN/ServiceLib/GlobalUsings.cs @@ -1,11 +1,13 @@ -global using ServiceLib.Base; +global using ServiceLib.Base; global using ServiceLib.Common; global using ServiceLib.Enums; global using ServiceLib.Handler; +global using ServiceLib.Helper; +global using ServiceLib.Manager; global using ServiceLib.Handler.Fmt; global using ServiceLib.Services; global using ServiceLib.Services.Statistics; global using ServiceLib.Services.CoreConfig; global using ServiceLib.Models; global using ServiceLib.Resx; -global using ServiceLib.Handler.SysProxy; \ No newline at end of file +global using ServiceLib.Handler.SysProxy; diff --git a/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 8450cba8a7..9df5f6b45d 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -3,7 +3,7 @@ using System.Text.RegularExpressions; namespace ServiceLib.Handler; -public class ConfigHandler +public static class ConfigHandler { private static readonly string _configRes = Global.ConfigFileName; private static readonly string _tag = "ConfigHandler"; @@ -216,7 +216,7 @@ public class ConfigHandler /// Result of the operation (0 if successful, -1 if failed) public static async Task AddServer(Config config, ProfileItem profileItem) { - var item = await AppHandler.Instance.GetProfileItem(profileItem.IndexId); + var item = await AppManager.Instance.GetProfileItem(profileItem.IndexId); if (item is null) { item = profileItem; @@ -336,7 +336,7 @@ public class ConfigHandler { foreach (var it in indexes) { - var item = await AppHandler.Instance.GetProfileItem(it.IndexId); + var item = await AppManager.Instance.GetProfileItem(it.IndexId); if (item is null) { continue; @@ -418,7 +418,7 @@ public class ConfigHandler /// The default profile item or null if none exists public static async Task GetDefaultServer(Config config) { - var item = await AppHandler.Instance.GetProfileItem(config.IndexId); + var item = await AppManager.Instance.GetProfileItem(config.IndexId); if (item is null) { var item2 = await SQLiteHelper.Instance.TableAsync().FirstOrDefaultAsync(); @@ -449,7 +449,7 @@ public class ConfigHandler for (int i = 0; i < lstProfile.Count; i++) { - ProfileExHandler.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10); + ProfileExManager.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10); } var sort = 0; @@ -461,7 +461,7 @@ public class ConfigHandler { return 0; } - sort = ProfileExHandler.Instance.GetSort(lstProfile.First().IndexId) - 1; + sort = ProfileExManager.Instance.GetSort(lstProfile.First().IndexId) - 1; break; } @@ -471,7 +471,7 @@ public class ConfigHandler { return 0; } - sort = ProfileExHandler.Instance.GetSort(lstProfile[index - 1].IndexId) - 1; + sort = ProfileExManager.Instance.GetSort(lstProfile[index - 1].IndexId) - 1; break; } @@ -482,7 +482,7 @@ public class ConfigHandler { return 0; } - sort = ProfileExHandler.Instance.GetSort(lstProfile[index + 1].IndexId) + 1; + sort = ProfileExManager.Instance.GetSort(lstProfile[index + 1].IndexId) + 1; break; } @@ -492,7 +492,7 @@ public class ConfigHandler { return 0; } - sort = ProfileExHandler.Instance.GetSort(lstProfile[^1].IndexId) + 1; + sort = ProfileExManager.Instance.GetSort(lstProfile[^1].IndexId) + 1; break; } @@ -501,7 +501,7 @@ public class ConfigHandler break; } - ProfileExHandler.Instance.SetSort(lstProfile[index].IndexId, sort); + ProfileExManager.Instance.SetSort(lstProfile[index].IndexId, sort); return await Task.FromResult(0); } @@ -559,7 +559,7 @@ public class ConfigHandler /// 0 if successful, -1 if failed public static async Task EditCustomServer(Config config, ProfileItem profileItem) { - var item = await AppHandler.Instance.GetProfileItem(profileItem.IndexId); + var item = await AppManager.Instance.GetProfileItem(profileItem.IndexId); if (item is null) { item = profileItem; @@ -601,7 +601,7 @@ public class ConfigHandler profileItem.Id = profileItem.Id.TrimEx(); profileItem.Security = profileItem.Security.TrimEx(); - if (!AppHandler.Instance.GetShadowsocksSecurities(profileItem).Contains(profileItem.Security)) + if (!AppManager.Instance.GetShadowsocksSecurities(profileItem).Contains(profileItem.Security)) { return -1; } @@ -829,13 +829,13 @@ public class ConfigHandler /// 0 if successful, -1 if failed public static async Task SortServers(Config config, string subId, string colName, bool asc) { - var lstModel = await AppHandler.Instance.ProfileItems(subId, ""); + var lstModel = await AppManager.Instance.ProfileItems(subId, ""); if (lstModel.Count <= 0) { return -1; } - var lstServerStat = (config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? []; - var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs(); + var lstServerStat = (config.GuiItem.EnableStatistics ? StatisticsManager.Instance.ServerStat : null) ?? []; + var lstProfileExs = await ProfileExManager.Instance.GetProfileExs(); var lstProfile = (from t in lstModel join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b from t22 in t2b.DefaultIfEmpty() @@ -905,7 +905,7 @@ public class ConfigHandler for (var i = 0; i < lstProfile.Count; i++) { - ProfileExHandler.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10); + ProfileExManager.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10); } switch (name) { @@ -914,7 +914,7 @@ public class ConfigHandler var maxSort = lstProfile.Max(t => t.Sort) + 10; foreach (var item in lstProfile.Where(item => item.Delay <= 0)) { - ProfileExHandler.Instance.SetSort(item.IndexId, maxSort); + ProfileExManager.Instance.SetSort(item.IndexId, maxSort); } break; @@ -924,7 +924,7 @@ public class ConfigHandler var maxSort = lstProfile.Max(t => t.Sort) + 10; foreach (var item in lstProfile.Where(item => item.Speed <= 0)) { - ProfileExHandler.Instance.SetSort(item.IndexId, maxSort); + ProfileExManager.Instance.SetSort(item.IndexId, maxSort); } break; @@ -963,7 +963,7 @@ public class ConfigHandler { return -1; } - if (profileItem.Security.IsNotEmpty() && profileItem.Security != Global.None) + if (profileItem.Security.IsNullOrEmpty()) { profileItem.Security = Global.None; } @@ -982,7 +982,7 @@ public class ConfigHandler /// Tuple with total count and remaining count after deduplication public static async Task> DedupServerList(Config config, string subId) { - var lstProfile = await AppHandler.Instance.ProfileItems(subId); + var lstProfile = await AppManager.Instance.ProfileItems(subId); if (lstProfile == null) { return new Tuple(0, 0); @@ -1052,15 +1052,15 @@ public class ConfigHandler if (profileItem.IndexId.IsNullOrEmpty()) { profileItem.IndexId = Utils.GetGuid(false); - maxSort = ProfileExHandler.Instance.GetMaxSort(); + maxSort = ProfileExManager.Instance.GetMaxSort(); } if (!toFile && maxSort < 0) { - maxSort = ProfileExHandler.Instance.GetMaxSort(); + maxSort = ProfileExManager.Instance.GetMaxSort(); } if (maxSort > 0) { - ProfileExHandler.Instance.SetSort(profileItem.IndexId, maxSort + 1); + ProfileExManager.Instance.SetSort(profileItem.IndexId, maxSort + 1); } if (toFile) @@ -1120,7 +1120,7 @@ public class ConfigHandler { try { - var item = await AppHandler.Instance.GetProfileItem(indexId); + var item = await AppManager.Instance.GetProfileItem(indexId); if (item == null) { return 0; @@ -1165,7 +1165,7 @@ public class ConfigHandler return result; } - var profileItem = await AppHandler.Instance.GetProfileItem(indexId) ?? new(); + var profileItem = await AppManager.Instance.GetProfileItem(indexId) ?? new(); profileItem.IndexId = indexId; if (coreType == ECoreType.Xray) { @@ -1211,7 +1211,7 @@ public class ConfigHandler ConfigType = EConfigType.SOCKS, Address = Global.Loopback, Sni = node.Address, //Tun2SocksAddress - Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks) + Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks) }; } else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)) @@ -1238,12 +1238,12 @@ public class ConfigHandler /// Number of removed servers or -1 if failed public static async Task RemoveInvalidServerResult(Config config, string subid) { - var lstModel = await AppHandler.Instance.ProfileItems(subid, ""); + var lstModel = await AppManager.Instance.ProfileItems(subid, ""); if (lstModel is { Count: <= 0 }) { return -1; } - var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs(); + var lstProfileExs = await ProfileExManager.Instance.GetProfileExs(); var lstProfile = (from t in lstModel join t2 in lstProfileExs on t.IndexId equals t2.IndexId where t2.Delay == -1 @@ -1279,7 +1279,7 @@ public class ConfigHandler if (isSub && subid.IsNotEmpty()) { await RemoveServersViaSubid(config, subid, isSub); - subFilter = (await AppHandler.Instance.GetSubItem(subid))?.Filter ?? ""; + subFilter = (await AppManager.Instance.GetSubItem(subid))?.Filter ?? ""; } var countServers = 0; @@ -1363,7 +1363,7 @@ public class ConfigHandler return -1; } - var subItem = await AppHandler.Instance.GetSubItem(subid); + var subItem = await AppManager.Instance.GetSubItem(subid); var subRemarks = subItem?.Remarks; var preSocksPort = subItem?.PreSocksPort; @@ -1519,7 +1519,7 @@ public class ConfigHandler ProfileItem? activeProfile = null; if (isSub && subid.IsNotEmpty()) { - lstOriSub = await AppHandler.Instance.ProfileItems(subid); + lstOriSub = await AppManager.Instance.ProfileItems(subid); activeProfile = lstOriSub?.FirstOrDefault(t => t.IndexId == config.IndexId); } @@ -1551,7 +1551,7 @@ public class ConfigHandler //Select active node if (activeProfile != null) { - var lstSub = await AppHandler.Instance.ProfileItems(subid); + var lstSub = await AppManager.Instance.ProfileItems(subid); var existItem = lstSub?.FirstOrDefault(t => config.UiItem.EnableUpdateSubOnlyRemarksExist ? t.Remarks == activeProfile.Remarks : CompareProfileItem(t, activeProfile, true)); if (existItem != null) { @@ -1562,13 +1562,13 @@ public class ConfigHandler //Keep the last traffic statistics if (lstOriSub != null) { - var lstSub = await AppHandler.Instance.ProfileItems(subid); + var lstSub = await AppManager.Instance.ProfileItems(subid); foreach (var item in lstSub) { var existItem = lstOriSub?.FirstOrDefault(t => config.UiItem.EnableUpdateSubOnlyRemarksExist ? t.Remarks == item.Remarks : CompareProfileItem(t, item, true)); if (existItem != null) { - await StatisticsHandler.Instance.CloneServerStatItem(existItem.IndexId, item.IndexId); + await StatisticsManager.Instance.CloneServerStatItem(existItem.IndexId, item.IndexId); } } } @@ -1608,7 +1608,7 @@ public class ConfigHandler if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost)) { //TODO Temporary reminder to be removed later - NoticeHandler.Instance.Enqueue(ResUI.InsecureUrlProtocol); + NoticeManager.Instance.Enqueue(ResUI.InsecureUrlProtocol); //return -1; } @@ -1626,7 +1626,7 @@ public class ConfigHandler /// 0 if successful, -1 if failed public static async Task AddSubItem(Config config, SubItem subItem) { - var item = await AppHandler.Instance.GetSubItem(subItem.Id); + var item = await AppManager.Instance.GetSubItem(subItem.Id); if (item is null) { item = subItem; @@ -1658,7 +1658,7 @@ public class ConfigHandler var maxSort = 0; if (await SQLiteHelper.Instance.TableAsync().CountAsync() > 0) { - var lstSubs = (await AppHandler.Instance.SubItems()); + var lstSubs = (await AppManager.Instance.SubItems()); maxSort = lstSubs.LastOrDefault()?.Sort ?? 0; } item.Sort = maxSort + 1; @@ -1712,7 +1712,7 @@ public class ConfigHandler /// 0 if successful public static async Task DeleteSubItem(Config config, string id) { - var item = await AppHandler.Instance.GetSubItem(id); + var item = await AppManager.Instance.GetSubItem(id); if (item is null) { return 0; @@ -1896,7 +1896,7 @@ public class ConfigHandler /// 0 if successful public static async Task SetDefaultRouting(Config config, RoutingItem routingItem) { - var items = await AppHandler.Instance.RoutingItems(); + var items = await AppManager.Instance.RoutingItems(); if (items.Any(t => t.Id == routingItem.Id && t.IsActive == true)) { return -1; @@ -1976,7 +1976,7 @@ public class ConfigHandler if (template == null) return await InitBuiltinRouting(config, blImportAdvancedRules); // fallback - var items = await AppHandler.Instance.RoutingItems(); + var items = await AppManager.Instance.RoutingItems(); var maxSort = items.Count; if (!blImportAdvancedRules && items.Where(t => t.Remarks.StartsWith(template.Version)).ToList().Count > 0) { @@ -2023,14 +2023,14 @@ public class ConfigHandler public static async Task InitBuiltinRouting(Config config, bool blImportAdvancedRules = false) { var ver = "V3-"; - var items = await AppHandler.Instance.RoutingItems(); + var items = await AppManager.Instance.RoutingItems(); //TODO Temporary code to be removed later var lockItem = items?.FirstOrDefault(t => t.Locked == true); if (lockItem != null) { await ConfigHandler.RemoveRoutingItem(lockItem); - items = await AppHandler.Instance.RoutingItems(); + items = await AppManager.Instance.RoutingItems(); } if (!blImportAdvancedRules && items.Count > 0) @@ -2107,7 +2107,7 @@ public class ConfigHandler /// 0 if successful public static async Task InitBuiltinDNS(Config config) { - var items = await AppHandler.Instance.DNSItems(); + var items = await AppManager.Instance.DNSItems(); // Check existing DNS items and disable those with empty NormalDNS var needsUpdate = false; @@ -2185,7 +2185,7 @@ public class ConfigHandler /// DNS item with configuration from the URL public static async Task GetExternalDNSItem(ECoreType type, string url) { - var currentItem = await AppHandler.Instance.GetDNSItem(type); + var currentItem = await AppManager.Instance.GetDNSItem(type); var downloadHandle = new DownloadService(); var templateContent = await downloadHandle.TryDownloadString(url, true, ""); @@ -2247,7 +2247,7 @@ public class ConfigHandler public static async Task InitBuiltinFullConfigTemplate(Config config) { - var items = await AppHandler.Instance.FullConfigTemplateItem(); + var items = await AppManager.Instance.FullConfigTemplateItem(); if (items.Count <= 0) { var item = new FullConfigTemplateItem() diff --git a/v2rayn/v2rayN/ServiceLib/Handler/ConnectionHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/ConnectionHandler.cs index 3cfc602000..38f3ae516c 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/ConnectionHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/ConnectionHandler.cs @@ -1,27 +1,28 @@ +using System.Net; + namespace ServiceLib.Handler; -public class ConnectionHandler +public static class ConnectionHandler { - private static readonly Lazy _instance = new(() => new()); - public static ConnectionHandler Instance => _instance.Value; + private static readonly string _tag = "ConnectionHandler"; - public async Task RunAvailabilityCheck() + public static async Task RunAvailabilityCheck() { - var downloadHandle = new DownloadService(); - var time = await downloadHandle.RunAvailabilityCheck(null); - var ip = time > 0 ? await GetIPInfo(downloadHandle) ?? Global.None : Global.None; + var time = await GetRealPingTime(); + var ip = time > 0 ? await GetIPInfo() ?? Global.None : Global.None; return string.Format(ResUI.TestMeOutput, time, ip); } - private async Task GetIPInfo(DownloadService downloadHandle) + private static async Task GetIPInfo() { - var url = AppHandler.Instance.Config.SpeedTestItem.IPAPIUrl; + var url = AppManager.Instance.Config.SpeedTestItem.IPAPIUrl; if (url.IsNullOrEmpty()) { return null; } + var downloadHandle = new DownloadService(); var result = await downloadHandle.TryDownloadString(url, true, ""); if (result == null) { @@ -39,4 +40,31 @@ public class ConnectionHandler return $"({country ?? "unknown"}) {ip}"; } + + private static async Task GetRealPingTime() + { + var responseTime = -1; + try + { + var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks); + var webProxy = new WebProxy($"socks5://{Global.Loopback}:{port}"); + var url = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl; + + for (var i = 0; i < 2; i++) + { + responseTime = await HttpClientHelper.Instance.GetRealPingTime(url, webProxy, 10); + if (responseTime > 0) + { + break; + } + await Task.Delay(500); + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + return -1; + } + return responseTime; + } } diff --git a/v2rayn/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs index 40c5eacf10..66d261ac7b 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs @@ -3,13 +3,13 @@ namespace ServiceLib.Handler; /// /// Core configuration file processing class /// -public class CoreConfigHandler +public static class CoreConfigHandler { private static readonly string _tag = "CoreConfigHandler"; public static async Task GenerateClientConfig(ProfileItem node, string? fileName) { - var config = AppHandler.Instance.Config; + var config = AppManager.Instance.Config; var result = new RetResult(); if (node.ConfigType == EConfigType.Custom) @@ -21,7 +21,7 @@ public class CoreConfigHandler _ => await GenerateClientCustomConfig(node, fileName) }; } - else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box) + else if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box) { result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node); } @@ -112,11 +112,11 @@ public class CoreConfigHandler public static async Task GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName) { var result = new RetResult(); - var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest); + var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest); var port = Utils.GetFreePort(initPort + testItem.QueueNum); testItem.Port = port; - if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box) + if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box) { result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port); } diff --git a/v2rayn/v2rayN/ServiceLib/Handler/SubscriptionHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/SubscriptionHandler.cs index f93eb2fada..746cbafaeb 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/SubscriptionHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/SubscriptionHandler.cs @@ -1,11 +1,11 @@ namespace ServiceLib.Handler; -public class SubscriptionHandler +public static class SubscriptionHandler { public static async Task UpdateProcess(Config config, string subId, bool blProxy, Action updateFunc) { updateFunc?.Invoke(false, ResUI.MsgUpdateSubscriptionStart); - var subItem = await AppHandler.Instance.SubItems(); + var subItem = await AppManager.Instance.SubItems(); if (subItem is not { Count: > 0 }) { diff --git a/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs b/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs index 9c2be056f5..828c2102ff 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs @@ -1,6 +1,6 @@ namespace ServiceLib.Handler.SysProxy; -public class ProxySettingLinux +public static class ProxySettingLinux { private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh"; diff --git a/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs b/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs index 9d15839bbe..85d9b821b8 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs @@ -1,6 +1,6 @@ namespace ServiceLib.Handler.SysProxy; -public class ProxySettingOSX +public static class ProxySettingOSX { private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh"; diff --git a/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs b/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs index 79b860b018..44b7e04695 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs @@ -3,7 +3,7 @@ using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionO namespace ServiceLib.Handler.SysProxy; -public class ProxySettingWindows +public static class ProxySettingWindows { private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings"; diff --git a/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs index 01361a9225..38ea04ae29 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs @@ -15,7 +15,7 @@ public static class SysProxyHandler try { - var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks); + var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks); var exceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", ""); if (port <= 0) { @@ -56,7 +56,7 @@ public static class SysProxyHandler if (type != ESysProxyType.Pac && Utils.IsWindows()) { - PacHandler.Stop(); + PacManager.Instance.Stop(); } } catch (Exception ex) @@ -90,8 +90,8 @@ public static class SysProxyHandler private static async Task SetWindowsProxyPac(int port) { - var portPac = AppHandler.Instance.GetLocalPort(EInboundProtocol.pac); - await PacHandler.Start(Utils.GetConfigPath(), port, portPac); + var portPac = AppManager.Instance.GetLocalPort(EInboundProtocol.pac); + await PacManager.Instance.StartAsync(Utils.GetConfigPath(), port, portPac); var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}"; ProxySettingWindows.SetProxy(strProxy, "", 4); } diff --git a/v2rayn/v2rayN/ServiceLib/Common/DownloaderHelper.cs b/v2rayn/v2rayN/ServiceLib/Helper/DownloaderHelper.cs similarity index 99% rename from v2rayn/v2rayN/ServiceLib/Common/DownloaderHelper.cs rename to v2rayn/v2rayN/ServiceLib/Helper/DownloaderHelper.cs index 073e1ab4ad..3764499c39 100644 --- a/v2rayn/v2rayN/ServiceLib/Common/DownloaderHelper.cs +++ b/v2rayn/v2rayN/ServiceLib/Helper/DownloaderHelper.cs @@ -1,7 +1,7 @@ using System.Net; using Downloader; -namespace ServiceLib.Common; +namespace ServiceLib.Helper; public class DownloaderHelper { diff --git a/v2rayn/v2rayN/ServiceLib/Common/HttpClientHelper.cs b/v2rayn/v2rayN/ServiceLib/Helper/HttpClientHelper.cs similarity index 84% rename from v2rayn/v2rayN/ServiceLib/Common/HttpClientHelper.cs rename to v2rayn/v2rayN/ServiceLib/Helper/HttpClientHelper.cs index 8bc1384a4f..a559800fbb 100644 --- a/v2rayn/v2rayN/ServiceLib/Common/HttpClientHelper.cs +++ b/v2rayn/v2rayN/ServiceLib/Helper/HttpClientHelper.cs @@ -1,8 +1,10 @@ +using System.Diagnostics; +using System.Net; using System.Net.Http.Headers; using System.Net.Mime; using System.Text; -namespace ServiceLib.Common; +namespace ServiceLib.Helper; /// /// @@ -202,4 +204,35 @@ public class HttpClientHelper } } while (isMoreToRead); } + + public async Task GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout) + { + var responseTime = -1; + try + { + using var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout)); + using var client = new HttpClient(new SocketsHttpHandler() + { + Proxy = webProxy, + UseProxy = webProxy != null + }); + + List oneTime = new(); + for (var i = 0; i < 2; i++) + { + var timer = Stopwatch.StartNew(); + await client.GetAsync(url, cts.Token).ConfigureAwait(false); + timer.Stop(); + oneTime.Add((int)timer.Elapsed.TotalMilliseconds); + await Task.Delay(100); + } + responseTime = oneTime.Where(x => x > 0).OrderBy(x => x).FirstOrDefault(); + } + catch //(Exception ex) + { + //Utile.SaveLog(ex.Message, ex); + } + return responseTime; + } } diff --git a/v2rayn/v2rayN/ServiceLib/Common/SqliteHelper.cs b/v2rayn/v2rayN/ServiceLib/Helper/SqliteHelper.cs similarity index 98% rename from v2rayn/v2rayN/ServiceLib/Common/SqliteHelper.cs rename to v2rayn/v2rayN/ServiceLib/Helper/SqliteHelper.cs index 62b0a830ae..1561d3a519 100644 --- a/v2rayn/v2rayN/ServiceLib/Common/SqliteHelper.cs +++ b/v2rayn/v2rayN/ServiceLib/Helper/SqliteHelper.cs @@ -1,7 +1,7 @@ using System.Collections; using SQLite; -namespace ServiceLib.Common; +namespace ServiceLib.Helper; public sealed class SQLiteHelper { diff --git a/v2rayn/v2rayN/ServiceLib/Handler/AppHandler.cs b/v2rayn/v2rayN/ServiceLib/Manager/AppManager.cs similarity index 96% rename from v2rayn/v2rayN/ServiceLib/Handler/AppHandler.cs rename to v2rayn/v2rayN/ServiceLib/Manager/AppManager.cs index 18ba19b829..6cf0e82619 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/AppHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/AppManager.cs @@ -1,15 +1,15 @@ -namespace ServiceLib.Handler; +namespace ServiceLib.Manager; -public sealed class AppHandler +public sealed class AppManager { #region Property - private static readonly Lazy _instance = new(() => new()); + private static readonly Lazy _instance = new(() => new()); private Config _config; private int? _statePort; private int? _statePort2; private Job? _processJob; - public static AppHandler Instance => _instance.Value; + public static AppManager Instance => _instance.Value; public Config Config => _config; public int StatePort @@ -97,7 +97,7 @@ public sealed class AppHandler return localPort + (int)protocol; } - public void AddProcess(IntPtr processHandle) + public void AddProcess(nint processHandle) { if (Utils.IsWindows()) { diff --git a/v2rayn/v2rayN/ServiceLib/Handler/ClashApiHandler.cs b/v2rayn/v2rayN/ServiceLib/Manager/ClashApiManager.cs similarity index 93% rename from v2rayn/v2rayN/ServiceLib/Handler/ClashApiHandler.cs rename to v2rayn/v2rayN/ServiceLib/Manager/ClashApiManager.cs index af5b0c579d..9147df052a 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/ClashApiHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/ClashApiManager.cs @@ -1,11 +1,11 @@ using static ServiceLib.Models.ClashProxies; -namespace ServiceLib.Handler; +namespace ServiceLib.Manager; -public sealed class ClashApiHandler +public sealed class ClashApiManager { - private static readonly Lazy instance = new(() => new()); - public static ClashApiHandler Instance => instance.Value; + private static readonly Lazy instance = new(() => new()); + public static ClashApiManager Instance => instance.Value; private static readonly string _tag = "ClashApiHandler"; private Dictionary? _proxies; @@ -65,7 +65,7 @@ public sealed class ClashApiHandler return; } var urlBase = $"{GetApiUrl()}/proxies"; - urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl; + urlBase += @"/{0}/delay?timeout=10000&url=" + AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl; var tasks = new List(); foreach (var it in lstProxy) @@ -182,6 +182,6 @@ public sealed class ClashApiHandler private string GetApiUrl() { - return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}"; + return $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort2}"; } } diff --git a/v2rayn/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs b/v2rayn/v2rayN/ServiceLib/Manager/CoreAdminManager.cs similarity index 92% rename from v2rayn/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs rename to v2rayn/v2rayN/ServiceLib/Manager/CoreAdminManager.cs index ec99b26b67..5ba1747b61 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/CoreAdminManager.cs @@ -3,12 +3,12 @@ using System.Text; using CliWrap; using CliWrap.Buffered; -namespace ServiceLib.Handler; +namespace ServiceLib.Manager; -public class CoreAdminHandler +public class CoreAdminManager { - private static readonly Lazy _instance = new(() => new()); - public static CoreAdminHandler Instance => _instance.Value; + private static readonly Lazy _instance = new(() => new()); + public static CoreAdminManager Instance => _instance.Value; private Config _config; private Action? _updateFunc; private int _linuxSudoPid = -1; @@ -72,7 +72,7 @@ public class CoreAdminHandler proc.BeginErrorReadLine(); await Task.Delay(10); - await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd); + await proc.StandardInput.WriteLineAsync(AppManager.Instance.LinuxSudoPwd); await Task.Delay(100); if (proc is null or { HasExited: true }) @@ -103,7 +103,7 @@ public class CoreAdminHandler var arg = new List() { "-c", $"sudo -S {shFilePath} {_linuxSudoPid}" }; var result = await Cli.Wrap(Global.LinuxBash) .WithArguments(arg) - .WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd)) + .WithStandardInputPipe(PipeSource.FromString(AppManager.Instance.LinuxSudoPwd)) .ExecuteBufferedAsync(); UpdateFunc(false, result.StandardOutput.ToString()); diff --git a/v2rayn/v2rayN/ServiceLib/Handler/CoreInfoHandler.cs b/v2rayn/v2rayN/ServiceLib/Manager/CoreInfoManager.cs similarity index 97% rename from v2rayn/v2rayN/ServiceLib/Handler/CoreInfoHandler.cs rename to v2rayn/v2rayN/ServiceLib/Manager/CoreInfoManager.cs index 6b7e1df26b..12345499fb 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/CoreInfoHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/CoreInfoManager.cs @@ -1,12 +1,12 @@ -namespace ServiceLib.Handler; +namespace ServiceLib.Manager; -public sealed class CoreInfoHandler +public sealed class CoreInfoManager { - private static readonly Lazy _instance = new(() => new()); + private static readonly Lazy _instance = new(() => new()); private List? _coreInfo; - public static CoreInfoHandler Instance => _instance.Value; + public static CoreInfoManager Instance => _instance.Value; - public CoreInfoHandler() + public CoreInfoManager() { InitCoreInfo(); } diff --git a/v2rayn/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayn/v2rayN/ServiceLib/Manager/CoreManager.cs similarity index 89% rename from v2rayn/v2rayN/ServiceLib/Handler/CoreHandler.cs rename to v2rayn/v2rayN/ServiceLib/Manager/CoreManager.cs index f7ad2285b3..d0fcc0fa03 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/CoreManager.cs @@ -1,15 +1,15 @@ using System.Diagnostics; using System.Text; -namespace ServiceLib.Handler; +namespace ServiceLib.Manager; /// /// Core process processing class /// -public class CoreHandler +public class CoreManager { - private static readonly Lazy _instance = new(() => new()); - public static CoreHandler Instance => _instance.Value; + private static readonly Lazy _instance = new(() => new()); + public static CoreManager Instance => _instance.Value; private Config _config; private Process? _process; private Process? _processPre; @@ -39,7 +39,7 @@ public class CoreHandler if (Utils.IsNonWindows()) { - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(); + var coreInfo = CoreInfoManager.Instance.GetCoreInfo(); foreach (var it in coreInfo) { if (it.CoreType == ECoreType.v2rayN) @@ -114,7 +114,7 @@ public class CoreHandler UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); UpdateFunc(false, configPath); - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); + var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType); var proc = await RunProcess(coreInfo, fileName, true, false); if (proc is null) { @@ -126,7 +126,7 @@ public class CoreHandler public async Task LoadCoreConfigSpeedtest(ServerTestItem testItem) { - var node = await AppHandler.Instance.GetProfileItem(testItem.IndexId); + var node = await AppManager.Instance.GetProfileItem(testItem.IndexId); if (node is null) { return -1; @@ -140,8 +140,8 @@ public class CoreHandler return -1; } - var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); + var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType); + var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType); var proc = await RunProcess(coreInfo, fileName, true, false); if (proc is null) { @@ -157,7 +157,7 @@ public class CoreHandler { if (_linuxSudo) { - await CoreAdminHandler.Instance.KillProcessAsLinuxSudo(); + await CoreAdminManager.Instance.KillProcessAsLinuxSudo(); _linuxSudo = false; } @@ -183,8 +183,8 @@ public class CoreHandler private async Task CoreStart(ProfileItem node) { - var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); + var coreType = _config.RunningCoreType = AppManager.Instance.GetCoreType(node, node.ConfigType); + var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType); var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog; var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true); @@ -199,7 +199,7 @@ public class CoreHandler { if (_process != null && !_process.HasExited) { - var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); + var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType); var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType); if (itemSocks != null) { @@ -208,7 +208,7 @@ public class CoreHandler var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName); if (result.Success) { - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType); + var coreInfo = CoreInfoManager.Instance.GetCoreInfo(preCoreType); var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true); if (proc is null) { @@ -231,7 +231,7 @@ public class CoreHandler private async Task RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo) { - var fileName = CoreInfoHandler.Instance.GetCoreExecFile(coreInfo, out var msg); + var fileName = CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out var msg); if (fileName.IsNullOrEmpty()) { UpdateFunc(false, msg); @@ -246,8 +246,8 @@ public class CoreHandler && Utils.IsNonWindows()) { _linuxSudo = true; - await CoreAdminHandler.Instance.Init(_config, _updateFunc); - return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath); + await CoreAdminManager.Instance.Init(_config, _updateFunc); + return await CoreAdminManager.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath); } return await RunProcessNormal(fileName, coreInfo, configPath, displayLog); @@ -299,7 +299,7 @@ public class CoreHandler } await Task.Delay(100); - AppHandler.Instance.AddProcess(proc.Handle); + AppManager.Instance.AddProcess(proc.Handle); if (proc is null or { HasExited: true }) { throw new Exception(ResUI.FailedToRunCore); diff --git a/v2rayn/v2rayN/ServiceLib/Handler/NoticeHandler.cs b/v2rayn/v2rayN/ServiceLib/Manager/NoticeManager.cs similarity index 81% rename from v2rayn/v2rayN/ServiceLib/Handler/NoticeHandler.cs rename to v2rayn/v2rayN/ServiceLib/Manager/NoticeManager.cs index 31cb2204eb..76c01a56c9 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/NoticeHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/NoticeManager.cs @@ -1,11 +1,11 @@ using ReactiveUI; -namespace ServiceLib.Handler; +namespace ServiceLib.Manager; -public class NoticeHandler +public class NoticeManager { - private static readonly Lazy _instance = new(() => new()); - public static NoticeHandler Instance => _instance.Value; + private static readonly Lazy _instance = new(() => new()); + public static NoticeManager Instance => _instance.Value; public void Enqueue(string? content) { diff --git a/v2rayn/v2rayN/ServiceLib/Handler/PacHandler.cs b/v2rayn/v2rayN/ServiceLib/Manager/PacManager.cs similarity index 79% rename from v2rayn/v2rayN/ServiceLib/Handler/PacHandler.cs rename to v2rayn/v2rayN/ServiceLib/Manager/PacManager.cs index 6d2e1f1906..10bedc2942 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/PacHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/PacManager.cs @@ -1,19 +1,22 @@ using System.Net.Sockets; using System.Text; -namespace ServiceLib.Handler; +namespace ServiceLib.Manager; -public class PacHandler +public class PacManager { - private static string _configPath; - private static int _httpPort; - private static int _pacPort; - private static TcpListener? _tcpListener; - private static byte[] _writeContent; - private static bool _isRunning; - private static bool _needRestart = true; + private static readonly Lazy _instance = new(() => new PacManager()); + public static PacManager Instance => _instance.Value; - public static async Task Start(string configPath, int httpPort, int pacPort) + private string _configPath; + private int _httpPort; + private int _pacPort; + private TcpListener? _tcpListener; + private byte[] _writeContent; + private bool _isRunning; + private bool _needRestart = true; + + public async Task StartAsync(string configPath, int httpPort, int pacPort) { _needRestart = configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning; @@ -30,7 +33,7 @@ public class PacHandler } } - private static async Task InitText() + private async Task InitText() { var path = Path.Combine(_configPath, "pac.txt"); @@ -59,7 +62,7 @@ public class PacHandler _writeContent = Encoding.UTF8.GetBytes(sb.ToString()); } - private static void RunListener() + private void RunListener() { _tcpListener = TcpListener.Create(_pacPort); _isRunning = true; @@ -87,14 +90,14 @@ public class PacHandler }, TaskCreationOptions.LongRunning); } - private static void WriteContent(TcpClient client) + private void WriteContent(TcpClient client) { var stream = client.GetStream(); stream.Write(_writeContent, 0, _writeContent.Length); stream.Flush(); } - public static void Stop() + public void Stop() { if (_tcpListener == null) { diff --git a/v2rayn/v2rayN/ServiceLib/Handler/ProfileExHandler.cs b/v2rayn/v2rayN/ServiceLib/Manager/ProfileExManager.cs similarity index 95% rename from v2rayn/v2rayN/ServiceLib/Handler/ProfileExHandler.cs rename to v2rayn/v2rayN/ServiceLib/Manager/ProfileExManager.cs index 5e8ff2e24e..0a3b7399ea 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/ProfileExHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/ProfileExManager.cs @@ -2,17 +2,17 @@ using System.Collections.Concurrent; //using System.Reactive.Linq; -namespace ServiceLib.Handler; +namespace ServiceLib.Manager; -public class ProfileExHandler +public class ProfileExManager { - private static readonly Lazy _instance = new(() => new()); + private static readonly Lazy _instance = new(() => new()); private ConcurrentBag _lstProfileEx = []; private readonly Queue _queIndexIds = new(); - public static ProfileExHandler Instance => _instance.Value; + public static ProfileExManager Instance => _instance.Value; private static readonly string _tag = "ProfileExHandler"; - public ProfileExHandler() + public ProfileExManager() { //Init(); } diff --git a/v2rayn/v2rayN/ServiceLib/Handler/StatisticsHandler.cs b/v2rayn/v2rayN/ServiceLib/Manager/StatisticsManager.cs similarity index 94% rename from v2rayn/v2rayN/ServiceLib/Handler/StatisticsHandler.cs rename to v2rayn/v2rayN/ServiceLib/Manager/StatisticsManager.cs index 890c047dbf..69a4785644 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/StatisticsHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/StatisticsManager.cs @@ -1,9 +1,9 @@ -namespace ServiceLib.Handler; +namespace ServiceLib.Manager; -public class StatisticsHandler +public class StatisticsManager { - private static readonly Lazy instance = new(() => new()); - public static StatisticsHandler Instance => instance.Value; + private static readonly Lazy instance = new(() => new()); + public static StatisticsManager Instance => instance.Value; private Config _config; private ServerStatItem? _serverStatItem; @@ -91,7 +91,7 @@ public class StatisticsHandler { await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )"); - long ticks = DateTime.Now.Date.Ticks; + var ticks = DateTime.Now.Date.Ticks; await SQLiteHelper.Instance.ExecuteAsync($"update ServerStatItem set todayUp = 0,todayDown=0,dateNow={ticks} where dateNow<>{ticks}"); _lstServerStat = await SQLiteHelper.Instance.TableAsync().ToListAsync(); @@ -128,7 +128,7 @@ public class StatisticsHandler private async Task GetServerStatItem(string indexId) { - long ticks = DateTime.Now.Date.Ticks; + var ticks = DateTime.Now.Date.Ticks; if (_serverStatItem != null && _serverStatItem.IndexId != indexId) { _serverStatItem = null; diff --git a/v2rayn/v2rayN/ServiceLib/Handler/TaskHandler.cs b/v2rayn/v2rayN/ServiceLib/Manager/TaskManager.cs similarity index 86% rename from v2rayn/v2rayN/ServiceLib/Handler/TaskHandler.cs rename to v2rayn/v2rayN/ServiceLib/Manager/TaskManager.cs index b49570be73..b968dc16f0 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/TaskHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/TaskManager.cs @@ -1,9 +1,9 @@ -namespace ServiceLib.Handler; +namespace ServiceLib.Manager; -public class TaskHandler +public class TaskManager { - private static readonly Lazy _instance = new(() => new()); - public static TaskHandler Instance => _instance.Value; + private static readonly Lazy _instance = new(() => new()); + public static TaskManager Instance => _instance.Value; public void RegUpdateTask(Config config, Action updateFunc) { @@ -29,7 +29,7 @@ public class TaskHandler //Logging.SaveLog("Execute save config"); await ConfigHandler.SaveConfig(config); - await ProfileExHandler.Instance.SaveTo(); + await ProfileExManager.Instance.SaveTo(); } //Execute once 1 hour @@ -52,7 +52,7 @@ public class TaskHandler private async Task UpdateTaskRunSubscription(Config config, Action updateFunc) { var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds(); - var lstSubs = (await AppHandler.Instance.SubItems())? + var lstSubs = (await AppManager.Instance.SubItems())? .Where(t => t.AutoUpdateInterval > 0) .Where(t => updateTime - t.UpdateTime >= t.AutoUpdateInterval * 60) .ToList(); @@ -66,7 +66,7 @@ public class TaskHandler foreach (var item in lstSubs) { - await SubscriptionHandler.UpdateProcess(config, item.Id, true, (bool success, string msg) => + await SubscriptionHandler.UpdateProcess(config, item.Id, true, (success, msg) => { updateFunc?.Invoke(success, msg); if (success) @@ -87,7 +87,7 @@ public class TaskHandler Logging.SaveLog("Execute update geo files"); var updateHandle = new UpdateService(); - await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) => + await updateHandle.UpdateGeoFileAll(config, (success, msg) => { updateFunc?.Invoke(false, msg); }); diff --git a/v2rayn/v2rayN/ServiceLib/Handler/WebDavHandler.cs b/v2rayn/v2rayN/ServiceLib/Manager/WebDavManager.cs similarity index 94% rename from v2rayn/v2rayN/ServiceLib/Handler/WebDavHandler.cs rename to v2rayn/v2rayN/ServiceLib/Manager/WebDavManager.cs index 865f45889d..3f5c9ea3c0 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/WebDavHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/WebDavManager.cs @@ -1,12 +1,12 @@ using System.Net; using WebDav; -namespace ServiceLib.Handler; +namespace ServiceLib.Manager; -public sealed class WebDavHandler +public sealed class WebDavManager { - private static readonly Lazy _instance = new(() => new()); - public static WebDavHandler Instance => _instance.Value; + private static readonly Lazy _instance = new(() => new()); + public static WebDavManager Instance => _instance.Value; private readonly Config? _config; private WebDavClient? _client; @@ -15,9 +15,9 @@ public sealed class WebDavHandler private readonly string _webFileName = "backup.zip"; private readonly string _tag = "WebDav--"; - public WebDavHandler() + public WebDavManager() { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; } private async Task GetClient() diff --git a/v2rayn/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayn/v2rayN/ServiceLib/Models/ConfigItems.cs index 1c3a57c311..148f2bd786 100644 --- a/v2rayn/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayn/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -165,7 +165,6 @@ public class RoutingBasicItem { public string DomainStrategy { get; set; } public string DomainStrategy4Singbox { get; set; } - public string DomainMatcher { get; set; } public string RoutingIndexId { get; set; } } diff --git a/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs index 702bbae144..cff3cf8bc2 100644 --- a/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs +++ b/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs @@ -233,8 +233,6 @@ public class Routing4Ray { public string domainStrategy { get; set; } - public string? domainMatcher { get; set; } - public List rules { get; set; } public List? balancers { get; set; } diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 2adb4f8de9..da0ee7730d 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -2463,15 +2463,6 @@ namespace ServiceLib.Resx { } } - /// - /// 查找类似 Domain Matcher 的本地化字符串。 - /// - public static string TbdomainMatcher { - get { - return ResourceManager.GetString("TbdomainMatcher", resourceCulture); - } - } - /// /// 查找类似 Domain strategy 的本地化字符串。 /// diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index d35c8d20e5..6873e1c827 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -825,9 +825,6 @@ تنظیم کردن به عنوان قانون فعال - - تطبیق دامنه - استراتژی دامنه diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 4bc190066d..d90ac86cba 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -825,9 +825,6 @@ Beállítás aktív szabályként (Enter) - - Tartomány illesztő - Tartomány stratégia diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx index 4cf1b91a1d..2b2e3abd4c 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx @@ -825,9 +825,6 @@ Set as active rule (Enter) - - Domain Matcher - Domain strategy diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 97c04c819e..bb1f618455 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1,17 +1,17 @@ - @@ -825,9 +825,6 @@ Установить как активное правило - - Сопоставитель доменов - Доменная стратегия @@ -1500,4 +1497,4 @@ Эта функция предназначена для продвинутых пользователей и особых случаев. После включения игнорируются базовые настройки ядра, DNS и маршрутизации. Вы должны самостоятельно корректно задать порт системного прокси, учёт трафика и другие связанные параметры — всё настраивается вручную. - + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index a2506ed8b9..b10ff6414c 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -825,9 +825,6 @@ 设为活动规则 (Enter) - - 域名匹配算法 - 域名解析策略 diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 3d16e5b5fc..b3738082c4 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -825,9 +825,6 @@ 設為活動規則 (Enter) - - 域名匹配演算法 - 域名解析策略 diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs index 50c6df67fd..e102f17d2a 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs @@ -73,12 +73,12 @@ public class CoreConfigClashService } //mixed-port - fileContent["mixed-port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks); + fileContent["mixed-port"] = AppManager.Instance.GetLocalPort(EInboundProtocol.socks); //log-level fileContent["log-level"] = GetLogLevel(_config.CoreBasicItem.Loglevel); //external-controller - fileContent["external-controller"] = $"{Global.Loopback}:{AppHandler.Instance.StatePort2}"; + fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}"; //allow-lan if (_config.Inbound.First().AllowLANConn) { @@ -139,7 +139,7 @@ public class CoreConfigClashService return ret; } - ClashApiHandler.Instance.ProfileContent = fileContent; + ClashApiManager.Instance.ProfileContent = fileContent; ret.Msg = string.Format(ResUI.SuccessfulConfiguration, $"{node.GetSummary()}"); ret.Success = true; diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs deleted file mode 100644 index fa2b14d80e..0000000000 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ /dev/null @@ -1,2277 +0,0 @@ -using System.Data; -using System.Net; -using System.Net.NetworkInformation; -using System.Text.Json.Nodes; - -namespace ServiceLib.Services.CoreConfig; - -public class CoreConfigSingboxService -{ - private Config _config; - private static readonly string _tag = "CoreConfigSingboxService"; - - public CoreConfigSingboxService(Config config) - { - _config = config; - } - - #region public gen function - - public async Task GenerateClientConfigContent(ProfileItem node) - { - var ret = new RetResult(); - try - { - if (node == null - || node.Port <= 0) - { - ret.Msg = ResUI.CheckServerSettings; - return ret; - } - if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp)) - { - ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; - return ret; - } - - ret.Msg = ResUI.InitialConfiguration; - - string result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient); - if (result.IsNullOrEmpty()) - { - ret.Msg = ResUI.FailedGetDefaultConfiguration; - return ret; - } - - var singboxConfig = JsonUtils.Deserialize(result); - if (singboxConfig == null) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - - await GenLog(singboxConfig); - - await GenInbounds(singboxConfig); - - if (node.ConfigType == EConfigType.WireGuard) - { - singboxConfig.outbounds.RemoveAt(0); - var endpoints = new Endpoints4Sbox(); - await GenEndpoint(node, endpoints); - endpoints.tag = Global.ProxyTag; - singboxConfig.endpoints = new() { endpoints }; - } - else - { - await GenOutbound(node, singboxConfig.outbounds.First()); - } - - await GenMoreOutbounds(node, singboxConfig); - - await GenRouting(singboxConfig); - - await GenDns(singboxConfig); - - await GenExperimental(singboxConfig); - - await ConvertGeo2Ruleset(singboxConfig); - - ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); - ret.Success = true; - - ret.Data = await ApplyFullConfigTemplate(singboxConfig); - return ret; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - } - - public async Task GenerateClientSpeedtestConfig(List selecteds) - { - var ret = new RetResult(); - try - { - if (_config == null) - { - ret.Msg = ResUI.CheckServerSettings; - return ret; - } - - ret.Msg = ResUI.InitialConfiguration; - - var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient); - var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); - if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty()) - { - ret.Msg = ResUI.FailedGetDefaultConfiguration; - return ret; - } - - var singboxConfig = JsonUtils.Deserialize(result); - if (singboxConfig == null) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - List lstIpEndPoints = new(); - List lstTcpConns = new(); - try - { - lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); - lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners()); - lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()); - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - - await GenLog(singboxConfig); - //GenDns(new(), singboxConfig); - singboxConfig.inbounds.Clear(); - singboxConfig.outbounds.RemoveAt(0); - - var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest); - - foreach (var it in selecteds) - { - if (it.ConfigType == EConfigType.Custom) - { - continue; - } - if (it.Port <= 0) - { - continue; - } - var item = await AppHandler.Instance.GetProfileItem(it.IndexId); - if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS) - { - if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id)) - { - continue; - } - } - - //find unused port - var port = initPort; - for (int k = initPort; k < Global.MaxPort; k++) - { - if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) - { - continue; - } - if (lstTcpConns?.FindIndex(_it => _it.LocalEndPoint.Port == k) >= 0) - { - continue; - } - //found - port = k; - initPort = port + 1; - break; - } - - //Port In Used - if (lstIpEndPoints?.FindIndex(_it => _it.Port == port) >= 0) - { - continue; - } - it.Port = port; - it.AllowTest = true; - - //inbound - Inbound4Sbox inbound = new() - { - listen = Global.Loopback, - listen_port = port, - type = EInboundProtocol.mixed.ToString(), - }; - inbound.tag = inbound.type + inbound.listen_port.ToString(); - singboxConfig.inbounds.Add(inbound); - - //outbound - if (item is null) - { - continue; - } - if (item.ConfigType == EConfigType.Shadowsocks - && !Global.SsSecuritiesInSingbox.Contains(item.Security)) - { - continue; - } - if (item.ConfigType == EConfigType.VLESS - && !Global.Flows.Contains(item.Flow)) - { - continue; - } - if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan - && item.StreamSecurity == Global.StreamSecurityReality - && item.PublicKey.IsNullOrEmpty()) - { - continue; - } - - var server = await GenServer(item); - if (server is null) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - var tag = Global.ProxyTag + inbound.listen_port.ToString(); - server.tag = tag; - if (server is Endpoints4Sbox endpoint) - { - singboxConfig.endpoints ??= new(); - singboxConfig.endpoints.Add(endpoint); - } - else if (server is Outbound4Sbox outbound) - { - singboxConfig.outbounds.Add(outbound); - } - - //rule - Rule4Sbox rule = new() - { - inbound = new List { inbound.tag }, - outbound = tag - }; - singboxConfig.route.rules.Add(rule); - } - - var rawDNSItem = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); - if (rawDNSItem != null && rawDNSItem.Enabled == true) - { - await GenDnsDomainsCompatible(singboxConfig, rawDNSItem); - } - else - { - await GenDnsDomains(singboxConfig, _config.SimpleDNSItem); - } - singboxConfig.route.default_domain_resolver = new() - { - server = Global.SingboxFinalResolverTag - }; - - ret.Success = true; - ret.Data = JsonUtils.Serialize(singboxConfig); - return ret; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - } - - public async Task GenerateClientSpeedtestConfig(ProfileItem node, int port) - { - var ret = new RetResult(); - try - { - if (node is not { Port: > 0 }) - { - ret.Msg = ResUI.CheckServerSettings; - return ret; - } - if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp)) - { - ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; - return ret; - } - - ret.Msg = ResUI.InitialConfiguration; - - var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient); - if (result.IsNullOrEmpty()) - { - ret.Msg = ResUI.FailedGetDefaultConfiguration; - return ret; - } - - var singboxConfig = JsonUtils.Deserialize(result); - if (singboxConfig == null) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - - await GenLog(singboxConfig); - if (node.ConfigType == EConfigType.WireGuard) - { - singboxConfig.outbounds.RemoveAt(0); - var endpoints = new Endpoints4Sbox(); - await GenEndpoint(node, endpoints); - endpoints.tag = Global.ProxyTag; - singboxConfig.endpoints = new() { endpoints }; - } - else - { - await GenOutbound(node, singboxConfig.outbounds.First()); - } - await GenMoreOutbounds(node, singboxConfig); - var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); - if (item != null && item.Enabled == true) - { - await GenDnsDomainsCompatible(singboxConfig, item); - } - else - { - await GenDnsDomains(singboxConfig, _config.SimpleDNSItem); - } - singboxConfig.route.default_domain_resolver = new() - { - server = Global.SingboxFinalResolverTag - }; - - singboxConfig.route.rules.Clear(); - singboxConfig.inbounds.Clear(); - singboxConfig.inbounds.Add(new() - { - tag = $"{EInboundProtocol.mixed}{port}", - listen = Global.Loopback, - listen_port = port, - type = EInboundProtocol.mixed.ToString(), - }); - - ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); - ret.Success = true; - ret.Data = JsonUtils.Serialize(singboxConfig); - return ret; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - } - - public async Task GenerateClientMultipleLoadConfig(List selecteds) - { - var ret = new RetResult(); - try - { - if (_config == null) - { - ret.Msg = ResUI.CheckServerSettings; - return ret; - } - - ret.Msg = ResUI.InitialConfiguration; - - string result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient); - string txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); - if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty()) - { - ret.Msg = ResUI.FailedGetDefaultConfiguration; - return ret; - } - - var singboxConfig = JsonUtils.Deserialize(result); - if (singboxConfig == null) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - - await GenLog(singboxConfig); - await GenInbounds(singboxConfig); - await GenRouting(singboxConfig); - await GenExperimental(singboxConfig); - singboxConfig.outbounds.RemoveAt(0); - - var proxyProfiles = new List(); - foreach (var it in selecteds) - { - if (it.ConfigType == EConfigType.Custom) - { - continue; - } - if (it.Port <= 0) - { - continue; - } - var item = await AppHandler.Instance.GetProfileItem(it.IndexId); - if (item is null) - { - continue; - } - if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS) - { - if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id)) - { - continue; - } - } - if (item.ConfigType == EConfigType.Shadowsocks - && !Global.SsSecuritiesInSingbox.Contains(item.Security)) - { - continue; - } - if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow)) - { - continue; - } - - //outbound - proxyProfiles.Add(item); - } - if (proxyProfiles.Count <= 0) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - await GenOutboundsList(proxyProfiles, singboxConfig); - - await GenDns(singboxConfig); - await ConvertGeo2Ruleset(singboxConfig); - - ret.Success = true; - - ret.Data = await ApplyFullConfigTemplate(singboxConfig); - return ret; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - } - - public async Task GenerateClientCustomConfig(ProfileItem node, string? fileName) - { - var ret = new RetResult(); - if (node == null || fileName is null) - { - ret.Msg = ResUI.CheckServerSettings; - return ret; - } - - ret.Msg = ResUI.InitialConfiguration; - - try - { - if (node == null) - { - ret.Msg = ResUI.CheckServerSettings; - return ret; - } - - if (File.Exists(fileName)) - { - File.Delete(fileName); - } - - string addressFileName = node.Address; - if (addressFileName.IsNullOrEmpty()) - { - ret.Msg = ResUI.FailedGetDefaultConfiguration; - return ret; - } - if (!File.Exists(addressFileName)) - { - addressFileName = Path.Combine(Utils.GetConfigPath(), addressFileName); - } - if (!File.Exists(addressFileName)) - { - ret.Msg = ResUI.FailedReadConfiguration + "1"; - return ret; - } - - if (node.Address == Global.CoreMultipleLoadConfigFileName) - { - var txtFile = File.ReadAllText(addressFileName); - var singboxConfig = JsonUtils.Deserialize(txtFile); - if (singboxConfig == null) - { - File.Copy(addressFileName, fileName); - } - else - { - await GenInbounds(singboxConfig); - await GenExperimental(singboxConfig); - - var content = JsonUtils.Serialize(singboxConfig, true); - await File.WriteAllTextAsync(fileName, content); - } - } - else - { - File.Copy(addressFileName, fileName); - } - - //check again - if (!File.Exists(fileName)) - { - ret.Msg = ResUI.FailedReadConfiguration + "2"; - return ret; - } - - ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); - ret.Success = true; - return ret; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - } - - #endregion public gen function - - #region private gen function - - private async Task GenLog(SingboxConfig singboxConfig) - { - try - { - switch (_config.CoreBasicItem.Loglevel) - { - case "debug": - case "info": - case "error": - singboxConfig.log.level = _config.CoreBasicItem.Loglevel; - break; - - case "warning": - singboxConfig.log.level = "warn"; - break; - - default: - break; - } - if (_config.CoreBasicItem.Loglevel == Global.None) - { - singboxConfig.log.disabled = true; - } - if (_config.CoreBasicItem.LogEnabled) - { - var dtNow = DateTime.Now; - singboxConfig.log.output = Utils.GetLogPath($"sbox_{dtNow:yyyy-MM-dd}.txt"); - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(0); - } - - private async Task GenInbounds(SingboxConfig singboxConfig) - { - try - { - var listen = "0.0.0.0"; - singboxConfig.inbounds = []; - - if (!_config.TunModeItem.EnableTun - || (_config.TunModeItem.EnableTun && _config.TunModeItem.EnableExInbound && _config.RunningCoreType == ECoreType.sing_box)) - { - var inbound = new Inbound4Sbox() - { - type = EInboundProtocol.mixed.ToString(), - tag = EInboundProtocol.socks.ToString(), - listen = Global.Loopback, - }; - singboxConfig.inbounds.Add(inbound); - - inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks); - - if (_config.Inbound.First().SecondLocalPortEnabled) - { - var inbound2 = GetInbound(inbound, EInboundProtocol.socks2, true); - singboxConfig.inbounds.Add(inbound2); - } - - if (_config.Inbound.First().AllowLANConn) - { - if (_config.Inbound.First().NewPort4LAN) - { - var inbound3 = GetInbound(inbound, EInboundProtocol.socks3, true); - inbound3.listen = listen; - singboxConfig.inbounds.Add(inbound3); - - //auth - if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty()) - { - inbound3.users = new() { new() { username = _config.Inbound.First().User, password = _config.Inbound.First().Pass } }; - } - } - else - { - inbound.listen = listen; - } - } - } - - if (_config.TunModeItem.EnableTun) - { - if (_config.TunModeItem.Mtu <= 0) - { - _config.TunModeItem.Mtu = Global.TunMtus.First(); - } - if (_config.TunModeItem.Stack.IsNullOrEmpty()) - { - _config.TunModeItem.Stack = Global.TunStacks.First(); - } - - var tunInbound = JsonUtils.Deserialize(EmbedUtils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { }; - tunInbound.interface_name = Utils.IsOSX() ? $"utun{new Random().Next(99)}" : "singbox_tun"; - tunInbound.mtu = _config.TunModeItem.Mtu; - tunInbound.auto_route = _config.TunModeItem.AutoRoute; - tunInbound.strict_route = _config.TunModeItem.StrictRoute; - tunInbound.stack = _config.TunModeItem.Stack; - if (_config.TunModeItem.EnableIPv6Address == false) - { - tunInbound.address = ["172.18.0.1/30"]; - } - - singboxConfig.inbounds.Add(tunInbound); - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return 0; - } - - private Inbound4Sbox GetInbound(Inbound4Sbox inItem, EInboundProtocol protocol, bool bSocks) - { - var inbound = JsonUtils.DeepCopy(inItem); - inbound.tag = protocol.ToString(); - inbound.listen_port = inItem.listen_port + (int)protocol; - inbound.type = EInboundProtocol.mixed.ToString(); - return inbound; - } - - private async Task GenOutbound(ProfileItem node, Outbound4Sbox outbound) - { - try - { - outbound.server = node.Address; - outbound.server_port = node.Port; - outbound.type = Global.ProtocolTypes[node.ConfigType]; - - switch (node.ConfigType) - { - case EConfigType.VMess: - { - outbound.uuid = node.Id; - outbound.alter_id = node.AlterId; - if (Global.VmessSecurities.Contains(node.Security)) - { - outbound.security = node.Security; - } - else - { - outbound.security = Global.DefaultSecurity; - } - - await GenOutboundMux(node, outbound); - break; - } - case EConfigType.Shadowsocks: - { - outbound.method = AppHandler.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : Global.None; - outbound.password = node.Id; - - await GenOutboundMux(node, outbound); - break; - } - case EConfigType.SOCKS: - { - outbound.version = "5"; - if (node.Security.IsNotEmpty() - && node.Id.IsNotEmpty()) - { - outbound.username = node.Security; - outbound.password = node.Id; - } - break; - } - case EConfigType.HTTP: - { - if (node.Security.IsNotEmpty() - && node.Id.IsNotEmpty()) - { - outbound.username = node.Security; - outbound.password = node.Id; - } - break; - } - case EConfigType.VLESS: - { - outbound.uuid = node.Id; - - outbound.packet_encoding = "xudp"; - - if (node.Flow.IsNullOrEmpty()) - { - await GenOutboundMux(node, outbound); - } - else - { - outbound.flow = node.Flow; - } - break; - } - case EConfigType.Trojan: - { - outbound.password = node.Id; - - await GenOutboundMux(node, outbound); - break; - } - case EConfigType.Hysteria2: - { - outbound.password = node.Id; - - if (node.Path.IsNotEmpty()) - { - outbound.obfs = new() - { - type = "salamander", - password = node.Path.TrimEx(), - }; - } - - outbound.up_mbps = _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null; - outbound.down_mbps = _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null; - if (node.Ports.IsNotEmpty() && (node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(','))) - { - outbound.server_port = null; - outbound.server_ports = node.Ports.Split(',') - .Select(p => p.Trim()) - .Where(p => p.IsNotEmpty()) - .Select(p => - { - var port = p.Replace('-', ':'); - return port.Contains(':') ? port : $"{port}:{port}"; - }) - .ToList(); - outbound.hop_interval = _config.HysteriaItem.HopInterval > 0 ? $"{_config.HysteriaItem.HopInterval}s" : null; - } - - break; - } - case EConfigType.TUIC: - { - outbound.uuid = node.Id; - outbound.password = node.Security; - outbound.congestion_control = node.HeaderType; - break; - } - case EConfigType.Anytls: - { - outbound.password = node.Id; - break; - } - } - - await GenOutboundTls(node, outbound); - - await GenOutboundTransport(node, outbound); - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return 0; - } - - private async Task GenEndpoint(ProfileItem node, Endpoints4Sbox endpoint) - { - try - { - endpoint.address = Utils.String2List(node.RequestHost); - endpoint.type = Global.ProtocolTypes[node.ConfigType]; - - switch (node.ConfigType) - { - case EConfigType.WireGuard: - { - var peer = new Peer4Sbox - { - public_key = node.PublicKey, - reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(), - address = node.Address, - port = node.Port, - // TODO default ["0.0.0.0/0", "::/0"] - allowed_ips = new() { "0.0.0.0/0", "::/0" }, - }; - endpoint.private_key = node.Id; - endpoint.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(); - endpoint.peers = new() { peer }; - break; - } - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(0); - } - - private async Task GenServer(ProfileItem node) - { - try - { - var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); - if (node.ConfigType == EConfigType.WireGuard) - { - var endpoint = JsonUtils.Deserialize(txtOutbound); - await GenEndpoint(node, endpoint); - return endpoint; - } - else - { - var outbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(node, outbound); - return outbound; - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(null); - } - - private async Task GenOutboundMux(ProfileItem node, Outbound4Sbox outbound) - { - try - { - var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled; - if (muxEnabled && _config.Mux4SboxItem.Protocol.IsNotEmpty()) - { - var mux = new Multiplex4Sbox() - { - enabled = true, - protocol = _config.Mux4SboxItem.Protocol, - max_connections = _config.Mux4SboxItem.MaxConnections, - padding = _config.Mux4SboxItem.Padding, - }; - outbound.multiplex = mux; - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(0); - } - - private async Task GenOutboundTls(ProfileItem node, Outbound4Sbox outbound) - { - try - { - if (node.StreamSecurity == Global.StreamSecurityReality || node.StreamSecurity == Global.StreamSecurity) - { - var server_name = string.Empty; - if (node.Sni.IsNotEmpty()) - { - server_name = node.Sni; - } - else if (node.RequestHost.IsNotEmpty()) - { - server_name = Utils.String2List(node.RequestHost)?.First(); - } - var tls = new Tls4Sbox() - { - enabled = true, - record_fragment = _config.CoreBasicItem.EnableFragment, - server_name = server_name, - insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure), - alpn = node.GetAlpn(), - }; - if (node.Fingerprint.IsNotEmpty()) - { - tls.utls = new Utls4Sbox() - { - enabled = true, - fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint - }; - } - if (node.StreamSecurity == Global.StreamSecurityReality) - { - tls.reality = new Reality4Sbox() - { - enabled = true, - public_key = node.PublicKey, - short_id = node.ShortId - }; - tls.insecure = false; - } - outbound.tls = tls; - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(0); - } - - private async Task GenOutboundTransport(ProfileItem node, Outbound4Sbox outbound) - { - try - { - var transport = new Transport4Sbox(); - - switch (node.GetNetwork()) - { - case nameof(ETransport.h2): - transport.type = nameof(ETransport.http); - transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost); - transport.path = node.Path.IsNullOrEmpty() ? null : node.Path; - break; - - case nameof(ETransport.tcp): //http - if (node.HeaderType == Global.TcpHeaderHttp) - { - if (node.ConfigType == EConfigType.Shadowsocks) - { - outbound.plugin = "obfs-local"; - outbound.plugin_opts = $"obfs=http;obfs-host={node.RequestHost};"; - } - else - { - transport.type = nameof(ETransport.http); - transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost); - transport.path = node.Path.IsNullOrEmpty() ? null : node.Path; - } - } - break; - - case nameof(ETransport.ws): - transport.type = nameof(ETransport.ws); - transport.path = node.Path.IsNullOrEmpty() ? null : node.Path; - if (node.RequestHost.IsNotEmpty()) - { - transport.headers = new() - { - Host = node.RequestHost - }; - } - break; - - case nameof(ETransport.httpupgrade): - transport.type = nameof(ETransport.httpupgrade); - transport.path = node.Path.IsNullOrEmpty() ? null : node.Path; - transport.host = node.RequestHost.IsNullOrEmpty() ? null : node.RequestHost; - - break; - - case nameof(ETransport.quic): - transport.type = nameof(ETransport.quic); - break; - - case nameof(ETransport.grpc): - transport.type = nameof(ETransport.grpc); - transport.service_name = node.Path; - transport.idle_timeout = _config.GrpcItem.IdleTimeout?.ToString("##s"); - transport.ping_timeout = _config.GrpcItem.HealthCheckTimeout?.ToString("##s"); - transport.permit_without_stream = _config.GrpcItem.PermitWithoutStream; - break; - - default: - break; - } - if (transport.type != null) - { - outbound.transport = transport; - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(0); - } - - private async Task GenMoreOutbounds(ProfileItem node, SingboxConfig singboxConfig) - { - if (node.Subid.IsNullOrEmpty()) - { - return 0; - } - try - { - var subItem = await AppHandler.Instance.GetSubItem(node.Subid); - if (subItem is null) - { - return 0; - } - - //current proxy - BaseServer4Sbox? outbound = singboxConfig.endpoints?.FirstOrDefault(t => t.tag == Global.ProxyTag, null); - outbound ??= singboxConfig.outbounds.First(); - - var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); - - //Previous proxy - var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); - string? prevOutboundTag = null; - if (prevNode is not null - && prevNode.ConfigType != EConfigType.Custom) - { - prevOutboundTag = $"prev-{Global.ProxyTag}"; - var prevServer = await GenServer(prevNode); - prevServer.tag = prevOutboundTag; - if (prevServer is Endpoints4Sbox endpoint) - { - singboxConfig.endpoints ??= new(); - singboxConfig.endpoints.Add(endpoint); - } - else if (prevServer is Outbound4Sbox outboundPrev) - { - singboxConfig.outbounds.Add(outboundPrev); - } - } - var nextServer = await GenChainOutbounds(subItem, outbound, prevOutboundTag); - - if (nextServer is not null) - { - if (nextServer is Endpoints4Sbox endpoint) - { - singboxConfig.endpoints ??= new(); - singboxConfig.endpoints.Insert(0, endpoint); - } - else if (nextServer is Outbound4Sbox outboundNext) - { - singboxConfig.outbounds.Insert(0, outboundNext); - } - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - - return 0; - } - - private async Task GenOutboundsList(List nodes, SingboxConfig singboxConfig) - { - try - { - // Get outbound template and initialize lists - var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); - if (txtOutbound.IsNullOrEmpty()) - { - return 0; - } - - var resultOutbounds = new List(); - var resultEndpoints = new List(); // For endpoints - var prevOutbounds = new List(); // Separate list for prev outbounds - var prevEndpoints = new List(); // Separate list for prev endpoints - var proxyTags = new List(); // For selector and urltest outbounds - - // Cache for chain proxies to avoid duplicate generation - var nextProxyCache = new Dictionary(); - var prevProxyTags = new Dictionary(); // Map from profile name to tag - int prevIndex = 0; // Index for prev outbounds - - // Process each node - int index = 0; - foreach (var node in nodes) - { - index++; - - // Handle proxy chain - string? prevTag = null; - var currentServer = await GenServer(node); - var nextServer = nextProxyCache.GetValueOrDefault(node.Subid, null); - if (nextServer != null) - { - nextServer = JsonUtils.DeepCopy(nextServer); - } - - var subItem = await AppHandler.Instance.GetSubItem(node.Subid); - - // current proxy - currentServer.tag = $"{Global.ProxyTag}-{index}"; - proxyTags.Add(currentServer.tag); - - if (!node.Subid.IsNullOrEmpty()) - { - if (prevProxyTags.TryGetValue(node.Subid, out var value)) - { - prevTag = value; // maybe null - } - else - { - var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); - if (prevNode is not null - && prevNode.ConfigType != EConfigType.Custom) - { - var prevOutbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(prevNode, prevOutbound); - prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}"; - prevOutbound.tag = prevTag; - prevOutbounds.Add(prevOutbound); - } - prevProxyTags[node.Subid] = prevTag; - } - - nextServer = await GenChainOutbounds(subItem, currentServer, prevTag, nextServer); - if (!nextProxyCache.ContainsKey(node.Subid)) - { - nextProxyCache[node.Subid] = nextServer; - } - } - - if (nextServer is not null) - { - if (nextServer is Endpoints4Sbox nextEndpoint) - { - resultEndpoints.Add(nextEndpoint); - } - else if (nextServer is Outbound4Sbox nextOutbound) - { - resultOutbounds.Add(nextOutbound); - } - } - if (currentServer is Endpoints4Sbox currentEndpoint) - { - resultEndpoints.Add(currentEndpoint); - } - else if (currentServer is Outbound4Sbox currentOutbound) - { - resultOutbounds.Add(currentOutbound); - } - } - - // Add urltest outbound (auto selection based on latency) - if (proxyTags.Count > 0) - { - var outUrltest = new Outbound4Sbox - { - type = "urltest", - tag = $"{Global.ProxyTag}-auto", - outbounds = proxyTags, - interrupt_exist_connections = false, - }; - - // Add selector outbound (manual selection) - var outSelector = new Outbound4Sbox - { - type = "selector", - tag = Global.ProxyTag, - outbounds = JsonUtils.DeepCopy(proxyTags), - interrupt_exist_connections = false, - }; - outSelector.outbounds.Insert(0, outUrltest.tag); - - // Insert these at the beginning - resultOutbounds.Insert(0, outUrltest); - resultOutbounds.Insert(0, outSelector); - } - - // Merge results: first the selector/urltest/proxies, then other outbounds, and finally prev outbounds - resultOutbounds.AddRange(prevOutbounds); - resultOutbounds.AddRange(singboxConfig.outbounds); - singboxConfig.outbounds = resultOutbounds; - singboxConfig.endpoints ??= new List(); - resultEndpoints.AddRange(singboxConfig.endpoints); - singboxConfig.endpoints = resultEndpoints; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - - return 0; - } - - /// - /// Generates a chained outbound configuration for the given subItem and outbound. - /// The outbound's tag must be set before calling this method. - /// Returns the next proxy's outbound configuration, which may be null if no next proxy exists. - /// - /// The subscription item containing proxy chain information. - /// The current outbound configuration. Its tag must be set before calling this method. - /// The tag of the previous outbound in the chain, if any. - /// The outbound for the next proxy in the chain, if already created. If null, will be created inside. - /// - /// The outbound configuration for the next proxy in the chain, or null if no next proxy exists. - /// - private async Task GenChainOutbounds(SubItem subItem, BaseServer4Sbox outbound, string? prevOutboundTag, BaseServer4Sbox? nextOutbound = null) - { - try - { - var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); - - if (!prevOutboundTag.IsNullOrEmpty()) - { - outbound.detour = prevOutboundTag; - } - - // Next proxy - var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); - if (nextNode is not null - && nextNode.ConfigType != EConfigType.Custom) - { - nextOutbound ??= await GenServer(nextNode); - nextOutbound.tag = outbound.tag; - - outbound.tag = $"mid-{outbound.tag}"; - nextOutbound.detour = outbound.tag; - } - return nextOutbound; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return null; - } - - private async Task GenRouting(SingboxConfig singboxConfig) - { - try - { - singboxConfig.route.final = Global.ProxyTag; - var item = _config.SimpleDNSItem; - - var defaultDomainResolverTag = Global.SingboxOutboundResolverTag; - var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct; - - var rawDNSItem = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); - if (rawDNSItem != null && rawDNSItem.Enabled == true) - { - defaultDomainResolverTag = Global.SingboxFinalResolverTag; - directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom; - } - singboxConfig.route.default_domain_resolver = new() - { - server = defaultDomainResolverTag, - strategy = directDNSStrategy - }; - - if (_config.TunModeItem.EnableTun) - { - singboxConfig.route.auto_detect_interface = true; - - var tunRules = JsonUtils.Deserialize>(EmbedUtils.GetEmbedText(Global.TunSingboxRulesFileName)); - if (tunRules != null) - { - singboxConfig.route.rules.AddRange(tunRules); - } - - GenRoutingDirectExe(out List lstDnsExe, out List lstDirectExe); - singboxConfig.route.rules.Add(new() - { - port = new() { 53 }, - action = "hijack-dns", - process_name = lstDnsExe - }); - - singboxConfig.route.rules.Add(new() - { - outbound = Global.DirectTag, - process_name = lstDirectExe - }); - } - - if (_config.Inbound.First().SniffingEnabled) - { - singboxConfig.route.rules.Add(new() - { - action = "sniff" - }); - singboxConfig.route.rules.Add(new() - { - protocol = new() { "dns" }, - action = "hijack-dns" - }); - } - else - { - singboxConfig.route.rules.Add(new() - { - port = new() { 53 }, - network = new() { "udp" }, - action = "hijack-dns" - }); - } - - singboxConfig.route.rules.Add(new() - { - outbound = Global.DirectTag, - clash_mode = ERuleMode.Direct.ToString() - }); - singboxConfig.route.rules.Add(new() - { - outbound = Global.ProxyTag, - clash_mode = ERuleMode.Global.ToString() - }); - - var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox; - var defaultRouting = await ConfigHandler.GetDefaultRouting(_config); - if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty()) - { - domainStrategy = defaultRouting.DomainStrategy4Singbox; - } - var resolveRule = new Rule4Sbox - { - action = "resolve", - strategy = domainStrategy - }; - if (_config.RoutingBasicItem.DomainStrategy == Global.IPOnDemand) - { - singboxConfig.route.rules.Add(resolveRule); - } - - var routing = await ConfigHandler.GetDefaultRouting(_config); - var ipRules = new List(); - if (routing != null) - { - var rules = JsonUtils.Deserialize>(routing.RuleSet); - foreach (var item1 in rules ?? []) - { - if (item1.Enabled) - { - await GenRoutingUserRule(item1, singboxConfig); - if (item1.Ip != null && item1.Ip.Count > 0) - { - ipRules.Add(item1); - } - } - } - } - if (_config.RoutingBasicItem.DomainStrategy == Global.IPIfNonMatch) - { - singboxConfig.route.rules.Add(resolveRule); - foreach (var item2 in ipRules) - { - await GenRoutingUserRule(item2, singboxConfig); - } - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return 0; - } - - private void GenRoutingDirectExe(out List lstDnsExe, out List lstDirectExe) - { - var dnsExeSet = new HashSet(StringComparer.OrdinalIgnoreCase); - var directExeSet = new HashSet(StringComparer.OrdinalIgnoreCase); - - var coreInfoResult = CoreInfoHandler.Instance.GetCoreInfo(); - - foreach (var coreConfig in coreInfoResult) - { - if (coreConfig.CoreType == ECoreType.v2rayN) - { - continue; - } - - foreach (var baseExeName in coreConfig.CoreExes) - { - if (coreConfig.CoreType != ECoreType.sing_box) - { - dnsExeSet.Add(Utils.GetExeName(baseExeName)); - } - directExeSet.Add(Utils.GetExeName(baseExeName)); - } - } - - lstDnsExe = new List(dnsExeSet); - lstDirectExe = new List(directExeSet); - } - - private async Task GenRoutingUserRule(RulesItem item, SingboxConfig singboxConfig) - { - try - { - if (item == null) - { - return 0; - } - item.OutboundTag = await GenRoutingUserRuleOutbound(item.OutboundTag, singboxConfig); - var rules = singboxConfig.route.rules; - - var rule = new Rule4Sbox(); - if (item.OutboundTag == "block") - { - rule.action = "reject"; - } - else - { - rule.outbound = item.OutboundTag; - } - - if (item.Port.IsNotEmpty()) - { - var portRanges = item.Port.Split(',').Where(it => it.Contains('-')).Select(it => it.Replace("-", ":")).ToList(); - var ports = item.Port.Split(',').Where(it => !it.Contains('-')).Select(it => it.ToInt()).ToList(); - - rule.port_range = portRanges.Count > 0 ? portRanges : null; - rule.port = ports.Count > 0 ? ports : null; - } - if (item.Network.IsNotEmpty()) - { - rule.network = Utils.String2List(item.Network); - } - if (item.Protocol?.Count > 0) - { - rule.protocol = item.Protocol; - } - if (item.InboundTag?.Count >= 0) - { - rule.inbound = item.InboundTag; - } - var rule1 = JsonUtils.DeepCopy(rule); - var rule2 = JsonUtils.DeepCopy(rule); - var rule3 = JsonUtils.DeepCopy(rule); - - var hasDomainIp = false; - if (item.Domain?.Count > 0) - { - var countDomain = 0; - foreach (var it in item.Domain) - { - if (ParseV2Domain(it, rule1)) - countDomain++; - } - if (countDomain > 0) - { - rules.Add(rule1); - hasDomainIp = true; - } - } - - if (item.Ip?.Count > 0) - { - var countIp = 0; - foreach (var it in item.Ip) - { - if (ParseV2Address(it, rule2)) - countIp++; - } - if (countIp > 0) - { - rules.Add(rule2); - hasDomainIp = true; - } - } - - if (_config.TunModeItem.EnableTun && item.Process?.Count > 0) - { - rule3.process_name = item.Process; - rules.Add(rule3); - hasDomainIp = true; - } - - if (!hasDomainIp - && (rule.port != null || rule.port_range != null || rule.protocol != null || rule.inbound != null || rule.network != null)) - { - rules.Add(rule); - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(0); - } - - private bool ParseV2Domain(string domain, Rule4Sbox rule) - { - if (domain.StartsWith("#") || domain.StartsWith("ext:") || domain.StartsWith("ext-domain:")) - { - return false; - } - else if (domain.StartsWith("geosite:")) - { - rule.geosite ??= []; - rule.geosite?.Add(domain.Substring(8)); - } - else if (domain.StartsWith("regexp:")) - { - rule.domain_regex ??= []; - rule.domain_regex?.Add(domain.Replace(Global.RoutingRuleComma, ",").Substring(7)); - } - else if (domain.StartsWith("domain:")) - { - rule.domain ??= []; - rule.domain_suffix ??= []; - rule.domain?.Add(domain.Substring(7)); - rule.domain_suffix?.Add("." + domain.Substring(7)); - } - else if (domain.StartsWith("full:")) - { - rule.domain ??= []; - rule.domain?.Add(domain.Substring(5)); - } - else if (domain.StartsWith("keyword:")) - { - rule.domain_keyword ??= []; - rule.domain_keyword?.Add(domain.Substring(8)); - } - else - { - rule.domain_keyword ??= []; - rule.domain_keyword?.Add(domain); - } - return true; - } - - private bool ParseV2Address(string address, Rule4Sbox rule) - { - if (address.StartsWith("ext:") || address.StartsWith("ext-ip:")) - { - return false; - } - else if (address.Equals("geoip:private")) - { - rule.ip_is_private = true; - } - else if (address.StartsWith("geoip:")) - { - rule.geoip ??= new(); - rule.geoip?.Add(address.Substring(6)); - } - else if (address.Equals("geoip:!private")) - { - rule.ip_is_private = false; - } - else if (address.StartsWith("geoip:!")) - { - rule.geoip ??= new(); - rule.geoip?.Add(address.Substring(6)); - rule.invert = true; - } - else - { - rule.ip_cidr ??= new(); - rule.ip_cidr?.Add(address); - } - return true; - } - - private async Task GenRoutingUserRuleOutbound(string outboundTag, SingboxConfig singboxConfig) - { - if (Global.OutboundTags.Contains(outboundTag)) - { - return outboundTag; - } - - var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag); - if (node == null - || node.ConfigType == EConfigType.Custom) - { - return Global.ProxyTag; - } - - var server = await GenServer(node); - if (server is null) - { - return Global.ProxyTag; - } - - server.tag = Global.ProxyTag + node.IndexId.ToString(); - if (server is Endpoints4Sbox endpoint) - { - singboxConfig.endpoints ??= new(); - singboxConfig.endpoints.Add(endpoint); - } - else if (server is Outbound4Sbox outbound) - { - singboxConfig.outbounds.Add(outbound); - } - - return server.tag; - } - - private async Task GenDns(SingboxConfig singboxConfig) - { - try - { - var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); - if (item != null && item.Enabled == true) - { - return await GenDnsCompatible(singboxConfig); - } - - var simpleDNSItem = _config.SimpleDNSItem; - await GenDnsServers(singboxConfig, simpleDNSItem); - await GenDnsRules(singboxConfig, simpleDNSItem); - - singboxConfig.dns ??= new Dns4Sbox(); - singboxConfig.dns.independent_cache = true; - - var routing = await ConfigHandler.GetDefaultRouting(_config); - var useDirectDns = false; - if (routing != null) - { - var rules = JsonUtils.Deserialize>(routing.RuleSet) ?? []; - - useDirectDns = rules?.LastOrDefault() is { } lastRule && - lastRule.OutboundTag == Global.DirectTag && - (lastRule.Port == "0-65535" || - lastRule.Network == "tcp,udp" || - lastRule.Ip?.Contains("0.0.0.0/0") == true); - } - singboxConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return 0; - } - - private async Task GenDnsServers(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem) - { - var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem); - - var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS); - directDns.tag = Global.SingboxDirectDNSTag; - directDns.domain_resolver = Global.SingboxFinalResolverTag; - - var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS); - remoteDns.tag = Global.SingboxRemoteDNSTag; - remoteDns.detour = Global.ProxyTag; - remoteDns.domain_resolver = Global.SingboxFinalResolverTag; - - var resolverDns = ParseDnsAddress(simpleDNSItem.SingboxOutboundsResolveDNS); - resolverDns.tag = Global.SingboxOutboundResolverTag; - resolverDns.domain_resolver = Global.SingboxFinalResolverTag; - - var hostsDns = new Server4Sbox - { - tag = Global.SingboxHostsDNSTag, - type = "hosts", - }; - if (simpleDNSItem.AddCommonHosts == true) - { - hostsDns.predefined = Global.PredefinedHosts; - } - var userHostsMap = simpleDNSItem.Hosts? - .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) - .Where(line => !string.IsNullOrWhiteSpace(line)) - .Where(line => line.Contains(' ')) - .ToDictionary( - line => - { - var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - return parts[0]; - }, - line => - { - var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - var values = parts.Skip(1).ToList(); - return values; - } - ); - - if (userHostsMap != null) - { - foreach (var kvp in userHostsMap) - { - hostsDns.predefined[kvp.Key] = kvp.Value; - } - } - - if (simpleDNSItem.UseSystemHosts == true) - { - var systemHosts = Utils.GetSystemHosts(); - if (systemHosts.Count > 0) - { - foreach (var host in systemHosts) - { - if (userHostsMap[host.Key] != null) - { - continue; - } - userHostsMap[host.Key] = new List { host.Value }; - } - } - } - - foreach (var host in hostsDns.predefined) - { - if (finalDns.server == host.Key) - { - finalDns.domain_resolver = Global.SingboxHostsDNSTag; - } - if (remoteDns.server == host.Key) - { - remoteDns.domain_resolver = Global.SingboxHostsDNSTag; - } - if (resolverDns.server == host.Key) - { - resolverDns.domain_resolver = Global.SingboxHostsDNSTag; - } - if (directDns.server == host.Key) - { - directDns.domain_resolver = Global.SingboxHostsDNSTag; - } - } - - singboxConfig.dns ??= new Dns4Sbox(); - singboxConfig.dns.servers ??= new List(); - singboxConfig.dns.servers.Add(remoteDns); - singboxConfig.dns.servers.Add(directDns); - singboxConfig.dns.servers.Add(resolverDns); - singboxConfig.dns.servers.Add(hostsDns); - - // fake ip - if (simpleDNSItem.FakeIP == true) - { - var fakeip = new Server4Sbox - { - tag = Global.SingboxFakeDNSTag, - type = "fakeip", - inet4_range = "198.18.0.0/15", - inet6_range = "fc00::/18", - }; - singboxConfig.dns.servers.Add(fakeip); - } - - return await Task.FromResult(0); - } - - private async Task GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem) - { - var finalDns = ParseDnsAddress(simpleDNSItem.SingboxFinalResolveDNS); - finalDns.tag = Global.SingboxFinalResolverTag; - singboxConfig.dns ??= new Dns4Sbox(); - singboxConfig.dns.servers ??= new List(); - singboxConfig.dns.servers.Add(finalDns); - return await Task.FromResult(finalDns); - } - - private async Task GenDnsRules(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem) - { - singboxConfig.dns ??= new Dns4Sbox(); - singboxConfig.dns.rules ??= new List(); - - singboxConfig.dns.rules.AddRange(new[] - { - new Rule4Sbox { ip_accept_any = true, server = Global.SingboxHostsDNSTag }, - new Rule4Sbox - { - server = Global.SingboxRemoteDNSTag, - strategy = simpleDNSItem.SingboxStrategy4Proxy.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Proxy, - clash_mode = ERuleMode.Global.ToString() - }, - new Rule4Sbox - { - server = Global.SingboxDirectDNSTag, - strategy = simpleDNSItem.SingboxStrategy4Direct.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Direct, - clash_mode = ERuleMode.Direct.ToString() - } - }); - - if (simpleDNSItem.BlockBindingQuery == true) - { - singboxConfig.dns.rules.Add(new() - { - query_type = new List { 64, 65 }, - action = "predefined", - rcode = "NOTIMP" - }); - } - - var routing = await ConfigHandler.GetDefaultRouting(_config); - if (routing == null) - return 0; - - var rules = JsonUtils.Deserialize>(routing.RuleSet) ?? []; - var expectedIPCidr = new List(); - var expectedIPsRegions = new List(); - var regionNames = new HashSet(); - - if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs)) - { - var ipItems = simpleDNSItem.DirectExpectedIPs - .Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) - .Select(s => s.Trim()) - .Where(s => !string.IsNullOrEmpty(s)) - .ToList(); - - foreach (var ip in ipItems) - { - if (ip.StartsWith("geoip:", StringComparison.OrdinalIgnoreCase)) - { - var region = ip["geoip:".Length..]; - if (!string.IsNullOrEmpty(region)) - { - expectedIPsRegions.Add(region); - regionNames.Add(region); - regionNames.Add($"geolocation-{region}"); - regionNames.Add($"tld-{region}"); - } - } - else - { - expectedIPCidr.Add(ip); - } - } - } - - foreach (var item in rules) - { - if (!item.Enabled || item.Domain is null || item.Domain.Count == 0) - { - continue; - } - - var rule = new Rule4Sbox(); - var validDomains = item.Domain.Count(it => ParseV2Domain(it, rule)); - if (validDomains <= 0) - { - continue; - } - - if (item.OutboundTag == Global.DirectTag) - { - rule.server = Global.SingboxDirectDNSTag; - rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Direct) ? null : simpleDNSItem.SingboxStrategy4Direct; - - if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0) - { - var geositeSet = new HashSet(rule.geosite); - if (regionNames.Intersect(geositeSet).Any()) - { - if (expectedIPsRegions.Count > 0) - { - rule.geoip = expectedIPsRegions; - } - if (expectedIPCidr.Count > 0) - { - rule.ip_cidr = expectedIPCidr; - } - } - } - } - else if (item.OutboundTag == Global.BlockTag) - { - rule.action = "predefined"; - rule.rcode = "NXDOMAIN"; - } - else - { - if (simpleDNSItem.FakeIP == true) - { - var rule4Fake = JsonUtils.DeepCopy(rule); - rule4Fake.server = Global.SingboxFakeDNSTag; - singboxConfig.dns.rules.Add(rule4Fake); - } - rule.server = Global.SingboxRemoteDNSTag; - rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Proxy) ? null : simpleDNSItem.SingboxStrategy4Proxy; - } - - singboxConfig.dns.rules.Add(rule); - } - - return 0; - } - - private async Task GenDnsCompatible(SingboxConfig singboxConfig) - { - try - { - var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); - var strDNS = string.Empty; - if (_config.TunModeItem.EnableTun) - { - strDNS = string.IsNullOrEmpty(item?.TunDNS) ? EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName) : item?.TunDNS; - } - else - { - strDNS = string.IsNullOrEmpty(item?.NormalDNS) ? EmbedUtils.GetEmbedText(Global.DNSSingboxNormalFileName) : item?.NormalDNS; - } - - var dns4Sbox = JsonUtils.Deserialize(strDNS); - if (dns4Sbox is null) - { - return 0; - } - singboxConfig.dns = dns4Sbox; - - if (dns4Sbox.servers != null && dns4Sbox.servers.Count > 0 && dns4Sbox.servers.First().address.IsNullOrEmpty()) - { - await GenDnsDomainsCompatible(singboxConfig, item); - } - else - { - await GenDnsDomainsLegacyCompatible(singboxConfig, item); - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return 0; - } - - private async Task GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem) - { - var dns4Sbox = singboxConfig.dns ?? new(); - dns4Sbox.servers ??= []; - dns4Sbox.rules ??= []; - - var tag = Global.SingboxFinalResolverTag; - var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress; - - var localDnsServer = ParseDnsAddress(localDnsAddress); - localDnsServer.tag = tag; - - dns4Sbox.servers.Add(localDnsServer); - - singboxConfig.dns = dns4Sbox; - return await Task.FromResult(0); - } - - private async Task GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem) - { - var dns4Sbox = singboxConfig.dns ?? new(); - dns4Sbox.servers ??= []; - dns4Sbox.rules ??= []; - - var tag = Global.SingboxFinalResolverTag; - dns4Sbox.servers.Add(new() - { - tag = tag, - address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, - detour = Global.DirectTag, - strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom, - }); - dns4Sbox.rules.Insert(0, new() - { - server = tag, - clash_mode = ERuleMode.Direct.ToString() - }); - dns4Sbox.rules.Insert(0, new() - { - server = dns4Sbox.servers.Where(t => t.detour == Global.ProxyTag).Select(t => t.tag).FirstOrDefault() ?? "remote", - clash_mode = ERuleMode.Global.ToString() - }); - - var lstDomain = singboxConfig.outbounds - .Where(t => t.server.IsNotEmpty() && Utils.IsDomain(t.server)) - .Select(t => t.server) - .Distinct() - .ToList(); - if (lstDomain != null && lstDomain.Count > 0) - { - dns4Sbox.rules.Insert(0, new() - { - server = tag, - domain = lstDomain - }); - } - - singboxConfig.dns = dns4Sbox; - return await Task.FromResult(0); - } - - private static Server4Sbox? ParseDnsAddress(string address) - { - var addressFirst = address?.Split(address.Contains(',') ? ',' : ';').FirstOrDefault()?.Trim(); - if (string.IsNullOrEmpty(addressFirst)) - { - return null; - } - - var server = new Server4Sbox(); - - if (addressFirst is "local" or "localhost") - { - server.type = "local"; - return server; - } - - if (addressFirst.StartsWith("dhcp://", StringComparison.OrdinalIgnoreCase)) - { - var interface_name = addressFirst.Substring(7); - server.type = "dhcp"; - server.Interface = interface_name == "auto" ? null : interface_name; - return server; - } - - if (!addressFirst.Contains("://")) - { - // udp dns - server.type = "udp"; - server.server = addressFirst; - return server; - } - - try - { - var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal); - server.type = addressFirst.Substring(0, protocolEndIndex).ToLower(); - - var uri = new Uri(addressFirst); - server.server = uri.Host; - - if (!uri.IsDefaultPort) - { - server.server_port = uri.Port; - } - - if ((server.type == "https" || server.type == "h3") && !string.IsNullOrEmpty(uri.AbsolutePath) && uri.AbsolutePath != "/") - { - server.path = uri.AbsolutePath; - } - } - catch (UriFormatException) - { - var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal); - if (protocolEndIndex > 0) - { - server.type = addressFirst.Substring(0, protocolEndIndex).ToLower(); - var remaining = addressFirst.Substring(protocolEndIndex + 3); - - var portIndex = remaining.IndexOf(':'); - var pathIndex = remaining.IndexOf('/'); - - if (portIndex > 0) - { - server.server = remaining.Substring(0, portIndex); - var portPart = pathIndex > portIndex - ? remaining.Substring(portIndex + 1, pathIndex - portIndex - 1) - : remaining.Substring(portIndex + 1); - - if (int.TryParse(portPart, out var parsedPort)) - { - server.server_port = parsedPort; - } - } - else if (pathIndex > 0) - { - server.server = remaining.Substring(0, pathIndex); - } - else - { - server.server = remaining; - } - - if (pathIndex > 0 && (server.type == "https" || server.type == "h3")) - { - server.path = remaining.Substring(pathIndex); - } - } - } - - return server; - } - - private async Task GenExperimental(SingboxConfig singboxConfig) - { - //if (_config.guiItem.enableStatistics) - { - singboxConfig.experimental ??= new Experimental4Sbox(); - singboxConfig.experimental.clash_api = new Clash_Api4Sbox() - { - external_controller = $"{Global.Loopback}:{AppHandler.Instance.StatePort2}", - }; - } - - if (_config.CoreBasicItem.EnableCacheFile4Sbox) - { - singboxConfig.experimental ??= new Experimental4Sbox(); - singboxConfig.experimental.cache_file = new CacheFile4Sbox() - { - enabled = true, - path = Utils.GetBinPath("cache.db"), - store_fakeip = _config.SimpleDNSItem.FakeIP == true - }; - } - - return await Task.FromResult(0); - } - - private async Task ConvertGeo2Ruleset(SingboxConfig singboxConfig) - { - static void AddRuleSets(List ruleSets, List? rule_set) - { - if (rule_set != null) - ruleSets.AddRange(rule_set); - } - var geosite = "geosite"; - var geoip = "geoip"; - var ruleSets = new List(); - - //convert route geosite & geoip to ruleset - foreach (var rule in singboxConfig.route.rules.Where(t => t.geosite?.Count > 0).ToList() ?? []) - { - rule.rule_set ??= new List(); - rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList()); - rule.geosite = null; - AddRuleSets(ruleSets, rule.rule_set); - } - foreach (var rule in singboxConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? []) - { - rule.rule_set ??= new List(); - rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList()); - rule.geoip = null; - AddRuleSets(ruleSets, rule.rule_set); - } - - //convert dns geosite & geoip to ruleset - foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geosite?.Count > 0).ToList() ?? []) - { - rule.rule_set ??= new List(); - rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList()); - rule.geosite = null; - } - foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geoip?.Count > 0).ToList() ?? []) - { - rule.rule_set ??= new List(); - rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList()); - rule.geoip = null; - } - foreach (var dnsRule in singboxConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? []) - { - AddRuleSets(ruleSets, dnsRule.rule_set); - } - //rules in rules - foreach (var item in singboxConfig.dns?.rules.Where(t => t.rules?.Count > 0).Select(t => t.rules).ToList() ?? []) - { - foreach (var item2 in item ?? []) - { - AddRuleSets(ruleSets, item2.rule_set); - } - } - - //load custom ruleset file - List customRulesets = []; - - var routing = await ConfigHandler.GetDefaultRouting(_config); - if (routing.CustomRulesetPath4Singbox.IsNotEmpty()) - { - var result = EmbedUtils.LoadResource(routing.CustomRulesetPath4Singbox); - if (result.IsNotEmpty()) - { - customRulesets = (JsonUtils.Deserialize>(result) ?? []) - .Where(t => t.tag != null) - .Where(t => t.type != null) - .Where(t => t.format != null) - .ToList(); - } - } - - //Local srs files address - var localSrss = Utils.GetBinPath("srss"); - - //Add ruleset srs - singboxConfig.route.rule_set = []; - foreach (var item in new HashSet(ruleSets)) - { - if (item.IsNullOrEmpty()) - { continue; } - var customRuleset = customRulesets.FirstOrDefault(t => t.tag != null && t.tag.Equals(item)); - if (customRuleset is null) - { - var pathSrs = Path.Combine(localSrss, $"{item}.srs"); - if (File.Exists(pathSrs)) - { - customRuleset = new() - { - type = "local", - format = "binary", - tag = item, - path = pathSrs - }; - } - else - { - var srsUrl = string.IsNullOrEmpty(_config.ConstItem.SrsSourceUrl) - ? Global.SingboxRulesetUrl - : _config.ConstItem.SrsSourceUrl; - - customRuleset = new() - { - type = "remote", - format = "binary", - tag = item, - url = string.Format(srsUrl, item.StartsWith(geosite) ? geosite : geoip, item), - download_detour = Global.ProxyTag - }; - } - } - singboxConfig.route.rule_set.Add(customRuleset); - } - - return 0; - } - - private async Task ApplyFullConfigTemplate(SingboxConfig singboxConfig) - { - var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box); - if (fullConfigTemplate == null || !fullConfigTemplate.Enabled) - { - return JsonUtils.Serialize(singboxConfig); - } - - var fullConfigTemplateItem = _config.TunModeItem.EnableTun ? fullConfigTemplate.TunConfig : fullConfigTemplate.Config; - if (fullConfigTemplateItem.IsNullOrEmpty()) - { - return JsonUtils.Serialize(singboxConfig); - } - - var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplateItem); - if (fullConfigTemplateNode == null) - { - return JsonUtils.Serialize(singboxConfig); - } - - // Process outbounds - var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray(); - foreach (var outbound in singboxConfig.outbounds) - { - if (outbound.type.ToLower() is "direct" or "block") - { - if (fullConfigTemplate.AddProxyOnly == true) - { - continue; - } - } - else if (outbound.detour.IsNullOrEmpty() && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) && !Utils.IsPrivateNetwork(outbound.server ?? string.Empty)) - { - outbound.detour = fullConfigTemplate.ProxyDetour; - } - customOutboundsNode.Add(JsonUtils.DeepCopy(outbound)); - } - fullConfigTemplateNode["outbounds"] = customOutboundsNode; - - // Process endpoints - if (singboxConfig.endpoints != null && singboxConfig.endpoints.Count > 0) - { - var customEndpointsNode = fullConfigTemplateNode["endpoints"] is JsonArray endpoints ? endpoints : new JsonArray(); - foreach (var endpoint in singboxConfig.endpoints) - { - if (endpoint.detour.IsNullOrEmpty() && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty())) - { - endpoint.detour = fullConfigTemplate.ProxyDetour; - } - customEndpointsNode.Add(JsonUtils.DeepCopy(endpoint)); - } - fullConfigTemplateNode["endpoints"] = customEndpointsNode; - } - - return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode)); - } - - #endregion private gen function -} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs deleted file mode 100644 index 62c2e1822b..0000000000 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ /dev/null @@ -1,1953 +0,0 @@ -using System.Net; -using System.Net.NetworkInformation; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -namespace ServiceLib.Services.CoreConfig; - -public class CoreConfigV2rayService -{ - private Config _config; - private static readonly string _tag = "CoreConfigV2rayService"; - - public CoreConfigV2rayService(Config config) - { - _config = config; - } - - #region public gen function - - public async Task GenerateClientConfigContent(ProfileItem node) - { - var ret = new RetResult(); - try - { - if (node == null - || node.Port <= 0) - { - ret.Msg = ResUI.CheckServerSettings; - return ret; - } - - if (node.GetNetwork() is nameof(ETransport.quic)) - { - ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; - return ret; - } - - ret.Msg = ResUI.InitialConfiguration; - - var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); - if (result.IsNullOrEmpty()) - { - ret.Msg = ResUI.FailedGetDefaultConfiguration; - return ret; - } - - var v2rayConfig = JsonUtils.Deserialize(result); - if (v2rayConfig == null) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - - await GenLog(v2rayConfig); - - await GenInbounds(v2rayConfig); - - await GenOutbound(node, v2rayConfig.outbounds.First()); - - await GenMoreOutbounds(node, v2rayConfig); - - await GenRouting(v2rayConfig); - - await GenDns(node, v2rayConfig); - - await GenStatistic(v2rayConfig); - - ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); - ret.Success = true; - ret.Data = await ApplyFullConfigTemplate(v2rayConfig); - return ret; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - } - - public async Task GenerateClientMultipleLoadConfig(List selecteds, EMultipleLoad multipleLoad) - { - var ret = new RetResult(); - - try - { - if (_config == null) - { - ret.Msg = ResUI.CheckServerSettings; - return ret; - } - - ret.Msg = ResUI.InitialConfiguration; - - string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); - string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); - if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty()) - { - ret.Msg = ResUI.FailedGetDefaultConfiguration; - return ret; - } - - var v2rayConfig = JsonUtils.Deserialize(result); - if (v2rayConfig == null) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - - await GenLog(v2rayConfig); - await GenInbounds(v2rayConfig); - await GenRouting(v2rayConfig); - await GenDns(null, v2rayConfig); - await GenStatistic(v2rayConfig); - v2rayConfig.outbounds.RemoveAt(0); - - var proxyProfiles = new List(); - foreach (var it in selecteds) - { - if (it.ConfigType == EConfigType.Custom) - { - continue; - } - if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) - { - continue; - } - if (it.Port <= 0) - { - continue; - } - var item = await AppHandler.Instance.GetProfileItem(it.IndexId); - if (item is null) - { - continue; - } - if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS) - { - if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id)) - { - continue; - } - } - if (item.ConfigType == EConfigType.Shadowsocks - && !Global.SsSecuritiesInSingbox.Contains(item.Security)) - { - continue; - } - if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow)) - { - continue; - } - - //outbound - proxyProfiles.Add(item); - } - if (proxyProfiles.Count <= 0) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - await GenOutboundsList(proxyProfiles, v2rayConfig); - - //add balancers - await GenBalancer(v2rayConfig, multipleLoad); - - var balancer = v2rayConfig.routing.balancers.First(); - - //add rule - var rules = v2rayConfig.routing.rules.Where(t => t.outboundTag == Global.ProxyTag).ToList(); - if (rules?.Count > 0) - { - foreach (var rule in rules) - { - rule.outboundTag = null; - rule.balancerTag = balancer.tag; - } - } - if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) - { - v2rayConfig.routing.rules.Add(new() - { - ip = ["0.0.0.0/0", "::/0"], - balancerTag = balancer.tag, - type = "field" - }); - } - else - { - v2rayConfig.routing.rules.Add(new() - { - network = "tcp,udp", - balancerTag = balancer.tag, - type = "field" - }); - } - - ret.Success = true; - - ret.Data = await ApplyFullConfigTemplate(v2rayConfig, true); - return ret; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - } - - public async Task GenerateClientSpeedtestConfig(List selecteds) - { - var ret = new RetResult(); - try - { - if (_config == null) - { - ret.Msg = ResUI.CheckServerSettings; - return ret; - } - - ret.Msg = ResUI.InitialConfiguration; - - var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); - var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); - if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty()) - { - ret.Msg = ResUI.FailedGetDefaultConfiguration; - return ret; - } - - var v2rayConfig = JsonUtils.Deserialize(result); - if (v2rayConfig == null) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - List lstIpEndPoints = new(); - List lstTcpConns = new(); - try - { - lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); - lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners()); - lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()); - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - - await GenLog(v2rayConfig); - v2rayConfig.inbounds.Clear(); - v2rayConfig.outbounds.Clear(); - v2rayConfig.routing.rules.Clear(); - - var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest); - - foreach (var it in selecteds) - { - if (it.ConfigType == EConfigType.Custom) - { - continue; - } - if (it.Port <= 0) - { - continue; - } - var item = await AppHandler.Instance.GetProfileItem(it.IndexId); - if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS) - { - if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id)) - { - continue; - } - } - - //find unused port - var port = initPort; - for (var k = initPort; k < Global.MaxPort; k++) - { - if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) - { - continue; - } - if (lstTcpConns?.FindIndex(_it => _it.LocalEndPoint.Port == k) >= 0) - { - continue; - } - //found - port = k; - initPort = port + 1; - break; - } - - //Port In Used - if (lstIpEndPoints?.FindIndex(_it => _it.Port == port) >= 0) - { - continue; - } - it.Port = port; - it.AllowTest = true; - - //outbound - if (item is null) - { - continue; - } - if (item.ConfigType == EConfigType.Shadowsocks - && !Global.SsSecuritiesInXray.Contains(item.Security)) - { - continue; - } - if (item.ConfigType == EConfigType.VLESS - && !Global.Flows.Contains(item.Flow)) - { - continue; - } - if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan - && item.StreamSecurity == Global.StreamSecurityReality - && item.PublicKey.IsNullOrEmpty()) - { - continue; - } - - //inbound - Inbounds4Ray inbound = new() - { - listen = Global.Loopback, - port = port, - protocol = EInboundProtocol.mixed.ToString(), - }; - inbound.tag = inbound.protocol + inbound.port.ToString(); - v2rayConfig.inbounds.Add(inbound); - - var outbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(item, outbound); - outbound.tag = Global.ProxyTag + inbound.port.ToString(); - v2rayConfig.outbounds.Add(outbound); - - //rule - RulesItem4Ray rule = new() - { - inboundTag = new List { inbound.tag }, - outboundTag = outbound.tag, - type = "field" - }; - v2rayConfig.routing.rules.Add(rule); - } - - //ret.Msg =string.Format(ResUI.SuccessfulConfiguration"), node.getSummary()); - ret.Success = true; - ret.Data = JsonUtils.Serialize(v2rayConfig); - return ret; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - } - - public async Task GenerateClientSpeedtestConfig(ProfileItem node, int port) - { - var ret = new RetResult(); - try - { - if (node is not { Port: > 0 }) - { - ret.Msg = ResUI.CheckServerSettings; - return ret; - } - - if (node.GetNetwork() is nameof(ETransport.quic)) - { - ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; - return ret; - } - - var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); - if (result.IsNullOrEmpty()) - { - ret.Msg = ResUI.FailedGetDefaultConfiguration; - return ret; - } - - var v2rayConfig = JsonUtils.Deserialize(result); - if (v2rayConfig == null) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - - await GenLog(v2rayConfig); - await GenOutbound(node, v2rayConfig.outbounds.First()); - await GenMoreOutbounds(node, v2rayConfig); - - v2rayConfig.routing.rules.Clear(); - v2rayConfig.inbounds.Clear(); - v2rayConfig.inbounds.Add(new() - { - tag = $"{EInboundProtocol.socks}{port}", - listen = Global.Loopback, - port = port, - protocol = EInboundProtocol.mixed.ToString(), - }); - - ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); - ret.Success = true; - ret.Data = JsonUtils.Serialize(v2rayConfig); - return ret; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - } - - #endregion public gen function - - #region private gen function - - private async Task GenLog(V2rayConfig v2rayConfig) - { - try - { - if (_config.CoreBasicItem.LogEnabled) - { - var dtNow = DateTime.Now; - v2rayConfig.log.loglevel = _config.CoreBasicItem.Loglevel; - v2rayConfig.log.access = Utils.GetLogPath($"Vaccess_{dtNow:yyyy-MM-dd}.txt"); - v2rayConfig.log.error = Utils.GetLogPath($"Verror_{dtNow:yyyy-MM-dd}.txt"); - } - else - { - v2rayConfig.log.loglevel = _config.CoreBasicItem.Loglevel; - v2rayConfig.log.access = null; - v2rayConfig.log.error = null; - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(0); - } - - private async Task GenInbounds(V2rayConfig v2rayConfig) - { - try - { - var listen = "0.0.0.0"; - v2rayConfig.inbounds = []; - - var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.socks, true); - v2rayConfig.inbounds.Add(inbound); - - if (_config.Inbound.First().SecondLocalPortEnabled) - { - var inbound2 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks2, true); - v2rayConfig.inbounds.Add(inbound2); - } - - if (_config.Inbound.First().AllowLANConn) - { - if (_config.Inbound.First().NewPort4LAN) - { - var inbound3 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks3, true); - inbound3.listen = listen; - v2rayConfig.inbounds.Add(inbound3); - - //auth - if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty()) - { - inbound3.settings.auth = "password"; - inbound3.settings.accounts = new List { new AccountsItem4Ray() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } }; - } - } - else - { - inbound.listen = listen; - } - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(0); - } - - private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks) - { - string result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound); - if (result.IsNullOrEmpty()) - { - return new(); - } - - var inbound = JsonUtils.Deserialize(result); - if (inbound == null) - { - return new(); - } - inbound.tag = protocol.ToString(); - inbound.port = inItem.LocalPort + (int)protocol; - inbound.protocol = EInboundProtocol.mixed.ToString(); - inbound.settings.udp = inItem.UdpEnabled; - inbound.sniffing.enabled = inItem.SniffingEnabled; - inbound.sniffing.destOverride = inItem.DestOverride; - inbound.sniffing.routeOnly = inItem.RouteOnly; - - return inbound; - } - - private async Task GenRouting(V2rayConfig v2rayConfig) - { - try - { - if (v2rayConfig.routing?.rules != null) - { - v2rayConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy; - v2rayConfig.routing.domainMatcher = _config.RoutingBasicItem.DomainMatcher.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainMatcher; - - var routing = await ConfigHandler.GetDefaultRouting(_config); - if (routing != null) - { - if (routing.DomainStrategy.IsNotEmpty()) - { - v2rayConfig.routing.domainStrategy = routing.DomainStrategy; - } - var rules = JsonUtils.Deserialize>(routing.RuleSet); - foreach (var item in rules) - { - if (item.Enabled) - { - var item2 = JsonUtils.Deserialize(JsonUtils.Serialize(item)); - await GenRoutingUserRule(item2, v2rayConfig); - } - } - } - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return 0; - } - - private async Task GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig) - { - try - { - if (rule == null) - { - return 0; - } - rule.outboundTag = await GenRoutingUserRuleOutbound(rule.outboundTag, v2rayConfig); - - if (rule.port.IsNullOrEmpty()) - { - rule.port = null; - } - if (rule.network.IsNullOrEmpty()) - { - rule.network = null; - } - if (rule.domain?.Count == 0) - { - rule.domain = null; - } - if (rule.ip?.Count == 0) - { - rule.ip = null; - } - if (rule.protocol?.Count == 0) - { - rule.protocol = null; - } - if (rule.inboundTag?.Count == 0) - { - rule.inboundTag = null; - } - - var hasDomainIp = false; - if (rule.domain?.Count > 0) - { - var it = JsonUtils.DeepCopy(rule); - it.ip = null; - it.type = "field"; - for (var k = it.domain.Count - 1; k >= 0; k--) - { - if (it.domain[k].StartsWith("#")) - { - it.domain.RemoveAt(k); - } - it.domain[k] = it.domain[k].Replace(Global.RoutingRuleComma, ","); - } - v2rayConfig.routing.rules.Add(it); - hasDomainIp = true; - } - if (rule.ip?.Count > 0) - { - var it = JsonUtils.DeepCopy(rule); - it.domain = null; - it.type = "field"; - v2rayConfig.routing.rules.Add(it); - hasDomainIp = true; - } - if (!hasDomainIp) - { - if (rule.port.IsNotEmpty() - || rule.protocol?.Count > 0 - || rule.inboundTag?.Count > 0 - || rule.network != null - ) - { - var it = JsonUtils.DeepCopy(rule); - it.type = "field"; - v2rayConfig.routing.rules.Add(it); - } - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(0); - } - - private async Task GenRoutingUserRuleOutbound(string outboundTag, V2rayConfig v2rayConfig) - { - if (Global.OutboundTags.Contains(outboundTag)) - { - return outboundTag; - } - - var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag); - if (node == null - || node.ConfigType == EConfigType.Custom - || node.ConfigType == EConfigType.Hysteria2 - || node.ConfigType == EConfigType.TUIC - || node.ConfigType == EConfigType.Anytls) - { - return Global.ProxyTag; - } - - var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); - var outbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(node, outbound); - outbound.tag = Global.ProxyTag + node.IndexId.ToString(); - v2rayConfig.outbounds.Add(outbound); - - return outbound.tag; - } - - private async Task GenOutbound(ProfileItem node, Outbounds4Ray outbound) - { - try - { - var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled; - switch (node.ConfigType) - { - case EConfigType.VMess: - { - VnextItem4Ray vnextItem; - if (outbound.settings.vnext.Count <= 0) - { - vnextItem = new VnextItem4Ray(); - outbound.settings.vnext.Add(vnextItem); - } - else - { - vnextItem = outbound.settings.vnext.First(); - } - vnextItem.address = node.Address; - vnextItem.port = node.Port; - - UsersItem4Ray usersItem; - if (vnextItem.users.Count <= 0) - { - usersItem = new UsersItem4Ray(); - vnextItem.users.Add(usersItem); - } - else - { - usersItem = vnextItem.users.First(); - } - - usersItem.id = node.Id; - usersItem.alterId = node.AlterId; - usersItem.email = Global.UserEMail; - if (Global.VmessSecurities.Contains(node.Security)) - { - usersItem.security = node.Security; - } - else - { - usersItem.security = Global.DefaultSecurity; - } - - await GenOutboundMux(node, outbound, muxEnabled, muxEnabled); - - outbound.settings.servers = null; - break; - } - case EConfigType.Shadowsocks: - { - ServersItem4Ray serversItem; - if (outbound.settings.servers.Count <= 0) - { - serversItem = new ServersItem4Ray(); - outbound.settings.servers.Add(serversItem); - } - else - { - serversItem = outbound.settings.servers.First(); - } - serversItem.address = node.Address; - serversItem.port = node.Port; - serversItem.password = node.Id; - serversItem.method = AppHandler.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : "none"; - - serversItem.ota = false; - serversItem.level = 1; - - await GenOutboundMux(node, outbound); - - outbound.settings.vnext = null; - break; - } - case EConfigType.SOCKS: - case EConfigType.HTTP: - { - ServersItem4Ray serversItem; - if (outbound.settings.servers.Count <= 0) - { - serversItem = new ServersItem4Ray(); - outbound.settings.servers.Add(serversItem); - } - else - { - serversItem = outbound.settings.servers.First(); - } - serversItem.address = node.Address; - serversItem.port = node.Port; - serversItem.method = null; - serversItem.password = null; - - if (node.Security.IsNotEmpty() - && node.Id.IsNotEmpty()) - { - SocksUsersItem4Ray socksUsersItem = new() - { - user = node.Security, - pass = node.Id, - level = 1 - }; - - serversItem.users = new List() { socksUsersItem }; - } - - await GenOutboundMux(node, outbound); - - outbound.settings.vnext = null; - break; - } - case EConfigType.VLESS: - { - VnextItem4Ray vnextItem; - if (outbound.settings.vnext?.Count <= 0) - { - vnextItem = new VnextItem4Ray(); - outbound.settings.vnext.Add(vnextItem); - } - else - { - vnextItem = outbound.settings.vnext.First(); - } - vnextItem.address = node.Address; - vnextItem.port = node.Port; - - UsersItem4Ray usersItem; - if (vnextItem.users.Count <= 0) - { - usersItem = new UsersItem4Ray(); - vnextItem.users.Add(usersItem); - } - else - { - usersItem = vnextItem.users.First(); - } - usersItem.id = node.Id; - usersItem.email = Global.UserEMail; - usersItem.encryption = node.Security; - - if (node.Flow.IsNullOrEmpty()) - { - await GenOutboundMux(node, outbound, muxEnabled, muxEnabled); - } - else - { - usersItem.flow = node.Flow; - await GenOutboundMux(node, outbound, false, muxEnabled); - } - outbound.settings.servers = null; - break; - } - case EConfigType.Trojan: - { - ServersItem4Ray serversItem; - if (outbound.settings.servers.Count <= 0) - { - serversItem = new ServersItem4Ray(); - outbound.settings.servers.Add(serversItem); - } - else - { - serversItem = outbound.settings.servers.First(); - } - serversItem.address = node.Address; - serversItem.port = node.Port; - serversItem.password = node.Id; - - serversItem.ota = false; - serversItem.level = 1; - - await GenOutboundMux(node, outbound); - - outbound.settings.vnext = null; - break; - } - case EConfigType.WireGuard: - { - var peer = new WireguardPeer4Ray - { - publicKey = node.PublicKey, - endpoint = node.Address + ":" + node.Port.ToString() - }; - var setting = new Outboundsettings4Ray - { - address = Utils.String2List(node.RequestHost), - secretKey = node.Id, - reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(), - mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(), - peers = new List { peer } - }; - outbound.settings = setting; - outbound.settings.vnext = null; - outbound.settings.servers = null; - break; - } - } - - outbound.protocol = Global.ProtocolTypes[node.ConfigType]; - await GenBoundStreamSettings(node, outbound); - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return 0; - } - - private async Task GenOutboundMux(ProfileItem node, Outbounds4Ray outbound, bool enabledTCP = false, bool enabledUDP = false) - { - try - { - outbound.mux.enabled = false; - outbound.mux.concurrency = -1; - - if (enabledTCP) - { - outbound.mux.enabled = true; - outbound.mux.concurrency = _config.Mux4RayItem.Concurrency; - } - else if (enabledUDP) - { - outbound.mux.enabled = true; - outbound.mux.xudpConcurrency = _config.Mux4RayItem.XudpConcurrency; - outbound.mux.xudpProxyUDP443 = _config.Mux4RayItem.XudpProxyUDP443; - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(0); - } - - private async Task GenBoundStreamSettings(ProfileItem node, Outbounds4Ray outbound) - { - try - { - var streamSettings = outbound.streamSettings; - streamSettings.network = node.GetNetwork(); - var host = node.RequestHost.TrimEx(); - var path = node.Path.TrimEx(); - var sni = node.Sni.TrimEx(); - var useragent = ""; - if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty()) - { - try - { - useragent = Global.UserAgentTexts[_config.CoreBasicItem.DefUserAgent]; - } - catch (KeyNotFoundException) - { - useragent = _config.CoreBasicItem.DefUserAgent; - } - } - - //if tls - if (node.StreamSecurity == Global.StreamSecurity) - { - streamSettings.security = node.StreamSecurity; - - TlsSettings4Ray tlsSettings = new() - { - allowInsecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure), - alpn = node.GetAlpn(), - fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint - }; - if (sni.IsNotEmpty()) - { - tlsSettings.serverName = sni; - } - else if (host.IsNotEmpty()) - { - tlsSettings.serverName = Utils.String2List(host)?.First(); - } - streamSettings.tlsSettings = tlsSettings; - } - - //if Reality - if (node.StreamSecurity == Global.StreamSecurityReality) - { - streamSettings.security = node.StreamSecurity; - - TlsSettings4Ray realitySettings = new() - { - fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint, - serverName = sni, - publicKey = node.PublicKey, - shortId = node.ShortId, - spiderX = node.SpiderX, - mldsa65Verify = node.Mldsa65Verify, - show = false, - }; - - streamSettings.realitySettings = realitySettings; - } - - //streamSettings - switch (node.GetNetwork()) - { - case nameof(ETransport.kcp): - KcpSettings4Ray kcpSettings = new() - { - mtu = _config.KcpItem.Mtu, - tti = _config.KcpItem.Tti - }; - - kcpSettings.uplinkCapacity = _config.KcpItem.UplinkCapacity; - kcpSettings.downlinkCapacity = _config.KcpItem.DownlinkCapacity; - - kcpSettings.congestion = _config.KcpItem.Congestion; - kcpSettings.readBufferSize = _config.KcpItem.ReadBufferSize; - kcpSettings.writeBufferSize = _config.KcpItem.WriteBufferSize; - kcpSettings.header = new Header4Ray - { - type = node.HeaderType, - domain = host.IsNullOrEmpty() ? null : host - }; - if (path.IsNotEmpty()) - { - kcpSettings.seed = path; - } - streamSettings.kcpSettings = kcpSettings; - break; - //ws - case nameof(ETransport.ws): - WsSettings4Ray wsSettings = new(); - wsSettings.headers = new Headers4Ray(); - - if (host.IsNotEmpty()) - { - wsSettings.host = host; - wsSettings.headers.Host = host; - } - if (path.IsNotEmpty()) - { - wsSettings.path = path; - } - if (useragent.IsNotEmpty()) - { - wsSettings.headers.UserAgent = useragent; - } - streamSettings.wsSettings = wsSettings; - - break; - //httpupgrade - case nameof(ETransport.httpupgrade): - HttpupgradeSettings4Ray httpupgradeSettings = new(); - - if (path.IsNotEmpty()) - { - httpupgradeSettings.path = path; - } - if (host.IsNotEmpty()) - { - httpupgradeSettings.host = host; - } - streamSettings.httpupgradeSettings = httpupgradeSettings; - - break; - //xhttp - case nameof(ETransport.xhttp): - streamSettings.network = ETransport.xhttp.ToString(); - XhttpSettings4Ray xhttpSettings = new(); - - if (path.IsNotEmpty()) - { - xhttpSettings.path = path; - } - if (host.IsNotEmpty()) - { - xhttpSettings.host = host; - } - if (node.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(node.HeaderType)) - { - xhttpSettings.mode = node.HeaderType; - } - if (node.Extra.IsNotEmpty()) - { - xhttpSettings.extra = JsonUtils.ParseJson(node.Extra); - } - - streamSettings.xhttpSettings = xhttpSettings; - await GenOutboundMux(node, outbound); - - break; - //h2 - case nameof(ETransport.h2): - HttpSettings4Ray httpSettings = new(); - - if (host.IsNotEmpty()) - { - httpSettings.host = Utils.String2List(host); - } - httpSettings.path = path; - - streamSettings.httpSettings = httpSettings; - - break; - //quic - case nameof(ETransport.quic): - QuicSettings4Ray quicsettings = new() - { - security = host, - key = path, - header = new Header4Ray - { - type = node.HeaderType - } - }; - streamSettings.quicSettings = quicsettings; - if (node.StreamSecurity == Global.StreamSecurity) - { - if (sni.IsNotEmpty()) - { - streamSettings.tlsSettings.serverName = sni; - } - else - { - streamSettings.tlsSettings.serverName = node.Address; - } - } - break; - - case nameof(ETransport.grpc): - GrpcSettings4Ray grpcSettings = new() - { - authority = host.IsNullOrEmpty() ? null : host, - serviceName = path, - multiMode = node.HeaderType == Global.GrpcMultiMode, - idle_timeout = _config.GrpcItem.IdleTimeout, - health_check_timeout = _config.GrpcItem.HealthCheckTimeout, - permit_without_stream = _config.GrpcItem.PermitWithoutStream, - initial_windows_size = _config.GrpcItem.InitialWindowsSize, - }; - streamSettings.grpcSettings = grpcSettings; - break; - - default: - //tcp - if (node.HeaderType == Global.TcpHeaderHttp) - { - TcpSettings4Ray tcpSettings = new() - { - header = new Header4Ray - { - type = node.HeaderType - } - }; - - //request Host - string request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName); - string[] arrHost = host.Split(','); - string host2 = string.Join(",".AppendQuotes(), arrHost); - request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}"); - request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}"); - //Path - string pathHttp = @"/"; - if (path.IsNotEmpty()) - { - string[] arrPath = path.Split(','); - pathHttp = string.Join(",".AppendQuotes(), arrPath); - } - request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}"); - tcpSettings.header.request = JsonUtils.Deserialize(request); - - streamSettings.tcpSettings = tcpSettings; - } - break; - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return 0; - } - - private async Task GenDns(ProfileItem? node, V2rayConfig v2rayConfig) - { - try - { - var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray); - if (item != null && item.Enabled == true) - { - var result = await GenDnsCompatible(node, v2rayConfig); - - if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) - { - // DNS routing - v2rayConfig.dns.tag = Global.DnsTag; - v2rayConfig.routing.rules.Add(new RulesItem4Ray - { - type = "field", - inboundTag = new List { Global.DnsTag }, - outboundTag = Global.ProxyTag, - }); - } - - return result; - } - var simpleDNSItem = _config.SimpleDNSItem; - var domainStrategy4Freedom = simpleDNSItem?.RayStrategy4Freedom; - - //Outbound Freedom domainStrategy - if (domainStrategy4Freedom.IsNotEmpty()) - { - var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag }); - if (outbound != null) - { - outbound.settings = new() - { - domainStrategy = domainStrategy4Freedom, - userLevel = 0 - }; - } - } - - await GenDnsServers(node, v2rayConfig, simpleDNSItem); - await GenDnsHosts(v2rayConfig, simpleDNSItem); - - if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) - { - // DNS routing - v2rayConfig.dns.tag = Global.DnsTag; - v2rayConfig.routing.rules.Add(new RulesItem4Ray - { - type = "field", - inboundTag = new List { Global.DnsTag }, - outboundTag = Global.ProxyTag, - }); - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return 0; - } - - private async Task GenDnsServers(ProfileItem? node, V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem) - { - static List ParseDnsAddresses(string? dnsInput, string defaultAddress) - { - var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';') - .Select(addr => addr.Trim()) - .Where(addr => !string.IsNullOrEmpty(addr)) - .Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr) - .Distinct() - .ToList() ?? new List { defaultAddress }; - return addresses.Count > 0 ? addresses : new List { defaultAddress }; - } - - static object CreateDnsServer(string dnsAddress, List domains, List? expectedIPs = null) - { - var dnsServer = new DnsServer4Ray - { - address = dnsAddress, - skipFallback = true, - domains = domains.Count > 0 ? domains : null, - expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null - }; - return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }); - } - - var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.FirstOrDefault()); - var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.FirstOrDefault()); - - var directDomainList = new List(); - var directGeositeList = new List(); - var proxyDomainList = new List(); - var proxyGeositeList = new List(); - var expectedDomainList = new List(); - var expectedIPs = new List(); - var regionNames = new HashSet(); - - if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs)) - { - expectedIPs = simpleDNSItem.DirectExpectedIPs - .Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) - .Select(s => s.Trim()) - .Where(s => !string.IsNullOrEmpty(s)) - .ToList(); - - foreach (var ip in expectedIPs) - { - if (ip.StartsWith("geoip:", StringComparison.OrdinalIgnoreCase)) - { - var region = ip["geoip:".Length..]; - if (!string.IsNullOrEmpty(region)) - { - regionNames.Add($"geosite:{region}"); - regionNames.Add($"geosite:geolocation-{region}"); - regionNames.Add($"geosite:tld-{region}"); - } - } - } - } - - var routing = await ConfigHandler.GetDefaultRouting(_config); - List? rules = null; - if (routing != null) - { - rules = JsonUtils.Deserialize>(routing.RuleSet) ?? []; - foreach (var item in rules) - { - if (!item.Enabled || item.Domain is null || item.Domain.Count == 0) - { - continue; - } - - foreach (var domain in item.Domain) - { - if (domain.StartsWith('#')) - continue; - var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ","); - - if (item.OutboundTag == Global.DirectTag) - { - if (normalizedDomain.StartsWith("geosite:")) - { - (regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain); - } - else - { - directDomainList.Add(normalizedDomain); - } - } - else if (item.OutboundTag != Global.BlockTag) - { - if (normalizedDomain.StartsWith("geosite:")) - { - proxyGeositeList.Add(normalizedDomain); - } - else - { - proxyDomainList.Add(normalizedDomain); - } - } - } - } - } - - if (Utils.IsDomain(node?.Address)) - { - directDomainList.Add(node.Address); - } - - if (node?.Subid is not null) - { - var subItem = await AppHandler.Instance.GetSubItem(node.Subid); - if (subItem is not null) - { - foreach (var profile in new[] { subItem.PrevProfile, subItem.NextProfile }) - { - var profileNode = await AppHandler.Instance.GetProfileItemViaRemarks(profile); - if (profileNode is not null && - profileNode.ConfigType is not (EConfigType.Custom or EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) && - Utils.IsDomain(profileNode.Address)) - { - directDomainList.Add(profileNode.Address); - } - } - } - } - - v2rayConfig.dns ??= new Dns4Ray(); - v2rayConfig.dns.servers ??= new List(); - - void AddDnsServers(List dnsAddresses, List domains, List? expectedIPs = null) - { - if (domains.Count > 0) - { - foreach (var dnsAddress in dnsAddresses) - { - v2rayConfig.dns.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs)); - } - } - } - - AddDnsServers(remoteDNSAddress, proxyDomainList); - AddDnsServers(directDNSAddress, directDomainList); - AddDnsServers(remoteDNSAddress, proxyGeositeList); - AddDnsServers(directDNSAddress, directGeositeList); - AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs); - - var useDirectDns = rules?.LastOrDefault() is { } lastRule && - lastRule.OutboundTag == Global.DirectTag && - (lastRule.Port == "0-65535" || - lastRule.Network == "tcp,udp" || - lastRule.Ip?.Contains("0.0.0.0/0") == true); - - var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress; - v2rayConfig.dns.servers.AddRange(defaultDnsServers); - - return 0; - } - - private async Task GenDnsHosts(V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem) - { - if (simpleDNSItem.AddCommonHosts == false && simpleDNSItem.UseSystemHosts == false && simpleDNSItem.Hosts.IsNullOrEmpty()) - { - return await Task.FromResult(0); - } - v2rayConfig.dns ??= new Dns4Ray(); - v2rayConfig.dns.hosts ??= new Dictionary(); - if (simpleDNSItem.AddCommonHosts == true) - { - v2rayConfig.dns.hosts = Global.PredefinedHosts.ToDictionary( - kvp => kvp.Key, - kvp => (object)kvp.Value - ); - } - - if (simpleDNSItem.UseSystemHosts == true) - { - var systemHosts = Utils.GetSystemHosts(); - if (systemHosts.Count > 0) - { - var normalHost = v2rayConfig.dns.hosts; - if (normalHost != null) - { - foreach (var host in systemHosts) - { - if (normalHost[host.Key] != null) - { - continue; - } - normalHost[host.Key] = new List { host.Value }; - } - } - } - } - - var userHostsMap = simpleDNSItem.Hosts? - .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) - .Where(line => !string.IsNullOrWhiteSpace(line)) - .Where(line => line.Contains(' ')) - .ToDictionary( - line => - { - var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - return parts[0]; - }, - line => - { - var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - var values = parts.Skip(1).ToList(); - return values; - } - ); - - if (userHostsMap != null) - { - foreach (var kvp in userHostsMap) - { - v2rayConfig.dns.hosts[kvp.Key] = kvp.Value; - } - } - return await Task.FromResult(0); - } - - private async Task GenDnsCompatible(ProfileItem? node, V2rayConfig v2rayConfig) - { - try - { - var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray); - var normalDNS = item?.NormalDNS; - var domainStrategy4Freedom = item?.DomainStrategy4Freedom; - if (normalDNS.IsNullOrEmpty()) - { - normalDNS = EmbedUtils.GetEmbedText(Global.DNSV2rayNormalFileName); - } - - //Outbound Freedom domainStrategy - if (domainStrategy4Freedom.IsNotEmpty()) - { - var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag }); - if (outbound != null) - { - outbound.settings = new(); - outbound.settings.domainStrategy = domainStrategy4Freedom; - outbound.settings.userLevel = 0; - } - } - - var obj = JsonUtils.ParseJson(normalDNS); - if (obj is null) - { - List servers = []; - string[] arrDNS = normalDNS.Split(','); - foreach (string str in arrDNS) - { - servers.Add(str); - } - obj = JsonUtils.ParseJson("{}"); - obj["servers"] = JsonUtils.SerializeToNode(servers); - } - - // Append to dns settings - if (item.UseSystemHosts) - { - var systemHosts = Utils.GetSystemHosts(); - if (systemHosts.Count > 0) - { - var normalHost1 = obj["hosts"]; - if (normalHost1 != null) - { - foreach (var host in systemHosts) - { - if (normalHost1[host.Key] != null) - continue; - normalHost1[host.Key] = host.Value; - } - } - } - } - var normalHost = obj["hosts"]; - if (normalHost != null) - { - foreach (var hostProp in normalHost.AsObject().ToList()) - { - if (hostProp.Value is JsonValue value && value.TryGetValue(out var ip)) - { - normalHost[hostProp.Key] = new JsonArray(ip); - } - } - } - - await GenDnsDomainsCompatible(node, obj, item); - - v2rayConfig.dns = JsonUtils.Deserialize(JsonUtils.Serialize(obj)); - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return 0; - } - - private async Task GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dNSItem) - { - if (node == null) - { - return 0; - } - var servers = dns["servers"]; - if (servers != null) - { - var domainList = new List(); - if (Utils.IsDomain(node.Address)) - { - domainList.Add(node.Address); - } - var subItem = await AppHandler.Instance.GetSubItem(node.Subid); - if (subItem is not null) - { - // Previous proxy - var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); - if (prevNode is not null - && prevNode.ConfigType != EConfigType.Custom - && prevNode.ConfigType != EConfigType.Hysteria2 - && prevNode.ConfigType != EConfigType.TUIC - && Utils.IsDomain(prevNode.Address)) - { - domainList.Add(prevNode.Address); - } - - // Next proxy - var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); - if (nextNode is not null - && nextNode.ConfigType != EConfigType.Custom - && nextNode.ConfigType != EConfigType.Hysteria2 - && nextNode.ConfigType != EConfigType.TUIC - && Utils.IsDomain(nextNode.Address)) - { - domainList.Add(nextNode.Address); - } - } - if (domainList.Count > 0) - { - var dnsServer = new DnsServer4Ray() - { - address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, - skipFallback = true, - domains = domainList - }; - servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer)); - } - } - return await Task.FromResult(0); - } - - private async Task GenStatistic(V2rayConfig v2rayConfig) - { - if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed) - { - string tag = EInboundProtocol.api.ToString(); - Metrics4Ray apiObj = new(); - Policy4Ray policyObj = new(); - SystemPolicy4Ray policySystemSetting = new(); - - v2rayConfig.stats = new Stats4Ray(); - - apiObj.tag = tag; - v2rayConfig.metrics = apiObj; - - policySystemSetting.statsOutboundDownlink = true; - policySystemSetting.statsOutboundUplink = true; - policyObj.system = policySystemSetting; - v2rayConfig.policy = policyObj; - - if (!v2rayConfig.inbounds.Exists(item => item.tag == tag)) - { - Inbounds4Ray apiInbound = new(); - Inboundsettings4Ray apiInboundSettings = new(); - apiInbound.tag = tag; - apiInbound.listen = Global.Loopback; - apiInbound.port = AppHandler.Instance.StatePort; - apiInbound.protocol = Global.InboundAPIProtocol; - apiInboundSettings.address = Global.Loopback; - apiInbound.settings = apiInboundSettings; - v2rayConfig.inbounds.Add(apiInbound); - } - - if (!v2rayConfig.routing.rules.Exists(item => item.outboundTag == tag)) - { - RulesItem4Ray apiRoutingRule = new() - { - inboundTag = new List { tag }, - outboundTag = tag, - type = "field" - }; - - v2rayConfig.routing.rules.Add(apiRoutingRule); - } - } - return await Task.FromResult(0); - } - - private async Task GenMoreOutbounds(ProfileItem node, V2rayConfig v2rayConfig) - { - //fragment proxy - if (_config.CoreBasicItem.EnableFragment - && v2rayConfig.outbounds.First().streamSettings?.security.IsNullOrEmpty() == false) - { - var fragmentOutbound = new Outbounds4Ray - { - protocol = "freedom", - tag = $"{Global.ProxyTag}3", - settings = new() - { - fragment = new() - { - packets = _config.Fragment4RayItem?.Packets, - length = _config.Fragment4RayItem?.Length, - interval = _config.Fragment4RayItem?.Interval - } - } - }; - - v2rayConfig.outbounds.Add(fragmentOutbound); - v2rayConfig.outbounds.First().streamSettings.sockopt = new() - { - dialerProxy = fragmentOutbound.tag - }; - return 0; - } - - if (node.Subid.IsNullOrEmpty()) - { - return 0; - } - try - { - var subItem = await AppHandler.Instance.GetSubItem(node.Subid); - if (subItem is null) - { - return 0; - } - - //current proxy - var outbound = v2rayConfig.outbounds.First(); - var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); - - //Previous proxy - var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); - string? prevOutboundTag = null; - if (prevNode is not null - && prevNode.ConfigType != EConfigType.Custom - && prevNode.ConfigType != EConfigType.Hysteria2 - && prevNode.ConfigType != EConfigType.TUIC - && prevNode.ConfigType != EConfigType.Anytls) - { - var prevOutbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(prevNode, prevOutbound); - prevOutboundTag = $"prev-{Global.ProxyTag}"; - prevOutbound.tag = prevOutboundTag; - v2rayConfig.outbounds.Add(prevOutbound); - } - var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag); - - if (nextOutbound is not null) - { - v2rayConfig.outbounds.Insert(0, nextOutbound); - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - - return 0; - } - - private async Task GenOutboundsList(List nodes, V2rayConfig v2rayConfig) - { - try - { - // Get template and initialize list - var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); - if (txtOutbound.IsNullOrEmpty()) - { - return 0; - } - - var resultOutbounds = new List(); - var prevOutbounds = new List(); // Separate list for prev outbounds and fragment - - // Cache for chain proxies to avoid duplicate generation - var nextProxyCache = new Dictionary(); - var prevProxyTags = new Dictionary(); // Map from profile name to tag - int prevIndex = 0; // Index for prev outbounds - - // Process nodes - int index = 0; - foreach (var node in nodes) - { - index++; - - // Handle proxy chain - string? prevTag = null; - var currentOutbound = JsonUtils.Deserialize(txtOutbound); - var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null); - if (nextOutbound != null) - { - nextOutbound = JsonUtils.DeepCopy(nextOutbound); - } - - var subItem = await AppHandler.Instance.GetSubItem(node.Subid); - - // current proxy - await GenOutbound(node, currentOutbound); - currentOutbound.tag = $"{Global.ProxyTag}-{index}"; - - if (!node.Subid.IsNullOrEmpty()) - { - if (prevProxyTags.TryGetValue(node.Subid, out var value)) - { - prevTag = value; // maybe null - } - else - { - var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); - if (prevNode is not null - && prevNode.ConfigType != EConfigType.Custom - && prevNode.ConfigType != EConfigType.Hysteria2 - && prevNode.ConfigType != EConfigType.TUIC - && prevNode.ConfigType != EConfigType.Anytls) - { - var prevOutbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(prevNode, prevOutbound); - prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}"; - prevOutbound.tag = prevTag; - prevOutbounds.Add(prevOutbound); - } - prevProxyTags[node.Subid] = prevTag; - } - - nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound); - if (!nextProxyCache.ContainsKey(node.Subid)) - { - nextProxyCache[node.Subid] = nextOutbound; - } - } - - if (nextOutbound is not null) - { - resultOutbounds.Add(nextOutbound); - } - resultOutbounds.Add(currentOutbound); - } - - // Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds - resultOutbounds.AddRange(prevOutbounds); - resultOutbounds.AddRange(v2rayConfig.outbounds); - v2rayConfig.outbounds = resultOutbounds; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - - return 0; - } - - /// - /// Generates a chained outbound configuration for the given subItem and outbound. - /// The outbound's tag must be set before calling this method. - /// Returns the next proxy's outbound configuration, which may be null if no next proxy exists. - /// - /// The subscription item containing proxy chain information. - /// The current outbound configuration. Its tag must be set before calling this method. - /// The tag of the previous outbound in the chain, if any. - /// The outbound for the next proxy in the chain, if already created. If null, will be created inside. - /// - /// The outbound configuration for the next proxy in the chain, or null if no next proxy exists. - /// - private async Task GenChainOutbounds(SubItem subItem, Outbounds4Ray outbound, string? prevOutboundTag, Outbounds4Ray? nextOutbound = null) - { - try - { - var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); - - if (!prevOutboundTag.IsNullOrEmpty()) - { - outbound.streamSettings.sockopt = new() - { - dialerProxy = prevOutboundTag - }; - } - - // Next proxy - var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); - if (nextNode is not null - && nextNode.ConfigType != EConfigType.Custom - && nextNode.ConfigType != EConfigType.Hysteria2 - && nextNode.ConfigType != EConfigType.TUIC - && nextNode.ConfigType != EConfigType.Anytls) - { - if (nextOutbound == null) - { - nextOutbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(nextNode, nextOutbound); - } - nextOutbound.tag = outbound.tag; - - outbound.tag = $"mid-{outbound.tag}"; - nextOutbound.streamSettings.sockopt = new() - { - dialerProxy = outbound.tag - }; - } - return nextOutbound; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return null; - } - - private async Task GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad) - { - if (multipleLoad == EMultipleLoad.LeastPing) - { - var observatory = new Observatory4Ray - { - subjectSelector = [Global.ProxyTag], - probeUrl = AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl, - probeInterval = "3m", - enableConcurrency = true, - }; - v2rayConfig.observatory = observatory; - } - else if (multipleLoad == EMultipleLoad.LeastLoad) - { - var burstObservatory = new BurstObservatory4Ray - { - subjectSelector = [Global.ProxyTag], - pingConfig = new() - { - destination = AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl, - interval = "5m", - timeout = "30s", - sampling = 2, - } - }; - v2rayConfig.burstObservatory = burstObservatory; - } - var strategyType = multipleLoad switch - { - EMultipleLoad.Random => "random", - EMultipleLoad.RoundRobin => "roundRobin", - EMultipleLoad.LeastPing => "leastPing", - EMultipleLoad.LeastLoad => "leastLoad", - _ => "roundRobin", - }; - var balancer = new BalancersItem4Ray - { - selector = [Global.ProxyTag], - strategy = new() { type = strategyType }, - tag = $"{Global.ProxyTag}-round", - }; - v2rayConfig.routing.balancers = [balancer]; - return await Task.FromResult(0); - } - - private async Task ApplyFullConfigTemplate(V2rayConfig v2rayConfig, bool handleBalancerAndRules = false) - { - var fullConfigTemplate = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray); - if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty()) - { - return JsonUtils.Serialize(v2rayConfig); - } - - var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplate.Config); - if (fullConfigTemplateNode == null) - { - return JsonUtils.Serialize(v2rayConfig); - } - - // Handle balancer and rules modifications (for multiple load scenarios) - if (handleBalancerAndRules && v2rayConfig.routing?.balancers?.Count > 0) - { - var balancer = v2rayConfig.routing.balancers.First(); - - // Modify existing rules in custom config - var rulesNode = fullConfigTemplateNode["routing"]?["rules"]; - if (rulesNode != null) - { - foreach (var rule in rulesNode.AsArray()) - { - if (rule["outboundTag"]?.GetValue() == Global.ProxyTag) - { - rule.AsObject().Remove("outboundTag"); - rule["balancerTag"] = balancer.tag; - } - } - } - - // Ensure routing node exists - if (fullConfigTemplateNode["routing"] == null) - { - fullConfigTemplateNode["routing"] = new JsonObject(); - } - - // Handle balancers - append instead of override - if (fullConfigTemplateNode["routing"]["balancers"] is JsonArray customBalancersNode) - { - if (JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)) is JsonArray newBalancers) - { - foreach (var balancerNode in newBalancers) - { - customBalancersNode.Add(balancerNode?.DeepClone()); - } - } - } - else - { - fullConfigTemplateNode["routing"]["balancers"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)); - } - } - - // Handle outbounds - append instead of override - var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray(); - foreach (var outbound in v2rayConfig.outbounds) - { - if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom") - { - if (fullConfigTemplate.AddProxyOnly == true) - { - continue; - } - } - else if ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == true) && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) && !(Utils.IsPrivateNetwork(outbound.settings?.servers?.FirstOrDefault()?.address ?? string.Empty) || Utils.IsPrivateNetwork(outbound.settings?.vnext?.FirstOrDefault()?.address ?? string.Empty))) - { - outbound.streamSettings ??= new StreamSettings4Ray(); - outbound.streamSettings.sockopt ??= new Sockopt4Ray(); - outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour; - } - customOutboundsNode.Add(JsonUtils.DeepCopy(outbound)); - } - fullConfigTemplateNode["outbounds"] = customOutboundsNode; - - return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode)); - } - - #endregion private gen function -} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs new file mode 100644 index 0000000000..7c41418c0b --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs @@ -0,0 +1,522 @@ +using System.Net; +using System.Net.NetworkInformation; + +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigSingboxService(Config config) +{ + private readonly Config _config = config; + private static readonly string _tag = "CoreConfigSingboxService"; + + #region public gen function + + public async Task GenerateClientConfigContent(ProfileItem node) + { + var ret = new RetResult(); + try + { + if (node == null + || node.Port <= 0) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp)) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; + return ret; + } + + ret.Msg = ResUI.InitialConfiguration; + + var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient); + if (result.IsNullOrEmpty()) + { + ret.Msg = ResUI.FailedGetDefaultConfiguration; + return ret; + } + + var singboxConfig = JsonUtils.Deserialize(result); + if (singboxConfig == null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + + await GenLog(singboxConfig); + + await GenInbounds(singboxConfig); + + if (node.ConfigType == EConfigType.WireGuard) + { + singboxConfig.outbounds.RemoveAt(0); + var endpoints = new Endpoints4Sbox(); + await GenEndpoint(node, endpoints); + endpoints.tag = Global.ProxyTag; + singboxConfig.endpoints = new() { endpoints }; + } + else + { + await GenOutbound(node, singboxConfig.outbounds.First()); + } + + await GenMoreOutbounds(node, singboxConfig); + + await GenRouting(singboxConfig); + + await GenDns(singboxConfig); + + await GenExperimental(singboxConfig); + + await ConvertGeo2Ruleset(singboxConfig); + + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); + ret.Success = true; + + ret.Data = await ApplyFullConfigTemplate(singboxConfig); + return ret; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } + + public async Task GenerateClientSpeedtestConfig(List selecteds) + { + var ret = new RetResult(); + try + { + if (_config == null) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + ret.Msg = ResUI.InitialConfiguration; + + var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient); + var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); + if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty()) + { + ret.Msg = ResUI.FailedGetDefaultConfiguration; + return ret; + } + + var singboxConfig = JsonUtils.Deserialize(result); + if (singboxConfig == null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + List lstIpEndPoints = new(); + List lstTcpConns = new(); + try + { + lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); + lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners()); + lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + + await GenLog(singboxConfig); + //GenDns(new(), singboxConfig); + singboxConfig.inbounds.Clear(); + singboxConfig.outbounds.RemoveAt(0); + + var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest); + + foreach (var it in selecteds) + { + if (it.ConfigType == EConfigType.Custom) + { + continue; + } + if (it.Port <= 0) + { + continue; + } + var item = await AppManager.Instance.GetProfileItem(it.IndexId); + if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS) + { + if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id)) + { + continue; + } + } + + //find unused port + var port = initPort; + for (var k = initPort; k < Global.MaxPort; k++) + { + if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) + { + continue; + } + if (lstTcpConns?.FindIndex(_it => _it.LocalEndPoint.Port == k) >= 0) + { + continue; + } + //found + port = k; + initPort = port + 1; + break; + } + + //Port In Used + if (lstIpEndPoints?.FindIndex(_it => _it.Port == port) >= 0) + { + continue; + } + it.Port = port; + it.AllowTest = true; + + //inbound + Inbound4Sbox inbound = new() + { + listen = Global.Loopback, + listen_port = port, + type = EInboundProtocol.mixed.ToString(), + }; + inbound.tag = inbound.type + inbound.listen_port.ToString(); + singboxConfig.inbounds.Add(inbound); + + //outbound + if (item is null) + { + continue; + } + if (item.ConfigType == EConfigType.Shadowsocks + && !Global.SsSecuritiesInSingbox.Contains(item.Security)) + { + continue; + } + if (item.ConfigType == EConfigType.VLESS + && !Global.Flows.Contains(item.Flow)) + { + continue; + } + if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan + && item.StreamSecurity == Global.StreamSecurityReality + && item.PublicKey.IsNullOrEmpty()) + { + continue; + } + + var server = await GenServer(item); + if (server is null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + var tag = Global.ProxyTag + inbound.listen_port.ToString(); + server.tag = tag; + if (server is Endpoints4Sbox endpoint) + { + singboxConfig.endpoints ??= new(); + singboxConfig.endpoints.Add(endpoint); + } + else if (server is Outbound4Sbox outbound) + { + singboxConfig.outbounds.Add(outbound); + } + + //rule + Rule4Sbox rule = new() + { + inbound = new List { inbound.tag }, + outbound = tag + }; + singboxConfig.route.rules.Add(rule); + } + + var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); + if (rawDNSItem != null && rawDNSItem.Enabled == true) + { + await GenDnsDomainsCompatible(singboxConfig, rawDNSItem); + } + else + { + await GenDnsDomains(singboxConfig, _config.SimpleDNSItem); + } + singboxConfig.route.default_domain_resolver = new() + { + server = Global.SingboxFinalResolverTag + }; + + ret.Success = true; + ret.Data = JsonUtils.Serialize(singboxConfig); + return ret; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } + + public async Task GenerateClientSpeedtestConfig(ProfileItem node, int port) + { + var ret = new RetResult(); + try + { + if (node is not { Port: > 0 }) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp)) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; + return ret; + } + + ret.Msg = ResUI.InitialConfiguration; + + var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient); + if (result.IsNullOrEmpty()) + { + ret.Msg = ResUI.FailedGetDefaultConfiguration; + return ret; + } + + var singboxConfig = JsonUtils.Deserialize(result); + if (singboxConfig == null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + + await GenLog(singboxConfig); + if (node.ConfigType == EConfigType.WireGuard) + { + singboxConfig.outbounds.RemoveAt(0); + var endpoints = new Endpoints4Sbox(); + await GenEndpoint(node, endpoints); + endpoints.tag = Global.ProxyTag; + singboxConfig.endpoints = new() { endpoints }; + } + else + { + await GenOutbound(node, singboxConfig.outbounds.First()); + } + await GenMoreOutbounds(node, singboxConfig); + var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); + if (item != null && item.Enabled == true) + { + await GenDnsDomainsCompatible(singboxConfig, item); + } + else + { + await GenDnsDomains(singboxConfig, _config.SimpleDNSItem); + } + singboxConfig.route.default_domain_resolver = new() + { + server = Global.SingboxFinalResolverTag + }; + + singboxConfig.route.rules.Clear(); + singboxConfig.inbounds.Clear(); + singboxConfig.inbounds.Add(new() + { + tag = $"{EInboundProtocol.mixed}{port}", + listen = Global.Loopback, + listen_port = port, + type = EInboundProtocol.mixed.ToString(), + }); + + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); + ret.Success = true; + ret.Data = JsonUtils.Serialize(singboxConfig); + return ret; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } + + public async Task GenerateClientMultipleLoadConfig(List selecteds) + { + var ret = new RetResult(); + try + { + if (_config == null) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + ret.Msg = ResUI.InitialConfiguration; + + var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient); + var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); + if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty()) + { + ret.Msg = ResUI.FailedGetDefaultConfiguration; + return ret; + } + + var singboxConfig = JsonUtils.Deserialize(result); + if (singboxConfig == null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + + await GenLog(singboxConfig); + await GenInbounds(singboxConfig); + await GenRouting(singboxConfig); + await GenExperimental(singboxConfig); + singboxConfig.outbounds.RemoveAt(0); + + var proxyProfiles = new List(); + foreach (var it in selecteds) + { + if (it.ConfigType == EConfigType.Custom) + { + continue; + } + if (it.Port <= 0) + { + continue; + } + var item = await AppManager.Instance.GetProfileItem(it.IndexId); + if (item is null) + { + continue; + } + if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS) + { + if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id)) + { + continue; + } + } + if (item.ConfigType == EConfigType.Shadowsocks + && !Global.SsSecuritiesInSingbox.Contains(item.Security)) + { + continue; + } + if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow)) + { + continue; + } + + //outbound + proxyProfiles.Add(item); + } + if (proxyProfiles.Count <= 0) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + await GenOutboundsList(proxyProfiles, singboxConfig); + + await GenDns(singboxConfig); + await ConvertGeo2Ruleset(singboxConfig); + + ret.Success = true; + + ret.Data = await ApplyFullConfigTemplate(singboxConfig); + return ret; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } + + public async Task GenerateClientCustomConfig(ProfileItem node, string? fileName) + { + var ret = new RetResult(); + if (node == null || fileName is null) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + ret.Msg = ResUI.InitialConfiguration; + + try + { + if (node == null) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + if (File.Exists(fileName)) + { + File.Delete(fileName); + } + + var addressFileName = node.Address; + if (addressFileName.IsNullOrEmpty()) + { + ret.Msg = ResUI.FailedGetDefaultConfiguration; + return ret; + } + if (!File.Exists(addressFileName)) + { + addressFileName = Path.Combine(Utils.GetConfigPath(), addressFileName); + } + if (!File.Exists(addressFileName)) + { + ret.Msg = ResUI.FailedReadConfiguration + "1"; + return ret; + } + + if (node.Address == Global.CoreMultipleLoadConfigFileName) + { + var txtFile = File.ReadAllText(addressFileName); + var singboxConfig = JsonUtils.Deserialize(txtFile); + if (singboxConfig == null) + { + File.Copy(addressFileName, fileName); + } + else + { + await GenInbounds(singboxConfig); + await GenExperimental(singboxConfig); + + var content = JsonUtils.Serialize(singboxConfig, true); + await File.WriteAllTextAsync(fileName, content); + } + } + else + { + File.Copy(addressFileName, fileName); + } + + //check again + if (!File.Exists(fileName)) + { + ret.Msg = ResUI.FailedReadConfiguration + "2"; + return ret; + } + + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); + ret.Success = true; + return ret; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } + + #endregion public gen function +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxConfigTemplateService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxConfigTemplateService.cs new file mode 100644 index 0000000000..c6bec22b1b --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxConfigTemplateService.cs @@ -0,0 +1,63 @@ +using System.Text.Json.Nodes; + +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigSingboxService +{ + private async Task ApplyFullConfigTemplate(SingboxConfig singboxConfig) + { + var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.sing_box); + if (fullConfigTemplate == null || !fullConfigTemplate.Enabled) + { + return JsonUtils.Serialize(singboxConfig); + } + + var fullConfigTemplateItem = _config.TunModeItem.EnableTun ? fullConfigTemplate.TunConfig : fullConfigTemplate.Config; + if (fullConfigTemplateItem.IsNullOrEmpty()) + { + return JsonUtils.Serialize(singboxConfig); + } + + var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplateItem); + if (fullConfigTemplateNode == null) + { + return JsonUtils.Serialize(singboxConfig); + } + + // Process outbounds + var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray(); + foreach (var outbound in singboxConfig.outbounds) + { + if (outbound.type.ToLower() is "direct" or "block") + { + if (fullConfigTemplate.AddProxyOnly == true) + { + continue; + } + } + else if (outbound.detour.IsNullOrEmpty() && !fullConfigTemplate.ProxyDetour.IsNullOrEmpty() && !Utils.IsPrivateNetwork(outbound.server ?? string.Empty)) + { + outbound.detour = fullConfigTemplate.ProxyDetour; + } + customOutboundsNode.Add(JsonUtils.DeepCopy(outbound)); + } + fullConfigTemplateNode["outbounds"] = customOutboundsNode; + + // Process endpoints + if (singboxConfig.endpoints != null && singboxConfig.endpoints.Count > 0) + { + var customEndpointsNode = fullConfigTemplateNode["endpoints"] is JsonArray endpoints ? endpoints : new JsonArray(); + foreach (var endpoint in singboxConfig.endpoints) + { + if (endpoint.detour.IsNullOrEmpty() && !fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) + { + endpoint.detour = fullConfigTemplate.ProxyDetour; + } + customEndpointsNode.Add(JsonUtils.DeepCopy(endpoint)); + } + fullConfigTemplateNode["endpoints"] = customEndpointsNode; + } + + return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode)); + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs new file mode 100644 index 0000000000..ef22d926f8 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs @@ -0,0 +1,482 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigSingboxService +{ + private async Task GenDns(SingboxConfig singboxConfig) + { + try + { + var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); + if (item != null && item.Enabled == true) + { + return await GenDnsCompatible(singboxConfig); + } + + var simpleDNSItem = _config.SimpleDNSItem; + await GenDnsServers(singboxConfig, simpleDNSItem); + await GenDnsRules(singboxConfig, simpleDNSItem); + + singboxConfig.dns ??= new Dns4Sbox(); + singboxConfig.dns.independent_cache = true; + + var routing = await ConfigHandler.GetDefaultRouting(_config); + var useDirectDns = false; + if (routing != null) + { + var rules = JsonUtils.Deserialize>(routing.RuleSet) ?? []; + + useDirectDns = rules?.LastOrDefault() is { } lastRule && + lastRule.OutboundTag == Global.DirectTag && + (lastRule.Port == "0-65535" || + lastRule.Network == "tcp,udp" || + lastRule.Ip?.Contains("0.0.0.0/0") == true); + } + singboxConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private async Task GenDnsServers(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem) + { + var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem); + + var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS); + directDns.tag = Global.SingboxDirectDNSTag; + directDns.domain_resolver = Global.SingboxFinalResolverTag; + + var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS); + remoteDns.tag = Global.SingboxRemoteDNSTag; + remoteDns.detour = Global.ProxyTag; + remoteDns.domain_resolver = Global.SingboxFinalResolverTag; + + var resolverDns = ParseDnsAddress(simpleDNSItem.SingboxOutboundsResolveDNS); + resolverDns.tag = Global.SingboxOutboundResolverTag; + resolverDns.domain_resolver = Global.SingboxFinalResolverTag; + + var hostsDns = new Server4Sbox + { + tag = Global.SingboxHostsDNSTag, + type = "hosts", + predefined = new(), + }; + if (simpleDNSItem.AddCommonHosts == true) + { + hostsDns.predefined = Global.PredefinedHosts; + } + var userHostsMap = simpleDNSItem.Hosts? + .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) + .Where(line => !string.IsNullOrWhiteSpace(line)) + .Where(line => line.Contains(' ')) + .ToDictionary( + line => + { + var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + return parts[0]; + }, + line => + { + var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + var values = parts.Skip(1).ToList(); + return values; + } + ); + + if (userHostsMap != null) + { + foreach (var kvp in userHostsMap) + { + hostsDns.predefined[kvp.Key] = kvp.Value; + } + } + + if (simpleDNSItem.UseSystemHosts == true) + { + var systemHosts = Utils.GetSystemHosts(); + if (systemHosts.Count > 0) + { + foreach (var host in systemHosts) + { + if (userHostsMap[host.Key] != null) + { + continue; + } + userHostsMap[host.Key] = new List { host.Value }; + } + } + } + + foreach (var host in hostsDns.predefined) + { + if (finalDns.server == host.Key) + { + finalDns.domain_resolver = Global.SingboxHostsDNSTag; + } + if (remoteDns.server == host.Key) + { + remoteDns.domain_resolver = Global.SingboxHostsDNSTag; + } + if (resolverDns.server == host.Key) + { + resolverDns.domain_resolver = Global.SingboxHostsDNSTag; + } + if (directDns.server == host.Key) + { + directDns.domain_resolver = Global.SingboxHostsDNSTag; + } + } + + singboxConfig.dns ??= new Dns4Sbox(); + singboxConfig.dns.servers ??= new List(); + singboxConfig.dns.servers.Add(remoteDns); + singboxConfig.dns.servers.Add(directDns); + singboxConfig.dns.servers.Add(resolverDns); + singboxConfig.dns.servers.Add(hostsDns); + + // fake ip + if (simpleDNSItem.FakeIP == true) + { + var fakeip = new Server4Sbox + { + tag = Global.SingboxFakeDNSTag, + type = "fakeip", + inet4_range = "198.18.0.0/15", + inet6_range = "fc00::/18", + }; + singboxConfig.dns.servers.Add(fakeip); + } + + return await Task.FromResult(0); + } + + private async Task GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem) + { + var finalDns = ParseDnsAddress(simpleDNSItem.SingboxFinalResolveDNS); + finalDns.tag = Global.SingboxFinalResolverTag; + singboxConfig.dns ??= new Dns4Sbox(); + singboxConfig.dns.servers ??= new List(); + singboxConfig.dns.servers.Add(finalDns); + return await Task.FromResult(finalDns); + } + + private async Task GenDnsRules(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem) + { + singboxConfig.dns ??= new Dns4Sbox(); + singboxConfig.dns.rules ??= new List(); + + singboxConfig.dns.rules.AddRange(new[] + { + new Rule4Sbox { ip_accept_any = true, server = Global.SingboxHostsDNSTag }, + new Rule4Sbox + { + server = Global.SingboxRemoteDNSTag, + strategy = simpleDNSItem.SingboxStrategy4Proxy.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Proxy, + clash_mode = ERuleMode.Global.ToString() + }, + new Rule4Sbox + { + server = Global.SingboxDirectDNSTag, + strategy = simpleDNSItem.SingboxStrategy4Direct.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Direct, + clash_mode = ERuleMode.Direct.ToString() + } + }); + + if (simpleDNSItem.BlockBindingQuery == true) + { + singboxConfig.dns.rules.Add(new() + { + query_type = new List { 64, 65 }, + action = "predefined", + rcode = "NOTIMP" + }); + } + + var routing = await ConfigHandler.GetDefaultRouting(_config); + if (routing == null) + return 0; + + var rules = JsonUtils.Deserialize>(routing.RuleSet) ?? []; + var expectedIPCidr = new List(); + var expectedIPsRegions = new List(); + var regionNames = new HashSet(); + + if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs)) + { + var ipItems = simpleDNSItem.DirectExpectedIPs + .Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.Trim()) + .Where(s => !string.IsNullOrEmpty(s)) + .ToList(); + + foreach (var ip in ipItems) + { + if (ip.StartsWith("geoip:", StringComparison.OrdinalIgnoreCase)) + { + var region = ip["geoip:".Length..]; + if (!string.IsNullOrEmpty(region)) + { + expectedIPsRegions.Add(region); + regionNames.Add(region); + regionNames.Add($"geolocation-{region}"); + regionNames.Add($"tld-{region}"); + } + } + else + { + expectedIPCidr.Add(ip); + } + } + } + + foreach (var item in rules) + { + if (!item.Enabled || item.Domain is null || item.Domain.Count == 0) + { + continue; + } + + var rule = new Rule4Sbox(); + var validDomains = item.Domain.Count(it => ParseV2Domain(it, rule)); + if (validDomains <= 0) + { + continue; + } + + if (item.OutboundTag == Global.DirectTag) + { + rule.server = Global.SingboxDirectDNSTag; + rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Direct) ? null : simpleDNSItem.SingboxStrategy4Direct; + + if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0) + { + var geositeSet = new HashSet(rule.geosite); + if (regionNames.Intersect(geositeSet).Any()) + { + if (expectedIPsRegions.Count > 0) + { + rule.geoip = expectedIPsRegions; + } + if (expectedIPCidr.Count > 0) + { + rule.ip_cidr = expectedIPCidr; + } + } + } + } + else if (item.OutboundTag == Global.BlockTag) + { + rule.action = "predefined"; + rule.rcode = "NXDOMAIN"; + } + else + { + if (simpleDNSItem.FakeIP == true) + { + var rule4Fake = JsonUtils.DeepCopy(rule); + rule4Fake.server = Global.SingboxFakeDNSTag; + singboxConfig.dns.rules.Add(rule4Fake); + } + rule.server = Global.SingboxRemoteDNSTag; + rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Proxy) ? null : simpleDNSItem.SingboxStrategy4Proxy; + } + + singboxConfig.dns.rules.Add(rule); + } + + return 0; + } + + private async Task GenDnsCompatible(SingboxConfig singboxConfig) + { + try + { + var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); + var strDNS = string.Empty; + if (_config.TunModeItem.EnableTun) + { + strDNS = string.IsNullOrEmpty(item?.TunDNS) ? EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName) : item?.TunDNS; + } + else + { + strDNS = string.IsNullOrEmpty(item?.NormalDNS) ? EmbedUtils.GetEmbedText(Global.DNSSingboxNormalFileName) : item?.NormalDNS; + } + + var dns4Sbox = JsonUtils.Deserialize(strDNS); + if (dns4Sbox is null) + { + return 0; + } + singboxConfig.dns = dns4Sbox; + + if (dns4Sbox.servers != null && dns4Sbox.servers.Count > 0 && dns4Sbox.servers.First().address.IsNullOrEmpty()) + { + await GenDnsDomainsCompatible(singboxConfig, item); + } + else + { + await GenDnsDomainsLegacyCompatible(singboxConfig, item); + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private async Task GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem) + { + var dns4Sbox = singboxConfig.dns ?? new(); + dns4Sbox.servers ??= []; + dns4Sbox.rules ??= []; + + var tag = Global.SingboxFinalResolverTag; + var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress; + + var localDnsServer = ParseDnsAddress(localDnsAddress); + localDnsServer.tag = tag; + + dns4Sbox.servers.Add(localDnsServer); + + singboxConfig.dns = dns4Sbox; + return await Task.FromResult(0); + } + + private async Task GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem) + { + var dns4Sbox = singboxConfig.dns ?? new(); + dns4Sbox.servers ??= []; + dns4Sbox.rules ??= []; + + var tag = Global.SingboxFinalResolverTag; + dns4Sbox.servers.Add(new() + { + tag = tag, + address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, + detour = Global.DirectTag, + strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom, + }); + dns4Sbox.rules.Insert(0, new() + { + server = tag, + clash_mode = ERuleMode.Direct.ToString() + }); + dns4Sbox.rules.Insert(0, new() + { + server = dns4Sbox.servers.Where(t => t.detour == Global.ProxyTag).Select(t => t.tag).FirstOrDefault() ?? "remote", + clash_mode = ERuleMode.Global.ToString() + }); + + var lstDomain = singboxConfig.outbounds + .Where(t => t.server.IsNotEmpty() && Utils.IsDomain(t.server)) + .Select(t => t.server) + .Distinct() + .ToList(); + if (lstDomain != null && lstDomain.Count > 0) + { + dns4Sbox.rules.Insert(0, new() + { + server = tag, + domain = lstDomain + }); + } + + singboxConfig.dns = dns4Sbox; + return await Task.FromResult(0); + } + + private static Server4Sbox? ParseDnsAddress(string address) + { + var addressFirst = address?.Split(address.Contains(',') ? ',' : ';').FirstOrDefault()?.Trim(); + if (string.IsNullOrEmpty(addressFirst)) + { + return null; + } + + var server = new Server4Sbox(); + + if (addressFirst is "local" or "localhost") + { + server.type = "local"; + return server; + } + + if (addressFirst.StartsWith("dhcp://", StringComparison.OrdinalIgnoreCase)) + { + var interface_name = addressFirst.Substring(7); + server.type = "dhcp"; + server.Interface = interface_name == "auto" ? null : interface_name; + return server; + } + + if (!addressFirst.Contains("://")) + { + // udp dns + server.type = "udp"; + server.server = addressFirst; + return server; + } + + try + { + var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal); + server.type = addressFirst.Substring(0, protocolEndIndex).ToLower(); + + var uri = new Uri(addressFirst); + server.server = uri.Host; + + if (!uri.IsDefaultPort) + { + server.server_port = uri.Port; + } + + if ((server.type == "https" || server.type == "h3") && !string.IsNullOrEmpty(uri.AbsolutePath) && uri.AbsolutePath != "/") + { + server.path = uri.AbsolutePath; + } + } + catch (UriFormatException) + { + var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal); + if (protocolEndIndex > 0) + { + server.type = addressFirst.Substring(0, protocolEndIndex).ToLower(); + var remaining = addressFirst.Substring(protocolEndIndex + 3); + + var portIndex = remaining.IndexOf(':'); + var pathIndex = remaining.IndexOf('/'); + + if (portIndex > 0) + { + server.server = remaining.Substring(0, portIndex); + var portPart = pathIndex > portIndex + ? remaining.Substring(portIndex + 1, pathIndex - portIndex - 1) + : remaining.Substring(portIndex + 1); + + if (int.TryParse(portPart, out var parsedPort)) + { + server.server_port = parsedPort; + } + } + else if (pathIndex > 0) + { + server.server = remaining.Substring(0, pathIndex); + } + else + { + server.server = remaining; + } + + if (pathIndex > 0 && (server.type == "https" || server.type == "h3")) + { + server.path = remaining.Substring(pathIndex); + } + } + } + + return server; + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxInboundService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxInboundService.cs new file mode 100644 index 0000000000..c79d5b4205 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxInboundService.cs @@ -0,0 +1,92 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigSingboxService +{ + private async Task GenInbounds(SingboxConfig singboxConfig) + { + try + { + var listen = "0.0.0.0"; + singboxConfig.inbounds = []; + + if (!_config.TunModeItem.EnableTun + || _config.TunModeItem.EnableTun && _config.TunModeItem.EnableExInbound && _config.RunningCoreType == ECoreType.sing_box) + { + var inbound = new Inbound4Sbox() + { + type = EInboundProtocol.mixed.ToString(), + tag = EInboundProtocol.socks.ToString(), + listen = Global.Loopback, + }; + singboxConfig.inbounds.Add(inbound); + + inbound.listen_port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks); + + if (_config.Inbound.First().SecondLocalPortEnabled) + { + var inbound2 = GetInbound(inbound, EInboundProtocol.socks2, true); + singboxConfig.inbounds.Add(inbound2); + } + + if (_config.Inbound.First().AllowLANConn) + { + if (_config.Inbound.First().NewPort4LAN) + { + var inbound3 = GetInbound(inbound, EInboundProtocol.socks3, true); + inbound3.listen = listen; + singboxConfig.inbounds.Add(inbound3); + + //auth + if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty()) + { + inbound3.users = new() { new() { username = _config.Inbound.First().User, password = _config.Inbound.First().Pass } }; + } + } + else + { + inbound.listen = listen; + } + } + } + + if (_config.TunModeItem.EnableTun) + { + if (_config.TunModeItem.Mtu <= 0) + { + _config.TunModeItem.Mtu = Global.TunMtus.First(); + } + if (_config.TunModeItem.Stack.IsNullOrEmpty()) + { + _config.TunModeItem.Stack = Global.TunStacks.First(); + } + + var tunInbound = JsonUtils.Deserialize(EmbedUtils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { }; + tunInbound.interface_name = Utils.IsOSX() ? $"utun{new Random().Next(99)}" : "singbox_tun"; + tunInbound.mtu = _config.TunModeItem.Mtu; + tunInbound.auto_route = _config.TunModeItem.AutoRoute; + tunInbound.strict_route = _config.TunModeItem.StrictRoute; + tunInbound.stack = _config.TunModeItem.Stack; + if (_config.TunModeItem.EnableIPv6Address == false) + { + tunInbound.address = ["172.18.0.1/30"]; + } + + singboxConfig.inbounds.Add(tunInbound); + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private Inbound4Sbox GetInbound(Inbound4Sbox inItem, EInboundProtocol protocol, bool bSocks) + { + var inbound = JsonUtils.DeepCopy(inItem); + inbound.tag = protocol.ToString(); + inbound.listen_port = inItem.listen_port + (int)protocol; + inbound.type = EInboundProtocol.mixed.ToString(); + return inbound; + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxLogService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxLogService.cs new file mode 100644 index 0000000000..59e65471ac --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxLogService.cs @@ -0,0 +1,40 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigSingboxService +{ + private async Task GenLog(SingboxConfig singboxConfig) + { + try + { + switch (_config.CoreBasicItem.Loglevel) + { + case "debug": + case "info": + case "error": + singboxConfig.log.level = _config.CoreBasicItem.Loglevel; + break; + + case "warning": + singboxConfig.log.level = "warn"; + break; + + default: + break; + } + if (_config.CoreBasicItem.Loglevel == Global.None) + { + singboxConfig.log.disabled = true; + } + if (_config.CoreBasicItem.LogEnabled) + { + var dtNow = DateTime.Now; + singboxConfig.log.output = Utils.GetLogPath($"sbox_{dtNow:yyyy-MM-dd}.txt"); + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs new file mode 100644 index 0000000000..5c48c01433 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -0,0 +1,577 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigSingboxService +{ + private async Task GenOutbound(ProfileItem node, Outbound4Sbox outbound) + { + try + { + outbound.server = node.Address; + outbound.server_port = node.Port; + outbound.type = Global.ProtocolTypes[node.ConfigType]; + + switch (node.ConfigType) + { + case EConfigType.VMess: + { + outbound.uuid = node.Id; + outbound.alter_id = node.AlterId; + if (Global.VmessSecurities.Contains(node.Security)) + { + outbound.security = node.Security; + } + else + { + outbound.security = Global.DefaultSecurity; + } + + await GenOutboundMux(node, outbound); + break; + } + case EConfigType.Shadowsocks: + { + outbound.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : Global.None; + outbound.password = node.Id; + + await GenOutboundMux(node, outbound); + break; + } + case EConfigType.SOCKS: + { + outbound.version = "5"; + if (node.Security.IsNotEmpty() + && node.Id.IsNotEmpty()) + { + outbound.username = node.Security; + outbound.password = node.Id; + } + break; + } + case EConfigType.HTTP: + { + if (node.Security.IsNotEmpty() + && node.Id.IsNotEmpty()) + { + outbound.username = node.Security; + outbound.password = node.Id; + } + break; + } + case EConfigType.VLESS: + { + outbound.uuid = node.Id; + + outbound.packet_encoding = "xudp"; + + if (node.Flow.IsNullOrEmpty()) + { + await GenOutboundMux(node, outbound); + } + else + { + outbound.flow = node.Flow; + } + break; + } + case EConfigType.Trojan: + { + outbound.password = node.Id; + + await GenOutboundMux(node, outbound); + break; + } + case EConfigType.Hysteria2: + { + outbound.password = node.Id; + + if (node.Path.IsNotEmpty()) + { + outbound.obfs = new() + { + type = "salamander", + password = node.Path.TrimEx(), + }; + } + + outbound.up_mbps = _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null; + outbound.down_mbps = _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null; + if (node.Ports.IsNotEmpty() && (node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(','))) + { + outbound.server_port = null; + outbound.server_ports = node.Ports.Split(',') + .Select(p => p.Trim()) + .Where(p => p.IsNotEmpty()) + .Select(p => + { + var port = p.Replace('-', ':'); + return port.Contains(':') ? port : $"{port}:{port}"; + }) + .ToList(); + outbound.hop_interval = _config.HysteriaItem.HopInterval > 0 ? $"{_config.HysteriaItem.HopInterval}s" : null; + } + + break; + } + case EConfigType.TUIC: + { + outbound.uuid = node.Id; + outbound.password = node.Security; + outbound.congestion_control = node.HeaderType; + break; + } + case EConfigType.Anytls: + { + outbound.password = node.Id; + break; + } + } + + await GenOutboundTls(node, outbound); + + await GenOutboundTransport(node, outbound); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private async Task GenEndpoint(ProfileItem node, Endpoints4Sbox endpoint) + { + try + { + endpoint.address = Utils.String2List(node.RequestHost); + endpoint.type = Global.ProtocolTypes[node.ConfigType]; + + switch (node.ConfigType) + { + case EConfigType.WireGuard: + { + var peer = new Peer4Sbox + { + public_key = node.PublicKey, + reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(), + address = node.Address, + port = node.Port, + // TODO default ["0.0.0.0/0", "::/0"] + allowed_ips = new() { "0.0.0.0/0", "::/0" }, + }; + endpoint.private_key = node.Id; + endpoint.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(); + endpoint.peers = new() { peer }; + break; + } + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } + + private async Task GenServer(ProfileItem node) + { + try + { + var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); + if (node.ConfigType == EConfigType.WireGuard) + { + var endpoint = JsonUtils.Deserialize(txtOutbound); + await GenEndpoint(node, endpoint); + return endpoint; + } + else + { + var outbound = JsonUtils.Deserialize(txtOutbound); + await GenOutbound(node, outbound); + return outbound; + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(null); + } + + private async Task GenOutboundMux(ProfileItem node, Outbound4Sbox outbound) + { + try + { + var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled; + if (muxEnabled && _config.Mux4SboxItem.Protocol.IsNotEmpty()) + { + var mux = new Multiplex4Sbox() + { + enabled = true, + protocol = _config.Mux4SboxItem.Protocol, + max_connections = _config.Mux4SboxItem.MaxConnections, + padding = _config.Mux4SboxItem.Padding, + }; + outbound.multiplex = mux; + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } + + private async Task GenOutboundTls(ProfileItem node, Outbound4Sbox outbound) + { + try + { + if (node.StreamSecurity == Global.StreamSecurityReality || node.StreamSecurity == Global.StreamSecurity) + { + var server_name = string.Empty; + if (node.Sni.IsNotEmpty()) + { + server_name = node.Sni; + } + else if (node.RequestHost.IsNotEmpty()) + { + server_name = Utils.String2List(node.RequestHost)?.First(); + } + var tls = new Tls4Sbox() + { + enabled = true, + record_fragment = _config.CoreBasicItem.EnableFragment, + server_name = server_name, + insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure), + alpn = node.GetAlpn(), + }; + if (node.Fingerprint.IsNotEmpty()) + { + tls.utls = new Utls4Sbox() + { + enabled = true, + fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint + }; + } + if (node.StreamSecurity == Global.StreamSecurityReality) + { + tls.reality = new Reality4Sbox() + { + enabled = true, + public_key = node.PublicKey, + short_id = node.ShortId + }; + tls.insecure = false; + } + outbound.tls = tls; + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } + + private async Task GenOutboundTransport(ProfileItem node, Outbound4Sbox outbound) + { + try + { + var transport = new Transport4Sbox(); + + switch (node.GetNetwork()) + { + case nameof(ETransport.h2): + transport.type = nameof(ETransport.http); + transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost); + transport.path = node.Path.IsNullOrEmpty() ? null : node.Path; + break; + + case nameof(ETransport.tcp): //http + if (node.HeaderType == Global.TcpHeaderHttp) + { + if (node.ConfigType == EConfigType.Shadowsocks) + { + outbound.plugin = "obfs-local"; + outbound.plugin_opts = $"obfs=http;obfs-host={node.RequestHost};"; + } + else + { + transport.type = nameof(ETransport.http); + transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost); + transport.path = node.Path.IsNullOrEmpty() ? null : node.Path; + } + } + break; + + case nameof(ETransport.ws): + transport.type = nameof(ETransport.ws); + transport.path = node.Path.IsNullOrEmpty() ? null : node.Path; + if (node.RequestHost.IsNotEmpty()) + { + transport.headers = new() + { + Host = node.RequestHost + }; + } + break; + + case nameof(ETransport.httpupgrade): + transport.type = nameof(ETransport.httpupgrade); + transport.path = node.Path.IsNullOrEmpty() ? null : node.Path; + transport.host = node.RequestHost.IsNullOrEmpty() ? null : node.RequestHost; + + break; + + case nameof(ETransport.quic): + transport.type = nameof(ETransport.quic); + break; + + case nameof(ETransport.grpc): + transport.type = nameof(ETransport.grpc); + transport.service_name = node.Path; + transport.idle_timeout = _config.GrpcItem.IdleTimeout?.ToString("##s"); + transport.ping_timeout = _config.GrpcItem.HealthCheckTimeout?.ToString("##s"); + transport.permit_without_stream = _config.GrpcItem.PermitWithoutStream; + break; + + default: + break; + } + if (transport.type != null) + { + outbound.transport = transport; + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } + + private async Task GenMoreOutbounds(ProfileItem node, SingboxConfig singboxConfig) + { + if (node.Subid.IsNullOrEmpty()) + { + return 0; + } + try + { + var subItem = await AppManager.Instance.GetSubItem(node.Subid); + if (subItem is null) + { + return 0; + } + + //current proxy + BaseServer4Sbox? outbound = singboxConfig.endpoints?.FirstOrDefault(t => t.tag == Global.ProxyTag, null); + outbound ??= singboxConfig.outbounds.First(); + + var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); + + //Previous proxy + var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); + string? prevOutboundTag = null; + if (prevNode is not null + && prevNode.ConfigType != EConfigType.Custom) + { + prevOutboundTag = $"prev-{Global.ProxyTag}"; + var prevServer = await GenServer(prevNode); + prevServer.tag = prevOutboundTag; + if (prevServer is Endpoints4Sbox endpoint) + { + singboxConfig.endpoints ??= new(); + singboxConfig.endpoints.Add(endpoint); + } + else if (prevServer is Outbound4Sbox outboundPrev) + { + singboxConfig.outbounds.Add(outboundPrev); + } + } + var nextServer = await GenChainOutbounds(subItem, outbound, prevOutboundTag); + + if (nextServer is not null) + { + if (nextServer is Endpoints4Sbox endpoint) + { + singboxConfig.endpoints ??= new(); + singboxConfig.endpoints.Insert(0, endpoint); + } + else if (nextServer is Outbound4Sbox outboundNext) + { + singboxConfig.outbounds.Insert(0, outboundNext); + } + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + + return 0; + } + + private async Task GenOutboundsList(List nodes, SingboxConfig singboxConfig) + { + try + { + // Get outbound template and initialize lists + var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); + if (txtOutbound.IsNullOrEmpty()) + { + return 0; + } + + var resultOutbounds = new List(); + var resultEndpoints = new List(); // For endpoints + var prevOutbounds = new List(); // Separate list for prev outbounds + var prevEndpoints = new List(); // Separate list for prev endpoints + var proxyTags = new List(); // For selector and urltest outbounds + + // Cache for chain proxies to avoid duplicate generation + var nextProxyCache = new Dictionary(); + var prevProxyTags = new Dictionary(); // Map from profile name to tag + var prevIndex = 0; // Index for prev outbounds + + // Process each node + var index = 0; + foreach (var node in nodes) + { + index++; + + // Handle proxy chain + string? prevTag = null; + var currentServer = await GenServer(node); + var nextServer = nextProxyCache.GetValueOrDefault(node.Subid, null); + if (nextServer != null) + { + nextServer = JsonUtils.DeepCopy(nextServer); + } + + var subItem = await AppManager.Instance.GetSubItem(node.Subid); + + // current proxy + currentServer.tag = $"{Global.ProxyTag}-{index}"; + proxyTags.Add(currentServer.tag); + + if (!node.Subid.IsNullOrEmpty()) + { + if (prevProxyTags.TryGetValue(node.Subid, out var value)) + { + prevTag = value; // maybe null + } + else + { + var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); + if (prevNode is not null + && prevNode.ConfigType != EConfigType.Custom) + { + var prevOutbound = JsonUtils.Deserialize(txtOutbound); + await GenOutbound(prevNode, prevOutbound); + prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}"; + prevOutbound.tag = prevTag; + prevOutbounds.Add(prevOutbound); + } + prevProxyTags[node.Subid] = prevTag; + } + + nextServer = await GenChainOutbounds(subItem, currentServer, prevTag, nextServer); + if (!nextProxyCache.ContainsKey(node.Subid)) + { + nextProxyCache[node.Subid] = nextServer; + } + } + + if (nextServer is not null) + { + if (nextServer is Endpoints4Sbox nextEndpoint) + { + resultEndpoints.Add(nextEndpoint); + } + else if (nextServer is Outbound4Sbox nextOutbound) + { + resultOutbounds.Add(nextOutbound); + } + } + if (currentServer is Endpoints4Sbox currentEndpoint) + { + resultEndpoints.Add(currentEndpoint); + } + else if (currentServer is Outbound4Sbox currentOutbound) + { + resultOutbounds.Add(currentOutbound); + } + } + + // Add urltest outbound (auto selection based on latency) + if (proxyTags.Count > 0) + { + var outUrltest = new Outbound4Sbox + { + type = "urltest", + tag = $"{Global.ProxyTag}-auto", + outbounds = proxyTags, + interrupt_exist_connections = false, + }; + + // Add selector outbound (manual selection) + var outSelector = new Outbound4Sbox + { + type = "selector", + tag = Global.ProxyTag, + outbounds = JsonUtils.DeepCopy(proxyTags), + interrupt_exist_connections = false, + }; + outSelector.outbounds.Insert(0, outUrltest.tag); + + // Insert these at the beginning + resultOutbounds.Insert(0, outUrltest); + resultOutbounds.Insert(0, outSelector); + } + + // Merge results: first the selector/urltest/proxies, then other outbounds, and finally prev outbounds + resultOutbounds.AddRange(prevOutbounds); + resultOutbounds.AddRange(singboxConfig.outbounds); + singboxConfig.outbounds = resultOutbounds; + singboxConfig.endpoints ??= new List(); + resultEndpoints.AddRange(singboxConfig.endpoints); + singboxConfig.endpoints = resultEndpoints; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + + return 0; + } + + private async Task GenChainOutbounds(SubItem subItem, BaseServer4Sbox outbound, string? prevOutboundTag, BaseServer4Sbox? nextOutbound = null) + { + try + { + var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); + + if (!prevOutboundTag.IsNullOrEmpty()) + { + outbound.detour = prevOutboundTag; + } + + // Next proxy + var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile); + if (nextNode is not null + && nextNode.ConfigType != EConfigType.Custom) + { + nextOutbound ??= await GenServer(nextNode); + nextOutbound.tag = outbound.tag; + + outbound.tag = $"mid-{outbound.tag}"; + nextOutbound.detour = outbound.tag; + } + return nextOutbound; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return null; + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs new file mode 100644 index 0000000000..69492d9b65 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs @@ -0,0 +1,365 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigSingboxService +{ + private async Task GenRouting(SingboxConfig singboxConfig) + { + try + { + singboxConfig.route.final = Global.ProxyTag; + var item = _config.SimpleDNSItem; + + var defaultDomainResolverTag = Global.SingboxOutboundResolverTag; + var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct; + + var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); + if (rawDNSItem != null && rawDNSItem.Enabled == true) + { + defaultDomainResolverTag = Global.SingboxFinalResolverTag; + directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom; + } + singboxConfig.route.default_domain_resolver = new() + { + server = defaultDomainResolverTag, + strategy = directDNSStrategy + }; + + if (_config.TunModeItem.EnableTun) + { + singboxConfig.route.auto_detect_interface = true; + + var tunRules = JsonUtils.Deserialize>(EmbedUtils.GetEmbedText(Global.TunSingboxRulesFileName)); + if (tunRules != null) + { + singboxConfig.route.rules.AddRange(tunRules); + } + + GenRoutingDirectExe(out var lstDnsExe, out var lstDirectExe); + singboxConfig.route.rules.Add(new() + { + port = new() { 53 }, + action = "hijack-dns", + process_name = lstDnsExe + }); + + singboxConfig.route.rules.Add(new() + { + outbound = Global.DirectTag, + process_name = lstDirectExe + }); + } + + if (_config.Inbound.First().SniffingEnabled) + { + singboxConfig.route.rules.Add(new() + { + action = "sniff" + }); + singboxConfig.route.rules.Add(new() + { + protocol = new() { "dns" }, + action = "hijack-dns" + }); + } + else + { + singboxConfig.route.rules.Add(new() + { + port = new() { 53 }, + network = new() { "udp" }, + action = "hijack-dns" + }); + } + + singboxConfig.route.rules.Add(new() + { + outbound = Global.DirectTag, + clash_mode = ERuleMode.Direct.ToString() + }); + singboxConfig.route.rules.Add(new() + { + outbound = Global.ProxyTag, + clash_mode = ERuleMode.Global.ToString() + }); + + var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox; + var defaultRouting = await ConfigHandler.GetDefaultRouting(_config); + if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty()) + { + domainStrategy = defaultRouting.DomainStrategy4Singbox; + } + var resolveRule = new Rule4Sbox + { + action = "resolve", + strategy = domainStrategy + }; + if (_config.RoutingBasicItem.DomainStrategy == Global.IPOnDemand) + { + singboxConfig.route.rules.Add(resolveRule); + } + + var routing = await ConfigHandler.GetDefaultRouting(_config); + var ipRules = new List(); + if (routing != null) + { + var rules = JsonUtils.Deserialize>(routing.RuleSet); + foreach (var item1 in rules ?? []) + { + if (item1.Enabled) + { + await GenRoutingUserRule(item1, singboxConfig); + if (item1.Ip != null && item1.Ip.Count > 0) + { + ipRules.Add(item1); + } + } + } + } + if (_config.RoutingBasicItem.DomainStrategy == Global.IPIfNonMatch) + { + singboxConfig.route.rules.Add(resolveRule); + foreach (var item2 in ipRules) + { + await GenRoutingUserRule(item2, singboxConfig); + } + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private void GenRoutingDirectExe(out List lstDnsExe, out List lstDirectExe) + { + var dnsExeSet = new HashSet(StringComparer.OrdinalIgnoreCase); + var directExeSet = new HashSet(StringComparer.OrdinalIgnoreCase); + + var coreInfoResult = CoreInfoManager.Instance.GetCoreInfo(); + + foreach (var coreConfig in coreInfoResult) + { + if (coreConfig.CoreType == ECoreType.v2rayN) + { + continue; + } + + foreach (var baseExeName in coreConfig.CoreExes) + { + if (coreConfig.CoreType != ECoreType.sing_box) + { + dnsExeSet.Add(Utils.GetExeName(baseExeName)); + } + directExeSet.Add(Utils.GetExeName(baseExeName)); + } + } + + lstDnsExe = new List(dnsExeSet); + lstDirectExe = new List(directExeSet); + } + + private async Task GenRoutingUserRule(RulesItem item, SingboxConfig singboxConfig) + { + try + { + if (item == null) + { + return 0; + } + item.OutboundTag = await GenRoutingUserRuleOutbound(item.OutboundTag, singboxConfig); + var rules = singboxConfig.route.rules; + + var rule = new Rule4Sbox(); + if (item.OutboundTag == "block") + { + rule.action = "reject"; + } + else + { + rule.outbound = item.OutboundTag; + } + + if (item.Port.IsNotEmpty()) + { + var portRanges = item.Port.Split(',').Where(it => it.Contains('-')).Select(it => it.Replace("-", ":")).ToList(); + var ports = item.Port.Split(',').Where(it => !it.Contains('-')).Select(it => it.ToInt()).ToList(); + + rule.port_range = portRanges.Count > 0 ? portRanges : null; + rule.port = ports.Count > 0 ? ports : null; + } + if (item.Network.IsNotEmpty()) + { + rule.network = Utils.String2List(item.Network); + } + if (item.Protocol?.Count > 0) + { + rule.protocol = item.Protocol; + } + if (item.InboundTag?.Count >= 0) + { + rule.inbound = item.InboundTag; + } + var rule1 = JsonUtils.DeepCopy(rule); + var rule2 = JsonUtils.DeepCopy(rule); + var rule3 = JsonUtils.DeepCopy(rule); + + var hasDomainIp = false; + if (item.Domain?.Count > 0) + { + var countDomain = 0; + foreach (var it in item.Domain) + { + if (ParseV2Domain(it, rule1)) + countDomain++; + } + if (countDomain > 0) + { + rules.Add(rule1); + hasDomainIp = true; + } + } + + if (item.Ip?.Count > 0) + { + var countIp = 0; + foreach (var it in item.Ip) + { + if (ParseV2Address(it, rule2)) + countIp++; + } + if (countIp > 0) + { + rules.Add(rule2); + hasDomainIp = true; + } + } + + if (_config.TunModeItem.EnableTun && item.Process?.Count > 0) + { + rule3.process_name = item.Process; + rules.Add(rule3); + hasDomainIp = true; + } + + if (!hasDomainIp + && (rule.port != null || rule.port_range != null || rule.protocol != null || rule.inbound != null || rule.network != null)) + { + rules.Add(rule); + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } + + private bool ParseV2Domain(string domain, Rule4Sbox rule) + { + if (domain.StartsWith("#") || domain.StartsWith("ext:") || domain.StartsWith("ext-domain:")) + { + return false; + } + else if (domain.StartsWith("geosite:")) + { + rule.geosite ??= []; + rule.geosite?.Add(domain.Substring(8)); + } + else if (domain.StartsWith("regexp:")) + { + rule.domain_regex ??= []; + rule.domain_regex?.Add(domain.Replace(Global.RoutingRuleComma, ",").Substring(7)); + } + else if (domain.StartsWith("domain:")) + { + rule.domain ??= []; + rule.domain_suffix ??= []; + rule.domain?.Add(domain.Substring(7)); + rule.domain_suffix?.Add("." + domain.Substring(7)); + } + else if (domain.StartsWith("full:")) + { + rule.domain ??= []; + rule.domain?.Add(domain.Substring(5)); + } + else if (domain.StartsWith("keyword:")) + { + rule.domain_keyword ??= []; + rule.domain_keyword?.Add(domain.Substring(8)); + } + else + { + rule.domain_keyword ??= []; + rule.domain_keyword?.Add(domain); + } + return true; + } + + private bool ParseV2Address(string address, Rule4Sbox rule) + { + if (address.StartsWith("ext:") || address.StartsWith("ext-ip:")) + { + return false; + } + else if (address.Equals("geoip:private")) + { + rule.ip_is_private = true; + } + else if (address.StartsWith("geoip:")) + { + rule.geoip ??= new(); + rule.geoip?.Add(address.Substring(6)); + } + else if (address.Equals("geoip:!private")) + { + rule.ip_is_private = false; + } + else if (address.StartsWith("geoip:!")) + { + rule.geoip ??= new(); + rule.geoip?.Add(address.Substring(6)); + rule.invert = true; + } + else + { + rule.ip_cidr ??= new(); + rule.ip_cidr?.Add(address); + } + return true; + } + + private async Task GenRoutingUserRuleOutbound(string outboundTag, SingboxConfig singboxConfig) + { + if (Global.OutboundTags.Contains(outboundTag)) + { + return outboundTag; + } + + var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag); + if (node == null + || node.ConfigType == EConfigType.Custom) + { + return Global.ProxyTag; + } + + var server = await GenServer(node); + if (server is null) + { + return Global.ProxyTag; + } + + server.tag = Global.ProxyTag + node.IndexId.ToString(); + if (server is Endpoints4Sbox endpoint) + { + singboxConfig.endpoints ??= new(); + singboxConfig.endpoints.Add(endpoint); + } + else if (server is Outbound4Sbox outbound) + { + singboxConfig.outbounds.Add(outbound); + } + + return server.tag; + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRulesetService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRulesetService.cs new file mode 100644 index 0000000000..ef611c91b2 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRulesetService.cs @@ -0,0 +1,119 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigSingboxService +{ + private async Task ConvertGeo2Ruleset(SingboxConfig singboxConfig) + { + static void AddRuleSets(List ruleSets, List? rule_set) + { + if (rule_set != null) + ruleSets.AddRange(rule_set); + } + var geosite = "geosite"; + var geoip = "geoip"; + var ruleSets = new List(); + + //convert route geosite & geoip to ruleset + foreach (var rule in singboxConfig.route.rules.Where(t => t.geosite?.Count > 0).ToList() ?? []) + { + rule.rule_set ??= new List(); + rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList()); + rule.geosite = null; + AddRuleSets(ruleSets, rule.rule_set); + } + foreach (var rule in singboxConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? []) + { + rule.rule_set ??= new List(); + rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList()); + rule.geoip = null; + AddRuleSets(ruleSets, rule.rule_set); + } + + //convert dns geosite & geoip to ruleset + foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geosite?.Count > 0).ToList() ?? []) + { + rule.rule_set ??= new List(); + rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList()); + rule.geosite = null; + } + foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geoip?.Count > 0).ToList() ?? []) + { + rule.rule_set ??= new List(); + rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList()); + rule.geoip = null; + } + foreach (var dnsRule in singboxConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? []) + { + AddRuleSets(ruleSets, dnsRule.rule_set); + } + //rules in rules + foreach (var item in singboxConfig.dns?.rules.Where(t => t.rules?.Count > 0).Select(t => t.rules).ToList() ?? []) + { + foreach (var item2 in item ?? []) + { + AddRuleSets(ruleSets, item2.rule_set); + } + } + + //load custom ruleset file + List customRulesets = []; + + var routing = await ConfigHandler.GetDefaultRouting(_config); + if (routing.CustomRulesetPath4Singbox.IsNotEmpty()) + { + var result = EmbedUtils.LoadResource(routing.CustomRulesetPath4Singbox); + if (result.IsNotEmpty()) + { + customRulesets = (JsonUtils.Deserialize>(result) ?? []) + .Where(t => t.tag != null) + .Where(t => t.type != null) + .Where(t => t.format != null) + .ToList(); + } + } + + //Local srs files address + var localSrss = Utils.GetBinPath("srss"); + + //Add ruleset srs + singboxConfig.route.rule_set = []; + foreach (var item in new HashSet(ruleSets)) + { + if (item.IsNullOrEmpty()) + { continue; } + var customRuleset = customRulesets.FirstOrDefault(t => t.tag != null && t.tag.Equals(item)); + if (customRuleset is null) + { + var pathSrs = Path.Combine(localSrss, $"{item}.srs"); + if (File.Exists(pathSrs)) + { + customRuleset = new() + { + type = "local", + format = "binary", + tag = item, + path = pathSrs + }; + } + else + { + var srsUrl = string.IsNullOrEmpty(_config.ConstItem.SrsSourceUrl) + ? Global.SingboxRulesetUrl + : _config.ConstItem.SrsSourceUrl; + + customRuleset = new() + { + type = "remote", + format = "binary", + tag = item, + url = string.Format(srsUrl, item.StartsWith(geosite) ? geosite : geoip, item), + download_detour = Global.ProxyTag + }; + } + } + singboxConfig.route.rule_set.Add(customRuleset); + } + + return 0; + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxStatisticService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxStatisticService.cs new file mode 100644 index 0000000000..c3acd810b2 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxStatisticService.cs @@ -0,0 +1,29 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigSingboxService +{ + private async Task GenExperimental(SingboxConfig singboxConfig) + { + //if (_config.guiItem.enableStatistics) + { + singboxConfig.experimental ??= new Experimental4Sbox(); + singboxConfig.experimental.clash_api = new Clash_Api4Sbox() + { + external_controller = $"{Global.Loopback}:{AppManager.Instance.StatePort2}", + }; + } + + if (_config.CoreBasicItem.EnableCacheFile4Sbox) + { + singboxConfig.experimental ??= new Experimental4Sbox(); + singboxConfig.experimental.cache_file = new CacheFile4Sbox() + { + enabled = true, + path = Utils.GetBinPath("cache.db"), + store_fakeip = _config.SimpleDNSItem.FakeIP == true + }; + } + + return await Task.FromResult(0); + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs new file mode 100644 index 0000000000..7f1ada5d9b --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs @@ -0,0 +1,415 @@ +using System.Net; +using System.Net.NetworkInformation; + +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigV2rayService(Config config) +{ + private readonly Config _config = config; + private static readonly string _tag = "CoreConfigV2rayService"; + + #region public gen function + + public async Task GenerateClientConfigContent(ProfileItem node) + { + var ret = new RetResult(); + try + { + if (node == null + || node.Port <= 0) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + if (node.GetNetwork() is nameof(ETransport.quic)) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; + return ret; + } + + ret.Msg = ResUI.InitialConfiguration; + + var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); + if (result.IsNullOrEmpty()) + { + ret.Msg = ResUI.FailedGetDefaultConfiguration; + return ret; + } + + var v2rayConfig = JsonUtils.Deserialize(result); + if (v2rayConfig == null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + + await GenLog(v2rayConfig); + + await GenInbounds(v2rayConfig); + + await GenOutbound(node, v2rayConfig.outbounds.First()); + + await GenMoreOutbounds(node, v2rayConfig); + + await GenRouting(v2rayConfig); + + await GenDns(node, v2rayConfig); + + await GenStatistic(v2rayConfig); + + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); + ret.Success = true; + ret.Data = await ApplyFullConfigTemplate(v2rayConfig); + return ret; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } + + public async Task GenerateClientMultipleLoadConfig(List selecteds, EMultipleLoad multipleLoad) + { + var ret = new RetResult(); + + try + { + if (_config == null) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + ret.Msg = ResUI.InitialConfiguration; + + string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); + string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); + if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty()) + { + ret.Msg = ResUI.FailedGetDefaultConfiguration; + return ret; + } + + var v2rayConfig = JsonUtils.Deserialize(result); + if (v2rayConfig == null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + + await GenLog(v2rayConfig); + await GenInbounds(v2rayConfig); + await GenRouting(v2rayConfig); + await GenDns(null, v2rayConfig); + await GenStatistic(v2rayConfig); + v2rayConfig.outbounds.RemoveAt(0); + + var proxyProfiles = new List(); + foreach (var it in selecteds) + { + if (it.ConfigType == EConfigType.Custom) + { + continue; + } + if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) + { + continue; + } + if (it.Port <= 0) + { + continue; + } + var item = await AppManager.Instance.GetProfileItem(it.IndexId); + if (item is null) + { + continue; + } + if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS) + { + if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id)) + { + continue; + } + } + if (item.ConfigType == EConfigType.Shadowsocks + && !Global.SsSecuritiesInSingbox.Contains(item.Security)) + { + continue; + } + if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow)) + { + continue; + } + + //outbound + proxyProfiles.Add(item); + } + if (proxyProfiles.Count <= 0) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + await GenOutboundsList(proxyProfiles, v2rayConfig); + + //add balancers + await GenBalancer(v2rayConfig, multipleLoad); + + var balancer = v2rayConfig.routing.balancers.First(); + + //add rule + var rules = v2rayConfig.routing.rules.Where(t => t.outboundTag == Global.ProxyTag).ToList(); + if (rules?.Count > 0) + { + foreach (var rule in rules) + { + rule.outboundTag = null; + rule.balancerTag = balancer.tag; + } + } + if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) + { + v2rayConfig.routing.rules.Add(new() + { + ip = ["0.0.0.0/0", "::/0"], + balancerTag = balancer.tag, + type = "field" + }); + } + else + { + v2rayConfig.routing.rules.Add(new() + { + network = "tcp,udp", + balancerTag = balancer.tag, + type = "field" + }); + } + + ret.Success = true; + + ret.Data = await ApplyFullConfigTemplate(v2rayConfig, true); + return ret; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } + + public async Task GenerateClientSpeedtestConfig(List selecteds) + { + var ret = new RetResult(); + try + { + if (_config == null) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + ret.Msg = ResUI.InitialConfiguration; + + var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); + var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); + if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty()) + { + ret.Msg = ResUI.FailedGetDefaultConfiguration; + return ret; + } + + var v2rayConfig = JsonUtils.Deserialize(result); + if (v2rayConfig == null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + List lstIpEndPoints = new(); + List lstTcpConns = new(); + try + { + lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); + lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners()); + lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + + await GenLog(v2rayConfig); + v2rayConfig.inbounds.Clear(); + v2rayConfig.outbounds.Clear(); + v2rayConfig.routing.rules.Clear(); + + var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest); + + foreach (var it in selecteds) + { + if (it.ConfigType == EConfigType.Custom) + { + continue; + } + if (it.Port <= 0) + { + continue; + } + var item = await AppManager.Instance.GetProfileItem(it.IndexId); + if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS) + { + if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id)) + { + continue; + } + } + + //find unused port + var port = initPort; + for (var k = initPort; k < Global.MaxPort; k++) + { + if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) + { + continue; + } + if (lstTcpConns?.FindIndex(_it => _it.LocalEndPoint.Port == k) >= 0) + { + continue; + } + //found + port = k; + initPort = port + 1; + break; + } + + //Port In Used + if (lstIpEndPoints?.FindIndex(_it => _it.Port == port) >= 0) + { + continue; + } + it.Port = port; + it.AllowTest = true; + + //outbound + if (item is null) + { + continue; + } + if (item.ConfigType == EConfigType.Shadowsocks + && !Global.SsSecuritiesInXray.Contains(item.Security)) + { + continue; + } + if (item.ConfigType == EConfigType.VLESS + && !Global.Flows.Contains(item.Flow)) + { + continue; + } + if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan + && item.StreamSecurity == Global.StreamSecurityReality + && item.PublicKey.IsNullOrEmpty()) + { + continue; + } + + //inbound + Inbounds4Ray inbound = new() + { + listen = Global.Loopback, + port = port, + protocol = EInboundProtocol.mixed.ToString(), + }; + inbound.tag = inbound.protocol + inbound.port.ToString(); + v2rayConfig.inbounds.Add(inbound); + + var outbound = JsonUtils.Deserialize(txtOutbound); + await GenOutbound(item, outbound); + outbound.tag = Global.ProxyTag + inbound.port.ToString(); + v2rayConfig.outbounds.Add(outbound); + + //rule + RulesItem4Ray rule = new() + { + inboundTag = new List { inbound.tag }, + outboundTag = outbound.tag, + type = "field" + }; + v2rayConfig.routing.rules.Add(rule); + } + + //ret.Msg =string.Format(ResUI.SuccessfulConfiguration"), node.getSummary()); + ret.Success = true; + ret.Data = JsonUtils.Serialize(v2rayConfig); + return ret; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } + + public async Task GenerateClientSpeedtestConfig(ProfileItem node, int port) + { + var ret = new RetResult(); + try + { + if (node is not { Port: > 0 }) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + if (node.GetNetwork() is nameof(ETransport.quic)) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; + return ret; + } + + var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); + if (result.IsNullOrEmpty()) + { + ret.Msg = ResUI.FailedGetDefaultConfiguration; + return ret; + } + + var v2rayConfig = JsonUtils.Deserialize(result); + if (v2rayConfig == null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + + await GenLog(v2rayConfig); + await GenOutbound(node, v2rayConfig.outbounds.First()); + await GenMoreOutbounds(node, v2rayConfig); + + v2rayConfig.routing.rules.Clear(); + v2rayConfig.inbounds.Clear(); + v2rayConfig.inbounds.Add(new() + { + tag = $"{EInboundProtocol.socks}{port}", + listen = Global.Loopback, + port = port, + protocol = EInboundProtocol.mixed.ToString(), + }); + + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); + ret.Success = true; + ret.Data = JsonUtils.Serialize(v2rayConfig); + return ret; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } + + #endregion public gen function +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs new file mode 100644 index 0000000000..8d2476e69c --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs @@ -0,0 +1,50 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigV2rayService +{ + private async Task GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad) + { + if (multipleLoad == EMultipleLoad.LeastPing) + { + var observatory = new Observatory4Ray + { + subjectSelector = [Global.ProxyTag], + probeUrl = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl, + probeInterval = "3m", + enableConcurrency = true, + }; + v2rayConfig.observatory = observatory; + } + else if (multipleLoad == EMultipleLoad.LeastLoad) + { + var burstObservatory = new BurstObservatory4Ray + { + subjectSelector = [Global.ProxyTag], + pingConfig = new() + { + destination = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl, + interval = "5m", + timeout = "30s", + sampling = 2, + } + }; + v2rayConfig.burstObservatory = burstObservatory; + } + var strategyType = multipleLoad switch + { + EMultipleLoad.Random => "random", + EMultipleLoad.RoundRobin => "roundRobin", + EMultipleLoad.LeastPing => "leastPing", + EMultipleLoad.LeastLoad => "leastLoad", + _ => "roundRobin", + }; + var balancer = new BalancersItem4Ray + { + selector = [Global.ProxyTag], + strategy = new() { type = strategyType }, + tag = $"{Global.ProxyTag}-round", + }; + v2rayConfig.routing.balancers = [balancer]; + return await Task.FromResult(0); + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs new file mode 100644 index 0000000000..5d1f7d63ee --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs @@ -0,0 +1,86 @@ +using System.Text.Json.Nodes; + +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigV2rayService +{ + private async Task ApplyFullConfigTemplate(V2rayConfig v2rayConfig, bool handleBalancerAndRules = false) + { + var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray); + if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty()) + { + return JsonUtils.Serialize(v2rayConfig); + } + + var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplate.Config); + if (fullConfigTemplateNode == null) + { + return JsonUtils.Serialize(v2rayConfig); + } + + // Handle balancer and rules modifications (for multiple load scenarios) + if (handleBalancerAndRules && v2rayConfig.routing?.balancers?.Count > 0) + { + var balancer = v2rayConfig.routing.balancers.First(); + + // Modify existing rules in custom config + var rulesNode = fullConfigTemplateNode["routing"]?["rules"]; + if (rulesNode != null) + { + foreach (var rule in rulesNode.AsArray()) + { + if (rule["outboundTag"]?.GetValue() == Global.ProxyTag) + { + rule.AsObject().Remove("outboundTag"); + rule["balancerTag"] = balancer.tag; + } + } + } + + // Ensure routing node exists + if (fullConfigTemplateNode["routing"] == null) + { + fullConfigTemplateNode["routing"] = new JsonObject(); + } + + // Handle balancers - append instead of override + if (fullConfigTemplateNode["routing"]["balancers"] is JsonArray customBalancersNode) + { + if (JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)) is JsonArray newBalancers) + { + foreach (var balancerNode in newBalancers) + { + customBalancersNode.Add(balancerNode?.DeepClone()); + } + } + } + else + { + fullConfigTemplateNode["routing"]["balancers"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)); + } + } + + // Handle outbounds - append instead of override + var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray(); + foreach (var outbound in v2rayConfig.outbounds) + { + if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom") + { + if (fullConfigTemplate.AddProxyOnly == true) + { + continue; + } + } + else if ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == true) && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) && !(Utils.IsPrivateNetwork(outbound.settings?.servers?.FirstOrDefault()?.address ?? string.Empty) || Utils.IsPrivateNetwork(outbound.settings?.vnext?.FirstOrDefault()?.address ?? string.Empty))) + { + outbound.streamSettings ??= new StreamSettings4Ray(); + outbound.streamSettings.sockopt ??= new Sockopt4Ray(); + outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour; + } + customOutboundsNode.Add(JsonUtils.DeepCopy(outbound)); + } + fullConfigTemplateNode["outbounds"] = customOutboundsNode; + + return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode)); + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs new file mode 100644 index 0000000000..ddb47c99c9 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs @@ -0,0 +1,426 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigV2rayService +{ + private async Task GenDns(ProfileItem? node, V2rayConfig v2rayConfig) + { + try + { + var item = await AppManager.Instance.GetDNSItem(ECoreType.Xray); + if (item != null && item.Enabled == true) + { + var result = await GenDnsCompatible(node, v2rayConfig); + + if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) + { + // DNS routing + v2rayConfig.dns.tag = Global.DnsTag; + v2rayConfig.routing.rules.Add(new RulesItem4Ray + { + type = "field", + inboundTag = new List { Global.DnsTag }, + outboundTag = Global.ProxyTag, + }); + } + + return result; + } + var simpleDNSItem = _config.SimpleDNSItem; + var domainStrategy4Freedom = simpleDNSItem?.RayStrategy4Freedom; + + //Outbound Freedom domainStrategy + if (domainStrategy4Freedom.IsNotEmpty()) + { + var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag }); + if (outbound != null) + { + outbound.settings = new() + { + domainStrategy = domainStrategy4Freedom, + userLevel = 0 + }; + } + } + + await GenDnsServers(node, v2rayConfig, simpleDNSItem); + await GenDnsHosts(v2rayConfig, simpleDNSItem); + + if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) + { + // DNS routing + v2rayConfig.dns.tag = Global.DnsTag; + v2rayConfig.routing.rules.Add(new RulesItem4Ray + { + type = "field", + inboundTag = new List { Global.DnsTag }, + outboundTag = Global.ProxyTag, + }); + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private async Task GenDnsServers(ProfileItem? node, V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem) + { + static List ParseDnsAddresses(string? dnsInput, string defaultAddress) + { + var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';') + .Select(addr => addr.Trim()) + .Where(addr => !string.IsNullOrEmpty(addr)) + .Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr) + .Distinct() + .ToList() ?? new List { defaultAddress }; + return addresses.Count > 0 ? addresses : new List { defaultAddress }; + } + + static object CreateDnsServer(string dnsAddress, List domains, List? expectedIPs = null) + { + var dnsServer = new DnsServer4Ray + { + address = dnsAddress, + skipFallback = true, + domains = domains.Count > 0 ? domains : null, + expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null + }; + return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }); + } + + var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.FirstOrDefault()); + var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.FirstOrDefault()); + + var directDomainList = new List(); + var directGeositeList = new List(); + var proxyDomainList = new List(); + var proxyGeositeList = new List(); + var expectedDomainList = new List(); + var expectedIPs = new List(); + var regionNames = new HashSet(); + + if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs)) + { + expectedIPs = simpleDNSItem.DirectExpectedIPs + .Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.Trim()) + .Where(s => !string.IsNullOrEmpty(s)) + .ToList(); + + foreach (var ip in expectedIPs) + { + if (ip.StartsWith("geoip:", StringComparison.OrdinalIgnoreCase)) + { + var region = ip["geoip:".Length..]; + if (!string.IsNullOrEmpty(region)) + { + regionNames.Add($"geosite:{region}"); + regionNames.Add($"geosite:geolocation-{region}"); + regionNames.Add($"geosite:tld-{region}"); + } + } + } + } + + var routing = await ConfigHandler.GetDefaultRouting(_config); + List? rules = null; + if (routing != null) + { + rules = JsonUtils.Deserialize>(routing.RuleSet) ?? []; + foreach (var item in rules) + { + if (!item.Enabled || item.Domain is null || item.Domain.Count == 0) + { + continue; + } + + foreach (var domain in item.Domain) + { + if (domain.StartsWith('#')) + continue; + var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ","); + + if (item.OutboundTag == Global.DirectTag) + { + if (normalizedDomain.StartsWith("geosite:")) + { + (regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain); + } + else + { + directDomainList.Add(normalizedDomain); + } + } + else if (item.OutboundTag != Global.BlockTag) + { + if (normalizedDomain.StartsWith("geosite:")) + { + proxyGeositeList.Add(normalizedDomain); + } + else + { + proxyDomainList.Add(normalizedDomain); + } + } + } + } + } + + if (Utils.IsDomain(node?.Address)) + { + directDomainList.Add(node.Address); + } + + if (node?.Subid is not null) + { + var subItem = await AppManager.Instance.GetSubItem(node.Subid); + if (subItem is not null) + { + foreach (var profile in new[] { subItem.PrevProfile, subItem.NextProfile }) + { + var profileNode = await AppManager.Instance.GetProfileItemViaRemarks(profile); + if (profileNode is not null && + profileNode.ConfigType is not (EConfigType.Custom or EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) && + Utils.IsDomain(profileNode.Address)) + { + directDomainList.Add(profileNode.Address); + } + } + } + } + + v2rayConfig.dns ??= new Dns4Ray(); + v2rayConfig.dns.servers ??= new List(); + + void AddDnsServers(List dnsAddresses, List domains, List? expectedIPs = null) + { + if (domains.Count > 0) + { + foreach (var dnsAddress in dnsAddresses) + { + v2rayConfig.dns.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs)); + } + } + } + + AddDnsServers(remoteDNSAddress, proxyDomainList); + AddDnsServers(directDNSAddress, directDomainList); + AddDnsServers(remoteDNSAddress, proxyGeositeList); + AddDnsServers(directDNSAddress, directGeositeList); + AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs); + + var useDirectDns = rules?.LastOrDefault() is { } lastRule && + lastRule.OutboundTag == Global.DirectTag && + (lastRule.Port == "0-65535" || + lastRule.Network == "tcp,udp" || + lastRule.Ip?.Contains("0.0.0.0/0") == true); + + var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress; + v2rayConfig.dns.servers.AddRange(defaultDnsServers); + + return 0; + } + + private async Task GenDnsHosts(V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem) + { + if (simpleDNSItem.AddCommonHosts == false && simpleDNSItem.UseSystemHosts == false && simpleDNSItem.Hosts.IsNullOrEmpty()) + { + return await Task.FromResult(0); + } + v2rayConfig.dns ??= new Dns4Ray(); + v2rayConfig.dns.hosts ??= new Dictionary(); + if (simpleDNSItem.AddCommonHosts == true) + { + v2rayConfig.dns.hosts = Global.PredefinedHosts.ToDictionary( + kvp => kvp.Key, + kvp => (object)kvp.Value + ); + } + + if (simpleDNSItem.UseSystemHosts == true) + { + var systemHosts = Utils.GetSystemHosts(); + if (systemHosts.Count > 0) + { + var normalHost = v2rayConfig.dns.hosts; + if (normalHost != null) + { + foreach (var host in systemHosts) + { + if (normalHost[host.Key] != null) + { + continue; + } + normalHost[host.Key] = new List { host.Value }; + } + } + } + } + + var userHostsMap = simpleDNSItem.Hosts? + .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) + .Where(line => !string.IsNullOrWhiteSpace(line)) + .Where(line => line.Contains(' ')) + .ToDictionary( + line => + { + var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + return parts[0]; + }, + line => + { + var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + var values = parts.Skip(1).ToList(); + return values; + } + ); + + if (userHostsMap != null) + { + foreach (var kvp in userHostsMap) + { + v2rayConfig.dns.hosts[kvp.Key] = kvp.Value; + } + } + return await Task.FromResult(0); + } + + private async Task GenDnsCompatible(ProfileItem? node, V2rayConfig v2rayConfig) + { + try + { + var item = await AppManager.Instance.GetDNSItem(ECoreType.Xray); + var normalDNS = item?.NormalDNS; + var domainStrategy4Freedom = item?.DomainStrategy4Freedom; + if (normalDNS.IsNullOrEmpty()) + { + normalDNS = EmbedUtils.GetEmbedText(Global.DNSV2rayNormalFileName); + } + + //Outbound Freedom domainStrategy + if (domainStrategy4Freedom.IsNotEmpty()) + { + var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag }); + if (outbound != null) + { + outbound.settings = new(); + outbound.settings.domainStrategy = domainStrategy4Freedom; + outbound.settings.userLevel = 0; + } + } + + var obj = JsonUtils.ParseJson(normalDNS); + if (obj is null) + { + List servers = []; + string[] arrDNS = normalDNS.Split(','); + foreach (string str in arrDNS) + { + servers.Add(str); + } + obj = JsonUtils.ParseJson("{}"); + obj["servers"] = JsonUtils.SerializeToNode(servers); + } + + // Append to dns settings + if (item.UseSystemHosts) + { + var systemHosts = Utils.GetSystemHosts(); + if (systemHosts.Count > 0) + { + var normalHost1 = obj["hosts"]; + if (normalHost1 != null) + { + foreach (var host in systemHosts) + { + if (normalHost1[host.Key] != null) + continue; + normalHost1[host.Key] = host.Value; + } + } + } + } + var normalHost = obj["hosts"]; + if (normalHost != null) + { + foreach (var hostProp in normalHost.AsObject().ToList()) + { + if (hostProp.Value is JsonValue value && value.TryGetValue(out var ip)) + { + normalHost[hostProp.Key] = new JsonArray(ip); + } + } + } + + await GenDnsDomainsCompatible(node, obj, item); + + v2rayConfig.dns = JsonUtils.Deserialize(JsonUtils.Serialize(obj)); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private async Task GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dNSItem) + { + if (node == null) + { + return 0; + } + var servers = dns["servers"]; + if (servers != null) + { + var domainList = new List(); + if (Utils.IsDomain(node.Address)) + { + domainList.Add(node.Address); + } + var subItem = await AppManager.Instance.GetSubItem(node.Subid); + if (subItem is not null) + { + // Previous proxy + var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); + if (prevNode is not null + && prevNode.ConfigType != EConfigType.Custom + && prevNode.ConfigType != EConfigType.Hysteria2 + && prevNode.ConfigType != EConfigType.TUIC + && Utils.IsDomain(prevNode.Address)) + { + domainList.Add(prevNode.Address); + } + + // Next proxy + var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile); + if (nextNode is not null + && nextNode.ConfigType != EConfigType.Custom + && nextNode.ConfigType != EConfigType.Hysteria2 + && nextNode.ConfigType != EConfigType.TUIC + && Utils.IsDomain(nextNode.Address)) + { + domainList.Add(nextNode.Address); + } + } + if (domainList.Count > 0) + { + var dnsServer = new DnsServer4Ray() + { + address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, + skipFallback = true, + domains = domainList + }; + servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer)); + } + } + return await Task.FromResult(0); + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs new file mode 100644 index 0000000000..7753c21e25 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs @@ -0,0 +1,72 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigV2rayService +{ + private async Task GenInbounds(V2rayConfig v2rayConfig) + { + try + { + var listen = "0.0.0.0"; + v2rayConfig.inbounds = []; + + var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.socks, true); + v2rayConfig.inbounds.Add(inbound); + + if (_config.Inbound.First().SecondLocalPortEnabled) + { + var inbound2 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks2, true); + v2rayConfig.inbounds.Add(inbound2); + } + + if (_config.Inbound.First().AllowLANConn) + { + if (_config.Inbound.First().NewPort4LAN) + { + var inbound3 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks3, true); + inbound3.listen = listen; + v2rayConfig.inbounds.Add(inbound3); + + //auth + if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty()) + { + inbound3.settings.auth = "password"; + inbound3.settings.accounts = new List { new AccountsItem4Ray() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } }; + } + } + else + { + inbound.listen = listen; + } + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } + + private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks) + { + string result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound); + if (result.IsNullOrEmpty()) + { + return new(); + } + + var inbound = JsonUtils.Deserialize(result); + if (inbound == null) + { + return new(); + } + inbound.tag = protocol.ToString(); + inbound.port = inItem.LocalPort + (int)protocol; + inbound.protocol = EInboundProtocol.mixed.ToString(); + inbound.settings.udp = inItem.UdpEnabled; + inbound.sniffing.enabled = inItem.SniffingEnabled; + inbound.sniffing.destOverride = inItem.DestOverride; + inbound.sniffing.routeOnly = inItem.RouteOnly; + + return inbound; + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayLogService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayLogService.cs new file mode 100644 index 0000000000..5b9344fb6c --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayLogService.cs @@ -0,0 +1,29 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigV2rayService +{ + private async Task GenLog(V2rayConfig v2rayConfig) + { + try + { + if (_config.CoreBasicItem.LogEnabled) + { + var dtNow = DateTime.Now; + v2rayConfig.log.loglevel = _config.CoreBasicItem.Loglevel; + v2rayConfig.log.access = Utils.GetLogPath($"Vaccess_{dtNow:yyyy-MM-dd}.txt"); + v2rayConfig.log.error = Utils.GetLogPath($"Verror_{dtNow:yyyy-MM-dd}.txt"); + } + else + { + v2rayConfig.log.loglevel = _config.CoreBasicItem.Loglevel; + v2rayConfig.log.access = null; + v2rayConfig.log.error = null; + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs new file mode 100644 index 0000000000..a40f2d7314 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -0,0 +1,704 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigV2rayService +{ + private async Task GenOutbound(ProfileItem node, Outbounds4Ray outbound) + { + try + { + var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled; + switch (node.ConfigType) + { + case EConfigType.VMess: + { + VnextItem4Ray vnextItem; + if (outbound.settings.vnext.Count <= 0) + { + vnextItem = new VnextItem4Ray(); + outbound.settings.vnext.Add(vnextItem); + } + else + { + vnextItem = outbound.settings.vnext.First(); + } + vnextItem.address = node.Address; + vnextItem.port = node.Port; + + UsersItem4Ray usersItem; + if (vnextItem.users.Count <= 0) + { + usersItem = new UsersItem4Ray(); + vnextItem.users.Add(usersItem); + } + else + { + usersItem = vnextItem.users.First(); + } + + usersItem.id = node.Id; + usersItem.alterId = node.AlterId; + usersItem.email = Global.UserEMail; + if (Global.VmessSecurities.Contains(node.Security)) + { + usersItem.security = node.Security; + } + else + { + usersItem.security = Global.DefaultSecurity; + } + + await GenOutboundMux(node, outbound, muxEnabled, muxEnabled); + + outbound.settings.servers = null; + break; + } + case EConfigType.Shadowsocks: + { + ServersItem4Ray serversItem; + if (outbound.settings.servers.Count <= 0) + { + serversItem = new ServersItem4Ray(); + outbound.settings.servers.Add(serversItem); + } + else + { + serversItem = outbound.settings.servers.First(); + } + serversItem.address = node.Address; + serversItem.port = node.Port; + serversItem.password = node.Id; + serversItem.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : "none"; + + serversItem.ota = false; + serversItem.level = 1; + + await GenOutboundMux(node, outbound); + + outbound.settings.vnext = null; + break; + } + case EConfigType.SOCKS: + case EConfigType.HTTP: + { + ServersItem4Ray serversItem; + if (outbound.settings.servers.Count <= 0) + { + serversItem = new ServersItem4Ray(); + outbound.settings.servers.Add(serversItem); + } + else + { + serversItem = outbound.settings.servers.First(); + } + serversItem.address = node.Address; + serversItem.port = node.Port; + serversItem.method = null; + serversItem.password = null; + + if (node.Security.IsNotEmpty() + && node.Id.IsNotEmpty()) + { + SocksUsersItem4Ray socksUsersItem = new() + { + user = node.Security, + pass = node.Id, + level = 1 + }; + + serversItem.users = new List() { socksUsersItem }; + } + + await GenOutboundMux(node, outbound); + + outbound.settings.vnext = null; + break; + } + case EConfigType.VLESS: + { + VnextItem4Ray vnextItem; + if (outbound.settings.vnext?.Count <= 0) + { + vnextItem = new VnextItem4Ray(); + outbound.settings.vnext.Add(vnextItem); + } + else + { + vnextItem = outbound.settings.vnext.First(); + } + vnextItem.address = node.Address; + vnextItem.port = node.Port; + + UsersItem4Ray usersItem; + if (vnextItem.users.Count <= 0) + { + usersItem = new UsersItem4Ray(); + vnextItem.users.Add(usersItem); + } + else + { + usersItem = vnextItem.users.First(); + } + usersItem.id = node.Id; + usersItem.email = Global.UserEMail; + usersItem.encryption = node.Security; + + if (node.Flow.IsNullOrEmpty()) + { + await GenOutboundMux(node, outbound, muxEnabled, muxEnabled); + } + else + { + usersItem.flow = node.Flow; + await GenOutboundMux(node, outbound, false, muxEnabled); + } + outbound.settings.servers = null; + break; + } + case EConfigType.Trojan: + { + ServersItem4Ray serversItem; + if (outbound.settings.servers.Count <= 0) + { + serversItem = new ServersItem4Ray(); + outbound.settings.servers.Add(serversItem); + } + else + { + serversItem = outbound.settings.servers.First(); + } + serversItem.address = node.Address; + serversItem.port = node.Port; + serversItem.password = node.Id; + + serversItem.ota = false; + serversItem.level = 1; + + await GenOutboundMux(node, outbound); + + outbound.settings.vnext = null; + break; + } + case EConfigType.WireGuard: + { + var peer = new WireguardPeer4Ray + { + publicKey = node.PublicKey, + endpoint = node.Address + ":" + node.Port.ToString() + }; + var setting = new Outboundsettings4Ray + { + address = Utils.String2List(node.RequestHost), + secretKey = node.Id, + reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(), + mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(), + peers = new List { peer } + }; + outbound.settings = setting; + outbound.settings.vnext = null; + outbound.settings.servers = null; + break; + } + } + + outbound.protocol = Global.ProtocolTypes[node.ConfigType]; + await GenBoundStreamSettings(node, outbound); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private async Task GenOutboundMux(ProfileItem node, Outbounds4Ray outbound, bool enabledTCP = false, bool enabledUDP = false) + { + try + { + outbound.mux.enabled = false; + outbound.mux.concurrency = -1; + + if (enabledTCP) + { + outbound.mux.enabled = true; + outbound.mux.concurrency = _config.Mux4RayItem.Concurrency; + } + else if (enabledUDP) + { + outbound.mux.enabled = true; + outbound.mux.xudpConcurrency = _config.Mux4RayItem.XudpConcurrency; + outbound.mux.xudpProxyUDP443 = _config.Mux4RayItem.XudpProxyUDP443; + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } + + private async Task GenBoundStreamSettings(ProfileItem node, Outbounds4Ray outbound) + { + try + { + var streamSettings = outbound.streamSettings; + streamSettings.network = node.GetNetwork(); + var host = node.RequestHost.TrimEx(); + var path = node.Path.TrimEx(); + var sni = node.Sni.TrimEx(); + var useragent = ""; + if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty()) + { + try + { + useragent = Global.UserAgentTexts[_config.CoreBasicItem.DefUserAgent]; + } + catch (KeyNotFoundException) + { + useragent = _config.CoreBasicItem.DefUserAgent; + } + } + + //if tls + if (node.StreamSecurity == Global.StreamSecurity) + { + streamSettings.security = node.StreamSecurity; + + TlsSettings4Ray tlsSettings = new() + { + allowInsecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure), + alpn = node.GetAlpn(), + fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint + }; + if (sni.IsNotEmpty()) + { + tlsSettings.serverName = sni; + } + else if (host.IsNotEmpty()) + { + tlsSettings.serverName = Utils.String2List(host)?.First(); + } + streamSettings.tlsSettings = tlsSettings; + } + + //if Reality + if (node.StreamSecurity == Global.StreamSecurityReality) + { + streamSettings.security = node.StreamSecurity; + + TlsSettings4Ray realitySettings = new() + { + fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint, + serverName = sni, + publicKey = node.PublicKey, + shortId = node.ShortId, + spiderX = node.SpiderX, + mldsa65Verify = node.Mldsa65Verify, + show = false, + }; + + streamSettings.realitySettings = realitySettings; + } + + //streamSettings + switch (node.GetNetwork()) + { + case nameof(ETransport.kcp): + KcpSettings4Ray kcpSettings = new() + { + mtu = _config.KcpItem.Mtu, + tti = _config.KcpItem.Tti + }; + + kcpSettings.uplinkCapacity = _config.KcpItem.UplinkCapacity; + kcpSettings.downlinkCapacity = _config.KcpItem.DownlinkCapacity; + + kcpSettings.congestion = _config.KcpItem.Congestion; + kcpSettings.readBufferSize = _config.KcpItem.ReadBufferSize; + kcpSettings.writeBufferSize = _config.KcpItem.WriteBufferSize; + kcpSettings.header = new Header4Ray + { + type = node.HeaderType, + domain = host.IsNullOrEmpty() ? null : host + }; + if (path.IsNotEmpty()) + { + kcpSettings.seed = path; + } + streamSettings.kcpSettings = kcpSettings; + break; + //ws + case nameof(ETransport.ws): + WsSettings4Ray wsSettings = new(); + wsSettings.headers = new Headers4Ray(); + + if (host.IsNotEmpty()) + { + wsSettings.host = host; + wsSettings.headers.Host = host; + } + if (path.IsNotEmpty()) + { + wsSettings.path = path; + } + if (useragent.IsNotEmpty()) + { + wsSettings.headers.UserAgent = useragent; + } + streamSettings.wsSettings = wsSettings; + + break; + //httpupgrade + case nameof(ETransport.httpupgrade): + HttpupgradeSettings4Ray httpupgradeSettings = new(); + + if (path.IsNotEmpty()) + { + httpupgradeSettings.path = path; + } + if (host.IsNotEmpty()) + { + httpupgradeSettings.host = host; + } + streamSettings.httpupgradeSettings = httpupgradeSettings; + + break; + //xhttp + case nameof(ETransport.xhttp): + streamSettings.network = ETransport.xhttp.ToString(); + XhttpSettings4Ray xhttpSettings = new(); + + if (path.IsNotEmpty()) + { + xhttpSettings.path = path; + } + if (host.IsNotEmpty()) + { + xhttpSettings.host = host; + } + if (node.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(node.HeaderType)) + { + xhttpSettings.mode = node.HeaderType; + } + if (node.Extra.IsNotEmpty()) + { + xhttpSettings.extra = JsonUtils.ParseJson(node.Extra); + } + + streamSettings.xhttpSettings = xhttpSettings; + await GenOutboundMux(node, outbound); + + break; + //h2 + case nameof(ETransport.h2): + HttpSettings4Ray httpSettings = new(); + + if (host.IsNotEmpty()) + { + httpSettings.host = Utils.String2List(host); + } + httpSettings.path = path; + + streamSettings.httpSettings = httpSettings; + + break; + //quic + case nameof(ETransport.quic): + QuicSettings4Ray quicsettings = new() + { + security = host, + key = path, + header = new Header4Ray + { + type = node.HeaderType + } + }; + streamSettings.quicSettings = quicsettings; + if (node.StreamSecurity == Global.StreamSecurity) + { + if (sni.IsNotEmpty()) + { + streamSettings.tlsSettings.serverName = sni; + } + else + { + streamSettings.tlsSettings.serverName = node.Address; + } + } + break; + + case nameof(ETransport.grpc): + GrpcSettings4Ray grpcSettings = new() + { + authority = host.IsNullOrEmpty() ? null : host, + serviceName = path, + multiMode = node.HeaderType == Global.GrpcMultiMode, + idle_timeout = _config.GrpcItem.IdleTimeout, + health_check_timeout = _config.GrpcItem.HealthCheckTimeout, + permit_without_stream = _config.GrpcItem.PermitWithoutStream, + initial_windows_size = _config.GrpcItem.InitialWindowsSize, + }; + streamSettings.grpcSettings = grpcSettings; + break; + + default: + //tcp + if (node.HeaderType == Global.TcpHeaderHttp) + { + TcpSettings4Ray tcpSettings = new() + { + header = new Header4Ray + { + type = node.HeaderType + } + }; + + //request Host + string request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName); + string[] arrHost = host.Split(','); + string host2 = string.Join(",".AppendQuotes(), arrHost); + request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}"); + request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}"); + //Path + string pathHttp = @"/"; + if (path.IsNotEmpty()) + { + string[] arrPath = path.Split(','); + pathHttp = string.Join(",".AppendQuotes(), arrPath); + } + request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}"); + tcpSettings.header.request = JsonUtils.Deserialize(request); + + streamSettings.tcpSettings = tcpSettings; + } + break; + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private async Task GenMoreOutbounds(ProfileItem node, V2rayConfig v2rayConfig) + { + //fragment proxy + if (_config.CoreBasicItem.EnableFragment + && v2rayConfig.outbounds.First().streamSettings?.security.IsNullOrEmpty() == false) + { + var fragmentOutbound = new Outbounds4Ray + { + protocol = "freedom", + tag = $"{Global.ProxyTag}3", + settings = new() + { + fragment = new() + { + packets = _config.Fragment4RayItem?.Packets, + length = _config.Fragment4RayItem?.Length, + interval = _config.Fragment4RayItem?.Interval + } + } + }; + + v2rayConfig.outbounds.Add(fragmentOutbound); + v2rayConfig.outbounds.First().streamSettings.sockopt = new() + { + dialerProxy = fragmentOutbound.tag + }; + return 0; + } + + if (node.Subid.IsNullOrEmpty()) + { + return 0; + } + try + { + var subItem = await AppManager.Instance.GetSubItem(node.Subid); + if (subItem is null) + { + return 0; + } + + //current proxy + var outbound = v2rayConfig.outbounds.First(); + var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); + + //Previous proxy + var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); + string? prevOutboundTag = null; + if (prevNode is not null + && prevNode.ConfigType != EConfigType.Custom + && prevNode.ConfigType != EConfigType.Hysteria2 + && prevNode.ConfigType != EConfigType.TUIC + && prevNode.ConfigType != EConfigType.Anytls) + { + var prevOutbound = JsonUtils.Deserialize(txtOutbound); + await GenOutbound(prevNode, prevOutbound); + prevOutboundTag = $"prev-{Global.ProxyTag}"; + prevOutbound.tag = prevOutboundTag; + v2rayConfig.outbounds.Add(prevOutbound); + } + var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag); + + if (nextOutbound is not null) + { + v2rayConfig.outbounds.Insert(0, nextOutbound); + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + + return 0; + } + + private async Task GenOutboundsList(List nodes, V2rayConfig v2rayConfig) + { + try + { + // Get template and initialize list + var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); + if (txtOutbound.IsNullOrEmpty()) + { + return 0; + } + + var resultOutbounds = new List(); + var prevOutbounds = new List(); // Separate list for prev outbounds and fragment + + // Cache for chain proxies to avoid duplicate generation + var nextProxyCache = new Dictionary(); + var prevProxyTags = new Dictionary(); // Map from profile name to tag + int prevIndex = 0; // Index for prev outbounds + + // Process nodes + int index = 0; + foreach (var node in nodes) + { + index++; + + // Handle proxy chain + string? prevTag = null; + var currentOutbound = JsonUtils.Deserialize(txtOutbound); + var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null); + if (nextOutbound != null) + { + nextOutbound = JsonUtils.DeepCopy(nextOutbound); + } + + var subItem = await AppManager.Instance.GetSubItem(node.Subid); + + // current proxy + await GenOutbound(node, currentOutbound); + currentOutbound.tag = $"{Global.ProxyTag}-{index}"; + + if (!node.Subid.IsNullOrEmpty()) + { + if (prevProxyTags.TryGetValue(node.Subid, out var value)) + { + prevTag = value; // maybe null + } + else + { + var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); + if (prevNode is not null + && prevNode.ConfigType != EConfigType.Custom + && prevNode.ConfigType != EConfigType.Hysteria2 + && prevNode.ConfigType != EConfigType.TUIC + && prevNode.ConfigType != EConfigType.Anytls) + { + var prevOutbound = JsonUtils.Deserialize(txtOutbound); + await GenOutbound(prevNode, prevOutbound); + prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}"; + prevOutbound.tag = prevTag; + prevOutbounds.Add(prevOutbound); + } + prevProxyTags[node.Subid] = prevTag; + } + + nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound); + if (!nextProxyCache.ContainsKey(node.Subid)) + { + nextProxyCache[node.Subid] = nextOutbound; + } + } + + if (nextOutbound is not null) + { + resultOutbounds.Add(nextOutbound); + } + resultOutbounds.Add(currentOutbound); + } + + // Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds + resultOutbounds.AddRange(prevOutbounds); + resultOutbounds.AddRange(v2rayConfig.outbounds); + v2rayConfig.outbounds = resultOutbounds; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + + return 0; + } + + /// + /// Generates a chained outbound configuration for the given subItem and outbound. + /// The outbound's tag must be set before calling this method. + /// Returns the next proxy's outbound configuration, which may be null if no next proxy exists. + /// + /// The subscription item containing proxy chain information. + /// The current outbound configuration. Its tag must be set before calling this method. + /// The tag of the previous outbound in the chain, if any. + /// The outbound for the next proxy in the chain, if already created. If null, will be created inside. + /// + /// The outbound configuration for the next proxy in the chain, or null if no next proxy exists. + /// + private async Task GenChainOutbounds(SubItem subItem, Outbounds4Ray outbound, string? prevOutboundTag, Outbounds4Ray? nextOutbound = null) + { + try + { + var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); + + if (!prevOutboundTag.IsNullOrEmpty()) + { + outbound.streamSettings.sockopt = new() + { + dialerProxy = prevOutboundTag + }; + } + + // Next proxy + var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile); + if (nextNode is not null + && nextNode.ConfigType != EConfigType.Custom + && nextNode.ConfigType != EConfigType.Hysteria2 + && nextNode.ConfigType != EConfigType.TUIC + && nextNode.ConfigType != EConfigType.Anytls) + { + if (nextOutbound == null) + { + nextOutbound = JsonUtils.Deserialize(txtOutbound); + await GenOutbound(nextNode, nextOutbound); + } + nextOutbound.tag = outbound.tag; + + outbound.tag = $"mid-{outbound.tag}"; + nextOutbound.streamSettings.sockopt = new() + { + dialerProxy = outbound.tag + }; + } + return nextOutbound; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return null; + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs new file mode 100644 index 0000000000..fe5d64dc7f --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs @@ -0,0 +1,145 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigV2rayService +{ + private async Task GenRouting(V2rayConfig v2rayConfig) + { + try + { + if (v2rayConfig.routing?.rules != null) + { + v2rayConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy; + + var routing = await ConfigHandler.GetDefaultRouting(_config); + if (routing != null) + { + if (routing.DomainStrategy.IsNotEmpty()) + { + v2rayConfig.routing.domainStrategy = routing.DomainStrategy; + } + var rules = JsonUtils.Deserialize>(routing.RuleSet); + foreach (var item in rules) + { + if (item.Enabled) + { + var item2 = JsonUtils.Deserialize(JsonUtils.Serialize(item)); + await GenRoutingUserRule(item2, v2rayConfig); + } + } + } + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private async Task GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig) + { + try + { + if (rule == null) + { + return 0; + } + rule.outboundTag = await GenRoutingUserRuleOutbound(rule.outboundTag, v2rayConfig); + + if (rule.port.IsNullOrEmpty()) + { + rule.port = null; + } + if (rule.network.IsNullOrEmpty()) + { + rule.network = null; + } + if (rule.domain?.Count == 0) + { + rule.domain = null; + } + if (rule.ip?.Count == 0) + { + rule.ip = null; + } + if (rule.protocol?.Count == 0) + { + rule.protocol = null; + } + if (rule.inboundTag?.Count == 0) + { + rule.inboundTag = null; + } + + var hasDomainIp = false; + if (rule.domain?.Count > 0) + { + var it = JsonUtils.DeepCopy(rule); + it.ip = null; + it.type = "field"; + for (var k = it.domain.Count - 1; k >= 0; k--) + { + if (it.domain[k].StartsWith("#")) + { + it.domain.RemoveAt(k); + } + it.domain[k] = it.domain[k].Replace(Global.RoutingRuleComma, ","); + } + v2rayConfig.routing.rules.Add(it); + hasDomainIp = true; + } + if (rule.ip?.Count > 0) + { + var it = JsonUtils.DeepCopy(rule); + it.domain = null; + it.type = "field"; + v2rayConfig.routing.rules.Add(it); + hasDomainIp = true; + } + if (!hasDomainIp) + { + if (rule.port.IsNotEmpty() + || rule.protocol?.Count > 0 + || rule.inboundTag?.Count > 0 + || rule.network != null + ) + { + var it = JsonUtils.DeepCopy(rule); + it.type = "field"; + v2rayConfig.routing.rules.Add(it); + } + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } + + private async Task GenRoutingUserRuleOutbound(string outboundTag, V2rayConfig v2rayConfig) + { + if (Global.OutboundTags.Contains(outboundTag)) + { + return outboundTag; + } + + var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag); + if (node == null + || node.ConfigType == EConfigType.Custom + || node.ConfigType == EConfigType.Hysteria2 + || node.ConfigType == EConfigType.TUIC + || node.ConfigType == EConfigType.Anytls) + { + return Global.ProxyTag; + } + + var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); + var outbound = JsonUtils.Deserialize(txtOutbound); + await GenOutbound(node, outbound); + outbound.tag = Global.ProxyTag + node.IndexId.ToString(); + v2rayConfig.outbounds.Add(outbound); + + return outbound.tag; + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs new file mode 100644 index 0000000000..1269a11fca --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs @@ -0,0 +1,51 @@ +namespace ServiceLib.Services.CoreConfig; + +public partial class CoreConfigV2rayService +{ + private async Task GenStatistic(V2rayConfig v2rayConfig) + { + if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed) + { + string tag = EInboundProtocol.api.ToString(); + Metrics4Ray apiObj = new(); + Policy4Ray policyObj = new(); + SystemPolicy4Ray policySystemSetting = new(); + + v2rayConfig.stats = new Stats4Ray(); + + apiObj.tag = tag; + v2rayConfig.metrics = apiObj; + + policySystemSetting.statsOutboundDownlink = true; + policySystemSetting.statsOutboundUplink = true; + policyObj.system = policySystemSetting; + v2rayConfig.policy = policyObj; + + if (!v2rayConfig.inbounds.Exists(item => item.tag == tag)) + { + Inbounds4Ray apiInbound = new(); + Inboundsettings4Ray apiInboundSettings = new(); + apiInbound.tag = tag; + apiInbound.listen = Global.Loopback; + apiInbound.port = AppManager.Instance.StatePort; + apiInbound.protocol = Global.InboundAPIProtocol; + apiInboundSettings.address = Global.Loopback; + apiInbound.settings = apiInboundSettings; + v2rayConfig.inbounds.Add(apiInbound); + } + + if (!v2rayConfig.routing.rules.Exists(item => item.outboundTag == tag)) + { + RulesItem4Ray apiRoutingRule = new() + { + inboundTag = new List { tag }, + outboundTag = tag, + type = "field" + }; + + v2rayConfig.routing.rules.Add(apiRoutingRule); + } + } + return await Task.FromResult(0); + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Services/DownloadService.cs b/v2rayn/v2rayN/ServiceLib/Services/DownloadService.cs index 51dee97a7d..fe17d52369 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/DownloadService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/DownloadService.cs @@ -20,7 +20,7 @@ public class DownloadService { try { - SetSecurityProtocol(AppHandler.Instance.Config.GuiItem.EnableSecurityProtocolTls13); + SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13); var progress = new Progress(); progress.ProgressChanged += (sender, value) => updateFunc?.Invoke(false, $"{value}"); @@ -45,7 +45,7 @@ public class DownloadService { try { - SetSecurityProtocol(AppHandler.Instance.Config.GuiItem.EnableSecurityProtocolTls13); + SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13); UpdateCompleted?.Invoke(this, new RetResult(false, $"{ResUI.Downloading} {url}")); var progress = new Progress(); @@ -72,7 +72,7 @@ public class DownloadService public async Task UrlRedirectAsync(string url, bool blProxy) { - SetSecurityProtocol(AppHandler.Instance.Config.GuiItem.EnableSecurityProtocolTls13); + SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13); var webRequestHandler = new SocketsHttpHandler { AllowAutoRedirect = false, @@ -142,7 +142,7 @@ public class DownloadService { try { - SetSecurityProtocol(AppHandler.Instance.Config.GuiItem.EnableSecurityProtocolTls13); + SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13); var webProxy = await GetWebProxy(blProxy); var client = new HttpClient(new SocketsHttpHandler() { @@ -187,7 +187,7 @@ public class DownloadService { try { - SetSecurityProtocol(AppHandler.Instance.Config.GuiItem.EnableSecurityProtocolTls13); + SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13); var webProxy = await GetWebProxy(blProxy); @@ -210,70 +210,13 @@ public class DownloadService return null; } - public async Task RunAvailabilityCheck(IWebProxy? webProxy) - { - var responseTime = -1; - try - { - webProxy ??= await GetWebProxy(true); - var config = AppHandler.Instance.Config; - - for (var i = 0; i < 2; i++) - { - responseTime = await GetRealPingTime(config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10); - if (responseTime > 0) - { - break; - } - await Task.Delay(500); - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - return -1; - } - return responseTime; - } - - public async Task GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout) - { - var responseTime = -1; - try - { - using var cts = new CancellationTokenSource(); - cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout)); - using var client = new HttpClient(new SocketsHttpHandler() - { - Proxy = webProxy, - UseProxy = webProxy != null - }); - - List oneTime = new(); - for (var i = 0; i < 2; i++) - { - var timer = Stopwatch.StartNew(); - await client.GetAsync(url, cts.Token).ConfigureAwait(false); - timer.Stop(); - oneTime.Add((int)timer.Elapsed.TotalMilliseconds); - await Task.Delay(100); - } - responseTime = oneTime.Where(x => x > 0).OrderBy(x => x).FirstOrDefault(); - } - catch //(Exception ex) - { - //Utile.SaveLog(ex.Message, ex); - } - return responseTime; - } - private async Task GetWebProxy(bool blProxy) { if (!blProxy) { return null; } - var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks); + var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks); if (await SocketCheck(Global.Loopback, port) == false) { return null; diff --git a/v2rayn/v2rayN/ServiceLib/Services/SpeedtestService.cs b/v2rayn/v2rayN/ServiceLib/Services/SpeedtestService.cs index 9c97a21764..67b9bc94f2 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/SpeedtestService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/SpeedtestService.cs @@ -23,7 +23,7 @@ public class SpeedtestService Task.Run(async () => { await RunAsync(actionType, selecteds); - await ProfileExHandler.Instance.SaveTo(); + await ProfileExManager.Instance.SaveTo(); UpdateFunc("", ResUI.SpeedtestingCompleted); }); } @@ -98,18 +98,18 @@ public class SpeedtestService case ESpeedActionType.Tcping: case ESpeedActionType.Realping: UpdateFunc(it.IndexId, ResUI.Speedtesting, ""); - ProfileExHandler.Instance.SetTestDelay(it.IndexId, 0); + ProfileExManager.Instance.SetTestDelay(it.IndexId, 0); break; case ESpeedActionType.Speedtest: UpdateFunc(it.IndexId, "", ResUI.SpeedtestingWait); - ProfileExHandler.Instance.SetTestSpeed(it.IndexId, 0); + ProfileExManager.Instance.SetTestSpeed(it.IndexId, 0); break; case ESpeedActionType.Mixedtest: UpdateFunc(it.IndexId, ResUI.Speedtesting, ResUI.SpeedtestingWait); - ProfileExHandler.Instance.SetTestDelay(it.IndexId, 0); - ProfileExHandler.Instance.SetTestSpeed(it.IndexId, 0); + ProfileExManager.Instance.SetTestDelay(it.IndexId, 0); + ProfileExManager.Instance.SetTestSpeed(it.IndexId, 0); break; } } @@ -132,7 +132,7 @@ public class SpeedtestService { var responseTime = await GetTcpingTime(it.Address, it.Port); - ProfileExHandler.Instance.SetTestDelay(it.IndexId, responseTime); + ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime); UpdateFunc(it.IndexId, responseTime.ToString()); } catch (Exception ex) @@ -191,15 +191,13 @@ public class SpeedtestService var pid = -1; try { - pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds); + pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(selecteds); if (pid < 0) { return false; } await Task.Delay(1000); - var downloadHandle = new DownloadService(); - List tasks = new(); foreach (var it in selecteds) { @@ -213,7 +211,7 @@ public class SpeedtestService } tasks.Add(Task.Run(async () => { - await DoRealPing(downloadHandle, it); + await DoRealPing(it); })); } await Task.WhenAll(tasks); @@ -255,7 +253,7 @@ public class SpeedtestService var pid = -1; try { - pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(it); + pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(it); if (pid < 0) { UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore); @@ -263,7 +261,7 @@ public class SpeedtestService else { await Task.Delay(1000); - var delay = await DoRealPing(downloadHandle, it); + var delay = await DoRealPing(it); if (blSpeedTest) { if (delay > 0) @@ -294,12 +292,12 @@ public class SpeedtestService await Task.WhenAll(tasks); } - private async Task DoRealPing(DownloadService downloadHandle, ServerTestItem it) + private async Task DoRealPing(ServerTestItem it) { var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}"); - var responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10); + var responseTime = await HttpClientHelper.Instance.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10); - ProfileExHandler.Instance.SetTestDelay(it.IndexId, responseTime); + ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime); UpdateFunc(it.IndexId, responseTime.ToString()); return responseTime; } @@ -316,7 +314,7 @@ public class SpeedtestService decimal.TryParse(msg, out var dec); if (dec > 0) { - ProfileExHandler.Instance.SetTestSpeed(it.IndexId, dec); + ProfileExManager.Instance.SetTestSpeed(it.IndexId, dec); } UpdateFunc(it.IndexId, "", msg); }); @@ -378,7 +376,7 @@ public class SpeedtestService _updateFunc?.Invoke(new() { IndexId = indexId, Delay = delay, Speed = speed }); if (indexId.IsNotEmpty() && speed.IsNotEmpty()) { - ProfileExHandler.Instance.SetTestMessage(indexId, speed); + ProfileExManager.Instance.SetTestMessage(indexId, speed); } } } diff --git a/v2rayn/v2rayN/ServiceLib/Services/Statistics/StatisticsSingboxService.cs b/v2rayn/v2rayN/ServiceLib/Services/Statistics/StatisticsSingboxService.cs index f419f831b6..82a5330d94 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/Statistics/StatisticsSingboxService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/Statistics/StatisticsSingboxService.cs @@ -9,7 +9,7 @@ public class StatisticsSingboxService private bool _exitFlag; private ClientWebSocket? webSocket; private Action? _updateFunc; - private string Url => $"ws://{Global.Loopback}:{AppHandler.Instance.StatePort2}/traffic"; + private string Url => $"ws://{Global.Loopback}:{AppManager.Instance.StatePort2}/traffic"; private static readonly string _tag = "StatisticsSingboxService"; public StatisticsSingboxService(Config config, Action updateFunc) diff --git a/v2rayn/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs b/v2rayn/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs index 743114c6a1..d09ae83631 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs @@ -7,7 +7,7 @@ public class StatisticsXrayService private readonly Config _config; private bool _exitFlag; private Action? _updateFunc; - private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars"; + private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort}/debug/vars"; public StatisticsXrayService(Config config, Action updateFunc) { diff --git a/v2rayn/v2rayN/ServiceLib/Services/UpdateService.cs b/v2rayn/v2rayN/ServiceLib/Services/UpdateService.cs index b60ddc6cc3..e87522e232 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/UpdateService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/UpdateService.cs @@ -135,7 +135,7 @@ public class UpdateService private async Task GetRemoteVersion(DownloadService downloadHandle, ECoreType type, bool preRelease) { - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type); + var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type); var tagName = string.Empty; if (preRelease) { @@ -169,7 +169,7 @@ public class UpdateService { try { - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type); + var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type); string filePath = string.Empty; foreach (var name in coreInfo.CoreExes) { @@ -221,7 +221,7 @@ public class UpdateService { try { - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type); + var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type); var coreUrl = await GetUrlFromCore(coreInfo) ?? string.Empty; SemanticVersion curVersion; string message; @@ -379,7 +379,7 @@ public class UpdateService var geoSiteFiles = new List(); //Collect used files list - var routingItems = await AppHandler.Instance.RoutingItems(); + var routingItems = await AppManager.Instance.RoutingItems(); foreach (var routing in routingItems) { var rules = JsonUtils.Deserialize>(routing.RuleSet); diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/AddServer2ViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/AddServer2ViewModel.cs index 46c5298c88..a94ecf74e2 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/AddServer2ViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/AddServer2ViewModel.cs @@ -19,7 +19,7 @@ public class AddServer2ViewModel : MyReactiveObject public AddServer2ViewModel(ProfileItem profileItem, Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; BrowseServerCmd = ReactiveCommand.CreateFromTask(async () => @@ -45,25 +45,25 @@ public class AddServer2ViewModel : MyReactiveObject var remarks = SelectedSource.Remarks; if (remarks.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks); + NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks); return; } if (SelectedSource.Address.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.FillServerAddressCustom); + NoticeManager.Instance.Enqueue(ResUI.FillServerAddressCustom); return; } SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType); if (await ConfigHandler.EditCustomServer(_config, SelectedSource) == 0) { - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); _updateView?.Invoke(EViewAction.CloseWindow, null); } else { - NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); + NoticeManager.Instance.Enqueue(ResUI.OperationFailed); } } @@ -74,12 +74,12 @@ public class AddServer2ViewModel : MyReactiveObject return; } - var item = await AppHandler.Instance.GetProfileItem(SelectedSource.IndexId); + var item = await AppManager.Instance.GetProfileItem(SelectedSource.IndexId); item ??= SelectedSource; item.Address = fileName; if (await ConfigHandler.AddCustomServer(_config, item, false) == 0) { - NoticeHandler.Instance.Enqueue(ResUI.SuccessfullyImportedCustomServer); + NoticeManager.Instance.Enqueue(ResUI.SuccessfullyImportedCustomServer); if (item.IndexId.IsNotEmpty()) { SelectedSource = JsonUtils.DeepCopy(item); @@ -88,7 +88,7 @@ public class AddServer2ViewModel : MyReactiveObject } else { - NoticeHandler.Instance.Enqueue(ResUI.FailedImportedCustomServer); + NoticeManager.Instance.Enqueue(ResUI.FailedImportedCustomServer); } } @@ -97,7 +97,7 @@ public class AddServer2ViewModel : MyReactiveObject var address = SelectedSource.Address; if (address.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.FillServerAddressCustom); + NoticeManager.Instance.Enqueue(ResUI.FillServerAddressCustom); return; } @@ -108,7 +108,7 @@ public class AddServer2ViewModel : MyReactiveObject } else { - NoticeHandler.Instance.Enqueue(ResUI.FailedReadConfiguration); + NoticeManager.Instance.Enqueue(ResUI.FailedReadConfiguration); } await Task.CompletedTask; } diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index c268ca4221..cd2399bce5 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -16,7 +16,7 @@ public class AddServerViewModel : MyReactiveObject public AddServerViewModel(ProfileItem profileItem, Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; SaveCmd = ReactiveCommand.CreateFromTask(async () => @@ -43,32 +43,32 @@ public class AddServerViewModel : MyReactiveObject { if (SelectedSource.Remarks.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks); + NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks); return; } if (SelectedSource.Address.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.FillServerAddress); + NoticeManager.Instance.Enqueue(ResUI.FillServerAddress); return; } var port = SelectedSource.Port.ToString(); if (port.IsNullOrEmpty() || !Utils.IsNumeric(port) || SelectedSource.Port <= 0 || SelectedSource.Port >= Global.MaxPort) { - NoticeHandler.Instance.Enqueue(ResUI.FillCorrectServerPort); + NoticeManager.Instance.Enqueue(ResUI.FillCorrectServerPort); return; } if (SelectedSource.ConfigType == EConfigType.Shadowsocks) { if (SelectedSource.Id.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.FillPassword); + NoticeManager.Instance.Enqueue(ResUI.FillPassword); return; } if (SelectedSource.Security.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectEncryption); + NoticeManager.Instance.Enqueue(ResUI.PleaseSelectEncryption); return; } } @@ -76,7 +76,7 @@ public class AddServerViewModel : MyReactiveObject { if (SelectedSource.Id.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.FillUUID); + NoticeManager.Instance.Enqueue(ResUI.FillUUID); return; } } @@ -84,12 +84,12 @@ public class AddServerViewModel : MyReactiveObject if (await ConfigHandler.AddServer(_config, SelectedSource) == 0) { - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); _updateView?.Invoke(EViewAction.CloseWindow, null); } else { - NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); + NoticeManager.Instance.Enqueue(ResUI.OperationFailed); } } } diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/BackupAndRestoreViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/BackupAndRestoreViewModel.cs index 82fd80d77d..e258d3b22d 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/BackupAndRestoreViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/BackupAndRestoreViewModel.cs @@ -22,7 +22,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject public BackupAndRestoreViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; WebDavCheckCmd = ReactiveCommand.CreateFromTask(async () => @@ -52,14 +52,14 @@ public class BackupAndRestoreViewModel : MyReactiveObject _config.WebDavItem = SelectedSource; _ = await ConfigHandler.SaveConfig(_config); - var result = await WebDavHandler.Instance.CheckConnection(); + var result = await WebDavManager.Instance.CheckConnection(); if (result) { DisplayOperationMsg(ResUI.OperationSuccess); } else { - DisplayOperationMsg(WebDavHandler.Instance.GetLastError()); + DisplayOperationMsg(WebDavManager.Instance.GetLastError()); } } @@ -70,7 +70,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject var result = await CreateZipFileFromDirectory(fileName); if (result) { - var result2 = await WebDavHandler.Instance.PutFile(fileName); + var result2 = await WebDavManager.Instance.PutFile(fileName); if (result2) { DisplayOperationMsg(ResUI.OperationSuccess); @@ -78,21 +78,21 @@ public class BackupAndRestoreViewModel : MyReactiveObject } } - DisplayOperationMsg(WebDavHandler.Instance.GetLastError()); + DisplayOperationMsg(WebDavManager.Instance.GetLastError()); } private async Task RemoteRestore() { DisplayOperationMsg(); var fileName = Utils.GetTempPath(Utils.GetGuid()); - var result = await WebDavHandler.Instance.GetRawFile(fileName); + var result = await WebDavManager.Instance.GetRawFile(fileName); if (result) { await LocalRestore(fileName); return; } - DisplayOperationMsg(WebDavHandler.Instance.GetLastError()); + DisplayOperationMsg(WebDavManager.Instance.GetLastError()); } public async Task LocalBackup(string fileName) @@ -105,7 +105,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject } else { - DisplayOperationMsg(WebDavHandler.Instance.GetLastError()); + DisplayOperationMsg(WebDavManager.Instance.GetLastError()); } return result; @@ -158,7 +158,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject } else { - DisplayOperationMsg(WebDavHandler.Instance.GetLastError()); + DisplayOperationMsg(WebDavManager.Instance.GetLastError()); } } diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs index f3247798e9..642149f9d5 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs @@ -21,7 +21,7 @@ public class CheckUpdateViewModel : MyReactiveObject public CheckUpdateViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; CheckUpdateCmd = ReactiveCommand.CreateFromTask(async () => @@ -75,10 +75,7 @@ public class CheckUpdateViewModel : MyReactiveObject private async Task CheckUpdate() { - await Task.Run(async () => - { - await CheckUpdateTask(); - }); + await Task.Run(CheckUpdateTask); } private async Task CheckUpdateTask() diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/ClashConnectionsViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/ClashConnectionsViewModel.cs index a042da5116..222e1a8d48 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/ClashConnectionsViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/ClashConnectionsViewModel.cs @@ -26,7 +26,7 @@ public class ClashConnectionsViewModel : MyReactiveObject public ClashConnectionsViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh; @@ -58,7 +58,7 @@ public class ClashConnectionsViewModel : MyReactiveObject private async Task GetClashConnections() { - var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync(); + var ret = await ClashApiManager.Instance.GetClashConnectionsAsync(); if (ret == null) { return; @@ -118,7 +118,7 @@ public class ClashConnectionsViewModel : MyReactiveObject { _connectionItems.Clear(); } - await ClashApiHandler.Instance.ClashConnectionClose(id); + await ClashApiManager.Instance.ClashConnectionClose(id); await GetClashConnections(); } diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs index 5356672686..b3a668925d 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs @@ -43,7 +43,7 @@ public class ClashProxiesViewModel : MyReactiveObject public ClashProxiesViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; ProxiesReloadCmd = ReactiveCommand.CreateFromTask(async () => @@ -152,13 +152,13 @@ public class ClashProxiesViewModel : MyReactiveObject { { "mode", mode.ToString().ToLower() } }; - await ClashApiHandler.Instance.ClashConfigUpdate(headers); + await ClashApiManager.Instance.ClashConfigUpdate(headers); } } private async Task GetClashProxies(bool refreshUI) { - var ret = await ClashApiHandler.Instance.GetClashProxiesAsync(); + var ret = await ClashApiManager.Instance.GetClashProxiesAsync(); if (ret?.Item1 == null || ret.Item2 == null) { return; @@ -182,7 +182,7 @@ public class ClashProxiesViewModel : MyReactiveObject var selectedName = SelectedGroup?.Name; _proxyGroups.Clear(); - var proxyGroups = ClashApiHandler.Instance.GetClashProxyGroups(); + var proxyGroups = ClashApiManager.Instance.GetClashProxyGroups(); if (proxyGroups != null && proxyGroups.Count > 0) { foreach (var it in proxyGroups) @@ -352,11 +352,11 @@ public class ClashProxiesViewModel : MyReactiveObject var selectedProxy = TryGetProxy(name); if (selectedProxy == null || selectedProxy.type != "Selector") { - NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); + NoticeManager.Instance.Enqueue(ResUI.OperationFailed); return; } - await ClashApiHandler.Instance.ClashSetActiveProxy(name, nameNode); + await ClashApiManager.Instance.ClashSetActiveProxy(name, nameNode); selectedProxy.now = nameNode; var group = _proxyGroups.FirstOrDefault(it => it.Name == SelectedGroup.Name); @@ -368,12 +368,12 @@ public class ClashProxiesViewModel : MyReactiveObject SelectedGroup = group2; } - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); } private async Task ProxiesDelayTest(bool blAll = true) { - ClashApiHandler.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), (item, result) => + ClashApiManager.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), (item, result) => { if (item == null || result.IsNullOrEmpty()) { diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs index a679b8c170..9ca2d40775 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs @@ -38,7 +38,7 @@ public class DNSSettingViewModel : MyReactiveObject public DNSSettingViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; SaveCmd = ReactiveCommand.CreateFromTask(SaveSettingAsync); @@ -60,7 +60,7 @@ public class DNSSettingViewModel : MyReactiveObject private async Task Init() { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; var item = _config.SimpleDNSItem; UseSystemHosts = item.UseSystemHosts; AddCommonHosts = item.AddCommonHosts; @@ -76,14 +76,14 @@ public class DNSSettingViewModel : MyReactiveObject Hosts = item.Hosts; DirectExpectedIPs = item.DirectExpectedIPs; - var item1 = await AppHandler.Instance.GetDNSItem(ECoreType.Xray); + var item1 = await AppManager.Instance.GetDNSItem(ECoreType.Xray); RayCustomDNSEnableCompatible = item1.Enabled; UseSystemHostsCompatible = item1.UseSystemHosts; DomainStrategy4FreedomCompatible = item1?.DomainStrategy4Freedom ?? string.Empty; DomainDNSAddressCompatible = item1?.DomainDNSAddress ?? string.Empty; NormalDNSCompatible = item1?.NormalDNS ?? string.Empty; - var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); + var item2 = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); SBCustomDNSEnableCompatible = item2.Enabled; DomainStrategy4Freedom2Compatible = item2?.DomainStrategy4Freedom ?? string.Empty; DomainDNSAddress2Compatible = item2?.DomainDNSAddress ?? string.Empty; @@ -117,7 +117,7 @@ public class DNSSettingViewModel : MyReactiveObject { if (NormalDNSCompatible.Contains('{') || NormalDNSCompatible.Contains('}')) { - NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText); + NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText); return; } } @@ -127,7 +127,7 @@ public class DNSSettingViewModel : MyReactiveObject var obj2 = JsonUtils.Deserialize(NormalDNS2Compatible); if (obj2 == null) { - NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText); + NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText); return; } } @@ -136,12 +136,12 @@ public class DNSSettingViewModel : MyReactiveObject var obj2 = JsonUtils.Deserialize(TunDNS2Compatible); if (obj2 == null) { - NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText); + NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText); return; } } - var item1 = await AppHandler.Instance.GetDNSItem(ECoreType.Xray); + var item1 = await AppManager.Instance.GetDNSItem(ECoreType.Xray); item1.Enabled = RayCustomDNSEnableCompatible; item1.DomainStrategy4Freedom = DomainStrategy4FreedomCompatible; item1.DomainDNSAddress = DomainDNSAddressCompatible; @@ -149,7 +149,7 @@ public class DNSSettingViewModel : MyReactiveObject item1.NormalDNS = NormalDNSCompatible; await ConfigHandler.SaveDNSItems(_config, item1); - var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); + var item2 = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); item2.Enabled = SBCustomDNSEnableCompatible; item2.DomainStrategy4Freedom = DomainStrategy4Freedom2Compatible; item2.DomainDNSAddress = DomainDNSAddress2Compatible; diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/FullConfigTemplateViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/FullConfigTemplateViewModel.cs index f36a7fb603..3619ddef2f 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/FullConfigTemplateViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/FullConfigTemplateViewModel.cs @@ -41,7 +41,7 @@ public class FullConfigTemplateViewModel : MyReactiveObject public FullConfigTemplateViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; SaveCmd = ReactiveCommand.CreateFromTask(async () => { @@ -53,13 +53,13 @@ public class FullConfigTemplateViewModel : MyReactiveObject private async Task Init() { - var item = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray); + var item = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray); EnableFullConfigTemplate4Ray = item?.Enabled ?? false; FullConfigTemplate4Ray = item?.Config ?? string.Empty; AddProxyOnly4Ray = item?.AddProxyOnly ?? false; ProxyDetour4Ray = item?.ProxyDetour ?? string.Empty; - var item2 = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box); + var item2 = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.sing_box); EnableFullConfigTemplate4Singbox = item2?.Enabled ?? false; FullConfigTemplate4Singbox = item2?.Config ?? string.Empty; FullTunConfigTemplate4Singbox = item2?.TunConfig ?? string.Empty; @@ -75,13 +75,13 @@ public class FullConfigTemplateViewModel : MyReactiveObject if (!await SaveSingboxConfigAsync()) return; - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); _ = _updateView?.Invoke(EViewAction.CloseWindow, null); } private async Task SaveXrayConfigAsync() { - var item = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray); + var item = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray); item.Enabled = EnableFullConfigTemplate4Ray; item.Config = null; @@ -96,7 +96,7 @@ public class FullConfigTemplateViewModel : MyReactiveObject private async Task SaveSingboxConfigAsync() { - var item = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box); + var item = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.sing_box); item.Enabled = EnableFullConfigTemplate4Singbox; item.Config = null; item.TunConfig = null; diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/GlobalHotkeySettingViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/GlobalHotkeySettingViewModel.cs index ba3293c9dc..0acb8726c4 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/GlobalHotkeySettingViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/GlobalHotkeySettingViewModel.cs @@ -11,7 +11,7 @@ public class GlobalHotkeySettingViewModel : MyReactiveObject public GlobalHotkeySettingViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; _globalHotkeys = JsonUtils.DeepCopy(_config.GlobalHotkeys); @@ -58,7 +58,7 @@ public class GlobalHotkeySettingViewModel : MyReactiveObject } else { - NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); + NoticeManager.Instance.Enqueue(ResUI.OperationFailed); } } } diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index 8578036d7a..e04f62309e 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -71,7 +71,7 @@ public class MainWindowViewModel : MyReactiveObject public MainWindowViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; #region WhenAnyValue && ReactiveCommand @@ -178,7 +178,7 @@ public class MainWindowViewModel : MyReactiveObject { if (await _updateView?.Invoke(EViewAction.GlobalHotkeySettingWindow, null) == true) { - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); } }); RebootAsAdminCmd = ReactiveCommand.CreateFromTask(async () => @@ -226,13 +226,13 @@ public class MainWindowViewModel : MyReactiveObject await ConfigHandler.InitBuiltinRouting(_config); await ConfigHandler.InitBuiltinDNS(_config); await ConfigHandler.InitBuiltinFullConfigTemplate(_config); - await ProfileExHandler.Instance.Init(); - await CoreHandler.Instance.Init(_config, UpdateHandler); - TaskHandler.Instance.RegUpdateTask(_config, UpdateTaskHandler); + await ProfileExManager.Instance.Init(); + await CoreManager.Instance.Init(_config, UpdateHandler); + TaskManager.Instance.RegUpdateTask(_config, UpdateTaskHandler); if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed) { - await StatisticsHandler.Instance.Init(_config, UpdateStatisticsHandler); + await StatisticsManager.Instance.Init(_config, UpdateStatisticsHandler); } BlReloadEnabled = true; @@ -247,16 +247,16 @@ public class MainWindowViewModel : MyReactiveObject private void UpdateHandler(bool notify, string msg) { - NoticeHandler.Instance.SendMessage(msg); + NoticeManager.Instance.SendMessage(msg); if (notify) { - NoticeHandler.Instance.Enqueue(msg); + NoticeManager.Instance.Enqueue(msg); } } private void UpdateTaskHandler(bool success, string msg) { - NoticeHandler.Instance.SendMessageEx(msg); + NoticeManager.Instance.SendMessageEx(msg); if (success) { var indexIdOld = _config.IndexId; @@ -303,10 +303,10 @@ public class MainWindowViewModel : MyReactiveObject MessageBus.Current.SendMessage("", EMsgCommand.AppExit.ToString()); await ConfigHandler.SaveConfig(_config); - await ProfileExHandler.Instance.SaveTo(); - await StatisticsHandler.Instance.SaveTo(); - await CoreHandler.Instance.CoreStop(); - StatisticsHandler.Instance.Close(); + await ProfileExManager.Instance.SaveTo(); + await StatisticsManager.Instance.SaveTo(); + await CoreManager.Instance.CoreStop(); + StatisticsManager.Instance.Close(); Logging.SaveLog("MyAppExitAsync End"); } @@ -324,7 +324,7 @@ public class MainWindowViewModel : MyReactiveObject { if (!Utils.UpgradeAppExists(out var upgradeFileName)) { - NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.UpgradeAppNotExistTip); + NoticeManager.Instance.SendMessageAndEnqueue(ResUI.UpgradeAppNotExistTip); Logging.SaveLog("UpgradeApp does not exist"); return; } @@ -404,11 +404,11 @@ public class MainWindowViewModel : MyReactiveObject { RefreshSubscriptions(); RefreshServers(); - NoticeHandler.Instance.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret)); + NoticeManager.Instance.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret)); } else { - NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); + NoticeManager.Instance.Enqueue(ResUI.OperationFailed); } } @@ -420,7 +420,7 @@ public class MainWindowViewModel : MyReactiveObject public async Task ScanScreenResult(byte[]? bytes) { - var result = QRCodeHelper.ParseBarcode(bytes); + var result = QRCodeUtils.ParseBarcode(bytes); await AddScanResultAsync(result); } @@ -437,7 +437,7 @@ public class MainWindowViewModel : MyReactiveObject return; } - var result = QRCodeHelper.ParseBarcode(fileName); + var result = QRCodeUtils.ParseBarcode(fileName); await AddScanResultAsync(result); } @@ -445,7 +445,7 @@ public class MainWindowViewModel : MyReactiveObject { if (result.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.NoValidQRcodeFound); + NoticeManager.Instance.Enqueue(ResUI.NoValidQRcodeFound); } else { @@ -454,11 +454,11 @@ public class MainWindowViewModel : MyReactiveObject { RefreshSubscriptions(); RefreshServers(); - NoticeHandler.Instance.Enqueue(ResUI.SuccessfullyImportedServerViaScan); + NoticeManager.Instance.Enqueue(ResUI.SuccessfullyImportedServerViaScan); } else { - NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); + NoticeManager.Instance.Enqueue(ResUI.OperationFailed); } } } @@ -531,7 +531,7 @@ public class MainWindowViewModel : MyReactiveObject private async Task ClearServerStatistics() { - await StatisticsHandler.Instance.ClearAllServerStatistics(); + await StatisticsManager.Instance.ClearAllServerStatistics(); RefreshServers(); } @@ -602,13 +602,13 @@ public class MainWindowViewModel : MyReactiveObject private async Task LoadCore() { var node = await ConfigHandler.GetDefaultServer(_config); - await CoreHandler.Instance.LoadCore(node); + await CoreManager.Instance.LoadCore(node); } public async Task CloseCore() { await ConfigHandler.SaveConfig(_config); - await CoreHandler.Instance.CoreStop(); + await CoreManager.Instance.CoreStop(); } private async Task AutoHideStartup() diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs index 80c19ebd79..4d13d00777 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs @@ -20,7 +20,7 @@ public class MsgViewModel : MyReactiveObject public MsgViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; MsgFilter = _config.MsgUIItem.MainMsgFilter ?? string.Empty; AutoRefresh = _config.MsgUIItem.AutoRefresh ?? true; diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index b45eb30c72..7f446cf237 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -109,7 +109,7 @@ public class OptionSettingViewModel : MyReactiveObject public OptionSettingViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; SaveCmd = ReactiveCommand.CreateFromTask(async () => @@ -276,7 +276,7 @@ public class OptionSettingViewModel : MyReactiveObject if (localPort.ToString().IsNullOrEmpty() || !Utils.IsNumeric(localPort.ToString()) || localPort <= 0 || localPort >= Global.MaxPort) { - NoticeHandler.Instance.Enqueue(ResUI.FillLocalListeningPort); + NoticeManager.Instance.Enqueue(ResUI.FillLocalListeningPort); return; } var needReboot = (EnableStatistics != _config.GuiItem.EnableStatistics @@ -369,14 +369,14 @@ public class OptionSettingViewModel : MyReactiveObject if (await ConfigHandler.SaveConfig(_config) == 0) { await AutoStartupHandler.UpdateTask(_config); - AppHandler.Instance.Reset(); + AppManager.Instance.Reset(); - NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess); _updateView?.Invoke(EViewAction.CloseWindow, null); } else { - NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); + NoticeManager.Instance.Enqueue(ResUI.OperationFailed); } } diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs index f312b8907c..8362ecea5f 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs @@ -100,7 +100,7 @@ public class ProfilesViewModel : MyReactiveObject public ProfilesViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; #region WhenAnyValue && ReactiveCommand @@ -284,8 +284,8 @@ public class ProfilesViewModel : MyReactiveObject { if (result.IndexId.IsNullOrEmpty()) { - NoticeHandler.Instance.SendMessageEx(result.Delay); - NoticeHandler.Instance.Enqueue(result.Delay); + NoticeManager.Instance.SendMessageEx(result.Delay); + NoticeManager.Instance.Enqueue(result.Delay); return; } var item = _profileItems.FirstOrDefault(it => it.IndexId == result.IndexId); @@ -403,7 +403,7 @@ public class ProfilesViewModel : MyReactiveObject _subItems.Add(new SubItem { Remarks = ResUI.AllGroupServers }); - foreach (var item in await AppHandler.Instance.SubItems()) + foreach (var item in await AppManager.Instance.SubItems()) { _subItems.Add(item); } @@ -419,12 +419,12 @@ public class ProfilesViewModel : MyReactiveObject private async Task?> GetProfileItemsEx(string subid, string filter) { - var lstModel = await AppHandler.Instance.ProfileItems(_config.SubIndexId, filter); + var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, filter); await ConfigHandler.SetDefaultServer(_config, lstModel); - var lstServerStat = (_config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? []; - var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs(); + var lstServerStat = (_config.GuiItem.EnableStatistics ? StatisticsManager.Instance.ServerStat : null) ?? []; + var lstProfileExs = await ProfileExManager.Instance.GetProfileExs(); lstModel = (from t in lstModel join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b from t22 in t2b.DefaultIfEmpty() @@ -474,7 +474,7 @@ public class ProfilesViewModel : MyReactiveObject { foreach (var profile in orderProfiles) { - var item = await AppHandler.Instance.GetProfileItem(profile.IndexId); + var item = await AppManager.Instance.GetProfileItem(profile.IndexId); if (item is not null) { lstSelected.Add(item); @@ -495,10 +495,10 @@ public class ProfilesViewModel : MyReactiveObject { return; } - var item = await AppHandler.Instance.GetProfileItem(SelectedProfile.IndexId); + var item = await AppManager.Instance.GetProfileItem(SelectedProfile.IndexId); if (item is null) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectServer); + NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer); return; } eConfigType = item.ConfigType; @@ -536,7 +536,7 @@ public class ProfilesViewModel : MyReactiveObject var exists = lstSelected.Exists(t => t.IndexId == _config.IndexId); await ConfigHandler.RemoveServers(_config, lstSelected); - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); if (lstSelected.Count == _profileItems.Count) { _profileItems.Clear(); @@ -556,7 +556,7 @@ public class ProfilesViewModel : MyReactiveObject RefreshServers(); Reload(); } - NoticeHandler.Instance.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2)); + NoticeManager.Instance.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2)); } private async Task CopyServer() @@ -569,7 +569,7 @@ public class ProfilesViewModel : MyReactiveObject if (await ConfigHandler.CopyServer(_config, lstSelected) == 0) { RefreshServers(); - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); } } @@ -592,10 +592,10 @@ public class ProfilesViewModel : MyReactiveObject { return; } - var item = await AppHandler.Instance.GetProfileItem(indexId); + var item = await AppManager.Instance.GetProfileItem(indexId); if (item is null) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectServer); + NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer); return; } @@ -621,10 +621,10 @@ public class ProfilesViewModel : MyReactiveObject public async Task ShareServerAsync() { - var item = await AppHandler.Instance.GetProfileItem(SelectedProfile.IndexId); + var item = await AppManager.Instance.GetProfileItem(SelectedProfile.IndexId); if (item is null) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectServer); + NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer); return; } var url = FmtHandler.GetShareUri(item); @@ -647,7 +647,7 @@ public class ProfilesViewModel : MyReactiveObject var ret = await ConfigHandler.AddCustomServer4Multiple(_config, lstSelected, coreType, multipleLoad); if (ret.Success != true) { - NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); + NoticeManager.Instance.Enqueue(ResUI.OperationFailed); return; } if (ret?.Data?.ToString() == _config.IndexId) @@ -682,7 +682,7 @@ public class ProfilesViewModel : MyReactiveObject { var count = await ConfigHandler.RemoveInvalidServerResult(_config, _config.SubIndexId); RefreshServers(); - NoticeHandler.Instance.Enqueue(string.Format(ResUI.RemoveInvalidServerResultTip, count)); + NoticeManager.Instance.Enqueue(string.Format(ResUI.RemoveInvalidServerResultTip, count)); } //move server @@ -700,7 +700,7 @@ public class ProfilesViewModel : MyReactiveObject } await ConfigHandler.MoveToGroup(_config, lstSelected, SelectedMoveToGroup.Id); - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); RefreshServers(); SelectedMoveToGroup = null; @@ -712,7 +712,7 @@ public class ProfilesViewModel : MyReactiveObject var item = _lstProfile.FirstOrDefault(t => t.IndexId == SelectedProfile.IndexId); if (item is null) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectServer); + NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer); return; } @@ -762,10 +762,10 @@ public class ProfilesViewModel : MyReactiveObject private async Task Export2ClientConfigAsync(bool blClipboard) { - var item = await AppHandler.Instance.GetProfileItem(SelectedProfile.IndexId); + var item = await AppManager.Instance.GetProfileItem(SelectedProfile.IndexId); if (item is null) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectServer); + NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer); return; } if (blClipboard) @@ -773,12 +773,12 @@ public class ProfilesViewModel : MyReactiveObject var result = await CoreConfigHandler.GenerateClientConfig(item, null); if (result.Success != true) { - NoticeHandler.Instance.Enqueue(result.Msg); + NoticeManager.Instance.Enqueue(result.Msg); } else { await _updateView?.Invoke(EViewAction.SetClipboardData, result.Data); - NoticeHandler.Instance.SendMessage(ResUI.OperationSuccess); + NoticeManager.Instance.SendMessage(ResUI.OperationSuccess); } } else @@ -796,11 +796,11 @@ public class ProfilesViewModel : MyReactiveObject var result = await CoreConfigHandler.GenerateClientConfig(item, fileName); if (result.Success != true) { - NoticeHandler.Instance.Enqueue(result.Msg); + NoticeManager.Instance.Enqueue(result.Msg); } else { - NoticeHandler.Instance.SendMessageAndEnqueue(string.Format(ResUI.SaveClientConfigurationIn, fileName)); + NoticeManager.Instance.SendMessageAndEnqueue(string.Format(ResUI.SaveClientConfigurationIn, fileName)); } } @@ -833,7 +833,7 @@ public class ProfilesViewModel : MyReactiveObject { await _updateView?.Invoke(EViewAction.SetClipboardData, sb.ToString()); } - NoticeHandler.Instance.SendMessage(ResUI.BatchExportURLSuccessfully); + NoticeManager.Instance.SendMessage(ResUI.BatchExportURLSuccessfully); } } @@ -850,7 +850,7 @@ public class ProfilesViewModel : MyReactiveObject } else { - item = await AppHandler.Instance.GetSubItem(_config.SubIndexId); + item = await AppManager.Instance.GetSubItem(_config.SubIndexId); if (item is null) { return; diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingRuleDetailsViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingRuleDetailsViewModel.cs index c4970f840e..758aa8fe55 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingRuleDetailsViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingRuleDetailsViewModel.cs @@ -28,7 +28,7 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject public RoutingRuleDetailsViewModel(RulesItem rulesItem, Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; SaveCmd = ReactiveCommand.CreateFromTask(async () => @@ -83,7 +83,7 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject if (!hasRule) { - NoticeHandler.Instance.Enqueue(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Network/Port/Protocol/Domain/IP/Process")); + NoticeManager.Instance.Enqueue(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Network/Port/Protocol/Domain/IP/Process")); return; } //NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs index 93658a0766..c3acc7ba18 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs @@ -37,7 +37,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject public RoutingRuleSettingViewModel(RoutingItem routingItem, Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; var canEditRemove = this.WhenAnyValue( @@ -151,7 +151,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject { if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules); + NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules); return; } if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false) @@ -174,7 +174,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject { if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules); + NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules); return; } @@ -205,7 +205,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject { if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules); + NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules); return; } @@ -226,7 +226,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject string remarks = SelectedRouting.Remarks; if (remarks.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks); + NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks); return; } var item = SelectedRouting; @@ -239,12 +239,12 @@ public class RoutingRuleSettingViewModel : MyReactiveObject if (await ConfigHandler.SaveRoutingItem(_config, item) == 0) { - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); _updateView?.Invoke(EViewAction.CloseWindow, null); } else { - NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); + NoticeManager.Instance.Enqueue(ResUI.OperationFailed); } } @@ -266,7 +266,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject if (ret == 0) { RefreshRulesItems(); - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); } } @@ -281,7 +281,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject if (ret == 0) { RefreshRulesItems(); - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); } } @@ -290,7 +290,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject var url = SelectedRouting.Url; if (url.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.MsgNeedUrl); + NoticeManager.Instance.Enqueue(ResUI.MsgNeedUrl); return; } @@ -300,7 +300,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject if (ret == 0) { RefreshRulesItems(); - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); } } diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingSettingViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingSettingViewModel.cs index 73b18fb7ee..8aa36f0c27 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingSettingViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/RoutingSettingViewModel.cs @@ -20,9 +20,6 @@ public class RoutingSettingViewModel : MyReactiveObject [Reactive] public string DomainStrategy { get; set; } - [Reactive] - public string DomainMatcher { get; set; } - [Reactive] public string DomainStrategy4Singbox { get; set; } @@ -38,7 +35,7 @@ public class RoutingSettingViewModel : MyReactiveObject public RoutingSettingViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; var canEditRemove = this.WhenAnyValue( @@ -75,7 +72,6 @@ public class RoutingSettingViewModel : MyReactiveObject SelectedSource = new(); DomainStrategy = _config.RoutingBasicItem.DomainStrategy; - DomainMatcher = _config.RoutingBasicItem.DomainMatcher; DomainStrategy4Singbox = _config.RoutingBasicItem.DomainStrategy4Singbox; await ConfigHandler.InitBuiltinRouting(_config); @@ -88,7 +84,7 @@ public class RoutingSettingViewModel : MyReactiveObject { _routingItems.Clear(); - var routings = await AppHandler.Instance.RoutingItems(); + var routings = await AppManager.Instance.RoutingItems(); foreach (var item in routings) { var it = new RoutingItemModel() @@ -109,17 +105,16 @@ public class RoutingSettingViewModel : MyReactiveObject private async Task SaveRoutingAsync() { _config.RoutingBasicItem.DomainStrategy = DomainStrategy; - _config.RoutingBasicItem.DomainMatcher = DomainMatcher; _config.RoutingBasicItem.DomainStrategy4Singbox = DomainStrategy4Singbox; if (await ConfigHandler.SaveConfig(_config) == 0) { - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); _updateView?.Invoke(EViewAction.CloseWindow, null); } else { - NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); + NoticeManager.Instance.Enqueue(ResUI.OperationFailed); } } @@ -134,7 +129,7 @@ public class RoutingSettingViewModel : MyReactiveObject } else { - item = await AppHandler.Instance.GetRoutingItem(SelectedSource?.Id); + item = await AppManager.Instance.GetRoutingItem(SelectedSource?.Id); if (item is null) { return; @@ -151,7 +146,7 @@ public class RoutingSettingViewModel : MyReactiveObject { if (SelectedSource is null || SelectedSource.Remarks.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules); + NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules); return; } if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false) @@ -160,7 +155,7 @@ public class RoutingSettingViewModel : MyReactiveObject } foreach (var it in SelectedSources ?? [SelectedSource]) { - var item = await AppHandler.Instance.GetRoutingItem(it?.Id); + var item = await AppManager.Instance.GetRoutingItem(it?.Id); if (item != null) { await ConfigHandler.RemoveRoutingItem(item); @@ -173,10 +168,10 @@ public class RoutingSettingViewModel : MyReactiveObject public async Task RoutingAdvancedSetDefault() { - var item = await AppHandler.Instance.GetRoutingItem(SelectedSource?.Id); + var item = await AppManager.Instance.GetRoutingItem(SelectedSource?.Id); if (item is null) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules); + NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules); return; } diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index 5c2acab6d0..ae097c3c11 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -100,7 +100,7 @@ public class StatusBarViewModel : MyReactiveObject public StatusBarViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; SelectedRouting = new(); SelectedServer = new(); RunningServerToolTipText = "-"; @@ -228,7 +228,7 @@ public class StatusBarViewModel : MyReactiveObject private async Task CopyProxyCmdToClipboard() { var cmd = Utils.IsWindows() ? "set" : "export"; - var address = $"{Global.Loopback}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}"; + var address = $"{Global.Loopback}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}"; var sb = new StringBuilder(); sb.AppendLine($"{cmd} http_proxy={Global.HttpProtocol}{address}"); @@ -283,7 +283,7 @@ public class StatusBarViewModel : MyReactiveObject private async Task RefreshServersMenu() { - var lstModel = await AppHandler.Instance.ProfileItems(_config.SubIndexId, ""); + var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, ""); _servers.Clear(); if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit) @@ -334,12 +334,9 @@ public class StatusBarViewModel : MyReactiveObject _updateView?.Invoke(EViewAction.DispatcherServerAvailability, ResUI.Speedtesting); - var msg = await Task.Run(async () => - { - return await ConnectionHandler.Instance.RunAvailabilityCheck(); - }); + var msg = await Task.Run(ConnectionHandler.RunAvailabilityCheck); - NoticeHandler.Instance.SendMessageEx(msg); + NoticeManager.Instance.SendMessageEx(msg); _updateView?.Invoke(EViewAction.DispatcherServerAvailability, msg); } @@ -358,7 +355,7 @@ public class StatusBarViewModel : MyReactiveObject } _config.SystemProxyItem.SysProxyType = type; await ChangeSystemProxyAsync(type, true); - NoticeHandler.Instance.SendMessageEx($"{ResUI.TipChangeSystemProxy} - {_config.SystemProxyItem.SysProxyType.ToString()}"); + NoticeManager.Instance.SendMessageEx($"{ResUI.TipChangeSystemProxy} - {_config.SystemProxyItem.SysProxyType.ToString()}"); SystemProxySelected = (int)_config.SystemProxyItem.SysProxyType; await ConfigHandler.SaveConfig(_config); @@ -384,7 +381,7 @@ public class StatusBarViewModel : MyReactiveObject _routingItems.Clear(); BlRouting = true; - var routings = await AppHandler.Instance.RoutingItems(); + var routings = await AppManager.Instance.RoutingItems(); foreach (var item in routings) { _routingItems.Add(item); @@ -407,7 +404,7 @@ public class StatusBarViewModel : MyReactiveObject return; } - var item = await AppHandler.Instance.GetRoutingItem(SelectedRouting?.Id); + var item = await AppManager.Instance.GetRoutingItem(SelectedRouting?.Id); if (item is null) { return; @@ -415,7 +412,7 @@ public class StatusBarViewModel : MyReactiveObject if (await ConfigHandler.SetDefaultRouting(_config, item) == 0) { - NoticeHandler.Instance.SendMessageEx(ResUI.TipChangeRouting); + NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting); Locator.Current.GetService()?.Reload(); _updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null); } @@ -474,11 +471,11 @@ public class StatusBarViewModel : MyReactiveObject } else if (Utils.IsLinux()) { - return AppHandler.Instance.LinuxSudoPwd.IsNotEmpty(); + return AppManager.Instance.LinuxSudoPwd.IsNotEmpty(); } else if (Utils.IsOSX()) { - return AppHandler.Instance.LinuxSudoPwd.IsNotEmpty(); + return AppManager.Instance.LinuxSudoPwd.IsNotEmpty(); } return false; } @@ -490,10 +487,10 @@ public class StatusBarViewModel : MyReactiveObject public async Task InboundDisplayStatus() { StringBuilder sb = new(); - sb.Append($"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}"); + sb.Append($"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}"); if (_config.Inbound.First().SecondLocalPortEnabled) { - sb.Append($",{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}"); + sb.Append($",{AppManager.Instance.GetLocalPort(EInboundProtocol.socks2)}"); } sb.Append(']'); InboundDisplay = $"{ResUI.LabLocal}:{sb}"; @@ -501,8 +498,8 @@ public class StatusBarViewModel : MyReactiveObject if (_config.Inbound.First().AllowLANConn) { var lan = _config.Inbound.First().NewPort4LAN - ? $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks3)}]" - : $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]"; + ? $"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks3)}]" + : $"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}]"; InboundLanDisplay = $"{ResUI.LabLAN}:{lan}"; } else diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/SubEditViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/SubEditViewModel.cs index e98a8a34cf..bfbfbbe7f4 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/SubEditViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/SubEditViewModel.cs @@ -13,7 +13,7 @@ public class SubEditViewModel : MyReactiveObject public SubEditViewModel(SubItem subItem, Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; SaveCmd = ReactiveCommand.CreateFromTask(async () => @@ -29,7 +29,7 @@ public class SubEditViewModel : MyReactiveObject var remarks = SelectedSource.Remarks; if (remarks.IsNullOrEmpty()) { - NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks); + NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks); return; } @@ -39,25 +39,25 @@ public class SubEditViewModel : MyReactiveObject var uri = Utils.TryUri(url); if (uri == null) { - NoticeHandler.Instance.Enqueue(ResUI.InvalidUrlTip); + NoticeManager.Instance.Enqueue(ResUI.InvalidUrlTip); return; } //Do not allow http protocol if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost)) { - NoticeHandler.Instance.Enqueue(ResUI.InsecureUrlProtocol); + NoticeManager.Instance.Enqueue(ResUI.InsecureUrlProtocol); //return; } } if (await ConfigHandler.AddSubItem(_config, SelectedSource) == 0) { - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); _updateView?.Invoke(EViewAction.CloseWindow, null); } else { - NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); + NoticeManager.Instance.Enqueue(ResUI.OperationFailed); } } } diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/SubSettingViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/SubSettingViewModel.cs index 71cf1c6161..e9c657c488 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/SubSettingViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/SubSettingViewModel.cs @@ -24,7 +24,7 @@ public class SubSettingViewModel : MyReactiveObject public SubSettingViewModel(Func>? updateView) { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _updateView = updateView; var canEditRemove = this.WhenAnyValue( @@ -61,7 +61,7 @@ public class SubSettingViewModel : MyReactiveObject public async Task RefreshSubItems() { _subItems.Clear(); - _subItems.AddRange(await AppHandler.Instance.SubItems()); + _subItems.AddRange(await AppManager.Instance.SubItems()); } public async Task EditSubAsync(bool blNew) @@ -73,7 +73,7 @@ public class SubSettingViewModel : MyReactiveObject } else { - item = await AppHandler.Instance.GetSubItem(SelectedSource?.Id); + item = await AppManager.Instance.GetSubItem(SelectedSource?.Id); if (item is null) { return; @@ -98,7 +98,7 @@ public class SubSettingViewModel : MyReactiveObject await ConfigHandler.DeleteSubItem(_config, it.Id); } await RefreshSubItems(); - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); + NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); IsModified = true; } } diff --git a/v2rayn/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/App.axaml.cs index ebd5c29a87..a1b97b771e 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/App.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/App.axaml.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; +using ServiceLib.Manager; using Splat; using v2rayN.Desktop.Common; using v2rayN.Desktop.Views; @@ -25,7 +26,7 @@ public partial class App : Application { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - AppHandler.Instance.InitComponents(); + AppManager.Instance.InitComponents(); desktop.Exit += OnExit; desktop.MainWindow = new MainWindow(); diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Base/WindowBase.cs b/v2rayn/v2rayN/v2rayN.Desktop/Base/WindowBase.cs index a84fc86c10..c2c9a0bc63 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Base/WindowBase.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Base/WindowBase.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Interactivity; using Avalonia.ReactiveUI; +using ServiceLib.Manager; namespace v2rayN.Desktop.Base; @@ -20,7 +21,7 @@ public class WindowBase : ReactiveWindow where TViewMode { try { - var sizeItem = ConfigHandler.GetWindowSizeItem(AppHandler.Instance.Config, GetType().Name); + var sizeItem = ConfigHandler.GetWindowSizeItem(AppManager.Instance.Config, GetType().Name); if (sizeItem == null) { return; @@ -45,7 +46,7 @@ public class WindowBase : ReactiveWindow where TViewMode base.OnClosed(e); try { - ConfigHandler.SaveWindowSizeItem(AppHandler.Instance.Config, GetType().Name, Width, Height); + ConfigHandler.SaveWindowSizeItem(AppManager.Instance.Config, GetType().Name, Width, Height); } catch { } } diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Program.cs b/v2rayn/v2rayN/v2rayN.Desktop/Program.cs index 52fdbf9223..58ddb9f94e 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Program.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Program.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.ReactiveUI; +using ServiceLib.Manager; using v2rayN.Desktop.Common; namespace v2rayN.Desktop; @@ -46,7 +47,7 @@ internal class Program } } - if (!AppHandler.Instance.InitApp()) + if (!AppManager.Instance.InitApp()) { return false; } @@ -62,6 +63,6 @@ internal class Program .WithFontByDefault() .LogToTrace() .UseReactiveUI() - .With(new MacOSPlatformOptions { ShowInDock = AppHandler.Instance.Config.UiItem.MacOSShowInDock }); + .With(new MacOSPlatformOptions { ShowInDock = AppManager.Instance.Config.UiItem.MacOSShowInDock }); } } diff --git a/v2rayn/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs b/v2rayn/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs index 877a3b4f00..21899bfc45 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs @@ -8,6 +8,7 @@ using Avalonia.Styling; using ReactiveUI; using ReactiveUI.Fody.Helpers; using Semi.Avalonia; +using ServiceLib.Manager; namespace v2rayN.Desktop.ViewModels; @@ -21,7 +22,7 @@ public class ThemeSettingViewModel : MyReactiveObject public ThemeSettingViewModel() { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; BindingUI(); RestoreUI(); @@ -74,7 +75,7 @@ public class ThemeSettingViewModel : MyReactiveObject _config.UiItem.CurrentLanguage = CurrentLanguage; Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage); ConfigHandler.SaveConfig(_config); - NoticeHandler.Instance.Enqueue(ResUI.NeedRebootTips); + NoticeManager.Instance.Enqueue(ResUI.NeedRebootTips); } }); } diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index 9d24175fba..6880e23deb 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -2,6 +2,7 @@ using System.Reactive.Disposables; using Avalonia.Controls; using Avalonia.Interactivity; using ReactiveUI; +using ServiceLib.Manager; using v2rayN.Desktop.Base; namespace v2rayN.Desktop.Views; @@ -50,7 +51,7 @@ public partial class AddServerWindow : WindowBase case EConfigType.Shadowsocks: gridSs.IsVisible = true; - cmbSecurity3.ItemsSource = AppHandler.Instance.GetShadowsocksSecurities(profileItem); + cmbSecurity3.ItemsSource = AppManager.Instance.GetShadowsocksSecurities(profileItem); break; case EConfigType.SOCKS: diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs index 7b239ff4d8..3bd2dec7d8 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs @@ -2,6 +2,7 @@ using System.Reactive.Disposables; using Avalonia.Controls; using Avalonia.Interactivity; using ReactiveUI; +using ServiceLib.Manager; using v2rayN.Desktop.Base; namespace v2rayN.Desktop.Views; @@ -14,7 +15,7 @@ public partial class DNSSettingWindow : WindowBase { InitializeComponent(); - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; btnCancel.Click += (s, e) => this.Close(); ViewModel = new DNSSettingViewModel(UpdateViewHandler); diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs index 502fef366b..a9b060fe86 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs @@ -1,6 +1,7 @@ using System.Reactive.Disposables; using Avalonia.Interactivity; using ReactiveUI; +using ServiceLib.Manager; using v2rayN.Desktop.Base; namespace v2rayN.Desktop.Views; @@ -13,7 +14,7 @@ public partial class FullConfigTemplateWindow : WindowBase this.Close(); ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler); diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index 581e0a35f1..e808e54e17 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -9,6 +9,7 @@ using Avalonia.Threading; using DialogHostAvalonia; using MsBox.Avalonia.Enums; using ReactiveUI; +using ServiceLib.Manager; using Splat; using v2rayN.Desktop.Base; using v2rayN.Desktop.Common; @@ -28,7 +29,7 @@ public partial class MainWindow : WindowBase { InitializeComponent(); - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.TopRight }; this.KeyDown += MainWindow_KeyDown; @@ -351,7 +352,7 @@ public partial class MainWindow : WindowBase { //ShowHideWindow(false); - NoticeHandler.Instance.SendMessageAndEnqueue("Not yet implemented.(还未实现)"); + NoticeManager.Instance.SendMessageAndEnqueue("Not yet implemented.(还未实现)"); await Task.CompletedTask; //if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) //{ @@ -477,7 +478,7 @@ public partial class MainWindow : WindowBase private void AddHelpMenuItem() { - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(); + var coreInfo = CoreInfoManager.Instance.GetCoreInfo(); foreach (var it in coreInfo .Where(t => t.CoreType != ECoreType.v2fly && t.CoreType != ECoreType.hysteria)) diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs index a7529369d4..82243f1051 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs @@ -1,6 +1,7 @@ using System.Reactive.Disposables; using Avalonia.Controls; using ReactiveUI; +using ServiceLib.Manager; using v2rayN.Desktop.Base; namespace v2rayN.Desktop.Views; @@ -14,7 +15,7 @@ public partial class OptionSettingWindow : WindowBase InitializeComponent(); btnCancel.Click += (s, e) => this.Close(); - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; ViewModel = new OptionSettingViewModel(UpdateViewHandler); diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs index f72c1f51f5..492dc23748 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs @@ -7,6 +7,7 @@ using Avalonia.Threading; using DialogHostAvalonia; using MsBox.Avalonia.Enums; using ReactiveUI; +using ServiceLib.Manager; using Splat; using v2rayN.Desktop.Common; @@ -26,7 +27,7 @@ public partial class ProfilesView : ReactiveUserControl { InitializeComponent(); - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; _window = window; menuSelectAll.Click += menuSelectAll_Click; diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml.cs index c4e3052c72..3c498cb699 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml.cs @@ -23,7 +23,7 @@ public partial class QrcodeView : UserControl private Bitmap? GetQRCode(string? url) { - var bytes = QRCodeHelper.GenQRCode(url); + var bytes = QRCodeUtils.GenQRCode(url); return ByteToBitmap(bytes); } diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml b/v2rayn/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml index fba1863d1c..710980c4ca 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml @@ -50,7 +50,7 @@ Margin="{StaticResource Margin4}" ColumnDefinitions="Auto,Auto" DockPanel.Dock="Top" - RowDefinitions="Auto,Auto,Auto"> + RowDefinitions="Auto,Auto"> - - - @@ -94,7 +81,7 @@ ViewModel = new RoutingSettingViewModel(UpdateViewHandler); cmbdomainStrategy.ItemsSource = Global.DomainStrategies; - cmbdomainMatcher.ItemsSource = Global.DomainMatchers; cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox; this.WhenActivated(disposables => @@ -36,7 +35,6 @@ public partial class RoutingSettingWindow : WindowBase this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstRoutings.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainStrategy, v => v.cmbdomainStrategy.SelectedValue).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.DomainMatcher, v => v.cmbdomainMatcher.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainStrategy4Singbox, v => v.cmbdomainStrategy4Singbox.SelectedValue).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RoutingAdvancedAddCmd, v => v.menuRoutingAdvancedAdd).DisposeWith(disposables); diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs index 92472c50af..34ae19530f 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs @@ -6,6 +6,7 @@ using Avalonia.ReactiveUI; using Avalonia.Threading; using DialogHostAvalonia; using ReactiveUI; +using ServiceLib.Manager; using Splat; using v2rayN.Desktop.Common; @@ -19,7 +20,7 @@ public partial class StatusBarView : ReactiveUserControl { InitializeComponent(); - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; //ViewModel = new StatusBarViewModel(UpdateViewHandler); //Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel)); ViewModel = Locator.Current.GetService(); @@ -112,7 +113,7 @@ public partial class StatusBarView : ReactiveUserControl return false; } - AppHandler.Instance.LinuxSudoPwd = password; + AppManager.Instance.LinuxSudoPwd = password; return true; } diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs index 9a5e908f25..90b6ba7616 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs @@ -2,6 +2,7 @@ using Avalonia.Controls; using Avalonia.Threading; using CliWrap.Buffered; using DialogHostAvalonia; +using ServiceLib.Manager; namespace v2rayN.Desktop.Views; @@ -46,7 +47,7 @@ public partial class SudoPasswordInputView : UserControl else { // Password verification failed, display error and let user try again - NoticeHandler.Instance.Enqueue(ResUI.SudoIncorrectPasswordTip); + NoticeManager.Instance.Enqueue(ResUI.SudoIncorrectPasswordTip); txtPassword.Focus(); } } diff --git a/v2rayn/v2rayN/v2rayN/App.xaml.cs b/v2rayn/v2rayN/v2rayN/App.xaml.cs index 0a28273380..611fad7226 100644 --- a/v2rayn/v2rayN/v2rayN/App.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/App.xaml.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using System.Windows; using System.Windows.Threading; +using ServiceLib.Manager; namespace v2rayN; @@ -35,14 +36,14 @@ public partial class App : Application return; } - if (!AppHandler.Instance.InitApp()) + if (!AppManager.Instance.InitApp()) { UI.Show($"Loading GUI configuration file is abnormal,please restart the application{Environment.NewLine}加载GUI配置文件异常,请重启应用"); Environment.Exit(0); return; } - AppHandler.Instance.InitComponents(); + AppManager.Instance.InitComponents(); base.OnStartup(e); } diff --git a/v2rayn/v2rayN/v2rayN/Base/WindowBase.cs b/v2rayn/v2rayN/v2rayN/Base/WindowBase.cs index f73d2b6949..3e1c30040f 100644 --- a/v2rayn/v2rayN/v2rayN/Base/WindowBase.cs +++ b/v2rayn/v2rayN/v2rayN/Base/WindowBase.cs @@ -1,5 +1,6 @@ using System.Windows; using ReactiveUI; +using ServiceLib.Manager; namespace v2rayN.Base; @@ -14,7 +15,7 @@ public class WindowBase : ReactiveWindow where TViewMode { try { - var sizeItem = ConfigHandler.GetWindowSizeItem(AppHandler.Instance.Config, GetType().Name); + var sizeItem = ConfigHandler.GetWindowSizeItem(AppManager.Instance.Config, GetType().Name); if (sizeItem == null) { return; @@ -35,7 +36,7 @@ public class WindowBase : ReactiveWindow where TViewMode try { - ConfigHandler.SaveWindowSizeItem(AppHandler.Instance.Config, GetType().Name, Width, Height); + ConfigHandler.SaveWindowSizeItem(AppManager.Instance.Config, GetType().Name, Width, Height); } catch { } } diff --git a/v2rayn/v2rayN/v2rayN/Common/QRCodeHelper.cs b/v2rayn/v2rayN/v2rayN/Common/QRCodeHelper.cs index 74bf954510..6d0709724c 100644 --- a/v2rayn/v2rayN/v2rayN/Common/QRCodeHelper.cs +++ b/v2rayn/v2rayN/v2rayN/Common/QRCodeHelper.cs @@ -17,7 +17,7 @@ public class QRCodeHelper } try { - var qrCodeImage = ServiceLib.Common.QRCodeHelper.GenQRCode(strContent); + var qrCodeImage = QRCodeUtils.GenQRCode(strContent); return qrCodeImage is null ? null : ByteToImage(qrCodeImage); } catch diff --git a/v2rayn/v2rayN/v2rayN/Converters/MaterialDesignFonts.cs b/v2rayn/v2rayN/v2rayN/Converters/MaterialDesignFonts.cs index 1cc021c78f..73fd563206 100644 --- a/v2rayn/v2rayN/v2rayN/Converters/MaterialDesignFonts.cs +++ b/v2rayn/v2rayN/v2rayN/Converters/MaterialDesignFonts.cs @@ -1,4 +1,5 @@ using System.Windows.Media; +using ServiceLib.Manager; namespace v2rayN.Converters; @@ -10,7 +11,7 @@ public class MaterialDesignFonts { try { - var fontFamily = AppHandler.Instance.Config.UiItem.CurrentFontFamily; + var fontFamily = AppManager.Instance.Config.UiItem.CurrentFontFamily; if (fontFamily.IsNotEmpty()) { var fontPath = Utils.GetFontsPath(); diff --git a/v2rayn/v2rayN/v2rayN/Handler/HotkeyHandler.cs b/v2rayn/v2rayN/v2rayN/Handler/HotkeyHandler.cs index 5f261463b0..cfc2d4636d 100644 --- a/v2rayn/v2rayN/v2rayN/Handler/HotkeyHandler.cs +++ b/v2rayn/v2rayN/v2rayN/Handler/HotkeyHandler.cs @@ -4,6 +4,7 @@ using System.Text; using System.Windows; using System.Windows.Input; using System.Windows.Interop; +using ServiceLib.Manager; namespace v2rayN.Handler; @@ -29,7 +30,7 @@ public sealed class HotkeyHandler private void Init() { _hotkeyTriggerDic.Clear(); - foreach (var item in AppHandler.Instance.Config.GlobalHotkeys) + foreach (var item in AppManager.Instance.Config.GlobalHotkeys) { if (item.KeyCode != null && (Key)item.KeyCode != Key.None) { diff --git a/v2rayn/v2rayN/v2rayN/ViewModels/ThemeSettingViewModel.cs b/v2rayn/v2rayN/v2rayN/ViewModels/ThemeSettingViewModel.cs index 37d3b654a8..a9bd4b0654 100644 --- a/v2rayn/v2rayN/v2rayN/ViewModels/ThemeSettingViewModel.cs +++ b/v2rayn/v2rayN/v2rayN/ViewModels/ThemeSettingViewModel.cs @@ -9,6 +9,7 @@ using MaterialDesignColors.ColorManipulation; using MaterialDesignThemes.Wpf; using ReactiveUI; using ReactiveUI.Fody.Helpers; +using ServiceLib.Manager; namespace v2rayN.ViewModels; @@ -30,7 +31,7 @@ public class ThemeSettingViewModel : MyReactiveObject public ThemeSettingViewModel() { - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; RegisterSystemColorSet(_config, Application.Current.MainWindow, ModifyTheme); @@ -121,7 +122,7 @@ public class ThemeSettingViewModel : MyReactiveObject _config.UiItem.CurrentLanguage = CurrentLanguage; Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage); ConfigHandler.SaveConfig(_config); - NoticeHandler.Instance.Enqueue(ResUI.NeedRebootTips); + NoticeManager.Instance.Enqueue(ResUI.NeedRebootTips); } }); } diff --git a/v2rayn/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs index 87a476f6e6..b455ecd4ea 100644 --- a/v2rayn/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs @@ -1,6 +1,7 @@ using System.Reactive.Disposables; using System.Windows; using ReactiveUI; +using ServiceLib.Manager; namespace v2rayN.Views; @@ -28,7 +29,7 @@ public partial class AddServer2Window this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.btnEdit).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveServerCmd, v => v.btnSave).DisposeWith(disposables); }); - WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); + WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } private async Task UpdateViewHandler(EViewAction action, object? obj) diff --git a/v2rayn/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index b06002a9f9..75f1bd1cc7 100644 --- a/v2rayn/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -2,6 +2,7 @@ using System.Reactive.Disposables; using System.Windows; using System.Windows.Controls; using ReactiveUI; +using ServiceLib.Manager; namespace v2rayN.Views; @@ -44,7 +45,7 @@ public partial class AddServerWindow case EConfigType.Shadowsocks: gridSs.Visibility = Visibility.Visible; - cmbSecurity3.ItemsSource = AppHandler.Instance.GetShadowsocksSecurities(profileItem); + cmbSecurity3.ItemsSource = AppManager.Instance.GetShadowsocksSecurities(profileItem); break; case EConfigType.SOCKS: @@ -195,7 +196,7 @@ public partial class AddServerWindow }); this.Title = $"{profileItem.ConfigType}"; - WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); + WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } private async Task UpdateViewHandler(EViewAction action, object? obj) diff --git a/v2rayn/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs index cb6b44a83f..9df0cf2cbd 100644 --- a/v2rayn/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs @@ -1,6 +1,7 @@ using System.Reactive.Disposables; using System.Windows; using ReactiveUI; +using ServiceLib.Manager; namespace v2rayN.Views; @@ -13,7 +14,7 @@ public partial class DNSSettingWindow InitializeComponent(); this.Owner = Application.Current.MainWindow; - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; ViewModel = new DNSSettingViewModel(UpdateViewHandler); @@ -78,7 +79,7 @@ public partial class DNSSettingWindow .BindTo(this, x => x.txtAdvancedDNSSettingsInvalid.Visibility) .DisposeWith(disposables); }); - WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); + WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } private async Task UpdateViewHandler(EViewAction action, object? obj) diff --git a/v2rayn/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs index 3e38bf1238..583ccd2242 100644 --- a/v2rayn/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs @@ -1,6 +1,7 @@ using System.Reactive.Disposables; using System.Windows; using ReactiveUI; +using ServiceLib.Manager; namespace v2rayN.Views; @@ -13,7 +14,7 @@ public partial class FullConfigTemplateWindow InitializeComponent(); this.Owner = Application.Current.MainWindow; - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler); @@ -31,7 +32,7 @@ public partial class FullConfigTemplateWindow this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); - WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); + WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } private async Task UpdateViewHandler(EViewAction action, object? obj) diff --git a/v2rayn/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs index ce6aeb72ee..d6d25445c7 100644 --- a/v2rayn/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs @@ -4,6 +4,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; using ReactiveUI; +using ServiceLib.Manager; using v2rayN.Handler; namespace v2rayN.Views; @@ -29,7 +30,7 @@ public partial class GlobalHotkeySettingWindow { this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); - WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); + WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); Init(); BindingData(); diff --git a/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml.cs index 99f3fc038d..95f3b218c7 100644 --- a/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml.cs @@ -8,6 +8,7 @@ using System.Windows.Media; using System.Windows.Threading; using MaterialDesignThemes.Wpf; using ReactiveUI; +using ServiceLib.Manager; using Splat; using v2rayN.Handler; @@ -23,7 +24,7 @@ public partial class MainWindow { InitializeComponent(); - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false); App.Current.SessionEnding += Current_SessionEnding; @@ -438,7 +439,7 @@ public partial class MainWindow private void AddHelpMenuItem() { - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(); + var coreInfo = CoreInfoManager.Instance.GetCoreInfo(); foreach (var it in coreInfo .Where(t => t.CoreType != ECoreType.v2fly && t.CoreType != ECoreType.hysteria)) diff --git a/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs index dea7302bf6..163477104a 100644 --- a/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs @@ -4,6 +4,7 @@ using System.Reactive.Disposables; using System.Windows; using System.Windows.Media; using ReactiveUI; +using ServiceLib.Manager; namespace v2rayN.Views; @@ -16,7 +17,7 @@ public partial class OptionSettingWindow InitializeComponent(); this.Owner = Application.Current.MainWindow; - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; ViewModel = new OptionSettingViewModel(UpdateViewHandler); @@ -134,7 +135,7 @@ public partial class OptionSettingWindow this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); - WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); + WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } private async Task UpdateViewHandler(EViewAction action, object? obj) diff --git a/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml.cs index 3501747e48..a17e0585a3 100644 --- a/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml.cs @@ -7,6 +7,7 @@ using System.Windows.Media; using System.Windows.Threading; using MaterialDesignThemes.Wpf; using ReactiveUI; +using ServiceLib.Manager; using Splat; using v2rayN.Base; using Point = System.Windows.Point; @@ -22,7 +23,7 @@ public partial class ProfilesView InitializeComponent(); lstGroup.MaxHeight = Math.Floor(SystemParameters.WorkArea.Height * 0.20 / 40) * 40; - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click; txtServerFilter.PreviewKeyDown += TxtServerFilter_PreviewKeyDown; diff --git a/v2rayn/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs index 9db3fb83d8..86490e8bc0 100644 --- a/v2rayn/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs @@ -1,6 +1,7 @@ using System.Reactive.Disposables; using System.Windows; using ReactiveUI; +using ServiceLib.Manager; namespace v2rayN.Views; @@ -48,7 +49,7 @@ public partial class RoutingRuleDetailsWindow this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); - WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); + WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } private async Task UpdateViewHandler(EViewAction action, object? obj) diff --git a/v2rayn/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs index bec5d1f7a3..8b836a0236 100644 --- a/v2rayn/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs @@ -2,6 +2,7 @@ using System.Reactive.Disposables; using System.Windows; using System.Windows.Input; using ReactiveUI; +using ServiceLib.Manager; namespace v2rayN.Views; @@ -54,7 +55,7 @@ public partial class RoutingRuleSettingWindow this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); - WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); + WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } private async Task UpdateViewHandler(EViewAction action, object? obj) diff --git a/v2rayn/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml b/v2rayn/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml index 463a69d4b1..36b4240904 100644 --- a/v2rayn/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml +++ b/v2rayn/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml @@ -78,7 +78,6 @@ - @@ -109,21 +108,6 @@ Grid.Column="0" Margin="{StaticResource Margin4}" VerticalAlignment="Center" - Style="{StaticResource ToolbarTextBlock}" - Text="{x:Static resx:ResUI.TbdomainMatcher}" /> - - - @@ -132,7 +116,7 @@ @@ -31,7 +31,6 @@ public partial class RoutingSettingWindow this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstRoutings.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainStrategy, v => v.cmbdomainStrategy.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.DomainMatcher, v => v.cmbdomainMatcher.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainStrategy4Singbox, v => v.cmbdomainStrategy4Singbox.Text).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RoutingAdvancedAddCmd, v => v.menuRoutingAdvancedAdd).DisposeWith(disposables); @@ -43,7 +42,7 @@ public partial class RoutingSettingWindow this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); - WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); + WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } private async Task UpdateViewHandler(EViewAction action, object? obj) diff --git a/v2rayn/v2rayN/v2rayN/Views/StatusBarView.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/StatusBarView.xaml.cs index a5217f427b..683767a75b 100644 --- a/v2rayn/v2rayN/v2rayN/Views/StatusBarView.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/StatusBarView.xaml.cs @@ -3,6 +3,7 @@ using System.Windows; using System.Windows.Input; using System.Windows.Threading; using ReactiveUI; +using ServiceLib.Manager; using Splat; using v2rayN.Handler; @@ -15,7 +16,7 @@ public partial class StatusBarView public StatusBarView() { InitializeComponent(); - _config = AppHandler.Instance.Config; + _config = AppManager.Instance.Config; ViewModel = new StatusBarViewModel(UpdateViewHandler); Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel)); diff --git a/v2rayn/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs index c9ab564b7d..7c3cb8479c 100644 --- a/v2rayn/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs @@ -1,6 +1,7 @@ using System.Reactive.Disposables; using System.Windows; using ReactiveUI; +using ServiceLib.Manager; namespace v2rayN.Views; @@ -35,7 +36,7 @@ public partial class SubEditWindow this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); - WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); + WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } private async Task UpdateViewHandler(EViewAction action, object? obj) diff --git a/v2rayn/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs index 9da5ed2f0a..797f3cdc1e 100644 --- a/v2rayn/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs @@ -4,6 +4,7 @@ using System.Windows; using System.Windows.Input; using MaterialDesignThemes.Wpf; using ReactiveUI; +using ServiceLib.Manager; namespace v2rayN.Views; @@ -31,7 +32,7 @@ public partial class SubSettingWindow this.BindCommand(ViewModel, vm => vm.SubEditCmd, v => v.menuSubEdit).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SubShareCmd, v => v.menuSubShare).DisposeWith(disposables); }); - WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); + WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } private async Task UpdateViewHandler(EViewAction action, object? obj) diff --git a/v2rayng/V2rayNG/app/build.gradle.kts b/v2rayng/V2rayNG/app/build.gradle.kts index e101f48aa6..cc29c0a07f 100644 --- a/v2rayng/V2rayNG/app/build.gradle.kts +++ b/v2rayng/V2rayNG/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 35 - versionCode = 668 - versionName = "1.10.18" + versionCode = 669 + versionName = "1.10.19" multiDexEnabled = true val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt index 7172728ec7..f06c0426ac 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt @@ -12,7 +12,9 @@ import java.net.IDN import java.net.Inet6Address import java.net.InetAddress import java.net.InetSocketAddress +import java.net.MalformedURLException import java.net.Proxy +import java.net.URI import java.net.URL object HttpUtil { @@ -140,7 +142,7 @@ object HttpUtil { val responseCode = conn.responseCode when (responseCode) { in 300..399 -> { - val location = conn.getHeaderField("Location") + val location = resolveLocation(conn) conn.disconnect() if (location.isNullOrEmpty()) { throw IOException("Redirect location not found") @@ -219,5 +221,29 @@ object HttpUtil { } return conn } + + // Returns absolute URL string location header sets + fun resolveLocation(conn: HttpURLConnection): String? { + val raw = conn.getHeaderField("Location")?.trim()?.takeIf { it.isNotEmpty() } ?: return null + + // Try check url is relative or absolute + return try { + val locUri = URI(raw) + val baseUri = conn.url.toURI() + val resolved = if (locUri.isAbsolute) locUri else baseUri.resolve(locUri) + resolved.toURL().toString() + } catch (_: Exception) { + // Fallback: url resolver, also should handles //host/... + try { + URL(raw).toString() // absolute with protocol + } catch (_: MalformedURLException) { + try { + URL(conn.url, raw).toString() + } catch (_: MalformedURLException) { + null + } + } + } + } } diff --git a/xray-core/app/router/command/command.pb.go b/xray-core/app/router/command/command.pb.go index 65bf3078b8..b8b1864d12 100644 --- a/xray-core/app/router/command/command.pb.go +++ b/xray-core/app/router/command/command.pb.go @@ -44,6 +44,7 @@ type RoutingContext struct { OutboundTag string `protobuf:"bytes,12,opt,name=OutboundTag,proto3" json:"OutboundTag,omitempty"` LocalIPs [][]byte `protobuf:"bytes,13,rep,name=LocalIPs,proto3" json:"LocalIPs,omitempty"` LocalPort uint32 `protobuf:"varint,14,opt,name=LocalPort,proto3" json:"LocalPort,omitempty"` + VlessRoute uint32 `protobuf:"varint,15,opt,name=VlessRoute,proto3" json:"VlessRoute,omitempty"` } func (x *RoutingContext) Reset() { @@ -174,6 +175,13 @@ func (x *RoutingContext) GetLocalPort() uint32 { return 0 } +func (x *RoutingContext) GetVlessRoute() uint32 { + if x != nil { + return x.VlessRoute + } + return 0 +} + // SubscribeRoutingStatsRequest subscribes to routing statistics channel if // opened by xray-core. // * FieldSelectors selects a subset of fields in routing statistics to return. @@ -843,7 +851,7 @@ var file_app_router_command_command_proto_rawDesc = []byte{ 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd6, 0x04, 0x0a, 0x0e, 0x52, 0x6f, 0x75, + 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf6, 0x04, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x4e, @@ -877,7 +885,9 @@ var file_app_router_command_command_proto_rawDesc = []byte{ 0x6c, 0x49, 0x50, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, - 0x72, 0x74, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, diff --git a/xray-core/app/router/command/command.proto b/xray-core/app/router/command/command.proto index 2a764b31fa..17aea57528 100644 --- a/xray-core/app/router/command/command.proto +++ b/xray-core/app/router/command/command.proto @@ -27,6 +27,7 @@ message RoutingContext { string OutboundTag = 12; repeated bytes LocalIPs = 13; uint32 LocalPort = 14; + uint32 VlessRoute = 15; } // SubscribeRoutingStatsRequest subscribes to routing statistics channel if diff --git a/xray-core/app/router/command/config.go b/xray-core/app/router/command/config.go index b5d677a622..13a319b155 100644 --- a/xray-core/app/router/command/config.go +++ b/xray-core/app/router/command/config.go @@ -36,6 +36,10 @@ func (c routingContext) GetLocalPort() net.Port { return net.Port(c.RoutingContext.GetLocalPort()) } +func (c routingContext) GetVlessRoute() net.Port { + return net.Port(c.RoutingContext.GetVlessRoute()) +} + func (c routingContext) GetRuleTag() string { return "" } diff --git a/xray-core/app/router/condition.go b/xray-core/app/router/condition.go index 1d43e536c4..4127ca47bc 100644 --- a/xray-core/app/router/condition.go +++ b/xray-core/app/router/condition.go @@ -166,6 +166,8 @@ func (v *PortMatcher) Apply(ctx routing.Context) bool { return v.port.Contains(ctx.GetSourcePort()) case "target": return v.port.Contains(ctx.GetTargetPort()) + case "vlessRoute": + return v.port.Contains(ctx.GetVlessRoute()) default: panic("unreachable, asType should be local or source or target") } diff --git a/xray-core/app/router/config.go b/xray-core/app/router/config.go index f8140c268f..b7338e351e 100644 --- a/xray-core/app/router/config.go +++ b/xray-core/app/router/config.go @@ -45,6 +45,10 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) { conds.Add(NewUserMatcher(rr.UserEmail)) } + if rr.VlessRouteList != nil { + conds.Add(NewPortMatcher(rr.VlessRouteList, "vlessRoute")) + } + if len(rr.InboundTag) > 0 { conds.Add(NewInboundTagMatcher(rr.InboundTag)) } diff --git a/xray-core/app/router/config.pb.go b/xray-core/app/router/config.pb.go index 5e10010223..046e689624 100644 --- a/xray-core/app/router/config.pb.go +++ b/xray-core/app/router/config.pb.go @@ -493,6 +493,7 @@ type RoutingRule struct { Attributes map[string]string `protobuf:"bytes,15,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` LocalGeoip []*GeoIP `protobuf:"bytes,17,rep,name=local_geoip,json=localGeoip,proto3" json:"local_geoip,omitempty"` LocalPortList *net.PortList `protobuf:"bytes,18,opt,name=local_port_list,json=localPortList,proto3" json:"local_port_list,omitempty"` + VlessRouteList *net.PortList `protobuf:"bytes,20,opt,name=vless_route_list,json=vlessRouteList,proto3" json:"vless_route_list,omitempty"` } func (x *RoutingRule) Reset() { @@ -637,6 +638,13 @@ func (x *RoutingRule) GetLocalPortList() *net.PortList { return nil } +func (x *RoutingRule) GetVlessRouteList() *net.PortList { + if x != nil { + return x.VlessRouteList + } + return nil +} + type isRoutingRule_TargetTag interface { isRoutingRule_TargetTag() } @@ -1077,7 +1085,7 @@ var file_app_router_config_proto_rawDesc = []byte{ 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69, - 0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xa3, 0x06, 0x0a, 0x0b, 0x52, 0x6f, + 0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xe8, 0x06, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x0c, @@ -1123,66 +1131,71 @@ var file_app_router_config_proto_rawDesc = []byte{ 0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, - 0x73, 0x74, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x22, - 0xdc, 0x01, 0x0a, 0x0d, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x74, 0x61, 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, - 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, - 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x4d, 0x0a, 0x11, - 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, - 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x10, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x66, - 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x67, 0x22, 0x54, - 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x06, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, - 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x17, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x35, 0x0a, 0x05, 0x63, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x52, 0x05, 0x63, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, - 0x69, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x73, 0x65, - 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x54, 0x54, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x54, 0x54, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x6f, 0x6c, - 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x74, 0x6f, - 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x9b, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x12, 0x30, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, - 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, - 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, - 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, - 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x62, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x22, 0x47, 0x0a, 0x0e, - 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, - 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x49, - 0x70, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66, 0x4e, 0x6f, 0x6e, 0x4d, 0x61, - 0x74, 0x63, 0x68, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70, 0x4f, 0x6e, 0x44, 0x65, 0x6d, - 0x61, 0x6e, 0x64, 0x10, 0x03, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, - 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x24, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, - 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x74, 0x12, 0x43, 0x0a, 0x10, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, + 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x5f, 0x74, 0x61, 0x67, 0x22, 0xdc, 0x01, 0x0a, 0x0d, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, + 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x12, 0x4d, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x5f, 0x73, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, + 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x10, + 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x74, 0x61, 0x67, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, + 0x54, 0x61, 0x67, 0x22, 0x54, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x57, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x12, 0x14, 0x0a, + 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x02, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x17, 0x53, 0x74, + 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x4c, 0x6f, 0x61, 0x64, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x05, 0x63, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x57, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x05, 0x63, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, + 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x03, 0x52, + 0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, + 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, + 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x54, 0x54, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x54, 0x54, 0x12, 0x1c, + 0x0a, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x02, 0x52, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x9b, 0x02, 0x0a, + 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, + 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, + 0x52, 0x75, 0x6c, 0x65, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, + 0x6c, 0x65, 0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, + 0x65, 0x22, 0x47, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x09, 0x0a, + 0x05, 0x55, 0x73, 0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66, + 0x4e, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70, + 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, + 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, + 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, + 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -1235,16 +1248,17 @@ var file_app_router_config_proto_depIdxs = []int32{ 14, // 12: xray.app.router.RoutingRule.attributes:type_name -> xray.app.router.RoutingRule.AttributesEntry 4, // 13: xray.app.router.RoutingRule.local_geoip:type_name -> xray.app.router.GeoIP 15, // 14: xray.app.router.RoutingRule.local_port_list:type_name -> xray.common.net.PortList - 17, // 15: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage - 10, // 16: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight - 1, // 17: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy - 8, // 18: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule - 9, // 19: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule - 20, // [20:20] is the sub-list for method output_type - 20, // [20:20] is the sub-list for method input_type - 20, // [20:20] is the sub-list for extension type_name - 20, // [20:20] is the sub-list for extension extendee - 0, // [0:20] is the sub-list for field type_name + 15, // 15: xray.app.router.RoutingRule.vless_route_list:type_name -> xray.common.net.PortList + 17, // 16: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage + 10, // 17: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight + 1, // 18: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy + 8, // 19: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule + 9, // 20: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule + 21, // [21:21] is the sub-list for method output_type + 21, // [21:21] is the sub-list for method input_type + 21, // [21:21] is the sub-list for extension type_name + 21, // [21:21] is the sub-list for extension extendee + 0, // [0:21] is the sub-list for field type_name } func init() { file_app_router_config_proto_init() } diff --git a/xray-core/app/router/config.proto b/xray-core/app/router/config.proto index d357c98a1f..20dda4ba66 100644 --- a/xray-core/app/router/config.proto +++ b/xray-core/app/router/config.proto @@ -111,6 +111,8 @@ message RoutingRule { repeated GeoIP local_geoip = 17; xray.common.net.PortList local_port_list = 18; + + xray.common.net.PortList vless_route_list = 20; } message BalancingRule { diff --git a/xray-core/common/mux/server.go b/xray-core/common/mux/server.go index 30dcf06e4f..99a144a51e 100644 --- a/xray-core/common/mux/server.go +++ b/xray-core/common/mux/server.go @@ -118,9 +118,7 @@ func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.Bu } func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error { - // deep-clone outbounds because it is going to be mutated concurrently - // (Target and OriginalTarget) - ctx = session.ContextCloneOutboundsAndContent(ctx) + ctx = session.SubContextFromMuxInbound(ctx) errors.LogInfo(ctx, "received request for ", meta.Target) { msg := &log.AccessMessage{ diff --git a/xray-core/common/session/context.go b/xray-core/common/session/context.go index 0e2558c2c2..ba3530b5bb 100644 --- a/xray-core/common/session/context.go +++ b/xray-core/common/session/context.go @@ -16,15 +16,15 @@ const ( inboundSessionKey ctx.SessionKey = 1 outboundSessionKey ctx.SessionKey = 2 contentSessionKey ctx.SessionKey = 3 - muxPreferredSessionKey ctx.SessionKey = 4 - sockoptSessionKey ctx.SessionKey = 5 - trackedConnectionErrorKey ctx.SessionKey = 6 - dispatcherKey ctx.SessionKey = 7 - timeoutOnlyKey ctx.SessionKey = 8 - allowedNetworkKey ctx.SessionKey = 9 - handlerSessionKey ctx.SessionKey = 10 - mitmAlpn11Key ctx.SessionKey = 11 - mitmServerNameKey ctx.SessionKey = 12 + muxPreferredSessionKey ctx.SessionKey = 4 // unused + sockoptSessionKey ctx.SessionKey = 5 // used by dokodemo to only receive sockopt.Mark + trackedConnectionErrorKey ctx.SessionKey = 6 // used by observer to get outbound error + dispatcherKey ctx.SessionKey = 7 // used by ss2022 inbounds to get dispatcher + timeoutOnlyKey ctx.SessionKey = 8 // mux context's child contexts to only cancel when its own traffic times out + allowedNetworkKey ctx.SessionKey = 9 // muxcool server control incoming request tcp/udp + handlerSessionKey ctx.SessionKey = 10 // unused + mitmAlpn11Key ctx.SessionKey = 11 // used by TLS dialer + mitmServerNameKey ctx.SessionKey = 12 // used by TLS dialer ) func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context { @@ -42,18 +42,8 @@ func ContextWithOutbounds(ctx context.Context, outbounds []*Outbound) context.Co return context.WithValue(ctx, outboundSessionKey, outbounds) } -func ContextCloneOutboundsAndContent(ctx context.Context) context.Context { - outbounds := OutboundsFromContext(ctx) - newOutbounds := make([]*Outbound, len(outbounds)) - for i, ob := range outbounds { - if ob == nil { - continue - } - - // copy outbound by value - v := *ob - newOutbounds[i] = &v - } +func SubContextFromMuxInbound(ctx context.Context) context.Context { + newOutbounds := []*Outbound{{}} content := ContentFromContext(ctx) newContent := Content{} diff --git a/xray-core/common/session/session.go b/xray-core/common/session/session.go index d341cc63d7..aad31cdfeb 100644 --- a/xray-core/common/session/session.go +++ b/xray-core/common/session/session.go @@ -46,9 +46,11 @@ type Inbound struct { Name string // User is the user that authenticates for the inbound. May be nil if the protocol allows anonymous traffic. User *protocol.MemoryUser - // Conn is actually internet.Connection. May be nil. + // VlessRoute is the user-sent VLESS UUID's last byte. + VlessRoute net.Port + // Used by splice copy. Conn is actually internet.Connection. May be nil. Conn net.Conn - // Timer of the inbound buf copier. May be nil. + // Used by splice copy. Timer of the inbound buf copier. May be nil. Timer *signal.ActivityTimer // CanSpliceCopy is a property for this connection // 1 = can, 2 = after processing protocol info should be able to, 3 = cannot @@ -67,31 +69,33 @@ type Outbound struct { Tag string // Name of the outbound proxy that handles the connection. Name string - // Conn is actually internet.Connection. May be nil. It is currently nil for outbound with proxySettings + // Unused. Conn is actually internet.Connection. May be nil. It is currently nil for outbound with proxySettings Conn net.Conn // CanSpliceCopy is a property for this connection // 1 = can, 2 = after processing protocol info should be able to, 3 = cannot CanSpliceCopy int } -// SniffingRequest controls the behavior of content sniffing. +// SniffingRequest controls the behavior of content sniffing. They are from inbound config. Read-only type SniffingRequest struct { - ExcludeForDomain []string // read-only once set - OverrideDestinationForProtocol []string // read-only once set + ExcludeForDomain []string + OverrideDestinationForProtocol []string Enabled bool MetadataOnly bool RouteOnly bool } -// Content is the metadata of the connection content. +// Content is the metadata of the connection content. Mainly used for routing. type Content struct { // Protocol of current content. Protocol string SniffingRequest SniffingRequest + // HTTP traffic sniffed headers Attributes map[string]string + // SkipDNSResolve is set from DNS module. the DOH remote server maybe a domain name, this prevents cycle resolving dead loop SkipDNSResolve bool } diff --git a/xray-core/features/routing/context.go b/xray-core/features/routing/context.go index 77ec0d50c9..d876e5bb1f 100644 --- a/xray-core/features/routing/context.go +++ b/xray-core/features/routing/context.go @@ -41,6 +41,9 @@ type Context interface { // GetUser returns the user email from the connection content, if exists. GetUser() string + // GetVlessRoute returns the user-sent VLESS UUID's last byte, if exists. + GetVlessRoute() net.Port + // GetAttributes returns extra attributes from the conneciont content. GetAttributes() map[string]string diff --git a/xray-core/features/routing/session/context.go b/xray-core/features/routing/session/context.go index 54ba907c48..45fa8d8f44 100644 --- a/xray-core/features/routing/session/context.go +++ b/xray-core/features/routing/session/context.go @@ -128,6 +128,14 @@ func (ctx *Context) GetUser() string { return ctx.Inbound.User.Email } +// GetVlessRoute implements routing.Context. +func (ctx *Context) GetVlessRoute() net.Port { + if ctx.Inbound == nil { + return 0 + } + return ctx.Inbound.VlessRoute +} + // GetAttributes implements routing.Context. func (ctx *Context) GetAttributes() map[string]string { if ctx.Content == nil { diff --git a/xray-core/infra/conf/dns_proxy.go b/xray-core/infra/conf/dns_proxy.go index e731ee7c3b..b223e502c3 100644 --- a/xray-core/infra/conf/dns_proxy.go +++ b/xray-core/infra/conf/dns_proxy.go @@ -28,9 +28,7 @@ func (c *DNSOutboundConfig) Build() (proto.Message, error) { config.Server.Address = c.Address.Build() } switch c.NonIPQuery { - case "": - c.NonIPQuery = "drop" - case "drop", "skip", "reject": + case "", "reject", "drop", "skip": default: return nil, errors.New(`unknown "nonIPQuery": `, c.NonIPQuery) } diff --git a/xray-core/infra/conf/dns_proxy_test.go b/xray-core/infra/conf/dns_proxy_test.go index 5c5dfecddd..805ac323a2 100644 --- a/xray-core/infra/conf/dns_proxy_test.go +++ b/xray-core/infra/conf/dns_proxy_test.go @@ -27,7 +27,6 @@ func TestDnsProxyConfig(t *testing.T) { Address: net.NewIPOrDomain(net.IPAddress([]byte{8, 8, 8, 8})), Port: 53, }, - Non_IPQuery: "drop", }, }, }) diff --git a/xray-core/infra/conf/router.go b/xray-core/infra/conf/router.go index 083c51b094..354d31f1de 100644 --- a/xray-core/infra/conf/router.go +++ b/xray-core/infra/conf/router.go @@ -531,6 +531,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { Source *StringList `json:"source"` SourcePort *PortList `json:"sourcePort"` User *StringList `json:"user"` + VlessRoute *PortList `json:"vlessRoute"` InboundTag *StringList `json:"inboundTag"` Protocols *StringList `json:"protocol"` Attributes map[string]string `json:"attrs"` @@ -628,6 +629,10 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { } } + if rawFieldRule.VlessRoute != nil { + rule.VlessRouteList = rawFieldRule.VlessRoute.Build() + } + if rawFieldRule.InboundTag != nil { for _, s := range *rawFieldRule.InboundTag { rule.InboundTag = append(rule.InboundTag, s) diff --git a/xray-core/proxy/dns/dns.go b/xray-core/proxy/dns/dns.go index 0f83f5acfa..40db53a8ab 100644 --- a/xray-core/proxy/dns/dns.go +++ b/xray-core/proxy/dns/dns.go @@ -65,6 +65,9 @@ func (h *Handler) Init(config *Config, dnsClient dns.Client, policyManager polic h.server = config.Server.AsDestination() } h.nonIPQuery = config.Non_IPQuery + if h.nonIPQuery == "" { + h.nonIPQuery = "reject" + } h.blockTypes = config.BlockTypes return nil } diff --git a/xray-core/proxy/vless/encoding/encoding.go b/xray-core/proxy/vless/encoding/encoding.go index 38043e683c..61bfb911b2 100644 --- a/xray-core/proxy/vless/encoding/encoding.go +++ b/xray-core/proxy/vless/encoding/encoding.go @@ -62,7 +62,7 @@ func EncodeRequestHeader(writer io.Writer, request *protocol.RequestHeader, requ } // DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream. -func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validator vless.Validator) (*protocol.RequestHeader, *Addons, bool, error) { +func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validator vless.Validator) ([]byte, *protocol.RequestHeader, *Addons, bool, error) { buffer := buf.StackNew() defer buffer.Release() @@ -72,7 +72,7 @@ func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validat request.Version = first.Byte(0) } else { if _, err := buffer.ReadFullFrom(reader, 1); err != nil { - return nil, nil, false, errors.New("failed to read request version").Base(err) + return nil, nil, nil, false, errors.New("failed to read request version").Base(err) } request.Version = buffer.Byte(0) } @@ -87,13 +87,13 @@ func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validat } else { buffer.Clear() if _, err := buffer.ReadFullFrom(reader, 16); err != nil { - return nil, nil, false, errors.New("failed to read request user id").Base(err) + return nil, nil, nil, false, errors.New("failed to read request user id").Base(err) } copy(id[:], buffer.Bytes()) } if request.User = validator.Get(id); request.User == nil { - return nil, nil, isfb, errors.New("invalid request user id") + return nil, nil, nil, isfb, errors.New("invalid request user id") } if isfb { @@ -102,12 +102,12 @@ func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validat requestAddons, err := DecodeHeaderAddons(&buffer, reader) if err != nil { - return nil, nil, false, errors.New("failed to decode request header addons").Base(err) + return nil, nil, nil, false, errors.New("failed to decode request header addons").Base(err) } buffer.Clear() if _, err := buffer.ReadFullFrom(reader, 1); err != nil { - return nil, nil, false, errors.New("failed to read request command").Base(err) + return nil, nil, nil, false, errors.New("failed to read request command").Base(err) } request.Command = protocol.RequestCommand(buffer.Byte(0)) @@ -122,11 +122,11 @@ func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validat } } if request.Address == nil { - return nil, nil, false, errors.New("invalid request address") + return nil, nil, nil, false, errors.New("invalid request address") } - return request, requestAddons, false, nil + return id[:], request, requestAddons, false, nil default: - return nil, nil, isfb, errors.New("invalid request version") + return nil, nil, nil, isfb, errors.New("invalid request version") } } diff --git a/xray-core/proxy/vless/encoding/encoding_test.go b/xray-core/proxy/vless/encoding/encoding_test.go index 9180154a04..ae55886513 100644 --- a/xray-core/proxy/vless/encoding/encoding_test.go +++ b/xray-core/proxy/vless/encoding/encoding_test.go @@ -45,7 +45,7 @@ func TestRequestSerialization(t *testing.T) { Validator := new(vless.MemoryValidator) Validator.Add(user) - actualRequest, actualAddons, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) + _, actualRequest, actualAddons, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) common.Must(err) if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" { @@ -86,7 +86,7 @@ func TestInvalidRequest(t *testing.T) { Validator := new(vless.MemoryValidator) Validator.Add(user) - _, _, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) + _, _, _, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) if err == nil { t.Error("nil error") } @@ -117,7 +117,7 @@ func TestMuxRequest(t *testing.T) { Validator := new(vless.MemoryValidator) Validator.Add(user) - actualRequest, actualAddons, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) + _, actualRequest, actualAddons, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) common.Must(err) if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" { diff --git a/xray-core/proxy/vless/inbound/inbound.go b/xray-core/proxy/vless/inbound/inbound.go index df1f9f3cae..5359405e8f 100644 --- a/xray-core/proxy/vless/inbound/inbound.go +++ b/xray-core/proxy/vless/inbound/inbound.go @@ -217,6 +217,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s Buffer: buf.MultiBuffer{first}, } + var userSentID []byte // not MemoryAccount.ID var request *protocol.RequestHeader var requestAddons *encoding.Addons var err error @@ -227,7 +228,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s if isfb && firstLen < 18 { err = errors.New("fallback directly") } else { - request, requestAddons, isfb, err = encoding.DecodeRequestHeader(isfb, first, reader, h.validator) + userSentID, request, requestAddons, isfb, err = encoding.DecodeRequestHeader(isfb, first, reader, h.validator) } if err != nil { @@ -455,6 +456,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s } inbound.Name = "vless" inbound.User = request.User + inbound.VlessRoute = net.Port(userSentID[15]) account := request.User.Account.(*vless.MemoryAccount) @@ -530,7 +532,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s serverReader := link.Reader // .(*pipe.Reader) serverWriter := link.Writer // .(*pipe.Writer) - trafficState := proxy.NewTrafficState(account.ID.Bytes()) + trafficState := proxy.NewTrafficState(userSentID) postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) diff --git a/xray-core/proxy/vless/validator.go b/xray-core/proxy/vless/validator.go index d1356c5f4d..61d5e0256d 100644 --- a/xray-core/proxy/vless/validator.go +++ b/xray-core/proxy/vless/validator.go @@ -33,7 +33,7 @@ func (v *MemoryValidator) Add(u *protocol.MemoryUser) error { return errors.New("User ", u.Email, " already exists.") } } - v.users.Store(u.Account.(*MemoryAccount).ID.UUID(), u) + v.users.Store([15]byte(u.Account.(*MemoryAccount).ID.Bytes()), u) return nil } @@ -48,13 +48,13 @@ func (v *MemoryValidator) Del(e string) error { return errors.New("User ", e, " not found.") } v.email.Delete(le) - v.users.Delete(u.(*protocol.MemoryUser).Account.(*MemoryAccount).ID.UUID()) + v.users.Delete([15]byte(u.(*protocol.MemoryUser).Account.(*MemoryAccount).ID.Bytes())) return nil } // Get a VLESS user with UUID, nil if user doesn't exist. func (v *MemoryValidator) Get(id uuid.UUID) *protocol.MemoryUser { - u, _ := v.users.Load(id) + u, _ := v.users.Load([15]byte(id[:])) if u != nil { return u.(*protocol.MemoryUser) } diff --git a/xray-core/proxy/wireguard/server.go b/xray-core/proxy/wireguard/server.go index bdf2756854..6144f5c751 100644 --- a/xray-core/proxy/wireguard/server.go +++ b/xray-core/proxy/wireguard/server.go @@ -7,6 +7,7 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" + c "github.com/xtls/xray-core/common/ctx" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" @@ -33,7 +34,6 @@ type routingInfo struct { ctx context.Context dispatcher routing.Dispatcher inboundTag *session.Inbound - outboundTag *session.Outbound contentTag *session.Content } @@ -78,18 +78,11 @@ func (*Server) Network() []net.Network { // Process implements proxy.Inbound. func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { - inbound := session.InboundFromContext(ctx) - inbound.Name = "wireguard" - inbound.CanSpliceCopy = 3 - outbounds := session.OutboundsFromContext(ctx) - ob := outbounds[len(outbounds)-1] - s.info = routingInfo{ - ctx: core.ToBackgroundDetachedContext(ctx), - dispatcher: dispatcher, - inboundTag: session.InboundFromContext(ctx), - outboundTag: ob, - contentTag: session.ContentFromContext(ctx), + ctx: ctx, + dispatcher: dispatcher, + inboundTag: session.InboundFromContext(ctx), + contentTag: session.ContentFromContext(ctx), } ep, err := s.bindServer.ParseEndpoint(conn.RemoteAddr().String()) @@ -134,6 +127,25 @@ func (s *Server) forwardConnection(dest net.Destination, conn net.Conn) { defer conn.Close() ctx, cancel := context.WithCancel(core.ToBackgroundDetachedContext(s.info.ctx)) + sid := session.NewID() + ctx = c.ContextWithID(ctx, sid) + inbound := session.Inbound{} // since promiscuousModeHandler mixed-up context, we shallow copy inbound (tag) and content (configs) + if s.info.inboundTag != nil { + inbound = *s.info.inboundTag + } + inbound.Name = "wireguard" + inbound.CanSpliceCopy = 3 + + // overwrite the source to use the tun address for each sub context. + // Since gvisor.ForwarderRequest doesn't provide any info to associate the sub-context with the Parent context + // Currently we have no way to link to the original source address + inbound.Source = net.DestinationFromAddr(conn.RemoteAddr()) + ctx = session.ContextWithInbound(ctx, &inbound) + if s.info.contentTag != nil { + ctx = session.ContextWithContent(ctx, s.info.contentTag) + } + ctx = session.SubContextFromMuxInbound(ctx) + plcy := s.policyManager.ForLevel(0) timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle) @@ -144,25 +156,9 @@ func (s *Server) forwardConnection(dest net.Destination, conn net.Conn) { Reason: "", }) - if s.info.inboundTag != nil { - ctx = session.ContextWithInbound(ctx, s.info.inboundTag) - } - - // what's this? - // Session information should not be shared between different connections - // why reuse them in server level? This will cause incorrect destoverride and unexpected routing behavior. - // Disable it temporarily. Maybe s.info should be removed. - - // if s.info.outboundTag != nil { - // ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{s.info.outboundTag}) - // } - // if s.info.contentTag != nil { - // ctx = session.ContextWithContent(ctx, s.info.contentTag) - // } - link, err := s.info.dispatcher.Dispatch(ctx, dest) if err != nil { - errors.LogErrorInner(s.info.ctx, err, "dispatch connection") + errors.LogErrorInner(ctx, err, "dispatch connection") } defer cancel() @@ -188,7 +184,7 @@ func (s *Server) forwardConnection(dest net.Destination, conn net.Conn) { if err := task.Run(ctx, requestDonePost, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) - errors.LogDebugInner(s.info.ctx, err, "connection ends") + errors.LogDebugInner(ctx, err, "connection ends") return } } diff --git a/yt-dlp/yt_dlp/__init__.py b/yt-dlp/yt_dlp/__init__.py index bc9384b085..3277cbfa1a 100644 --- a/yt-dlp/yt_dlp/__init__.py +++ b/yt-dlp/yt_dlp/__init__.py @@ -500,6 +500,14 @@ def validate_options(opts): 'To let yt-dlp download and merge the best available formats, simply do not pass any format selection', 'If you know what you are doing and want only the best pre-merged format, use "-f b" instead to suppress this warning'))) + # Common mistake: -f mp4 + if opts.format == 'mp4': + warnings.append('.\n '.join(( + '"-f mp4" selects the best pre-merged mp4 format which is often not what\'s intended', + 'Pre-merged mp4 formats are not available from all sites, or may only be available in lower quality', + 'To prioritize the best h264 video and aac audio in an mp4 container, use "-t mp4" instead', + 'If you know what you are doing and want a pre-merged mp4 format, use "-f b[ext=mp4]" instead to suppress this warning'))) + # --(postprocessor/downloader)-args without name def report_args_compat(name, value, key1, key2=None, where=None): if key1 in value and key2 not in value: diff --git a/yt-dlp/yt_dlp/extractor/tiktok.py b/yt-dlp/yt_dlp/extractor/tiktok.py index d9280cec14..18407a0820 100644 --- a/yt-dlp/yt_dlp/extractor/tiktok.py +++ b/yt-dlp/yt_dlp/extractor/tiktok.py @@ -65,7 +65,7 @@ class TikTokBaseIE(InfoExtractor): @functools.cached_property def _DEVICE_ID(self): - return self._KNOWN_DEVICE_ID or str(random.randint(7250000000000000000, 7351147085025500000)) + return self._KNOWN_DEVICE_ID or str(random.randint(7250000000000000000, 7325099899999994577)) @functools.cached_property def _API_HOSTNAME(self): @@ -942,7 +942,6 @@ class TikTokUserIE(TikTokBaseIE): 'id': 'MS4wLjABAAAAM3R2BtjzVT-uAtstkl2iugMzC6AtnpkojJbjiOdDDrdsTiTR75-8lyWJCY5VvDrZ', }, }] - _USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:115.0) Gecko/20100101 Firefox/115.0' _API_BASE_URL = 'https://www.tiktok.com/api/creator/item_list/' def _build_web_query(self, sec_uid, cursor): @@ -986,9 +985,23 @@ class TikTokUserIE(TikTokBaseIE): cursor = int(time.time() * 1E3) for page in itertools.count(1): - response = self._download_json( - self._API_BASE_URL, display_id, f'Downloading page {page}', - query=self._build_web_query(sec_uid, cursor), headers={'User-Agent': self._USER_AGENT}) + for retry in self.RetryManager(): + response = self._download_json( + self._API_BASE_URL, display_id, f'Downloading page {page}', + query=self._build_web_query(sec_uid, cursor)) + + # Avoid infinite loop caused by bad device_id + # See: https://github.com/yt-dlp/yt-dlp/issues/14031 + current_batch = sorted(traverse_obj(response, ('itemList', ..., 'id', {str}))) + if current_batch and current_batch == sorted(seen_ids): + message = 'TikTok API keeps sending the same page' + if self._KNOWN_DEVICE_ID: + raise ExtractorError( + f'{message}. Try again with a different device_id', expected=True) + # The user didn't pass a device_id so we can reset it and retry + del self._DEVICE_ID + retry.error = ExtractorError( + f'{message}. Taking measures to avoid an infinite loop', expected=True) for video in traverse_obj(response, ('itemList', lambda _, v: v['id'])): video_id = video['id'] @@ -1008,42 +1021,52 @@ class TikTokUserIE(TikTokBaseIE): cursor = old_cursor - 7 * 86_400_000 # In case 'hasMorePrevious' is wrong, break if we have gone back before TikTok existed if cursor < 1472706000000 or not traverse_obj(response, 'hasMorePrevious'): - break + return - def _get_sec_uid(self, user_url, user_name, msg): + # User directly passed sec_uid via prefix URL, bypassing our private account detection + if not user_name and not seen_ids: + self.raise_login_required( + 'This user\'s account is likely private. Log into an account that has access') + + def _extract_sec_uid_from_embed(self, user_name): webpage = self._download_webpage( - user_url, user_name, fatal=False, headers={'User-Agent': 'Mozilla/5.0'}, - note=f'Downloading {msg} webpage', errnote=f'Unable to download {msg} webpage') or '' - return (traverse_obj(self._get_universal_data(webpage, user_name), - ('webapp.user-detail', 'userInfo', 'user', 'secUid', {str})) - or traverse_obj(self._get_sigi_state(webpage, user_name), - ('LiveRoom', 'liveRoomUserInfo', 'user', 'secUid', {str}), - ('UserModule', 'users', ..., 'secUid', {str}, any))) + f'https://www.tiktok.com/embed/@{user_name}', user_name, + 'Downloading user embed page', errnote=False, fatal=False) + if not webpage: + self.report_warning('This user\'s account is either private or has embedding disabled') + return None + + data = traverse_obj(self._search_json( + r']+\bid=[\'"]__FRONTITY_CONNECT_STATE__[\'"][^>]*>', + webpage, 'data', user_name, default={}), + ('source', 'data', f'/embed/@{user_name}', {dict})) + + for aweme_id in traverse_obj(data, ('videoList', ..., 'id', {str})): + webpage_url = self._create_url(user_name, aweme_id) + video_data, _ = self._extract_web_data_and_status(webpage_url, aweme_id, fatal=False) + sec_uid = self._parse_aweme_video_web( + video_data, webpage_url, aweme_id, extract_flat=True).get('channel_id') + if sec_uid: + return sec_uid + + return None def _real_extract(self, url): user_name, sec_uid = self._match_id(url), None if mobj := re.fullmatch(r'MS4wLjABAAAA[\w-]{64}', user_name): user_name, sec_uid = None, mobj.group(0) else: - sec_uid = (self._get_sec_uid(self._UPLOADER_URL_FORMAT % user_name, user_name, 'user') - or self._get_sec_uid(self._UPLOADER_URL_FORMAT % f'{user_name}/live', user_name, 'live')) - - if not sec_uid: webpage = self._download_webpage( - f'https://www.tiktok.com/embed/@{user_name}', user_name, - note='Downloading user embed page', fatal=False) or '' - data = traverse_obj(self._search_json( - r']+\bid=[\'"]__FRONTITY_CONNECT_STATE__[\'"][^>]*>', - webpage, 'data', user_name, default={}), - ('source', 'data', f'/embed/@{user_name}', {dict})) - - for aweme_id in traverse_obj(data, ('videoList', ..., 'id', {str})): - webpage_url = self._create_url(user_name, aweme_id) - video_data, _ = self._extract_web_data_and_status(webpage_url, aweme_id, fatal=False) - sec_uid = self._parse_aweme_video_web( - video_data, webpage_url, aweme_id, extract_flat=True).get('channel_id') - if sec_uid: - break + self._UPLOADER_URL_FORMAT % user_name, user_name, + 'Downloading user webpage', 'Unable to download user webpage', + fatal=False, headers={'User-Agent': 'Mozilla/5.0'}) or '' + detail = traverse_obj( + self._get_universal_data(webpage, user_name), ('webapp.user-detail', {dict})) or {} + if detail.get('statusCode') == 10222: + self.raise_login_required( + 'This user\'s account is private. Log into an account that has access') + sec_uid = traverse_obj(detail, ( + 'userInfo', 'user', 'secUid', {str})) or self._extract_sec_uid_from_embed(user_name) if not sec_uid: raise ExtractorError( diff --git a/yt-dlp/yt_dlp/extractor/weibo.py b/yt-dlp/yt_dlp/extractor/weibo.py index d5210850cc..dc8a2cd753 100644 --- a/yt-dlp/yt_dlp/extractor/weibo.py +++ b/yt-dlp/yt_dlp/extractor/weibo.py @@ -8,6 +8,7 @@ from ..utils import ( int_or_none, make_archive_id, mimetype2ext, + parse_qs, parse_resolution, str_or_none, strip_jsonp, @@ -209,7 +210,11 @@ class WeiboIE(WeiboBaseIE): class WeiboVideoIE(WeiboBaseIE): - _VALID_URL = r'https?://(?:www\.)?weibo\.com/tv/show/(?P\d+:(?:[\da-f]{32}|\d{16,}))' + _VIDEO_ID_RE = r'\d+:(?:[\da-f]{32}|\d{16,})' + _VALID_URL = [ + fr'https?://(?:www\.)?weibo\.com/tv/show/(?P{_VIDEO_ID_RE})', + fr'https?://video\.weibo\.com/show/?\?(?:[^#]+&)?fid=(?P{_VIDEO_ID_RE})', + ] _TESTS = [{ 'url': 'https://weibo.com/tv/show/1034:4797699866951785?from=old_pc_videoshow', 'info_dict': { @@ -252,6 +257,28 @@ class WeiboVideoIE(WeiboBaseIE): 'upload_date': '20171226', '_old_archive_ids': ['weibomobile 4189191225395228'], }, + }, { + 'url': 'https://video.weibo.com/show?fid=1034:4967272104787984', + 'info_dict': { + 'id': '4967273022359838', + 'ext': 'mp4', + 'display_id': 'Nse4S9TTU', + 'title': '#张婧仪[超话]#📸#婧仪的相册集#  早收工的一天,小张@张婧仪 变身可可爱爱小导游,来次说走就走的泉州City Walk[举手]', + 'alt_title': '#张婧仪[超话]#📸#婧仪的相册集# \n早收工的一天,小张@张婧仪 变身可可爱爱小导游,来次说走就走的泉州City Walk[举手]', + 'description': '#张婧仪[超话]#📸#婧仪的相册集# \n早收工的一天,小张@张婧仪 变身可可爱爱小导游,来次说走就走的泉州City Walk[举手] http://t.cn/A6WTpbEu \u200B\u200B\u200B', + 'uploader': '张婧仪工作室', + 'uploader_id': '7610808848', + 'uploader_url': 'https://weibo.com/u/7610808848', + 'view_count': int, + 'like_count': int, + 'repost_count': int, + 'duration': 85, + 'thumbnail': 'https://wx2.sinaimg.cn/orj480/008j4b3qly1hjsce01gnqj30u00gvwf8.jpg', + 'tags': ['婧仪的相册集'], + 'timestamp': 1699773545, + 'upload_date': '20231112', + '_old_archive_ids': ['weibomobile 4967273022359838'], + }, }] def _real_extract(self, url): @@ -275,6 +302,38 @@ class WeiboUserIE(WeiboBaseIE): 'uploader': '萧影殿下', }, 'playlist_mincount': 195, + }, { + 'url': 'https://weibo.com/u/7610808848?tabtype=newVideo&layerid=4967273022359838', + 'info_dict': { + 'id': '7610808848', + 'title': '张婧仪工作室的视频', + 'description': '张婧仪工作室的全部视频', + 'uploader': '张婧仪工作室', + }, + 'playlist_mincount': 61, + }, { + 'url': 'https://weibo.com/u/7610808848?tabtype=newVideo&layerid=4967273022359838', + 'info_dict': { + 'id': '4967273022359838', + 'ext': 'mp4', + 'display_id': 'Nse4S9TTU', + 'title': '#张婧仪[超话]#📸#婧仪的相册集#  早收工的一天,小张@张婧仪 变身可可爱爱小导游,来次说走就走的泉州City Walk[举手]', + 'alt_title': '#张婧仪[超话]#📸#婧仪的相册集# \n早收工的一天,小张@张婧仪 变身可可爱爱小导游,来次说走就走的泉州City Walk[举手]', + 'description': '#张婧仪[超话]#📸#婧仪的相册集# \n早收工的一天,小张@张婧仪 变身可可爱爱小导游,来次说走就走的泉州City Walk[举手] http://t.cn/A6WTpbEu \u200B\u200B\u200B', + 'uploader': '张婧仪工作室', + 'uploader_id': '7610808848', + 'uploader_url': 'https://weibo.com/u/7610808848', + 'view_count': int, + 'like_count': int, + 'repost_count': int, + 'duration': 85, + 'thumbnail': 'https://wx2.sinaimg.cn/orj480/008j4b3qly1hjsce01gnqj30u00gvwf8.jpg', + 'tags': ['婧仪的相册集'], + 'timestamp': 1699773545, + 'upload_date': '20231112', + '_old_archive_ids': ['weibomobile 4967273022359838'], + }, + 'params': {'noplaylist': True}, }] def _fetch_page(self, uid, cursor=0, page=1): @@ -295,6 +354,11 @@ class WeiboUserIE(WeiboBaseIE): def _real_extract(self, url): uid = self._match_id(url) + params = {k: v[-1] for k, v in parse_qs(url).items()} + video_id = params.get('layerid') if params.get('tabtype') == 'newVideo' else None + if not self._yes_playlist(uid, video_id): + return self.url_result(f'https://weibo.com/{uid}/{video_id}', WeiboIE, video_id) + first_page = self._fetch_page(uid) uploader = traverse_obj(first_page, ('list', ..., 'user', 'screen_name', {str}), get_all=False) metainfo = { diff --git a/yt-dlp/yt_dlp/extractor/youtube/_tab.py b/yt-dlp/yt_dlp/extractor/youtube/_tab.py index 226e5ede3b..5870786978 100644 --- a/yt-dlp/yt_dlp/extractor/youtube/_tab.py +++ b/yt-dlp/yt_dlp/extractor/youtube/_tab.py @@ -566,6 +566,7 @@ class YoutubeTabBaseInfoExtractor(YoutubeBaseInfoExtractor): 'gridContinuation': (self._grid_entries, None), 'itemSectionContinuation': (self._post_thread_continuation_entries, None), 'sectionListContinuation': (extract_entries, None), # for feeds + 'lockupViewModel': (self._grid_entries, 'items'), # for playlists tab } continuation_items = traverse_obj(response, ( @@ -1026,7 +1027,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): 'info_dict': { 'id': 'UCqj7Cz7revf5maW9g5pgNcg', 'title': 'Igor Kleiner - Playlists', - 'description': 'md5:15d7dd9e333cb987907fcb0d604b233a', + 'description': r're:(?s)Добро пожаловать на мой канал! Здесь вы найдете видео .{504}/a1/50b/10a$', 'uploader': 'Igor Kleiner ', 'uploader_id': '@IgorDataScience', 'uploader_url': 'https://www.youtube.com/@IgorDataScience', @@ -1043,7 +1044,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): 'info_dict': { 'id': 'UCqj7Cz7revf5maW9g5pgNcg', 'title': 'Igor Kleiner - Playlists', - 'description': 'md5:15d7dd9e333cb987907fcb0d604b233a', + 'description': r're:(?s)Добро пожаловать на мой канал! Здесь вы найдете видео .{504}/a1/50b/10a$', 'uploader': 'Igor Kleiner ', 'uploader_id': '@IgorDataScience', 'uploader_url': 'https://www.youtube.com/@IgorDataScience', @@ -1093,7 +1094,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): 'url': 'https://www.youtube.com/c/ChristophLaimer/playlists', 'only_matching': True, }, { - # TODO: fix availability extraction + # TODO: fix availability and view_count extraction 'note': 'basic, single video playlist', 'url': 'https://www.youtube.com/playlist?list=PLt5yu3-wZAlSLRHmI1qNm0wjyVNWw1pCU', 'info_dict': { @@ -1215,6 +1216,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): 'uploader': 'lex will', }, 'playlist_mincount': 18, + 'skip': 'This Community isn\'t available', }, { # TODO: fix channel_is_verified extraction 'note': 'Search tab', @@ -1399,7 +1401,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): }, { 'url': 'https://www.youtube.com/channel/UCoMdktPbSTixAyNGwb-UYkQ/live', 'info_dict': { - 'id': 'YDvsBbKfLPA', # This will keep changing + 'id': 'VFGoUmo74wE', # This will keep changing 'ext': 'mp4', 'title': str, 'upload_date': r're:\d{8}', @@ -1578,6 +1580,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): 'playlist_count': 50, 'expected_warnings': ['YouTube Music is not directly supported'], }, { + # TODO: fix test suite, 208163447408c78673b08c172beafe5c310fb167 broke this test 'note': 'unlisted single video playlist', 'url': 'https://www.youtube.com/playlist?list=PLt5yu3-wZAlQLfIN0MMgp0wVV6MP3bM4_', 'info_dict': { @@ -1597,19 +1600,19 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): }, 'playlist': [{ 'info_dict': { - 'title': 'youtube-dl test video "\'/\\ä↭𝕐', - 'id': 'BaW_jenozKc', + 'title': 'Big Buck Bunny 60fps 4K - Official Blender Foundation Short Film', + 'id': 'aqz-KE-bpKQ', '_type': 'url', 'ie_key': 'Youtube', - 'duration': 10, - 'channel_id': 'UCLqxVugv74EIW3VWh2NOa3Q', - 'channel_url': 'https://www.youtube.com/channel/UCLqxVugv74EIW3VWh2NOa3Q', + 'duration': 635, + 'channel_id': 'UCSMOQeBJ2RAnuFungnQOxLg', + 'channel_url': 'https://www.youtube.com/channel/UCSMOQeBJ2RAnuFungnQOxLg', 'view_count': int, - 'url': 'https://www.youtube.com/watch?v=BaW_jenozKc', - 'channel': 'Philipp Hagemeister', - 'uploader_id': '@PhilippHagemeister', - 'uploader_url': 'https://www.youtube.com/@PhilippHagemeister', - 'uploader': 'Philipp Hagemeister', + 'url': 'https://www.youtube.com/watch?v=aqz-KE-bpKQ', + 'channel': 'Blender', + 'uploader_id': '@BlenderOfficial', + 'uploader_url': 'https://www.youtube.com/@BlenderOfficial', + 'uploader': 'Blender', }, }], 'playlist_count': 1, @@ -1675,7 +1678,6 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): 'url': 'https://www.youtube.com/channel/UCwVVpHQ2Cs9iGJfpdFngePQ', 'only_matching': True, }, { - # TODO: fix metadata extraction 'note': 'collaborative playlist (uploader name in the form "by and x other(s)")', 'url': 'https://www.youtube.com/playlist?list=PLx-_-Kk4c89oOHEDQAojOXzEzemXxoqx6', 'info_dict': { @@ -1694,6 +1696,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): 'uploader': 'pukkandan', }, 'playlist_mincount': 2, + 'skip': 'https://github.com/yt-dlp/yt-dlp/issues/13690', }, { 'note': 'translated tab name', 'url': 'https://www.youtube.com/channel/UCiu-3thuViMebBjw_5nWYrA/playlists', @@ -1801,7 +1804,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): 'title': 'Not Just Bikes - Shorts', 'tags': 'count:10', 'channel_url': 'https://www.youtube.com/channel/UC0intLFzLaudFG-xAvUEO-A', - 'description': 'md5:1d9fc1bad7f13a487299d1fe1712e031', + 'description': 'md5:295758591d0d43d8594277be54584da7', 'channel_follower_count': int, 'channel_id': 'UC0intLFzLaudFG-xAvUEO-A', 'channel': 'Not Just Bikes', @@ -1822,7 +1825,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): 'channel_url': 'https://www.youtube.com/channel/UC3eYAvjCVwNHgkaGbXX3sig', 'channel': '中村悠一', 'channel_follower_count': int, - 'description': 'md5:e8fd705073a594f27d6d6d020da560dc', + 'description': 'md5:76b312b48a26c3b0e4d90e2dfc1b417d', 'uploader_url': 'https://www.youtube.com/@Yuichi-Nakamura', 'uploader_id': '@Yuichi-Nakamura', 'uploader': '中村悠一', @@ -1865,12 +1868,13 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): 'tags': [], }, 'playlist_mincount': 30, + 'skip': 'The channel/playlist does not exist and the URL redirected to youtube.com home page', }, { # Trending Gaming Tab. tab id is empty 'url': 'https://www.youtube.com/feed/trending?bp=4gIcGhpnYW1pbmdfY29ycHVzX21vc3RfcG9wdWxhcg%3D%3D', 'info_dict': { 'id': 'trending', - 'title': 'trending - Gaming', + 'title': 'trending', 'tags': [], }, 'playlist_mincount': 30, @@ -2018,7 +2022,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor): 'uploader': 'A Himitsu', 'channel_id': 'UCgFwu-j5-xNJml2FtTrrB3A', 'tags': 'count:12', - 'description': 'I make music', + 'description': 'Music producer, sometimes.', 'channel_url': 'https://www.youtube.com/channel/UCgFwu-j5-xNJml2FtTrrB3A', 'channel_follower_count': int, 'channel_is_verified': True, @@ -2304,19 +2308,20 @@ class YoutubePlaylistIE(YoutubeBaseInfoExtractor): ) IE_NAME = 'youtube:playlist' _TESTS = [{ + # TODO: fix availability extraction 'note': 'issue #673', 'url': 'PLBB231211A4F62143', 'info_dict': { - 'title': '[OLD]Team Fortress 2 (Class-based LP)', + 'title': 'Team Fortress 2 [2010 Version]', 'id': 'PLBB231211A4F62143', - 'uploader': 'Wickman', - 'uploader_id': '@WickmanVT', + 'uploader': 'Wickman Wish', + 'uploader_id': '@WickmanWish', 'description': 'md5:8fa6f52abb47a9552002fa3ddfc57fc2', 'view_count': int, - 'uploader_url': 'https://www.youtube.com/@WickmanVT', + 'uploader_url': 'https://www.youtube.com/@WickmanWish', 'modified_date': r're:\d{8}', 'channel_id': 'UCKSpbfbl5kRQpTdL7kMc-1Q', - 'channel': 'Wickman', + 'channel': 'Wickman Wish', 'tags': [], 'channel_url': 'https://www.youtube.com/channel/UCKSpbfbl5kRQpTdL7kMc-1Q', 'availability': 'public', @@ -2331,6 +2336,7 @@ class YoutubePlaylistIE(YoutubeBaseInfoExtractor): 'playlist_count': 2, 'skip': 'This playlist is private', }, { + # TODO: fix availability extraction 'note': 'embedded', 'url': 'https://www.youtube.com/embed/videoseries?list=PL6IaIsEjSbf96XFRuNccS_RuEXwNdsoEu', 'playlist_count': 4, @@ -2351,6 +2357,7 @@ class YoutubePlaylistIE(YoutubeBaseInfoExtractor): }, 'expected_warnings': [r'[Uu]navailable videos? (is|are|will be) hidden', 'Retrying', 'Giving up'], }, { + # TODO: fix availability extraction 'url': 'http://www.youtube.com/embed/_xDOZElKyNU?list=PLsyOSbh5bs16vubvKePAQ1x3PhKavfBIl', 'playlist_mincount': 455, 'info_dict': {