Custom updater code

This commit is contained in:
Gregory Schier
2023-11-12 21:16:33 -08:00
parent 758154fa14
commit dfc01d51ca
3 changed files with 128 additions and 42 deletions

View File

@@ -1,6 +1,6 @@
#![cfg_attr( #![cfg_attr(
all(not(debug_assertions), target_os = "windows"), all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows" windows_subsystem = "windows"
)] )]
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@@ -15,27 +15,28 @@ use std::process::exit;
use base64::Engine; use base64::Engine;
use fern::colors::ColoredLevelConfig; use fern::colors::ColoredLevelConfig;
use http::header::{HeaderName, ACCEPT, USER_AGENT};
use http::{HeaderMap, HeaderValue, Method}; use http::{HeaderMap, HeaderValue, Method};
use log::{info, warn}; use http::header::{ACCEPT, HeaderName, USER_AGENT};
use log::{debug, error, info, warn};
use rand::random; use rand::random;
use reqwest::redirect::Policy; use reqwest::redirect::Policy;
use serde::Serialize; use serde::Serialize;
use sqlx::{Pool, Sqlite, SqlitePool};
use sqlx::migrate::Migrator; use sqlx::migrate::Migrator;
use sqlx::types::Json; use sqlx::types::Json;
use sqlx::{Pool, Sqlite, SqlitePool};
#[cfg(target_os = "macos")]
use tauri::TitleBarStyle;
use tauri::{AppHandle, Menu, RunEvent, State, Submenu, Window, WindowUrl, Wry}; use tauri::{AppHandle, Menu, RunEvent, State, Submenu, Window, WindowUrl, Wry};
use tauri::{CustomMenuItem, Manager, WindowEvent}; use tauri::{CustomMenuItem, Manager, WindowEvent};
#[cfg(target_os = "macos")]
use tauri::TitleBarStyle;
use tauri_plugin_log::{fern, LogTarget}; use tauri_plugin_log::{fern, LogTarget};
use tauri_plugin_window_state::{StateFlags, WindowExt}; use tauri_plugin_window_state::{StateFlags, WindowExt};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use window_ext::TrafficLightWindowExt; use window_ext::TrafficLightWindowExt;
use crate::analytics::{track_event, AnalyticsAction, AnalyticsResource}; use crate::analytics::{AnalyticsAction, AnalyticsResource, track_event};
use crate::plugin::{ImportResources, ImportResult}; use crate::plugin::{ImportResources, ImportResult};
use crate::updates::YaakUpdater;
mod analytics; mod analytics;
mod models; mod models;
@@ -43,6 +44,7 @@ mod plugin;
mod render; mod render;
mod window_ext; mod window_ext;
mod window_menu; mod window_menu;
mod updates;
#[derive(serde::Serialize)] #[derive(serde::Serialize)]
pub struct CustomResponse { pub struct CustomResponse {
@@ -189,7 +191,7 @@ async fn actually_send_request(
let empty_value = &serde_json::to_value("").unwrap(); let empty_value = &serde_json::to_value("").unwrap();
let b = request.body.0; let b = request.body.0;
if t == "basic" { if b.contains_key("text") {
let raw_text = b.get("text").unwrap_or(empty_value).as_str().unwrap_or(""); let raw_text = b.get("text").unwrap_or(empty_value).as_str().unwrap_or("");
let body = render::render(raw_text, &workspace, environment_ref); let body = render::render(raw_text, &workspace, environment_ref);
request_builder = request_builder.body(body); request_builder = request_builder.body(body);
@@ -283,7 +285,7 @@ async fn import_data(
plugin_name, plugin_name,
file_paths.first().unwrap(), file_paths.first().unwrap(),
) )
.await .await
{ {
result = Some(r); result = Some(r);
break; break;
@@ -446,8 +448,8 @@ async fn create_workspace(
..Default::default() ..Default::default()
}, },
) )
.await .await
.expect("Failed to create Workspace"); .expect("Failed to create Workspace");
emit_and_return(&window, "created_model", created_workspace) emit_and_return(&window, "created_model", created_workspace)
} }
@@ -470,8 +472,8 @@ async fn create_environment(
..Default::default() ..Default::default()
}, },
) )
.await .await
.expect("Failed to create environment"); .expect("Failed to create environment");
emit_and_return(&window, "created_model", created_environment) emit_and_return(&window, "created_model", created_environment)
} }
@@ -497,8 +499,8 @@ async fn create_request(
..Default::default() ..Default::default()
}, },
) )
.await .await
.expect("Failed to create request"); .expect("Failed to create request");
emit_and_return(&window, "created_model", created_request) emit_and_return(&window, "created_model", created_request)
} }
@@ -603,8 +605,8 @@ async fn create_folder(
..Default::default() ..Default::default()
}, },
) )
.await .await
.expect("Failed to create folder"); .expect("Failed to create folder");
emit_and_return(&window, "created_model", created_request) emit_and_return(&window, "created_model", created_request)
} }
@@ -769,8 +771,8 @@ async fn list_workspaces(
..Default::default() ..Default::default()
}, },
) )
.await .await
.expect("Failed to create Workspace"); .expect("Failed to create Workspace");
Ok(vec![workspace]) Ok(vec![workspace])
} else { } else {
Ok(workspaces) Ok(workspaces)
@@ -796,6 +798,12 @@ async fn delete_workspace(
emit_and_return(&window, "deleted_model", workspace) emit_and_return(&window, "deleted_model", workspace)
} }
#[tauri::command]
async fn check_for_updates(app_handle: AppHandle<Wry>, yaak_updater: State<'_, Mutex<YaakUpdater>>,
) -> Result<(), String> {
yaak_updater.lock().await.check(&app_handle).await.map_err(|e| e.to_string())
}
fn main() { fn main() {
tauri::Builder::default() tauri::Builder::default()
.plugin( .plugin(
@@ -836,12 +844,17 @@ fn main() {
.expect("Failed to migrate database"); .expect("Failed to migrate database");
app.manage(m); app.manage(m);
let yaak_updater = YaakUpdater::new();
app.manage(Mutex::new(yaak_updater));
let _ = models::cancel_pending_responses(&pool).await; let _ = models::cancel_pending_responses(&pool).await;
Ok(()) Ok(())
}) })
}) })
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
check_for_updates,
create_environment, create_environment,
create_folder, create_folder,
create_request, create_request,
@@ -877,18 +890,50 @@ fn main() {
.build(tauri::generate_context!()) .build(tauri::generate_context!())
.expect("error while running tauri application") .expect("error while running tauri application")
.run(|app_handle, event| { .run(|app_handle, event| {
if let RunEvent::Ready = event { match event {
let w = create_window(app_handle, None); RunEvent::Updater(updater_event) => match updater_event {
w.restore_state(StateFlags::all()) tauri::UpdaterEvent::Pending => {
.expect("Failed to restore window state"); debug!("Updater pending");
}
tauri::UpdaterEvent::Updated => {
debug!("Updater updated");
}
tauri::UpdaterEvent::UpdateAvailable {
body,
version,
date: _,
} => {
debug!("Updater update available body={} version={}", body, version);
}
tauri::UpdaterEvent::Downloaded => {
debug!("Updater downloaded");
}
tauri::UpdaterEvent::Error(e) => {
error!("Updater error: {:?}", e);
}
_ => {}
},
RunEvent::Ready => {
let w = create_window(app_handle, None);
w.restore_state(StateFlags::all())
.expect("Failed to restore window state");
track_event( track_event(
app_handle, app_handle,
AnalyticsResource::App, AnalyticsResource::App,
AnalyticsAction::Launch, AnalyticsAction::Launch,
None, None,
); );
} }
RunEvent::WindowEvent { label, event: WindowEvent::Focused(true), .. } => {
let h = app_handle.clone();
tauri::async_runtime::spawn(async move {
let val: State<'_, Mutex<YaakUpdater>> = h.state();
_ = val.lock().await.check(&h).await;
});
}
_ => {}
};
}); });
} }
@@ -922,16 +967,16 @@ fn create_window(handle: &AppHandle<Wry>, url: Option<&str>) -> Window<Wry> {
window_id, window_id,
WindowUrl::App(url.unwrap_or_default().into()), WindowUrl::App(url.unwrap_or_default().into()),
) )
.menu(app_menu) .menu(app_menu)
.fullscreen(false) .fullscreen(false)
.resizable(true) .resizable(true)
.inner_size(1100.0, 600.0) .inner_size(1100.0, 600.0)
.position( .position(
// Randomly offset so windows don't stack exactly // Randomly offset so windows don't stack exactly
100.0 + random::<f64>() * 30.0, 100.0 + random::<f64>() * 30.0,
100.0 + random::<f64>() * 30.0, 100.0 + random::<f64>() * 30.0,
) )
.title(handle.package_info().name.to_string()); .title(handle.package_info().name.to_string());
// Add macOS-only things // Add macOS-only things
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]

