Files
friendolls-desktop/src-tauri/src/services/ws/client.rs

118 lines
3.4 KiB
Rust

use std::time::Duration;
use rust_socketio::ClientBuilder;
use tauri::async_runtime;
use tokio::time::sleep;
use tracing::{error, info};
use crate::{
lock_r, lock_w,
services::{auth::get_access_token, client_config::AppConfig},
state::FDOLL,
};
use super::handlers;
pub async fn establish_websocket_connection() {
const MAX_ATTEMPTS: u8 = 5;
const BACKOFF: Duration = Duration::from_millis(300);
for _attempt in 1..=MAX_ATTEMPTS {
if get_access_token().await.is_some() {
if init_ws_client().await {
return; // Success
} else {
// Connection failed, trigger disaster recovery
crate::services::session::handle_disastrous_failure(
Some("WebSocket connection failed. Please check your network and try again.".to_string())
).await;
return;
}
}
sleep(BACKOFF).await;
}
// If we exhausted retries without valid token
crate::services::session::handle_disastrous_failure(
Some("Failed to authenticate. Please restart and sign in again.".to_string())
).await;
}
pub async fn init_ws_client() -> bool {
let app_config = {
let guard = lock_r!(FDOLL);
guard.app_config.clone()
};
match build_ws_client(&app_config).await {
Ok(ws_client) => {
let mut guard = lock_w!(FDOLL);
if let Some(clients) = guard.network.clients.as_mut() {
clients.ws_client = Some(ws_client);
clients.is_ws_initialized = false; // wait for initialized event
}
true
}
Err(e) => {
error!("Failed to initialize WebSocket client: {}", e);
clear_ws_client().await;
false
}
}
}
pub async fn clear_ws_client() {
let mut guard = lock_w!(FDOLL);
if let Some(clients) = guard.network.clients.as_mut() {
clients.ws_client = None;
clients.is_ws_initialized = false;
clients.ws_emit_failures = 0;
}
}
pub async fn build_ws_client(
app_config: &AppConfig,
) -> Result<rust_socketio::client::Client, String> {
// Always fetch a fresh/valid token (refreshing if needed)
let token = match crate::services::auth::get_access_token().await {
Some(t) => t,
None => return Err("No access token available for WebSocket connection".to_string()),
};
let api_base_url = app_config
.api_base_url
.clone()
.ok_or("Missing API base URL")?;
let client_result = async_runtime::spawn_blocking(move || {
let builder = ClientBuilder::new(api_base_url)
.namespace("/")
.auth(serde_json::json!({ "token": token }));
let builder_with_handlers = handlers::register_event_handlers(builder);
builder_with_handlers.connect()
})
.await;
match client_result {
Ok(connect_result) => match connect_result {
Ok(c) => {
info!("WebSocket client connected successfully");
Ok(c)
}
Err(e) => {
let err_msg = format!("Failed to connect WebSocket: {:?}", e);
error!("{}", err_msg);
Err(err_msg)
}
},
Err(e) => {
let err_msg = format!("Failed to spawn blocking task: {:?}", e);
error!("{}", err_msg);
Err(err_msg)
}
}
}