mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 05:13:44 +08:00
refactor: wip
This commit is contained in:
parent
01dfe83fcd
commit
401e6b5a57
328
src-tauri/Cargo.lock
generated
328
src-tauri/Cargo.lock
generated
@ -14,6 +14,17 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.6",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.18"
|
version = "0.7.18"
|
||||||
@ -59,6 +70,115 @@ version = "0.4.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8"
|
checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-channel"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"event-listener",
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-executor"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
|
||||||
|
dependencies = [
|
||||||
|
"async-task",
|
||||||
|
"concurrent-queue",
|
||||||
|
"fastrand",
|
||||||
|
"futures-lite",
|
||||||
|
"once_cell",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-fs"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2"
|
||||||
|
dependencies = [
|
||||||
|
"async-lock",
|
||||||
|
"blocking",
|
||||||
|
"futures-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-io"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"futures-lite",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"parking",
|
||||||
|
"polling",
|
||||||
|
"slab",
|
||||||
|
"socket2",
|
||||||
|
"waker-fn",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-lock"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
|
||||||
|
dependencies = [
|
||||||
|
"event-listener",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-net"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5373304df79b9b4395068fb080369ec7178608827306ce4d081cba51cac551df"
|
||||||
|
dependencies = [
|
||||||
|
"async-io",
|
||||||
|
"blocking",
|
||||||
|
"futures-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-process"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83137067e3a2a6a06d67168e49e68a0957d215410473a740cea95a2425c0b7c6"
|
||||||
|
dependencies = [
|
||||||
|
"async-io",
|
||||||
|
"blocking",
|
||||||
|
"cfg-if",
|
||||||
|
"event-listener",
|
||||||
|
"futures-lite",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"signal-hook",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-task"
|
||||||
|
version = "4.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atk"
|
name = "atk"
|
||||||
version = "0.15.1"
|
version = "0.15.1"
|
||||||
@ -83,6 +203,12 @@ dependencies = [
|
|||||||
"system-deps 6.0.2",
|
"system-deps 6.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-waker"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "attohttpc"
|
name = "attohttpc"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
@ -162,6 +288,20 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blocking"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
|
||||||
|
dependencies = [
|
||||||
|
"async-channel",
|
||||||
|
"async-task",
|
||||||
|
"atomic-waker",
|
||||||
|
"fastrand",
|
||||||
|
"futures-lite",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brotli"
|
name = "brotli"
|
||||||
version = "3.3.3"
|
version = "3.3.3"
|
||||||
@ -220,6 +360,12 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cache-padded"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cairo-rs"
|
name = "cairo-rs"
|
||||||
version = "0.15.10"
|
version = "0.15.10"
|
||||||
@ -336,6 +482,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"auto-launch",
|
"auto-launch",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"delay_timer",
|
||||||
"dirs",
|
"dirs",
|
||||||
"dunce",
|
"dunce",
|
||||||
"log",
|
"log",
|
||||||
@ -398,6 +545,25 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concat-idents"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b6f90860248d75014b7b103db8fee4f291c07bfb41306cdf77a0a5ab7a10d2f"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concurrent-queue"
|
||||||
|
version = "1.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
|
||||||
|
dependencies = [
|
||||||
|
"cache-padded",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -463,6 +629,17 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cron_clock"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a8699d8ed16e3db689f8ae04d8dc3c6666a4ba7e724e5a157884b7cc385d16b"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"nom",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.4"
|
version = "0.5.4"
|
||||||
@ -606,6 +783,16 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dashmap"
|
||||||
|
version = "4.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"num_cpus",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deflate"
|
name = "deflate"
|
||||||
version = "0.7.20"
|
version = "0.7.20"
|
||||||
@ -625,6 +812,31 @@ dependencies = [
|
|||||||
"adler32",
|
"adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "delay_timer"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68db8078186fbb91582bc7ba4f7b90e52cc4618a585748dd6a9fb6f4a25ea3c4"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"async-trait",
|
||||||
|
"autocfg",
|
||||||
|
"concat-idents",
|
||||||
|
"cron_clock",
|
||||||
|
"dashmap",
|
||||||
|
"event-listener",
|
||||||
|
"futures",
|
||||||
|
"log",
|
||||||
|
"lru",
|
||||||
|
"once_cell",
|
||||||
|
"rs-snowflake",
|
||||||
|
"rustc_version 0.2.3",
|
||||||
|
"smol",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derivative"
|
name = "derivative"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@ -757,6 +969,12 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener"
|
||||||
|
version = "2.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
@ -1289,6 +1507,9 @@ name = "hashbrown"
|
|||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "headers"
|
name = "headers"
|
||||||
@ -1728,6 +1949,15 @@ dependencies = [
|
|||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru"
|
||||||
|
version = "0.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ea2d928b485416e8908cff2d97d621db22b27f7b3b6729e438bcf42c671ba91"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mac"
|
name = "mac"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -1803,6 +2033,12 @@ dependencies = [
|
|||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minisign-verify"
|
name = "minisign-verify"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -1961,6 +2197,16 @@ version = "0.1.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
@ -2404,6 +2650,19 @@ dependencies = [
|
|||||||
"miniz_oxide 0.5.1",
|
"miniz_oxide 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "polling"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"wepoll-ffi",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "port_scanner"
|
name = "port_scanner"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@ -2706,6 +2965,21 @@ dependencies = [
|
|||||||
"windows 0.33.0",
|
"windows 0.33.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rs-snowflake"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3148be9baabb59959aeaf4b27597bce9f071511a6a74a4a20a2c5681722a21e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
dependencies = [
|
||||||
|
"semver 0.9.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -2816,13 +3090,22 @@ dependencies = [
|
|||||||
"thin-slice",
|
"thin-slice",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser 0.7.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"semver-parser",
|
"semver-parser 0.10.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2831,6 +3114,12 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
|
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver-parser"
|
name = "semver-parser"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
@ -3025,6 +3314,16 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@ -3052,6 +3351,24 @@ version = "1.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smol"
|
||||||
|
version = "1.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85cf3b5351f3e783c1d79ab5fc604eeed8b8ae9abd36b166e8b87a089efd85e4"
|
||||||
|
dependencies = [
|
||||||
|
"async-channel",
|
||||||
|
"async-executor",
|
||||||
|
"async-fs",
|
||||||
|
"async-io",
|
||||||
|
"async-lock",
|
||||||
|
"async-net",
|
||||||
|
"async-process",
|
||||||
|
"blocking",
|
||||||
|
"futures-lite",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
@ -4090,6 +4407,15 @@ dependencies = [
|
|||||||
"windows-bindgen",
|
"windows-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wepoll-ffi"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "which"
|
name = "which"
|
||||||
version = "4.2.5"
|
version = "4.2.5"
|
||||||
|
@ -21,6 +21,7 @@ nanoid = "0.4.0"
|
|||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
|
delay_timer = "0.11.1"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tauri = { version = "1.0.0-rc.6", features = ["process-all", "shell-all", "system-tray", "updater", "window-all"] }
|
tauri = { version = "1.0.0-rc.6", features = ["process-all", "shell-all", "system-tray", "updater", "window-all"] }
|
||||||
window-shadows = { git = "https://github.com/tauri-apps/window-shadows" }
|
window-shadows = { git = "https://github.com/tauri-apps/window-shadows" }
|
||||||
@ -39,8 +40,8 @@ port_scanner = "0.1.5"
|
|||||||
winreg = { version = "0.10", features = ["transactions"] }
|
winreg = { version = "0.10", features = ["transactions"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ "custom-protocol" ]
|
default = ["custom-protocol"]
|
||||||
custom-protocol = [ "tauri/custom-protocol" ]
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
verge-dev = []
|
verge-dev = []
|
||||||
debug-yml = []
|
debug-yml = []
|
||||||
|
|
||||||
|
@ -1,25 +1,26 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{ClashInfo, PrfItem, PrfOption, Profiles, VergeConfig},
|
core::{ClashInfo, Core, PrfItem, PrfOption, Profiles, VergeConfig},
|
||||||
states::{ClashState, ProfilesState, VergeState},
|
|
||||||
utils::{dirs, sysopt::SysProxyConfig},
|
utils::{dirs, sysopt::SysProxyConfig},
|
||||||
};
|
};
|
||||||
use crate::{ret_err, wrap_err};
|
use crate::{log_if_err, ret_err, wrap_err};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde_yaml::Mapping;
|
use serde_yaml::Mapping;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use tauri::{api, Manager, State};
|
use tauri::{api, Manager, State};
|
||||||
|
|
||||||
|
type CmdResult<T = ()> = Result<T, String>;
|
||||||
|
|
||||||
/// get all profiles from `profiles.yaml`
|
/// get all profiles from `profiles.yaml`
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_profiles<'a>(profiles_state: State<'_, ProfilesState>) -> Result<Profiles, String> {
|
pub fn get_profiles(core: State<'_, Core>) -> CmdResult<Profiles> {
|
||||||
let profiles = profiles_state.0.lock().unwrap();
|
let profiles = core.profiles.lock().unwrap();
|
||||||
Ok(profiles.clone())
|
Ok(profiles.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// synchronize data irregularly
|
/// synchronize data irregularly
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn sync_profiles(profiles_state: State<'_, ProfilesState>) -> Result<(), String> {
|
pub fn sync_profiles(core: State<'_, Core>) -> CmdResult {
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
wrap_err!(profiles.sync_file())
|
wrap_err!(profiles.sync_file())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,11 +30,11 @@ pub fn sync_profiles(profiles_state: State<'_, ProfilesState>) -> Result<(), Str
|
|||||||
pub async fn import_profile(
|
pub async fn import_profile(
|
||||||
url: String,
|
url: String,
|
||||||
option: Option<PrfOption>,
|
option: Option<PrfOption>,
|
||||||
profiles_state: State<'_, ProfilesState>,
|
core: State<'_, Core>,
|
||||||
) -> Result<(), String> {
|
) -> CmdResult {
|
||||||
let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?;
|
let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?;
|
||||||
|
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
wrap_err!(profiles.append_item(item))
|
wrap_err!(profiles.append_item(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,11 +45,11 @@ pub async fn import_profile(
|
|||||||
pub async fn create_profile(
|
pub async fn create_profile(
|
||||||
item: PrfItem, // partial
|
item: PrfItem, // partial
|
||||||
file_data: Option<String>,
|
file_data: Option<String>,
|
||||||
profiles_state: State<'_, ProfilesState>,
|
core: State<'_, Core>,
|
||||||
) -> Result<(), String> {
|
) -> CmdResult {
|
||||||
let item = wrap_err!(PrfItem::from(item, file_data).await)?;
|
let item = wrap_err!(PrfItem::from(item, file_data).await)?;
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
|
||||||
|
|
||||||
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
wrap_err!(profiles.append_item(item))
|
wrap_err!(profiles.append_item(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,12 +58,11 @@ pub async fn create_profile(
|
|||||||
pub async fn update_profile(
|
pub async fn update_profile(
|
||||||
index: String,
|
index: String,
|
||||||
option: Option<PrfOption>,
|
option: Option<PrfOption>,
|
||||||
clash_state: State<'_, ClashState>,
|
core: State<'_, Core>,
|
||||||
profiles_state: State<'_, ProfilesState>,
|
) -> CmdResult {
|
||||||
) -> Result<(), String> {
|
|
||||||
let (url, opt) = {
|
let (url, opt) = {
|
||||||
// must release the lock here
|
// must release the lock here
|
||||||
let profiles = profiles_state.0.lock().unwrap();
|
let profiles = core.profiles.lock().unwrap();
|
||||||
let item = wrap_err!(profiles.get_item(&index))?;
|
let item = wrap_err!(profiles.get_item(&index))?;
|
||||||
|
|
||||||
// check the profile type
|
// check the profile type
|
||||||
@ -82,13 +82,12 @@ pub async fn update_profile(
|
|||||||
let fetch_opt = PrfOption::merge(opt, option);
|
let fetch_opt = PrfOption::merge(opt, option);
|
||||||
let item = wrap_err!(PrfItem::from_url(&url, None, None, fetch_opt).await)?;
|
let item = wrap_err!(PrfItem::from_url(&url, None, None, fetch_opt).await)?;
|
||||||
|
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
wrap_err!(profiles.update_item(index.clone(), item))?;
|
wrap_err!(profiles.update_item(index.clone(), item))?;
|
||||||
|
|
||||||
// reactivate the profile
|
// reactivate the profile
|
||||||
if Some(index) == profiles.get_current() {
|
if Some(index) == profiles.get_current() {
|
||||||
let clash = clash_state.0.lock().unwrap();
|
log_if_err!(core.activate_enhanced(false, false));
|
||||||
wrap_err!(clash.activate_enhanced(&profiles, false, false))?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -96,79 +95,55 @@ pub async fn update_profile(
|
|||||||
|
|
||||||
/// change the current profile
|
/// change the current profile
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn select_profile(
|
pub fn select_profile(index: String, core: State<'_, Core>) -> CmdResult {
|
||||||
index: String,
|
{
|
||||||
clash_state: State<'_, ClashState>,
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
profiles_state: State<'_, ProfilesState>,
|
wrap_err!(profiles.put_current(index))?;
|
||||||
) -> Result<(), String> {
|
}
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
|
||||||
wrap_err!(profiles.put_current(index))?;
|
|
||||||
|
|
||||||
let clash = clash_state.0.lock().unwrap();
|
log_if_err!(core.activate_enhanced(false, false));
|
||||||
wrap_err!(clash.activate_enhanced(&profiles, false, false))
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// change the profile chain
|
/// change the profile chain
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn change_profile_chain(
|
pub fn change_profile_chain(chain: Option<Vec<String>>, core: State<'_, Core>) -> CmdResult {
|
||||||
chain: Option<Vec<String>>,
|
{
|
||||||
app_handle: tauri::AppHandle,
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
clash_state: State<'_, ClashState>,
|
profiles.put_chain(chain);
|
||||||
profiles_state: State<'_, ProfilesState>,
|
}
|
||||||
) -> Result<(), String> {
|
|
||||||
let mut clash = clash_state.0.lock().unwrap();
|
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
|
||||||
|
|
||||||
profiles.put_chain(chain);
|
log_if_err!(core.activate_enhanced(false, false));
|
||||||
clash.set_window(app_handle.get_window("main"));
|
|
||||||
|
|
||||||
wrap_err!(clash.activate_enhanced(&profiles, false, false))
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// change the profile valid fields
|
/// change the profile valid fields
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn change_profile_valid(
|
pub fn change_profile_valid(valid: Option<Vec<String>>, core: State<Core>) -> CmdResult {
|
||||||
valid: Option<Vec<String>>,
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
app_handle: tauri::AppHandle,
|
|
||||||
clash_state: State<'_, ClashState>,
|
|
||||||
profiles_state: State<'_, ProfilesState>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let mut clash = clash_state.0.lock().unwrap();
|
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
|
||||||
|
|
||||||
profiles.put_valid(valid);
|
profiles.put_valid(valid);
|
||||||
clash.set_window(app_handle.get_window("main"));
|
|
||||||
|
|
||||||
wrap_err!(clash.activate_enhanced(&profiles, false, false))
|
log_if_err!(core.activate_enhanced(false, false));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// manually exec enhanced profile
|
/// manually exec enhanced profile
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn enhance_profiles(
|
pub fn enhance_profiles(core: State<'_, Core>) -> CmdResult {
|
||||||
app_handle: tauri::AppHandle,
|
log_if_err!(core.activate_enhanced(false, false));
|
||||||
clash_state: State<'_, ClashState>,
|
Ok(())
|
||||||
profiles_state: State<'_, ProfilesState>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let mut clash = clash_state.0.lock().unwrap();
|
|
||||||
let profiles = profiles_state.0.lock().unwrap();
|
|
||||||
|
|
||||||
clash.set_window(app_handle.get_window("main"));
|
|
||||||
|
|
||||||
wrap_err!(clash.activate_enhanced(&profiles, false, false))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// delete profile item
|
/// delete profile item
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn delete_profile(
|
pub fn delete_profile(index: String, core: State<'_, Core>) -> CmdResult {
|
||||||
index: String,
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
clash_state: State<'_, ClashState>,
|
|
||||||
profiles_state: State<'_, ProfilesState>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
|
||||||
|
|
||||||
if wrap_err!(profiles.delete_item(index))? {
|
if wrap_err!(profiles.delete_item(index))? {
|
||||||
let clash = clash_state.0.lock().unwrap();
|
log_if_err!(core.activate_enhanced(false, false));
|
||||||
wrap_err!(clash.activate_enhanced(&profiles, false, false))?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -176,19 +151,16 @@ pub fn delete_profile(
|
|||||||
|
|
||||||
/// patch the profile config
|
/// patch the profile config
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn patch_profile(
|
pub fn patch_profile(index: String, profile: PrfItem, core: State<'_, Core>) -> CmdResult {
|
||||||
index: String,
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
profile: PrfItem,
|
|
||||||
profiles_state: State<'_, ProfilesState>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
|
||||||
wrap_err!(profiles.patch_item(index, profile))
|
wrap_err!(profiles.patch_item(index, profile))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// run vscode command to edit the profile
|
/// run vscode command to edit the profile
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn view_profile(index: String, profiles_state: State<'_, ProfilesState>) -> Result<(), String> {
|
pub fn view_profile(index: String, core: State<'_, Core>) -> CmdResult {
|
||||||
let profiles = profiles_state.0.lock().unwrap();
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
let item = wrap_err!(profiles.get_item(&index))?;
|
let item = wrap_err!(profiles.get_item(&index))?;
|
||||||
|
|
||||||
let file = item.file.clone();
|
let file = item.file.clone();
|
||||||
@ -231,11 +203,9 @@ pub fn view_profile(index: String, profiles_state: State<'_, ProfilesState>) ->
|
|||||||
|
|
||||||
/// read the profile item file data
|
/// read the profile item file data
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn read_profile_file(
|
pub fn read_profile_file(index: String, core: State<'_, Core>) -> CmdResult<String> {
|
||||||
index: String,
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
profiles_state: State<'_, ProfilesState>,
|
|
||||||
) -> Result<String, String> {
|
|
||||||
let profiles = profiles_state.0.lock().unwrap();
|
|
||||||
let item = wrap_err!(profiles.get_item(&index))?;
|
let item = wrap_err!(profiles.get_item(&index))?;
|
||||||
let data = wrap_err!(item.read_file())?;
|
let data = wrap_err!(item.read_file())?;
|
||||||
|
|
||||||
@ -247,34 +217,36 @@ pub fn read_profile_file(
|
|||||||
pub fn save_profile_file(
|
pub fn save_profile_file(
|
||||||
index: String,
|
index: String,
|
||||||
file_data: Option<String>,
|
file_data: Option<String>,
|
||||||
profiles_state: State<'_, ProfilesState>,
|
core: State<'_, Core>,
|
||||||
) -> Result<(), String> {
|
) -> CmdResult {
|
||||||
if file_data.is_none() {
|
if file_data.is_none() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let profiles = profiles_state.0.lock().unwrap();
|
let mut profiles = core.profiles.lock().unwrap();
|
||||||
let item = wrap_err!(profiles.get_item(&index))?;
|
let item = wrap_err!(profiles.get_item(&index))?;
|
||||||
wrap_err!(item.save_file(file_data.unwrap()))
|
wrap_err!(item.save_file(file_data.unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// restart the sidecar
|
/// restart the sidecar
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn restart_sidecar(
|
pub fn restart_sidecar(core: State<'_, Core>) -> CmdResult {
|
||||||
clash_state: State<'_, ClashState>,
|
let mut service = core.service.lock().unwrap();
|
||||||
profiles_state: State<'_, ProfilesState>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let mut clash = clash_state.0.lock().unwrap();
|
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
|
||||||
|
|
||||||
wrap_err!(clash.restart_sidecar(&mut profiles))
|
wrap_err!(service.restart())?;
|
||||||
|
|
||||||
|
// 更新配置
|
||||||
|
|
||||||
|
log_if_err!(core.activate_enhanced(false, false));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get the clash core info from the state
|
/// get the clash core info from the state
|
||||||
/// the caller can also get the infomation by clash's api
|
/// the caller can also get the infomation by clash's api
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_clash_info(clash_state: State<'_, ClashState>) -> Result<ClashInfo, String> {
|
pub fn get_clash_info(core: State<'_, Core>) -> CmdResult<ClashInfo> {
|
||||||
let clash = clash_state.0.lock().unwrap();
|
let clash = core.clash.lock().unwrap();
|
||||||
Ok(clash.info.clone())
|
Ok(clash.info.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,16 +254,8 @@ pub fn get_clash_info(clash_state: State<'_, ClashState>) -> Result<ClashInfo, S
|
|||||||
/// after putting the change to the clash core
|
/// after putting the change to the clash core
|
||||||
/// then we should save the latest config
|
/// then we should save the latest config
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn patch_clash_config(
|
pub fn patch_clash_config(payload: Mapping, core: State<'_, Core>) -> CmdResult {
|
||||||
payload: Mapping,
|
wrap_err!(core.patch_clash(payload))
|
||||||
clash_state: State<'_, ClashState>,
|
|
||||||
verge_state: State<'_, VergeState>,
|
|
||||||
profiles_state: State<'_, ProfilesState>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let mut clash = clash_state.0.lock().unwrap();
|
|
||||||
let mut verge = verge_state.0.lock().unwrap();
|
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
|
||||||
wrap_err!(clash.patch_config(payload, &mut verge, &mut profiles))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get the system proxy
|
/// get the system proxy
|
||||||
@ -303,15 +267,15 @@ pub fn get_sys_proxy() -> Result<SysProxyConfig, String> {
|
|||||||
/// get the current proxy config
|
/// get the current proxy config
|
||||||
/// which may not the same as system proxy
|
/// which may not the same as system proxy
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_cur_proxy(verge_state: State<'_, VergeState>) -> Result<Option<SysProxyConfig>, String> {
|
pub fn get_cur_proxy(core: State<'_, Core>) -> CmdResult<Option<SysProxyConfig>> {
|
||||||
let verge = verge_state.0.lock().unwrap();
|
let verge = core.verge.lock().unwrap();
|
||||||
Ok(verge.cur_sysproxy.clone())
|
Ok(verge.cur_sysproxy.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get the verge config
|
/// get the verge config
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_verge_config(verge_state: State<'_, VergeState>) -> Result<VergeConfig, String> {
|
pub fn get_verge_config(core: State<'_, Core>) -> CmdResult<VergeConfig> {
|
||||||
let verge = verge_state.0.lock().unwrap();
|
let verge = core.verge.lock().unwrap();
|
||||||
let mut config = verge.config.clone();
|
let mut config = verge.config.clone();
|
||||||
|
|
||||||
if config.system_proxy_bypass.is_none() && verge.cur_sysproxy.is_some() {
|
if config.system_proxy_bypass.is_none() && verge.cur_sysproxy.is_some() {
|
||||||
@ -327,14 +291,12 @@ pub fn get_verge_config(verge_state: State<'_, VergeState>) -> Result<VergeConfi
|
|||||||
pub fn patch_verge_config(
|
pub fn patch_verge_config(
|
||||||
payload: VergeConfig,
|
payload: VergeConfig,
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
clash_state: State<'_, ClashState>,
|
core: State<'_, Core>,
|
||||||
verge_state: State<'_, VergeState>,
|
|
||||||
profiles_state: State<'_, ProfilesState>,
|
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let tun_mode = payload.enable_tun_mode.clone();
|
let tun_mode = payload.enable_tun_mode.clone();
|
||||||
let system_proxy = payload.enable_system_proxy.clone();
|
let system_proxy = payload.enable_system_proxy.clone();
|
||||||
|
|
||||||
let mut verge = verge_state.0.lock().unwrap();
|
let mut verge = core.verge.lock().unwrap();
|
||||||
wrap_err!(verge.patch_config(payload))?;
|
wrap_err!(verge.patch_config(payload))?;
|
||||||
|
|
||||||
// change tun mode
|
// change tun mode
|
||||||
@ -348,10 +310,9 @@ pub fn patch_verge_config(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let clash = clash_state.0.lock().unwrap();
|
let profiles = core.profiles.lock().unwrap();
|
||||||
let profiles = profiles_state.0.lock().unwrap();
|
|
||||||
|
|
||||||
wrap_err!(clash.activate_enhanced(&profiles, false, false))?;
|
log_if_err!(core.activate_enhanced(false, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// change system tray
|
// change system tray
|
||||||
|
520
src-tauri/src/core/clash copy.rs
Normal file
520
src-tauri/src/core/clash copy.rs
Normal file
@ -0,0 +1,520 @@
|
|||||||
|
use super::{PrfEnhancedResult, Profiles, Verge, VergeConfig};
|
||||||
|
use crate::log_if_err;
|
||||||
|
use crate::utils::{config, dirs, help};
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use reqwest::header::HeaderMap;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_yaml::{Mapping, Value};
|
||||||
|
use std::{collections::HashMap, time::Duration};
|
||||||
|
use tauri::api::process::{Command, CommandChild, CommandEvent};
|
||||||
|
use tauri::Window;
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct ClashInfo {
|
||||||
|
/// clash sidecar status
|
||||||
|
pub status: String,
|
||||||
|
|
||||||
|
/// clash core port
|
||||||
|
pub port: Option<String>,
|
||||||
|
|
||||||
|
/// same as `external-controller`
|
||||||
|
pub server: Option<String>,
|
||||||
|
|
||||||
|
/// clash secret
|
||||||
|
pub secret: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Clash {
|
||||||
|
/// maintain the clash config
|
||||||
|
pub config: Mapping,
|
||||||
|
|
||||||
|
/// some info
|
||||||
|
pub info: ClashInfo,
|
||||||
|
|
||||||
|
/// clash sidecar
|
||||||
|
pub sidecar: Option<CommandChild>,
|
||||||
|
|
||||||
|
/// save the main window
|
||||||
|
pub window: Option<Window>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clash {
|
||||||
|
pub fn new() -> Clash {
|
||||||
|
let config = Clash::read_config();
|
||||||
|
let info = Clash::get_info(&config);
|
||||||
|
|
||||||
|
Clash {
|
||||||
|
config,
|
||||||
|
info,
|
||||||
|
sidecar: None,
|
||||||
|
window: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get clash config
|
||||||
|
fn read_config() -> Mapping {
|
||||||
|
config::read_yaml::<Mapping>(dirs::clash_path())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// save the clash config
|
||||||
|
fn save_config(&self) -> Result<()> {
|
||||||
|
config::save_yaml(
|
||||||
|
dirs::clash_path(),
|
||||||
|
&self.config,
|
||||||
|
Some("# Default Config For Clash Core\n\n"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// parse the clash's config.yaml
|
||||||
|
/// get some information
|
||||||
|
fn get_info(clash_config: &Mapping) -> ClashInfo {
|
||||||
|
let key_port_1 = Value::from("port");
|
||||||
|
let key_port_2 = Value::from("mixed-port");
|
||||||
|
let key_server = Value::from("external-controller");
|
||||||
|
let key_secret = Value::from("secret");
|
||||||
|
|
||||||
|
let port = match clash_config.get(&key_port_1) {
|
||||||
|
Some(value) => match value {
|
||||||
|
Value::String(val_str) => Some(val_str.clone()),
|
||||||
|
Value::Number(val_num) => Some(val_num.to_string()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let port = match port {
|
||||||
|
Some(_) => port,
|
||||||
|
None => match clash_config.get(&key_port_2) {
|
||||||
|
Some(value) => match value {
|
||||||
|
Value::String(val_str) => Some(val_str.clone()),
|
||||||
|
Value::Number(val_num) => Some(val_num.to_string()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let server = match clash_config.get(&key_server) {
|
||||||
|
Some(value) => match value {
|
||||||
|
Value::String(val_str) => {
|
||||||
|
// `external-controller` could be
|
||||||
|
// "127.0.0.1:9090" or ":9090"
|
||||||
|
// Todo: maybe it could support single port
|
||||||
|
let server = val_str.clone();
|
||||||
|
let server = match server.starts_with(":") {
|
||||||
|
true => format!("127.0.0.1{server}"),
|
||||||
|
false => server,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(server)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let secret = match clash_config.get(&key_secret) {
|
||||||
|
Some(value) => match value {
|
||||||
|
Value::String(val_str) => Some(val_str.clone()),
|
||||||
|
Value::Bool(val_bool) => Some(val_bool.to_string()),
|
||||||
|
Value::Number(val_num) => Some(val_num.to_string()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
ClashInfo {
|
||||||
|
status: "init".into(),
|
||||||
|
port,
|
||||||
|
server,
|
||||||
|
secret,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// save the main window
|
||||||
|
pub fn set_window(&mut self, win: Option<Window>) {
|
||||||
|
self.window = win;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// run clash sidecar
|
||||||
|
pub fn run_sidecar(&mut self, profiles: &Profiles, delay: bool) -> Result<()> {
|
||||||
|
let app_dir = dirs::app_home_dir();
|
||||||
|
let app_dir = app_dir.as_os_str().to_str().unwrap();
|
||||||
|
|
||||||
|
let cmd = Command::new_sidecar("clash")?;
|
||||||
|
let (mut rx, cmd_child) = cmd.args(["-d", app_dir]).spawn()?;
|
||||||
|
|
||||||
|
self.sidecar = Some(cmd_child);
|
||||||
|
|
||||||
|
// clash log
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
while let Some(event) = rx.recv().await {
|
||||||
|
match event {
|
||||||
|
CommandEvent::Stdout(line) => log::info!("[clash]: {}", line),
|
||||||
|
CommandEvent::Stderr(err) => log::error!("[clash]: {}", err),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// activate profile
|
||||||
|
log_if_err!(self.activate(&profiles));
|
||||||
|
log_if_err!(self.activate_enhanced(&profiles, delay, true));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// drop clash sidecar
|
||||||
|
pub fn drop_sidecar(&mut self) -> Result<()> {
|
||||||
|
if let Some(sidecar) = self.sidecar.take() {
|
||||||
|
sidecar.kill()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// restart clash sidecar
|
||||||
|
/// should reactivate profile after restart
|
||||||
|
pub fn restart_sidecar(&mut self, profiles: &mut Profiles) -> Result<()> {
|
||||||
|
self.update_config();
|
||||||
|
self.drop_sidecar()?;
|
||||||
|
self.run_sidecar(profiles, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// update the clash info
|
||||||
|
pub fn update_config(&mut self) {
|
||||||
|
self.config = Clash::read_config();
|
||||||
|
self.info = Clash::get_info(&self.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// patch update the clash config
|
||||||
|
pub fn patch_config(
|
||||||
|
&mut self,
|
||||||
|
patch: Mapping,
|
||||||
|
verge: &mut Verge,
|
||||||
|
profiles: &mut Profiles,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mix_port_key = Value::from("mixed-port");
|
||||||
|
let mut port = None;
|
||||||
|
|
||||||
|
for (key, value) in patch.into_iter() {
|
||||||
|
let value = value.clone();
|
||||||
|
|
||||||
|
// check whether the mix_port is changed
|
||||||
|
if key == mix_port_key {
|
||||||
|
if value.is_number() {
|
||||||
|
port = value.as_i64().as_ref().map(|n| n.to_string());
|
||||||
|
} else {
|
||||||
|
port = value.as_str().as_ref().map(|s| s.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.config.insert(key.clone(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.save_config()?;
|
||||||
|
|
||||||
|
if let Some(port) = port {
|
||||||
|
self.restart_sidecar(profiles)?;
|
||||||
|
verge.init_sysproxy(Some(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// revise the `tun` and `dns` config
|
||||||
|
fn _tun_mode(mut config: Mapping, enable: bool) -> Mapping {
|
||||||
|
macro_rules! revise {
|
||||||
|
($map: expr, $key: expr, $val: expr) => {
|
||||||
|
let ret_key = Value::String($key.into());
|
||||||
|
$map.insert(ret_key, Value::from($val));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// if key not exists then append value
|
||||||
|
macro_rules! append {
|
||||||
|
($map: expr, $key: expr, $val: expr) => {
|
||||||
|
let ret_key = Value::String($key.into());
|
||||||
|
if !$map.contains_key(&ret_key) {
|
||||||
|
$map.insert(ret_key, Value::from($val));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// tun config
|
||||||
|
let tun_val = config.get(&Value::from("tun"));
|
||||||
|
let mut new_tun = Mapping::new();
|
||||||
|
|
||||||
|
if tun_val.is_some() && tun_val.as_ref().unwrap().is_mapping() {
|
||||||
|
new_tun = tun_val.as_ref().unwrap().as_mapping().unwrap().clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
revise!(new_tun, "enable", enable);
|
||||||
|
|
||||||
|
if enable {
|
||||||
|
append!(new_tun, "stack", "gvisor");
|
||||||
|
append!(new_tun, "dns-hijack", vec!["198.18.0.2:53"]);
|
||||||
|
append!(new_tun, "auto-route", true);
|
||||||
|
append!(new_tun, "auto-detect-interface", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
revise!(config, "tun", new_tun);
|
||||||
|
|
||||||
|
// dns config
|
||||||
|
let dns_val = config.get(&Value::from("dns"));
|
||||||
|
let mut new_dns = Mapping::new();
|
||||||
|
|
||||||
|
if dns_val.is_some() && dns_val.as_ref().unwrap().is_mapping() {
|
||||||
|
new_dns = dns_val.as_ref().unwrap().as_mapping().unwrap().clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 借鉴cfw的默认配置
|
||||||
|
revise!(new_dns, "enable", enable);
|
||||||
|
|
||||||
|
if enable {
|
||||||
|
append!(new_dns, "enhanced-mode", "fake-ip");
|
||||||
|
append!(
|
||||||
|
new_dns,
|
||||||
|
"nameserver",
|
||||||
|
vec!["114.114.114.114", "223.5.5.5", "8.8.8.8"]
|
||||||
|
);
|
||||||
|
append!(new_dns, "fallback", vec![] as Vec<&str>);
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
append!(
|
||||||
|
new_dns,
|
||||||
|
"fake-ip-filter",
|
||||||
|
vec![
|
||||||
|
"dns.msftncsi.com",
|
||||||
|
"www.msftncsi.com",
|
||||||
|
"www.msftconnecttest.com"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
revise!(config, "dns", new_dns);
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
/// activate the profile
|
||||||
|
/// generate a new profile to the temp_dir
|
||||||
|
/// then put the path to the clash core
|
||||||
|
fn _activate(info: ClashInfo, config: Mapping, window: Option<Window>) -> Result<()> {
|
||||||
|
let verge_config = VergeConfig::new();
|
||||||
|
let tun_enable = verge_config.enable_tun_mode.unwrap_or(false);
|
||||||
|
|
||||||
|
let config = Clash::_tun_mode(config, tun_enable);
|
||||||
|
|
||||||
|
let temp_path = dirs::profiles_temp_path();
|
||||||
|
config::save_yaml(temp_path.clone(), &config, Some("# Clash Verge Temp File"))?;
|
||||||
|
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
if info.server.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let server = info.server.unwrap();
|
||||||
|
let server = format!("http://{server}/configs");
|
||||||
|
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
headers.insert("Content-Type", "application/json".parse().unwrap());
|
||||||
|
|
||||||
|
if let Some(secret) = info.secret.as_ref() {
|
||||||
|
let secret = format!("Bearer {}", secret.clone()).parse().unwrap();
|
||||||
|
headers.insert("Authorization", secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut data = HashMap::new();
|
||||||
|
data.insert("path", temp_path.as_os_str().to_str().unwrap());
|
||||||
|
|
||||||
|
// retry 5 times
|
||||||
|
for _ in 0..5 {
|
||||||
|
match reqwest::ClientBuilder::new().no_proxy().build() {
|
||||||
|
Ok(client) => {
|
||||||
|
let builder = client.put(&server).headers(headers.clone()).json(&data);
|
||||||
|
|
||||||
|
match builder.send().await {
|
||||||
|
Ok(resp) => {
|
||||||
|
if resp.status() != 204 {
|
||||||
|
log::error!("failed to activate clash for status \"{}\"", resp.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
// emit the window to update something
|
||||||
|
if let Some(window) = window {
|
||||||
|
window.emit("verge://refresh-clash-config", "yes").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not retry
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(err) => log::error!("failed to activate for `{err}`"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => log::error!("failed to activate for `{err}`"),
|
||||||
|
}
|
||||||
|
sleep(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// enhanced profiles mode
|
||||||
|
/// - (sync) refresh config if enhance chain is null
|
||||||
|
/// - (async) enhanced config
|
||||||
|
pub fn activate_enhanced(&self, profiles: &Profiles, delay: bool, skip: bool) -> Result<()> {
|
||||||
|
if self.window.is_none() {
|
||||||
|
bail!("failed to get the main window");
|
||||||
|
}
|
||||||
|
|
||||||
|
let event_name = help::get_uid("e");
|
||||||
|
let event_name = format!("enhanced-cb-{event_name}");
|
||||||
|
|
||||||
|
// generate the payload
|
||||||
|
let payload = profiles.gen_enhanced(event_name.clone())?;
|
||||||
|
|
||||||
|
let info = self.info.clone();
|
||||||
|
|
||||||
|
// do not run enhanced
|
||||||
|
if payload.chain.len() == 0 {
|
||||||
|
if skip {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut config = self.config.clone();
|
||||||
|
let filter_data = Clash::strict_filter(payload.current);
|
||||||
|
|
||||||
|
for (key, value) in filter_data.into_iter() {
|
||||||
|
config.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Clash::_activate(info, config, self.window.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let window = self.window.clone().unwrap();
|
||||||
|
let window_move = self.window.clone();
|
||||||
|
|
||||||
|
window.once(&event_name, move |event| {
|
||||||
|
if let Some(result) = event.payload() {
|
||||||
|
let result: PrfEnhancedResult = serde_json::from_str(result).unwrap();
|
||||||
|
|
||||||
|
if let Some(data) = result.data {
|
||||||
|
let mut config = Clash::read_config();
|
||||||
|
let filter_data = Clash::loose_filter(data); // loose filter
|
||||||
|
|
||||||
|
for (key, value) in filter_data.into_iter() {
|
||||||
|
config.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_if_err!(Clash::_activate(info, config, window_move));
|
||||||
|
log::info!("profile enhanced status {}", result.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.error.map(|err| log::error!("{err}"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
// wait the window setup during resolve app
|
||||||
|
if delay {
|
||||||
|
sleep(Duration::from_secs(2)).await;
|
||||||
|
}
|
||||||
|
window.emit("script-handler", payload).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// activate the profile
|
||||||
|
/// auto activate enhanced profile
|
||||||
|
pub fn activate(&self, profiles: &Profiles) -> Result<()> {
|
||||||
|
let data = profiles.gen_activate()?;
|
||||||
|
let data = Clash::strict_filter(data);
|
||||||
|
|
||||||
|
let info = self.info.clone();
|
||||||
|
let mut config = self.config.clone();
|
||||||
|
|
||||||
|
for (key, value) in data.into_iter() {
|
||||||
|
config.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Clash::_activate(info, config, self.window.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// only 5 default fields available (clash config fields)
|
||||||
|
/// convert to lowercase
|
||||||
|
fn strict_filter(config: Mapping) -> Mapping {
|
||||||
|
// Only the following fields are allowed:
|
||||||
|
// proxies/proxy-providers/proxy-groups/rule-providers/rules
|
||||||
|
let valid_keys = vec![
|
||||||
|
"proxies",
|
||||||
|
"proxy-providers",
|
||||||
|
"proxy-groups",
|
||||||
|
"rules",
|
||||||
|
"rule-providers",
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut new_config = Mapping::new();
|
||||||
|
|
||||||
|
for (key, value) in config.into_iter() {
|
||||||
|
key.as_str().map(|key_str| {
|
||||||
|
// change to lowercase
|
||||||
|
let mut key_str = String::from(key_str);
|
||||||
|
key_str.make_ascii_lowercase();
|
||||||
|
|
||||||
|
// filter
|
||||||
|
if valid_keys.contains(&&*key_str) {
|
||||||
|
new_config.insert(Value::String(key_str), value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
new_config
|
||||||
|
}
|
||||||
|
|
||||||
|
/// more clash config fields available
|
||||||
|
/// convert to lowercase
|
||||||
|
fn loose_filter(config: Mapping) -> Mapping {
|
||||||
|
// all of these can not be revised by script or merge
|
||||||
|
// http/https/socks port should be under control
|
||||||
|
let not_allow = vec![
|
||||||
|
"port",
|
||||||
|
"socks-port",
|
||||||
|
"mixed-port",
|
||||||
|
"allow-lan",
|
||||||
|
"mode",
|
||||||
|
"external-controller",
|
||||||
|
"secret",
|
||||||
|
"log-level",
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut new_config = Mapping::new();
|
||||||
|
|
||||||
|
for (key, value) in config.into_iter() {
|
||||||
|
key.as_str().map(|key_str| {
|
||||||
|
// change to lowercase
|
||||||
|
let mut key_str = String::from(key_str);
|
||||||
|
key_str.make_ascii_lowercase();
|
||||||
|
|
||||||
|
// filter
|
||||||
|
if !not_allow.contains(&&*key_str) {
|
||||||
|
new_config.insert(Value::String(key_str), value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
new_config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Clash {
|
||||||
|
fn default() -> Self {
|
||||||
|
Clash::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Clash {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Err(err) = self.drop_sidecar() {
|
||||||
|
log::error!("{err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,7 @@
|
|||||||
use super::{PrfEnhancedResult, Profiles, Verge, VergeConfig};
|
use crate::utils::{config, dirs};
|
||||||
use crate::log_if_err;
|
use anyhow::Result;
|
||||||
use crate::utils::{config, dirs, help};
|
|
||||||
use anyhow::{bail, Result};
|
|
||||||
use reqwest::header::HeaderMap;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml::{Mapping, Value};
|
use serde_yaml::{Mapping, Value};
|
||||||
use std::{collections::HashMap, time::Duration};
|
|
||||||
use tauri::api::process::{Command, CommandChild, CommandEvent};
|
|
||||||
use tauri::Window;
|
|
||||||
use tokio::time::sleep;
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct ClashInfo {
|
pub struct ClashInfo {
|
||||||
@ -25,56 +18,16 @@ pub struct ClashInfo {
|
|||||||
pub secret: Option<String>,
|
pub secret: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Clash {
|
impl ClashInfo {
|
||||||
/// maintain the clash config
|
|
||||||
pub config: Mapping,
|
|
||||||
|
|
||||||
/// some info
|
|
||||||
pub info: ClashInfo,
|
|
||||||
|
|
||||||
/// clash sidecar
|
|
||||||
pub sidecar: Option<CommandChild>,
|
|
||||||
|
|
||||||
/// save the main window
|
|
||||||
pub window: Option<Window>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clash {
|
|
||||||
pub fn new() -> Clash {
|
|
||||||
let config = Clash::read_config();
|
|
||||||
let info = Clash::get_info(&config);
|
|
||||||
|
|
||||||
Clash {
|
|
||||||
config,
|
|
||||||
info,
|
|
||||||
sidecar: None,
|
|
||||||
window: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get clash config
|
|
||||||
fn read_config() -> Mapping {
|
|
||||||
config::read_yaml::<Mapping>(dirs::clash_path())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// save the clash config
|
|
||||||
fn save_config(&self) -> Result<()> {
|
|
||||||
config::save_yaml(
|
|
||||||
dirs::clash_path(),
|
|
||||||
&self.config,
|
|
||||||
Some("# Default Config For Clash Core\n\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// parse the clash's config.yaml
|
/// parse the clash's config.yaml
|
||||||
/// get some information
|
/// get some information
|
||||||
fn get_info(clash_config: &Mapping) -> ClashInfo {
|
pub fn from(config: &Mapping) -> ClashInfo {
|
||||||
let key_port_1 = Value::from("port");
|
let key_port_1 = Value::from("port");
|
||||||
let key_port_2 = Value::from("mixed-port");
|
let key_port_2 = Value::from("mixed-port");
|
||||||
let key_server = Value::from("external-controller");
|
let key_server = Value::from("external-controller");
|
||||||
let key_secret = Value::from("secret");
|
let key_secret = Value::from("secret");
|
||||||
|
|
||||||
let port = match clash_config.get(&key_port_1) {
|
let port = match config.get(&key_port_1) {
|
||||||
Some(value) => match value {
|
Some(value) => match value {
|
||||||
Value::String(val_str) => Some(val_str.clone()),
|
Value::String(val_str) => Some(val_str.clone()),
|
||||||
Value::Number(val_num) => Some(val_num.to_string()),
|
Value::Number(val_num) => Some(val_num.to_string()),
|
||||||
@ -84,7 +37,7 @@ impl Clash {
|
|||||||
};
|
};
|
||||||
let port = match port {
|
let port = match port {
|
||||||
Some(_) => port,
|
Some(_) => port,
|
||||||
None => match clash_config.get(&key_port_2) {
|
None => match config.get(&key_port_2) {
|
||||||
Some(value) => match value {
|
Some(value) => match value {
|
||||||
Value::String(val_str) => Some(val_str.clone()),
|
Value::String(val_str) => Some(val_str.clone()),
|
||||||
Value::Number(val_num) => Some(val_num.to_string()),
|
Value::Number(val_num) => Some(val_num.to_string()),
|
||||||
@ -94,7 +47,7 @@ impl Clash {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let server = match clash_config.get(&key_server) {
|
let server = match config.get(&key_server) {
|
||||||
Some(value) => match value {
|
Some(value) => match value {
|
||||||
Value::String(val_str) => {
|
Value::String(val_str) => {
|
||||||
// `external-controller` could be
|
// `external-controller` could be
|
||||||
@ -112,7 +65,8 @@ impl Clash {
|
|||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
let secret = match clash_config.get(&key_secret) {
|
|
||||||
|
let secret = match config.get(&key_secret) {
|
||||||
Some(value) => match value {
|
Some(value) => match value {
|
||||||
Value::String(val_str) => Some(val_str.clone()),
|
Value::String(val_str) => Some(val_str.clone()),
|
||||||
Value::Bool(val_bool) => Some(val_bool.to_string()),
|
Value::Bool(val_bool) => Some(val_bool.to_string()),
|
||||||
@ -129,99 +83,78 @@ impl Clash {
|
|||||||
secret,
|
secret,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// save the main window
|
pub struct Clash {
|
||||||
pub fn set_window(&mut self, win: Option<Window>) {
|
/// maintain the clash config
|
||||||
self.window = win;
|
pub config: Mapping,
|
||||||
|
|
||||||
|
/// some info
|
||||||
|
pub info: ClashInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clash {
|
||||||
|
pub fn new() -> Clash {
|
||||||
|
let config = Clash::read_config();
|
||||||
|
let info = ClashInfo::from(&config);
|
||||||
|
|
||||||
|
Clash { config, info }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// run clash sidecar
|
/// get clash config
|
||||||
pub fn run_sidecar(&mut self, profiles: &Profiles, delay: bool) -> Result<()> {
|
pub fn read_config() -> Mapping {
|
||||||
let app_dir = dirs::app_home_dir();
|
config::read_yaml::<Mapping>(dirs::clash_path())
|
||||||
let app_dir = app_dir.as_os_str().to_str().unwrap();
|
|
||||||
|
|
||||||
let cmd = Command::new_sidecar("clash")?;
|
|
||||||
let (mut rx, cmd_child) = cmd.args(["-d", app_dir]).spawn()?;
|
|
||||||
|
|
||||||
self.sidecar = Some(cmd_child);
|
|
||||||
|
|
||||||
// clash log
|
|
||||||
tauri::async_runtime::spawn(async move {
|
|
||||||
while let Some(event) = rx.recv().await {
|
|
||||||
match event {
|
|
||||||
CommandEvent::Stdout(line) => log::info!("[clash]: {}", line),
|
|
||||||
CommandEvent::Stderr(err) => log::error!("[clash]: {}", err),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// activate profile
|
|
||||||
log_if_err!(self.activate(&profiles));
|
|
||||||
log_if_err!(self.activate_enhanced(&profiles, delay, true));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// drop clash sidecar
|
/// save the clash config
|
||||||
pub fn drop_sidecar(&mut self) -> Result<()> {
|
pub fn save_config(&self) -> Result<()> {
|
||||||
if let Some(sidecar) = self.sidecar.take() {
|
config::save_yaml(
|
||||||
sidecar.kill()?;
|
dirs::clash_path(),
|
||||||
}
|
&self.config,
|
||||||
Ok(())
|
Some("# Default Config For Clash Core\n\n"),
|
||||||
}
|
)
|
||||||
|
|
||||||
/// restart clash sidecar
|
|
||||||
/// should reactivate profile after restart
|
|
||||||
pub fn restart_sidecar(&mut self, profiles: &mut Profiles) -> Result<()> {
|
|
||||||
self.update_config();
|
|
||||||
self.drop_sidecar()?;
|
|
||||||
self.run_sidecar(profiles, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// todo: delete
|
||||||
/// update the clash info
|
/// update the clash info
|
||||||
pub fn update_config(&mut self) {
|
pub fn update_config(&mut self) {
|
||||||
self.config = Clash::read_config();
|
self.config = Clash::read_config();
|
||||||
self.info = Clash::get_info(&self.config);
|
self.info = ClashInfo::from(&self.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// patch update the clash config
|
/// patch update the clash config
|
||||||
pub fn patch_config(
|
/// if the port is changed then return true
|
||||||
&mut self,
|
pub fn patch_config(&mut self, patch: Mapping) -> Result<bool> {
|
||||||
patch: Mapping,
|
let port_key = Value::from("mixed-port");
|
||||||
verge: &mut Verge,
|
let server_key = Value::from("external-controller");
|
||||||
profiles: &mut Profiles,
|
let secret_key = Value::from("secret");
|
||||||
) -> Result<()> {
|
|
||||||
let mix_port_key = Value::from("mixed-port");
|
let mut change_port = false;
|
||||||
let mut port = None;
|
let mut change_info = false;
|
||||||
|
|
||||||
for (key, value) in patch.into_iter() {
|
for (key, value) in patch.into_iter() {
|
||||||
let value = value.clone();
|
if key == port_key {
|
||||||
|
change_port = true;
|
||||||
// check whether the mix_port is changed
|
|
||||||
if key == mix_port_key {
|
|
||||||
if value.is_number() {
|
|
||||||
port = value.as_i64().as_ref().map(|n| n.to_string());
|
|
||||||
} else {
|
|
||||||
port = value.as_str().as_ref().map(|s| s.to_string());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.config.insert(key.clone(), value);
|
if key == port_key || key == server_key || key == secret_key {
|
||||||
|
change_info = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.config.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if change_info {
|
||||||
|
self.info = ClashInfo::from(&self.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.save_config()?;
|
self.save_config()?;
|
||||||
|
|
||||||
if let Some(port) = port {
|
Ok(change_port)
|
||||||
self.restart_sidecar(profiles)?;
|
|
||||||
verge.init_sysproxy(Some(port));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// revise the `tun` and `dns` config
|
/// revise the `tun` and `dns` config
|
||||||
fn _tun_mode(mut config: Mapping, enable: bool) -> Mapping {
|
pub fn _tun_mode(mut config: Mapping, enable: bool) -> Mapping {
|
||||||
macro_rules! revise {
|
macro_rules! revise {
|
||||||
($map: expr, $key: expr, $val: expr) => {
|
($map: expr, $key: expr, $val: expr) => {
|
||||||
let ret_key = Value::String($key.into());
|
let ret_key = Value::String($key.into());
|
||||||
@ -294,154 +227,9 @@ impl Clash {
|
|||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
/// activate the profile
|
|
||||||
/// generate a new profile to the temp_dir
|
|
||||||
/// then put the path to the clash core
|
|
||||||
fn _activate(info: ClashInfo, config: Mapping, window: Option<Window>) -> Result<()> {
|
|
||||||
let verge_config = VergeConfig::new();
|
|
||||||
let tun_enable = verge_config.enable_tun_mode.unwrap_or(false);
|
|
||||||
|
|
||||||
let config = Clash::_tun_mode(config, tun_enable);
|
|
||||||
|
|
||||||
let temp_path = dirs::profiles_temp_path();
|
|
||||||
config::save_yaml(temp_path.clone(), &config, Some("# Clash Verge Temp File"))?;
|
|
||||||
|
|
||||||
tauri::async_runtime::spawn(async move {
|
|
||||||
if info.server.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let server = info.server.unwrap();
|
|
||||||
let server = format!("http://{server}/configs");
|
|
||||||
|
|
||||||
let mut headers = HeaderMap::new();
|
|
||||||
headers.insert("Content-Type", "application/json".parse().unwrap());
|
|
||||||
|
|
||||||
if let Some(secret) = info.secret.as_ref() {
|
|
||||||
let secret = format!("Bearer {}", secret.clone()).parse().unwrap();
|
|
||||||
headers.insert("Authorization", secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut data = HashMap::new();
|
|
||||||
data.insert("path", temp_path.as_os_str().to_str().unwrap());
|
|
||||||
|
|
||||||
// retry 5 times
|
|
||||||
for _ in 0..5 {
|
|
||||||
match reqwest::ClientBuilder::new().no_proxy().build() {
|
|
||||||
Ok(client) => {
|
|
||||||
let builder = client.put(&server).headers(headers.clone()).json(&data);
|
|
||||||
|
|
||||||
match builder.send().await {
|
|
||||||
Ok(resp) => {
|
|
||||||
if resp.status() != 204 {
|
|
||||||
log::error!("failed to activate clash for status \"{}\"", resp.status());
|
|
||||||
}
|
|
||||||
|
|
||||||
// emit the window to update something
|
|
||||||
if let Some(window) = window {
|
|
||||||
window.emit("verge://refresh-clash-config", "yes").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// do not retry
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(err) => log::error!("failed to activate for `{err}`"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => log::error!("failed to activate for `{err}`"),
|
|
||||||
}
|
|
||||||
sleep(Duration::from_millis(500)).await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// enhanced profiles mode
|
|
||||||
/// - (sync) refresh config if enhance chain is null
|
|
||||||
/// - (async) enhanced config
|
|
||||||
pub fn activate_enhanced(&self, profiles: &Profiles, delay: bool, skip: bool) -> Result<()> {
|
|
||||||
if self.window.is_none() {
|
|
||||||
bail!("failed to get the main window");
|
|
||||||
}
|
|
||||||
|
|
||||||
let event_name = help::get_uid("e");
|
|
||||||
let event_name = format!("enhanced-cb-{event_name}");
|
|
||||||
|
|
||||||
// generate the payload
|
|
||||||
let payload = profiles.gen_enhanced(event_name.clone())?;
|
|
||||||
|
|
||||||
let info = self.info.clone();
|
|
||||||
|
|
||||||
// do not run enhanced
|
|
||||||
if payload.chain.len() == 0 {
|
|
||||||
if skip {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut config = self.config.clone();
|
|
||||||
let filter_data = Clash::strict_filter(payload.current);
|
|
||||||
|
|
||||||
for (key, value) in filter_data.into_iter() {
|
|
||||||
config.insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Clash::_activate(info, config, self.window.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let window = self.window.clone().unwrap();
|
|
||||||
let window_move = self.window.clone();
|
|
||||||
|
|
||||||
window.once(&event_name, move |event| {
|
|
||||||
if let Some(result) = event.payload() {
|
|
||||||
let result: PrfEnhancedResult = serde_json::from_str(result).unwrap();
|
|
||||||
|
|
||||||
if let Some(data) = result.data {
|
|
||||||
let mut config = Clash::read_config();
|
|
||||||
let filter_data = Clash::loose_filter(data); // loose filter
|
|
||||||
|
|
||||||
for (key, value) in filter_data.into_iter() {
|
|
||||||
config.insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
log_if_err!(Clash::_activate(info, config, window_move));
|
|
||||||
log::info!("profile enhanced status {}", result.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.error.map(|err| log::error!("{err}"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tauri::async_runtime::spawn(async move {
|
|
||||||
// wait the window setup during resolve app
|
|
||||||
if delay {
|
|
||||||
sleep(Duration::from_secs(2)).await;
|
|
||||||
}
|
|
||||||
window.emit("script-handler", payload).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// activate the profile
|
|
||||||
/// auto activate enhanced profile
|
|
||||||
pub fn activate(&self, profiles: &Profiles) -> Result<()> {
|
|
||||||
let data = profiles.gen_activate()?;
|
|
||||||
let data = Clash::strict_filter(data);
|
|
||||||
|
|
||||||
let info = self.info.clone();
|
|
||||||
let mut config = self.config.clone();
|
|
||||||
|
|
||||||
for (key, value) in data.into_iter() {
|
|
||||||
config.insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Clash::_activate(info, config, self.window.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// only 5 default fields available (clash config fields)
|
/// only 5 default fields available (clash config fields)
|
||||||
/// convert to lowercase
|
/// convert to lowercase
|
||||||
fn strict_filter(config: Mapping) -> Mapping {
|
pub fn strict_filter(config: Mapping) -> Mapping {
|
||||||
// Only the following fields are allowed:
|
// Only the following fields are allowed:
|
||||||
// proxies/proxy-providers/proxy-groups/rule-providers/rules
|
// proxies/proxy-providers/proxy-groups/rule-providers/rules
|
||||||
let valid_keys = vec![
|
let valid_keys = vec![
|
||||||
@ -472,7 +260,7 @@ impl Clash {
|
|||||||
|
|
||||||
/// more clash config fields available
|
/// more clash config fields available
|
||||||
/// convert to lowercase
|
/// convert to lowercase
|
||||||
fn loose_filter(config: Mapping) -> Mapping {
|
pub fn loose_filter(config: Mapping) -> Mapping {
|
||||||
// all of these can not be revised by script or merge
|
// all of these can not be revised by script or merge
|
||||||
// http/https/socks port should be under control
|
// http/https/socks port should be under control
|
||||||
let not_allow = vec![
|
let not_allow = vec![
|
||||||
@ -510,11 +298,3 @@ impl Default for Clash {
|
|||||||
Clash::new()
|
Clash::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Clash {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Err(err) = self.drop_sidecar() {
|
|
||||||
log::error!("{err}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
66
src-tauri/src/core/enhance.rs
Normal file
66
src-tauri/src/core/enhance.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use super::prfitem::PrfItem;
|
||||||
|
use crate::utils::{config, dirs};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_yaml::Mapping;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PrfEnhanced {
|
||||||
|
pub current: Mapping,
|
||||||
|
|
||||||
|
pub chain: Vec<PrfData>,
|
||||||
|
|
||||||
|
pub valid: Vec<String>,
|
||||||
|
|
||||||
|
pub callback: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PrfEnhancedResult {
|
||||||
|
pub data: Option<Mapping>,
|
||||||
|
|
||||||
|
pub status: String,
|
||||||
|
|
||||||
|
pub error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PrfData {
|
||||||
|
item: PrfItem,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
merge: Option<Mapping>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
script: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrfData {
|
||||||
|
pub fn from_item(item: &PrfItem) -> Option<PrfData> {
|
||||||
|
match item.itype.as_ref() {
|
||||||
|
Some(itype) => {
|
||||||
|
let file = item.file.clone()?;
|
||||||
|
let path = dirs::app_profiles_dir().join(file);
|
||||||
|
|
||||||
|
if !path.exists() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match itype.as_str() {
|
||||||
|
"script" => Some(PrfData {
|
||||||
|
item: item.clone(),
|
||||||
|
script: Some(fs::read_to_string(path).unwrap_or("".into())),
|
||||||
|
merge: None,
|
||||||
|
}),
|
||||||
|
"merge" => Some(PrfData {
|
||||||
|
item: item.clone(),
|
||||||
|
merge: Some(config::read_yaml::<Mapping>(path)),
|
||||||
|
script: None,
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,227 @@
|
|||||||
|
use self::notice::Notice;
|
||||||
|
use self::service::Service;
|
||||||
|
use crate::core::enhance::PrfEnhancedResult;
|
||||||
|
use crate::log_if_err;
|
||||||
|
use crate::utils::{config, dirs, help};
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use serde_yaml::Mapping;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::time::Duration;
|
||||||
|
use tauri::Window;
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
mod clash;
|
mod clash;
|
||||||
|
mod enhance;
|
||||||
|
mod notice;
|
||||||
|
mod prfitem;
|
||||||
mod profiles;
|
mod profiles;
|
||||||
|
mod service;
|
||||||
|
mod timer;
|
||||||
mod verge;
|
mod verge;
|
||||||
|
|
||||||
pub use self::clash::*;
|
pub use self::clash::*;
|
||||||
|
pub use self::prfitem::*;
|
||||||
pub use self::profiles::*;
|
pub use self::profiles::*;
|
||||||
pub use self::verge::*;
|
pub use self::verge::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Core {
|
||||||
|
pub clash: Arc<Mutex<Clash>>,
|
||||||
|
|
||||||
|
pub verge: Arc<Mutex<Verge>>,
|
||||||
|
|
||||||
|
pub profiles: Arc<Mutex<Profiles>>,
|
||||||
|
|
||||||
|
pub service: Arc<Mutex<Service>>,
|
||||||
|
|
||||||
|
pub window: Arc<Mutex<Option<Window>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Core {
|
||||||
|
pub fn new() -> Core {
|
||||||
|
let clash = Clash::new();
|
||||||
|
let verge = Verge::new();
|
||||||
|
let profiles = Profiles::new();
|
||||||
|
let service = Service::new();
|
||||||
|
|
||||||
|
Core {
|
||||||
|
clash: Arc::new(Mutex::new(clash)),
|
||||||
|
verge: Arc::new(Mutex::new(verge)),
|
||||||
|
profiles: Arc::new(Mutex::new(profiles)),
|
||||||
|
service: Arc::new(Mutex::new(service)),
|
||||||
|
window: Arc::new(Mutex::new(None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&self) {
|
||||||
|
log_if_err!(self.restart_clash());
|
||||||
|
|
||||||
|
let clash = self.clash.lock().unwrap();
|
||||||
|
let mut verge = self.verge.lock().unwrap();
|
||||||
|
verge.init_sysproxy(clash.info.port.clone());
|
||||||
|
|
||||||
|
log_if_err!(verge.init_launch());
|
||||||
|
|
||||||
|
// system tray
|
||||||
|
// verge.config.enable_system_proxy.map(|enable| {
|
||||||
|
// log_if_err!(app
|
||||||
|
// .tray_handle()
|
||||||
|
// .get_item("system_proxy")
|
||||||
|
// .set_selected(enable));
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// save the window instance
|
||||||
|
pub fn set_win(&self, win: Option<Window>) {
|
||||||
|
let mut window = self.window.lock().unwrap();
|
||||||
|
*window = win;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// restart the clash sidecar
|
||||||
|
pub fn restart_clash(&self) -> Result<()> {
|
||||||
|
{
|
||||||
|
let mut service = self.service.lock().unwrap();
|
||||||
|
service.restart()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.activate()?;
|
||||||
|
self.activate_enhanced(false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// handle the clash config changed
|
||||||
|
pub fn patch_clash(&self, patch: Mapping) -> Result<()> {
|
||||||
|
let (changed, port) = {
|
||||||
|
let mut clash = self.clash.lock().unwrap();
|
||||||
|
(clash.patch_config(patch)?, clash.info.port.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
// todo: port check
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
let mut service = self.service.lock().unwrap();
|
||||||
|
service.restart()?;
|
||||||
|
|
||||||
|
self.activate()?;
|
||||||
|
self.activate_enhanced(false, true)?;
|
||||||
|
|
||||||
|
let mut verge = self.verge.lock().unwrap();
|
||||||
|
verge.init_sysproxy(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// activate the profile
|
||||||
|
/// auto activate enhanced profile
|
||||||
|
pub fn activate(&self) -> Result<()> {
|
||||||
|
let data = {
|
||||||
|
let profiles = self.profiles.lock().unwrap();
|
||||||
|
let data = profiles.gen_activate()?;
|
||||||
|
Clash::strict_filter(data)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mut config, info) = {
|
||||||
|
let clash = self.clash.lock().unwrap();
|
||||||
|
let config = clash.config.clone();
|
||||||
|
let info = clash.info.clone();
|
||||||
|
(config, info)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (key, value) in data.into_iter() {
|
||||||
|
config.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
let verge = self.verge.lock().unwrap();
|
||||||
|
let tun_mode = verge.config.enable_tun_mode.unwrap_or(false);
|
||||||
|
Clash::_tun_mode(config, tun_mode)
|
||||||
|
};
|
||||||
|
|
||||||
|
let notice = {
|
||||||
|
let window = self.window.lock().unwrap();
|
||||||
|
Notice::from(window.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
let service = self.service.lock().unwrap();
|
||||||
|
service.set_config(info, config, notice)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// enhanced profiles mode
|
||||||
|
pub fn activate_enhanced(&self, delay: bool, skip: bool) -> Result<()> {
|
||||||
|
let window = self.window.lock().unwrap();
|
||||||
|
if window.is_none() {
|
||||||
|
bail!("failed to get the main window");
|
||||||
|
}
|
||||||
|
|
||||||
|
let event_name = help::get_uid("e");
|
||||||
|
let event_name = format!("enhanced-cb-{event_name}");
|
||||||
|
|
||||||
|
// generate the payload
|
||||||
|
let payload = {
|
||||||
|
let profiles = self.profiles.lock().unwrap();
|
||||||
|
profiles.gen_enhanced(event_name.clone())?
|
||||||
|
};
|
||||||
|
|
||||||
|
// do not run enhanced
|
||||||
|
if payload.chain.len() == 0 {
|
||||||
|
if skip {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
let tun_mode = {
|
||||||
|
let verge = self.verge.lock().unwrap();
|
||||||
|
verge.config.enable_tun_mode.unwrap_or(false)
|
||||||
|
};
|
||||||
|
|
||||||
|
let info = {
|
||||||
|
let clash = self.clash.lock().unwrap();
|
||||||
|
clash.info.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let notice = Notice::from(window.clone());
|
||||||
|
let service = self.service.clone();
|
||||||
|
|
||||||
|
let window = window.clone().unwrap();
|
||||||
|
window.once(&event_name, move |event| {
|
||||||
|
let result = event.payload();
|
||||||
|
|
||||||
|
if result.is_none() {
|
||||||
|
log::warn!("event payload result is none");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = result.unwrap();
|
||||||
|
let result: PrfEnhancedResult = serde_json::from_str(result).unwrap();
|
||||||
|
|
||||||
|
if let Some(data) = result.data {
|
||||||
|
let mut config = Clash::read_config();
|
||||||
|
let filter_data = Clash::loose_filter(data); // loose filter
|
||||||
|
|
||||||
|
for (key, value) in filter_data.into_iter() {
|
||||||
|
config.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let config = Clash::_tun_mode(config, tun_mode);
|
||||||
|
|
||||||
|
let service = service.lock().unwrap();
|
||||||
|
log_if_err!(service.set_config(info, config, notice));
|
||||||
|
|
||||||
|
log::info!("profile enhanced status {}", result.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.error.map(|err| log::error!("{err}"));
|
||||||
|
});
|
||||||
|
|
||||||
|
// wait the window setup during resolve app
|
||||||
|
// if delay {
|
||||||
|
// sleep(Duration::from_secs(2)).await;
|
||||||
|
// }
|
||||||
|
|
||||||
|
window.emit("script-handler", payload).unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
35
src-tauri/src/core/notice.rs
Normal file
35
src-tauri/src/core/notice.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use crate::log_if_err;
|
||||||
|
use tauri::Window;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct Notice {
|
||||||
|
win: Option<Window>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Notice {
|
||||||
|
pub fn from(win: Option<Window>) -> Notice {
|
||||||
|
Notice { win }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_win(&mut self, win: Option<Window>) {
|
||||||
|
self.win = win;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_clash(&self) {
|
||||||
|
if let Some(window) = self.win.as_ref() {
|
||||||
|
log_if_err!(window.emit("verge://refresh-clash-config", "yes"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_verge(&self) {
|
||||||
|
if let Some(window) = self.win.as_ref() {
|
||||||
|
log_if_err!(window.emit("verge://refresh-verge-config", "yes"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_profiles(&self) {
|
||||||
|
if let Some(window) = self.win.as_ref() {
|
||||||
|
log_if_err!(window.emit("verge://refresh-profiles-config", "yes"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
320
src-tauri/src/core/prfitem.rs
Normal file
320
src-tauri/src/core/prfitem.rs
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
use crate::utils::{dirs, help, tmpl};
|
||||||
|
use anyhow::{bail, Context, Result};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct PrfItem {
|
||||||
|
pub uid: Option<String>,
|
||||||
|
|
||||||
|
/// profile item type
|
||||||
|
/// enum value: remote | local | script | merge
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub itype: Option<String>,
|
||||||
|
|
||||||
|
/// profile name
|
||||||
|
pub name: Option<String>,
|
||||||
|
|
||||||
|
/// profile description
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub desc: Option<String>,
|
||||||
|
|
||||||
|
/// profile file
|
||||||
|
pub file: Option<String>,
|
||||||
|
|
||||||
|
/// source url
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub url: Option<String>,
|
||||||
|
|
||||||
|
/// selected infomation
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub selected: Option<Vec<PrfSelected>>,
|
||||||
|
|
||||||
|
/// subscription user info
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub extra: Option<PrfExtra>,
|
||||||
|
|
||||||
|
/// updated time
|
||||||
|
pub updated: Option<usize>,
|
||||||
|
|
||||||
|
/// some options of the item
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub option: Option<PrfOption>,
|
||||||
|
|
||||||
|
/// the file data
|
||||||
|
#[serde(skip)]
|
||||||
|
pub file_data: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct PrfSelected {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub now: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy, Deserialize, Serialize)]
|
||||||
|
pub struct PrfExtra {
|
||||||
|
pub upload: usize,
|
||||||
|
pub download: usize,
|
||||||
|
pub total: usize,
|
||||||
|
pub expire: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct PrfOption {
|
||||||
|
/// for `remote` profile's http request
|
||||||
|
/// see issue #13
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub user_agent: Option<String>,
|
||||||
|
|
||||||
|
/// for `remote` profile
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub with_proxy: Option<bool>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub update_interval: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrfOption {
|
||||||
|
pub fn merge(one: Option<Self>, other: Option<Self>) -> Option<Self> {
|
||||||
|
if one.is_none() {
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
|
||||||
|
if one.is_some() && other.is_some() {
|
||||||
|
let mut one = one.unwrap();
|
||||||
|
let other = other.unwrap();
|
||||||
|
|
||||||
|
if let Some(val) = other.user_agent {
|
||||||
|
one.user_agent = Some(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = other.with_proxy {
|
||||||
|
one.with_proxy = Some(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = other.update_interval {
|
||||||
|
one.update_interval = Some(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(one);
|
||||||
|
}
|
||||||
|
|
||||||
|
return one;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn diff_update_interval(one: Option<&Self>, other: Option<&Self>) -> bool {
|
||||||
|
if one.is_some() && other.is_some() {
|
||||||
|
let one = one.unwrap();
|
||||||
|
let other = other.unwrap();
|
||||||
|
|
||||||
|
return one.update_interval == other.update_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PrfItem {
|
||||||
|
fn default() -> Self {
|
||||||
|
PrfItem {
|
||||||
|
uid: None,
|
||||||
|
itype: None,
|
||||||
|
name: None,
|
||||||
|
desc: None,
|
||||||
|
file: None,
|
||||||
|
url: None,
|
||||||
|
selected: None,
|
||||||
|
extra: None,
|
||||||
|
updated: None,
|
||||||
|
option: None,
|
||||||
|
file_data: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrfItem {
|
||||||
|
/// From partial item
|
||||||
|
/// must contain `itype`
|
||||||
|
pub async fn from(item: PrfItem, file_data: Option<String>) -> Result<PrfItem> {
|
||||||
|
if item.itype.is_none() {
|
||||||
|
bail!("type should not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
match item.itype.unwrap().as_str() {
|
||||||
|
"remote" => {
|
||||||
|
if item.url.is_none() {
|
||||||
|
bail!("url should not be null");
|
||||||
|
}
|
||||||
|
let url = item.url.as_ref().unwrap().as_str();
|
||||||
|
let name = item.name;
|
||||||
|
let desc = item.desc;
|
||||||
|
PrfItem::from_url(url, name, desc, item.option).await
|
||||||
|
}
|
||||||
|
"local" => {
|
||||||
|
let name = item.name.unwrap_or("Local File".into());
|
||||||
|
let desc = item.desc.unwrap_or("".into());
|
||||||
|
PrfItem::from_local(name, desc, file_data)
|
||||||
|
}
|
||||||
|
"merge" => {
|
||||||
|
let name = item.name.unwrap_or("Merge".into());
|
||||||
|
let desc = item.desc.unwrap_or("".into());
|
||||||
|
PrfItem::from_merge(name, desc)
|
||||||
|
}
|
||||||
|
"script" => {
|
||||||
|
let name = item.name.unwrap_or("Script".into());
|
||||||
|
let desc = item.desc.unwrap_or("".into());
|
||||||
|
PrfItem::from_script(name, desc)
|
||||||
|
}
|
||||||
|
typ @ _ => bail!("invalid type \"{typ}\""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ## Local type
|
||||||
|
/// create a new item from name/desc
|
||||||
|
pub fn from_local(name: String, desc: String, file_data: Option<String>) -> Result<PrfItem> {
|
||||||
|
let uid = help::get_uid("l");
|
||||||
|
let file = format!("{uid}.yaml");
|
||||||
|
|
||||||
|
Ok(PrfItem {
|
||||||
|
uid: Some(uid),
|
||||||
|
itype: Some("local".into()),
|
||||||
|
name: Some(name),
|
||||||
|
desc: Some(desc),
|
||||||
|
file: Some(file),
|
||||||
|
url: None,
|
||||||
|
selected: None,
|
||||||
|
extra: None,
|
||||||
|
option: None,
|
||||||
|
updated: Some(help::get_now()),
|
||||||
|
file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ## Remote type
|
||||||
|
/// create a new item from url
|
||||||
|
pub async fn from_url(
|
||||||
|
url: &str,
|
||||||
|
name: Option<String>,
|
||||||
|
desc: Option<String>,
|
||||||
|
option: Option<PrfOption>,
|
||||||
|
) -> Result<PrfItem> {
|
||||||
|
let with_proxy = match option.as_ref() {
|
||||||
|
Some(opt) => opt.with_proxy.unwrap_or(false),
|
||||||
|
None => false,
|
||||||
|
};
|
||||||
|
let user_agent = match option.as_ref() {
|
||||||
|
Some(opt) => opt.user_agent.clone(),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut builder = reqwest::ClientBuilder::new();
|
||||||
|
|
||||||
|
if !with_proxy {
|
||||||
|
builder = builder.no_proxy();
|
||||||
|
}
|
||||||
|
|
||||||
|
builder = builder.user_agent(user_agent.unwrap_or("clash-verge/v0.1.0".into()));
|
||||||
|
|
||||||
|
let resp = builder.build()?.get(url).send().await?;
|
||||||
|
let header = resp.headers();
|
||||||
|
|
||||||
|
// parse the Subscription Userinfo
|
||||||
|
let extra = match header.get("Subscription-Userinfo") {
|
||||||
|
Some(value) => {
|
||||||
|
let sub_info = value.to_str().unwrap_or("");
|
||||||
|
|
||||||
|
Some(PrfExtra {
|
||||||
|
upload: help::parse_str(sub_info, "upload=").unwrap_or(0),
|
||||||
|
download: help::parse_str(sub_info, "download=").unwrap_or(0),
|
||||||
|
total: help::parse_str(sub_info, "total=").unwrap_or(0),
|
||||||
|
expire: help::parse_str(sub_info, "expire=").unwrap_or(0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let uid = help::get_uid("r");
|
||||||
|
let file = format!("{uid}.yaml");
|
||||||
|
let name = name.unwrap_or(uid.clone());
|
||||||
|
let data = resp.text_with_charset("utf-8").await?;
|
||||||
|
|
||||||
|
Ok(PrfItem {
|
||||||
|
uid: Some(uid),
|
||||||
|
itype: Some("remote".into()),
|
||||||
|
name: Some(name),
|
||||||
|
desc,
|
||||||
|
file: Some(file),
|
||||||
|
url: Some(url.into()),
|
||||||
|
selected: None,
|
||||||
|
extra,
|
||||||
|
option,
|
||||||
|
updated: Some(help::get_now()),
|
||||||
|
file_data: Some(data),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ## Merge type (enhance)
|
||||||
|
/// create the enhanced item by using `merge` rule
|
||||||
|
pub fn from_merge(name: String, desc: String) -> Result<PrfItem> {
|
||||||
|
let uid = help::get_uid("m");
|
||||||
|
let file = format!("{uid}.yaml");
|
||||||
|
|
||||||
|
Ok(PrfItem {
|
||||||
|
uid: Some(uid),
|
||||||
|
itype: Some("merge".into()),
|
||||||
|
name: Some(name),
|
||||||
|
desc: Some(desc),
|
||||||
|
file: Some(file),
|
||||||
|
url: None,
|
||||||
|
selected: None,
|
||||||
|
extra: None,
|
||||||
|
option: None,
|
||||||
|
updated: Some(help::get_now()),
|
||||||
|
file_data: Some(tmpl::ITEM_MERGE.into()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ## Script type (enhance)
|
||||||
|
/// create the enhanced item by using javascript(browserjs)
|
||||||
|
pub fn from_script(name: String, desc: String) -> Result<PrfItem> {
|
||||||
|
let uid = help::get_uid("s");
|
||||||
|
let file = format!("{uid}.js"); // js ext
|
||||||
|
|
||||||
|
Ok(PrfItem {
|
||||||
|
uid: Some(uid),
|
||||||
|
itype: Some("script".into()),
|
||||||
|
name: Some(name),
|
||||||
|
desc: Some(desc),
|
||||||
|
file: Some(file),
|
||||||
|
url: None,
|
||||||
|
selected: None,
|
||||||
|
extra: None,
|
||||||
|
option: None,
|
||||||
|
updated: Some(help::get_now()),
|
||||||
|
file_data: Some(tmpl::ITEM_SCRIPT.into()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get the file data
|
||||||
|
pub fn read_file(&self) -> Result<String> {
|
||||||
|
if self.file.is_none() {
|
||||||
|
bail!("could not find the file");
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = self.file.clone().unwrap();
|
||||||
|
let path = dirs::app_profiles_dir().join(file);
|
||||||
|
fs::read_to_string(path).context("failed to read the file")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// save the file data
|
||||||
|
pub fn save_file(&self, data: String) -> Result<()> {
|
||||||
|
if self.file.is_none() {
|
||||||
|
bail!("could not find the file");
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = self.file.clone().unwrap();
|
||||||
|
let path = dirs::app_profiles_dir().join(file);
|
||||||
|
fs::write(path, data.as_bytes()).context("failed to save the file")
|
||||||
|
}
|
||||||
|
}
|
@ -1,307 +1,11 @@
|
|||||||
use crate::utils::{config, dirs, help, tmpl};
|
use super::enhance::{PrfData, PrfEnhanced};
|
||||||
|
use super::prfitem::PrfItem;
|
||||||
|
use crate::utils::{config, dirs, help};
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml::Mapping;
|
use serde_yaml::Mapping;
|
||||||
use std::{fs, io::Write};
|
use std::{fs, io::Write};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct PrfItem {
|
|
||||||
pub uid: Option<String>,
|
|
||||||
|
|
||||||
/// profile item type
|
|
||||||
/// enum value: remote | local | script | merge
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
pub itype: Option<String>,
|
|
||||||
|
|
||||||
/// profile name
|
|
||||||
pub name: Option<String>,
|
|
||||||
|
|
||||||
/// profile description
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub desc: Option<String>,
|
|
||||||
|
|
||||||
/// profile file
|
|
||||||
pub file: Option<String>,
|
|
||||||
|
|
||||||
/// source url
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub url: Option<String>,
|
|
||||||
|
|
||||||
/// selected infomation
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub selected: Option<Vec<PrfSelected>>,
|
|
||||||
|
|
||||||
/// subscription user info
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub extra: Option<PrfExtra>,
|
|
||||||
|
|
||||||
/// updated time
|
|
||||||
pub updated: Option<usize>,
|
|
||||||
|
|
||||||
/// some options of the item
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub option: Option<PrfOption>,
|
|
||||||
|
|
||||||
/// the file data
|
|
||||||
#[serde(skip)]
|
|
||||||
pub file_data: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct PrfSelected {
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub now: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Copy, Deserialize, Serialize)]
|
|
||||||
pub struct PrfExtra {
|
|
||||||
pub upload: usize,
|
|
||||||
pub download: usize,
|
|
||||||
pub total: usize,
|
|
||||||
pub expire: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct PrfOption {
|
|
||||||
/// for `remote` profile's http request
|
|
||||||
/// see issue #13
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub user_agent: Option<String>,
|
|
||||||
|
|
||||||
/// for `remote` profile
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub with_proxy: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrfOption {
|
|
||||||
pub fn merge(one: Option<Self>, other: Option<Self>) -> Option<Self> {
|
|
||||||
if one.is_some() && other.is_some() {
|
|
||||||
let mut one = one.unwrap();
|
|
||||||
let other = other.unwrap();
|
|
||||||
|
|
||||||
if let Some(val) = other.user_agent {
|
|
||||||
one.user_agent = Some(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(val) = other.with_proxy {
|
|
||||||
one.with_proxy = Some(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Some(one);
|
|
||||||
}
|
|
||||||
|
|
||||||
if one.is_none() {
|
|
||||||
return other;
|
|
||||||
}
|
|
||||||
|
|
||||||
return one;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PrfItem {
|
|
||||||
fn default() -> Self {
|
|
||||||
PrfItem {
|
|
||||||
uid: None,
|
|
||||||
itype: None,
|
|
||||||
name: None,
|
|
||||||
desc: None,
|
|
||||||
file: None,
|
|
||||||
url: None,
|
|
||||||
selected: None,
|
|
||||||
extra: None,
|
|
||||||
updated: None,
|
|
||||||
option: None,
|
|
||||||
file_data: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrfItem {
|
|
||||||
/// From partial item
|
|
||||||
/// must contain `itype`
|
|
||||||
pub async fn from(item: PrfItem, file_data: Option<String>) -> Result<PrfItem> {
|
|
||||||
if item.itype.is_none() {
|
|
||||||
bail!("type should not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
match item.itype.unwrap().as_str() {
|
|
||||||
"remote" => {
|
|
||||||
if item.url.is_none() {
|
|
||||||
bail!("url should not be null");
|
|
||||||
}
|
|
||||||
let url = item.url.as_ref().unwrap().as_str();
|
|
||||||
let name = item.name;
|
|
||||||
let desc = item.desc;
|
|
||||||
PrfItem::from_url(url, name, desc, item.option).await
|
|
||||||
}
|
|
||||||
"local" => {
|
|
||||||
let name = item.name.unwrap_or("Local File".into());
|
|
||||||
let desc = item.desc.unwrap_or("".into());
|
|
||||||
PrfItem::from_local(name, desc, file_data)
|
|
||||||
}
|
|
||||||
"merge" => {
|
|
||||||
let name = item.name.unwrap_or("Merge".into());
|
|
||||||
let desc = item.desc.unwrap_or("".into());
|
|
||||||
PrfItem::from_merge(name, desc)
|
|
||||||
}
|
|
||||||
"script" => {
|
|
||||||
let name = item.name.unwrap_or("Script".into());
|
|
||||||
let desc = item.desc.unwrap_or("".into());
|
|
||||||
PrfItem::from_script(name, desc)
|
|
||||||
}
|
|
||||||
typ @ _ => bail!("invalid type \"{typ}\""),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ## Local type
|
|
||||||
/// create a new item from name/desc
|
|
||||||
pub fn from_local(name: String, desc: String, file_data: Option<String>) -> Result<PrfItem> {
|
|
||||||
let uid = help::get_uid("l");
|
|
||||||
let file = format!("{uid}.yaml");
|
|
||||||
|
|
||||||
Ok(PrfItem {
|
|
||||||
uid: Some(uid),
|
|
||||||
itype: Some("local".into()),
|
|
||||||
name: Some(name),
|
|
||||||
desc: Some(desc),
|
|
||||||
file: Some(file),
|
|
||||||
url: None,
|
|
||||||
selected: None,
|
|
||||||
extra: None,
|
|
||||||
option: None,
|
|
||||||
updated: Some(help::get_now()),
|
|
||||||
file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ## Remote type
|
|
||||||
/// create a new item from url
|
|
||||||
pub async fn from_url(
|
|
||||||
url: &str,
|
|
||||||
name: Option<String>,
|
|
||||||
desc: Option<String>,
|
|
||||||
option: Option<PrfOption>,
|
|
||||||
) -> Result<PrfItem> {
|
|
||||||
let with_proxy = match option.as_ref() {
|
|
||||||
Some(opt) => opt.with_proxy.unwrap_or(false),
|
|
||||||
None => false,
|
|
||||||
};
|
|
||||||
let user_agent = match option.as_ref() {
|
|
||||||
Some(opt) => opt.user_agent.clone(),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut builder = reqwest::ClientBuilder::new();
|
|
||||||
|
|
||||||
if !with_proxy {
|
|
||||||
builder = builder.no_proxy();
|
|
||||||
}
|
|
||||||
|
|
||||||
builder = builder.user_agent(user_agent.unwrap_or("clash-verge/v0.1.0".into()));
|
|
||||||
|
|
||||||
let resp = builder.build()?.get(url).send().await?;
|
|
||||||
let header = resp.headers();
|
|
||||||
|
|
||||||
// parse the Subscription Userinfo
|
|
||||||
let extra = match header.get("Subscription-Userinfo") {
|
|
||||||
Some(value) => {
|
|
||||||
let sub_info = value.to_str().unwrap_or("");
|
|
||||||
|
|
||||||
Some(PrfExtra {
|
|
||||||
upload: help::parse_str(sub_info, "upload=").unwrap_or(0),
|
|
||||||
download: help::parse_str(sub_info, "download=").unwrap_or(0),
|
|
||||||
total: help::parse_str(sub_info, "total=").unwrap_or(0),
|
|
||||||
expire: help::parse_str(sub_info, "expire=").unwrap_or(0),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let uid = help::get_uid("r");
|
|
||||||
let file = format!("{uid}.yaml");
|
|
||||||
let name = name.unwrap_or(uid.clone());
|
|
||||||
let data = resp.text_with_charset("utf-8").await?;
|
|
||||||
|
|
||||||
Ok(PrfItem {
|
|
||||||
uid: Some(uid),
|
|
||||||
itype: Some("remote".into()),
|
|
||||||
name: Some(name),
|
|
||||||
desc,
|
|
||||||
file: Some(file),
|
|
||||||
url: Some(url.into()),
|
|
||||||
selected: None,
|
|
||||||
extra,
|
|
||||||
option,
|
|
||||||
updated: Some(help::get_now()),
|
|
||||||
file_data: Some(data),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ## Merge type (enhance)
|
|
||||||
/// create the enhanced item by using `merge` rule
|
|
||||||
pub fn from_merge(name: String, desc: String) -> Result<PrfItem> {
|
|
||||||
let uid = help::get_uid("m");
|
|
||||||
let file = format!("{uid}.yaml");
|
|
||||||
|
|
||||||
Ok(PrfItem {
|
|
||||||
uid: Some(uid),
|
|
||||||
itype: Some("merge".into()),
|
|
||||||
name: Some(name),
|
|
||||||
desc: Some(desc),
|
|
||||||
file: Some(file),
|
|
||||||
url: None,
|
|
||||||
selected: None,
|
|
||||||
extra: None,
|
|
||||||
option: None,
|
|
||||||
updated: Some(help::get_now()),
|
|
||||||
file_data: Some(tmpl::ITEM_MERGE.into()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ## Script type (enhance)
|
|
||||||
/// create the enhanced item by using javascript(browserjs)
|
|
||||||
pub fn from_script(name: String, desc: String) -> Result<PrfItem> {
|
|
||||||
let uid = help::get_uid("s");
|
|
||||||
let file = format!("{uid}.js"); // js ext
|
|
||||||
|
|
||||||
Ok(PrfItem {
|
|
||||||
uid: Some(uid),
|
|
||||||
itype: Some("script".into()),
|
|
||||||
name: Some(name),
|
|
||||||
desc: Some(desc),
|
|
||||||
file: Some(file),
|
|
||||||
url: None,
|
|
||||||
selected: None,
|
|
||||||
extra: None,
|
|
||||||
option: None,
|
|
||||||
updated: Some(help::get_now()),
|
|
||||||
file_data: Some(tmpl::ITEM_SCRIPT.into()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get the file data
|
|
||||||
pub fn read_file(&self) -> Result<String> {
|
|
||||||
if self.file.is_none() {
|
|
||||||
bail!("could not find the file");
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = self.file.clone().unwrap();
|
|
||||||
let path = dirs::app_profiles_dir().join(file);
|
|
||||||
fs::read_to_string(path).context("failed to read the file")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// save the file data
|
|
||||||
pub fn save_file(&self, data: String) -> Result<()> {
|
|
||||||
if self.file.is_none() {
|
|
||||||
bail!("could not find the file");
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = self.file.clone().unwrap();
|
|
||||||
let path = dirs::app_profiles_dir().join(file);
|
|
||||||
fs::write(path, data.as_bytes()).context("failed to save the file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// ## Profiles Config
|
/// ## Profiles Config
|
||||||
///
|
///
|
||||||
@ -331,6 +35,10 @@ macro_rules! patch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Profiles {
|
impl Profiles {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Profiles::read_file()
|
||||||
|
}
|
||||||
|
|
||||||
/// read the config from the file
|
/// read the config from the file
|
||||||
pub fn read_file() -> Self {
|
pub fn read_file() -> Self {
|
||||||
let mut profiles = config::read_yaml::<Self>(dirs::profiles_path());
|
let mut profiles = config::read_yaml::<Self>(dirs::profiles_path());
|
||||||
@ -616,64 +324,3 @@ impl Profiles {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct PrfEnhanced {
|
|
||||||
pub current: Mapping,
|
|
||||||
|
|
||||||
pub chain: Vec<PrfData>,
|
|
||||||
|
|
||||||
pub valid: Vec<String>,
|
|
||||||
|
|
||||||
pub callback: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct PrfEnhancedResult {
|
|
||||||
pub data: Option<Mapping>,
|
|
||||||
|
|
||||||
pub status: String,
|
|
||||||
|
|
||||||
pub error: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct PrfData {
|
|
||||||
item: PrfItem,
|
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
merge: Option<Mapping>,
|
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
script: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrfData {
|
|
||||||
pub fn from_item(item: &PrfItem) -> Option<PrfData> {
|
|
||||||
match item.itype.as_ref() {
|
|
||||||
Some(itype) => {
|
|
||||||
let file = item.file.clone()?;
|
|
||||||
let path = dirs::app_profiles_dir().join(file);
|
|
||||||
|
|
||||||
if !path.exists() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match itype.as_str() {
|
|
||||||
"script" => Some(PrfData {
|
|
||||||
item: item.clone(),
|
|
||||||
script: Some(fs::read_to_string(path).unwrap_or("".into())),
|
|
||||||
merge: None,
|
|
||||||
}),
|
|
||||||
"merge" => Some(PrfData {
|
|
||||||
item: item.clone(),
|
|
||||||
merge: Some(config::read_yaml::<Mapping>(path)),
|
|
||||||
script: None,
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
121
src-tauri/src/core/service.rs
Normal file
121
src-tauri/src/core/service.rs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
use super::{notice::Notice, ClashInfo};
|
||||||
|
use crate::log_if_err;
|
||||||
|
use crate::utils::{config, dirs};
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use reqwest::header::HeaderMap;
|
||||||
|
use serde_yaml::Mapping;
|
||||||
|
use std::{collections::HashMap, time::Duration};
|
||||||
|
use tauri::api::process::{Command, CommandChild, CommandEvent};
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Service {
|
||||||
|
sidecar: Option<CommandChild>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service {
|
||||||
|
pub fn new() -> Service {
|
||||||
|
Service { sidecar: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self) -> Result<()> {
|
||||||
|
if self.sidecar.is_some() {
|
||||||
|
bail!("could not run clash sidecar twice");
|
||||||
|
}
|
||||||
|
|
||||||
|
let app_dir = dirs::app_home_dir();
|
||||||
|
let app_dir = app_dir.as_os_str().to_str().unwrap();
|
||||||
|
|
||||||
|
let cmd = Command::new_sidecar("clash")?;
|
||||||
|
let (mut rx, cmd_child) = cmd.args(["-d", app_dir]).spawn()?;
|
||||||
|
|
||||||
|
self.sidecar = Some(cmd_child);
|
||||||
|
|
||||||
|
// clash log
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
while let Some(event) = rx.recv().await {
|
||||||
|
match event {
|
||||||
|
CommandEvent::Stdout(line) => log::info!("[clash]: {}", line),
|
||||||
|
CommandEvent::Stderr(err) => log::error!("[clash]: {}", err),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self) -> Result<()> {
|
||||||
|
if let Some(sidecar) = self.sidecar.take() {
|
||||||
|
sidecar.kill()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restart(&mut self) -> Result<()> {
|
||||||
|
self.stop()?;
|
||||||
|
self.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_config(&self, info: ClashInfo, config: Mapping, notice: Notice) -> Result<()> {
|
||||||
|
if self.sidecar.is_none() {
|
||||||
|
bail!("did not start sidecar");
|
||||||
|
}
|
||||||
|
|
||||||
|
let temp_path = dirs::profiles_temp_path();
|
||||||
|
config::save_yaml(temp_path.clone(), &config, Some("# Clash Verge Temp File"))?;
|
||||||
|
|
||||||
|
if info.server.is_none() {
|
||||||
|
bail!("failed to parse the server");
|
||||||
|
}
|
||||||
|
|
||||||
|
let server = info.server.unwrap();
|
||||||
|
let server = format!("http://{server}/configs");
|
||||||
|
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
headers.insert("Content-Type", "application/json".parse().unwrap());
|
||||||
|
|
||||||
|
if let Some(secret) = info.secret.as_ref() {
|
||||||
|
let secret = format!("Bearer {}", secret.clone()).parse().unwrap();
|
||||||
|
headers.insert("Authorization", secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
let mut data = HashMap::new();
|
||||||
|
data.insert("path", temp_path.as_os_str().to_str().unwrap());
|
||||||
|
|
||||||
|
// retry 5 times
|
||||||
|
for _ in 0..5 {
|
||||||
|
match reqwest::ClientBuilder::new().no_proxy().build() {
|
||||||
|
Ok(client) => {
|
||||||
|
let builder = client.put(&server).headers(headers.clone()).json(&data);
|
||||||
|
|
||||||
|
match builder.send().await {
|
||||||
|
Ok(resp) => {
|
||||||
|
if resp.status() != 204 {
|
||||||
|
log::error!("failed to activate clash with status \"{}\"", resp.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
notice.refresh_clash();
|
||||||
|
|
||||||
|
// do not retry
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(err) => log::error!("failed to activate for `{err}`"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => log::error!("failed to activate for `{err}`"),
|
||||||
|
}
|
||||||
|
sleep(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Service {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
log_if_err!(self.stop());
|
||||||
|
}
|
||||||
|
}
|
20
src-tauri/src/core/timer.rs
Normal file
20
src-tauri/src/core/timer.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, Task, TaskBuilder};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct Timer {
|
||||||
|
delay_timer: DelayTimer,
|
||||||
|
|
||||||
|
timer_map: HashMap<String, u64>,
|
||||||
|
|
||||||
|
timer_count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Timer {
|
||||||
|
delay_timer: DelayTimerBuilder::default().build(),
|
||||||
|
timer_map: HashMap::new(),
|
||||||
|
timer_count: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
mod cmds;
|
mod cmds;
|
||||||
mod core;
|
mod core;
|
||||||
mod states;
|
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -39,9 +38,7 @@ fn main() -> std::io::Result<()> {
|
|||||||
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut builder = tauri::Builder::default()
|
let mut builder = tauri::Builder::default()
|
||||||
.manage(states::VergeState::default())
|
.manage(core::Core::new())
|
||||||
.manage(states::ClashState::default())
|
|
||||||
.manage(states::ProfilesState::default())
|
|
||||||
.setup(|app| Ok(resolve::resolve_setup(app)))
|
.setup(|app| Ok(resolve::resolve_setup(app)))
|
||||||
.system_tray(SystemTray::new().with_menu(tray_menu))
|
.system_tray(SystemTray::new().with_menu(tray_menu))
|
||||||
.on_system_tray_event(move |app_handle, event| match event {
|
.on_system_tray_event(move |app_handle, event| match event {
|
||||||
@ -53,8 +50,8 @@ fn main() -> std::io::Result<()> {
|
|||||||
window.set_focus().unwrap();
|
window.set_focus().unwrap();
|
||||||
}
|
}
|
||||||
"system_proxy" => {
|
"system_proxy" => {
|
||||||
let verge_state = app_handle.state::<states::VergeState>();
|
let core = app_handle.state::<core::Core>();
|
||||||
let mut verge = verge_state.0.lock().unwrap();
|
let mut verge = core.verge.lock().unwrap();
|
||||||
|
|
||||||
let old_value = verge.config.enable_system_proxy.clone().unwrap_or(false);
|
let old_value = verge.config.enable_system_proxy.clone().unwrap_or(false);
|
||||||
let new_value = !old_value;
|
let new_value = !old_value;
|
||||||
@ -68,8 +65,8 @@ fn main() -> std::io::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"tun_mode" => {
|
"tun_mode" => {
|
||||||
let verge_state = app_handle.state::<states::VergeState>();
|
let core = app_handle.state::<core::Core>();
|
||||||
let mut verge = verge_state.0.lock().unwrap();
|
let mut verge = core.verge.lock().unwrap();
|
||||||
|
|
||||||
let old_value = verge.config.enable_tun_mode.clone().unwrap_or(false);
|
let old_value = verge.config.enable_tun_mode.clone().unwrap_or(false);
|
||||||
let new_value = !old_value;
|
let new_value = !old_value;
|
||||||
@ -83,12 +80,8 @@ fn main() -> std::io::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"restart_clash" => {
|
"restart_clash" => {
|
||||||
let clash_state = app_handle.state::<states::ClashState>();
|
let core = app_handle.state::<core::Core>();
|
||||||
let profiles_state = app_handle.state::<states::ProfilesState>();
|
crate::log_if_err!(core.restart_clash());
|
||||||
let mut clash = clash_state.0.lock().unwrap();
|
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
|
||||||
|
|
||||||
crate::log_if_err!(clash.restart_sidecar(&mut profiles));
|
|
||||||
}
|
}
|
||||||
"quit" => {
|
"quit" => {
|
||||||
resolve::resolve_reset(app_handle);
|
resolve::resolve_reset(app_handle);
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use crate::core::{Clash, Profiles, Verge};
|
// use crate::core::{Clash, Profiles, Verge};
|
||||||
use std::sync::{Arc, Mutex};
|
// use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
#[derive(Default)]
|
// #[derive(Default)]
|
||||||
pub struct ProfilesState(pub Arc<Mutex<Profiles>>);
|
// pub struct ProfilesState(pub Arc<Mutex<Profiles>>);
|
||||||
|
|
||||||
#[derive(Default)]
|
// #[derive(Default)]
|
||||||
pub struct ClashState(pub Arc<Mutex<Clash>>);
|
// pub struct ClashState(pub Arc<Mutex<Clash>>);
|
||||||
|
|
||||||
#[derive(Default)]
|
// #[derive(Default)]
|
||||||
pub struct VergeState(pub Arc<Mutex<Verge>>);
|
// pub struct VergeState(pub Arc<Mutex<Verge>>);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::log_if_err;
|
use crate::log_if_err;
|
||||||
use crate::{core::Profiles, states, utils::init, utils::server};
|
use crate::{core::Core, utils::init, utils::server};
|
||||||
use tauri::{App, AppHandle, Manager};
|
use tauri::{App, AppHandle, Manager};
|
||||||
|
|
||||||
/// handle something when start app
|
/// handle something when start app
|
||||||
@ -11,36 +11,21 @@ pub fn resolve_setup(app: &App) {
|
|||||||
init::init_app(app.package_info());
|
init::init_app(app.package_info());
|
||||||
|
|
||||||
// init states
|
// init states
|
||||||
let clash_state = app.state::<states::ClashState>();
|
let core = app.state::<Core>();
|
||||||
let verge_state = app.state::<states::VergeState>();
|
|
||||||
let profiles_state = app.state::<states::ProfilesState>();
|
|
||||||
|
|
||||||
let mut clash = clash_state.0.lock().unwrap();
|
core.set_win(app.get_window("main"));
|
||||||
let mut verge = verge_state.0.lock().unwrap();
|
core.init();
|
||||||
let mut profiles = profiles_state.0.lock().unwrap();
|
|
||||||
|
|
||||||
*profiles = Profiles::read_file();
|
// clash.set_window(app.get_window("main"));
|
||||||
|
// log_if_err!(clash.run_sidecar(&profiles, true));
|
||||||
|
|
||||||
clash.set_window(app.get_window("main"));
|
resolve_window(app, None);
|
||||||
log_if_err!(clash.run_sidecar(&profiles, true));
|
|
||||||
|
|
||||||
verge.init_sysproxy(clash.info.port.clone());
|
|
||||||
log_if_err!(verge.init_launch());
|
|
||||||
|
|
||||||
verge.config.enable_system_proxy.map(|enable| {
|
|
||||||
log_if_err!(app
|
|
||||||
.tray_handle()
|
|
||||||
.get_item("system_proxy")
|
|
||||||
.set_selected(enable));
|
|
||||||
});
|
|
||||||
|
|
||||||
resolve_window(app, verge.config.enable_silent_start.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// reset system proxy
|
/// reset system proxy
|
||||||
pub fn resolve_reset(app_handle: &AppHandle) {
|
pub fn resolve_reset(app_handle: &AppHandle) {
|
||||||
let verge_state = app_handle.state::<states::VergeState>();
|
let core = app_handle.state::<Core>();
|
||||||
let mut verge = verge_state.0.lock().unwrap();
|
let mut verge = core.verge.lock().unwrap();
|
||||||
|
|
||||||
verge.reset_sysproxy();
|
verge.reset_sysproxy();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user