I have tauri apps that also running a background http services. I want to make this app controllable via http on localhost, but it seems like tauri wont allow me to have that. Here is my main.rs code
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use actix_web::web;
use actix_web::{get, App, HttpResponse, HttpServer, Responder};
use std::sync::{Once, ONCE_INIT};
use tauri::Manager;
use serde::Deserialize;
static INIT: Once = ONCE_INIT;
static mut APP: Option<tauri::App> = None;
#[derive(Deserialize)]
struct AppSession {
action: String,
}
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
#[get("/actions")]
async fn handle_actions(query: web::Query<AppSession>) -> impl Responder {
let action = &query.action;
unsafe {
if let Some(app) = APP.as_ref() {
match action.as_str() {
"session_end" => {
app.get_window("main").unwrap().show().unwrap();
HttpResponse::Ok().body("Session ending, app shown")
}
"session_start" => {
app.get_window("main").unwrap().hide().unwrap();
HttpResponse::Ok().body("Session starting, app hidden")
}
_ => HttpResponse::Ok().body("Unknown action"),
}
} else {
HttpResponse::InternalServerError().body("App instance not available")
}
}
}
fn main() {
let app = tauri::Builder::default()
.plugin(tauri_plugin_websocket::init())
.setup(|app| {
tauri::async_runtime::spawn(
HttpServer::new(move || {
let app = app.clone();
App::new()
.service(hello)
.service(handle_actions)
.app_data(app)
})
.bind(("127.0.0.1", 8080))
.expect("Failed to bind address")
.run(),
);
Ok(())
})
.build(tauri::generate_context!())
.expect("error while running tauri application");
INIT.call_once(|| {
// Initialize your tauri::App instance here
unsafe {
APP = Some(app);
}
});
app.run(|_, _| {})
}
I tried to use Arc value so I can use the tauri app between thread but it seems not doable, since the code always errored that tauri:App doesn't implement the trait Send
static INIT: std::sync::Once = std::sync::Once::new();
static mut APP: Option<Arc<Mutex<Option<tauri::App>>>> = None;
Right now using the above full code, It still errored in line
let app = app.clone()
Saying that
no method named `clone` found for mutable reference `&mut tauri::App` in the current scope
method not found in `&mut App`
How to resolve this issue?
I ended up using OneLock
with Window to initialize global variables. This will store the window reference and can be used to controls Tauri window instances. Here is the complete code :
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::sync::OnceLock;
use actix_web::web;
use actix_web::{App, HttpResponse, HttpServer, Responder};
use serde::Deserialize;
use tauri::{Manager, Window};
static WINDOW: OnceLock<Window> = OnceLock::new();
#[derive(Deserialize)]
struct AppSession {
event_type: String,
}
#[derive(Clone, serde::Serialize)]
struct Payload {
message: String,
}
fn main() {
let app = tauri::Builder::default()
.plugin(tauri_plugin_websocket::init())
.setup(|app| {
let window = app.get_window("main").unwrap();
_ = WINDOW.set(window);
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
async fn handle_actions(query: web::Query<AppSession>) -> impl Responder {
let action = &query.event_type;
match action.as_str() {
"session_end" => {
WINDOW.get()
.expect("Window is available")
.emit("session_end", Payload { message: "Sesion End".into() })
.unwrap();
HttpResponse::Ok().body("Session ending, app shown")
}
_ => HttpResponse::Ok().body("Unknown action"),
}
}
tauri::async_runtime::spawn(
HttpServer::new(move || {
App::new()
.route("/", web::get().to(hello))
.route("/actions", web::get().to(handle_actions))
})
.bind(("127.0.0.1", 8081))
.expect("Failed to bind address")
.run(),
);
Ok(())
})
.build(tauri::generate_context!())
.expect("error while running tauri application");
app.run(|_, _| {})
}