tauri: Replace portmaster plugin with interface

This commit is contained in:
Vladimir Stoilov 2024-04-23 15:48:04 +03:00
parent 338abd6090
commit 7ee1faa68c
No known key found for this signature in database
GPG key ID: 2F190B67A43A81AF
6 changed files with 1906 additions and 18857 deletions

File diff suppressed because it is too large Load diff

View file

@ -37,13 +37,13 @@
"@fortawesome/free-brands-svg-icons": "^6.4.0", "@fortawesome/free-brands-svg-icons": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0",
"@tauri-apps/api": "^2.0.0-beta.3", "@tauri-apps/api": ">=2.0.0-beta.0",
"@tauri-apps/plugin-cli": "^2.0.0-beta.1", "@tauri-apps/plugin-cli": ">=2.0.0-beta.0",
"@tauri-apps/plugin-clipboard-manager": "^2.0.0-alpha.4", "@tauri-apps/plugin-clipboard-manager": ">=2.0.0-beta.0",
"@tauri-apps/plugin-dialog": "^2.0.0-alpha.4", "@tauri-apps/plugin-dialog": ">=2.0.0-beta.0",
"@tauri-apps/plugin-notification": "^2.0.0-alpha.4", "@tauri-apps/plugin-notification": ">=2.0.0-beta.0",
"@tauri-apps/plugin-os": "^2.0.0-alpha.5", "@tauri-apps/plugin-os": ">=2.0.0-beta.0",
"@tauri-apps/plugin-shell": "^2.0.0-alpha.4", "@tauri-apps/plugin-shell": "^2.0.0-beta",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"d3": "^7.8.4", "d3": "^7.8.4",
"data-urls": "^5.0.0", "data-urls": "^5.0.0",
@ -101,4 +101,4 @@
"webpack-ext-reloader": "^1.1.9", "webpack-ext-reloader": "^1.1.9",
"zip-a-folder": "^1.1.5" "zip-a-folder": "^1.1.5"
} }
} }

View file

