From b44a95c67bb2c46a76f0a5b961e0034dd2d9b940 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sun, 12 May 2024 14:40:06 +0200 Subject: [PATCH] Configured sidecar - Add the sidecar to start the .NET server when running the app - Add proper logging - Add message handling for communication between .NET and Rust/Tauri - Add dynamic port allocation for the .NET server - Add transition to .NET app, i.e., a loading screen --- runtime/Cargo.lock | 445 +++++++++++++++++++++++++++++++++++++++- runtime/Cargo.toml | 5 +- runtime/src/main.rs | 289 +++++++++++++++++++++++--- runtime/tauri.conf.json | 26 ++- 4 files changed, 726 insertions(+), 39 deletions(-) diff --git a/runtime/Cargo.lock b/runtime/Cargo.lock index 256b470..81d6a9f 100644 --- a/runtime/Cargo.lock +++ b/runtime/Cargo.lock @@ -157,7 +157,7 @@ dependencies = [ "polling 2.8.0", "rustix 0.37.27", "slab", - "socket2", + "socket2 0.4.10", "waker-fn", ] @@ -448,6 +448,9 @@ name = "bytes" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +dependencies = [ + "serde", +] [[package]] name = "cairo-rs" @@ -914,7 +917,7 @@ dependencies = [ "rustc_version", "toml 0.8.12", "vswhom", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -1090,6 +1093,21 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flexi_logger" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f248c29a6d4bc5d065c9e9068d858761a0dcd796759f7801cc14db35db23abd8" +dependencies = [ + "chrono", + "glob", + "is-terminal", + "log", + "nu-ansi-term 0.49.0", + "regex", + "thiserror", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1573,6 +1591,25 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "h2" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1661,12 +1698,72 @@ dependencies = [ "itoa 1.0.11", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + [[package]] name = "http-range" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 1.0.11", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -1808,6 +1905,23 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "itoa" version = "0.4.8" @@ -2067,16 +2181,25 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "mindwork-ai-studio" version = "0.1.0" dependencies = [ "arboard", + "flexi_logger", "keyring", + "log", "serde", "serde_json", "tauri", "tauri-build", + "tokio", ] [[package]] @@ -2089,6 +2212,35 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndk" version = "0.6.0" @@ -2151,6 +2303,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "num" version = "0.4.1" @@ -2377,6 +2538,50 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "openssl" +version = "0.10.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.55", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -2387,6 +2592,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_pipe" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "overload" version = "0.1.1" @@ -2916,6 +3131,48 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "reqwest" +version = "0.11.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg 0.50.0", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -2958,6 +3215,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -2979,6 +3245,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -3114,6 +3389,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.11", + "ryu", + "serde", +] + [[package]] name = "serde_with" version = "3.7.0" @@ -3207,6 +3494,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shared_child" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -3253,6 +3550,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "soup2" version = "0.2.1" @@ -3362,6 +3669,33 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "system-deps" version = "5.0.0" @@ -3470,6 +3804,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f078117725e36d55d29fafcbb4b1e909073807ca328ae8deb8c0b3843aac0fed" dependencies = [ "anyhow", + "bytes", "cocoa", "dirs-next", "dunce", @@ -3483,18 +3818,22 @@ dependencies = [ "heck 0.4.1", "http", "ignore", + "indexmap 1.9.3", "objc", "once_cell", "open", + "os_pipe", "percent-encoding", "rand 0.8.5", "raw-window-handle", "regex", + "reqwest", "semver", "serde", "serde_json", "serde_repr", "serialize-to-javascript", + "shared_child", "state", "tar", "tauri-macros", @@ -3775,8 +4114,36 @@ checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", + "libc", + "mio", "num_cpus", "pin-project-lite", + "socket2 0.5.7", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", ] [[package]] @@ -3847,6 +4214,12 @@ dependencies = [ "winnow 0.6.5", ] +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" @@ -3897,7 +4270,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", - "nu-ansi-term", + "nu-ansi-term 0.46.0", "once_cell", "regex", "sharded-slab", @@ -3917,6 +4290,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.17.0" @@ -3994,6 +4373,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version-compare" version = "0.0.11" @@ -4048,6 +4433,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -4085,6 +4479,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -4114,6 +4520,29 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webkit2gtk" version = "0.18.2" @@ -4546,6 +4975,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "winreg" version = "0.52.0" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index fad6011..006b3ad 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -9,11 +9,14 @@ authors = ["Thorsten Sommer"] tauri-build = { version = "1.5", features = [] } [dependencies] -tauri = { version = "1.6", features = [ "path-all", "shell-open"] } +tauri = { version = "1.6", features = [ "http-all", "shell-sidecar", "path-all", "shell-open"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" keyring = "2" arboard = "3.4.0" +tokio = "1.37.0" +flexi_logger = "0.28" +log = "0.4" [features] # this feature is used for production builds or when `devPath` points to the filesystem diff --git a/runtime/src/main.rs b/runtime/src/main.rs index 7991690..b9486cc 100644 --- a/runtime/src/main.rs +++ b/runtime/src/main.rs @@ -1,30 +1,230 @@ // Prevents an additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +extern crate core; + +use std::net::TcpListener; +use std::sync::{Arc, Mutex}; + use arboard::Clipboard; use keyring::Entry; use serde::Serialize; +use tauri::{Manager, Url, Window, WindowUrl}; +use tauri::api::process::{Command, CommandChild, CommandEvent}; +use tauri::utils::config::AppUrl; +use tokio::time; +use flexi_logger::{AdaptiveFormat, Logger}; +use log::{debug, error, info, warn}; fn main() { + + Logger::try_with_str("debug").expect("Cannot create logging") + .log_to_stdout() + .adaptive_format_for_stdout(AdaptiveFormat::Detailed) + .start().expect("Cannot start logging"); + + if is_dev() { + warn!("Running in development mode."); + } else { + info!("Running in production mode."); + } + + let port = match is_dev() { + true => 5000, + false => get_available_port().unwrap(), + }; + + let url = match Url::parse(format!("http://localhost:{port}").as_str()) + { + Ok(url) => url, + Err(msg) => { + error!("Error while parsing URL: {msg}"); + return; + } + }; + + let app_url = AppUrl::Url(WindowUrl::External(url.clone())); + let app_url_log = app_url.clone(); + info!("Try to start the .NET server on {app_url_log}..."); + + // Arc for the server process to stop it later: + let server: Arc>> = Arc::new(Mutex::new(None)); + let server_spawn_clone = server.clone(); + + // Channel to communicate with the server process: + let (sender, mut receiver) = tauri::async_runtime::channel(100); + + if is_prod() { + tauri::async_runtime::spawn(async move { + let (mut rx, child) = Command::new_sidecar("mindworkAIStudio") + .expect("Failed to create sidecar") + .args([format!("{port}").as_str()]) + .spawn() + .expect("Failed to spawn .NET server process."); + + let server_pid = child.pid(); + debug!(".NET server process started with PID={server_pid}."); + + // Save the server process to stop it later: + *server_spawn_clone.lock().unwrap() = Some(child); + + info!("Waiting for .NET server to boot..."); + while let Some(CommandEvent::Stdout(line)) = rx.recv().await { + let line_lower = line.to_lowercase(); + let line_cleared = line_lower.trim(); + match line_cleared + { + "rust/tauri server started" => _ = sender.send(ServerEvent::Started).await, + + _ if line_cleared.contains("fail") || line_cleared.contains("error") || line_cleared.contains("exception") => _ = sender.send(ServerEvent::Error(line)).await, + _ if line_cleared.contains("warn") => _ = sender.send(ServerEvent::Warning(line)).await, + _ if line_cleared.contains("404") => _ = sender.send(ServerEvent::NotFound(line)).await, + _ => (), + } + } + + let sending_stop_result = sender.send(ServerEvent::Stopped).await; + match sending_stop_result { + Ok(_) => (), + Err(e) => error!("Was not able to send the server stop message: {e}."), + } + }); + } + + let main_window: Arc>> = Arc::new(Mutex::new(None)); + let main_window_spawn_clone = main_window.clone(); + let server_receive_clone = server.clone(); + + // Create a thread to handle server events: + tauri::async_runtime::spawn(async move { + loop { + match receiver.recv().await { + Some(ServerEvent::Started) => { + info!("The .NET server was booted successfully."); + + // Try to get the main window. If it is not available yet, wait for it: + let mut main_window_ready = false; + let mut main_window_status_reported = false; + while !main_window_ready + { + main_window_ready = { + let main_window = main_window_spawn_clone.lock().unwrap(); + main_window.is_some() + }; + + if !main_window_ready { + if !main_window_status_reported { + info!("Waiting for main window to be ready, because .NET was faster than Tauri."); + main_window_status_reported = true; + } + + time::sleep(time::Duration::from_millis(100)).await; + } + } + + let main_window = main_window_spawn_clone.lock().unwrap(); + let js_location_change = format!("window.location = '{url}';"); + let location_change_result = main_window.as_ref().unwrap().eval(js_location_change.as_str()); + match location_change_result { + Ok(_) => info!("Location was changed to {url}."), + Err(e) => error!("Failed to change location to {url}: {e}."), + } + }, + + Some(ServerEvent::NotFound(line)) => { + warn!("The .NET server issued a 404 error: {line}."); + }, + + Some(ServerEvent::Warning(line)) => { + warn!("The .NET server issued a warning: {line}."); + }, + + Some(ServerEvent::Error(line)) => { + error!("The .NET server issued an error: {line}."); + }, + + Some(ServerEvent::Stopped) => { + warn!("The .NET server was stopped."); + *server_receive_clone.lock().unwrap() = None; + }, + + None => { + debug!("Server event channel was closed."); + break; + }, + } + } + }); + + info!("Starting Tauri app..."); tauri::Builder::default() + .setup(move |app| { + let window = app.get_window("main").expect("Failed to get main window."); + *main_window.lock().unwrap() = Some(window); + Ok(()) + }) .invoke_handler(tauri::generate_handler![store_secret, get_secret, delete_secret, set_clipboard]) .run(tauri::generate_context!()) - .expect("error while running tauri application"); + .expect("Error while running Tauri application"); + + info!("Tauri app was stopped."); + if is_prod() { + info!("Try to stop the .NET server as well..."); + if let Some(server_process) = server.lock().unwrap().take() { + let server_kill_result = server_process.kill(); + match server_kill_result { + Ok(_) => info!("The .NET server process was stopped."), + Err(e) => error!("Failed to stop the .NET server process: {e}."), + } + } else { + warn!("The .NET server process was not started or already stopped."); + } + } +} + +// Enum for server events: +enum ServerEvent { + Started, + NotFound(String), + Warning(String), + Error(String), + Stopped, +} + +pub fn is_dev() -> bool { + cfg!(dev) +} + +pub fn is_prod() -> bool { + !is_dev() +} + +fn get_available_port() -> Option { + TcpListener::bind(("127.0.0.1", 0)) + .map(|listener| listener.local_addr().unwrap().port()) + .ok() } #[tauri::command] fn store_secret(destination: String, user_name: String, secret: String) -> StoreSecretResponse { - let entry = Entry::new(&format!("mindwork-ai-studio::{}", destination), user_name.as_str()).unwrap(); + let service = format!("mindwork-ai-studio::{}", destination); + let entry = Entry::new(service.as_str(), user_name.as_str()).unwrap(); let result = entry.set_password(secret.as_str()); match result { - Ok(_) => StoreSecretResponse { - success: true, - issue: String::from(""), + Ok(_) => { + info!("Secret for {service} and user {user_name} was stored successfully."); + StoreSecretResponse { + success: true, + issue: String::from(""), + } }, - Err(e) => StoreSecretResponse { - success: false, - issue: e.to_string(), + Err(e) => { + error!("Failed to store secret for {service} and user {user_name}: {e}."); + StoreSecretResponse { + success: false, + issue: e.to_string(), + } }, } } @@ -37,19 +237,26 @@ struct StoreSecretResponse { #[tauri::command] fn get_secret(destination: String, user_name: String) -> RequestedSecret { - let entry = Entry::new(&format!("mindwork-ai-studio::{}", destination), user_name.as_str()).unwrap(); + let service = format!("mindwork-ai-studio::{}", destination); + let entry = Entry::new(service.as_str(), user_name.as_str()).unwrap(); let secret = entry.get_password(); match secret { - Ok(s) => RequestedSecret { - success: true, - secret: s, - issue: String::from(""), + Ok(s) => { + info!("Secret for {service} and user {user_name} was retrieved successfully."); + RequestedSecret { + success: true, + secret: s, + issue: String::from(""), + } }, - Err(e) => RequestedSecret { - success: false, - secret: String::from(""), - issue: e.to_string(), + Err(e) => { + error!("Failed to retrieve secret for {service} and user {user_name}: {e}."); + RequestedSecret { + success: false, + secret: String::from(""), + issue: e.to_string(), + } }, } } @@ -63,17 +270,24 @@ struct RequestedSecret { #[tauri::command] fn delete_secret(destination: String, user_name: String) -> DeleteSecretResponse { - let entry = Entry::new(&format!("mindwork-ai-studio::{}", destination), user_name.as_str()).unwrap(); + let service = format!("mindwork-ai-studio::{}", destination); + let entry = Entry::new(service.as_str(), user_name.as_str()).unwrap(); let result = entry.delete_password(); match result { - Ok(_) => DeleteSecretResponse { - success: true, - issue: String::from(""), + Ok(_) => { + warn!("Secret for {service} and user {user_name} was deleted successfully."); + DeleteSecretResponse { + success: true, + issue: String::from(""), + } }, - Err(e) => DeleteSecretResponse { - success: false, - issue: e.to_string(), + Err(e) => { + error!("Failed to delete secret for {service} and user {user_name}: {e}."); + DeleteSecretResponse { + success: false, + issue: e.to_string(), + } }, } } @@ -89,22 +303,31 @@ fn set_clipboard(text: String) -> SetClipboardResponse { let clipboard_result = Clipboard::new(); let mut clipboard = match clipboard_result { Ok(clipboard) => clipboard, - Err(e) => return SetClipboardResponse { - success: false, - issue: e.to_string(), + Err(e) => { + error!("Failed to get the clipboard instance: {e}."); + return SetClipboardResponse { + success: false, + issue: e.to_string(), + } }, }; let set_text_result = clipboard.set_text(text); match set_text_result { - Ok(_) => SetClipboardResponse { - success: true, - issue: String::from(""), + Ok(_) => { + debug!("Text was set to the clipboard successfully."); + SetClipboardResponse { + success: true, + issue: String::from(""), + } }, - Err(e) => SetClipboardResponse { - success: false, - issue: e.to_string(), + Err(e) => { + error!("Failed to set text to the clipboard: {e}."); + SetClipboardResponse { + success: false, + issue: e.to_string(), + } }, } } diff --git a/runtime/tauri.conf.json b/runtime/tauri.conf.json index e77beaa..1595fd7 100644 --- a/runtime/tauri.conf.json +++ b/runtime/tauri.conf.json @@ -12,11 +12,26 @@ "allowlist": { "all": false, "shell": { + "sidecar": true, "all": false, - "open": true + "open": true, + "scope": [ + { + "name": "../app/MindWork AI Studio/bin/dist/mindworkAIStudio", + "sidecar": true, + "args": true + } + ] }, "path": { "all": true + }, + "http" : { + "all": true, + "request": true, + "scope": [ + "http://localhost" + ] } }, "windows": [ @@ -29,12 +44,19 @@ } ], "security": { - "csp": null + "csp": null, + "dangerousUseHttpScheme": true }, "bundle": { "active": true, "targets": "all", "identifier": "org.tsommer.mindwork-ai-studio", + "externalBin": [ + "../app/MindWork AI Studio/bin/dist/mindworkAIStudio" + ], + "macOS": { + "exceptionDomain": "localhost" + }, "icon": [ "icons/32x32.png", "icons/128x128.png",