mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 08:45:41 +08:00
fix: clippy errors with new config (#4428)
* refactor: improve code quality with clippy fixes and standardized logging
- Replace dangerous unwrap()/expect() calls with proper error handling
- Standardize logging from log:: to logging\! macro with Type:: classifications
- Fix app handle panics with graceful fallback patterns
- Improve error resilience across 35+ modules without breaking functionality
- Reduce clippy warnings from 300+ to 0 in main library code
* chore: update Cargo.toml configuration
* refactor: resolve all clippy warnings
- Fix Arc clone warnings using explicit Arc::clone syntax across 9 files
- Add #[allow(clippy::expect_used)] to test functions for appropriate expect usage
- Remove no-effect statements from debug code cleanup
- Apply clippy auto-fixes for dbg\! macro removals and path statements
- Achieve zero clippy warnings on all targets with -D warnings flag
* chore: update Cargo.toml clippy configuration
* refactor: simplify macOS job configuration and improve caching
* refactor: remove unnecessary async/await from service and proxy functions
* refactor: streamline pnpm installation in CI configuration
* refactor: simplify error handling and remove unnecessary else statements
* refactor: replace async/await with synchronous locks for core management
* refactor: add workflow_dispatch trigger to clippy job
* refactor: convert async functions to synchronous for service management
* refactor: convert async functions to synchronous for UWP tool invocation
* fix: change wrong logging
* refactor: convert proxy restoration functions to async
* Revert "refactor: convert proxy restoration functions to async"
This reverts commit b82f5d250b.
* refactor: update proxy restoration functions to return Result types
* fix: handle errors during proxy restoration and update async function signatures
* fix: handle errors during proxy restoration and update async function signatures
* refactor: update restore_pac_proxy and restore_sys_proxy functions to async
* fix: convert restore_pac_proxy and restore_sys_proxy functions to async
* fix: await restore_sys_proxy calls in proxy restoration logic
* fix: suppress clippy warnings for unused async functions in proxy restoration
* fix: suppress clippy warnings for unused async functions in proxy restoration
This commit is contained in:
1
.github/workflows/clippy.yml
vendored
1
.github/workflows/clippy.yml
vendored
@@ -2,6 +2,7 @@ name: Clippy Lint
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
clippy:
|
clippy:
|
||||||
|
|||||||
30
.github/workflows/dev.yml
vendored
30
.github/workflows/dev.yml
vendored
@@ -13,11 +13,6 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
run_macos_x86_64:
|
|
||||||
description: "运行 macOS x86_64"
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
|
|
||||||
permissions: write-all
|
permissions: write-all
|
||||||
env:
|
env:
|
||||||
@@ -45,14 +40,13 @@ jobs:
|
|||||||
bundle: dmg
|
bundle: dmg
|
||||||
id: macos-aarch64
|
id: macos-aarch64
|
||||||
input: run_macos_aarch64
|
input: run_macos_aarch64
|
||||||
- os: macos-latest
|
|
||||||
target: x86_64-apple-darwin
|
|
||||||
bundle: dmg
|
|
||||||
id: macos-x86_64
|
|
||||||
input: run_macos_x86_64
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
|
- name: Skip job if not selected
|
||||||
|
if: github.event.inputs[matrix.input] != 'true'
|
||||||
|
run: echo "Job ${{ matrix.id }} skipped as requested"
|
||||||
|
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
if: github.event.inputs[matrix.input] == 'true'
|
if: github.event.inputs[matrix.input] == 'true'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -71,21 +65,22 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
workspaces: src-tauri
|
workspaces: src-tauri
|
||||||
save-if: false
|
save-if: false
|
||||||
cache-all-crates: false
|
cache-all-crates: true
|
||||||
shared-key: autobuild-shared
|
shared-key: autobuild-shared
|
||||||
|
|
||||||
- name: Install Node
|
|
||||||
if: github.event.inputs[matrix.input] == 'true'
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "20"
|
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
if: github.event.inputs[matrix.input] == 'true'
|
if: github.event.inputs[matrix.input] == 'true'
|
||||||
with:
|
with:
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
if: github.event.inputs[matrix.input] == 'true'
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "20"
|
||||||
|
cache: "pnpm"
|
||||||
|
|
||||||
- name: Pnpm install and check
|
- name: Pnpm install and check
|
||||||
if: github.event.inputs[matrix.input] == 'true'
|
if: github.event.inputs[matrix.input] == 'true'
|
||||||
run: |
|
run: |
|
||||||
@@ -93,6 +88,7 @@ jobs:
|
|||||||
pnpm run prebuild ${{ matrix.target }}
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Release ${{ env.TAG_CHANNEL }} Version
|
- name: Release ${{ env.TAG_CHANNEL }} Version
|
||||||
|
if: github.event.inputs[matrix.input] == 'true'
|
||||||
run: pnpm release-version ${{ env.TAG_NAME }}
|
run: pnpm release-version ${{ env.TAG_NAME }}
|
||||||
|
|
||||||
- name: Tauri build
|
- name: Tauri build
|
||||||
|
|||||||
347
src-tauri/Cargo.lock
generated
347
src-tauri/Cargo.lock
generated
@@ -74,15 +74,6 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aligned-vec"
|
|
||||||
version = "0.6.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
|
|
||||||
dependencies = [
|
|
||||||
"equator",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloc-no-stdlib"
|
name = "alloc-no-stdlib"
|
||||||
version = "2.0.4"
|
version = "2.0.4"
|
||||||
@@ -173,17 +164,6 @@ version = "1.7.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arg_enum_proc_macro"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.104",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
@@ -490,29 +470,6 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "av1-grain"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"arrayvec",
|
|
||||||
"log",
|
|
||||||
"nom 7.1.3",
|
|
||||||
"num-rational",
|
|
||||||
"v_frame",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "avif-serialize"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42"
|
|
||||||
dependencies = [
|
|
||||||
"arrayvec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.6.20"
|
version = "0.6.20"
|
||||||
@@ -591,12 +548,6 @@ version = "0.22.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit_field"
|
|
||||||
version = "0.10.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@@ -612,12 +563,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitstream-io"
|
|
||||||
version = "2.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block"
|
name = "block"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
@@ -833,12 +778,6 @@ dependencies = [
|
|||||||
"alloc-stdlib",
|
"alloc-stdlib",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "built"
|
|
||||||
version = "0.7.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.19.0"
|
version = "3.19.0"
|
||||||
@@ -1111,7 +1050,6 @@ version = "2.4.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"boa_engine",
|
"boa_engine",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -1122,14 +1060,11 @@ dependencies = [
|
|||||||
"dirs 6.0.0",
|
"dirs 6.0.0",
|
||||||
"dunce",
|
"dunce",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
|
||||||
"gethostname 1.0.2",
|
"gethostname 1.0.2",
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"hex",
|
"hex",
|
||||||
"hmac",
|
"hmac",
|
||||||
"image",
|
|
||||||
"kode-bridge",
|
"kode-bridge",
|
||||||
"lazy_static",
|
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"log4rs",
|
"log4rs",
|
||||||
@@ -1166,7 +1101,6 @@ dependencies = [
|
|||||||
"tauri-plugin-shell",
|
"tauri-plugin-shell",
|
||||||
"tauri-plugin-updater",
|
"tauri-plugin-updater",
|
||||||
"tauri-plugin-window-state",
|
"tauri-plugin-window-state",
|
||||||
"tempfile",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"users",
|
"users",
|
||||||
"warp",
|
"warp",
|
||||||
@@ -1213,12 +1147,6 @@ dependencies = [
|
|||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "color_quant"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colored"
|
name = "colored"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@@ -1991,26 +1919,6 @@ dependencies = [
|
|||||||
"syn 2.0.104",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "equator"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
|
|
||||||
dependencies = [
|
|
||||||
"equator-macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "equator-macro"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.104",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@@ -2081,21 +1989,6 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "exr"
|
|
||||||
version = "1.73.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
|
|
||||||
dependencies = [
|
|
||||||
"bit_field",
|
|
||||||
"half",
|
|
||||||
"lebe",
|
|
||||||
"miniz_oxide",
|
|
||||||
"rayon-core",
|
|
||||||
"smallvec",
|
|
||||||
"zune-inflate",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fast-float2"
|
name = "fast-float2"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
@@ -2552,16 +2445,6 @@ dependencies = [
|
|||||||
"polyval",
|
"polyval",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gif"
|
|
||||||
version = "0.13.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b"
|
|
||||||
dependencies = [
|
|
||||||
"color_quant",
|
|
||||||
"weezl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.31.1"
|
version = "0.31.1"
|
||||||
@@ -3378,37 +3261,11 @@ checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder-lite",
|
"byteorder-lite",
|
||||||
"color_quant",
|
|
||||||
"exr",
|
|
||||||
"gif",
|
|
||||||
"image-webp",
|
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"png",
|
"png",
|
||||||
"qoi",
|
|
||||||
"ravif",
|
|
||||||
"rayon",
|
|
||||||
"rgb",
|
|
||||||
"tiff",
|
"tiff",
|
||||||
"zune-core",
|
|
||||||
"zune-jpeg",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "image-webp"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder-lite",
|
|
||||||
"quick-error",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "imgref"
|
|
||||||
version = "1.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.3"
|
version = "1.9.3"
|
||||||
@@ -3474,17 +3331,6 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "interpolate_name"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.104",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "interprocess"
|
name = "interprocess"
|
||||||
version = "2.2.3"
|
version = "2.2.3"
|
||||||
@@ -3747,12 +3593,6 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lebe"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libappindicator"
|
name = "libappindicator"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@@ -3783,16 +3623,6 @@ version = "0.2.175"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libfuzzer-sys"
|
|
||||||
version = "0.4.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
|
|
||||||
dependencies = [
|
|
||||||
"arbitrary",
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
@@ -3954,15 +3784,6 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "loop9"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
|
|
||||||
dependencies = [
|
|
||||||
"imgref",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru"
|
name = "lru"
|
||||||
version = "0.12.5"
|
version = "0.12.5"
|
||||||
@@ -4051,16 +3872,6 @@ version = "0.7.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "maybe-rayon"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"rayon",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "md-5"
|
name = "md-5"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -4323,12 +4134,6 @@ dependencies = [
|
|||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "noop_proc_macro"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notify-rust"
|
name = "notify-rust"
|
||||||
version = "4.11.7"
|
version = "4.11.7"
|
||||||
@@ -4390,17 +4195,6 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-derive"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.104",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.46"
|
version = "0.1.46"
|
||||||
@@ -4410,17 +4204,6 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-rational"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
|
||||||
dependencies = [
|
|
||||||
"num-bigint",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -5403,25 +5186,6 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "profiling"
|
|
||||||
version = "1.0.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
|
|
||||||
dependencies = [
|
|
||||||
"profiling-procmacros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "profiling-procmacros"
|
|
||||||
version = "1.0.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.104",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost"
|
name = "prost"
|
||||||
version = "0.12.6"
|
version = "0.12.6"
|
||||||
@@ -5470,15 +5234,6 @@ dependencies = [
|
|||||||
"psl-types",
|
"psl-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "qoi"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-error"
|
name = "quick-error"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
@@ -5683,56 +5438,6 @@ dependencies = [
|
|||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rav1e"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
|
|
||||||
dependencies = [
|
|
||||||
"arbitrary",
|
|
||||||
"arg_enum_proc_macro",
|
|
||||||
"arrayvec",
|
|
||||||
"av1-grain",
|
|
||||||
"bitstream-io",
|
|
||||||
"built",
|
|
||||||
"cfg-if",
|
|
||||||
"interpolate_name",
|
|
||||||
"itertools 0.12.1",
|
|
||||||
"libc",
|
|
||||||
"libfuzzer-sys",
|
|
||||||
"log",
|
|
||||||
"maybe-rayon",
|
|
||||||
"new_debug_unreachable",
|
|
||||||
"noop_proc_macro",
|
|
||||||
"num-derive 0.4.2",
|
|
||||||
"num-traits",
|
|
||||||
"once_cell",
|
|
||||||
"paste",
|
|
||||||
"profiling",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"rand_chacha 0.3.1",
|
|
||||||
"simd_helpers",
|
|
||||||
"system-deps",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"v_frame",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ravif"
|
|
||||||
version = "0.11.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b"
|
|
||||||
dependencies = [
|
|
||||||
"avif-serialize",
|
|
||||||
"imgref",
|
|
||||||
"loop9",
|
|
||||||
"quick-error",
|
|
||||||
"rav1e",
|
|
||||||
"rayon",
|
|
||||||
"rgb",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raw-window-handle"
|
name = "raw-window-handle"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
@@ -5964,12 +5669,6 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rgb"
|
|
||||||
version = "0.8.52"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.14"
|
version = "0.17.14"
|
||||||
@@ -6621,15 +6320,6 @@ version = "0.3.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "simd_helpers"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
@@ -7542,7 +7232,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"memmem",
|
"memmem",
|
||||||
"num-derive 0.3.3",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"regex",
|
"regex",
|
||||||
@@ -8354,17 +8044,6 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "v_frame"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
|
|
||||||
dependencies = [
|
|
||||||
"aligned-vec",
|
|
||||||
"num-traits",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -9812,30 +9491,6 @@ dependencies = [
|
|||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zune-core"
|
|
||||||
version = "0.4.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zune-inflate"
|
|
||||||
version = "0.2.54"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
|
||||||
dependencies = [
|
|
||||||
"simd-adler32",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zune-jpeg"
|
|
||||||
version = "0.4.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a"
|
|
||||||
dependencies = [
|
|
||||||
"zune-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant"
|
name = "zvariant"
|
||||||
version = "5.6.0"
|
version = "5.6.0"
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ boa_engine = "0.20.0"
|
|||||||
serde_json = "1.0.142"
|
serde_json = "1.0.142"
|
||||||
serde_yaml = "0.9.34"
|
serde_yaml = "0.9.34"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
lazy_static = "1.5.0"
|
|
||||||
port_scanner = "0.1.5"
|
port_scanner = "0.1.5"
|
||||||
delay_timer = "0.11.6"
|
delay_timer = "0.11.6"
|
||||||
parking_lot = "0.12.4"
|
parking_lot = "0.12.4"
|
||||||
@@ -41,12 +40,10 @@ tokio = { version = "1.47.1", features = [
|
|||||||
"time",
|
"time",
|
||||||
"sync",
|
"sync",
|
||||||
] }
|
] }
|
||||||
futures-util = "0.3.31"
|
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
reqwest = { version = "0.12.23", features = ["json", "rustls-tls", "cookies"] }
|
reqwest = { version = "0.12.23", features = ["json", "rustls-tls", "cookies"] }
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs" }
|
sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs" }
|
||||||
image = "0.25.6"
|
|
||||||
tauri = { version = "2.7.0", features = [
|
tauri = { version = "2.7.0", features = [
|
||||||
"protocol-asset",
|
"protocol-asset",
|
||||||
"devtools",
|
"devtools",
|
||||||
@@ -70,7 +67,6 @@ base64 = "0.22.1"
|
|||||||
getrandom = "0.3.3"
|
getrandom = "0.3.3"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
sys-locale = "0.3.2"
|
sys-locale = "0.3.2"
|
||||||
async-trait = "0.1.89"
|
|
||||||
libc = "0.2.175"
|
libc = "0.2.175"
|
||||||
gethostname = "1.0.2"
|
gethostname = "1.0.2"
|
||||||
hmac = "0.12.1"
|
hmac = "0.12.1"
|
||||||
@@ -141,4 +137,55 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.7.0"
|
criterion = "0.7.0"
|
||||||
tempfile = "3.20.0"
|
|
||||||
|
[lints.clippy]
|
||||||
|
# Core categories - most important for code safety and correctness
|
||||||
|
correctness = { level = "deny", priority = -1 }
|
||||||
|
suspicious = { level = "deny", priority = -1 }
|
||||||
|
|
||||||
|
# Critical safety lints - warn for now due to extensive existing usage
|
||||||
|
unwrap_used = "warn"
|
||||||
|
expect_used = "warn"
|
||||||
|
panic = "deny"
|
||||||
|
unimplemented = "deny"
|
||||||
|
|
||||||
|
# Development quality lints
|
||||||
|
todo = "warn"
|
||||||
|
dbg_macro = "warn"
|
||||||
|
#print_stdout = "warn"
|
||||||
|
#print_stderr = "warn"
|
||||||
|
|
||||||
|
# Performance lints for proxy application
|
||||||
|
clone_on_ref_ptr = "warn"
|
||||||
|
rc_clone_in_vec_init = "warn"
|
||||||
|
large_stack_arrays = "warn"
|
||||||
|
large_const_arrays = "warn"
|
||||||
|
|
||||||
|
# Security lints
|
||||||
|
#integer_division = "warn"
|
||||||
|
#lossy_float_literal = "warn"
|
||||||
|
#default_numeric_fallback = "warn"
|
||||||
|
|
||||||
|
# Mutex and async lints - strict control
|
||||||
|
async_yields_async = "deny" # Prevents missing await in async blocks
|
||||||
|
mutex_atomic = "deny" # Use atomics instead of Mutex<bool/int>
|
||||||
|
mutex_integer = "deny" # Use AtomicInt instead of Mutex<int>
|
||||||
|
rc_mutex = "deny" # Single-threaded Rc with Mutex is wrong
|
||||||
|
unused_async = "deny" # Too many false positives in Tauri/framework code
|
||||||
|
await_holding_lock = "deny"
|
||||||
|
large_futures = "deny"
|
||||||
|
future_not_send = "deny"
|
||||||
|
|
||||||
|
# Common style improvements
|
||||||
|
redundant_else = "deny" # Too many in existing code
|
||||||
|
needless_continue = "deny" # Too many in existing code
|
||||||
|
needless_raw_string_hashes = "deny" # Too many in existing code
|
||||||
|
|
||||||
|
# Disable noisy categories for existing codebase but keep them available
|
||||||
|
#style = { level = "allow", priority = -1 }
|
||||||
|
#complexity = { level = "allow", priority = -1 }
|
||||||
|
#perf = { level = "allow", priority = -1 }
|
||||||
|
#pedantic = { level = "allow", priority = -1 }
|
||||||
|
#nursery = { level = "allow", priority = -1 }
|
||||||
|
#restriction = { level = "allow", priority = -1 }
|
||||||
|
|
||||||
|
|||||||
@@ -121,9 +121,8 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String>
|
|||||||
Err(_) => {
|
Err(_) => {
|
||||||
if icon_path.exists() {
|
if icon_path.exists() {
|
||||||
return Ok(icon_path.to_string_lossy().to_string());
|
return Ok(icon_path.to_string_lossy().to_string());
|
||||||
} else {
|
|
||||||
return Err("Failed to create temporary file".into());
|
|
||||||
}
|
}
|
||||||
|
return Err("Failed to create temporary file".into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ pub async fn patch_clash_mode(payload: String) -> CmdResult {
|
|||||||
/// 切换Clash核心
|
/// 切换Clash核心
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>> {
|
pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>> {
|
||||||
log::info!(target: "app", "changing core to {clash_core}");
|
logging!(info, Type::Config, "changing core to {clash_core}");
|
||||||
|
|
||||||
match CoreManager::global()
|
match CoreManager::global()
|
||||||
.change_core(Some(clash_core.clone()))
|
.change_core(Some(clash_core.clone()))
|
||||||
@@ -54,14 +54,18 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>>
|
|||||||
// 切换内核后重启内核
|
// 切换内核后重启内核
|
||||||
match CoreManager::global().restart_core().await {
|
match CoreManager::global().restart_core().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
log::info!(target: "app", "core changed and restarted to {clash_core}");
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Core,
|
||||||
|
"core changed and restarted to {clash_core}"
|
||||||
|
);
|
||||||
handle::Handle::notice_message("config_core::change_success", &clash_core);
|
handle::Handle::notice_message("config_core::change_success", &clash_core);
|
||||||
handle::Handle::refresh_clash();
|
handle::Handle::refresh_clash();
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let error_msg = format!("Core changed but failed to restart: {err}");
|
let error_msg = format!("Core changed but failed to restart: {err}");
|
||||||
log::error!(target: "app", "{error_msg}");
|
logging!(error, Type::Core, "{error_msg}");
|
||||||
handle::Handle::notice_message("config_core::change_error", &error_msg);
|
handle::Handle::notice_message("config_core::change_error", &error_msg);
|
||||||
Ok(Some(error_msg))
|
Ok(Some(error_msg))
|
||||||
}
|
}
|
||||||
@@ -69,7 +73,7 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>>
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let error_msg = err.to_string();
|
let error_msg = err.to_string();
|
||||||
log::error!(target: "app", "failed to change core: {error_msg}");
|
logging!(error, Type::Core, "failed to change core: {error_msg}");
|
||||||
handle::Handle::notice_message("config_core::change_error", &error_msg);
|
handle::Handle::notice_message("config_core::change_error", &error_msg);
|
||||||
Ok(Some(error_msg))
|
Ok(Some(error_msg))
|
||||||
}
|
}
|
||||||
@@ -141,7 +145,7 @@ pub async fn save_dns_config(dns_config: Mapping) -> CmdResult {
|
|||||||
// 保存DNS配置到文件
|
// 保存DNS配置到文件
|
||||||
let yaml_str = serde_yaml::to_string(&dns_config).map_err(|e| e.to_string())?;
|
let yaml_str = serde_yaml::to_string(&dns_config).map_err(|e| e.to_string())?;
|
||||||
fs::write(&dns_path, yaml_str).map_err(|e| e.to_string())?;
|
fs::write(&dns_path, yaml_str).map_err(|e| e.to_string())?;
|
||||||
log::info!(target: "app", "DNS config saved to {dns_path:?}");
|
logging!(info, Type::Config, "DNS config saved to {dns_path:?}");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -162,20 +166,20 @@ pub fn apply_dns_config(apply: bool) -> CmdResult {
|
|||||||
let dns_path = match dirs::app_home_dir() {
|
let dns_path = match dirs::app_home_dir() {
|
||||||
Ok(path) => path.join("dns_config.yaml"),
|
Ok(path) => path.join("dns_config.yaml"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!(target: "app", "Failed to get home dir: {e}");
|
logging!(error, Type::Config, "Failed to get home dir: {e}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if !dns_path.exists() {
|
if !dns_path.exists() {
|
||||||
log::warn!(target: "app", "DNS config file not found");
|
logging!(warn, Type::Config, "DNS config file not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dns_yaml = match std::fs::read_to_string(&dns_path) {
|
let dns_yaml = match std::fs::read_to_string(&dns_path) {
|
||||||
Ok(content) => content,
|
Ok(content) => content,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!(target: "app", "Failed to read DNS config: {e}");
|
logging!(error, Type::Config, "Failed to read DNS config: {e}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -188,12 +192,12 @@ pub fn apply_dns_config(apply: bool) -> CmdResult {
|
|||||||
patch
|
patch
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!(target: "app", "Failed to parse DNS config: {e}");
|
logging!(error, Type::Config, "Failed to parse DNS config: {e}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
log::info!(target: "app", "Applying DNS config from file");
|
logging!(info, Type::Config, "Applying DNS config from file");
|
||||||
|
|
||||||
// 重新生成配置,确保DNS配置被正确应用
|
// 重新生成配置,确保DNS配置被正确应用
|
||||||
// 这里不调用patch_clash以避免将DNS配置写入config.yaml
|
// 这里不调用patch_clash以避免将DNS配置写入config.yaml
|
||||||
@@ -202,37 +206,53 @@ pub fn apply_dns_config(apply: bool) -> CmdResult {
|
|||||||
.patch_config(patch_config.clone());
|
.patch_config(patch_config.clone());
|
||||||
|
|
||||||
// 首先重新生成配置
|
// 首先重新生成配置
|
||||||
if let Err(err) = Config::generate().await {
|
if let Err(err) = Config::generate() {
|
||||||
log::error!(target: "app", "Failed to regenerate config with DNS: {err}");
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Config,
|
||||||
|
"Failed to regenerate config with DNS: {err}"
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 然后应用新配置
|
// 然后应用新配置
|
||||||
if let Err(err) = CoreManager::global().update_config().await {
|
if let Err(err) = CoreManager::global().update_config().await {
|
||||||
log::error!(target: "app", "Failed to apply config with DNS: {err}");
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Config,
|
||||||
|
"Failed to apply config with DNS: {err}"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
log::info!(target: "app", "DNS config successfully applied");
|
logging!(info, Type::Config, "DNS config successfully applied");
|
||||||
handle::Handle::refresh_clash();
|
handle::Handle::refresh_clash();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 当关闭DNS设置时,不需要对配置进行任何修改
|
// 当关闭DNS设置时,不需要对配置进行任何修改
|
||||||
// 直接重新生成配置,让enhance函数自动跳过DNS配置的加载
|
// 直接重新生成配置,让enhance函数自动跳过DNS配置的加载
|
||||||
log::info!(target: "app", "DNS settings disabled, regenerating config");
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Config,
|
||||||
|
"DNS settings disabled, regenerating config"
|
||||||
|
);
|
||||||
|
|
||||||
// 重新生成配置
|
// 重新生成配置
|
||||||
if let Err(err) = Config::generate().await {
|
if let Err(err) = Config::generate() {
|
||||||
log::error!(target: "app", "Failed to regenerate config: {err}");
|
logging!(error, Type::Config, "Failed to regenerate config: {err}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用新配置
|
// 应用新配置
|
||||||
match CoreManager::global().update_config().await {
|
match CoreManager::global().update_config().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
log::info!(target: "app", "Config regenerated successfully");
|
logging!(info, Type::Config, "Config regenerated successfully");
|
||||||
handle::Handle::refresh_clash();
|
handle::Handle::refresh_clash();
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!(target: "app", "Failed to apply regenerated config: {err}");
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Config,
|
||||||
|
"Failed to apply regenerated config: {err}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,7 +327,10 @@ pub async fn get_clash_config() -> CmdResult<serde_json::Value> {
|
|||||||
let key = ProxyRequestCache::make_key("clash_config", "default");
|
let key = ProxyRequestCache::make_key("clash_config", "default");
|
||||||
let value = cache
|
let value = cache
|
||||||
.get_or_fetch(key, CONFIG_REFRESH_INTERVAL, || async {
|
.get_or_fetch(key, CONFIG_REFRESH_INTERVAL, || async {
|
||||||
manager.get_config().await.expect("fetch failed")
|
manager.get_config().await.unwrap_or_else(|e| {
|
||||||
|
logging!(error, Type::Cmd, "Failed to fetch clash config: {e}");
|
||||||
|
serde_json::Value::Object(serde_json::Map::new())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
Ok((*value).clone())
|
Ok((*value).clone())
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::{logging, utils::logging::Type};
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
@@ -250,7 +251,23 @@ async fn check_gemini(client: &Client) -> UnlockItem {
|
|||||||
let status = if is_ok { "Yes" } else { "No" };
|
let status = if is_ok { "Yes" } else { "No" };
|
||||||
|
|
||||||
// 尝试提取国家代码
|
// 尝试提取国家代码
|
||||||
let re = Regex::new(r#",2,1,200,"([A-Z]{3})""#).unwrap();
|
let re = match Regex::new(r#",2,1,200,"([A-Z]{3})""#) {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to compile Gemini regex: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Gemini".to_string(),
|
||||||
|
status: "Failed".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
let region = re.captures(&body).and_then(|caps| {
|
let region = re.captures(&body).and_then(|caps| {
|
||||||
caps.get(1).map(|m| {
|
caps.get(1).map(|m| {
|
||||||
let country_code = m.as_str();
|
let country_code = m.as_str();
|
||||||
@@ -303,7 +320,23 @@ async fn check_youtube_premium(client: &Client) -> UnlockItem {
|
|||||||
}
|
}
|
||||||
} else if body_lower.contains("ad-free") {
|
} else if body_lower.contains("ad-free") {
|
||||||
// 尝试解析国家代码
|
// 尝试解析国家代码
|
||||||
let re = Regex::new(r#"id="country-code"[^>]*>([^<]+)<"#).unwrap();
|
let re = match Regex::new(r#"id="country-code"[^>]*>([^<]+)<"#) {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to compile YouTube Premium regex: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Youtube Premium".to_string(),
|
||||||
|
status: "Failed".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
let region = re.captures(&body).and_then(|caps| {
|
let region = re.captures(&body).and_then(|caps| {
|
||||||
caps.get(1).map(|m| {
|
caps.get(1).map(|m| {
|
||||||
let country_code = m.as_str().trim();
|
let country_code = m.as_str().trim();
|
||||||
@@ -350,11 +383,16 @@ async fn check_bahamut_anime(client: &Client) -> UnlockItem {
|
|||||||
let cookie_store = Arc::new(reqwest::cookie::Jar::default());
|
let cookie_store = Arc::new(reqwest::cookie::Jar::default());
|
||||||
|
|
||||||
// 使用带Cookie的客户端
|
// 使用带Cookie的客户端
|
||||||
let client_with_cookies = reqwest::Client::builder()
|
let client_with_cookies = match reqwest::Client::builder()
|
||||||
.user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36")
|
.user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36")
|
||||||
.cookie_provider(Arc::clone(&cookie_store))
|
.cookie_provider(Arc::clone(&cookie_store))
|
||||||
.build()
|
.build() {
|
||||||
.unwrap_or_else(|_| client.clone());
|
Ok(client) => client,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(error, Type::Network, "Failed to create client with cookies for Bahamut Anime: {}", e);
|
||||||
|
client.clone()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 第一步:获取设备ID (会自动保存Cookie)
|
// 第一步:获取设备ID (会自动保存Cookie)
|
||||||
let device_url = "https://ani.gamer.com.tw/ajax/getdeviceid.php";
|
let device_url = "https://ani.gamer.com.tw/ajax/getdeviceid.php";
|
||||||
@@ -363,10 +401,21 @@ async fn check_bahamut_anime(client: &Client) -> UnlockItem {
|
|||||||
match response.text().await {
|
match response.text().await {
|
||||||
Ok(text) => {
|
Ok(text) => {
|
||||||
// 使用正则提取deviceid
|
// 使用正则提取deviceid
|
||||||
let re = Regex::new(r#""deviceid"\s*:\s*"([^"]+)"#).unwrap();
|
match Regex::new(r#""deviceid"\s*:\s*"([^"]+)"#) {
|
||||||
re.captures(&text)
|
Ok(re) => re
|
||||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
|
.captures(&text)
|
||||||
.unwrap_or_default()
|
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to compile deviceid regex for Bahamut Anime: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => String::new(),
|
Err(_) => String::new(),
|
||||||
}
|
}
|
||||||
@@ -421,17 +470,25 @@ async fn check_bahamut_anime(client: &Client) -> UnlockItem {
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(response) => match response.text().await {
|
Ok(response) => match response.text().await {
|
||||||
Ok(body) => {
|
Ok(body) => match Regex::new(r#"data-geo="([^"]+)"#) {
|
||||||
let region_re = Regex::new(r#"data-geo="([^"]+)"#).unwrap();
|
Ok(region_re) => region_re
|
||||||
region_re
|
|
||||||
.captures(&body)
|
.captures(&body)
|
||||||
.and_then(|caps| caps.get(1))
|
.and_then(|caps| caps.get(1))
|
||||||
.map(|m| {
|
.map(|m| {
|
||||||
let country_code = m.as_str();
|
let country_code = m.as_str();
|
||||||
let emoji = country_code_to_emoji(country_code);
|
let emoji = country_code_to_emoji(country_code);
|
||||||
format!("{emoji}{country_code}")
|
format!("{emoji}{country_code}")
|
||||||
})
|
}),
|
||||||
}
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to compile region regex for Bahamut Anime: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
},
|
},
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
@@ -495,8 +552,40 @@ async fn check_netflix(client: &Client) -> UnlockItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取状态码
|
// 获取状态码
|
||||||
let status1 = result1.unwrap().status().as_u16();
|
let status1 = match result1 {
|
||||||
let status2 = result2.unwrap().status().as_u16();
|
Ok(response) => response.status().as_u16(),
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to get Netflix response 1: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Netflix".to_string(),
|
||||||
|
status: "Failed".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let status2 = match result2 {
|
||||||
|
Ok(response) => response.status().as_u16(),
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to get Netflix response 2: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Netflix".to_string(),
|
||||||
|
status: "Failed".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 根据状态码判断解锁状况
|
// 根据状态码判断解锁状况
|
||||||
if status1 == 404 && status2 == 404 {
|
if status1 == 404 && status2 == 404 {
|
||||||
@@ -685,7 +774,23 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let device_response = device_result.unwrap();
|
let device_response = match device_result {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to get Disney+ device response: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Disney+".to_string(),
|
||||||
|
status: "Failed (Network Connection)".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 检查是否 403 错误
|
// 检查是否 403 错误
|
||||||
if device_response.status().as_u16() == 403 {
|
if device_response.status().as_u16() == 403 {
|
||||||
@@ -710,7 +815,23 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 提取 assertion
|
// 提取 assertion
|
||||||
let re = Regex::new(r#""assertion"\s*:\s*"([^"]+)"#).unwrap();
|
let re = match Regex::new(r#""assertion"\s*:\s*"([^"]+)"#) {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to compile assertion regex for Disney+: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Disney+".to_string(),
|
||||||
|
status: "Failed (Regex Error)".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
let assertion = match re.captures(&device_body) {
|
let assertion = match re.captures(&device_body) {
|
||||||
Some(caps) => caps.get(1).map(|m| m.as_str().to_string()),
|
Some(caps) => caps.get(1).map(|m| m.as_str().to_string()),
|
||||||
None => None,
|
None => None,
|
||||||
@@ -729,7 +850,18 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
|||||||
let token_url = "https://disney.api.edge.bamgrid.com/token";
|
let token_url = "https://disney.api.edge.bamgrid.com/token";
|
||||||
|
|
||||||
// 构建请求体 - 使用表单数据格式而非 JSON
|
// 构建请求体 - 使用表单数据格式而非 JSON
|
||||||
let assertion_str = assertion.unwrap();
|
let assertion_str = match assertion {
|
||||||
|
Some(assertion) => assertion,
|
||||||
|
None => {
|
||||||
|
logging!(error, Type::Network, "No assertion found for Disney+");
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Disney+".to_string(),
|
||||||
|
status: "Failed (No Assertion)".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
let token_body = [
|
let token_body = [
|
||||||
(
|
(
|
||||||
"grant_type",
|
"grant_type",
|
||||||
@@ -762,7 +894,23 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let token_response = token_result.unwrap();
|
let token_response = match token_result {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to get Disney+ token response: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Disney+".to_string(),
|
||||||
|
status: "Failed (Network Connection)".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
let token_status = token_response.status();
|
let token_status = token_response.status();
|
||||||
|
|
||||||
// 保存原始响应用于调试
|
// 保存原始响应用于调试
|
||||||
@@ -798,10 +946,20 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
|||||||
.map(|s| s.to_string()),
|
.map(|s| s.to_string()),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// 如果 JSON 解析失败,尝试使用正则表达式
|
// 如果 JSON 解析失败,尝试使用正则表达式
|
||||||
let refresh_token_re = Regex::new(r#""refresh_token"\s*:\s*"([^"]+)"#).unwrap();
|
match Regex::new(r#""refresh_token"\s*:\s*"([^"]+)"#) {
|
||||||
refresh_token_re
|
Ok(refresh_token_re) => refresh_token_re
|
||||||
.captures(&token_body_text)
|
.captures(&token_body_text)
|
||||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
|
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())),
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to compile refresh_token regex for Disney+: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -825,7 +983,7 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
|||||||
// GraphQL API 通常接受 JSON 格式
|
// GraphQL API 通常接受 JSON 格式
|
||||||
let graphql_payload = format!(
|
let graphql_payload = format!(
|
||||||
r#"{{"query":"mutation refreshToken($input: RefreshTokenInput!) {{ refreshToken(refreshToken: $input) {{ activeSession {{ sessionId }} }} }}","variables":{{"input":{{"refreshToken":"{}"}}}}}}"#,
|
r#"{{"query":"mutation refreshToken($input: RefreshTokenInput!) {{ refreshToken(refreshToken: $input) {{ activeSession {{ sessionId }} }} }}","variables":{{"input":{{"refreshToken":"{}"}}}}}}"#,
|
||||||
refresh_token.unwrap()
|
refresh_token.unwrap_or_default()
|
||||||
);
|
);
|
||||||
|
|
||||||
let graphql_result = client
|
let graphql_result = client
|
||||||
@@ -857,21 +1015,56 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 解析 GraphQL 响应获取区域信息
|
// 解析 GraphQL 响应获取区域信息
|
||||||
let graphql_response = graphql_result.unwrap();
|
let graphql_response = match graphql_result {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to get Disney+ GraphQL response: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Disney+".to_string(),
|
||||||
|
status: "Failed (Network Connection)".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
let graphql_status = graphql_response.status();
|
let graphql_status = graphql_response.status();
|
||||||
let graphql_body_text = (graphql_response.text().await).unwrap_or_default();
|
let graphql_body_text = match graphql_response.text().await {
|
||||||
|
Ok(text) => text,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to read Disney+ GraphQL response text: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 如果 GraphQL 响应为空或明显错误,尝试直接获取区域信息
|
// 如果 GraphQL 响应为空或明显错误,尝试直接获取区域信息
|
||||||
if graphql_body_text.is_empty() || graphql_status.as_u16() >= 400 {
|
if graphql_body_text.is_empty() || graphql_status.as_u16() >= 400 {
|
||||||
// 尝试直接从主页获取区域信息
|
// 尝试直接从主页获取区域信息
|
||||||
let region_from_main = match client.get("https://www.disneyplus.com/").send().await {
|
let region_from_main = match client.get("https://www.disneyplus.com/").send().await {
|
||||||
Ok(response) => match response.text().await {
|
Ok(response) => match response.text().await {
|
||||||
Ok(body) => {
|
Ok(body) => match Regex::new(r#"region"\s*:\s*"([^"]+)"#) {
|
||||||
let region_re = Regex::new(r#"region"\s*:\s*"([^"]+)"#).unwrap();
|
Ok(region_re) => region_re
|
||||||
region_re
|
|
||||||
.captures(&body)
|
.captures(&body)
|
||||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
|
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())),
|
||||||
}
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to compile Disney+ main page region regex: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
},
|
},
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
@@ -898,28 +1091,59 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
|||||||
region: None,
|
region: None,
|
||||||
check_time: Some(get_local_date_string()),
|
check_time: Some(get_local_date_string()),
|
||||||
};
|
};
|
||||||
} else {
|
}
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Disney+".to_string(),
|
||||||
|
status: format!(
|
||||||
|
"Failed (GraphQL error: {}, status: {})",
|
||||||
|
graphql_body_text.chars().take(50).collect::<String>() + "...",
|
||||||
|
graphql_status.as_u16()
|
||||||
|
),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取国家代码
|
||||||
|
let region_re = match Regex::new(r#""countryCode"\s*:\s*"([^"]+)"#) {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to compile Disney+ countryCode regex: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
return UnlockItem {
|
return UnlockItem {
|
||||||
name: "Disney+".to_string(),
|
name: "Disney+".to_string(),
|
||||||
status: format!(
|
status: "Failed (Regex Error)".to_string(),
|
||||||
"Failed (GraphQL error: {}, status: {})",
|
|
||||||
graphql_body_text.chars().take(50).collect::<String>() + "...",
|
|
||||||
graphql_status.as_u16()
|
|
||||||
),
|
|
||||||
region: None,
|
region: None,
|
||||||
check_time: Some(get_local_date_string()),
|
check_time: Some(get_local_date_string()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// 提取国家代码
|
|
||||||
let region_re = Regex::new(r#""countryCode"\s*:\s*"([^"]+)"#).unwrap();
|
|
||||||
let region_code = region_re
|
let region_code = region_re
|
||||||
.captures(&graphql_body_text)
|
.captures(&graphql_body_text)
|
||||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()));
|
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()));
|
||||||
|
|
||||||
// 提取支持状态
|
// 提取支持状态
|
||||||
let supported_re = Regex::new(r#""inSupportedLocation"\s*:\s*(false|true)"#).unwrap();
|
let supported_re = match Regex::new(r#""inSupportedLocation"\s*:\s*(false|true)"#) {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to compile Disney+ supported location regex: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Disney+".to_string(),
|
||||||
|
status: "Failed (Regex Error)".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
let in_supported_location = supported_re
|
let in_supported_location = supported_re
|
||||||
.captures(&graphql_body_text)
|
.captures(&graphql_body_text)
|
||||||
.and_then(|caps| caps.get(1).map(|m| m.as_str() == "true"));
|
.and_then(|caps| caps.get(1).map(|m| m.as_str() == "true"));
|
||||||
@@ -929,12 +1153,20 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
|||||||
// 尝试直接从主页获取区域信息
|
// 尝试直接从主页获取区域信息
|
||||||
let region_from_main = match client.get("https://www.disneyplus.com/").send().await {
|
let region_from_main = match client.get("https://www.disneyplus.com/").send().await {
|
||||||
Ok(response) => match response.text().await {
|
Ok(response) => match response.text().await {
|
||||||
Ok(body) => {
|
Ok(body) => match Regex::new(r#"region"\s*:\s*"([^"]+)"#) {
|
||||||
let region_re = Regex::new(r#"region"\s*:\s*"([^"]+)"#).unwrap();
|
Ok(region_re) => region_re
|
||||||
region_re
|
|
||||||
.captures(&body)
|
.captures(&body)
|
||||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
|
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())),
|
||||||
}
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to compile Disney+ main page region regex: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
},
|
},
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
@@ -958,7 +1190,18 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let region = region_code.unwrap();
|
let region = match region_code {
|
||||||
|
Some(code) => code,
|
||||||
|
None => {
|
||||||
|
logging!(error, Type::Network, "No region code found for Disney+");
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Disney+".to_string(),
|
||||||
|
status: "No".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 判断日本地区
|
// 判断日本地区
|
||||||
if region == "JP" {
|
if region == "JP" {
|
||||||
@@ -1028,13 +1271,47 @@ async fn check_prime_video(client: &Client) -> UnlockItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 解析响应内容
|
// 解析响应内容
|
||||||
match result.unwrap().text().await {
|
let response = match result {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to get Prime Video response: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Prime Video".to_string(),
|
||||||
|
status: "Failed (Network Connection)".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match response.text().await {
|
||||||
Ok(body) => {
|
Ok(body) => {
|
||||||
// 检查是否被地区限制
|
// 检查是否被地区限制
|
||||||
let is_blocked = body.contains("isServiceRestricted");
|
let is_blocked = body.contains("isServiceRestricted");
|
||||||
|
|
||||||
// 提取地区信息
|
// 提取地区信息
|
||||||
let region_re = Regex::new(r#""currentTerritory":"([^"]+)"#).unwrap();
|
let region_re = match Regex::new(r#""currentTerritory":"([^"]+)"#) {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to compile Prime Video region regex: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return UnlockItem {
|
||||||
|
name: "Prime Video".to_string(),
|
||||||
|
status: "Failed (Regex Error)".to_string(),
|
||||||
|
region: None,
|
||||||
|
check_time: Some(get_local_date_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
let region_code = region_re
|
let region_code = region_re
|
||||||
.captures(&body)
|
.captures(&body)
|
||||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()));
|
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()));
|
||||||
@@ -1182,8 +1459,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
|||||||
|
|
||||||
// 添加哔哩哔哩大陆检测任务
|
// 添加哔哩哔哩大陆检测任务
|
||||||
{
|
{
|
||||||
let client = client_arc.clone();
|
let client = Arc::clone(&client_arc);
|
||||||
let results = results.clone();
|
let results = Arc::clone(&results);
|
||||||
tasks.spawn(async move {
|
tasks.spawn(async move {
|
||||||
let result = check_bilibili_china_mainland(&client).await;
|
let result = check_bilibili_china_mainland(&client).await;
|
||||||
let mut results = results.lock().await;
|
let mut results = results.lock().await;
|
||||||
@@ -1193,8 +1470,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
|||||||
|
|
||||||
// 添加哔哩哔哩港澳台检测任务
|
// 添加哔哩哔哩港澳台检测任务
|
||||||
{
|
{
|
||||||
let client = client_arc.clone();
|
let client = Arc::clone(&client_arc);
|
||||||
let results = results.clone();
|
let results = Arc::clone(&results);
|
||||||
tasks.spawn(async move {
|
tasks.spawn(async move {
|
||||||
let result = check_bilibili_hk_mc_tw(&client).await;
|
let result = check_bilibili_hk_mc_tw(&client).await;
|
||||||
let mut results = results.lock().await;
|
let mut results = results.lock().await;
|
||||||
@@ -1204,8 +1481,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
|||||||
|
|
||||||
// 添加合并的ChatGPT检测任务
|
// 添加合并的ChatGPT检测任务
|
||||||
{
|
{
|
||||||
let client = client_arc.clone();
|
let client = Arc::clone(&client_arc);
|
||||||
let results = results.clone();
|
let results = Arc::clone(&results);
|
||||||
tasks.spawn(async move {
|
tasks.spawn(async move {
|
||||||
let chatgpt_results = check_chatgpt_combined(&client).await;
|
let chatgpt_results = check_chatgpt_combined(&client).await;
|
||||||
let mut results = results.lock().await;
|
let mut results = results.lock().await;
|
||||||
@@ -1215,8 +1492,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
|||||||
|
|
||||||
// 添加Gemini检测任务
|
// 添加Gemini检测任务
|
||||||
{
|
{
|
||||||
let client = client_arc.clone();
|
let client = Arc::clone(&client_arc);
|
||||||
let results = results.clone();
|
let results = Arc::clone(&results);
|
||||||
tasks.spawn(async move {
|
tasks.spawn(async move {
|
||||||
let result = check_gemini(&client).await;
|
let result = check_gemini(&client).await;
|
||||||
let mut results = results.lock().await;
|
let mut results = results.lock().await;
|
||||||
@@ -1226,8 +1503,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
|||||||
|
|
||||||
// 添加YouTube Premium检测任务
|
// 添加YouTube Premium检测任务
|
||||||
{
|
{
|
||||||
let client = client_arc.clone();
|
let client = Arc::clone(&client_arc);
|
||||||
let results = results.clone();
|
let results = Arc::clone(&results);
|
||||||
tasks.spawn(async move {
|
tasks.spawn(async move {
|
||||||
let result = check_youtube_premium(&client).await;
|
let result = check_youtube_premium(&client).await;
|
||||||
let mut results = results.lock().await;
|
let mut results = results.lock().await;
|
||||||
@@ -1237,8 +1514,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
|||||||
|
|
||||||
// 添加动画疯检测任务
|
// 添加动画疯检测任务
|
||||||
{
|
{
|
||||||
let client = client_arc.clone();
|
let client = Arc::clone(&client_arc);
|
||||||
let results = results.clone();
|
let results = Arc::clone(&results);
|
||||||
tasks.spawn(async move {
|
tasks.spawn(async move {
|
||||||
let result = check_bahamut_anime(&client).await;
|
let result = check_bahamut_anime(&client).await;
|
||||||
let mut results = results.lock().await;
|
let mut results = results.lock().await;
|
||||||
@@ -1248,8 +1525,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
|||||||
|
|
||||||
// 添加 Netflix 检测任务
|
// 添加 Netflix 检测任务
|
||||||
{
|
{
|
||||||
let client = client_arc.clone();
|
let client = Arc::clone(&client_arc);
|
||||||
let results = results.clone();
|
let results = Arc::clone(&results);
|
||||||
tasks.spawn(async move {
|
tasks.spawn(async move {
|
||||||
let result = check_netflix(&client).await;
|
let result = check_netflix(&client).await;
|
||||||
let mut results = results.lock().await;
|
let mut results = results.lock().await;
|
||||||
@@ -1259,8 +1536,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
|||||||
|
|
||||||
// 添加 Disney+ 检测任务
|
// 添加 Disney+ 检测任务
|
||||||
{
|
{
|
||||||
let client = client_arc.clone();
|
let client = Arc::clone(&client_arc);
|
||||||
let results = results.clone();
|
let results = Arc::clone(&results);
|
||||||
tasks.spawn(async move {
|
tasks.spawn(async move {
|
||||||
let result = check_disney_plus(&client).await;
|
let result = check_disney_plus(&client).await;
|
||||||
let mut results = results.lock().await;
|
let mut results = results.lock().await;
|
||||||
@@ -1270,8 +1547,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
|||||||
|
|
||||||
// 添加 Prime Video 检测任务
|
// 添加 Prime Video 检测任务
|
||||||
{
|
{
|
||||||
let client = client_arc.clone();
|
let client = Arc::clone(&client_arc);
|
||||||
let results = results.clone();
|
let results = Arc::clone(&results);
|
||||||
tasks.spawn(async move {
|
tasks.spawn(async move {
|
||||||
let result = check_prime_video(&client).await;
|
let result = check_prime_video(&client).await;
|
||||||
let mut results = results.lock().await;
|
let mut results = results.lock().await;
|
||||||
@@ -1287,9 +1564,17 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取所有结果
|
// 获取所有结果
|
||||||
let results = Arc::try_unwrap(results)
|
let results = match Arc::try_unwrap(results) {
|
||||||
.expect("无法获取结果,可能仍有引用存在")
|
Ok(mutex) => mutex.into_inner(),
|
||||||
.into_inner();
|
Err(_) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
"Failed to unwrap results Arc, references still exist"
|
||||||
|
);
|
||||||
|
return Err("Failed to collect results".to_string());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use super::CmdResult;
|
use super::CmdResult;
|
||||||
use crate::{ipc::IpcManager, state::proxy::ProxyRequestCache};
|
use crate::{ipc::IpcManager, logging, state::proxy::ProxyRequestCache, utils::logging::Type};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
const PROXIES_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
|
const PROXIES_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
|
||||||
@@ -12,7 +12,10 @@ pub async fn get_proxies() -> CmdResult<serde_json::Value> {
|
|||||||
let key = ProxyRequestCache::make_key("proxies", "default");
|
let key = ProxyRequestCache::make_key("proxies", "default");
|
||||||
let value = cache
|
let value = cache
|
||||||
.get_or_fetch(key, PROXIES_REFRESH_INTERVAL, || async {
|
.get_or_fetch(key, PROXIES_REFRESH_INTERVAL, || async {
|
||||||
manager.get_proxies().await.expect("fetch failed")
|
manager.get_proxies().await.unwrap_or_else(|e| {
|
||||||
|
logging!(error, Type::Cmd, "Failed to fetch proxies: {e}");
|
||||||
|
serde_json::Value::Object(serde_json::Map::new())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
Ok((*value).clone())
|
Ok((*value).clone())
|
||||||
@@ -34,7 +37,10 @@ pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
|
|||||||
let key = ProxyRequestCache::make_key("providers", "default");
|
let key = ProxyRequestCache::make_key("providers", "default");
|
||||||
let value = cache
|
let value = cache
|
||||||
.get_or_fetch(key, PROVIDERS_REFRESH_INTERVAL, || async {
|
.get_or_fetch(key, PROVIDERS_REFRESH_INTERVAL, || async {
|
||||||
manager.get_providers_proxies().await.expect("fetch failed")
|
manager.get_providers_proxies().await.unwrap_or_else(|e| {
|
||||||
|
logging!(error, Type::Cmd, "Failed to fetch provider proxies: {e}");
|
||||||
|
serde_json::Value::Object(serde_json::Map::new())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
Ok((*value).clone())
|
Ok((*value).clone())
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 保存新的配置文件
|
// 保存新的配置文件
|
||||||
wrap_err!(fs::write(&file_path, file_data.clone().unwrap()))?;
|
let file_data = file_data.ok_or("file_data is None")?;
|
||||||
|
wrap_err!(fs::write(&file_path, &file_data))?;
|
||||||
|
|
||||||
let file_path_str = file_path.to_string_lossy().to_string();
|
let file_path_str = file_path.to_string_lossy().to_string();
|
||||||
logging!(
|
logging!(
|
||||||
@@ -139,17 +140,29 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
|||||||
|| (!file_path_str.ends_with(".js") && !is_script_error)
|
|| (!file_path_str.ends_with(".js") && !is_script_error)
|
||||||
{
|
{
|
||||||
// 普通YAML错误使用YAML通知处理
|
// 普通YAML错误使用YAML通知处理
|
||||||
log::info!(target: "app", "[cmd配置save] YAML配置文件验证失败,发送通知");
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Config,
|
||||||
|
"[cmd配置save] YAML配置文件验证失败,发送通知"
|
||||||
|
);
|
||||||
let result = (false, error_msg.clone());
|
let result = (false, error_msg.clone());
|
||||||
crate::cmd::validate::handle_yaml_validation_notice(&result, "YAML配置文件");
|
crate::cmd::validate::handle_yaml_validation_notice(&result, "YAML配置文件");
|
||||||
} else if is_script_error {
|
} else if is_script_error {
|
||||||
// 脚本错误使用专门的通知处理
|
// 脚本错误使用专门的通知处理
|
||||||
log::info!(target: "app", "[cmd配置save] 脚本文件验证失败,发送通知");
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Config,
|
||||||
|
"[cmd配置save] 脚本文件验证失败,发送通知"
|
||||||
|
);
|
||||||
let result = (false, error_msg.clone());
|
let result = (false, error_msg.clone());
|
||||||
crate::cmd::validate::handle_script_validation_notice(&result, "脚本文件");
|
crate::cmd::validate::handle_script_validation_notice(&result, "脚本文件");
|
||||||
} else {
|
} else {
|
||||||
// 普通配置错误使用一般通知
|
// 普通配置错误使用一般通知
|
||||||
log::info!(target: "app", "[cmd配置save] 其他类型验证失败,发送一般通知");
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Config,
|
||||||
|
"[cmd配置save] 其他类型验证失败,发送一般通知"
|
||||||
|
);
|
||||||
handle::Handle::notice_message("config_validate::error", &error_msg);
|
handle::Handle::notice_message("config_validate::error", &error_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ use crate::{
|
|||||||
core::{service, CoreManager},
|
core::{service, CoreManager},
|
||||||
utils::i18n::t,
|
utils::i18n::t,
|
||||||
};
|
};
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
async fn execute_service_operation(
|
async fn execute_service_operation_sync<F, E>(service_op: F, op_type: &str) -> CmdResult
|
||||||
service_op: impl std::future::Future<Output = Result<(), impl ToString + std::fmt::Debug>>,
|
where
|
||||||
op_type: &str,
|
F: FnOnce() -> Result<(), E>,
|
||||||
) -> CmdResult {
|
E: ToString + std::fmt::Debug,
|
||||||
if service_op.await.is_err() {
|
{
|
||||||
let emsg = format!("{} {} failed", op_type, "Service");
|
if let Err(e) = service_op() {
|
||||||
|
let emsg = format!("{} {} failed: {}", op_type, "Service", e.to_string());
|
||||||
return Err(t(emsg.as_str()));
|
return Err(t(emsg.as_str()));
|
||||||
}
|
}
|
||||||
if CoreManager::global().restart_core().await.is_err() {
|
if CoreManager::global().restart_core().await.is_err() {
|
||||||
@@ -21,22 +23,22 @@ async fn execute_service_operation(
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn install_service() -> CmdResult {
|
pub async fn install_service() -> CmdResult {
|
||||||
execute_service_operation(service::install_service(), "Install").await
|
execute_service_operation_sync(service::install_service, "Install").await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn uninstall_service() -> CmdResult {
|
pub async fn uninstall_service() -> CmdResult {
|
||||||
execute_service_operation(service::uninstall_service(), "Uninstall").await
|
execute_service_operation_sync(service::uninstall_service, "Uninstall").await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn reinstall_service() -> CmdResult {
|
pub async fn reinstall_service() -> CmdResult {
|
||||||
execute_service_operation(service::reinstall_service(), "Reinstall").await
|
execute_service_operation_sync(service::reinstall_service, "Reinstall").await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn repair_service() -> CmdResult {
|
pub async fn repair_service() -> CmdResult {
|
||||||
execute_service_operation(service::force_reinstall_service(), "Repair").await
|
execute_service_operation_sync(service::force_reinstall_service, "Repair").await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use super::CmdResult;
|
use super::CmdResult;
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{handle, CoreManager},
|
core::{handle, CoreManager},
|
||||||
|
logging,
|
||||||
module::sysinfo::PlatformSpecification,
|
module::sysinfo::PlatformSpecification,
|
||||||
|
utils::logging::Type,
|
||||||
};
|
};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -23,20 +25,22 @@ static APP_START_TIME: Lazy<AtomicI64> = Lazy::new(|| {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn export_diagnostic_info() -> CmdResult<()> {
|
pub async fn export_diagnostic_info() -> CmdResult<()> {
|
||||||
let sysinfo = PlatformSpecification::new_async().await;
|
let sysinfo = PlatformSpecification::new_sync();
|
||||||
let info = format!("{sysinfo:?}");
|
let info = format!("{sysinfo:?}");
|
||||||
|
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = handle::Handle::global()
|
||||||
|
.app_handle()
|
||||||
|
.ok_or("Failed to get app handle")?;
|
||||||
let cliboard = app_handle.clipboard();
|
let cliboard = app_handle.clipboard();
|
||||||
if cliboard.write_text(info).is_err() {
|
if cliboard.write_text(info).is_err() {
|
||||||
log::error!(target: "app", "Failed to write to clipboard");
|
logging!(error, Type::System, "Failed to write to clipboard");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_system_info() -> CmdResult<String> {
|
pub async fn get_system_info() -> CmdResult<String> {
|
||||||
let sysinfo = PlatformSpecification::new_async().await;
|
let sysinfo = PlatformSpecification::new_sync();
|
||||||
let info = format!("{sysinfo:?}");
|
let info = format!("{sysinfo:?}");
|
||||||
Ok(info)
|
Ok(info)
|
||||||
}
|
}
|
||||||
@@ -44,7 +48,7 @@ pub async fn get_system_info() -> CmdResult<String> {
|
|||||||
/// 获取当前内核运行模式
|
/// 获取当前内核运行模式
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_running_mode() -> Result<String, String> {
|
pub async fn get_running_mode() -> Result<String, String> {
|
||||||
Ok(CoreManager::global().get_running_mode().await.to_string())
|
Ok(CoreManager::global().get_running_mode().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取应用的运行时间(毫秒)
|
/// 获取应用的运行时间(毫秒)
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ mod platform {
|
|||||||
use super::CmdResult;
|
use super::CmdResult;
|
||||||
use crate::{core::win_uwp, wrap_err};
|
use crate::{core::win_uwp, wrap_err};
|
||||||
|
|
||||||
pub async fn invoke_uwp_tool() -> CmdResult {
|
pub fn invoke_uwp_tool() -> CmdResult {
|
||||||
wrap_err!(win_uwp::invoke_uwptools().await)
|
wrap_err!(win_uwp::invoke_uwptools())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ mod platform {
|
|||||||
mod platform {
|
mod platform {
|
||||||
use super::CmdResult;
|
use super::CmdResult;
|
||||||
|
|
||||||
pub async fn invoke_uwp_tool() -> CmdResult {
|
pub fn invoke_uwp_tool() -> CmdResult {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,5 +24,5 @@ mod platform {
|
|||||||
/// Command exposed to Tauri
|
/// Command exposed to Tauri
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn invoke_uwp_tool() -> CmdResult {
|
pub async fn invoke_uwp_tool() -> CmdResult {
|
||||||
platform::invoke_uwp_tool().await
|
platform::invoke_uwp_tool()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ impl IClashTemp {
|
|||||||
Ok(mut map) => {
|
Ok(mut map) => {
|
||||||
template.0.keys().for_each(|key| {
|
template.0.keys().for_each(|key| {
|
||||||
if !map.contains_key(key) {
|
if !map.contains_key(key) {
|
||||||
map.insert(key.clone(), template.0.get(key).unwrap().clone());
|
if let Some(value) = template.0.get(key) {
|
||||||
|
map.insert(key.clone(), value.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 确保 secret 字段存在且不为空
|
// 确保 secret 字段存在且不为空
|
||||||
@@ -307,7 +309,13 @@ impl IClashTemp {
|
|||||||
|
|
||||||
pub fn guard_external_controller_ipc() -> String {
|
pub fn guard_external_controller_ipc() -> String {
|
||||||
// 总是使用当前的 IPC 路径,确保配置文件与运行时路径一致
|
// 总是使用当前的 IPC 路径,确保配置文件与运行时路径一致
|
||||||
path_to_str(&ipc_path().unwrap()).unwrap().to_string()
|
ipc_path()
|
||||||
|
.ok()
|
||||||
|
.and_then(|path| path_to_str(&path).ok().map(|s| s.to_string()))
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
log::error!(target: "app", "Failed to get IPC path, using default");
|
||||||
|
"127.0.0.1:9090".to_string()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ impl Config {
|
|||||||
.append_item(script_item.clone())?;
|
.append_item(script_item.clone())?;
|
||||||
}
|
}
|
||||||
// 生成运行时配置
|
// 生成运行时配置
|
||||||
if let Err(err) = Self::generate().await {
|
if let Err(err) = Self::generate() {
|
||||||
logging!(error, Type::Config, true, "生成运行时配置失败: {}", err);
|
logging!(error, Type::Config, true, "生成运行时配置失败: {}", err);
|
||||||
} else {
|
} else {
|
||||||
logging!(info, Type::Config, true, "生成运行时配置成功");
|
logging!(info, Type::Config, true, "生成运行时配置成功");
|
||||||
@@ -96,8 +96,7 @@ impl Config {
|
|||||||
error_msg
|
error_msg
|
||||||
);
|
);
|
||||||
CoreManager::global()
|
CoreManager::global()
|
||||||
.use_default_config("config_validate::boot_error", &error_msg)
|
.use_default_config("config_validate::boot_error", &error_msg)?;
|
||||||
.await?;
|
|
||||||
Some(("config_validate::boot_error", error_msg))
|
Some(("config_validate::boot_error", error_msg))
|
||||||
} else {
|
} else {
|
||||||
logging!(info, Type::Config, true, "配置验证成功");
|
logging!(info, Type::Config, true, "配置验证成功");
|
||||||
@@ -107,16 +106,13 @@ impl Config {
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
logging!(warn, Type::Config, true, "验证进程执行失败: {}", err);
|
logging!(warn, Type::Config, true, "验证进程执行失败: {}", err);
|
||||||
CoreManager::global()
|
CoreManager::global()
|
||||||
.use_default_config("config_validate::process_terminated", "")
|
.use_default_config("config_validate::process_terminated", "")?;
|
||||||
.await?;
|
|
||||||
Some(("config_validate::process_terminated", String::new()))
|
Some(("config_validate::process_terminated", String::new()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logging!(warn, Type::Config, true, "生成配置文件失败,使用默认配置");
|
logging!(warn, Type::Config, true, "生成配置文件失败,使用默认配置");
|
||||||
CoreManager::global()
|
CoreManager::global().use_default_config("config_validate::error", "")?;
|
||||||
.use_default_config("config_validate::error", "")
|
|
||||||
.await?;
|
|
||||||
Some(("config_validate::error", String::new()))
|
Some(("config_validate::error", String::new()))
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -150,8 +146,8 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 生成订阅存好
|
/// 生成订阅存好
|
||||||
pub async fn generate() -> Result<()> {
|
pub fn generate() -> Result<()> {
|
||||||
let (config, exists_keys, logs) = enhance::enhance().await;
|
let (config, exists_keys, logs) = enhance::enhance();
|
||||||
|
|
||||||
*Config::runtime().draft_mut() = Box::new(IRuntime {
|
*Config::runtime().draft_mut() = Box::new(IRuntime {
|
||||||
config: Some(config),
|
config: Some(config),
|
||||||
@@ -174,33 +170,33 @@ mod tests {
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
fn test_prfitem_from_merge_size() {
|
fn test_prfitem_from_merge_size() {
|
||||||
let merge_item = PrfItem::from_merge(Some("Merge".to_string())).unwrap();
|
let merge_item = PrfItem::from_merge(Some("Merge".to_string()))
|
||||||
dbg!(&merge_item);
|
.expect("Failed to create merge item in test");
|
||||||
let prfitem_size = mem::size_of_val(&merge_item);
|
let prfitem_size = mem::size_of_val(&merge_item);
|
||||||
dbg!(prfitem_size);
|
|
||||||
// Boxed version
|
// Boxed version
|
||||||
let boxed_merge_item = Box::new(merge_item);
|
let boxed_merge_item = Box::new(merge_item);
|
||||||
let box_prfitem_size = mem::size_of_val(&boxed_merge_item);
|
let box_prfitem_size = mem::size_of_val(&boxed_merge_item);
|
||||||
dbg!(box_prfitem_size);
|
|
||||||
// The size of Box<T> is always pointer-sized (usually 8 bytes on 64-bit)
|
// The size of Box<T> is always pointer-sized (usually 8 bytes on 64-bit)
|
||||||
// assert_eq!(box_prfitem_size, mem::size_of::<Box<PrfItem>>());
|
// assert_eq!(box_prfitem_size, mem::size_of::<Box<PrfItem>>());
|
||||||
assert!(box_prfitem_size < prfitem_size);
|
assert!(box_prfitem_size < prfitem_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(unused_variables)]
|
||||||
fn test_draft_size_non_boxed() {
|
fn test_draft_size_non_boxed() {
|
||||||
let draft = Draft::from(IRuntime::new());
|
let draft = Draft::from(IRuntime::new());
|
||||||
let iruntime_size = std::mem::size_of_val(&draft);
|
let iruntime_size = std::mem::size_of_val(&draft);
|
||||||
dbg!(iruntime_size);
|
|
||||||
assert_eq!(iruntime_size, std::mem::size_of::<Draft<IRuntime>>());
|
assert_eq!(iruntime_size, std::mem::size_of::<Draft<IRuntime>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(unused_variables)]
|
||||||
fn test_draft_size_boxed() {
|
fn test_draft_size_boxed() {
|
||||||
let draft = Draft::from(Box::new(IRuntime::new()));
|
let draft = Draft::from(Box::new(IRuntime::new()));
|
||||||
let box_iruntime_size = std::mem::size_of_val(&draft);
|
let box_iruntime_size = std::mem::size_of_val(&draft);
|
||||||
dbg!(box_iruntime_size);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
box_iruntime_size,
|
box_iruntime_size,
|
||||||
std::mem::size_of::<Draft<Box<IRuntime>>>()
|
std::mem::size_of::<Draft<Box<IRuntime>>>()
|
||||||
|
|||||||
@@ -46,11 +46,18 @@ impl<T: Clone + ToOwned> Draft<Box<T>> {
|
|||||||
if guard.1.is_none() {
|
if guard.1.is_none() {
|
||||||
let mut guard = RwLockUpgradableReadGuard::upgrade(guard);
|
let mut guard = RwLockUpgradableReadGuard::upgrade(guard);
|
||||||
guard.1 = Some(guard.0.clone());
|
guard.1 = Some(guard.0.clone());
|
||||||
return RwLockWriteGuard::map(guard, |inner| inner.1.as_mut().unwrap());
|
return RwLockWriteGuard::map(guard, |inner| {
|
||||||
|
inner.1.as_mut().unwrap_or_else(|| {
|
||||||
|
unreachable!("Draft was just created above, this should never fail")
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// 已存在草稿,升级为写锁映射
|
// 已存在草稿,升级为写锁映射
|
||||||
RwLockWriteGuard::map(RwLockUpgradableReadGuard::upgrade(guard), |inner| {
|
RwLockWriteGuard::map(RwLockUpgradableReadGuard::upgrade(guard), |inner| {
|
||||||
inner.1.as_mut().unwrap()
|
inner
|
||||||
|
.1
|
||||||
|
.as_mut()
|
||||||
|
.unwrap_or_else(|| unreachable!("Draft should exist when guard.1.is_some()"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -147,12 +147,15 @@ impl PrfItem {
|
|||||||
bail!("type should not be null");
|
bail!("type should not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
match item.itype.unwrap().as_str() {
|
let itype = item
|
||||||
|
.itype
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("type should not be null"))?;
|
||||||
|
match itype.as_str() {
|
||||||
"remote" => {
|
"remote" => {
|
||||||
if item.url.is_none() {
|
let url = item
|
||||||
bail!("url should not be null");
|
.url
|
||||||
}
|
.as_ref()
|
||||||
let url = item.url.as_ref().unwrap().as_str();
|
.ok_or_else(|| anyhow::anyhow!("url should not be null"))?;
|
||||||
let name = item.name;
|
let name = item.name;
|
||||||
let desc = item.desc;
|
let desc = item.desc;
|
||||||
PrfItem::from_url(url, name, desc, item.option).await
|
PrfItem::from_url(url, name, desc, item.option).await
|
||||||
@@ -549,22 +552,20 @@ impl PrfItem {
|
|||||||
|
|
||||||
/// get the file data
|
/// get the file data
|
||||||
pub fn read_file(&self) -> Result<String> {
|
pub fn read_file(&self) -> Result<String> {
|
||||||
if self.file.is_none() {
|
let file = self
|
||||||
bail!("could not find the file");
|
.file
|
||||||
}
|
.clone()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("could not find the file"))?;
|
||||||
let file = self.file.clone().unwrap();
|
|
||||||
let path = dirs::app_profiles_dir()?.join(file);
|
let path = dirs::app_profiles_dir()?.join(file);
|
||||||
fs::read_to_string(path).context("failed to read the file")
|
fs::read_to_string(path).context("failed to read the file")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// save the file data
|
/// save the file data
|
||||||
pub fn save_file(&self, data: String) -> Result<()> {
|
pub fn save_file(&self, data: String) -> Result<()> {
|
||||||
if self.file.is_none() {
|
let file = self
|
||||||
bail!("could not find the file");
|
.file
|
||||||
}
|
.clone()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("could not find the file"))?;
|
||||||
let file = self.file.clone().unwrap();
|
|
||||||
let path = dirs::app_profiles_dir()?.join(file);
|
let path = dirs::app_profiles_dir()?.join(file);
|
||||||
fs::write(path, data.as_bytes()).context("failed to save the file")
|
fs::write(path, data.as_bytes()).context("failed to save the file")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,10 +77,11 @@ impl IProfiles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(current) = patch.current {
|
if let Some(current) = patch.current {
|
||||||
let items = self.items.as_ref().unwrap();
|
if let Some(items) = self.items.as_ref() {
|
||||||
let some_uid = Some(current);
|
let some_uid = Some(current);
|
||||||
if items.iter().any(|e| e.uid == some_uid) {
|
if items.iter().any(|e| e.uid == some_uid) {
|
||||||
self.current = some_uid;
|
self.current = some_uid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +128,9 @@ impl IProfiles {
|
|||||||
bail!("the file should not be null");
|
bail!("the file should not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = item.file.clone().unwrap();
|
let file = item.file.clone().ok_or_else(|| {
|
||||||
|
anyhow::anyhow!("file field is required when file_data is provided")
|
||||||
|
})?;
|
||||||
let path = dirs::app_profiles_dir()?.join(&file);
|
let path = dirs::app_profiles_dir()?.join(&file);
|
||||||
|
|
||||||
fs::File::create(path)
|
fs::File::create(path)
|
||||||
@@ -168,11 +171,12 @@ impl IProfiles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if old_index.is_none() || new_index.is_none() {
|
let (old_idx, new_idx) = match (old_index, new_index) {
|
||||||
return Ok(());
|
(Some(old), Some(new)) => (old, new),
|
||||||
}
|
_ => return Ok(()),
|
||||||
let item = items.remove(old_index.unwrap());
|
};
|
||||||
items.insert(new_index.unwrap(), item);
|
let item = items.remove(old_idx);
|
||||||
|
items.insert(new_idx, item);
|
||||||
self.items = Some(items);
|
self.items = Some(items);
|
||||||
self.save_file()
|
self.save_file()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,8 +117,7 @@ impl WebDavClient {
|
|||||||
attempt.follow()
|
attempt.follow()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.build()
|
.build()?,
|
||||||
.unwrap(),
|
|
||||||
)
|
)
|
||||||
.set_host(config.url)
|
.set_host(config.url)
|
||||||
.set_auth(reqwest_dav::Auth::Basic(config.username, config.password))
|
.set_auth(reqwest_dav::Auth::Basic(config.username, config.password))
|
||||||
@@ -243,12 +242,17 @@ pub fn create_backup() -> Result<(String, PathBuf), Error> {
|
|||||||
let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
|
let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
|
||||||
if let Ok(entries) = fs::read_dir(dirs::app_profiles_dir()?) {
|
if let Ok(entries) = fs::read_dir(dirs::app_profiles_dir()?) {
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
let entry = entry.unwrap();
|
let entry = entry?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if path.is_file() {
|
if path.is_file() {
|
||||||
let backup_path = format!("profiles/{}", entry.file_name().to_str().unwrap());
|
let file_name_os = entry.file_name();
|
||||||
|
let file_name = file_name_os
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| anyhow::Error::msg("Invalid file name encoding"))?;
|
||||||
|
let backup_path = format!("profiles/{}", file_name);
|
||||||
zip.start_file(backup_path, options)?;
|
zip.start_file(backup_path, options)?;
|
||||||
zip.write_all(fs::read(path).unwrap().as_slice())?;
|
let file_content = fs::read(&path)?;
|
||||||
|
zip.write_all(&file_content)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
use parking_lot::Mutex;
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
fs::{create_dir_all, File},
|
fs::{create_dir_all, File},
|
||||||
@@ -22,7 +23,6 @@ use std::{
|
|||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use tauri_plugin_shell::{process::CommandChild, ShellExt};
|
use tauri_plugin_shell::{process::CommandChild, ShellExt};
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CoreManager {
|
pub struct CoreManager {
|
||||||
@@ -135,7 +135,7 @@ impl CoreManager {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
/// 使用默认配置
|
/// 使用默认配置
|
||||||
pub async fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> {
|
pub fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> {
|
||||||
let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG);
|
let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG);
|
||||||
*Config::runtime().draft_mut() = Box::new(IRuntime {
|
*Config::runtime().draft_mut() = Box::new(IRuntime {
|
||||||
config: Some(Config::clash().latest_ref().0.clone()),
|
config: Some(Config::clash().latest_ref().0.clone()),
|
||||||
@@ -185,7 +185,7 @@ impl CoreManager {
|
|||||||
"检测到Merge文件,仅进行语法检查: {}",
|
"检测到Merge文件,仅进行语法检查: {}",
|
||||||
config_path
|
config_path
|
||||||
);
|
);
|
||||||
return self.validate_file_syntax(config_path).await;
|
return self.validate_file_syntax(config_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否为脚本文件
|
// 检查是否为脚本文件
|
||||||
@@ -217,7 +217,7 @@ impl CoreManager {
|
|||||||
"检测到脚本文件,使用JavaScript验证: {}",
|
"检测到脚本文件,使用JavaScript验证: {}",
|
||||||
config_path
|
config_path
|
||||||
);
|
);
|
||||||
return self.validate_script_file(config_path).await;
|
return self.validate_script_file(config_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对YAML配置文件使用Clash内核验证
|
// 对YAML配置文件使用Clash内核验证
|
||||||
@@ -249,7 +249,11 @@ impl CoreManager {
|
|||||||
let clash_core = Config::verge().latest_ref().get_valid_clash_core();
|
let clash_core = Config::verge().latest_ref().get_valid_clash_core();
|
||||||
logging!(info, Type::Config, true, "使用内核: {}", clash_core);
|
logging!(info, Type::Config, true, "使用内核: {}", clash_core);
|
||||||
|
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = handle::Handle::global().app_handle().ok_or_else(|| {
|
||||||
|
let msg = "Failed to get app handle";
|
||||||
|
logging!(error, Type::Core, true, "{}", msg);
|
||||||
|
anyhow::anyhow!(msg)
|
||||||
|
})?;
|
||||||
let app_dir = dirs::app_home_dir()?;
|
let app_dir = dirs::app_home_dir()?;
|
||||||
let app_dir_str = dirs::path_to_str(&app_dir)?;
|
let app_dir_str = dirs::path_to_str(&app_dir)?;
|
||||||
logging!(info, Type::Config, true, "验证目录: {}", app_dir_str);
|
logging!(info, Type::Config, true, "验证目录: {}", app_dir_str);
|
||||||
@@ -297,7 +301,7 @@ impl CoreManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// 只进行文件语法检查,不进行完整验证
|
/// 只进行文件语法检查,不进行完整验证
|
||||||
async fn validate_file_syntax(&self, config_path: &str) -> Result<(bool, String)> {
|
fn validate_file_syntax(&self, config_path: &str) -> Result<(bool, String)> {
|
||||||
logging!(info, Type::Config, true, "开始检查文件: {}", config_path);
|
logging!(info, Type::Config, true, "开始检查文件: {}", config_path);
|
||||||
|
|
||||||
// 读取文件内容
|
// 读取文件内容
|
||||||
@@ -325,7 +329,7 @@ impl CoreManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// 验证脚本文件语法
|
/// 验证脚本文件语法
|
||||||
async fn validate_script_file(&self, path: &str) -> Result<(bool, String)> {
|
fn validate_script_file(&self, path: &str) -> Result<(bool, String)> {
|
||||||
// 读取脚本内容
|
// 读取脚本内容
|
||||||
let content = match std::fs::read_to_string(path) {
|
let content = match std::fs::read_to_string(path) {
|
||||||
Ok(content) => content,
|
Ok(content) => content,
|
||||||
@@ -382,7 +386,7 @@ impl CoreManager {
|
|||||||
|
|
||||||
// 1. 先生成新的配置内容
|
// 1. 先生成新的配置内容
|
||||||
logging!(info, Type::Config, true, "生成新的配置内容");
|
logging!(info, Type::Config, true, "生成新的配置内容");
|
||||||
Config::generate().await?;
|
Config::generate()?;
|
||||||
|
|
||||||
// 2. 验证配置
|
// 2. 验证配置
|
||||||
match self.validate_config().await {
|
match self.validate_config().await {
|
||||||
@@ -435,7 +439,7 @@ impl CoreManager {
|
|||||||
|
|
||||||
// 获取当前管理的进程 PID
|
// 获取当前管理的进程 PID
|
||||||
let current_pid = {
|
let current_pid = {
|
||||||
let child_guard = self.child_sidecar.lock().await;
|
let child_guard = self.child_sidecar.lock();
|
||||||
child_guard.as_ref().map(|child| child.pid())
|
child_guard.as_ref().map(|child| child.pid())
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -729,7 +733,7 @@ impl CoreManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_core_by_sidecar(&self) -> Result<()> {
|
fn start_core_by_sidecar(&self) -> Result<()> {
|
||||||
logging!(trace, Type::Core, true, "Running core by sidecar");
|
logging!(trace, Type::Core, true, "Running core by sidecar");
|
||||||
let config_file = &Config::generate_file(ConfigType::Run)?;
|
let config_file = &Config::generate_file(ConfigType::Run)?;
|
||||||
let app_handle = handle::Handle::global()
|
let app_handle = handle::Handle::global()
|
||||||
@@ -783,14 +787,14 @@ impl CoreManager {
|
|||||||
"Started core by sidecar pid: {}",
|
"Started core by sidecar pid: {}",
|
||||||
pid
|
pid
|
||||||
);
|
);
|
||||||
*self.child_sidecar.lock().await = Some(child);
|
*self.child_sidecar.lock() = Some(child);
|
||||||
self.set_running_mode(RunningMode::Sidecar).await;
|
self.set_running_mode(RunningMode::Sidecar);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn stop_core_by_sidecar(&self) -> Result<()> {
|
fn stop_core_by_sidecar(&self) -> Result<()> {
|
||||||
logging!(trace, Type::Core, true, "Stopping core by sidecar");
|
logging!(trace, Type::Core, true, "Stopping core by sidecar");
|
||||||
|
|
||||||
if let Some(child) = self.child_sidecar.lock().await.take() {
|
if let Some(child) = self.child_sidecar.lock().take() {
|
||||||
let pid = child.pid();
|
let pid = child.pid();
|
||||||
child.kill()?;
|
child.kill()?;
|
||||||
logging!(
|
logging!(
|
||||||
@@ -801,7 +805,7 @@ impl CoreManager {
|
|||||||
pid
|
pid
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.set_running_mode(RunningMode::NotRunning).await;
|
self.set_running_mode(RunningMode::NotRunning);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -811,13 +815,13 @@ impl CoreManager {
|
|||||||
logging!(trace, Type::Core, true, "Running core by service");
|
logging!(trace, Type::Core, true, "Running core by service");
|
||||||
let config_file = &Config::generate_file(ConfigType::Run)?;
|
let config_file = &Config::generate_file(ConfigType::Run)?;
|
||||||
service::run_core_by_service(config_file).await?;
|
service::run_core_by_service(config_file).await?;
|
||||||
self.set_running_mode(RunningMode::Service).await;
|
self.set_running_mode(RunningMode::Service);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn stop_core_by_service(&self) -> Result<()> {
|
async fn stop_core_by_service(&self) -> Result<()> {
|
||||||
logging!(trace, Type::Core, true, "Stopping core by service");
|
logging!(trace, Type::Core, true, "Stopping core by service");
|
||||||
service::stop_core_by_service().await?;
|
service::stop_core_by_service().await?;
|
||||||
self.set_running_mode(RunningMode::NotRunning).await;
|
self.set_running_mode(RunningMode::NotRunning);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -839,7 +843,7 @@ impl CoreManager {
|
|||||||
async fn attempt_service_init(&self) -> Result<()> {
|
async fn attempt_service_init(&self) -> Result<()> {
|
||||||
if service::check_service_needs_reinstall().await {
|
if service::check_service_needs_reinstall().await {
|
||||||
logging!(info, Type::Core, true, "服务版本不匹配或状态异常,执行重装");
|
logging!(info, Type::Core, true, "服务版本不匹配或状态异常,执行重装");
|
||||||
if let Err(e) = service::reinstall_service().await {
|
if let Err(e) = service::reinstall_service() {
|
||||||
logging!(
|
logging!(
|
||||||
warn,
|
warn,
|
||||||
Type::Core,
|
Type::Core,
|
||||||
@@ -944,7 +948,7 @@ impl CoreManager {
|
|||||||
true,
|
true,
|
||||||
"用户偏好Sidecar模式或先前服务启动失败,使用Sidecar模式启动"
|
"用户偏好Sidecar模式或先前服务启动失败,使用Sidecar模式启动"
|
||||||
);
|
);
|
||||||
self.start_core_by_sidecar().await?;
|
self.start_core_by_sidecar()?;
|
||||||
// 如果 sidecar 启动成功,我们可以认为核心初始化流程到此结束
|
// 如果 sidecar 启动成功,我们可以认为核心初始化流程到此结束
|
||||||
// 后续的 Tray::global().subscribe_traffic().await 仍然会执行
|
// 后续的 Tray::global().subscribe_traffic().await 仍然会执行
|
||||||
} else {
|
} else {
|
||||||
@@ -956,7 +960,7 @@ impl CoreManager {
|
|||||||
true,
|
true,
|
||||||
"无服务安装记录 (首次运行或状态重置),尝试安装服务"
|
"无服务安装记录 (首次运行或状态重置),尝试安装服务"
|
||||||
);
|
);
|
||||||
match service::install_service().await {
|
match service::install_service() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
logging!(info, Type::Core, true, "服务安装成功(首次尝试)");
|
logging!(info, Type::Core, true, "服务安装成功(首次尝试)");
|
||||||
let mut new_state = service::ServiceState::default();
|
let mut new_state = service::ServiceState::default();
|
||||||
@@ -980,7 +984,7 @@ impl CoreManager {
|
|||||||
final_state.last_error =
|
final_state.last_error =
|
||||||
Some("Newly installed service failed to start".to_string());
|
Some("Newly installed service failed to start".to_string());
|
||||||
final_state.save()?;
|
final_state.save()?;
|
||||||
self.start_core_by_sidecar().await?;
|
self.start_core_by_sidecar()?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logging!(
|
logging!(
|
||||||
@@ -996,7 +1000,7 @@ impl CoreManager {
|
|||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
final_state.save()?;
|
final_state.save()?;
|
||||||
self.start_core_by_sidecar().await?;
|
self.start_core_by_sidecar()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -1007,7 +1011,7 @@ impl CoreManager {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
new_state.save()?;
|
new_state.save()?;
|
||||||
self.start_core_by_sidecar().await?;
|
self.start_core_by_sidecar()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1036,7 +1040,7 @@ impl CoreManager {
|
|||||||
}));
|
}));
|
||||||
final_state.save()?;
|
final_state.save()?;
|
||||||
}
|
}
|
||||||
self.start_core_by_sidecar().await?;
|
self.start_core_by_sidecar()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1047,13 +1051,13 @@ impl CoreManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_running_mode(&self, mode: RunningMode) {
|
pub fn set_running_mode(&self, mode: RunningMode) {
|
||||||
let mut guard = self.running.lock().await;
|
let mut guard = self.running.lock();
|
||||||
*guard = mode;
|
*guard = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_running_mode(&self) -> RunningMode {
|
pub fn get_running_mode(&self) -> RunningMode {
|
||||||
let guard = self.running.lock().await;
|
let guard = self.running.lock();
|
||||||
(*guard).clone()
|
(*guard).clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1061,7 +1065,7 @@ impl CoreManager {
|
|||||||
pub async fn start_core(&self) -> Result<()> {
|
pub async fn start_core(&self) -> Result<()> {
|
||||||
if service::is_service_available().await.is_ok() {
|
if service::is_service_available().await.is_ok() {
|
||||||
if service::check_service_needs_reinstall().await {
|
if service::check_service_needs_reinstall().await {
|
||||||
service::reinstall_service().await?;
|
service::reinstall_service()?;
|
||||||
}
|
}
|
||||||
logging!(info, Type::Core, true, "服务可用,使用服务模式启动");
|
logging!(info, Type::Core, true, "服务可用,使用服务模式启动");
|
||||||
self.start_core_by_service().await?;
|
self.start_core_by_service().await?;
|
||||||
@@ -1075,10 +1079,10 @@ impl CoreManager {
|
|||||||
true,
|
true,
|
||||||
"服务不可用,根据用户偏好使用Sidecar模式"
|
"服务不可用,根据用户偏好使用Sidecar模式"
|
||||||
);
|
);
|
||||||
self.start_core_by_sidecar().await?;
|
self.start_core_by_sidecar()?;
|
||||||
} else {
|
} else {
|
||||||
logging!(info, Type::Core, true, "服务不可用,使用Sidecar模式");
|
logging!(info, Type::Core, true, "服务不可用,使用Sidecar模式");
|
||||||
self.start_core_by_sidecar().await?;
|
self.start_core_by_sidecar()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1086,9 +1090,9 @@ impl CoreManager {
|
|||||||
|
|
||||||
/// 停止核心运行
|
/// 停止核心运行
|
||||||
pub async fn stop_core(&self) -> Result<()> {
|
pub async fn stop_core(&self) -> Result<()> {
|
||||||
match self.get_running_mode().await {
|
match self.get_running_mode() {
|
||||||
RunningMode::Service => self.stop_core_by_service().await,
|
RunningMode::Service => self.stop_core_by_service().await,
|
||||||
RunningMode::Sidecar => self.stop_core_by_sidecar().await,
|
RunningMode::Sidecar => self.stop_core_by_sidecar(),
|
||||||
RunningMode::NotRunning => Ok(()),
|
RunningMode::NotRunning => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1108,8 +1112,12 @@ impl CoreManager {
|
|||||||
logging!(error, Type::Core, true, "{}", error_message);
|
logging!(error, Type::Core, true, "{}", error_message);
|
||||||
return Err(error_message.to_string());
|
return Err(error_message.to_string());
|
||||||
}
|
}
|
||||||
let core: &str = &clash_core.clone().unwrap();
|
let core = clash_core.as_ref().ok_or_else(|| {
|
||||||
if !IVerge::VALID_CLASH_CORES.contains(&core) {
|
let msg = "Clash core should not be None";
|
||||||
|
logging!(error, Type::Core, true, "{}", msg);
|
||||||
|
msg.to_string()
|
||||||
|
})?;
|
||||||
|
if !IVerge::VALID_CLASH_CORES.contains(&core.as_str()) {
|
||||||
let error_message = format!("Clash core invalid name: {core}");
|
let error_message = format!("Clash core invalid name: {core}");
|
||||||
logging!(error, Type::Core, true, "{}", error_message);
|
logging!(error, Type::Core, true, "{}", error_message);
|
||||||
return Err(error_message);
|
return Err(error_message);
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ impl EventDrivenProxyManager {
|
|||||||
let (event_tx, event_rx) = mpsc::unbounded_channel();
|
let (event_tx, event_rx) = mpsc::unbounded_channel();
|
||||||
let (query_tx, query_rx) = mpsc::unbounded_channel();
|
let (query_tx, query_rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
Self::start_event_loop(state.clone(), event_rx, query_rx);
|
Self::start_event_loop(Arc::clone(&state), event_rx, query_rx);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
state,
|
state,
|
||||||
@@ -218,7 +218,7 @@ impl EventDrivenProxyManager {
|
|||||||
Self::enable_system_proxy(state).await;
|
Self::enable_system_proxy(state).await;
|
||||||
}
|
}
|
||||||
ProxyEvent::DisableProxy => {
|
ProxyEvent::DisableProxy => {
|
||||||
Self::disable_system_proxy(state).await;
|
Self::disable_system_proxy(state);
|
||||||
}
|
}
|
||||||
ProxyEvent::SwitchToPac => {
|
ProxyEvent::SwitchToPac => {
|
||||||
Self::switch_proxy_mode(state, true).await;
|
Self::switch_proxy_mode(state, true).await;
|
||||||
@@ -307,7 +307,9 @@ impl EventDrivenProxyManager {
|
|||||||
|
|
||||||
if !current.enable || current.url != expected.url {
|
if !current.enable || current.url != expected.url {
|
||||||
log::info!(target: "app", "PAC代理设置异常,正在恢复...");
|
log::info!(target: "app", "PAC代理设置异常,正在恢复...");
|
||||||
Self::restore_pac_proxy(&expected.url).await;
|
if let Err(e) = Self::restore_pac_proxy(&expected.url).await {
|
||||||
|
log::error!(target: "app", "恢复PAC代理失败: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
sleep(Duration::from_millis(500)).await;
|
sleep(Duration::from_millis(500)).await;
|
||||||
let restored = Self::get_auto_proxy_with_timeout().await;
|
let restored = Self::get_auto_proxy_with_timeout().await;
|
||||||
@@ -329,7 +331,9 @@ impl EventDrivenProxyManager {
|
|||||||
|
|
||||||
if !current.enable || current.host != expected.host || current.port != expected.port {
|
if !current.enable || current.host != expected.host || current.port != expected.port {
|
||||||
log::info!(target: "app", "系统代理设置异常,正在恢复...");
|
log::info!(target: "app", "系统代理设置异常,正在恢复...");
|
||||||
Self::restore_sys_proxy(&expected).await;
|
if let Err(e) = Self::restore_sys_proxy(&expected).await {
|
||||||
|
log::error!(target: "app", "恢复系统代理失败: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
sleep(Duration::from_millis(500)).await;
|
sleep(Duration::from_millis(500)).await;
|
||||||
let restored = Self::get_sys_proxy_with_timeout().await;
|
let restored = Self::get_sys_proxy_with_timeout().await;
|
||||||
@@ -350,16 +354,20 @@ impl EventDrivenProxyManager {
|
|||||||
|
|
||||||
if pac_enabled {
|
if pac_enabled {
|
||||||
let expected = Self::get_expected_pac_config();
|
let expected = Self::get_expected_pac_config();
|
||||||
Self::restore_pac_proxy(&expected.url).await;
|
if let Err(e) = Self::restore_pac_proxy(&expected.url).await {
|
||||||
|
log::error!(target: "app", "启用PAC代理失败: {}", e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let expected = Self::get_expected_sys_proxy();
|
let expected = Self::get_expected_sys_proxy();
|
||||||
Self::restore_sys_proxy(&expected).await;
|
if let Err(e) = Self::restore_sys_proxy(&expected).await {
|
||||||
|
log::error!(target: "app", "启用系统代理失败: {}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::check_and_restore_proxy(state).await;
|
Self::check_and_restore_proxy(state).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn disable_system_proxy(_state: &Arc<RwLock<ProxyState>>) {
|
fn disable_system_proxy(_state: &Arc<RwLock<ProxyState>>) {
|
||||||
log::info!(target: "app", "禁用系统代理");
|
log::info!(target: "app", "禁用系统代理");
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
@@ -380,13 +388,17 @@ impl EventDrivenProxyManager {
|
|||||||
logging_error!(Type::System, true, disabled_sys.set_system_proxy());
|
logging_error!(Type::System, true, disabled_sys.set_system_proxy());
|
||||||
|
|
||||||
let expected = Self::get_expected_pac_config();
|
let expected = Self::get_expected_pac_config();
|
||||||
Self::restore_pac_proxy(&expected.url).await;
|
if let Err(e) = Self::restore_pac_proxy(&expected.url).await {
|
||||||
|
log::error!(target: "app", "切换到PAC模式失败: {}", e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let disabled_auto = Autoproxy::default();
|
let disabled_auto = Autoproxy::default();
|
||||||
logging_error!(Type::System, true, disabled_auto.set_auto_proxy());
|
logging_error!(Type::System, true, disabled_auto.set_auto_proxy());
|
||||||
|
|
||||||
let expected = Self::get_expected_sys_proxy();
|
let expected = Self::get_expected_sys_proxy();
|
||||||
Self::restore_sys_proxy(&expected).await;
|
if let Err(e) = Self::restore_sys_proxy(&expected).await {
|
||||||
|
log::error!(target: "app", "切换到HTTP代理模式失败: {}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::update_state_timestamp(state, |s| s.pac_enabled = to_pac);
|
Self::update_state_timestamp(state, |s| s.pac_enabled = to_pac);
|
||||||
@@ -506,37 +518,45 @@ impl EventDrivenProxyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn restore_pac_proxy(expected_url: &str) {
|
#[cfg(target_os = "windows")]
|
||||||
#[cfg(not(target_os = "windows"))]
|
async fn restore_pac_proxy(expected_url: &str) -> Result<(), anyhow::Error> {
|
||||||
|
Self::execute_sysproxy_command(&["pac", expected_url]).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_async)]
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
async fn restore_pac_proxy(expected_url: &str) -> Result<(), anyhow::Error> {
|
||||||
{
|
{
|
||||||
let new_autoproxy = Autoproxy {
|
let new_autoproxy = Autoproxy {
|
||||||
enable: true,
|
enable: true,
|
||||||
url: expected_url.to_string(),
|
url: expected_url.to_string(),
|
||||||
};
|
};
|
||||||
logging_error!(Type::System, true, new_autoproxy.set_auto_proxy());
|
// logging_error!(Type::System, true, new_autoproxy.set_auto_proxy());
|
||||||
}
|
new_autoproxy
|
||||||
|
.set_auto_proxy()
|
||||||
#[cfg(target_os = "windows")]
|
.map_err(|e| anyhow::anyhow!("Failed to set auto proxy: {}", e))
|
||||||
{
|
|
||||||
Self::execute_sysproxy_command(&["pac", expected_url]).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn restore_sys_proxy(expected: &Sysproxy) {
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
{
|
|
||||||
logging_error!(Type::System, true, expected.set_system_proxy());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
{
|
|
||||||
let address = format!("{}:{}", expected.host, expected.port);
|
|
||||||
Self::execute_sysproxy_command(&["global", &address, &expected.bypass]).await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
async fn execute_sysproxy_command(args: &[&str]) {
|
async fn restore_sys_proxy(expected: &Sysproxy) -> Result<(), anyhow::Error> {
|
||||||
|
let address = format!("{}:{}", expected.host, expected.port);
|
||||||
|
Self::execute_sysproxy_command(&["global", &address, &expected.bypass]).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_async)]
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
async fn restore_sys_proxy(expected: &Sysproxy) -> Result<(), anyhow::Error> {
|
||||||
|
{
|
||||||
|
// logging_error!(Type::System, true, expected.set_system_proxy());
|
||||||
|
expected
|
||||||
|
.set_system_proxy()
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to set system proxy: {}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
async fn execute_sysproxy_command(args: &[&str]) -> Result<(), anyhow::Error> {
|
||||||
use crate::utils::dirs;
|
use crate::utils::dirs;
|
||||||
#[allow(unused_imports)] // creation_flags必须
|
#[allow(unused_imports)] // creation_flags必须
|
||||||
use std::os::windows::process::CommandExt;
|
use std::os::windows::process::CommandExt;
|
||||||
@@ -546,37 +566,22 @@ impl EventDrivenProxyManager {
|
|||||||
Ok(path) => path,
|
Ok(path) => path,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!(target: "app", "获取服务路径失败: {e}");
|
log::error!(target: "app", "获取服务路径失败: {e}");
|
||||||
return;
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let sysproxy_exe = binary_path.with_file_name("sysproxy.exe");
|
let sysproxy_exe = binary_path.with_file_name("sysproxy.exe");
|
||||||
if !sysproxy_exe.exists() {
|
if !sysproxy_exe.exists() {
|
||||||
log::error!(target: "app", "sysproxy.exe 不存在");
|
log::error!(target: "app", "sysproxy.exe 不存在");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
anyhow::ensure!(sysproxy_exe.exists(), "sysproxy.exe does not exist");
|
||||||
|
|
||||||
let output = Command::new(sysproxy_exe)
|
let _output = Command::new(sysproxy_exe)
|
||||||
.args(args)
|
.args(args)
|
||||||
.creation_flags(0x08000000) // CREATE_NO_WINDOW - 隐藏窗口
|
.creation_flags(0x08000000) // CREATE_NO_WINDOW - 隐藏窗口
|
||||||
.output()
|
.output()
|
||||||
.await;
|
.await?;
|
||||||
|
|
||||||
match output {
|
Ok(())
|
||||||
Ok(output) => {
|
|
||||||
if !output.status.success() {
|
|
||||||
log::error!(target: "app", "执行sysproxy命令失败: {args:?}");
|
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
||||||
if !stderr.is_empty() {
|
|
||||||
log::error!(target: "app", "sysproxy错误输出: {stderr}");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::debug!(target: "app", "成功执行sysproxy命令: {args:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::error!(target: "app", "执行sysproxy命令出错: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,121 +82,123 @@ impl NotificationSystem {
|
|||||||
|
|
||||||
*self.last_emit_time.write() = Instant::now();
|
*self.last_emit_time.write() = Instant::now();
|
||||||
|
|
||||||
self.worker_handle = Some(
|
match thread::Builder::new()
|
||||||
thread::Builder::new()
|
.name("frontend-notifier".into())
|
||||||
.name("frontend-notifier".into())
|
.spawn(move || {
|
||||||
.spawn(move || {
|
let handle = Handle::global();
|
||||||
let handle = Handle::global();
|
|
||||||
|
|
||||||
while !handle.is_exiting() {
|
while !handle.is_exiting() {
|
||||||
match rx.recv_timeout(Duration::from_millis(100)) {
|
match rx.recv_timeout(Duration::from_millis(100)) {
|
||||||
Ok(event) => {
|
Ok(event) => {
|
||||||
let system_guard = handle.notification_system.read();
|
let system_guard = handle.notification_system.read();
|
||||||
if system_guard.as_ref().is_none() {
|
let Some(system) = system_guard.as_ref() else {
|
||||||
log::warn!("NotificationSystem not found in handle while processing event.");
|
log::warn!("NotificationSystem not found in handle while processing event.");
|
||||||
continue;
|
continue;
|
||||||
}
|
};
|
||||||
let system = system_guard.as_ref().unwrap();
|
|
||||||
|
|
||||||
let is_emergency = *system.emergency_mode.read();
|
let is_emergency = *system.emergency_mode.read();
|
||||||
|
|
||||||
if is_emergency {
|
if is_emergency {
|
||||||
if let FrontendEvent::NoticeMessage { ref status, .. } = event {
|
if let FrontendEvent::NoticeMessage { ref status, .. } = event {
|
||||||
if status == "info" {
|
if status == "info" {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"Emergency mode active, skipping info message"
|
"Emergency mode active, skipping info message"
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(window) = handle.get_window() {
|
if let Some(window) = handle.get_window() {
|
||||||
*system.last_emit_time.write() = Instant::now();
|
*system.last_emit_time.write() = Instant::now();
|
||||||
|
|
||||||
let (event_name_str, payload_result) = match event {
|
let (event_name_str, payload_result) = match event {
|
||||||
FrontendEvent::RefreshClash => {
|
FrontendEvent::RefreshClash => {
|
||||||
("verge://refresh-clash-config", Ok(serde_json::json!("yes")))
|
("verge://refresh-clash-config", Ok(serde_json::json!("yes")))
|
||||||
}
|
}
|
||||||
FrontendEvent::RefreshVerge => {
|
FrontendEvent::RefreshVerge => {
|
||||||
("verge://refresh-verge-config", Ok(serde_json::json!("yes")))
|
("verge://refresh-verge-config", Ok(serde_json::json!("yes")))
|
||||||
}
|
}
|
||||||
FrontendEvent::NoticeMessage { status, message } => {
|
FrontendEvent::NoticeMessage { status, message } => {
|
||||||
match serde_json::to_value((status, message)) {
|
match serde_json::to_value((status, message)) {
|
||||||
Ok(p) => ("verge://notice-message", Ok(p)),
|
Ok(p) => ("verge://notice-message", Ok(p)),
|
||||||
Err(e) => {
|
|
||||||
log::error!("Failed to serialize NoticeMessage payload: {e}");
|
|
||||||
("verge://notice-message", Err(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FrontendEvent::ProfileChanged { current_profile_id } => {
|
|
||||||
("profile-changed", Ok(serde_json::json!(current_profile_id)))
|
|
||||||
}
|
|
||||||
FrontendEvent::TimerUpdated { profile_index } => {
|
|
||||||
("verge://timer-updated", Ok(serde_json::json!(profile_index)))
|
|
||||||
}
|
|
||||||
FrontendEvent::StartupCompleted => {
|
|
||||||
("verge://startup-completed", Ok(serde_json::json!(null)))
|
|
||||||
}
|
|
||||||
FrontendEvent::ProfileUpdateStarted { uid } => {
|
|
||||||
("profile-update-started", Ok(serde_json::json!({ "uid": uid })))
|
|
||||||
}
|
|
||||||
FrontendEvent::ProfileUpdateCompleted { uid } => {
|
|
||||||
("profile-update-completed", Ok(serde_json::json!({ "uid": uid })))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(payload) = payload_result {
|
|
||||||
match window.emit(event_name_str, payload) {
|
|
||||||
Ok(_) => {
|
|
||||||
system.stats.total_sent.fetch_add(1, Ordering::SeqCst);
|
|
||||||
// 记录成功发送的事件
|
|
||||||
if log::log_enabled!(log::Level::Debug) {
|
|
||||||
log::debug!("Successfully emitted event: {event_name_str}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("Failed to emit event {event_name_str}: {e}");
|
log::error!("Failed to serialize NoticeMessage payload: {e}");
|
||||||
system.stats.total_errors.fetch_add(1, Ordering::SeqCst);
|
("verge://notice-message", Err(e))
|
||||||
*system.stats.last_error_time.write() = Some(Instant::now());
|
}
|
||||||
|
}
|
||||||
let errors = system.stats.total_errors.load(Ordering::SeqCst);
|
}
|
||||||
const EMIT_ERROR_THRESHOLD: u64 = 10;
|
FrontendEvent::ProfileChanged { current_profile_id } => {
|
||||||
if errors > EMIT_ERROR_THRESHOLD && !*system.emergency_mode.read() {
|
("profile-changed", Ok(serde_json::json!(current_profile_id)))
|
||||||
log::warn!(
|
}
|
||||||
"Reached {EMIT_ERROR_THRESHOLD} emit errors, entering emergency mode"
|
FrontendEvent::TimerUpdated { profile_index } => {
|
||||||
);
|
("verge://timer-updated", Ok(serde_json::json!(profile_index)))
|
||||||
*system.emergency_mode.write() = true;
|
}
|
||||||
}
|
FrontendEvent::StartupCompleted => {
|
||||||
|
("verge://startup-completed", Ok(serde_json::json!(null)))
|
||||||
|
}
|
||||||
|
FrontendEvent::ProfileUpdateStarted { uid } => {
|
||||||
|
("profile-update-started", Ok(serde_json::json!({ "uid": uid })))
|
||||||
|
}
|
||||||
|
FrontendEvent::ProfileUpdateCompleted { uid } => {
|
||||||
|
("profile-update-completed", Ok(serde_json::json!({ "uid": uid })))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(payload) = payload_result {
|
||||||
|
match window.emit(event_name_str, payload) {
|
||||||
|
Ok(_) => {
|
||||||
|
system.stats.total_sent.fetch_add(1, Ordering::SeqCst);
|
||||||
|
// 记录成功发送的事件
|
||||||
|
if log::log_enabled!(log::Level::Debug) {
|
||||||
|
log::debug!("Successfully emitted event: {event_name_str}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Failed to emit event {event_name_str}: {e}");
|
||||||
|
system.stats.total_errors.fetch_add(1, Ordering::SeqCst);
|
||||||
|
*system.stats.last_error_time.write() = Some(Instant::now());
|
||||||
|
|
||||||
|
let errors = system.stats.total_errors.load(Ordering::SeqCst);
|
||||||
|
const EMIT_ERROR_THRESHOLD: u64 = 10;
|
||||||
|
if errors > EMIT_ERROR_THRESHOLD && !*system.emergency_mode.read() {
|
||||||
|
log::warn!(
|
||||||
|
"Reached {EMIT_ERROR_THRESHOLD} emit errors, entering emergency mode"
|
||||||
|
);
|
||||||
|
*system.emergency_mode.write() = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
system.stats.total_errors.fetch_add(1, Ordering::SeqCst);
|
|
||||||
*system.stats.last_error_time.write() = Some(Instant::now());
|
|
||||||
log::warn!("Skipped emitting event due to payload serialization error for {event_name_str}");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log::warn!("No window found, skipping event emit.");
|
system.stats.total_errors.fetch_add(1, Ordering::SeqCst);
|
||||||
|
*system.stats.last_error_time.write() = Some(Instant::now());
|
||||||
|
log::warn!("Skipped emitting event due to payload serialization error for {event_name_str}");
|
||||||
}
|
}
|
||||||
thread::sleep(Duration::from_millis(20));
|
} else {
|
||||||
}
|
log::warn!("No window found, skipping event emit.");
|
||||||
Err(mpsc::RecvTimeoutError::Timeout) => {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Err(mpsc::RecvTimeoutError::Disconnected) => {
|
|
||||||
log::info!(
|
|
||||||
"Notification channel disconnected, exiting worker thread"
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
thread::sleep(Duration::from_millis(20));
|
||||||
|
}
|
||||||
|
Err(mpsc::RecvTimeoutError::Timeout) => {
|
||||||
|
}
|
||||||
|
Err(mpsc::RecvTimeoutError::Disconnected) => {
|
||||||
|
log::info!(
|
||||||
|
"Notification channel disconnected, exiting worker thread"
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log::info!("Notification worker thread exiting");
|
log::info!("Notification worker thread exiting");
|
||||||
})
|
}) {
|
||||||
.expect("Failed to start notification worker thread"),
|
Ok(handle) => {
|
||||||
);
|
self.worker_handle = Some(handle);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to start notification worker thread: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 发送事件到队列
|
/// 发送事件到队列
|
||||||
|
|||||||
@@ -195,7 +195,9 @@ impl Hotkey {
|
|||||||
hotkey: &str,
|
hotkey: &str,
|
||||||
function: HotkeyFunction,
|
function: HotkeyFunction,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = handle::Handle::global()
|
||||||
|
.app_handle()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
|
||||||
let manager = app_handle.global_shortcut();
|
let manager = app_handle.global_shortcut();
|
||||||
|
|
||||||
logging!(
|
logging!(
|
||||||
@@ -351,7 +353,9 @@ impl Hotkey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&self) -> Result<()> {
|
pub fn reset(&self) -> Result<()> {
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = handle::Handle::global()
|
||||||
|
.app_handle()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
|
||||||
let manager = app_handle.global_shortcut();
|
let manager = app_handle.global_shortcut();
|
||||||
manager.unregister_all()?;
|
manager.unregister_all()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -364,7 +368,9 @@ impl Hotkey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn unregister(&self, hotkey: &str) -> Result<()> {
|
pub fn unregister(&self, hotkey: &str) -> Result<()> {
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = handle::Handle::global()
|
||||||
|
.app_handle()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
|
||||||
let manager = app_handle.global_shortcut();
|
let manager = app_handle.global_shortcut();
|
||||||
manager.unregister(hotkey)?;
|
manager.unregister(hotkey)?;
|
||||||
logging!(debug, Type::Hotkey, "Unregister hotkey {}", hotkey);
|
logging!(debug, Type::Hotkey, "Unregister hotkey {}", hotkey);
|
||||||
@@ -438,7 +444,17 @@ impl Hotkey {
|
|||||||
|
|
||||||
impl Drop for Hotkey {
|
impl Drop for Hotkey {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = match handle::Handle::global().app_handle() {
|
||||||
|
Some(handle) => handle,
|
||||||
|
None => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Hotkey,
|
||||||
|
"Failed to get app handle during hotkey cleanup"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
if let Err(e) = app_handle.global_shortcut().unregister_all() {
|
if let Err(e) = app_handle.global_shortcut().unregister_all() {
|
||||||
logging!(
|
logging!(
|
||||||
error,
|
error,
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ pub struct JsonResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub async fn uninstall_service() -> Result<()> {
|
pub fn uninstall_service() -> Result<()> {
|
||||||
logging!(info, Type::Service, true, "uninstall service");
|
logging!(info, Type::Service, true, "uninstall service");
|
||||||
|
|
||||||
use deelevate::{PrivilegeLevel, Token};
|
use deelevate::{PrivilegeLevel, Token};
|
||||||
@@ -138,7 +138,7 @@ pub async fn uninstall_service() -> Result<()> {
|
|||||||
if !status.success() {
|
if !status.success() {
|
||||||
bail!(
|
bail!(
|
||||||
"failed to uninstall service with status {}",
|
"failed to uninstall service with status {}",
|
||||||
status.code().unwrap()
|
status.code().unwrap_or(-1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ pub async fn uninstall_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub async fn install_service() -> Result<()> {
|
pub fn install_service() -> Result<()> {
|
||||||
logging!(info, Type::Service, true, "install service");
|
logging!(info, Type::Service, true, "install service");
|
||||||
|
|
||||||
use deelevate::{PrivilegeLevel, Token};
|
use deelevate::{PrivilegeLevel, Token};
|
||||||
@@ -172,7 +172,7 @@ pub async fn install_service() -> Result<()> {
|
|||||||
if !status.success() {
|
if !status.success() {
|
||||||
bail!(
|
bail!(
|
||||||
"failed to install service with status {}",
|
"failed to install service with status {}",
|
||||||
status.code().unwrap()
|
status.code().unwrap_or(-1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ pub async fn install_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub async fn reinstall_service() -> Result<()> {
|
pub fn reinstall_service() -> Result<()> {
|
||||||
logging!(info, Type::Service, true, "reinstall service");
|
logging!(info, Type::Service, true, "reinstall service");
|
||||||
|
|
||||||
// 获取当前服务状态
|
// 获取当前服务状态
|
||||||
@@ -198,7 +198,7 @@ pub async fn reinstall_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 先卸载服务
|
// 先卸载服务
|
||||||
if let Err(err) = uninstall_service().await {
|
if let Err(err) = uninstall_service() {
|
||||||
logging!(
|
logging!(
|
||||||
warn,
|
warn,
|
||||||
Type::Service,
|
Type::Service,
|
||||||
@@ -209,7 +209,7 @@ pub async fn reinstall_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 再安装服务
|
// 再安装服务
|
||||||
match install_service().await {
|
match install_service() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// 记录安装信息并保存
|
// 记录安装信息并保存
|
||||||
service_state.record_install();
|
service_state.record_install();
|
||||||
@@ -228,7 +228,7 @@ pub async fn reinstall_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub async fn uninstall_service() -> Result<()> {
|
pub fn uninstall_service() -> Result<()> {
|
||||||
logging!(info, Type::Service, true, "uninstall service");
|
logging!(info, Type::Service, true, "uninstall service");
|
||||||
use users::get_effective_uid;
|
use users::get_effective_uid;
|
||||||
|
|
||||||
@@ -254,13 +254,13 @@ pub async fn uninstall_service() -> Result<()> {
|
|||||||
Type::Service,
|
Type::Service,
|
||||||
true,
|
true,
|
||||||
"uninstall status code:{}",
|
"uninstall status code:{}",
|
||||||
status.code().unwrap()
|
status.code().unwrap_or(-1)
|
||||||
);
|
);
|
||||||
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
bail!(
|
bail!(
|
||||||
"failed to uninstall service with status {}",
|
"failed to uninstall service with status {}",
|
||||||
status.code().unwrap()
|
status.code().unwrap_or(-1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,7 +268,7 @@ pub async fn uninstall_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub async fn install_service() -> Result<()> {
|
pub fn install_service() -> Result<()> {
|
||||||
logging!(info, Type::Service, true, "install service");
|
logging!(info, Type::Service, true, "install service");
|
||||||
use users::get_effective_uid;
|
use users::get_effective_uid;
|
||||||
|
|
||||||
@@ -294,13 +294,13 @@ pub async fn install_service() -> Result<()> {
|
|||||||
Type::Service,
|
Type::Service,
|
||||||
true,
|
true,
|
||||||
"install status code:{}",
|
"install status code:{}",
|
||||||
status.code().unwrap()
|
status.code().unwrap_or(-1)
|
||||||
);
|
);
|
||||||
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
bail!(
|
bail!(
|
||||||
"failed to install service with status {}",
|
"failed to install service with status {}",
|
||||||
status.code().unwrap()
|
status.code().unwrap_or(-1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ pub async fn install_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub async fn reinstall_service() -> Result<()> {
|
pub fn reinstall_service() -> Result<()> {
|
||||||
logging!(info, Type::Service, true, "reinstall service");
|
logging!(info, Type::Service, true, "reinstall service");
|
||||||
|
|
||||||
// 获取当前服务状态
|
// 获取当前服务状态
|
||||||
@@ -326,7 +326,7 @@ pub async fn reinstall_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 先卸载服务
|
// 先卸载服务
|
||||||
if let Err(err) = uninstall_service().await {
|
if let Err(err) = uninstall_service() {
|
||||||
logging!(
|
logging!(
|
||||||
warn,
|
warn,
|
||||||
Type::Service,
|
Type::Service,
|
||||||
@@ -337,7 +337,7 @@ pub async fn reinstall_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 再安装服务
|
// 再安装服务
|
||||||
match install_service().await {
|
match install_service() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// 记录安装信息并保存
|
// 记录安装信息并保存
|
||||||
service_state.record_install();
|
service_state.record_install();
|
||||||
@@ -356,7 +356,7 @@ pub async fn reinstall_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub async fn uninstall_service() -> Result<()> {
|
pub fn uninstall_service() -> Result<()> {
|
||||||
use crate::utils::i18n::t;
|
use crate::utils::i18n::t;
|
||||||
|
|
||||||
logging!(info, Type::Service, true, "uninstall service");
|
logging!(info, Type::Service, true, "uninstall service");
|
||||||
@@ -384,7 +384,7 @@ pub async fn uninstall_service() -> Result<()> {
|
|||||||
if !status.success() {
|
if !status.success() {
|
||||||
bail!(
|
bail!(
|
||||||
"failed to uninstall service with status {}",
|
"failed to uninstall service with status {}",
|
||||||
status.code().unwrap()
|
status.code().unwrap_or(-1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,7 +392,7 @@ pub async fn uninstall_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub async fn install_service() -> Result<()> {
|
pub fn install_service() -> Result<()> {
|
||||||
use crate::utils::i18n::t;
|
use crate::utils::i18n::t;
|
||||||
|
|
||||||
logging!(info, Type::Service, true, "install service");
|
logging!(info, Type::Service, true, "install service");
|
||||||
@@ -420,7 +420,7 @@ pub async fn install_service() -> Result<()> {
|
|||||||
if !status.success() {
|
if !status.success() {
|
||||||
bail!(
|
bail!(
|
||||||
"failed to install service with status {}",
|
"failed to install service with status {}",
|
||||||
status.code().unwrap()
|
status.code().unwrap_or(-1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,7 +428,7 @@ pub async fn install_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub async fn reinstall_service() -> Result<()> {
|
pub fn reinstall_service() -> Result<()> {
|
||||||
logging!(info, Type::Service, true, "reinstall service");
|
logging!(info, Type::Service, true, "reinstall service");
|
||||||
|
|
||||||
// 获取当前服务状态
|
// 获取当前服务状态
|
||||||
@@ -446,7 +446,7 @@ pub async fn reinstall_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 先卸载服务
|
// 先卸载服务
|
||||||
if let Err(err) = uninstall_service().await {
|
if let Err(err) = uninstall_service() {
|
||||||
logging!(
|
logging!(
|
||||||
warn,
|
warn,
|
||||||
Type::Service,
|
Type::Service,
|
||||||
@@ -457,7 +457,7 @@ pub async fn reinstall_service() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 再安装服务
|
// 再安装服务
|
||||||
match install_service().await {
|
match install_service() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// 记录安装信息并保存
|
// 记录安装信息并保存
|
||||||
service_state.record_install();
|
service_state.record_install();
|
||||||
@@ -856,17 +856,14 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
|||||||
if let Ok(()) = start_with_existing_service(config_file).await {
|
if let Ok(()) = start_with_existing_service(config_file).await {
|
||||||
log::info!(target: "app", "尽管版本不匹配,但成功启动了服务");
|
log::info!(target: "app", "尽管版本不匹配,但成功启动了服务");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
|
||||||
bail!("服务版本不匹配且无法重装,启动失败");
|
|
||||||
}
|
}
|
||||||
|
bail!("服务版本不匹配且无法重装,启动失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!(target: "app", "开始重装服务");
|
log::info!(target: "app", "开始重装服务");
|
||||||
if let Err(err) = reinstall_service().await {
|
if let Err(err) = reinstall_service() {
|
||||||
log::warn!(target: "app", "服务重装失败: {err}");
|
log::warn!(target: "app", "服务重装失败: {err}");
|
||||||
|
bail!("Failed to reinstall service: {}", err);
|
||||||
log::info!(target: "app", "尝试使用现有服务");
|
|
||||||
return start_with_existing_service(config_file).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!(target: "app", "服务重装成功,尝试启动");
|
log::info!(target: "app", "服务重装成功,尝试启动");
|
||||||
@@ -890,7 +887,7 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
|||||||
if check_service_needs_reinstall().await {
|
if check_service_needs_reinstall().await {
|
||||||
log::info!(target: "app", "服务需要重装");
|
log::info!(target: "app", "服务需要重装");
|
||||||
|
|
||||||
if let Err(err) = reinstall_service().await {
|
if let Err(err) = reinstall_service() {
|
||||||
log::warn!(target: "app", "服务重装失败: {err}");
|
log::warn!(target: "app", "服务重装失败: {err}");
|
||||||
bail!("Failed to reinstall service: {}", err);
|
bail!("Failed to reinstall service: {}", err);
|
||||||
}
|
}
|
||||||
@@ -970,7 +967,7 @@ pub async fn is_service_available() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 强制重装服务(UI修复按钮)
|
/// 强制重装服务(UI修复按钮)
|
||||||
pub async fn force_reinstall_service() -> Result<()> {
|
pub fn force_reinstall_service() -> Result<()> {
|
||||||
log::info!(target: "app", "用户请求强制重装服务");
|
log::info!(target: "app", "用户请求强制重装服务");
|
||||||
|
|
||||||
let service_state = ServiceState::default();
|
let service_state = ServiceState::default();
|
||||||
@@ -978,7 +975,7 @@ pub async fn force_reinstall_service() -> Result<()> {
|
|||||||
|
|
||||||
log::info!(target: "app", "已重置服务状态,开始执行重装");
|
log::info!(target: "app", "已重置服务状态,开始执行重装");
|
||||||
|
|
||||||
match reinstall_service().await {
|
match reinstall_service() {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
log::info!(target: "app", "服务重装成功");
|
log::info!(target: "app", "服务重装成功");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -149,7 +149,9 @@ impl Sysopt {
|
|||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use tauri_plugin_shell::ShellExt;
|
use tauri_plugin_shell::ShellExt;
|
||||||
|
|
||||||
let app_handle = Handle::global().app_handle().unwrap();
|
let app_handle = Handle::global()
|
||||||
|
.app_handle()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("App handle not available"))?;
|
||||||
|
|
||||||
let binary_path = dirs::service_path()?;
|
let binary_path = dirs::service_path()?;
|
||||||
let sysproxy_exe = binary_path.with_file_name("sysproxy.exe");
|
let sysproxy_exe = binary_path.with_file_name("sysproxy.exe");
|
||||||
@@ -160,23 +162,27 @@ impl Sysopt {
|
|||||||
let shell = app_handle.shell();
|
let shell = app_handle.shell();
|
||||||
let output = if pac_enable {
|
let output = if pac_enable {
|
||||||
let address = format!("http://{proxy_host}:{pac_port}/commands/pac");
|
let address = format!("http://{proxy_host}:{pac_port}/commands/pac");
|
||||||
let output = shell
|
let sysproxy_str = sysproxy_exe
|
||||||
.command(sysproxy_exe.as_path().to_str().unwrap())
|
.as_path()
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Invalid sysproxy.exe path"))?;
|
||||||
|
shell
|
||||||
|
.command(sysproxy_str)
|
||||||
.args(["pac", address.as_str()])
|
.args(["pac", address.as_str()])
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await?
|
||||||
.unwrap();
|
|
||||||
output
|
|
||||||
} else {
|
} else {
|
||||||
let address = format!("{proxy_host}:{port}");
|
let address = format!("{proxy_host}:{port}");
|
||||||
let bypass = get_bypass();
|
let bypass = get_bypass();
|
||||||
let output = shell
|
let sysproxy_str = sysproxy_exe
|
||||||
.command(sysproxy_exe.as_path().to_str().unwrap())
|
.as_path()
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Invalid sysproxy.exe path"))?;
|
||||||
|
shell
|
||||||
|
.command(sysproxy_str)
|
||||||
.args(["global", address.as_str(), bypass.as_ref()])
|
.args(["global", address.as_str(), bypass.as_ref()])
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await?
|
||||||
.unwrap();
|
|
||||||
output
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
@@ -218,7 +224,9 @@ impl Sysopt {
|
|||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use tauri_plugin_shell::ShellExt;
|
use tauri_plugin_shell::ShellExt;
|
||||||
|
|
||||||
let app_handle = Handle::global().app_handle().unwrap();
|
let app_handle = Handle::global()
|
||||||
|
.app_handle()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("App handle not available"))?;
|
||||||
|
|
||||||
let binary_path = dirs::service_path()?;
|
let binary_path = dirs::service_path()?;
|
||||||
let sysproxy_exe = binary_path.with_file_name("sysproxy.exe");
|
let sysproxy_exe = binary_path.with_file_name("sysproxy.exe");
|
||||||
@@ -228,12 +236,15 @@ impl Sysopt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let shell = app_handle.shell();
|
let shell = app_handle.shell();
|
||||||
|
let sysproxy_str = sysproxy_exe
|
||||||
|
.as_path()
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Invalid sysproxy.exe path"))?;
|
||||||
let output = shell
|
let output = shell
|
||||||
.command(sysproxy_exe.as_path().to_str().unwrap())
|
.command(sysproxy_str)
|
||||||
.args(["set", "1"])
|
.args(["set", "1"])
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
bail!("sysproxy exe run failed");
|
bail!("sysproxy exe run failed");
|
||||||
@@ -279,7 +290,10 @@ impl Sysopt {
|
|||||||
|
|
||||||
/// 尝试使用原来的自启动方法
|
/// 尝试使用原来的自启动方法
|
||||||
fn try_original_autostart_method(&self, is_enable: bool) {
|
fn try_original_autostart_method(&self, is_enable: bool) {
|
||||||
let app_handle = Handle::global().app_handle().unwrap();
|
let Some(app_handle) = Handle::global().app_handle() else {
|
||||||
|
log::error!(target: "app", "App handle not available for autostart");
|
||||||
|
return;
|
||||||
|
};
|
||||||
let autostart_manager = app_handle.autolaunch();
|
let autostart_manager = app_handle.autolaunch();
|
||||||
|
|
||||||
if is_enable {
|
if is_enable {
|
||||||
@@ -306,7 +320,9 @@ impl Sysopt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 回退到原来的方法
|
// 回退到原来的方法
|
||||||
let app_handle = Handle::global().app_handle().unwrap();
|
let app_handle = Handle::global()
|
||||||
|
.app_handle()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("App handle not available"))?;
|
||||||
let autostart_manager = app_handle.autolaunch();
|
let autostart_manager = app_handle.autolaunch();
|
||||||
|
|
||||||
match autostart_manager.is_enabled() {
|
match autostart_manager.is_enabled() {
|
||||||
|
|||||||
@@ -71,9 +71,10 @@ impl TrayState {
|
|||||||
let verge = Config::verge().latest_ref().clone();
|
let verge = Config::verge().latest_ref().clone();
|
||||||
let is_common_tray_icon = verge.common_tray_icon.unwrap_or(false);
|
let is_common_tray_icon = verge.common_tray_icon.unwrap_or(false);
|
||||||
if is_common_tray_icon {
|
if is_common_tray_icon {
|
||||||
if let Some(common_icon_path) = find_target_icons("common").unwrap() {
|
if let Ok(Some(common_icon_path)) = find_target_icons("common") {
|
||||||
let icon_data = fs::read(common_icon_path).unwrap();
|
if let Ok(icon_data) = fs::read(common_icon_path) {
|
||||||
return (true, icon_data);
|
return (true, icon_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@@ -105,9 +106,10 @@ impl TrayState {
|
|||||||
let verge = Config::verge().latest_ref().clone();
|
let verge = Config::verge().latest_ref().clone();
|
||||||
let is_sysproxy_tray_icon = verge.sysproxy_tray_icon.unwrap_or(false);
|
let is_sysproxy_tray_icon = verge.sysproxy_tray_icon.unwrap_or(false);
|
||||||
if is_sysproxy_tray_icon {
|
if is_sysproxy_tray_icon {
|
||||||
if let Some(sysproxy_icon_path) = find_target_icons("sysproxy").unwrap() {
|
if let Ok(Some(sysproxy_icon_path)) = find_target_icons("sysproxy") {
|
||||||
let icon_data = fs::read(sysproxy_icon_path).unwrap();
|
if let Ok(icon_data) = fs::read(sysproxy_icon_path) {
|
||||||
return (true, icon_data);
|
return (true, icon_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@@ -139,9 +141,10 @@ impl TrayState {
|
|||||||
let verge = Config::verge().latest_ref().clone();
|
let verge = Config::verge().latest_ref().clone();
|
||||||
let is_tun_tray_icon = verge.tun_tray_icon.unwrap_or(false);
|
let is_tun_tray_icon = verge.tun_tray_icon.unwrap_or(false);
|
||||||
if is_tun_tray_icon {
|
if is_tun_tray_icon {
|
||||||
if let Some(tun_icon_path) = find_target_icons("tun").unwrap() {
|
if let Ok(Some(tun_icon_path)) = find_target_icons("tun") {
|
||||||
let icon_data = fs::read(tun_icon_path).unwrap();
|
if let Ok(icon_data) = fs::read(tun_icon_path) {
|
||||||
return (true, icon_data);
|
return (true, icon_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@@ -188,10 +191,14 @@ impl Tray {
|
|||||||
|
|
||||||
/// 更新托盘点击行为
|
/// 更新托盘点击行为
|
||||||
pub fn update_click_behavior(&self) -> Result<()> {
|
pub fn update_click_behavior(&self) -> Result<()> {
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = handle::Handle::global()
|
||||||
|
.app_handle()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray update"))?;
|
||||||
let tray_event = { Config::verge().latest_ref().tray_event.clone() };
|
let tray_event = { Config::verge().latest_ref().tray_event.clone() };
|
||||||
let tray_event: String = tray_event.unwrap_or("main_window".into());
|
let tray_event: String = tray_event.unwrap_or("main_window".into());
|
||||||
let tray = app_handle.tray_by_id("main").unwrap();
|
let tray = app_handle
|
||||||
|
.tray_by_id("main")
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?;
|
||||||
match tray_event.as_str() {
|
match tray_event.as_str() {
|
||||||
"tray_menu" => tray.set_show_menu_on_left_click(true)?,
|
"tray_menu" => tray.set_show_menu_on_left_click(true)?,
|
||||||
_ => tray.set_show_menu_on_left_click(false)?,
|
_ => tray.set_show_menu_on_left_click(false)?,
|
||||||
@@ -360,8 +367,12 @@ impl Tray {
|
|||||||
|
|
||||||
/// 更新托盘显示状态的函数
|
/// 更新托盘显示状态的函数
|
||||||
pub fn update_tray_display(&self) -> Result<()> {
|
pub fn update_tray_display(&self) -> Result<()> {
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = handle::Handle::global()
|
||||||
let _tray = app_handle.tray_by_id("main").unwrap();
|
.app_handle()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray update"))?;
|
||||||
|
let _tray = app_handle
|
||||||
|
.tray_by_id("main")
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?;
|
||||||
|
|
||||||
// 更新菜单
|
// 更新菜单
|
||||||
self.update_menu()?;
|
self.update_menu()?;
|
||||||
@@ -562,9 +573,8 @@ fn create_tray_menu(
|
|||||||
is_current_profile,
|
is_current_profile,
|
||||||
None::<&str>,
|
None::<&str>,
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let profile_menu_items: Vec<&dyn IsMenuItem<Wry>> = profile_menu_items
|
let profile_menu_items: Vec<&dyn IsMenuItem<Wry>> = profile_menu_items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item| item as &dyn IsMenuItem<Wry>)
|
.map(|item| item as &dyn IsMenuItem<Wry>)
|
||||||
@@ -576,8 +586,7 @@ fn create_tray_menu(
|
|||||||
t("Dashboard"),
|
t("Dashboard"),
|
||||||
true,
|
true,
|
||||||
hotkeys.get("open_or_close_dashboard").map(|s| s.as_str()),
|
hotkeys.get("open_or_close_dashboard").map(|s| s.as_str()),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let rule_mode = &CheckMenuItem::with_id(
|
let rule_mode = &CheckMenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -586,8 +595,7 @@ fn create_tray_menu(
|
|||||||
true,
|
true,
|
||||||
mode == "rule",
|
mode == "rule",
|
||||||
hotkeys.get("clash_mode_rule").map(|s| s.as_str()),
|
hotkeys.get("clash_mode_rule").map(|s| s.as_str()),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let global_mode = &CheckMenuItem::with_id(
|
let global_mode = &CheckMenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -596,8 +604,7 @@ fn create_tray_menu(
|
|||||||
true,
|
true,
|
||||||
mode == "global",
|
mode == "global",
|
||||||
hotkeys.get("clash_mode_global").map(|s| s.as_str()),
|
hotkeys.get("clash_mode_global").map(|s| s.as_str()),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let direct_mode = &CheckMenuItem::with_id(
|
let direct_mode = &CheckMenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -606,8 +613,7 @@ fn create_tray_menu(
|
|||||||
true,
|
true,
|
||||||
mode == "direct",
|
mode == "direct",
|
||||||
hotkeys.get("clash_mode_direct").map(|s| s.as_str()),
|
hotkeys.get("clash_mode_direct").map(|s| s.as_str()),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let profiles = &Submenu::with_id_and_items(
|
let profiles = &Submenu::with_id_and_items(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -615,8 +621,7 @@ fn create_tray_menu(
|
|||||||
t("Profiles"),
|
t("Profiles"),
|
||||||
true,
|
true,
|
||||||
&profile_menu_items,
|
&profile_menu_items,
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let system_proxy = &CheckMenuItem::with_id(
|
let system_proxy = &CheckMenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -625,8 +630,7 @@ fn create_tray_menu(
|
|||||||
true,
|
true,
|
||||||
system_proxy_enabled,
|
system_proxy_enabled,
|
||||||
hotkeys.get("toggle_system_proxy").map(|s| s.as_str()),
|
hotkeys.get("toggle_system_proxy").map(|s| s.as_str()),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let tun_mode = &CheckMenuItem::with_id(
|
let tun_mode = &CheckMenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -635,8 +639,7 @@ fn create_tray_menu(
|
|||||||
true,
|
true,
|
||||||
tun_mode_enabled,
|
tun_mode_enabled,
|
||||||
hotkeys.get("toggle_tun_mode").map(|s| s.as_str()),
|
hotkeys.get("toggle_tun_mode").map(|s| s.as_str()),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let lighteweight_mode = &CheckMenuItem::with_id(
|
let lighteweight_mode = &CheckMenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -645,11 +648,9 @@ fn create_tray_menu(
|
|||||||
true,
|
true,
|
||||||
is_lightweight_mode,
|
is_lightweight_mode,
|
||||||
hotkeys.get("entry_lightweight_mode").map(|s| s.as_str()),
|
hotkeys.get("entry_lightweight_mode").map(|s| s.as_str()),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let copy_env =
|
let copy_env = &MenuItem::with_id(app_handle, "copy_env", t("Copy Env"), true, None::<&str>)?;
|
||||||
&MenuItem::with_id(app_handle, "copy_env", t("Copy Env"), true, None::<&str>).unwrap();
|
|
||||||
|
|
||||||
let open_app_dir = &MenuItem::with_id(
|
let open_app_dir = &MenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -657,8 +658,7 @@ fn create_tray_menu(
|
|||||||
t("Conf Dir"),
|
t("Conf Dir"),
|
||||||
true,
|
true,
|
||||||
None::<&str>,
|
None::<&str>,
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let open_core_dir = &MenuItem::with_id(
|
let open_core_dir = &MenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -666,8 +666,7 @@ fn create_tray_menu(
|
|||||||
t("Core Dir"),
|
t("Core Dir"),
|
||||||
true,
|
true,
|
||||||
None::<&str>,
|
None::<&str>,
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let open_logs_dir = &MenuItem::with_id(
|
let open_logs_dir = &MenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -675,8 +674,7 @@ fn create_tray_menu(
|
|||||||
t("Logs Dir"),
|
t("Logs Dir"),
|
||||||
true,
|
true,
|
||||||
None::<&str>,
|
None::<&str>,
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let open_dir = &Submenu::with_id_and_items(
|
let open_dir = &Submenu::with_id_and_items(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -684,8 +682,7 @@ fn create_tray_menu(
|
|||||||
t("Open Dir"),
|
t("Open Dir"),
|
||||||
true,
|
true,
|
||||||
&[open_app_dir, open_core_dir, open_logs_dir],
|
&[open_app_dir, open_core_dir, open_logs_dir],
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let restart_clash = &MenuItem::with_id(
|
let restart_clash = &MenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -693,8 +690,7 @@ fn create_tray_menu(
|
|||||||
t("Restart Clash Core"),
|
t("Restart Clash Core"),
|
||||||
true,
|
true,
|
||||||
None::<&str>,
|
None::<&str>,
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let restart_app = &MenuItem::with_id(
|
let restart_app = &MenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -702,8 +698,7 @@ fn create_tray_menu(
|
|||||||
t("Restart App"),
|
t("Restart App"),
|
||||||
true,
|
true,
|
||||||
None::<&str>,
|
None::<&str>,
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let app_version = &MenuItem::with_id(
|
let app_version = &MenuItem::with_id(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -711,8 +706,7 @@ fn create_tray_menu(
|
|||||||
format!("{} {version}", t("Verge Version")),
|
format!("{} {version}", t("Verge Version")),
|
||||||
true,
|
true,
|
||||||
None::<&str>,
|
None::<&str>,
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let more = &Submenu::with_id_and_items(
|
let more = &Submenu::with_id_and_items(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -720,13 +714,11 @@ fn create_tray_menu(
|
|||||||
t("More"),
|
t("More"),
|
||||||
true,
|
true,
|
||||||
&[restart_clash, restart_app, app_version],
|
&[restart_clash, restart_app, app_version],
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let quit =
|
let quit = &MenuItem::with_id(app_handle, "quit", t("Exit"), true, Some("CmdOrControl+Q"))?;
|
||||||
&MenuItem::with_id(app_handle, "quit", t("Exit"), true, Some("CmdOrControl+Q")).unwrap();
|
|
||||||
|
|
||||||
let separator = &PredefinedMenuItem::separator(app_handle).unwrap();
|
let separator = &PredefinedMenuItem::separator(app_handle)?;
|
||||||
|
|
||||||
let menu = tauri::menu::MenuBuilder::new(app_handle)
|
let menu = tauri::menu::MenuBuilder::new(app_handle)
|
||||||
.items(&[
|
.items(&[
|
||||||
@@ -748,8 +740,7 @@ fn create_tray_menu(
|
|||||||
separator,
|
separator,
|
||||||
quit,
|
quit,
|
||||||
])
|
])
|
||||||
.build()
|
.build()?;
|
||||||
.unwrap();
|
|
||||||
Ok(menu)
|
Ok(menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use deelevate::{PrivilegeLevel, Token};
|
|||||||
use runas::Command as RunasCommand;
|
use runas::Command as RunasCommand;
|
||||||
use std::process::Command as StdCommand;
|
use std::process::Command as StdCommand;
|
||||||
|
|
||||||
pub async fn invoke_uwptools() -> Result<()> {
|
pub fn invoke_uwptools() -> Result<()> {
|
||||||
let resource_dir = dirs::app_resources_dir()?;
|
let resource_dir = dirs::app_resources_dir()?;
|
||||||
let tool_path = resource_dir.join("enableLoopback.exe");
|
let tool_path = resource_dir.join("enableLoopback.exe");
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ pub fn use_merge(merge: Mapping, config: Mapping) -> Mapping {
|
|||||||
|
|
||||||
deep_merge(&mut config, &Value::from(merge));
|
deep_merge(&mut config, &Value::from(merge));
|
||||||
|
|
||||||
let config = config.as_mapping().unwrap().clone();
|
let config = config.as_mapping().cloned().unwrap_or_else(|| {
|
||||||
|
log::error!("Failed to convert merged config to mapping, using empty mapping");
|
||||||
|
Mapping::new()
|
||||||
|
});
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type ResultLog = Vec<(String, String)>;
|
|||||||
|
|
||||||
/// Enhance mode
|
/// Enhance mode
|
||||||
/// 返回最终订阅、该订阅包含的键、和script执行的结果
|
/// 返回最终订阅、该订阅包含的键、和script执行的结果
|
||||||
pub async fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
|
pub fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
|
||||||
// config.yaml 的订阅
|
// config.yaml 的订阅
|
||||||
let clash_config = { Config::clash().latest_ref().0.clone() };
|
let clash_config = { Config::clash().latest_ref().0.clone() };
|
||||||
|
|
||||||
@@ -274,7 +274,7 @@ pub async fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
config = use_tun(config, enable_tun).await;
|
config = use_tun(config, enable_tun);
|
||||||
config = use_sort(config);
|
config = use_sort(config);
|
||||||
|
|
||||||
// 应用独立的DNS配置(如果启用)
|
// 应用独立的DNS配置(如果启用)
|
||||||
|
|||||||
@@ -7,23 +7,42 @@ pub fn use_script(
|
|||||||
config: Mapping,
|
config: Mapping,
|
||||||
name: String,
|
name: String,
|
||||||
) -> Result<(Mapping, Vec<(String, String)>)> {
|
) -> Result<(Mapping, Vec<(String, String)>)> {
|
||||||
use boa_engine::{native_function::NativeFunction, Context, JsValue, Source};
|
use boa_engine::{native_function::NativeFunction, Context, JsString, JsValue, Source};
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
let mut context = Context::default();
|
let mut context = Context::default();
|
||||||
|
|
||||||
let outputs = Rc::new(RefCell::new(vec![]));
|
let outputs = Rc::new(RefCell::new(vec![]));
|
||||||
|
|
||||||
let copy_outputs = outputs.clone();
|
let copy_outputs = Rc::clone(&outputs);
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = context.register_global_builtin_callable(
|
let _ = context.register_global_builtin_callable(
|
||||||
"__verge_log__".into(),
|
"__verge_log__".into(),
|
||||||
2,
|
2,
|
||||||
NativeFunction::from_closure(
|
NativeFunction::from_closure(
|
||||||
move |_: &JsValue, args: &[JsValue], context: &mut Context| {
|
move |_: &JsValue, args: &[JsValue], context: &mut Context| {
|
||||||
let level = args.first().unwrap().to_string(context)?;
|
let level = args.first().ok_or_else(|| {
|
||||||
let level = level.to_std_string().unwrap();
|
boa_engine::JsError::from_opaque(
|
||||||
let data = args.get(1).unwrap().to_string(context)?;
|
JsString::from("Missing level argument").into(),
|
||||||
let data = data.to_std_string().unwrap();
|
)
|
||||||
|
})?;
|
||||||
|
let level = level.to_string(context)?;
|
||||||
|
let level = level.to_std_string().map_err(|_| {
|
||||||
|
boa_engine::JsError::from_opaque(
|
||||||
|
JsString::from("Failed to convert level to string").into(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let data = args.get(1).ok_or_else(|| {
|
||||||
|
boa_engine::JsError::from_opaque(
|
||||||
|
JsString::from("Missing data argument").into(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let data = data.to_string(context)?;
|
||||||
|
let data = data.to_std_string().map_err(|_| {
|
||||||
|
boa_engine::JsError::from_opaque(
|
||||||
|
JsString::from("Failed to convert data to string").into(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
let mut out = copy_outputs.borrow_mut();
|
let mut out = copy_outputs.borrow_mut();
|
||||||
out.push((level, data));
|
out.push((level, data));
|
||||||
Ok(JsValue::undefined())
|
Ok(JsValue::undefined())
|
||||||
@@ -49,20 +68,24 @@ pub fn use_script(
|
|||||||
let safe_name = escape_js_string_for_single_quote(&name);
|
let safe_name = escape_js_string_for_single_quote(&name);
|
||||||
|
|
||||||
let code = format!(
|
let code = format!(
|
||||||
r#"try{{
|
r"try{{
|
||||||
{script};
|
{script};
|
||||||
JSON.stringify(main({config_str},'{safe_name}')||'')
|
JSON.stringify(main({config_str},'{safe_name}')||'')
|
||||||
}} catch(err) {{
|
}} catch(err) {{
|
||||||
`__error_flag__ ${{err.toString()}}`
|
`__error_flag__ ${{err.toString()}}`
|
||||||
}}"#
|
}}"
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Ok(result) = context.eval(Source::from_bytes(code.as_str())) {
|
if let Ok(result) = context.eval(Source::from_bytes(code.as_str())) {
|
||||||
if !result.is_string() {
|
if !result.is_string() {
|
||||||
anyhow::bail!("main function should return object");
|
anyhow::bail!("main function should return object");
|
||||||
}
|
}
|
||||||
let result = result.to_string(&mut context).unwrap();
|
let result = result
|
||||||
let result = result.to_std_string().unwrap();
|
.to_string(&mut context)
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to convert JS result to string: {}", e))?;
|
||||||
|
let result = result
|
||||||
|
.to_std_string()
|
||||||
|
.map_err(|_| anyhow::anyhow!("Failed to convert JS string to std string"))?;
|
||||||
|
|
||||||
// 直接解析JSON结果,不做其他解析
|
// 直接解析JSON结果,不做其他解析
|
||||||
let res: Result<Mapping, Error> = parse_json_safely(&result);
|
let res: Result<Mapping, Error> = parse_json_safely(&result);
|
||||||
@@ -102,6 +125,8 @@ fn escape_js_string_for_single_quote(s: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
fn test_script() {
|
fn test_script() {
|
||||||
let script = r#"
|
let script = r#"
|
||||||
function main(config) {
|
function main(config) {
|
||||||
@@ -114,7 +139,7 @@ fn test_script() {
|
|||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let config = r#"
|
let config = r"
|
||||||
rules:
|
rules:
|
||||||
- 111
|
- 111
|
||||||
- 222
|
- 222
|
||||||
@@ -122,22 +147,21 @@ fn test_script() {
|
|||||||
enable: false
|
enable: false
|
||||||
dns:
|
dns:
|
||||||
enable: false
|
enable: false
|
||||||
"#;
|
";
|
||||||
|
|
||||||
let config = serde_yaml::from_str(config).unwrap();
|
let config = serde_yaml::from_str(config).expect("Failed to parse test config YAML");
|
||||||
let (config, results) = use_script(script.into(), config, "".to_string()).unwrap();
|
let (config, results) = use_script(script.into(), config, "".to_string())
|
||||||
|
.expect("Script execution should succeed in test");
|
||||||
|
|
||||||
let _ = serde_yaml::to_string(&config).unwrap();
|
let _ = serde_yaml::to_string(&config).expect("Failed to serialize config to YAML");
|
||||||
let yaml_config_size = std::mem::size_of_val(&config);
|
let yaml_config_size = std::mem::size_of_val(&config);
|
||||||
dbg!(yaml_config_size);
|
|
||||||
let box_yaml_config_size = std::mem::size_of_val(&Box::new(config));
|
let box_yaml_config_size = std::mem::size_of_val(&Box::new(config));
|
||||||
dbg!(box_yaml_config_size);
|
|
||||||
dbg!(results);
|
|
||||||
assert!(box_yaml_config_size < yaml_config_size);
|
assert!(box_yaml_config_size < yaml_config_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 测试特殊字符转义功能
|
// 测试特殊字符转义功能
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
fn test_escape_unescape() {
|
fn test_escape_unescape() {
|
||||||
let test_string = r#"Hello "World"!\nThis is a test with \u00A9 copyright symbol."#;
|
let test_string = r#"Hello "World"!\nThis is a test with \u00A9 copyright symbol."#;
|
||||||
let escaped = escape_js_string_for_single_quote(test_string);
|
let escaped = escape_js_string_for_single_quote(test_string);
|
||||||
@@ -145,13 +169,14 @@ fn test_escape_unescape() {
|
|||||||
println!("Escaped: {escaped}");
|
println!("Escaped: {escaped}");
|
||||||
|
|
||||||
let json_str = r#"{"key":"value","nested":{"key":"value"}}"#;
|
let json_str = r#"{"key":"value","nested":{"key":"value"}}"#;
|
||||||
let parsed = parse_json_safely(json_str).unwrap();
|
let parsed = parse_json_safely(json_str).expect("Failed to parse test JSON safely");
|
||||||
|
|
||||||
assert!(parsed.contains_key("key"));
|
assert!(parsed.contains_key("key"));
|
||||||
assert!(parsed.contains_key("nested"));
|
assert!(parsed.contains_key("nested"));
|
||||||
|
|
||||||
let quoted_json_str = r#""{"key":"value","nested":{"key":"value"}}""#;
|
let quoted_json_str = r#""{"key":"value","nested":{"key":"value"}}""#;
|
||||||
let parsed_quoted = parse_json_safely(quoted_json_str).unwrap();
|
let parsed_quoted =
|
||||||
|
parse_json_safely(quoted_json_str).expect("Failed to parse quoted test JSON safely");
|
||||||
|
|
||||||
assert!(parsed_quoted.contains_key("key"));
|
assert!(parsed_quoted.contains_key("key"));
|
||||||
assert!(parsed_quoted.contains_key("nested"));
|
assert!(parsed_quoted.contains_key("nested"));
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ mod tests {
|
|||||||
use serde_yaml::Value;
|
use serde_yaml::Value;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
fn test_delete_proxy_and_references() {
|
fn test_delete_proxy_and_references() {
|
||||||
let config_str = r#"
|
let config_str = r#"
|
||||||
proxies:
|
proxies:
|
||||||
@@ -107,7 +109,8 @@ proxy-groups:
|
|||||||
proxies:
|
proxies:
|
||||||
- "proxy1"
|
- "proxy1"
|
||||||
"#;
|
"#;
|
||||||
let mut config: Mapping = serde_yaml::from_str(config_str).unwrap();
|
let mut config: Mapping =
|
||||||
|
serde_yaml::from_str(config_str).expect("Failed to parse test config YAML");
|
||||||
|
|
||||||
let seq = SeqMap {
|
let seq = SeqMap {
|
||||||
prepend: Sequence::new(),
|
prepend: Sequence::new(),
|
||||||
@@ -118,38 +121,51 @@ proxy-groups:
|
|||||||
config = use_seq(seq, config, "proxies");
|
config = use_seq(seq, config, "proxies");
|
||||||
|
|
||||||
// Check if proxy1 is removed from proxies
|
// Check if proxy1 is removed from proxies
|
||||||
let proxies = config.get("proxies").unwrap().as_sequence().unwrap();
|
let proxies = config
|
||||||
|
.get("proxies")
|
||||||
|
.expect("proxies field should exist")
|
||||||
|
.as_sequence()
|
||||||
|
.expect("proxies should be a sequence");
|
||||||
assert_eq!(proxies.len(), 1);
|
assert_eq!(proxies.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
proxies[0]
|
proxies[0]
|
||||||
.as_mapping()
|
.as_mapping()
|
||||||
.unwrap()
|
.expect("proxy should be a mapping")
|
||||||
.get("name")
|
.get("name")
|
||||||
.unwrap()
|
.expect("proxy should have name")
|
||||||
.as_str()
|
.as_str()
|
||||||
.unwrap(),
|
.expect("name should be string"),
|
||||||
"proxy2"
|
"proxy2"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check if proxy1 is removed from all groups
|
// Check if proxy1 is removed from all groups
|
||||||
let groups = config.get("proxy-groups").unwrap().as_sequence().unwrap();
|
let groups = config
|
||||||
|
.get("proxy-groups")
|
||||||
|
.expect("proxy-groups field should exist")
|
||||||
|
.as_sequence()
|
||||||
|
.expect("proxy-groups should be a sequence");
|
||||||
let group1_proxies = groups[0]
|
let group1_proxies = groups[0]
|
||||||
.as_mapping()
|
.as_mapping()
|
||||||
.unwrap()
|
.expect("group should be a mapping")
|
||||||
.get("proxies")
|
.get("proxies")
|
||||||
.unwrap()
|
.expect("group should have proxies")
|
||||||
.as_sequence()
|
.as_sequence()
|
||||||
.unwrap();
|
.expect("group proxies should be a sequence");
|
||||||
let group2_proxies = groups[1]
|
let group2_proxies = groups[1]
|
||||||
.as_mapping()
|
.as_mapping()
|
||||||
.unwrap()
|
.expect("group should be a mapping")
|
||||||
.get("proxies")
|
.get("proxies")
|
||||||
.unwrap()
|
.expect("group should have proxies")
|
||||||
.as_sequence()
|
.as_sequence()
|
||||||
.unwrap();
|
.expect("group proxies should be a sequence");
|
||||||
|
|
||||||
assert_eq!(group1_proxies.len(), 1);
|
assert_eq!(group1_proxies.len(), 1);
|
||||||
assert_eq!(group1_proxies[0].as_str().unwrap(), "proxy2");
|
assert_eq!(
|
||||||
|
group1_proxies[0]
|
||||||
|
.as_str()
|
||||||
|
.expect("proxy name should be string"),
|
||||||
|
"proxy2"
|
||||||
|
);
|
||||||
assert_eq!(group2_proxies.len(), 0);
|
assert_eq!(group2_proxies.len(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ macro_rules! append {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
|
pub fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
|
||||||
let tun_key = Value::from("tun");
|
let tun_key = Value::from("tun");
|
||||||
let tun_val = config.get(&tun_key);
|
let tun_val = config.get(&tun_key);
|
||||||
let mut tun_val = tun_val.map_or(Mapping::new(), |val| {
|
let mut tun_val = tun_val.map_or(Mapping::new(), |val| {
|
||||||
@@ -59,8 +59,10 @@ pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
|
|||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
crate::utils::resolve::restore_public_dns().await;
|
tokio::spawn(async {
|
||||||
crate::utils::resolve::set_public_dns("223.6.6.6".to_string()).await;
|
crate::utils::resolve::restore_public_dns().await;
|
||||||
|
crate::utils::resolve::set_public_dns("223.6.6.6".to_string()).await;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +71,9 @@ pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
|
|||||||
} else {
|
} else {
|
||||||
// TUN未启用时,仅恢复系统DNS,不修改配置文件中的DNS设置
|
// TUN未启用时,仅恢复系统DNS,不修改配置文件中的DNS设置
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
crate::utils::resolve::restore_public_dns().await;
|
tokio::spawn(async {
|
||||||
|
crate::utils::resolve::restore_public_dns().await;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新TUN配置
|
// 更新TUN配置
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ pub async fn restore_webdav_backup(filename: String) -> Result<()> {
|
|||||||
let webdav_username = verge_data.webdav_username.clone();
|
let webdav_username = verge_data.webdav_username.clone();
|
||||||
let webdav_password = verge_data.webdav_password.clone();
|
let webdav_password = verge_data.webdav_password.clone();
|
||||||
|
|
||||||
let backup_storage_path = app_home_dir().unwrap().join(&filename);
|
let backup_storage_path = app_home_dir()
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to get app home dir: {e}"))?
|
||||||
|
.join(&filename);
|
||||||
backup::WebDavClient::global()
|
backup::WebDavClient::global()
|
||||||
.download(filename, backup_storage_path.clone())
|
.download(filename, backup_storage_path.clone())
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::{
|
|||||||
config::Config,
|
config::Config,
|
||||||
core::{handle, tray, CoreManager},
|
core::{handle, tray, CoreManager},
|
||||||
ipc::IpcManager,
|
ipc::IpcManager,
|
||||||
logging_error,
|
logging, logging_error,
|
||||||
process::AsyncHandler,
|
process::AsyncHandler,
|
||||||
utils::{logging::Type, resolve},
|
utils::{logging::Type, resolve},
|
||||||
};
|
};
|
||||||
@@ -30,8 +30,11 @@ pub fn restart_app() {
|
|||||||
AsyncHandler::spawn(move || async move {
|
AsyncHandler::spawn(move || async move {
|
||||||
logging_error!(Type::Core, true, CoreManager::global().stop_core().await);
|
logging_error!(Type::Core, true, CoreManager::global().stop_core().await);
|
||||||
resolve::resolve_reset_async().await;
|
resolve::resolve_reset_async().await;
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let Some(app_handle) = handle::Handle::global().app_handle() else {
|
||||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
logging!(error, Type::Core, "Failed to get app handle for restart");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
tauri::process::restart(&app_handle.env());
|
tauri::process::restart(&app_handle.env());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> {
|
|||||||
let res = {
|
let res = {
|
||||||
// 激活订阅
|
// 激活订阅
|
||||||
if patch.get("secret").is_some() || patch.get("external-controller").is_some() {
|
if patch.get("secret").is_some() || patch.get("external-controller").is_some() {
|
||||||
Config::generate().await?;
|
Config::generate()?;
|
||||||
CoreManager::global().restart_core().await?;
|
CoreManager::global().restart_core().await?;
|
||||||
} else {
|
} else {
|
||||||
if patch.get("mode").is_some() {
|
if patch.get("mode").is_some() {
|
||||||
@@ -173,7 +173,7 @@ pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> {
|
|||||||
|
|
||||||
// Process updates based on flags
|
// Process updates based on flags
|
||||||
if (update_flags & (UpdateFlags::RestartCore as i32)) != 0 {
|
if (update_flags & (UpdateFlags::RestartCore as i32)) != 0 {
|
||||||
Config::generate().await?;
|
Config::generate()?;
|
||||||
CoreManager::global().restart_core().await?;
|
CoreManager::global().restart_core().await?;
|
||||||
}
|
}
|
||||||
if (update_flags & (UpdateFlags::ClashConfig as i32)) != 0 {
|
if (update_flags & (UpdateFlags::ClashConfig as i32)) != 0 {
|
||||||
@@ -191,7 +191,9 @@ pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> {
|
|||||||
sysopt::Sysopt::global().update_sysproxy().await?;
|
sysopt::Sysopt::global().update_sysproxy().await?;
|
||||||
}
|
}
|
||||||
if (update_flags & (UpdateFlags::Hotkey as i32)) != 0 {
|
if (update_flags & (UpdateFlags::Hotkey as i32)) != 0 {
|
||||||
hotkey::Hotkey::global().update(patch.hotkeys.unwrap())?;
|
if let Some(hotkeys) = patch.hotkeys {
|
||||||
|
hotkey::Hotkey::global().update(hotkeys)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (update_flags & (UpdateFlags::SystrayMenu as i32)) != 0 {
|
if (update_flags & (UpdateFlags::SystrayMenu as i32)) != 0 {
|
||||||
tray::Tray::global().update_menu()?;
|
tray::Tray::global().update_menu()?;
|
||||||
@@ -206,7 +208,7 @@ pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> {
|
|||||||
tray::Tray::global().update_click_behavior()?;
|
tray::Tray::global().update_click_behavior()?;
|
||||||
}
|
}
|
||||||
if (update_flags & (UpdateFlags::LighteWeight as i32)) != 0 {
|
if (update_flags & (UpdateFlags::LighteWeight as i32)) != 0 {
|
||||||
if enable_auto_light_weight.unwrap() {
|
if enable_auto_light_weight.unwrap_or(false) {
|
||||||
lightweight::enable_auto_light_weight_mode();
|
lightweight::enable_auto_light_weight_mode();
|
||||||
} else {
|
} else {
|
||||||
lightweight::disable_auto_light_weight_mode();
|
lightweight::disable_auto_light_weight_mode();
|
||||||
|
|||||||
@@ -11,7 +11,14 @@ use anyhow::{bail, Result};
|
|||||||
/// Toggle proxy profile
|
/// Toggle proxy profile
|
||||||
pub fn toggle_proxy_profile(profile_index: String) {
|
pub fn toggle_proxy_profile(profile_index: String) {
|
||||||
AsyncHandler::spawn(|| async move {
|
AsyncHandler::spawn(|| async move {
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let Some(app_handle) = handle::Handle::global().app_handle() else {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Config,
|
||||||
|
"Failed to get app handle for profile toggle"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
match cmd::patch_profiles_config_by_profile_index(app_handle, profile_index).await {
|
match cmd::patch_profiles_config_by_profile_index(app_handle, profile_index).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let _ = tray::Tray::global().update_menu();
|
let _ = tray::Tray::global().update_menu();
|
||||||
@@ -50,9 +57,14 @@ pub async fn update_profile(
|
|||||||
log::info!(target: "app",
|
log::info!(target: "app",
|
||||||
"[订阅更新] {} 是远程订阅,URL: {}",
|
"[订阅更新] {} 是远程订阅,URL: {}",
|
||||||
uid,
|
uid,
|
||||||
item.url.clone().unwrap()
|
item.url.clone().ok_or_else(|| anyhow::anyhow!("Profile URL is None"))?
|
||||||
);
|
);
|
||||||
Some((item.url.clone().unwrap(), item.option.clone()))
|
Some((
|
||||||
|
item.url
|
||||||
|
.clone()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Profile URL is None"))?,
|
||||||
|
item.option.clone(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -137,7 +149,13 @@ pub async fn update_profile(
|
|||||||
logging!(info, Type::Config, true, "[订阅更新] 更新成功");
|
logging!(info, Type::Config, true, "[订阅更新] 更新成功");
|
||||||
handle::Handle::refresh_clash();
|
handle::Handle::refresh_clash();
|
||||||
if let Err(err) = cmd::proxy::force_refresh_proxies().await {
|
if let Err(err) = cmd::proxy::force_refresh_proxies().await {
|
||||||
logging!(error, Type::Config, true, "[订阅更新] 代理组刷新失败: {}", err);
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Config,
|
||||||
|
true,
|
||||||
|
"[订阅更新] 代理组刷新失败: {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ use crate::{
|
|||||||
config::{Config, IVerge},
|
config::{Config, IVerge},
|
||||||
core::handle,
|
core::handle,
|
||||||
ipc::IpcManager,
|
ipc::IpcManager,
|
||||||
|
logging,
|
||||||
process::AsyncHandler,
|
process::AsyncHandler,
|
||||||
|
utils::logging::Type,
|
||||||
};
|
};
|
||||||
use std::env;
|
use std::env;
|
||||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||||
@@ -71,7 +73,14 @@ pub fn copy_clash_env() {
|
|||||||
.unwrap_or_else(|| "127.0.0.1".to_string())
|
.unwrap_or_else(|| "127.0.0.1".to_string())
|
||||||
});
|
});
|
||||||
|
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let Some(app_handle) = handle::Handle::global().app_handle() else {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::System,
|
||||||
|
"Failed to get app handle for proxy operation"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
let port = {
|
let port = {
|
||||||
Config::verge()
|
Config::verge()
|
||||||
.latest_ref()
|
.latest_ref()
|
||||||
|
|||||||
@@ -67,7 +67,14 @@ pub fn quit() {
|
|||||||
logging!(debug, Type::System, true, "启动退出流程");
|
logging!(debug, Type::System, true, "启动退出流程");
|
||||||
|
|
||||||
// 获取应用句柄并设置退出标志
|
// 获取应用句柄并设置退出标志
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let Some(app_handle) = handle::Handle::global().app_handle() else {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::System,
|
||||||
|
"Failed to get app handle for quit operation"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
handle::Handle::global().set_is_exiting();
|
handle::Handle::global().set_is_exiting();
|
||||||
|
|
||||||
// 优先关闭窗口,提供立即反馈
|
// 优先关闭窗口,提供立即反馈
|
||||||
|
|||||||
@@ -26,7 +26,16 @@ pub struct IpcManager {
|
|||||||
|
|
||||||
impl IpcManager {
|
impl IpcManager {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
let ipc_path_buf = ipc_path().unwrap();
|
let ipc_path_buf = ipc_path().unwrap_or_else(|e| {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
crate::utils::logging::Type::Ipc,
|
||||||
|
true,
|
||||||
|
"Failed to get IPC path: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
std::path::PathBuf::from("/tmp/clash-verge-ipc") // fallback path
|
||||||
|
});
|
||||||
let ipc_path = ipc_path_buf.to_str().unwrap_or_default();
|
let ipc_path = ipc_path_buf.to_str().unwrap_or_default();
|
||||||
Self {
|
Self {
|
||||||
ipc_path: ipc_path.to_string(),
|
ipc_path: ipc_path.to_string(),
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ impl LogItem {
|
|||||||
|
|
||||||
let now = SystemTime::now()
|
let now = SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
.unwrap()
|
.unwrap_or_else(|_| std::time::Duration::from_secs(0))
|
||||||
.as_secs();
|
.as_secs();
|
||||||
|
|
||||||
// Simple time formatting (HH:MM:SS)
|
// Simple time formatting (HH:MM:SS)
|
||||||
@@ -109,9 +109,8 @@ impl LogsMonitor {
|
|||||||
filter_level
|
filter_level
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
true // Level changed
|
|
||||||
}
|
}
|
||||||
|
true // Level changed
|
||||||
} else {
|
} else {
|
||||||
true // First time or was stopped
|
true // First time or was stopped
|
||||||
}
|
}
|
||||||
@@ -152,12 +151,12 @@ impl LogsMonitor {
|
|||||||
*current_level = Some(filter_level.clone());
|
*current_level = Some(filter_level.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let monitor_current = self.current.clone();
|
let monitor_current = Arc::clone(&self.current);
|
||||||
|
|
||||||
let task = tokio::spawn(async move {
|
let task = tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
// Get fresh IPC path and client for each connection attempt
|
// Get fresh IPC path and client for each connection attempt
|
||||||
let (_ipc_path_buf, client) = match Self::create_ipc_client().await {
|
let (_ipc_path_buf, client) = match Self::create_ipc_client() {
|
||||||
Ok((path, client)) => (path, client),
|
Ok((path, client)) => (path, client),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging!(error, Type::Ipc, true, "Failed to create IPC client: {}", e);
|
logging!(error, Type::Ipc, true, "Failed to create IPC client: {}", e);
|
||||||
@@ -183,7 +182,9 @@ impl LogsMonitor {
|
|||||||
let _ = client
|
let _ = client
|
||||||
.get(&url)
|
.get(&url)
|
||||||
.timeout(Duration::from_secs(30))
|
.timeout(Duration::from_secs(30))
|
||||||
.process_lines(|line| Self::process_log_line(line, monitor_current.clone()))
|
.process_lines(|line| {
|
||||||
|
Self::process_log_line(line, Arc::clone(&monitor_current))
|
||||||
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Wait before retrying
|
// Wait before retrying
|
||||||
@@ -228,7 +229,7 @@ impl LogsMonitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_ipc_client() -> Result<
|
fn create_ipc_client() -> Result<
|
||||||
(std::path::PathBuf, kode_bridge::IpcStreamClient),
|
(std::path::PathBuf, kode_bridge::IpcStreamClient),
|
||||||
Box<dyn std::error::Error + Send + Sync>,
|
Box<dyn std::error::Error + Send + Sync>,
|
||||||
> {
|
> {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ where
|
|||||||
freshness_duration: Duration,
|
freshness_duration: Duration,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let current = Arc::new(RwLock::new(T::default()));
|
let current = Arc::new(RwLock::new(T::default()));
|
||||||
let monitor_current = current.clone();
|
let monitor_current = Arc::clone(¤t);
|
||||||
let endpoint_clone = endpoint.clone();
|
let endpoint_clone = endpoint.clone();
|
||||||
|
|
||||||
// Start the monitoring task
|
// Start the monitoring task
|
||||||
@@ -110,7 +110,7 @@ where
|
|||||||
let _ = client
|
let _ = client
|
||||||
.get(&endpoint)
|
.get(&endpoint)
|
||||||
.timeout(timeout)
|
.timeout(timeout)
|
||||||
.process_lines(|line| T::parse_and_update(line, current.clone()))
|
.process_lines(|line| T::parse_and_update(line, Arc::clone(¤t)))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
tokio::time::sleep(retry_interval).await;
|
tokio::time::sleep(retry_interval).await;
|
||||||
|
|||||||
@@ -60,7 +60,16 @@ impl AppHandleManager {
|
|||||||
|
|
||||||
/// Get the app handle, panics if it hasn't been initialized.
|
/// Get the app handle, panics if it hasn't been initialized.
|
||||||
pub fn get_handle(&self) -> AppHandle {
|
pub fn get_handle(&self) -> AppHandle {
|
||||||
self.get().expect("AppHandle not initialized")
|
if let Some(handle) = self.get() {
|
||||||
|
handle
|
||||||
|
} else {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Setup,
|
||||||
|
"AppHandle not initialized - ensure init() was called first"
|
||||||
|
);
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the app handle has been initialized.
|
/// Check if the app handle has been initialized.
|
||||||
@@ -181,9 +190,7 @@ mod app_init {
|
|||||||
builder = builder.plugin(tauri_plugin_devtools::init());
|
builder = builder.plugin(tauri_plugin_devtools::init());
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.manage(std::sync::Mutex::new(
|
builder.manage(Mutex::new(state::lightweight::LightWeightState::default()))
|
||||||
state::lightweight::LightWeightState::default(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Setup deep link handling
|
/// Setup deep link handling
|
||||||
@@ -667,7 +674,16 @@ pub fn run() {
|
|||||||
// Build the application
|
// Build the application
|
||||||
let app = builder
|
let app = builder
|
||||||
.build(tauri::generate_context!())
|
.build(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.unwrap_or_else(|e| {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Setup,
|
||||||
|
true,
|
||||||
|
"Failed to build Tauri application: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
app.run(|app_handle, e| match e {
|
app.run(|app_handle, e| match e {
|
||||||
tauri::RunEvent::Ready | tauri::RunEvent::Resumed => {
|
tauri::RunEvent::Ready | tauri::RunEvent::Resumed => {
|
||||||
|
|||||||
@@ -124,7 +124,10 @@ pub fn set_lightweight_mode(value: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable_auto_light_weight_mode() {
|
pub fn enable_auto_light_weight_mode() {
|
||||||
Timer::global().init().unwrap();
|
if let Err(e) = Timer::global().init() {
|
||||||
|
logging!(error, Type::Lightweight, "Failed to initialize timer: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
logging!(info, Type::Lightweight, true, "开启自动轻量模式");
|
logging!(info, Type::Lightweight, true, "开启自动轻量模式");
|
||||||
setup_window_close_listener();
|
setup_window_close_listener();
|
||||||
setup_webview_focus_listener();
|
setup_webview_focus_listener();
|
||||||
|
|||||||
@@ -32,7 +32,17 @@ impl PlatformSpecification {
|
|||||||
let system_kernel_version = System::kernel_version().unwrap_or("Null".into());
|
let system_kernel_version = System::kernel_version().unwrap_or("Null".into());
|
||||||
let system_arch = System::cpu_arch();
|
let system_arch = System::cpu_arch();
|
||||||
|
|
||||||
let handler = handle::Handle::global().app_handle().unwrap();
|
let Some(handler) = handle::Handle::global().app_handle() else {
|
||||||
|
return Self {
|
||||||
|
system_name,
|
||||||
|
system_version,
|
||||||
|
system_kernel_version,
|
||||||
|
system_arch,
|
||||||
|
verge_version: "unknown".into(),
|
||||||
|
running_mode: "NotRunning".to_string(),
|
||||||
|
is_admin: false,
|
||||||
|
};
|
||||||
|
};
|
||||||
let verge_version = handler.package_info().version.to_string();
|
let verge_version = handler.package_info().version.to_string();
|
||||||
|
|
||||||
// 使用默认值避免在同步上下文中执行异步操作
|
// 使用默认值避免在同步上下文中执行异步操作
|
||||||
@@ -52,10 +62,10 @@ impl PlatformSpecification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 异步方法来获取完整的系统信息
|
// 异步方法来获取完整的系统信息
|
||||||
pub async fn new_async() -> Self {
|
pub fn new_sync() -> Self {
|
||||||
let mut info = Self::new();
|
let mut info = Self::new();
|
||||||
|
|
||||||
let running_mode = CoreManager::global().get_running_mode().await;
|
let running_mode = CoreManager::global().get_running_mode();
|
||||||
info.running_mode = running_mode.to_string();
|
info.running_mode = running_mode.to_string();
|
||||||
|
|
||||||
info
|
info
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ impl ProxyRequestCache {
|
|||||||
self.map
|
self.map
|
||||||
.remove_if(&key_cloned, |_, v| Arc::ptr_eq(v, &cell));
|
.remove_if(&key_cloned, |_, v| Arc::ptr_eq(v, &cell));
|
||||||
let new_cell = Arc::new(OnceCell::new());
|
let new_cell = Arc::new(OnceCell::new());
|
||||||
self.map.insert(key_cloned.clone(), new_cell.clone());
|
self.map.insert(key_cloned.clone(), Arc::clone(&new_cell));
|
||||||
return Box::pin(self.get_or_fetch(key_cloned, ttl, fetch_fn)).await;
|
return Box::pin(self.get_or_fetch(key_cloned, ttl, fetch_fn)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,13 @@ impl ProxyRequestCache {
|
|||||||
expires_at: Instant::now() + ttl,
|
expires_at: Instant::now() + ttl,
|
||||||
};
|
};
|
||||||
let _ = cell.set(entry);
|
let _ = cell.set(entry);
|
||||||
Arc::clone(&cell.get().unwrap().value)
|
// Safe to unwrap here as we just set the value
|
||||||
|
Arc::clone(
|
||||||
|
&cell
|
||||||
|
.get()
|
||||||
|
.unwrap_or_else(|| unreachable!("Cell value should exist after set"))
|
||||||
|
.value,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -163,8 +163,13 @@ pub fn find_target_icons(target: &str) -> Result<Option<String>> {
|
|||||||
if matching_files.is_empty() {
|
if matching_files.is_empty() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
let first = path_to_str(matching_files.first().unwrap())?;
|
match matching_files.first() {
|
||||||
Ok(Some(first.to_string()))
|
Some(first_path) => {
|
||||||
|
let first = path_to_str(first_path)?;
|
||||||
|
Ok(Some(first.to_string()))
|
||||||
|
}
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -401,7 +401,14 @@ pub fn init_scheme() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn startup_script() -> Result<()> {
|
pub async fn startup_script() -> Result<()> {
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = match handle::Handle::global().app_handle() {
|
||||||
|
Some(handle) => handle,
|
||||||
|
None => {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"app_handle not available for startup script execution"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let script_path = {
|
let script_path = {
|
||||||
let verge = Config::verge();
|
let verge = Config::verge();
|
||||||
|
|||||||
@@ -40,13 +40,37 @@ singleton_lazy!(NetworkManager, NETWORK_MANAGER, NetworkManager::new);
|
|||||||
impl NetworkManager {
|
impl NetworkManager {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
// 创建专用的异步运行时,线程数限制为4个
|
// 创建专用的异步运行时,线程数限制为4个
|
||||||
let runtime = Builder::new_multi_thread()
|
let runtime = match Builder::new_multi_thread()
|
||||||
.worker_threads(4)
|
.worker_threads(4)
|
||||||
.thread_name("clash-verge-network")
|
.thread_name("clash-verge-network")
|
||||||
.enable_io()
|
.enable_io()
|
||||||
.enable_time()
|
.enable_time()
|
||||||
.build()
|
.build()
|
||||||
.expect("Failed to create network runtime");
|
{
|
||||||
|
Ok(runtime) => runtime,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"Failed to create network runtime: {}. Using fallback single-threaded runtime.",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
// Fallback to current thread runtime
|
||||||
|
match Builder::new_current_thread()
|
||||||
|
.enable_io()
|
||||||
|
.enable_time()
|
||||||
|
.thread_name("clash-verge-network-fallback")
|
||||||
|
.build()
|
||||||
|
{
|
||||||
|
Ok(fallback_runtime) => fallback_runtime,
|
||||||
|
Err(fallback_err) => {
|
||||||
|
log::error!(
|
||||||
|
"Failed to create fallback runtime: {}. This is critical.",
|
||||||
|
fallback_err
|
||||||
|
);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
NetworkManager {
|
NetworkManager {
|
||||||
runtime: Arc::new(runtime),
|
runtime: Arc::new(runtime),
|
||||||
@@ -66,7 +90,7 @@ impl NetworkManager {
|
|||||||
logging!(info, Type::Network, true, "初始化网络管理器");
|
logging!(info, Type::Network, true, "初始化网络管理器");
|
||||||
|
|
||||||
// 创建无代理客户端
|
// 创建无代理客户端
|
||||||
let no_proxy_client = ClientBuilder::new()
|
let no_proxy_client = match ClientBuilder::new()
|
||||||
.use_rustls_tls()
|
.use_rustls_tls()
|
||||||
.no_proxy()
|
.no_proxy()
|
||||||
.pool_max_idle_per_host(POOL_MAX_IDLE_PER_HOST)
|
.pool_max_idle_per_host(POOL_MAX_IDLE_PER_HOST)
|
||||||
@@ -74,7 +98,19 @@ impl NetworkManager {
|
|||||||
.connect_timeout(Duration::from_secs(10))
|
.connect_timeout(Duration::from_secs(10))
|
||||||
.timeout(Duration::from_secs(30))
|
.timeout(Duration::from_secs(30))
|
||||||
.build()
|
.build()
|
||||||
.expect("Failed to build no_proxy client");
|
{
|
||||||
|
Ok(client) => client,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
true,
|
||||||
|
"Failed to build no_proxy client: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut no_proxy_guard = NetworkManager::global().no_proxy_client.lock();
|
let mut no_proxy_guard = NetworkManager::global().no_proxy_client.lock();
|
||||||
*no_proxy_guard = Some(no_proxy_client);
|
*no_proxy_guard = Some(no_proxy_client);
|
||||||
@@ -214,7 +250,45 @@ impl NetworkManager {
|
|||||||
builder = builder.user_agent(version);
|
builder = builder.user_agent(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = builder.build().expect("Failed to build custom HTTP client");
|
let client = match builder.build() {
|
||||||
|
Ok(client) => client,
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
true,
|
||||||
|
"Failed to build custom HTTP client: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
// Return a simple no-proxy client as fallback
|
||||||
|
match ClientBuilder::new()
|
||||||
|
.use_rustls_tls()
|
||||||
|
.no_proxy()
|
||||||
|
.timeout(DEFAULT_REQUEST_TIMEOUT)
|
||||||
|
.build()
|
||||||
|
{
|
||||||
|
Ok(fallback_client) => fallback_client,
|
||||||
|
Err(fallback_err) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Network,
|
||||||
|
true,
|
||||||
|
"Failed to create fallback client: {}",
|
||||||
|
fallback_err
|
||||||
|
);
|
||||||
|
self.record_connection_error(&format!(
|
||||||
|
"Critical client build failure: {}",
|
||||||
|
fallback_err
|
||||||
|
));
|
||||||
|
// Return a minimal client that will likely fail but won't panic
|
||||||
|
ClientBuilder::new().build().unwrap_or_else(|_| {
|
||||||
|
// If even the most basic client fails, this is truly critical
|
||||||
|
std::process::exit(1);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
client.get(url)
|
client.get(url)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -344,8 +344,21 @@ pub fn create_window(is_show: bool) -> bool {
|
|||||||
logging!(debug, Type::Window, true, "[ScopeGuard] 窗口创建状态已重置");
|
logging!(debug, Type::Window, true, "[ScopeGuard] 窗口创建状态已重置");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let app_handle = match handle::Handle::global().app_handle() {
|
||||||
|
Some(handle) => handle,
|
||||||
|
None => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Window,
|
||||||
|
true,
|
||||||
|
"无法获取app_handle,窗口创建失败"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match tauri::WebviewWindowBuilder::new(
|
match tauri::WebviewWindowBuilder::new(
|
||||||
&handle::Handle::global().app_handle().unwrap(),
|
&app_handle,
|
||||||
"main", /* the unique window label */
|
"main", /* the unique window label */
|
||||||
tauri::WebviewUrl::App("index.html".into()),
|
tauri::WebviewUrl::App("index.html".into()),
|
||||||
)
|
)
|
||||||
@@ -544,13 +557,13 @@ pub fn create_window(is_show: bool) -> bool {
|
|||||||
logging!(info, Type::Window, true, "UI已完全加载就绪");
|
logging!(info, Type::Window, true, "UI已完全加载就绪");
|
||||||
handle::Handle::global()
|
handle::Handle::global()
|
||||||
.get_window()
|
.get_window()
|
||||||
.map(|window| window.eval(r#"
|
.map(|window| window.eval(r"
|
||||||
const overlay = document.getElementById('initial-loading-overlay');
|
const overlay = document.getElementById('initial-loading-overlay');
|
||||||
if (overlay) {
|
if (overlay) {
|
||||||
overlay.style.opacity = '0';
|
overlay.style.opacity = '0';
|
||||||
setTimeout(() => overlay.remove(), 300);
|
setTimeout(() => overlay.remove(), 300);
|
||||||
}
|
}
|
||||||
"#));
|
"));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging!(
|
logging!(
|
||||||
@@ -647,7 +660,17 @@ pub async fn resolve_scheme(param: String) -> Result<()> {
|
|||||||
create_window(false);
|
create_window(false);
|
||||||
match PrfItem::from_url(url.as_ref(), name, None, None).await {
|
match PrfItem::from_url(url.as_ref(), name, None, None).await {
|
||||||
Ok(item) => {
|
Ok(item) => {
|
||||||
let uid = item.uid.clone().unwrap();
|
let uid = match item.uid.clone() {
|
||||||
|
Some(uid) => uid,
|
||||||
|
None => {
|
||||||
|
logging!(error, Type::Config, true, "Profile item missing UID");
|
||||||
|
handle::Handle::notice_message(
|
||||||
|
"import_sub_url::error",
|
||||||
|
"Profile item missing UID".to_string(),
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
let _ = wrap_err!(Config::profiles().data_mut().append_item(item));
|
let _ = wrap_err!(Config::profiles().data_mut().append_item(item));
|
||||||
handle::Handle::notice_message("import_sub_url::ok", uid);
|
handle::Handle::notice_message("import_sub_url::ok", uid);
|
||||||
}
|
}
|
||||||
@@ -725,10 +748,22 @@ async fn resolve_random_port_config() -> Result<()> {
|
|||||||
pub async fn set_public_dns(dns_server: String) {
|
pub async fn set_public_dns(dns_server: String) {
|
||||||
use crate::{core::handle, utils::dirs};
|
use crate::{core::handle, utils::dirs};
|
||||||
use tauri_plugin_shell::ShellExt;
|
use tauri_plugin_shell::ShellExt;
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = match handle::Handle::global().app_handle() {
|
||||||
|
Some(handle) => handle,
|
||||||
|
None => {
|
||||||
|
log::error!(target: "app", "app_handle not available for DNS configuration");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
log::info!(target: "app", "try to set system dns");
|
log::info!(target: "app", "try to set system dns");
|
||||||
let resource_dir = dirs::app_resources_dir().unwrap();
|
let resource_dir = match dirs::app_resources_dir() {
|
||||||
|
Ok(dir) => dir,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(target: "app", "Failed to get resource directory: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
let script = resource_dir.join("set_dns.sh");
|
let script = resource_dir.join("set_dns.sh");
|
||||||
if !script.exists() {
|
if !script.exists() {
|
||||||
log::error!(target: "app", "set_dns.sh not found");
|
log::error!(target: "app", "set_dns.sh not found");
|
||||||
@@ -761,9 +796,21 @@ pub async fn set_public_dns(dns_server: String) {
|
|||||||
pub async fn restore_public_dns() {
|
pub async fn restore_public_dns() {
|
||||||
use crate::{core::handle, utils::dirs};
|
use crate::{core::handle, utils::dirs};
|
||||||
use tauri_plugin_shell::ShellExt;
|
use tauri_plugin_shell::ShellExt;
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = match handle::Handle::global().app_handle() {
|
||||||
|
Some(handle) => handle,
|
||||||
|
None => {
|
||||||
|
log::error!(target: "app", "app_handle not available for DNS restoration");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
log::info!(target: "app", "try to unset system dns");
|
log::info!(target: "app", "try to unset system dns");
|
||||||
let resource_dir = dirs::app_resources_dir().unwrap();
|
let resource_dir = match dirs::app_resources_dir() {
|
||||||
|
Ok(dir) => dir,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(target: "app", "Failed to get resource directory: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
let script = resource_dir.join("unset_dns.sh");
|
let script = resource_dir.join("unset_dns.sh");
|
||||||
if !script.exists() {
|
if !script.exists() {
|
||||||
log::error!(target: "app", "unset_dns.sh not found");
|
log::error!(target: "app", "unset_dns.sh not found");
|
||||||
|
|||||||
@@ -38,9 +38,8 @@ pub async fn check_singleton() -> Result<()> {
|
|||||||
}
|
}
|
||||||
log::error!("failed to setup singleton listen server");
|
log::error!("failed to setup singleton listen server");
|
||||||
bail!("app exists");
|
bail!("app exists");
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The embed server only be used to implement singleton process
|
/// The embed server only be used to implement singleton process
|
||||||
|
|||||||
Reference in New Issue
Block a user