Added update system to the app (#13)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2024-06-30 15:26:28 +02:00
1 parent 50a03e6c9d
commit d6e80a4563
33 files changed
+786 -81

No files matched your search

+2 -1
View File
@@ -2313,12 +2313,13 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mindwork-ai-studio"
version = "0.5.2"
version = "0.6.0"
dependencies = [
"arboard",
"flexi_logger",
"keyring",
"log",
"once_cell",
"reqwest 0.12.4",
"serde",
"serde_json",
+2 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "mindwork-ai-studio"
version = "0.5.2"
version = "0.6.0"
edition = "2021"
description = "MindWork AI Studio"
authors = ["Thorsten Sommer"]
@@ -18,6 +18,7 @@ arboard = "3.4.0"
tokio = "1.37.0"
flexi_logger = "0.28"
log = "0.4"
once_cell = "1.19.0"
[target.'cfg(target_os = "linux")'.dependencies]
# See issue https://github.com/tauri-apps/tauri/issues/4470
+175 -19
View File
@@ -5,6 +5,7 @@ extern crate core;
use std::net::TcpListener;
use std::sync::{Arc, Mutex};
use once_cell::sync::Lazy;
use arboard::Clipboard;
use keyring::Entry;
@@ -15,6 +16,11 @@ use tauri::utils::config::AppUrl;
use tokio::time;
use flexi_logger::{AdaptiveFormat, Logger};
use log::{debug, error, info, warn};
use tauri::updater::UpdateResponse;
static SERVER: Lazy<Arc<Mutex<Option<CommandChild>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
static MAIN_WINDOW: Lazy<Mutex<Option<Window>>> = Lazy::new(|| Mutex::new(None));
static CHECK_UPDATE_RESPONSE: Lazy<Mutex<Option<UpdateResponse<tauri::Wry>>>> = Lazy::new(|| Mutex::new(None));
fn main() {
@@ -30,7 +36,14 @@ fn main() {
let tauri_version = metadata_lines.next().unwrap();
let app_commit_hash = metadata_lines.next().unwrap();
Logger::try_with_str("debug").expect("Cannot create logging")
// Set the log level according to the environment:
// In debug mode, the log level is set to debug, in release mode to info.
let log_level = match is_dev() {
true => "debug",
false => "info",
};
Logger::try_with_str(log_level).expect("Cannot create logging")
.log_to_stdout()
.adaptive_format_for_stdout(AdaptiveFormat::Detailed)
.start().expect("Cannot start logging");
@@ -69,8 +82,7 @@ fn main() {
info!("Try to start the .NET server on {app_url_log}...");
// Arc for the server process to stop it later:
let server: Arc<Mutex<Option<CommandChild>>> = Arc::new(Mutex::new(None));
let server_spawn_clone = server.clone();
let server_spawn_clone = SERVER.clone();
// Channel to communicate with the server process:
let (sender, mut receiver) = tauri::async_runtime::channel(100);
@@ -114,9 +126,8 @@ fn main() {
warn!("Running in development mode, no .NET server will be started.");
}
let main_window: Arc<Mutex<Option<Window>>> = Arc::new(Mutex::new(None));
let main_window_spawn_clone = main_window.clone();
let server_receive_clone = server.clone();
let main_window_spawn_clone = &MAIN_WINDOW;
let server_receive_clone = SERVER.clone();
// Create a thread to handle server events:
tauri::async_runtime::spawn(async move {
@@ -181,29 +192,93 @@ fn main() {
});
info!("Starting Tauri app...");
tauri::Builder::default()
let 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);
*MAIN_WINDOW.lock().unwrap() = Some(window);
Ok(())
})
.plugin(tauri_plugin_window_state::Builder::default().build())
.invoke_handler(tauri::generate_handler![store_secret, get_secret, delete_secret, set_clipboard])
.run(tauri::generate_context!())
.invoke_handler(tauri::generate_handler![
store_secret, get_secret, delete_secret, set_clipboard,
check_for_update, install_update
])
.build(tauri::generate_context!())
.expect("Error while running Tauri application");
app.run(|app_handle, event| match event {
tauri::RunEvent::WindowEvent { event, label, .. } => {
match event {
tauri::WindowEvent::CloseRequested { .. } => {
warn!("Window '{label}': close was requested.");
}
tauri::WindowEvent::Destroyed => {
warn!("Window '{label}': was destroyed.");
}
tauri::WindowEvent::FileDrop(files) => {
info!("Window '{label}': files were dropped: {files:?}");
}
_ => (),
}
}
tauri::RunEvent::Updater(updater_event) => {
match updater_event {
tauri::UpdaterEvent::UpdateAvailable { body, date, version } => {
let body_len = body.len();
info!("Updater: update available: body size={body_len} time={date:?} version={version}");
}
tauri::UpdaterEvent::Pending => {
info!("Updater: update is pending!");
}
tauri::UpdaterEvent::DownloadProgress { chunk_length, content_length } => {
info!("Updater: downloaded {} of {:?}", chunk_length, content_length);
}
tauri::UpdaterEvent::Downloaded => {
info!("Updater: update has been downloaded!");
warn!("Try to stop the .NET server now...");
stop_server();
}
tauri::UpdaterEvent::Updated => {
info!("Updater: app has been updated");
warn!("Try to restart the app now...");
app_handle.restart();
}
tauri::UpdaterEvent::AlreadyUpToDate => {
info!("Updater: app is already up to date");
}
tauri::UpdaterEvent::Error(error) => {
warn!("Updater: failed to update: {error}");
}
}
}
tauri::RunEvent::ExitRequested { .. } => {
warn!("Run event: exit was requested.");
}
tauri::RunEvent::Ready => {
info!("Run event: Tauri app is ready.");
}
_ => {}
});
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.");
}
stop_server();
}
}
@@ -230,6 +305,87 @@ fn get_available_port() -> Option<u16> {
.ok()
}
fn stop_server() {
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.");
}
}
#[tauri::command]
async fn check_for_update() -> CheckUpdateResponse {
let app_handle = MAIN_WINDOW.lock().unwrap().as_ref().unwrap().app_handle();
tauri::async_runtime::spawn(async move {
let response = app_handle.updater().check().await;
match response {
Ok(update_response) => match update_response.is_update_available() {
true => {
*CHECK_UPDATE_RESPONSE.lock().unwrap() = Some(update_response.clone());
let new_version = update_response.latest_version();
info!("Updater: update to version '{new_version}' is available.");
let changelog = update_response.body();
CheckUpdateResponse {
update_is_available: true,
error: false,
new_version: new_version.to_string(),
changelog: match changelog {
Some(c) => c.to_string(),
None => String::from(""),
},
}
},
false => {
info!("Updater: no updates available.");
CheckUpdateResponse {
update_is_available: false,
error: false,
new_version: String::from(""),
changelog: String::from(""),
}
},
},
Err(e) => {
warn!("Failed to check updater: {e}.");
CheckUpdateResponse {
update_is_available: false,
error: true,
new_version: String::from(""),
changelog: String::from(""),
}
},
}
}).await.unwrap()
}
#[derive(Serialize)]
struct CheckUpdateResponse {
update_is_available: bool,
error: bool,
new_version: String,
changelog: String,
}
#[tauri::command]
async fn install_update() {
let cloned_response_option = CHECK_UPDATE_RESPONSE.lock().unwrap().clone();
match cloned_response_option {
Some(update_response) => {
update_response.download_and_install().await.unwrap();
},
None => {
error!("Update installer: no update available to install. Did you check for updates first?");
},
}
}
#[tauri::command]
fn store_secret(destination: String, user_name: String, secret: String) -> StoreSecretResponse {
let service = format!("mindwork-ai-studio::{}", destination);
+2 -2
View File
@@ -6,7 +6,7 @@
},
"package": {
"productName": "MindWork AI Studio",
"version": "0.5.2"
"version": "0.6.0"
},
"tauri": {
"allowlist": {
@@ -76,7 +76,7 @@
"endpoints": [
"https://github.com/MindWorkAI/AI-Studio/releases/latest/download/latest.json"
],
"dialog": true,
"dialog": false,
"windows": {
"installMode": "passive"
},