41
src-tauri/src/updates.rs Normal file
View File

@@ -0,0 +1,41 @@
use std::time::SystemTime;
use tauri::{AppHandle, updater, Window, Wry};
use tauri::api::dialog;
// Check for updates every 3 hours
const MAX_UPDATE_CHECK_SECONDS: u64 = 3600 * 3;
// Create updater struct
pub struct YaakUpdater {
last_update_check: SystemTime,
}
impl YaakUpdater {
pub fn new() -> Self {
Self {
last_update_check: SystemTime::UNIX_EPOCH,
}
}
pub async fn check(&mut self, app_handle: &AppHandle<Wry>) -> Result<(), updater::Error> {
if self.last_update_check.elapsed().unwrap().as_secs() < MAX_UPDATE_CHECK_SECONDS {
return Ok(());
}
self.last_update_check = SystemTime::now();
match app_handle.updater().check().await {
Ok(update) => {
if dialog::blocking::ask(
None::<&Window>,
"Update available",
"An update is available. Would you like to download and install it now?",
) {
_ = update.download_and_install().await;
}
Ok(())
}
Err(updater::Error::UpToDate) => Ok(()),
Err(e) => Err(e),
}
}
}

View File

@@ -103,7 +103,7 @@
"security": {}, "security": {},
"updater": { "updater": {
"active": true, "active": true,
"dialog": true, "dialog": false,
"endpoints": [ "endpoints": [
"https://update.yaak.app/check/{{target}}/{{arch}}/{{current_version}}" "https://update.yaak.app/check/{{target}}/{{arch}}/{{current_version}}"
], ],