@@ -13,8 +13,11 @@ use serde_json::Value as JsonValue;
1313use serde_json:: from_value;
1414use std:: collections:: HashMap ;
1515use std:: sync:: LazyLock ;
16+ use std:: thread;
17+ use std:: time:: Duration ;
1618use tauri:: { AppHandle , Manager } ;
1719use tauri_plugin_store:: StoreExt ;
20+ use tokio:: runtime;
1821use tokio:: sync:: RwLock ;
1922
2023/// Coco sever list
@@ -312,6 +315,109 @@ pub async fn refresh_all_coco_server_info(app_handle: AppHandle) {
312315 }
313316}
314317
318+ /// Start a background worker that periodically sends heartbeats (`GET /provider/_info`)
319+ /// to the connected Coco servers, checks if they are available and updates the
320+ /// `SearchSourceRegistry` accordingly.
321+ pub ( crate ) fn start_bg_heartbeat_worker ( tauri_app_handle : AppHandle ) {
322+ const THREAD_NAME : & str = "Coco background heartbeat worker" ;
323+ const SLEEP_DURATION : Duration = Duration :: from_secs ( 15 ) ;
324+
325+ let main_closure = || {
326+ let single_thread_rt = runtime:: Builder :: new_current_thread ( )
327+ . enable_all ( )
328+ . build ( )
329+ . unwrap_or_else ( |e| {
330+ panic ! (
331+ "failed to create a single-threaded Tokio runtime within thread [{}] because [{}]" ,
332+ THREAD_NAME , e
333+ ) ;
334+ } ) ;
335+
336+ single_thread_rt. block_on ( async move {
337+ let mut server_removed = Vec :: new ( ) ;
338+ let mut server_added = Vec :: new ( ) ;
339+
340+ let search_sources = tauri_app_handle. state :: < SearchSourceRegistry > ( ) ;
341+ loop {
342+ log:: info!( "Coco Server Heartbeat worker is working..." ) ;
343+
344+ refresh_all_coco_server_info ( tauri_app_handle. clone ( ) ) . await ;
345+
346+ /*
347+ * For the Coco servers that are included in the SearchSourceRegistry
348+ * but unavailable, they should be removed from the registry.
349+ *
350+ * We do this step first so that there are less search source to
351+ * scan.
352+ */
353+ for search_source in search_sources. get_sources ( ) . await {
354+ let query_source = search_source. get_type ( ) ;
355+ let search_source_id = query_source. id ;
356+ let search_source_name = query_source. name ;
357+
358+ let Some ( coco_server) = get_server_by_id ( & search_source_id) . await else {
359+ // This search source may not be a Coco server, try the next one.
360+ continue ;
361+ } ;
362+
363+ assert ! (
364+ coco_server. enabled,
365+ "Coco servers stored in search source list should all be enabled"
366+ ) ;
367+
368+ if !coco_server. available {
369+ let removed = search_sources. remove_source ( & search_source_id) . await ;
370+ if removed {
371+ server_removed. push ( ( search_source_id, search_source_name) ) ;
372+ }
373+ }
374+ }
375+
376+ /*
377+ * Coco servers that are available and enabled should be added to
378+ * the SearchSourceRegistry if they are not already included.
379+ */
380+ for coco_server in get_all_servers ( ) . await {
381+ if coco_server. enabled
382+ && coco_server. available
383+ && search_sources. get_source ( & coco_server. id ) . await . is_none ( )
384+ {
385+ server_added. push ( ( coco_server. id . clone ( ) , coco_server. name . clone ( ) ) ) ;
386+
387+ let source = CocoSearchSource :: new ( coco_server) ;
388+ search_sources. register_source ( source) . await ;
389+ }
390+ }
391+
392+ /*
393+ * Log the updates to SearchSourceRegistry
394+ */
395+ log:: info!(
396+ "Coco Server Heartbeat worker: removed {:?} from the SearchSourceRegistry" ,
397+ server_removed
398+ ) ;
399+ log:: info!(
400+ "Coco Server Heartbeat worker: added {:?} to the SearchSourceRegistry" ,
401+ server_added
402+ ) ;
403+
404+ // Sleep for a period of time
405+ tokio:: time:: sleep ( SLEEP_DURATION ) . await ;
406+ }
407+ } ) ;
408+ } ;
409+
410+ thread:: Builder :: new ( )
411+ . name ( THREAD_NAME . into ( ) )
412+ . spawn ( main_closure)
413+ . unwrap_or_else ( |e| {
414+ panic ! (
415+ "failed to start thread [{}] for reason [{}]" ,
416+ THREAD_NAME , e
417+ )
418+ } ) ;
419+ }
420+
315421#[ tauri:: command]
316422pub async fn refresh_coco_server_info ( app_handle : AppHandle , id : String ) -> Result < Server , String > {
317423 // Retrieve the server from the cache
0 commit comments