chore: 官网拆分 homepage + 关于页瘦身 + 端口 5175(当前可用状态基线)

This commit is contained in:
Logan.Li committed 2026-10-14 20:31:51 +08:00
1 parent 5eb11826db
commit be151cb68f
85 files changed
+4420 -1530

No files matched your search

+151 -267
View File
@@ -110,15 +110,15 @@ dependencies = [
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
checksum = "c2fb6cfd47bf496ff64095c20eaba0c201404ee38714d4142fcfa1dc334fcc7a"
[[package]]
name = "alloc-stdlib"
version = "0.2.4"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195"
checksum = "0b5c1865780388bfa186411ab5f247819487fc4864c6e9c3106611fa347586e1"
dependencies = [
"alloc-no-stdlib",
]
@@ -482,6 +482,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
[[package]]
name = "base64ct"
version = "1.8.3"
@@ -587,9 +593,9 @@ dependencies = [
[[package]]
name = "brotli"
version = "8.0.4"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3"
checksum = "f8b851b75c23ca7873623d612fe49bd1989aeb03d08fb9432187eb253d3d4c6b"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@@ -598,9 +604,9 @@ dependencies = [
[[package]]
name = "brotli-decompressor"
version = "5.0.3"
version = "6.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583"
checksum = "941cd9bd4ddab83cb46fa5a2d428f1c857b24ac78cb876cf7beb710840934bd7"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@@ -707,12 +713,13 @@ dependencies = [
[[package]]
name = "cargo_toml"
version = "0.22.3"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77"
checksum = "82f4b26e751e711a5302649417f2da046dce6391b2ea30a4820f37462314f0b9"
dependencies = [
"semver",
"serde",
"toml 0.9.12+spec-1.1.0",
"toml 1.1.4+spec-1.1.0",
]
[[package]]
@@ -744,13 +751,13 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfb"
version = "0.7.3"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f"
checksum = "a347dcabdae9c31b0825fd6a8bed285ec9c2acb89c47827126d52fa4f59cece3"
dependencies = [
"byteorder",
"fnv",
"uuid",
"web-time",
]
[[package]]
@@ -1093,9 +1100,9 @@ dependencies = [
[[package]]
name = "cssparser"
version = "0.36.0"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2"
checksum = "8c9cdaae01d5ed7882b04d795e7f752f46ff52d2fa3b50a20d28c464510bba98"
dependencies = [
"cssparser-macros",
"dtoa-short",
@@ -1106,29 +1113,19 @@ dependencies = [
[[package]]
name = "cssparser-macros"
version = "0.6.1"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
checksum = "d045de693cb712d0b22c6a64be5b953f67b3ce00ab5ad3dd5d8b441886ab8e1a"
dependencies = [
"quote",
"syn 2.0.119",
"syn 3.0.3",
]
[[package]]
name = "ctor"
version = "0.8.0"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98"
dependencies = [
"ctor-proc-macro",
"dtor",
]
[[package]]
name = "ctor-proc-macro"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1"
checksum = "914a755b7c2d4af2bdcff7ce1739e2db9a1b81a9b07123d8015786ae03c0980d"
[[package]]
name = "ctr"
@@ -1364,6 +1361,15 @@ dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d57d423b3c82e89b9a24ca3091fee61f456a26edbd28d26c65906f4bc1dcd8f"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.5.0"
@@ -1442,9 +1448,9 @@ dependencies = [
[[package]]
name = "dom_query"
version = "0.27.0"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89"
checksum = "fac5fca71e65e94cc718a6e2af65d6e0f9c6027751c2aa562fbb5087fda639bc"
dependencies = [
"bit-set",
"cssparser",
@@ -1485,21 +1491,6 @@ dependencies = [
"dtoa",
]
[[package]]
name = "dtor"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4"
dependencies = [
"dtor-proc-macro",
]
[[package]]
name = "dtor-proc-macro"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5"
[[package]]
name = "dunce"
version = "1.0.5"
@@ -2369,9 +2360,9 @@ dependencies = [
[[package]]
name = "html5ever"
version = "0.38.0"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2"
checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8"
dependencies = [
"log",
"markup5ever",
@@ -2498,7 +2489,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
"windows-core 0.62.2",
"windows-core",
]
[[package]]
@@ -2668,9 +2659,9 @@ dependencies = [
[[package]]
name = "infer"
version = "0.19.0"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7"
checksum = "f4200d433cbd5178df7797c9c2e75b348b728e39631cf14520d1e2fc424201f4"
dependencies = [
"cfb",
]
@@ -3060,21 +3051,21 @@ dependencies = [
[[package]]
name = "json-patch"
version = "3.0.1"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08"
checksum = "7421438de105a0827e44fadd05377727847d717c80ce29a229f85fd04c427b72"
dependencies = [
"jsonptr",
"serde",
"serde_json",
"thiserror 1.0.69",
"thiserror 2.0.19",
]
[[package]]
name = "jsonptr"
version = "0.6.3"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70"
checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe"
dependencies = [
"serde",
"serde_json",
@@ -3092,13 +3083,12 @@ dependencies = [
[[package]]
name = "keyboard-types"
version = "0.7.0"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a"
checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d"
dependencies = [
"bitflags 2.13.1",
"serde",
"unicode-segmentation",
]
[[package]]
@@ -3265,9 +3255,9 @@ dependencies = [
[[package]]
name = "markup5ever"
version = "0.38.0"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862"
checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de"
dependencies = [
"log",
"tendril",
@@ -3372,9 +3362,9 @@ dependencies = [
[[package]]
name = "muda"
version = "0.19.3"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878"
checksum = "cca139e57da4383727f189e43e66e84fa372347027fc996f9f1a007ec0a37996"
dependencies = [
"crossbeam-channel",
"dpi",
@@ -3406,6 +3396,12 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "ndk-context"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
[[package]]
name = "ndk-sys"
version = "0.6.0+11769913"
@@ -3563,6 +3559,7 @@ dependencies = [
"objc2-core-foundation",
"objc2-core-graphics",
"objc2-foundation",
"objc2-quartz-core",
]
[[package]]
@@ -3764,6 +3761,7 @@ version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0b3d059e795d52b8a72fef45658620edd4d9c359b338564aa14391ffa511ed5"
dependencies = [
"dunce",
"is-wsl",
"libc",
]
@@ -3804,7 +3802,7 @@ dependencies = [
"anyhow",
"arboard",
"base64 0.22.1",
"dirs",
"dirs 6.0.0",
"ed25519-dalek 2.2.0",
"hex",
"hmac 0.12.1",
@@ -3828,6 +3826,7 @@ dependencies = [
"tauri",
"tauri-build",
"tauri-plugin-deep-link",
"tauri-plugin-opener",
"tauri-plugin-single-instance",
"thiserror 2.0.19",
"tokio",
@@ -5139,9 +5138,9 @@ dependencies = [
[[package]]
name = "selectors"
version = "0.36.1"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c"
checksum = "8adfa1c298912827b8a28b223b3b874357397ae706e6190acd9bf28cee99114d"
dependencies = [
"bitflags 2.13.1",
"cssparser",
@@ -5684,7 +5683,7 @@ dependencies = [
"url",
"uuid",
"widestring",
"windows 0.62.2",
"windows",
"windows-registry 0.6.1",
"winscard",
"zeroize",
@@ -5734,9 +5733,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "swift-rs"
version = "1.0.7"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7"
checksum = "cefd87076fd385308ee4aff597256c902f1fbb4d031de1558efc12f8ffefadd9"
dependencies = [
"base64 0.21.7",
"serde",
@@ -5811,9 +5810,9 @@ dependencies = [
[[package]]
name = "tao"
version = "0.35.3"
version = "0.37.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9"
checksum = "f37f381f4e048e6cdf038b5705f8cf14ad108279d46eb968140a7b291aba9400"
dependencies = [
"bitflags 2.13.1",
"block2",
@@ -5831,20 +5830,20 @@ dependencies = [
"libc",
"log",
"ndk",
"ndk-context",
"ndk-sys",
"objc2",
"objc2-app-kit",
"objc2-foundation",
"objc2-ui-kit",
"once_cell",
"parking_lot",
"percent-encoding",
"raw-window-handle",
"tao-macros",
"unicode-segmentation",
"url",
"windows 0.61.3",
"windows-core 0.61.2",
"windows",
"windows-core",
"windows-version",
"x11-dl",
]
@@ -5874,14 +5873,14 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tauri"
version = "2.11.5"
version = "2.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "667b20e2726d572dea2de7370da16e188eb06008faf9a92fab7cdc46791190b5"
checksum = "c404ecf956ff241bf6b39dd688614724e1bf1f63255abb4fc46d1c40371672ea"
dependencies = [
"anyhow",
"bytes",
"cookie",
"dirs",
"dirs 7.0.0",
"dunce",
"embed_plist",
"getrandom 0.3.4",
@@ -5920,18 +5919,18 @@ dependencies = [
"webkit2gtk",
"webview2-com",
"window-vibrancy",
"windows 0.61.3",
"windows",
]
[[package]]
name = "tauri-build"
version = "2.6.3"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632"
checksum = "2eea0b6302f82c574b21af6fc6ae6e86183b9280853c22621d0abf0936911ffc"
dependencies = [
"anyhow",
"cargo_toml",
"dirs",
"dirs 7.0.0",
"glob",
"heck 0.5.0",
"json-patch",
@@ -5946,16 +5945,16 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.6.3"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5"
checksum = "02f468f9a9bcd404f57e5c191d91024e3de953d4b610f78c2907e7110615cb1c"
dependencies = [
"base64 0.22.1",
"base64 0.23.1",
"brotli",
"ico",
"json-patch",
"plist",
"png 0.17.16",
"png 0.18.1",
"proc-macro2",
"quote",
"semver",
@@ -5973,9 +5972,9 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.6.3"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45"
checksum = "131bffec939b20642380317f8403a33c8888489925e14bb965ceaff0d949b1cb"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@@ -5987,9 +5986,9 @@ dependencies = [
[[package]]
name = "tauri-plugin"
version = "2.6.3"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74be5dd4bed9afbd145e5716b5fa2ec28cbc29c34ffa61c258c9273d896c8020"
checksum = "1140cf34a3b3b836a13103dcab17f18831d5cc3534cbd435dc01a5c6daa65aa2"
dependencies = [
"anyhow",
"glob",
@@ -6022,6 +6021,28 @@ dependencies = [
"windows-result 0.3.4",
]
[[package]]
name = "tauri-plugin-opener"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85dead674ee41937d042ee855d785f07ee67206f19402200c12b9255964d5ac8"
dependencies = [
"dunce",
"glob",
"objc2-app-kit",
"objc2-foundation",
"open",
"schemars 0.8.22",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"thiserror 2.0.19",
"url",
"windows",
"zbus",
]
[[package]]
name = "tauri-plugin-single-instance"
version = "2.4.3"
@@ -6040,9 +6061,9 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.11.3"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8"
checksum = "c2a57a2a9b8c8b7fc00d35b510fe808ded7f9f1b44fc1cdda6aee38632ea5413"
dependencies = [
"cookie",
"dpi",
@@ -6060,14 +6081,14 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
"windows 0.61.3",
"windows",
]
[[package]]
name = "tauri-runtime-wry"
version = "2.11.4"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f"
checksum = "7f9e7e0c9f80a5130773cfabb6df4dc924518fb1b2c923726966804634b57d22"
dependencies = [
"gtk",
"http",
@@ -6085,15 +6106,15 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
"windows 0.61.3",
"windows",
"wry",
]
[[package]]
name = "tauri-utils"
version = "2.9.3"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887"
checksum = "2ff55a614843b9a3f010f211df637c94df2607175b8d0f6e0f4a7c19e18621ee"
dependencies = [
"anyhow",
"brotli",
@@ -6370,21 +6391,6 @@ dependencies = [
"toml_edit 0.20.2",
]
[[package]]
name = "toml"
version = "0.9.12+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
dependencies = [
"indexmap 2.14.0",
"serde_core",
"serde_spanned 1.1.1",
"toml_datetime 0.7.5+spec-1.1.0",
"toml_parser",
"toml_writer",
"winnow 0.7.15",
]
[[package]]
name = "toml"
version = "1.1.4+spec-1.1.0"
@@ -6409,15 +6415,6 @@ dependencies = [
"serde",
]
[[package]]
name = "toml_datetime"
version = "0.7.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_datetime"
version = "1.1.1+spec-1.1.0"
@@ -6587,12 +6584,12 @@ dependencies = [
[[package]]
name = "tray-icon"
version = "0.24.2"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "045979e3f037cd18ad1cb2a419dfda133c5c29c9f3453370079f2255d46c257e"
checksum = "b2b9c52859a94554803ccd4a24b98f74148ebc73b90676d783f3490b1bff9d72"
dependencies = [
"crossbeam-channel",
"dirs",
"dirs 7.0.0",
"libappindicator",
"muda",
"objc2",
@@ -6645,47 +6642,6 @@ dependencies = [
"thiserror 2.0.19",
]
[[package]]
name = "unic-char-property"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
dependencies = [
"unic-char-range",
]
[[package]]
name = "unic-char-range"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
[[package]]
name = "unic-common"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
[[package]]
name = "unic-ucd-ident"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987"
dependencies = [
"unic-char-property",
"unic-char-range",
"unic-ucd-version",
]
[[package]]
name = "unic-ucd-version"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
dependencies = [
"unic-common",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
@@ -6745,13 +6701,13 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "urlpattern"
version = "0.3.0"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d"
checksum = "df16f50ef4cc145211879a3867ba757076b25dfee812040dcb0658bd9ae7904b"
dependencies = [
"icu_properties",
"regex",
"serde",
"unic-ucd-ident",
"url",
]
@@ -7083,16 +7039,14 @@ dependencies = [
[[package]]
name = "webview2-com"
version = "0.38.2"
version = "0.39.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a"
checksum = "3f89fca7a704cee10dcb3654c1dbb8941d1783132f1917358af75bec37a7d7e6"
dependencies = [
"webview2-com-macros",
"webview2-com-sys",
"windows 0.61.3",
"windows-core 0.61.2",
"windows-implement",
"windows-interface",
"windows",
"windows-core",
]
[[package]]
@@ -7108,13 +7062,13 @@ dependencies = [
[[package]]
name = "webview2-com-sys"
version = "0.38.2"
version = "0.39.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
checksum = "b3a07132775117d6065853d9d1178157b8c90e228de47129d6bce2c7edebedfb"
dependencies = [
"thiserror 2.0.19",
"windows 0.61.3",
"windows-core 0.61.2",
"windows",
"windows-core",
]
[[package]]
@@ -7156,51 +7110,30 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "window-vibrancy"
version = "0.6.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c"
checksum = "111e51caca442cafd9bab396628ac4d814880eaea4e63dfd76a12e9f342b5580"
dependencies = [
"objc2",
"objc2-app-kit",
"objc2-core-foundation",
"objc2-foundation",
"objc2-quartz-core",
"raw-window-handle",
"windows-sys 0.59.0",
"windows-sys 0.61.2",
"windows-version",
]
[[package]]
name = "windows"
version = "0.61.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
dependencies = [
"windows-collections 0.2.0",
"windows-core 0.61.2",
"windows-future 0.2.1",
"windows-link 0.1.3",
"windows-numerics 0.2.0",
]
[[package]]
name = "windows"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
dependencies = [
"windows-collections 0.3.2",
"windows-core 0.62.2",
"windows-future 0.3.2",
"windows-numerics 0.3.1",
]
[[package]]
name = "windows-collections"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core 0.61.2",
"windows-collections",
"windows-core",
"windows-future",
"windows-numerics",
]
[[package]]
@@ -7209,20 +7142,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
dependencies = [
"windows-core 0.62.2",
]
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link 0.1.3",
"windows-result 0.3.4",
"windows-strings 0.4.2",
"windows-core",
]
[[package]]
@@ -7238,26 +7158,15 @@ dependencies = [
"windows-strings 0.5.1",
]
[[package]]
name = "windows-future"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
dependencies = [
"windows-core 0.61.2",
"windows-link 0.1.3",
"windows-threading 0.1.0",
]
[[package]]
name = "windows-future"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
dependencies = [
"windows-core 0.62.2",
"windows-core",
"windows-link 0.2.1",
"windows-threading 0.2.1",
"windows-threading",
]
[[package]]
@@ -7294,23 +7203,13 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-numerics"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core 0.61.2",
"windows-link 0.1.3",
]
[[package]]
name = "windows-numerics"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
dependencies = [
"windows-core 0.62.2",
"windows-core",
"windows-link 0.2.1",
]
@@ -7465,15 +7364,6 @@ dependencies = [
"windows_x86_64_msvc 0.53.1",
]
[[package]]
name = "windows-threading"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
dependencies = [
"windows-link 0.1.3",
]
[[package]]
name = "windows-threading"
version = "0.2.1"
@@ -7639,12 +7529,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
[[package]]
name = "winnow"
version = "1.0.4"
@@ -7710,15 +7594,15 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "wry"
version = "0.55.1"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514"
checksum = "a819957a01b3119af85e638a38d242af76dbc87d130dca67bfd0441072e21ff0"
dependencies = [
"base64 0.22.1",
"block2",
"cookie",
"crossbeam-channel",
"dirs",
"dirs 7.0.0",
"dom_query",
"dpi",
"dunce",
@@ -7746,8 +7630,8 @@ dependencies = [
"webkit2gtk",
"webkit2gtk-sys",
"webview2-com",
"windows 0.61.3",
"windows-core 0.61.2",
"windows",
"windows-core",
"windows-version",
"x11-dl",
]
+8 -2
View File
@@ -1,15 +1,20 @@
[package]
name = "opsxshell"
version = "2.1.1"
description = "OpsXShell native remote operations runtime"
authors = ["OpsXShell Team"]
description = "OpsxShell native remote operations runtime"
authors = ["OpsxShell Team"]
edition = "2021"
rust-version = "1.89"
default-run = "OpsxShell"
[lib]
name = "opsxshell_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[[bin]]
name = "OpsxShell"
path = "src/main.rs"
[build-dependencies]
tauri-build = { version = "2.4", features = [] }
@@ -52,6 +57,7 @@ rustls = { version = "0.23", default-features = false, features = ["std", "tls12
tokio-rustls = "0.26"
x509-cert = { version = "0.2", default-features = false, features = ["std"] }
vnc-rs = "0.5"
tauri-plugin-opener = "2.5.5"
[profile.release]
codegen-units = 1
+2 -1
View File
@@ -11,6 +11,7 @@
"core:event:default",
"core:window:default",
"core:window:allow-start-dragging",
"core:webview:allow-internal-toggle-devtools"
"core:webview:allow-internal-toggle-devtools",
"opener:default"
]
}
File diff suppressed because it is too large. Load diff
+19 -1
View File
@@ -1 +1,19 @@
{"desktop-default":{"identifier":"desktop-default","description":"Minimum capabilities for the OpsXShell main window","remote":{"urls":["http://127.0.0.1:1420","http://127.0.0.1:1420/*","http://localhost:1420","http://localhost:1420/*"]},"local":true,"windows":["main"],"permissions":["core:default","core:event:default","core:window:default","core:window:allow-start-dragging","core:webview:allow-internal-toggle-devtools"]}}
{
"desktop-default": {
"identifier": "desktop-default",
"description": "Minimum capabilities for the OpsXShell main window",
"remote": {
"urls": ["http://127.0.0.1:1420", "http://127.0.0.1:1420/*", "http://localhost:1420", "http://localhost:1420/*"]
},
"local": true,
"windows": ["main"],
"permissions": [
"core:default",
"core:event:default",
"core:window:default",
"core:window:allow-start-dragging",
"core:webview:allow-internal-toggle-devtools",
"opener:default"
]
}
}
+262 -41
View File
@@ -21,9 +21,7 @@
{
"description": "A list of capabilities.",
"type": "object",
"required": [
"capabilities"
],
"required": ["capabilities"],
"properties": {
"capabilities": {
"description": "The list of capabilities.",
@@ -39,10 +37,7 @@
"Capability": {
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
"type": "object",
"required": [
"identifier",
"permissions"
],
"required": ["identifier", "permissions"],
"properties": {
"identifier": {
"description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`",
@@ -93,10 +88,7 @@
},
"platforms": {
"description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`",
"type": [
"array",
"null"
],
"type": ["array", "null"],
"items": {
"$ref": "#/definitions/Target"
}
@@ -106,9 +98,7 @@
"CapabilityRemote": {
"description": "Configuration for remote URLs that are associated with the capability.",
"type": "object",
"required": [
"urls"
],
"required": ["urls"],
"properties": {
"urls": {
"description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api",
@@ -134,6 +124,166 @@
"description": "Reference a permission or permission set by identifier and extends its scope.",
"type": "object",
"allOf": [
{
"if": {
"properties": {
"identifier": {
"anyOf": [
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
"type": "string",
"const": "opener:default",
"markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls",
"markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application."
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path",
"markdownDescription": "Enables the open_path command without any pre-configured scope."
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url",
"markdownDescription": "Enables the open_url command without any pre-configured scope."
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir",
"markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope."
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path",
"markdownDescription": "Denies the open_path command without any pre-configured scope."
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url",
"markdownDescription": "Denies the open_url command without any pre-configured scope."
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir",
"markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope."
}
]
}
}
},
"then": {
"properties": {
"allow": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": ["url"],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": ["path"],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
},
"deny": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": ["url"],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": ["path"],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
}
}
},
"properties": {
"identifier": {
"description": "Identifier of the permission or permission set.",
"allOf": [
{
"$ref": "#/definitions/Identifier"
}
]
}
}
},
{
"properties": {
"identifier": {
@@ -146,20 +296,14 @@
},
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": [
"array",
"null"
],
"type": ["array", "null"],
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.",
"type": [
"array",
"null"
],
"type": ["array", "null"],
"items": {
"$ref": "#/definitions/Value"
}
@@ -167,9 +311,7 @@
}
}
],
"required": [
"identifier"
]
"required": ["identifier"]
}
]
},
@@ -212,6 +354,12 @@
"const": "core:app:allow-default-window-icon",
"markdownDescription": "Enables the default_window_icon command without any pre-configured scope."
},
{
"description": "Enables the exit command without any pre-configured scope.",
"type": "string",
"const": "core:app:allow-exit",
"markdownDescription": "Enables the exit command without any pre-configured scope."
},
{
"description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.",
"type": "string",
@@ -302,6 +450,12 @@
"const": "core:app:deny-default-window-icon",
"markdownDescription": "Denies the default_window_icon command without any pre-configured scope."
},
{
"description": "Denies the exit command without any pre-configured scope.",
"type": "string",
"const": "core:app:deny-exit",
"markdownDescription": "Denies the exit command without any pre-configured scope."
},
{
"description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.",
"type": "string",
@@ -1562,6 +1716,12 @@
"const": "core:window:allow-set-fullscreen",
"markdownDescription": "Enables the set_fullscreen command without any pre-configured scope."
},
{
"description": "Enables the set_fullscreen_on_monitor command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-fullscreen-on-monitor",
"markdownDescription": "Enables the set_fullscreen_on_monitor command without any pre-configured scope."
},
{
"description": "Enables the set_icon command without any pre-configured scope.",
"type": "string",
@@ -2030,6 +2190,12 @@
"const": "core:window:deny-set-fullscreen",
"markdownDescription": "Denies the set_fullscreen command without any pre-configured scope."
},
{
"description": "Denies the set_fullscreen_on_monitor command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-fullscreen-on-monitor",
"markdownDescription": "Denies the set_fullscreen_on_monitor command without any pre-configured scope."
},
{
"description": "Denies the set_icon command without any pre-configured scope.",
"type": "string",
@@ -2245,6 +2411,54 @@
"type": "string",
"const": "deep-link:deny-unregister",
"markdownDescription": "Denies the unregister command without any pre-configured scope."
},
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
"type": "string",
"const": "opener:default",
"markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls",
"markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application."
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path",
"markdownDescription": "Enables the open_path command without any pre-configured scope."
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url",
"markdownDescription": "Enables the open_url command without any pre-configured scope."
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir",
"markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope."
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path",
"markdownDescription": "Denies the open_path command without any pre-configured scope."
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url",
"markdownDescription": "Denies the open_url command without any pre-configured scope."
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir",
"markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope."
}
]
},
@@ -2308,39 +2522,46 @@
{
"description": "MacOS.",
"type": "string",
"enum": [
"macOS"
]
"enum": ["macOS"]
},
{
"description": "Windows.",
"type": "string",
"enum": [
"windows"
]
"enum": ["windows"]
},
{
"description": "Linux.",
"type": "string",
"enum": [
"linux"
]
"enum": ["linux"]
},
{
"description": "Android.",
"type": "string",
"enum": [
"android"
]
"enum": ["android"]
},
{
"description": "iOS.",
"type": "string",
"enum": [
"iOS"
]
"enum": ["iOS"]
}
]
},
"Application": {
"description": "Opener scope application.",
"anyOf": [
{
"description": "Open in default application.",
"type": "null"
},
{
"description": "If true, allow open with any application.",
"type": "boolean"
},
{
"description": "Allow specific application to open with.",
"type": "string"
}
]
}
}
}
}
+262 -41
View File
@@ -21,9 +21,7 @@
{
"description": "A list of capabilities.",
"type": "object",
"required": [
"capabilities"
],
"required": ["capabilities"],
"properties": {
"capabilities": {
"description": "The list of capabilities.",
@@ -39,10 +37,7 @@
"Capability": {
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
"type": "object",
"required": [
"identifier",
"permissions"
],
"required": ["identifier", "permissions"],
"properties": {
"identifier": {
"description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`",
@@ -93,10 +88,7 @@
},
"platforms": {
"description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`",
"type": [
"array",
"null"
],
"type": ["array", "null"],
"items": {
"$ref": "#/definitions/Target"
}
@@ -106,9 +98,7 @@
"CapabilityRemote": {
"description": "Configuration for remote URLs that are associated with the capability.",
"type": "object",
"required": [
"urls"
],
"required": ["urls"],
"properties": {
"urls": {
"description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api",
@@ -134,6 +124,166 @@
"description": "Reference a permission or permission set by identifier and extends its scope.",
"type": "object",
"allOf": [
{
"if": {
"properties": {
"identifier": {
"anyOf": [
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
"type": "string",
"const": "opener:default",
"markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls",
"markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application."
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path",
"markdownDescription": "Enables the open_path command without any pre-configured scope."
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url",
"markdownDescription": "Enables the open_url command without any pre-configured scope."
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir",
"markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope."
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path",
"markdownDescription": "Denies the open_path command without any pre-configured scope."
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url",
"markdownDescription": "Denies the open_url command without any pre-configured scope."
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir",
"markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope."
}
]
}
}
},
"then": {
"properties": {
"allow": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": ["url"],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": ["path"],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
},
"deny": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": ["url"],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": ["path"],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
}
}
},
"properties": {
"identifier": {
"description": "Identifier of the permission or permission set.",
"allOf": [
{
"$ref": "#/definitions/Identifier"
}
]
}
}
},
{
"properties": {
"identifier": {
@@ -146,20 +296,14 @@
},
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": [
"array",
"null"
],
"type": ["array", "null"],
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.",
"type": [
"array",
"null"
],
"type": ["array", "null"],
"items": {
"$ref": "#/definitions/Value"
}
@@ -167,9 +311,7 @@
}
}
],
"required": [
"identifier"
]
"required": ["identifier"]
}
]
},
@@ -212,6 +354,12 @@
"const": "core:app:allow-default-window-icon",
"markdownDescription": "Enables the default_window_icon command without any pre-configured scope."
},
{
"description": "Enables the exit command without any pre-configured scope.",
"type": "string",
"const": "core:app:allow-exit",
"markdownDescription": "Enables the exit command without any pre-configured scope."
},
{
"description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.",
"type": "string",
@@ -302,6 +450,12 @@
"const": "core:app:deny-default-window-icon",
"markdownDescription": "Denies the default_window_icon command without any pre-configured scope."
},
{
"description": "Denies the exit command without any pre-configured scope.",
"type": "string",
"const": "core:app:deny-exit",
"markdownDescription": "Denies the exit command without any pre-configured scope."
},
{
"description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.",
"type": "string",
@@ -1562,6 +1716,12 @@
"const": "core:window:allow-set-fullscreen",
"markdownDescription": "Enables the set_fullscreen command without any pre-configured scope."
},
{
"description": "Enables the set_fullscreen_on_monitor command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-fullscreen-on-monitor",
"markdownDescription": "Enables the set_fullscreen_on_monitor command without any pre-configured scope."
},
{
"description": "Enables the set_icon command without any pre-configured scope.",
"type": "string",
@@ -2030,6 +2190,12 @@
"const": "core:window:deny-set-fullscreen",
"markdownDescription": "Denies the set_fullscreen command without any pre-configured scope."
},
{
"description": "Denies the set_fullscreen_on_monitor command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-fullscreen-on-monitor",
"markdownDescription": "Denies the set_fullscreen_on_monitor command without any pre-configured scope."
},
{
"description": "Denies the set_icon command without any pre-configured scope.",
"type": "string",
@@ -2245,6 +2411,54 @@
"type": "string",
"const": "deep-link:deny-unregister",
"markdownDescription": "Denies the unregister command without any pre-configured scope."
},
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
"type": "string",
"const": "opener:default",
"markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls",
"markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application."
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path",
"markdownDescription": "Enables the open_path command without any pre-configured scope."
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url",
"markdownDescription": "Enables the open_url command without any pre-configured scope."
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir",
"markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope."
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path",
"markdownDescription": "Denies the open_path command without any pre-configured scope."
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url",
"markdownDescription": "Denies the open_url command without any pre-configured scope."
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir",
"markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope."
}
]
},
@@ -2308,39 +2522,46 @@
{
"description": "MacOS.",
"type": "string",
"enum": [
"macOS"
]
"enum": ["macOS"]
},
{
"description": "Windows.",
"type": "string",
"enum": [
"windows"
]
"enum": ["windows"]
},
{
"description": "Linux.",
"type": "string",
"enum": [
"linux"
]
"enum": ["linux"]
},
{
"description": "Android.",
"type": "string",
"enum": [
"android"
]
"enum": ["android"]
},
{
"description": "iOS.",
"type": "string",
"enum": [
"iOS"
]
"enum": ["iOS"]
}
]
},
"Application": {
"description": "Opener scope application.",
"anyOf": [
{
"description": "Open in default application.",
"type": "null"
},
{
"description": "If true, allow open with any application.",
"type": "boolean"
},
{
"description": "Allow specific application to open with.",
"type": "string"
}
]
}
}
}
}
+58 -262
View File
@@ -1,13 +1,12 @@
use std::{
fs,
path::{Path, PathBuf},
};
#[cfg(test)]
use std::path::Path;
use std::path::PathBuf;
use base64::{engine::general_purpose::STANDARD, Engine};
use serde::{Deserialize, Serialize};
use tauri::{AppHandle, Manager};
use crate::domain::ssh::OperationResponse;
use crate::domain::{files::*, ssh::OperationResponse};
use crate::services::files::{self, expand_home};
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -36,53 +35,6 @@ pub struct DialogResult {
file_paths: Option<Vec<String>>,
}
#[derive(Serialize)]
pub struct PathResponse {
success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
}
#[derive(Serialize)]
pub struct ReadFileResponse {
success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
data: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
}
#[derive(Serialize)]
pub struct LocalFileEntry {
name: String,
path: String,
#[serde(rename = "type")]
entry_type: String,
size: u64,
modified: u64,
permissions: String,
}
#[derive(Serialize)]
pub struct LocalFilesResponse {
success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
files: Option<Vec<LocalFileEntry>>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
}
#[derive(Serialize)]
pub struct LocalStatsResponse {
success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
stats: Option<LocalFileEntry>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
}
#[tauri::command]
pub fn show_open_dialog(options: DialogOptions) -> DialogResult {
let dialog = configure_dialog(rfd::FileDialog::new(), &options);
@@ -139,44 +91,25 @@ pub fn show_save_dialog(app: AppHandle, options: DialogOptions) -> DialogResult
#[tauri::command]
pub async fn read_file(file_path: String) -> ReadFileResponse {
tauri::async_runtime::spawn_blocking(move || match fs::read(expand_home(&file_path)) {
Ok(data) => ReadFileResponse {
success: true,
data: Some(STANDARD.encode(data)),
error: None,
},
Err(error) => ReadFileResponse {
tauri::async_runtime::spawn_blocking(move || files::read_file(file_path))
.await
.unwrap_or_else(|_| ReadFileResponse {
success: false,
data: None,
error: Some(error.to_string()),
},
})
.await
.unwrap_or_else(|_| ReadFileResponse {
success: false,
data: None,
error: Some(crate::domain::error::codes::TASK_JOIN.to_string()),
})
error: Some(crate::domain::error::codes::TASK_JOIN.to_string()),
})
}
#[tauri::command]
pub async fn write_file(file_path: String, data: String) -> OperationResponse {
tauri::async_runtime::spawn_blocking(move || match STANDARD.decode(&data) {
Err(error) => OperationResponse::coded(
crate::domain::error::codes::INVALID_BASE64,
error.to_string(),
),
Ok(bytes) => {
operation(fs::write(expand_home(&file_path), bytes).map_err(|error| error.to_string()))
}
})
.await
.unwrap_or_else(|_| {
OperationResponse::coded(
crate::domain::error::codes::TASK_JOIN,
"write file background task failed",
)
})
tauri::async_runtime::spawn_blocking(move || files::write_file(file_path, data))
.await
.unwrap_or_else(|_| {
OperationResponse::coded(
crate::domain::error::codes::TASK_JOIN,
"write file background task failed",
)
})
}
#[tauri::command]
@@ -204,111 +137,72 @@ pub fn get_downloads_path(app: AppHandle) -> Result<String, String> {
}
#[tauri::command]
pub fn open_downloads_folder(app: AppHandle) -> Result<(), String> {
pub async fn open_downloads_folder(app: AppHandle) -> Result<(), String> {
let path = app
.path()
.download_dir()
.map_err(|error| error.to_string())?;
fs::create_dir_all(&path).map_err(|error| format!("创建下载目录失败: {error}"))?;
open::that(&path).map_err(|error| format!("打开下载目录失败: {error}"))
tauri::async_runtime::spawn_blocking(move || files::open_downloads_folder(path))
.await
.map_err(|error| crate::domain::error::AppError::TaskJoin(error.to_string()).to_string())?
}
#[tauri::command]
pub async fn local_read_dir(path: String) -> LocalFilesResponse {
tauri::async_runtime::spawn_blocking(move || match read_local_dir(&expand_home(&path)) {
Ok(files) => LocalFilesResponse {
success: true,
files: Some(files),
error: None,
},
Err(error) => LocalFilesResponse {
tauri::async_runtime::spawn_blocking(move || files::local_read_dir(path))
.await
.unwrap_or_else(|_| LocalFilesResponse {
success: false,
files: None,
error: Some(error),
},
})
.await
.unwrap_or_else(|_| LocalFilesResponse {
success: false,
files: None,
error: Some(crate::domain::error::codes::TASK_JOIN.to_string()),
})
error: Some(crate::domain::error::codes::TASK_JOIN.to_string()),
})
}
#[tauri::command]
pub async fn local_get_stats(path: String) -> LocalStatsResponse {
tauri::async_runtime::spawn_blocking(move || {
let path = expand_home(&path);
match local_entry(&path) {
Ok(stats) => LocalStatsResponse {
success: true,
stats: Some(stats),
error: None,
},
Err(error) => LocalStatsResponse {
success: false,
stats: None,
error: Some(error),
},
}
})
.await
.unwrap_or_else(|_| LocalStatsResponse {
success: false,
stats: None,
error: Some(crate::domain::error::codes::TASK_JOIN.to_string()),
})
tauri::async_runtime::spawn_blocking(move || files::local_get_stats(path))
.await
.unwrap_or_else(|_| LocalStatsResponse {
success: false,
stats: None,
error: Some(crate::domain::error::codes::TASK_JOIN.to_string()),
})
}
#[tauri::command]
pub async fn local_mkdir(path: String) -> OperationResponse {
tauri::async_runtime::spawn_blocking(move || {
operation(fs::create_dir_all(expand_home(&path)).map_err(|error| error.to_string()))
})
.await
.unwrap_or_else(|_| {
OperationResponse::coded(
crate::domain::error::codes::TASK_JOIN,
"mkdir background task failed",
)
})
tauri::async_runtime::spawn_blocking(move || files::local_mkdir(path))
.await
.unwrap_or_else(|_| {
OperationResponse::coded(
crate::domain::error::codes::TASK_JOIN,
"mkdir background task failed",
)
})
}
#[tauri::command]
pub async fn local_delete(path: String) -> OperationResponse {
tauri::async_runtime::spawn_blocking(move || {
let path = expand_home(&path);
let result = if path.is_dir() {
fs::remove_dir_all(path)
} else {
fs::remove_file(path)
};
operation(result.map_err(|error| error.to_string()))
})
.await
.unwrap_or_else(|_| {
OperationResponse::coded(
crate::domain::error::codes::TASK_JOIN,
"delete background task failed",
)
})
tauri::async_runtime::spawn_blocking(move || files::local_delete(path))
.await
.unwrap_or_else(|_| {
OperationResponse::coded(
crate::domain::error::codes::TASK_JOIN,
"delete background task failed",
)
})
}
#[tauri::command]
pub async fn local_rename(old_path: String, new_path: String) -> OperationResponse {
tauri::async_runtime::spawn_blocking(move || {
operation(
fs::rename(expand_home(&old_path), expand_home(&new_path))
.map_err(|error| error.to_string()),
)
})
.await
.unwrap_or_else(|_| {
OperationResponse::coded(
crate::domain::error::codes::TASK_JOIN,
"rename background task failed",
)
})
tauri::async_runtime::spawn_blocking(move || files::local_rename(old_path, new_path))
.await
.unwrap_or_else(|_| {
OperationResponse::coded(
crate::domain::error::codes::TASK_JOIN,
"rename background task failed",
)
})
}
#[tauri::command]
@@ -372,97 +266,6 @@ fn configure_dialog(mut dialog: rfd::FileDialog, options: &DialogOptions) -> rfd
dialog
}
fn read_local_dir(path: &Path) -> Result<Vec<LocalFileEntry>, String> {
let mut files = Vec::new();
for entry in fs::read_dir(path).map_err(|error| error.to_string())? {
let entry = entry.map_err(|error| error.to_string())?;
files.push(local_entry(&entry.path())?);
}
files.sort_by(|a, b| {
((a.entry_type == "file"), a.name.to_lowercase())
.cmp(&((b.entry_type == "file"), b.name.to_lowercase()))
});
Ok(files)
}
fn local_entry(path: &Path) -> Result<LocalFileEntry, String> {
let metadata = fs::symlink_metadata(path).map_err(|error| error.to_string())?;
let entry_type = if metadata.file_type().is_symlink() {
"symlink"
} else if metadata.is_dir() {
"directory"
} else {
"file"
};
Ok(LocalFileEntry {
name: path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string(),
path: path.to_string_lossy().to_string(),
entry_type: entry_type.to_string(),
size: metadata.len(),
modified: metadata
.modified()
.ok()
.and_then(|time| time.duration_since(std::time::UNIX_EPOCH).ok())
.map(|duration| duration.as_secs())
.unwrap_or(0),
permissions: permissions(&metadata, entry_type),
})
}
#[cfg(unix)]
fn permissions(metadata: &fs::Metadata, kind: &str) -> String {
use std::os::unix::fs::PermissionsExt;
let mode = metadata.permissions().mode();
let prefix = if kind == "directory" {
'd'
} else if kind == "symlink" {
'l'
} else {
'-'
};
format!("{}{}", prefix, permission_bits(mode))
}
#[cfg(not(unix))]
fn permissions(_metadata: &fs::Metadata, kind: &str) -> String {
if kind == "directory" {
"drwxr-xr-x".to_string()
} else {
"-rw-r--r--".to_string()
}
}
#[cfg(unix)]
fn permission_bits(mode: u32) -> String {
let mut output = String::new();
for shift in [6, 3, 0] {
let bits = (mode >> shift) & 7;
output.push(if bits & 4 != 0 { 'r' } else { '-' });
output.push(if bits & 2 != 0 { 'w' } else { '-' });
output.push(if bits & 1 != 0 { 'x' } else { '-' });
}
output
}
fn expand_home(value: &str) -> PathBuf {
if value == "~" {
return dirs::home_dir().unwrap_or_else(|| PathBuf::from(value));
}
let relative = value
.strip_prefix("~/")
.or_else(|| value.strip_prefix("~\\"));
if let Some(relative) = relative {
if let Some(home) = dirs::home_dir() {
return home.join(std::path::Path::new(relative));
}
}
PathBuf::from(value)
}
fn path_result(path: PathBuf, open: bool) -> DialogResult {
let value = path.to_string_lossy().to_string();
DialogResult {
@@ -495,13 +298,6 @@ fn canceled() -> DialogResult {
}
}
fn operation(result: Result<(), String>) -> OperationResponse {
match result {
Ok(()) => OperationResponse::ok(),
Err(error) => OperationResponse::error(error),
}
}
#[cfg(test)]
mod tests {
use super::*;
+30
View File
@@ -9,3 +9,33 @@ pub mod codes {
pub const KNOWN_HOSTS_PATH: &str = "native.knownHostsPath";
pub const INVALID_HOST_KEY_DECISION: &str = "native.invalidHostKeyDecision";
}
/// Domain failures retain their cause while exposing stable IPC codes where available.
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
InvalidBase64(#[from] base64::DecodeError),
#[error("{0}")]
TaskJoin(String),
}
impl AppError {
pub fn code(&self) -> Option<&'static str> {
match self {
Self::Io(_) => None,
Self::InvalidBase64(_) => Some(codes::INVALID_BASE64),
Self::TaskJoin(_) => Some(codes::TASK_JOIN),
}
}
}
impl From<AppError> for crate::domain::ssh::OperationResponse {
fn from(error: AppError) -> Self {
match error.code() {
Some(code) => Self::coded(code, error.to_string()),
None => Self::error(error.to_string()),
}
}
}
+48
View File
@@ -0,0 +1,48 @@
use serde::Serialize;
#[derive(Serialize)]
pub struct PathResponse {
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Serialize)]
pub struct ReadFileResponse {
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Serialize)]
pub struct LocalFileEntry {
pub name: String,
pub path: String,
#[serde(rename = "type")]
pub entry_type: String,
pub size: u64,
pub modified: u64,
pub permissions: String,
}
#[derive(Serialize)]
pub struct LocalFilesResponse {
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub files: Option<Vec<LocalFileEntry>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Serialize)]
pub struct LocalStatsResponse {
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub stats: Option<LocalFileEntry>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
+1
View File
@@ -1,6 +1,7 @@
pub mod ai;
pub mod desktop;
pub mod error;
pub mod files;
pub mod local_terminal;
pub mod serial;
pub mod ssh;
+1
View File
@@ -47,6 +47,7 @@ pub fn run() {
infrastructure::file_log::init();
let builder = tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_single_instance::init(|app, args, _cwd| {
for argument in args {
if argument.starts_with("ssh://") || argument.starts_with("opsxshell://") {
+247
View File
@@ -0,0 +1,247 @@
use crate::domain::{error::AppError, files::*, ssh::OperationResponse};
use base64::{engine::general_purpose::STANDARD, Engine};
use std::{
fs,
path::{Path, PathBuf},
};
pub fn read_file(file_path: String) -> ReadFileResponse {
match fs::read(expand_home(&file_path)) {
Ok(data) => ReadFileResponse {
success: true,
data: Some(STANDARD.encode(data)),
error: None,
},
Err(error) => ReadFileResponse {
success: false,
data: None,
error: Some(error.to_string()),
},
}
}
pub fn write_file(file_path: String, data: String) -> OperationResponse {
let result = (|| -> Result<(), AppError> {
let bytes = STANDARD.decode(&data)?;
fs::write(expand_home(&file_path), bytes)?;
Ok(())
})();
match result {
Ok(()) => OperationResponse::ok(),
Err(error) => error.into(),
}
}
pub fn local_read_dir(path: String) -> LocalFilesResponse {
match read_local_dir(&expand_home(&path)) {
Ok(files) => LocalFilesResponse {
success: true,
files: Some(files),
error: None,
},
Err(error) => LocalFilesResponse {
success: false,
files: None,
error: Some(error),
},
}
}
pub fn local_get_stats(path: String) -> LocalStatsResponse {
let path = expand_home(&path);
match local_entry(&path) {
Ok(stats) => LocalStatsResponse {
success: true,
stats: Some(stats),
error: None,
},
Err(error) => LocalStatsResponse {
success: false,
stats: None,
error: Some(error),
},
}
}
pub fn local_mkdir(path: String) -> OperationResponse {
operation(fs::create_dir_all(expand_home(&path)).map_err(|error| error.to_string()))
}
pub fn local_delete(path: String) -> OperationResponse {
let path = expand_home(&path);
let result = if path.is_dir() {
fs::remove_dir_all(path)
} else {
fs::remove_file(path)
};
operation(result.map_err(|error| error.to_string()))
}
pub fn local_rename(old_path: String, new_path: String) -> OperationResponse {
operation(
fs::rename(expand_home(&old_path), expand_home(&new_path))
.map_err(|error| error.to_string()),
)
}
fn read_local_dir(path: &Path) -> Result<Vec<LocalFileEntry>, String> {
let mut files = Vec::new();
for entry in fs::read_dir(path).map_err(|error| error.to_string())? {
let entry = entry.map_err(|error| error.to_string())?;
files.push(local_entry(&entry.path())?);
}
files.sort_by(|a, b| {
((a.entry_type == "file"), a.name.to_lowercase())
.cmp(&((b.entry_type == "file"), b.name.to_lowercase()))
});
Ok(files)
}
fn local_entry(path: &Path) -> Result<LocalFileEntry, String> {
let metadata = fs::symlink_metadata(path).map_err(|error| error.to_string())?;
let entry_type = if metadata.file_type().is_symlink() {
"symlink"
} else if metadata.is_dir() {
"directory"
} else {
"file"
};
Ok(LocalFileEntry {
name: path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string(),
path: path.to_string_lossy().to_string(),
entry_type: entry_type.to_string(),
size: metadata.len(),
modified: metadata
.modified()
.ok()
.and_then(|time| time.duration_since(std::time::UNIX_EPOCH).ok())
.map(|duration| duration.as_secs())
.unwrap_or(0),
permissions: permissions(&metadata, entry_type),
})
}
#[cfg(unix)]
fn permissions(metadata: &fs::Metadata, kind: &str) -> String {
use std::os::unix::fs::PermissionsExt;
let mode = metadata.permissions().mode();
let prefix = if kind == "directory" {
'd'
} else if kind == "symlink" {
'l'
} else {
'-'
};
format!("{}{}", prefix, permission_bits(mode))
}
#[cfg(not(unix))]
fn permissions(_metadata: &fs::Metadata, kind: &str) -> String {
if kind == "directory" {
"drwxr-xr-x".to_string()
} else {
"-rw-r--r--".to_string()
}
}
#[cfg(unix)]
fn permission_bits(mode: u32) -> String {
let mut output = String::new();
for shift in [6, 3, 0] {
let bits = (mode >> shift) & 7;
output.push(if bits & 4 != 0 { 'r' } else { '-' });
output.push(if bits & 2 != 0 { 'w' } else { '-' });
output.push(if bits & 1 != 0 { 'x' } else { '-' });
}
output
}
pub fn expand_home(value: &str) -> PathBuf {
if value == "~" {
return dirs::home_dir().unwrap_or_else(|| PathBuf::from(value));
}
let relative = value
.strip_prefix("~/")
.or_else(|| value.strip_prefix("~\\"));
if let Some(relative) = relative {
if let Some(home) = dirs::home_dir() {
return home.join(std::path::Path::new(relative));
}
}
PathBuf::from(value)
}
fn operation(result: Result<(), String>) -> OperationResponse {
match result {
Ok(()) => OperationResponse::ok(),
Err(error) => OperationResponse::error(error),
}
}
/// Resolve the native downloads path in the command, then perform I/O off the UI thread.
pub fn open_downloads_folder(path: PathBuf) -> Result<(), String> {
fs::create_dir_all(&path).map_err(|error| format!("创建下载目录失败: {error}"))?;
open::that(&path).map_err(|error| format!("打开下载目录失败: {error}"))
}
#[cfg(test)]
mod tests {
use super::*;
struct TestDirectory(PathBuf);
impl TestDirectory {
fn new() -> Self {
let path =
std::env::temp_dir().join(format!("opsxshell-files-{}", uuid::Uuid::new_v4()));
fs::create_dir_all(&path).unwrap();
Self(path)
}
fn path(&self, name: &str) -> String {
self.0.join(name).to_string_lossy().into_owned()
}
}
impl Drop for TestDirectory {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.0);
}
}
#[test]
fn binary_file_roundtrip_and_directory_operations() {
let dir = TestDirectory::new();
let bytes = [0, 255, 128, 10];
let file = dir.path("z.bin");
assert!(write_file(file.clone(), STANDARD.encode(bytes)).success);
assert_eq!(read_file(file.clone()).data, Some(STANDARD.encode(bytes)));
assert!(local_mkdir(dir.path("folder")).success);
let listing = local_read_dir(dir.path(""));
assert!(listing.success);
let entries = listing.files.unwrap();
assert_eq!(entries[0].entry_type, "directory");
assert_eq!(entries[1].name, "z.bin");
assert_eq!(local_get_stats(file.clone()).stats.unwrap().size, 4);
let renamed = dir.path("renamed.bin");
assert!(local_rename(file.clone(), renamed.clone()).success);
assert!(!Path::new(&file).exists());
assert!(local_delete(renamed.clone()).success);
assert!(!read_file(renamed).success);
}
#[test]
fn invalid_base64_never_overwrites_existing_file() {
let dir = TestDirectory::new();
let path = dir.path("keep.bin");
fs::write(&path, b"keep").unwrap();
let response = write_file(path.clone(), "%%%".into());
assert!(!response.success);
assert_eq!(
response.error_code.as_deref(),
Some(crate::domain::error::codes::INVALID_BASE64)
);
assert_eq!(fs::read(path).unwrap(), b"keep");
}
}
+1
View File
@@ -4,6 +4,7 @@ pub mod ai;
pub mod credential_store;
pub mod deep_link;
pub mod desktop;
pub mod files;
pub mod license;
pub mod local_terminal;
pub mod serial;
+3 -3
View File
@@ -1,11 +1,11 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "OpsXShell",
"productName": "OpsxShell",
"version": "2.1.1",
"identifier": "com.opsxshell.desktop",
"build": {
"beforeDevCommand": "npm run dev:ui",
"devUrl": "http://127.0.0.1:1420",
"devUrl": "http://127.0.0.1:5175",
"beforeBuildCommand": "npm run build:ui",
"frontendDist": "../dist"
},
@@ -13,7 +13,7 @@
"windows": [
{
"label": "main",
"title": "OpsXShell",
"title": "OpsxShell",
"width": 1280,
"height": 800,
"minWidth": 1024,
+1 -1
View File
@@ -3,7 +3,7 @@
"app": {
"windows": [
{
"title": "OpsXShell",
"title": "OpsxShell",
"decorations": true,
"titleBarStyle": "Visible",
"hiddenTitle": false
+1 -1
View File
@@ -3,7 +3,7 @@
"app": {
"windows": [
{
"title": "OpsXShell",
"title": "OpsxShell",
"decorations": true,
"titleBarStyle": "Overlay",
"hiddenTitle": true,
+1 -1
View File
@@ -3,7 +3,7 @@
"app": {
"windows": [
{
"title": "OpsXShell",
"title": "OpsxShell",
"decorations": true,
"titleBarStyle": "Visible",
"hiddenTitle": false