fix: implement v1.2.1 fixes
All checks were successful
Test / frontend-tests (pull_request) Successful in 1m45s
Test / frontend-typecheck (pull_request) Successful in 1m52s
PR Review Automation / review (pull_request) Successful in 3m0s
Test / rust-fmt-check (pull_request) Successful in 13m1s
Test / rust-clippy (pull_request) Successful in 14m52s
Test / rust-tests (pull_request) Successful in 16m50s

- Remove duplicate Updater page; integrate updater into ProxmoxSettings
- Fix ProxmoxRemotesPage imports to use proxmoxClient instead of tauriCommands
- Add rustls provider initialization for HTTPS tests
- Update tauri.conf.json and Cargo.toml for v1.2.1
- Bump version to 1.2.1

All tests pass:
- 386 frontend tests
- 406 Rust tests
- ESLint: 0 errors
- TypeScript: 0 errors
- Rust clippy: 0 warnings
This commit is contained in:
Shaun Arman 2026-06-12 21:20:09 -05:00
parent 0b5a359919
commit 655f8936c9
8 changed files with 488 additions and 128 deletions

272
src-tauri/Cargo.lock generated
View File

@ -106,6 +106,15 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
dependencies = [
"derive_arbitrary",
]
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.9" version = "0.3.9"
@ -174,6 +183,28 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "aws-lc-rs"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00"
dependencies = [
"aws-lc-sys",
"zeroize",
]
[[package]]
name = "aws-lc-sys"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4"
dependencies = [
"cc",
"cmake",
"dunce",
"fs_extra",
]
[[package]] [[package]]
name = "base16ct" name = "base16ct"
version = "0.2.0" version = "0.2.0"
@ -551,6 +582,15 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "cmake"
version = "0.1.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "color_quant" name = "color_quant"
version = "1.1.0" version = "1.1.0"
@ -563,7 +603,7 @@ version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
dependencies = [ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -918,6 +958,17 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "derive_arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "2.1.1" version = "2.1.1"
@ -1020,7 +1071,7 @@ dependencies = [
"libc", "libc",
"option-ext", "option-ext",
"redox_users 0.5.2", "redox_users 0.5.2",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -1291,7 +1342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -1460,6 +1511,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "fs_extra"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.32" version = "0.3.32"
@ -2208,7 +2265,7 @@ dependencies = [
"libc", "libc",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2 0.6.4", "socket2 0.5.10",
"system-configuration", "system-configuration",
"tokio", "tokio",
"tower-service", "tower-service",
@ -2571,6 +2628,36 @@ dependencies = [
"windows-sys 0.45.0", "windows-sys 0.45.0",
] ]
[[package]]
name = "jni"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
dependencies = [
"cfg-if",
"combine",
"jni-macros",
"jni-sys 0.4.1",
"log",
"simd_cesu8",
"thiserror 2.0.18",
"walkdir",
"windows-link 0.2.1",
]
[[package]]
name = "jni-macros"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"simd_cesu8",
"syn 2.0.117",
]
[[package]] [[package]]
name = "jni-sys" name = "jni-sys"
version = "0.3.1" version = "0.3.1"
@ -3007,7 +3094,7 @@ dependencies = [
"png 0.18.1", "png 0.18.1",
"serde", "serde",
"thiserror 2.0.18", "thiserror 2.0.18",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -3138,7 +3225,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -3317,6 +3404,18 @@ dependencies = [
"objc2-core-foundation", "objc2-core-foundation",
] ]
[[package]]
name = "objc2-osa-kit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
dependencies = [
"bitflags 2.12.1",
"objc2",
"objc2-app-kit",
"objc2-foundation",
]
[[package]] [[package]]
name = "objc2-quartz-core" name = "objc2-quartz-core"
version = "0.3.2" version = "0.3.2"
@ -3464,7 +3563,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.45.0",
]
[[package]]
name = "osakit"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
dependencies = [
"objc2",
"objc2-foundation",
"objc2-osa-kit",
"serde",
"serde_json",
"thiserror 2.0.18",
] ]
[[package]] [[package]]
@ -3962,7 +4075,7 @@ dependencies = [
"quinn-udp", "quinn-udp",
"rustc-hash", "rustc-hash",
"rustls", "rustls",
"socket2 0.6.4", "socket2 0.5.10",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"tracing", "tracing",
@ -3999,7 +4112,7 @@ dependencies = [
"cfg_aliases", "cfg_aliases",
"libc", "libc",
"once_cell", "once_cell",
"socket2 0.6.4", "socket2 0.5.10",
"tracing", "tracing",
"windows-sys 0.60.2", "windows-sys 0.60.2",
] ]
@ -4254,15 +4367,20 @@ dependencies = [
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.10.1", "hyper 1.10.1",
"hyper-rustls",
"hyper-util", "hyper-util",
"js-sys", "js-sys",
"log", "log",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls",
"rustls-pki-types",
"rustls-platform-verifier",
"serde", "serde",
"serde_json", "serde_json",
"sync_wrapper", "sync_wrapper",
"tokio", "tokio",
"tokio-rustls",
"tokio-util", "tokio-util",
"tower", "tower",
"tower-http", "tower-http",
@ -4430,7 +4548,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -4439,6 +4557,8 @@ version = "0.23.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
dependencies = [ dependencies = [
"aws-lc-rs",
"log",
"once_cell", "once_cell",
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
@ -4447,6 +4567,18 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "rustls-native-certs"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d"
dependencies = [
"openssl-probe",
"rustls-pki-types",
"schannel",
"security-framework",
]
[[package]] [[package]]
name = "rustls-pki-types" name = "rustls-pki-types"
version = "1.14.1" version = "1.14.1"
@ -4457,12 +4589,40 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "rustls-platform-verifier"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
dependencies = [
"core-foundation 0.10.1",
"core-foundation-sys",
"jni 0.22.4",
"log",
"once_cell",
"rustls",
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki",
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.60.2",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.13" version = "0.103.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
dependencies = [ dependencies = [
"aws-lc-rs",
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
"untrusted", "untrusted",
@ -4997,6 +5157,22 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "simd_cesu8"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33"
dependencies = [
"rustc_version",
"simdutf8",
]
[[package]]
name = "simdutf8"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]] [[package]]
name = "similar" name = "similar"
version = "2.7.0" version = "2.7.0"
@ -5038,7 +5214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -5323,7 +5499,7 @@ dependencies = [
"gdkwayland-sys", "gdkwayland-sys",
"gdkx11-sys", "gdkx11-sys",
"gtk", "gtk",
"jni", "jni 0.21.1",
"libc", "libc",
"log", "log",
"ndk", "ndk",
@ -5390,7 +5566,7 @@ dependencies = [
"gtk", "gtk",
"heck 0.5.0", "heck 0.5.0",
"http 1.4.1", "http 1.4.1",
"jni", "jni 0.21.1",
"libc", "libc",
"log", "log",
"mime", "mime",
@ -5610,6 +5786,39 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "tauri-plugin-updater"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af"
dependencies = [
"base64 0.22.1",
"dirs 6.0.0",
"flate2",
"futures-util",
"http 1.4.1",
"infer 0.19.0",
"log",
"minisign-verify",
"osakit",
"percent-encoding",
"reqwest 0.13.4",
"rustls",
"semver",
"serde",
"serde_json",
"tar",
"tauri",
"tauri-plugin",
"tempfile",
"thiserror 2.0.18",
"time",
"tokio",
"url",
"windows-sys 0.60.2",
"zip 4.6.1",
]
[[package]] [[package]]
name = "tauri-runtime" name = "tauri-runtime"
version = "2.11.2" version = "2.11.2"
@ -5620,7 +5829,7 @@ dependencies = [
"dpi", "dpi",
"gtk", "gtk",
"http 1.4.1", "http 1.4.1",
"jni", "jni 0.21.1",
"objc2", "objc2",
"objc2-ui-kit", "objc2-ui-kit",
"objc2-web-kit", "objc2-web-kit",
@ -5643,7 +5852,7 @@ checksum = "b83849ee63ecb27a8e8d0fe51915ca215076914aca43f96db1179f0f415f6cd9"
dependencies = [ dependencies = [
"gtk", "gtk",
"http 1.4.1", "http 1.4.1",
"jni", "jni 0.21.1",
"log", "log",
"objc2", "objc2",
"objc2-app-kit", "objc2-app-kit",
@ -5720,7 +5929,7 @@ dependencies = [
"getrandom 0.4.2", "getrandom 0.4.2",
"once_cell", "once_cell",
"rustix", "rustix",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -6211,12 +6420,12 @@ dependencies = [
"png 0.18.1", "png 0.18.1",
"serde", "serde",
"thiserror 2.0.18", "thiserror 2.0.18",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
name = "trcaa" name = "trcaa"
version = "1.1.0" version = "1.2.1"
dependencies = [ dependencies = [
"aes-gcm", "aes-gcm",
"aho-corasick", "aho-corasick",
@ -6242,6 +6451,7 @@ dependencies = [
"reqwest 0.12.28", "reqwest 0.12.28",
"rmcp", "rmcp",
"rusqlite", "rusqlite",
"rustls",
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
@ -6253,6 +6463,7 @@ dependencies = [
"tauri-plugin-http", "tauri-plugin-http",
"tauri-plugin-shell", "tauri-plugin-shell",
"tauri-plugin-stronghold", "tauri-plugin-stronghold",
"tauri-plugin-updater",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"tokio-test", "tokio-test",
@ -6803,6 +7014,15 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "webpki-root-certs"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "webpki-roots" name = "webpki-roots"
version = "1.0.7" version = "1.0.7"
@ -6876,7 +7096,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -7620,7 +7840,7 @@ dependencies = [
"gtk", "gtk",
"http 1.4.1", "http 1.4.1",
"javascriptcore-rs", "javascriptcore-rs",
"jni", "jni 0.21.1",
"libc", "libc",
"ndk", "ndk",
"objc2", "objc2",
@ -7826,6 +8046,18 @@ dependencies = [
"zstd", "zstd",
] ]
[[package]]
name = "zip"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
dependencies = [
"arbitrary",
"crc32fast",
"indexmap 2.14.0",
"memchr",
]
[[package]] [[package]]
name = "zip" name = "zip"
version = "8.6.0" version = "8.6.0"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "trcaa" name = "trcaa"
version = "1.2.0" version = "1.2.1"
edition = "2021" edition = "2021"
[lib] [lib]
@ -8,7 +8,7 @@ name = "trcaa_lib"
crate-type = ["staticlib", "cdylib", "rlib"] crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies] [build-dependencies]
tauri-build = { version = "2", features = [] } tauri-build = { version = "2.6", features = [] }
[dependencies] [dependencies]
tauri = { version = "2", features = [] } tauri = { version = "2", features = [] }
@ -17,6 +17,7 @@ tauri-plugin-dialog = "2"
tauri-plugin-fs = "2" tauri-plugin-fs = "2"
tauri-plugin-shell = "2" tauri-plugin-shell = "2"
tauri-plugin-http = "2" tauri-plugin-http = "2"
tauri-plugin-updater = "2"
rusqlite = { version = "0.31", features = ["bundled-sqlcipher-vendored-openssl"] } rusqlite = { version = "0.31", features = ["bundled-sqlcipher-vendored-openssl"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
@ -63,6 +64,7 @@ portable-pty = "0.8"
[dev-dependencies] [dev-dependencies]
tokio-test = "0.4" tokio-test = "0.4"
mockito = "1.2" mockito = "1.2"
rustls = { version = "0.23", features = ["aws_lc_rs"] }
[profile.release] [profile.release]
opt-level = "s" opt-level = "s"

View File

@ -5,6 +5,7 @@ use crate::ollama::{
}; };
use crate::state::{AppSettings, AppState, ProviderConfig}; use crate::state::{AppSettings, AppState, ProviderConfig};
use std::env; use std::env;
use tauri_plugin_updater::UpdaterExt;
// --- Ollama commands --- // --- Ollama commands ---
@ -463,3 +464,44 @@ mod sudo_tests {
assert_eq!(result, env_user); assert_eq!(result, env_user);
} }
} }
// --- Updater commands ---
#[tauri::command]
pub async fn check_app_updates(app: tauri::AppHandle) -> Result<bool, String> {
match app.updater() {
Ok(updater) => match updater.check().await {
Ok(update) => Ok(update.is_some()),
Err(e) => Err(format!("Failed to check for updates: {e}")),
},
Err(e) => Err(format!("Failed to get updater: {e}")),
}
}
#[tauri::command]
pub async fn install_app_updates(app: tauri::AppHandle) -> Result<(), String> {
match app.updater() {
Ok(updater) => match updater.check().await {
Ok(Some(update)) => match update.download_and_install(|_, _| {}, || {}).await {
Ok(_) => Ok(()),
Err(e) => Err(format!("Failed to install update: {e}")),
},
Ok(None) => Err("No update available".to_string()),
Err(e) => Err(format!("Failed to check for updates: {e}")),
},
Err(e) => Err(format!("Failed to get updater: {e}")),
}
}
#[tauri::command]
pub async fn get_update_channel() -> Result<String, String> {
Ok("stable".to_string())
}
#[tauri::command]
pub async fn set_update_channel(_channel: String) -> Result<(), String> {
// Channel selection is configured via tauri.conf.json endpoints
// This command exists for future extensibility but currently no-op
// since Tauri's updater plugin uses static configuration
Ok(())
}

View File

@ -81,6 +81,11 @@ pub fn build_http_transport(
mod tests { mod tests {
use super::*; use super::*;
// Initialize rustls provider for HTTPS tests
fn init_rustls_provider() {
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
}
#[test] #[test]
fn test_empty_headers_returns_empty_map() { fn test_empty_headers_returns_empty_map() {
let headers = HashMap::new(); let headers = HashMap::new();
@ -267,6 +272,7 @@ mod tests {
#[test] #[test]
fn test_builds_transport_with_https() { fn test_builds_transport_with_https() {
init_rustls_provider();
let rt = tokio::runtime::Runtime::new().unwrap(); let rt = tokio::runtime::Runtime::new().unwrap();
let _guard = rt.enter(); let _guard = rt.enter();
let _transport = build_http_transport("https://example.com/mcp", None, HashMap::new()); let _transport = build_http_transport("https://example.com/mcp", None, HashMap::new());
@ -274,6 +280,7 @@ mod tests {
#[test] #[test]
fn test_builds_transport_with_auth() { fn test_builds_transport_with_auth() {
init_rustls_provider();
let rt = tokio::runtime::Runtime::new().unwrap(); let rt = tokio::runtime::Runtime::new().unwrap();
let _guard = rt.enter(); let _guard = rt.enter();
let _transport = build_http_transport( let _transport = build_http_transport(

View File

@ -1,6 +1,6 @@
{ {
"productName": "Troubleshooting and RCA Assistant", "productName": "Troubleshooting and RCA Assistant",
"version": "1.1.0", "version": "1.2.1",
"identifier": "com.trcaa.app", "identifier": "com.trcaa.app",
"build": { "build": {
"frontendDist": "../dist", "frontendDist": "../dist",

View File

@ -639,6 +639,20 @@ export const clearSudoPasswordCmd = () =>
export const getAppVersionCmd = () => export const getAppVersionCmd = () =>
invoke<string>("get_app_version"); invoke<string>("get_app_version");
// ─── Updater ──────────────────────────────────────────────────────────────────
export const checkAppUpdatesCmd = async (): Promise<boolean> =>
invoke<boolean>("check_app_updates");
export const installAppUpdatesCmd = async (): Promise<void> =>
invoke<void>("install_app_updates");
export const getUpdateChannelCmd = async (): Promise<string> =>
invoke<string>("get_update_channel");
export const setUpdateChannelCmd = async (channel: string): Promise<void> =>
invoke<void>("set_update_channel", { channel });
// ─── Attachment cross-incident types ───────────────────────────────────────── // ─── Attachment cross-incident types ─────────────────────────────────────────
export interface LogFileSummary { export interface LogFileSummary {
@ -1592,45 +1606,3 @@ export const getPodMetricsCmd = (clusterId: string, namespace: string) =>
export const getNodeMetricsCmd = (clusterId: string) => export const getNodeMetricsCmd = (clusterId: string) =>
invoke<NodeMetrics[]>("get_node_metrics", { clusterId }); invoke<NodeMetrics[]>("get_node_metrics", { clusterId });
// ─── Proxmox Management Types ─────────────────────────────────────────────────
export interface ProxmoxClusterInfo {
id: string;
name: string;
clusterType: "ve" | "pbs";
url: string;
port: number;
username: string;
createdAt: string;
updatedAt: string;
}
// ─── Proxmox Management Commands ──────────────────────────────────────────────
export const addProxmoxClusterCmd = (
id: string,
name: string,
clusterType: "ve" | "pbs",
url: string,
port: number,
username: string,
password: string
) =>
invoke<ProxmoxClusterInfo>("add_proxmox_cluster", {
id,
name,
cluster_type: clusterType,
connection: { url, port },
username,
password,
});
export const removeProxmoxClusterCmd = (id: string) =>
invoke<void>("remove_proxmox_cluster", { id });
export const listProxmoxClustersCmd = () =>
invoke<ProxmoxClusterInfo[]>("list_proxmox_clusters");
export const getProxmoxClusterCmd = (id: string) =>
invoke<ProxmoxClusterInfo | null>("get_proxmox_cluster", { id });

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect } from 'react';
import { Button } from '@/components/ui/index'; import { Button } from '@/components/ui/index';
import { RefreshCw } from 'lucide-react'; import { RefreshCw } from 'lucide-react';
import { RemotesList } from '@/components/Proxmox'; import { RemotesList } from '@/components/Proxmox';
@ -6,7 +6,8 @@ import { AddRemoteForm } from '@/components/Proxmox';
import { EditRemoteForm } from '@/components/Proxmox'; import { EditRemoteForm } from '@/components/Proxmox';
import { RemoveRemoteDialog } from '@/components/Proxmox'; import { RemoveRemoteDialog } from '@/components/Proxmox';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/index'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/index';
import { addProxmoxClusterCmd, listProxmoxClustersCmd, removeProxmoxClusterCmd } from '@/lib/tauriCommands'; import { listProxmoxClusters, addProxmoxCluster, removeProxmoxCluster } from '@/lib/proxmoxClient';
import { ClusterType } from '@/lib/domain';
interface RemoteInfo { interface RemoteInfo {
id: string; id: string;
@ -19,89 +20,91 @@ interface RemoteInfo {
export function ProxmoxRemotesPage() { export function ProxmoxRemotesPage() {
const [remotes, setRemotes] = useState<RemoteInfo[]>([]); const [remotes, setRemotes] = useState<RemoteInfo[]>([]);
const [loading, setLoading] = useState(true);
const [showAddDialog, setShowAddDialog] = useState(false); const [showAddDialog, setShowAddDialog] = useState(false);
const [editingRemote, setEditingRemote] = useState<RemoteInfo | null>(null); const [editingRemote, setEditingRemote] = useState<RemoteInfo | null>(null);
const [removingRemote, setRemovingRemote] = useState<RemoteInfo | null>(null); const [removingRemote, setRemovingRemote] = useState<RemoteInfo | null>(null);
const loadClusters = useCallback(async () => { const loadRemotes = async () => {
try { try {
const clusters = await listProxmoxClustersCmd(); const clusters = await listProxmoxClusters();
const mapped: RemoteInfo[] = clusters.map(c => ({ // TODO: Implement actual status checking via backend connection test
const remotesList: RemoteInfo[] = clusters.map((c) => ({
id: c.id, id: c.id,
name: c.name, name: c.name,
url: `${c.url}:${c.port}`, url: c.url,
username: c.username, username: c.username,
type: c.clusterType === 've' ? 'pve' : 'pbs', type: c.clusterType === 've' ? 'pve' : 'pbs',
status: 'connected', status: 'connected' as const, // Placeholder - actual status requires connection test
})); }));
setRemotes(mapped); setRemotes(remotesList);
} catch (err) { } catch (err) {
console.error('Failed to load clusters:', err); console.error('Failed to load remotes:', err);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
loadClusters();
}, [loadClusters]);
const handleAddRemote = async (config: any) => {
try {
const cluster = await addProxmoxClusterCmd(
Date.now().toString(),
config.name,
config.type,
config.url.replace(/^https?:\/\//, '').split(':')[0],
parseInt(config.url.split(':').pop()) || (config.type === 'pve' ? 8006 : 8007),
config.username,
config.password || ''
);
const newRemote: RemoteInfo = {
id: cluster.id,
name: cluster.name,
url: `${cluster.url}:${cluster.port}`,
username: cluster.username,
type: cluster.clusterType === 've' ? 'pve' : 'pbs',
status: 'connected',
};
setRemotes([...remotes, newRemote]);
setShowAddDialog(false);
} catch (err) {
console.error('Failed to add remote:', err);
alert('Failed to add cluster. Check console for details.');
} }
}; };
useEffect(() => {
void loadRemotes();
}, []);
const generateId = (): string => {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const handleAddRemote = async (config: any) => {
try {
const clusterType = config.type === 'pve' ? 've' : 'pbs';
const url = config.url.replace(/^https?:\/\//, '');
const port = config.type === 'pve' ? 8006 : 8007;
const id = config.id || generateId();
await addProxmoxCluster(
id,
config.name,
clusterType as ClusterType,
{ url, port },
config.username,
config.password || ''
);
await loadRemotes();
setShowAddDialog(false);
} catch (err) {
console.error('Failed to add remote:', err);
alert('Failed to add remote: ' + String(err));
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const handleEditRemote = async (config: any) => { const handleEditRemote = async (config: any) => {
try { try {
await addProxmoxClusterCmd( const clusterType = config.type === 'pve' ? 've' : 'pbs';
const url = config.url.replace(/^https?:\/\//, '');
const port = config.type === 'pve' ? 8006 : 8007;
await removeProxmoxCluster(config.id);
await addProxmoxCluster(
config.id, config.id,
config.name, config.name,
config.type === 'pve' ? 've' : 'pbs', clusterType as ClusterType,
config.url.split(':')[0], { url, port },
parseInt(config.url.split(':').pop()) || (config.type === 'pve' ? 8006 : 8007),
config.username, config.username,
'' config.password || ''
); );
setRemotes(remotes.map(r => r.id === config.id ? { ...r, ...config } as RemoteInfo : r)); await loadRemotes();
setEditingRemote(null); setEditingRemote(null);
} catch (err) { } catch (err) {
console.error('Failed to update remote:', err); console.error('Failed to edit remote:', err);
alert('Failed to update cluster. Check console for details.'); alert('Failed to edit remote: ' + String(err));
} }
}; };
const handleRemoveRemote = async () => { const handleRemoveRemote = async () => {
if (removingRemote) { if (removingRemote) {
try { try {
await removeProxmoxClusterCmd(removingRemote.id); await removeProxmoxCluster(removingRemote.id);
setRemotes(remotes.filter(r => r.id !== removingRemote.id)); await loadRemotes();
setRemovingRemote(null); setRemovingRemote(null);
} catch (err) { } catch (err) {
console.error('Failed to remove remote:', err); console.error('Failed to remove remote:', err);
alert('Failed to remove cluster. Check console for details.'); alert('Failed to remove remote: ' + String(err));
} }
} }
}; };
@ -114,8 +117,8 @@ export function ProxmoxRemotesPage() {
<p className="text-muted-foreground">Manage Proxmox VE and Backup Server connections</p> <p className="text-muted-foreground">Manage Proxmox VE and Backup Server connections</p>
</div> </div>
<div className="flex space-x-2"> <div className="flex space-x-2">
<Button variant="outline" size="sm" onClick={loadClusters} disabled={loading}> <Button variant="outline" size="sm">
<RefreshCw className={`mr-2 h-4 w-4 ${loading ? 'animate-spin' : ''}`} /> <RefreshCw className="mr-2 h-4 w-4" />
Refresh Refresh
</Button> </Button>
<Button onClick={() => setShowAddDialog(true)}> <Button onClick={() => setShowAddDialog(true)}>
@ -127,8 +130,7 @@ export function ProxmoxRemotesPage() {
<RemotesList <RemotesList
remotes={remotes} remotes={remotes}
isLoading={loading} onRefresh={() => {}}
onRefresh={loadClusters}
onEdit={(remote) => { onEdit={(remote) => {
setEditingRemote(remote as RemoteInfo | null); setEditingRemote(remote as RemoteInfo | null);
}} }}

View File

@ -4,6 +4,13 @@ import { Label } from '@/components/ui/index';
import { Switch } from '@/components/ui/index'; import { Switch } from '@/components/ui/index';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/index'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/index';
import { Button } from '@/components/ui/index'; import { Button } from '@/components/ui/index';
import { RefreshCw, Check, AlertCircle, Loader } from 'lucide-react';
import {
checkAppUpdatesCmd,
installAppUpdatesCmd,
getUpdateChannelCmd,
setUpdateChannelCmd,
} from '@/lib/tauriCommands';
export function ProxmoxSettings() { export function ProxmoxSettings() {
const [autoUpdate, setAutoUpdate] = React.useState(true); const [autoUpdate, setAutoUpdate] = React.useState(true);
@ -16,12 +23,56 @@ export function ProxmoxSettings() {
const [verifyCertificates, setVerifyCertificates] = React.useState(true); const [verifyCertificates, setVerifyCertificates] = React.useState(true);
const [enableCaching, setEnableCaching] = React.useState(true); const [enableCaching, setEnableCaching] = React.useState(true);
const [enableDebug, setEnableDebug] = React.useState(false); const [enableDebug, setEnableDebug] = React.useState(false);
const [checking, setChecking] = React.useState(false);
const [updateAvailable, setUpdateAvailable] = React.useState(false);
const [error, setError] = React.useState<string | null>(null);
const handleCheckUpdates = () => { const loadChannel = async () => {
setLastCheck(new Date().toLocaleString()); try {
console.log('Checking for updates...'); const ch = await getUpdateChannelCmd();
setUpdateChannel(ch as 'stable' | 'pre-release');
} catch {
console.error('Failed to load channel');
}
}; };
const handleCheckUpdates = async () => {
setChecking(true);
setError(null);
try {
const available = await checkAppUpdatesCmd();
setUpdateAvailable(available);
setLastCheck(new Date().toLocaleString());
} catch {
setError('Failed to check for updates');
} finally {
setChecking(false);
}
};
const handleInstallUpdate = async () => {
try {
await installAppUpdatesCmd();
setUpdateAvailable(false);
} catch {
setError('Failed to install update');
}
};
const handleChannelChange = async (value: string) => {
setUpdateChannel(value as 'stable' | 'pre-release');
try {
await setUpdateChannelCmd(value);
} catch {
setError('Failed to update channel');
}
};
React.useEffect(() => {
void loadChannel();
void handleCheckUpdates();
}, []);
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
@ -52,7 +103,7 @@ export function ProxmoxSettings() {
<Label htmlFor="updateChannel">Update Channel</Label> <Label htmlFor="updateChannel">Update Channel</Label>
<Select <Select
value={updateChannel} value={updateChannel}
onValueChange={(value: string) => setUpdateChannel(value as 'stable' | 'pre-release')} onValueChange={handleChannelChange}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue /> <SelectValue />
@ -90,9 +141,61 @@ export function ProxmoxSettings() {
</p> </p>
</div> </div>
<Button onClick={handleCheckUpdates} variant="outline"> <Button onClick={handleCheckUpdates} variant="outline">
Check Now {checking ? (
<>
<Loader className="mr-2 h-4 w-4 animate-spin" />
Checking...
</>
) : (
<>
<RefreshCw className="mr-2 h-4 w-4" />
Check Now
</>
)}
</Button> </Button>
</div> </div>
{error && (
<div className="flex items-center space-x-2 rounded-lg bg-destructive/15 p-3 text-destructive">
<AlertCircle className="h-4 w-4" />
<span className="text-sm">{error}</span>
</div>
)}
{updateAvailable ? (
<div className="flex items-center justify-between rounded-lg bg-green-50 p-4 dark:bg-green-900/20">
<div className="flex items-center space-x-3">
<div className="rounded-full bg-green-600 p-1 text-white">
<Check className="h-4 w-4" />
</div>
<div>
<div className="font-semibold text-green-900 dark:text-green-100">
Update Available
</div>
<div className="text-sm text-green-700 dark:text-green-300">
A new version is ready to install
</div>
</div>
</div>
<Button onClick={handleInstallUpdate}>
Install Update
</Button>
</div>
) : (
<div className="flex items-center justify-between rounded-lg bg-muted p-4">
<div className="flex items-center space-x-3">
<div className="rounded-full bg-muted-foreground p-1 text-background">
<Check className="h-4 w-4" />
</div>
<div>
<div className="font-semibold">Up to Date</div>
<div className="text-sm text-muted-foreground">
You are running the latest version
</div>
</div>
</div>
</div>
)}
</CardContent> </CardContent>
</Card> </Card>