@ -75,7 +75,7 @@ export class TauriIntegrationService implements IntegrationService {
} }
getAppInfo(info: ProcessInfo): Promise<AppInfo> { getAppInfo(info: ProcessInfo): Promise<AppInfo> {
return asyncInvoke("plugin:portmaster|get_app_info", { return asyncInvoke("get_app_info", {
...info, ...info,
}) })
} }
@ -112,7 +112,7 @@ export class TauriIntegrationService implements IntegrationService {
async shouldShow(): Promise<boolean> { async shouldShow(): Promise<boolean> {
try { try {
const response = await invoke<string>("plugin:portmaster|should_show"); const response = await invoke<string>("should_show");
return response === "show"; return response === "show";
} catch (err) { } catch (err) {
console.error(err); console.error(err);
@ -122,7 +122,7 @@ export class TauriIntegrationService implements IntegrationService {
async shouldHandlePrompts(): Promise<boolean> { async shouldHandlePrompts(): Promise<boolean> {
try { try {
const response = await invoke<string>("plugin:portmaster|should_handle_prompts") const response = await invoke<string>("should_handle_prompts")
return response === "true" return response === "true"
} catch (err) { } catch (err) {
console.error(err); console.error(err);
@ -131,22 +131,22 @@ export class TauriIntegrationService implements IntegrationService {
} }
get_state(key: string): Promise<string> { get_state(key: string): Promise<string> {
return invoke<string>("plugin:portmaster|get_state"); return invoke<string>("get_state");
} }
set_state(key: string, value: string): Promise<void> { set_state(key: string, value: string): Promise<void> {
return invoke<void>("plugin:portmaster|set_state", { return invoke<void>("set_state", {
key, key,
value value
}) })
} }
getServiceManagerStatus(): Promise<ServiceManagerStatus> { getServiceManagerStatus(): Promise<ServiceManagerStatus> {
return asyncInvoke("plugin:portmaster|get_service_manager_status", {}) return asyncInvoke("get_service_manager_status", {})
} }
startService(): Promise<any> { startService(): Promise<any> {
return asyncInvoke("plugin:portmaster|start_service", {}); return asyncInvoke("start_service", {});
} }
onExitRequest(cb: () => void): () => void { onExitRequest(cb: () => void): () => void {

View file

@ -102,16 +102,24 @@ fn main() {
.plugin(tauri_plugin_cli::init()) .plugin(tauri_plugin_cli::init())
// Notification support // Notification support
.plugin(tauri_plugin_notification::init()) .plugin(tauri_plugin_notification::init())
// Our Portmaster Plugin that handles communication between tauri and our angular app. .invoke_handler(tauri::generate_handler![
.plugin(portmaster::init()) portmaster::commands::get_app_info,
portmaster::commands::get_service_manager_status,
portmaster::commands::start_service,
portmaster::commands::get_state,
portmaster::commands::set_state,
portmaster::commands::should_show,
portmaster::commands::should_handle_prompts
])
// Setup the app an any listeners // Setup the app an any listeners
.setup(|app| { .setup(|app| {
setup_tray_menu(app)?; setup_tray_menu(app)?;
portmaster::setup(app.handle().clone());
// Setup the single-instance event listener that will create/focus the main window // Setup the single-instance event listener that will create/focus the main window
// or the splash-screen. // or the splash-screen.
let handle = app.handle().clone(); let handle = app.handle().clone();
app.listen("single-instance", move |_event| { app.listen_any("single-instance", move |_event| {
let _ = window::open_window(&handle); let _ = window::open_window(&handle);
}); });
@ -197,7 +205,12 @@ fn main() {
api.prevent_close(); api.prevent_close();
if let Some(window) = handle.get_webview_window(label.as_str()) { if let Some(window) = handle.get_webview_window(label.as_str()) {
let _ = window.emit("exit-requested", ""); let result = window.emit("exit-requested", "");
if let Err(err) = result {
error!("failed to emit event: {}", err.to_string());
}
} else {
error!("window was None");
} }
} }
_ => {} _ => {}

View file

@ -1,4 +1,4 @@
use super::PortmasterPlugin; use super::PortmasterInterface;
use crate::service::get_service_manager; use crate::service::get_service_manager;
use crate::service::ServiceManager; use crate::service::ServiceManager;
use log::debug; use log::debug;
@ -15,7 +15,7 @@ pub struct Error {
#[tauri::command] #[tauri::command]
pub fn should_show<R: Runtime>( pub fn should_show<R: Runtime>(
_window: Window<R>, _window: Window<R>,
portmaster: State<'_, PortmasterPlugin<R>>, portmaster: State<'_, PortmasterInterface<R>>,
) -> Result { ) -> Result {
if portmaster.get_show_after_bootstrap() { if portmaster.get_show_after_bootstrap() {
debug!("[tauri:rpc:should_show] application should show after bootstrap"); debug!("[tauri:rpc:should_show] application should show after bootstrap");
@ -31,7 +31,7 @@ pub fn should_show<R: Runtime>(
#[tauri::command] #[tauri::command]
pub fn should_handle_prompts<R: Runtime>( pub fn should_handle_prompts<R: Runtime>(
_window: Window<R>, _window: Window<R>,
portmaster: State<'_, PortmasterPlugin<R>>, portmaster: State<'_, PortmasterInterface<R>>,
) -> Result { ) -> Result {
if portmaster.handle_prompts.load(Ordering::Relaxed) { if portmaster.handle_prompts.load(Ordering::Relaxed) {
Ok("true".to_string()) Ok("true".to_string())
@ -43,7 +43,7 @@ pub fn should_handle_prompts<R: Runtime>(
#[tauri::command] #[tauri::command]
pub fn get_state<R: Runtime>( pub fn get_state<R: Runtime>(
_window: Window<R>, _window: Window<R>,
portmaster: State<'_, PortmasterPlugin<R>>, portmaster: State<'_, PortmasterInterface<R>>,
key: String, key: String,
) -> Result { ) -> Result {
let value = portmaster.get_state(key); let value = portmaster.get_state(key);
@ -58,7 +58,7 @@ pub fn get_state<R: Runtime>(
#[tauri::command] #[tauri::command]
pub fn set_state<R: Runtime>( pub fn set_state<R: Runtime>(
_window: Window<R>, _window: Window<R>,
portmaster: State<'_, PortmasterPlugin<R>>, portmaster: State<'_, PortmasterInterface<R>>,
key: String, key: String,
value: String, value: String,
) -> Result { ) -> Result {

View file

@ -13,7 +13,7 @@
/// in the crate root. /// in the crate root.
// The commands module contains tauri commands that are available to Javascript // The commands module contains tauri commands that are available to Javascript
// using the invoke() and our custom invokeAsync() command. // using the invoke() and our custom invokeAsync() command.
mod commands; pub mod commands;
// The websocket module spawns an async function on tauri's runtime that manages // The websocket module spawns an async function on tauri's runtime that manages
// a persistent connection to the Portmaster websocket API and updates the tauri Portmaster // a persistent connection to the Portmaster websocket API and updates the tauri Portmaster
@ -34,10 +34,7 @@ use std::{
use log::{debug, error}; use log::{debug, error};
use serde; use serde;
use std::sync::Mutex; use std::sync::Mutex;
use tauri::{ use tauri::{AppHandle, EventTarget, Manager, Runtime};
plugin::{Builder, TauriPlugin},
AppHandle, Manager, Runtime,
};
pub trait Handler { pub trait Handler {
fn on_connect(&mut self, cli: PortAPI) -> (); fn on_connect(&mut self, cli: PortAPI) -> ();
@ -45,7 +42,7 @@ pub trait Handler {
fn name(&self) -> String; fn name(&self) -> String;
} }
pub struct PortmasterPlugin<R: Runtime> { pub struct PortmasterInterface<R: Runtime> {
#[allow(dead_code)] #[allow(dead_code)]
app: AppHandle<R>, app: AppHandle<R>,
@ -76,7 +73,7 @@ pub struct PortmasterPlugin<R: Runtime> {
should_show_after_bootstrap: AtomicBool, should_show_after_bootstrap: AtomicBool,
} }
impl<R: Runtime> PortmasterPlugin<R> { impl<R: Runtime> PortmasterInterface<R> {
/// Returns a state stored in the portmaster plugin. /// Returns a state stored in the portmaster plugin.
pub fn get_state(&self, key: String) -> Option<String> { pub fn get_state(&self, key: String) -> Option<String> {
let map = self.state.lock(); let map = self.state.lock();
@ -118,7 +115,7 @@ impl<R: Runtime> PortmasterPlugin<R> {
handler.on_connect(api); handler.on_connect(api);
} else { } else {
debug!("not yet connected to Portmaster API, calling on_disconnect()"); debug!("not yet connected to Portmaster API, calling on_disconnect()");
handler.on_disconnect(); handler.on_disconnect();
} }
@ -174,7 +171,7 @@ impl<R: Runtime> PortmasterPlugin<R> {
self.should_show_after_bootstrap.load(Ordering::Relaxed) self.should_show_after_bootstrap.load(Ordering::Relaxed)
} }
/// Tells the angular applicatoin to show the window by emitting an event. /// Tells the angular application to show the window by emitting an event.
/// It calls set_show_after_bootstrap(true) automatically so the application /// It calls set_show_after_bootstrap(true) automatically so the application
/// also shows after bootstrapping. /// also shows after bootstrapping.
pub fn show_window(&self) { pub fn show_window(&self) {
@ -184,8 +181,9 @@ impl<R: Runtime> PortmasterPlugin<R> {
// misses the event below because it's still bootstrapping. // misses the event below because it's still bootstrapping.
self.set_show_after_bootstrap(true); self.set_show_after_bootstrap(true);
// ignore the error here, there's nothing we could do about it anyways. if let Err(err) = self.app.emit("portmaster:show", "") {
let _ = self.app.emit("portmaster:show", ""); error!("failed to emit show event: {}", err.to_string());
}
} }
/// Enables or disables the SPN. /// Enables or disables the SPN.
@ -262,47 +260,32 @@ impl<R: Runtime> PortmasterPlugin<R> {
} }
pub trait PortmasterExt<R: Runtime> { pub trait PortmasterExt<R: Runtime> {
fn portmaster(&self) -> &PortmasterPlugin<R>; fn portmaster(&self) -> &PortmasterInterface<R>;
} }
#[derive(serde::Serialize, serde::Deserialize, Debug)] #[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct Config {} pub struct Config {}
impl<R: Runtime, T: Manager<R>> PortmasterExt<R> for T { impl<R: Runtime, T: Manager<R>> PortmasterExt<R> for T {
fn portmaster(&self) -> &PortmasterPlugin<R> { fn portmaster(&self) -> &PortmasterInterface<R> {
self.state::<PortmasterPlugin<R>>().inner() self.state::<PortmasterInterface<R>>().inner()
} }
} }
pub fn init<R: Runtime>() -> TauriPlugin<R, Option<Config>> { pub fn setup(app: AppHandle) {
Builder::<R, Option<Config>>::new("portmaster") let interface = PortmasterInterface {
.invoke_handler(tauri::generate_handler![ app: app.clone(),
commands::get_app_info, state: Mutex::new(HashMap::new()),
commands::get_service_manager_status, is_reachable: AtomicBool::new(false),
commands::start_service, handlers: Mutex::new(Vec::new()),
commands::get_state, api: Mutex::new(None),
commands::set_state, handle_notifications: AtomicBool::new(false),
commands::should_show, handle_prompts: AtomicBool::new(false),
commands::should_handle_prompts should_show_after_bootstrap: AtomicBool::new(true),
]) };
.setup(|app, _api| {
let plugin = PortmasterPlugin {
app: app.clone(),
state: Mutex::new(HashMap::new()),
is_reachable: AtomicBool::new(false),
handlers: Mutex::new(Vec::new()),
api: Mutex::new(None),
handle_notifications: AtomicBool::new(false),
handle_prompts: AtomicBool::new(false),
should_show_after_bootstrap: AtomicBool::new(true),
};
app.manage(plugin); app.manage(interface);
// fire of the websocket handler // fire of the websocket handler
websocket::start_websocket_thread(app.clone()); websocket::start_websocket_thread(app.clone());
Ok(())
})
.build()
} }