1use std::sync::Arc;
222
223use log::{debug, info, warn};
224use rand::RngExt;
225use serde::{Deserialize, Serialize};
226use tauri::{AppHandle, Manager, command};
227use CommonLibrary::Configuration::DTO::{
229 ConfigurationOverridesDTO as ConfigurationOverridesDTOModule,
230 ConfigurationTarget as ConfigurationTargetModule,
231};
232type ConfigurationOverridesDTO = ConfigurationOverridesDTOModule::ConfigurationOverridesDTO;
233type ConfigurationTarget = ConfigurationTargetModule::ConfigurationTarget;
234
235use CommonLibrary::{Configuration::ConfigurationProvider::ConfigurationProvider, Environment::Requires::Requires};
236use sha2::Digest;
237
238use crate::{
239 IPC::WindServiceAdapters::{WindDesktopConfiguration, WindServiceAdapter},
240 RunTime::ApplicationRunTime::ApplicationRunTime,
241};
242
243pub struct ConfigurationBridge {
245 runtime:Arc<ApplicationRunTime>,
246}
247
248impl ConfigurationBridge {
249 pub fn new(runtime:Arc<ApplicationRunTime>) -> Self {
251 info!("[ConfigurationBridge] Creating configuration bridge");
252 Self { runtime }
253 }
254
255 pub async fn get_wind_desktop_configuration(&self) -> Result<WindDesktopConfiguration, String> {
257 debug!("[ConfigurationBridge] Getting Wind desktop configuration");
258
259 let mountain_config = self.get_mountain_configuration().await?;
261
262 let service_adapter = WindServiceAdapter::new(self.runtime.clone());
264 let wind_config = service_adapter.convert_to_wind_configuration(mountain_config).await?;
265
266 debug!("[ConfigurationBridge] Wind configuration ready");
267 Ok(wind_config)
268 }
269
270 pub async fn update_configuration_from_wind(&self, wind_config:WindDesktopConfiguration) -> Result<(), String> {
272 debug!("[ConfigurationBridge] Updating configuration from Wind");
273
274 let mountain_config = self.convert_to_mountain_configuration(wind_config).await?;
276
277 self.update_mountain_configuration(mountain_config).await?;
279
280 debug!("[ConfigurationBridge] Configuration updated successfully");
281 Ok(())
282 }
283
284 async fn get_mountain_configuration(&self) -> Result<serde_json::Value, String> {
286 debug!("[ConfigurationBridge] Getting Mountain configuration");
287
288 let config_provider:Arc<dyn ConfigurationProvider> = self.runtime.Environment.Require();
289
290 let config = config_provider
291 .GetConfigurationValue(None, ConfigurationOverridesDTO::default())
292 .await
293 .map_err(|e| format!("Failed to get Mountain configuration: {}", e))?;
294
295 Ok(config)
296 }
297
298 async fn update_mountain_configuration(&self, config:serde_json::Value) -> Result<(), String> {
300 debug!("[ConfigurationBridge] Updating Mountain configuration");
301
302 if !self.validate_configuration(&config) {
304 return Err("Invalid configuration data".to_string());
305 }
306
307 let config_provider:Arc<dyn ConfigurationProvider> = self.runtime.Environment.Require();
308
309 if let Some(obj) = config.as_object() {
311 for (key, value) in obj {
312 config_provider
313 .UpdateConfigurationValue(
314 key.clone(),
315 value.clone(),
316 ConfigurationTarget::User,
317 ConfigurationOverridesDTO::default(),
318 None,
319 )
320 .await
321 .map_err(|e| format!("Failed to update configuration key {}: {}", key, e))?;
322 }
323 }
324
325 Ok(())
326 }
327
328 fn validate_configuration(&self, config:&serde_json::Value) -> bool {
330 if !config.is_object() {
332 return false;
333 }
334
335 if let Some(obj) = config.as_object() {
337 for (key, value) in obj {
338 if key.trim().is_empty() {
340 return false;
341 }
342
343 match key.as_str() {
345 "zoom_level" | "font_size" => {
346 if let Some(num) = value.as_f64() {
347 if key == "zoom_level" && (num < -8.0 || num > 9.0) {
348 return false;
349 }
350 if key == "font_size" && (num < 6.0 || num > 100.0) {
351 return false;
352 }
353 } else {
354 return false;
355 }
356 },
357 "is_packaged" | "enable_feature" => {
358 if !value.is_boolean() {
359 return false;
360 }
361 },
362 "theme" | "platform" | "arch" => {
363 if !value.is_string() || value.as_str().unwrap().trim().is_empty() {
364 return false;
365 }
366 },
367 _ => {
368 if value.is_null() {
370 return false;
371 }
372 },
373 }
374 }
375 }
376
377 true
378 }
379
380 async fn convert_to_mountain_configuration(
382 &self,
383 wind_config:WindDesktopConfiguration,
384 ) -> Result<serde_json::Value, String> {
385 debug!("[ConfigurationBridge] Converting Wind config to Mountain format");
386
387 let machine_id = self.generate_machine_id().await.unwrap_or_else(|e| {
388 warn!("[ConfigurationBridge] Failed to generate machine ID: {}", e);
389 "wind-machine-fallback".to_string()
390 });
391
392 let session_id = self.generate_session_id().await.unwrap_or_else(|e| {
393 warn!("[ConfigurationBridge] Failed to generate session ID: {}", e);
394 "wind-session-fallback".to_string()
395 });
396
397 let mountain_config = serde_json::json!({
398 "window_id": wind_config.window_id.to_string(),
399 "machine_id": machine_id,
400 "session_id": session_id,
401 "log_level": wind_config.log_level,
402 "app_root": wind_config.app_root,
403 "user_data_dir": wind_config.user_data_path,
404 "tmp_dir": wind_config.temp_path,
405 "platform": wind_config.platform,
406 "arch": wind_config.arch,
407 "zoom_level": wind_config.zoom_level.unwrap_or(0.0),
408 "backup_path": wind_config.backup_path.unwrap_or_default(),
409 "home_dir": wind_config.profiles.home,
410 "is_packaged": wind_config.is_packaged,
411 });
412
413 Ok(mountain_config)
414 }
415
416 pub async fn synchronize_configuration(&self) -> Result<(), String> {
418 debug!("[ConfigurationBridge] Synchronizing configuration");
419
420 let mountain_config = self.get_mountain_configuration().await?;
422
423 let service_adapter = WindServiceAdapter::new(self.runtime.clone());
425 let wind_config = service_adapter.convert_to_wind_configuration(mountain_config).await?;
426
427 self.send_configuration_to_wind(wind_config).await?;
429
430 debug!("[ConfigurationBridge] Configuration synchronized");
431 Ok(())
432 }
433
434 async fn send_configuration_to_wind(&self, config:WindDesktopConfiguration) -> Result<(), String> {
436 debug!("[ConfigurationBridge] Sending configuration to Wind");
437
438 if let Some(ipc_server) = self
440 .runtime
441 .Environment
442 .ApplicationHandle
443 .try_state::<crate::IPC::TauriIPCServer::TauriIPCServer>()
444 {
445 let config_json =
446 serde_json::to_value(config).map_err(|e| format!("Failed to serialize configuration: {}", e))?;
447
448 ipc_server
449 .send("configuration:update", config_json)
450 .await
451 .map_err(|e| format!("Failed to send configuration to Wind: {}", e))?;
452 } else {
453 return Err("IPC Server not found".to_string());
454 }
455
456 Ok(())
457 }
458
459 pub async fn handle_wind_configuration_change(&self, new_config:serde_json::Value) -> Result<(), String> {
461 debug!("[ConfigurationBridge] Handling Wind configuration change");
462
463 let wind_config:WindDesktopConfiguration =
465 serde_json::from_value(new_config).map_err(|e| format!("Failed to parse Wind configuration: {}", e))?;
466
467 self.update_configuration_from_wind(wind_config).await?;
469
470 debug!("[ConfigurationBridge] Wind configuration change handled");
471 Ok(())
472 }
473
474 pub async fn get_configuration_status(&self) -> Result<ConfigurationStatus, String> {
476 debug!("[ConfigurationBridge] Getting configuration status");
477
478 let mountain_config = self.get_mountain_configuration().await?;
479 let is_valid = !mountain_config.is_null();
480
481 let status = ConfigurationStatus {
482 is_valid,
483 last_sync:std::time::SystemTime::now()
484 .duration_since(std::time::UNIX_EPOCH)
485 .unwrap_or_default()
486 .as_millis() as u64,
487 configuration_keys:if let Some(obj) = mountain_config.as_object() {
488 obj.keys().map(|k| k.clone()).collect()
489 } else {
490 Vec::new()
491 },
492 };
493
494 Ok(status)
495 }
496
497 async fn generate_machine_id(&self) -> Result<String, String> {
499 #[cfg(target_os = "macos")]
501 {
502 use std::process::Command;
503
504 let result = Command::new("system_profiler")
506 .arg("SPHardwareDataType")
507 .arg("-json")
508 .output()
509 .map_err(|e| format!("Failed to execute system_profiler: {}", e))?;
510
511 if result.status.success() {
512 let output_str = String::from_utf8_lossy(&result.stdout);
513 if let Ok(json) = serde_json::from_str::<serde_json::Value>(&output_str) {
514 if let Some(serial) = json["SPHardwareDataType"][0]["serial_number"].as_str() {
515 return Ok(format!("mac-{}", serial));
516 }
517 }
518 }
519 }
520
521 #[cfg(target_os = "windows")]
522 {
523 use std::process::Command;
524
525 let result = Command::new("wmic")
527 .arg("csproduct")
528 .arg("get")
529 .arg("UUID")
530 .output()
531 .map_err(|e| format!("Failed to execute wmic: {}", e))?;
532
533 if result.status.success() {
534 let output_str = String::from_utf8_lossy(&result.stdout);
535 let lines:Vec<&str> = output_str.lines().collect();
536 if lines.len() > 1 {
537 let uuid = lines[1].trim();
538 if !uuid.is_empty() {
539 return Ok(format!("win-{}", uuid));
540 }
541 }
542 }
543 }
544
545 #[cfg(target_os = "linux")]
546 {
547 use std::fs;
548
549 if let Ok(content) = fs::read_to_string("/etc/machine-id") {
551 let machine_id = content.trim();
552 if !machine_id.is_empty() {
553 return Ok(format!("linux-{}", machine_id));
554 }
555 }
556
557 if let Ok(content) = fs::read_to_string("/var/lib/dbus/machine-id") {
559 let machine_id = content.trim();
560 if !machine_id.is_empty() {
561 return Ok(format!("linux-{}", machine_id));
562 }
563 }
564 }
565
566 let hostname = hostname::get()
568 .map_err(|e| format!("Failed to get hostname: {}", e))?
569 .to_string_lossy()
570 .to_string();
571
572 let timestamp = std::time::SystemTime::now()
573 .duration_since(std::time::UNIX_EPOCH)
574 .unwrap_or_default()
575 .as_millis();
576
577 Ok(format!("fallback-{}-{}", hostname, timestamp))
578 }
579
580 async fn generate_session_id(&self) -> Result<String, String> {
582 use std::time::{SystemTime, UNIX_EPOCH};
583
584 use rand::{Rng, rng};
585
586 let mut rng = rng();
588 let random_part:u64 = rng.random_range(0..=u64::MAX);
589
590 let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_millis();
591
592 let process_id = std::process::id();
594
595 let session_data = format!("{}:{}:{}", timestamp, random_part, process_id);
597 let mut hasher = sha2::Sha256::new();
598 hasher.update(session_data.as_bytes());
599 let result = hasher.finalize();
600
601 let hex_string = format!("{:x}", result);
603 let session_id = hex_string.chars().take(16).collect::<String>();
604
605 info!("[ConfigurationBridge] Generated session ID: {}", session_id);
606 Ok(format!("session-{}", session_id))
607 }
608}
609
610#[derive(Debug, Clone, Serialize, Deserialize)]
612pub struct ConfigurationStatus {
613 pub is_valid:bool,
614 pub last_sync:u64,
615 pub configuration_keys:Vec<String>,
616}
617
618#[tauri::command]
620pub async fn mountain_get_wind_desktop_configuration(
621 app_handle:tauri::AppHandle,
622) -> Result<WindDesktopConfiguration, String> {
623 debug!("[ConfigurationBridge] Tauri command: get_wind_desktop_configuration");
624
625 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
626 let bridge = ConfigurationBridge::new(runtime.inner().clone());
627 bridge.get_wind_desktop_configuration().await
628 } else {
629 Err("ApplicationRunTime not found".to_string())
630 }
631}
632
633#[tauri::command]
635pub async fn get_configuration_data(app_handle:tauri::AppHandle) -> Result<serde_json::Value, String> {
636 debug!("[ConfigurationBridge] Tauri command: get_configuration_data");
637
638 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
639 let bridge = ConfigurationBridge::new(runtime.inner().clone());
640
641 let mountain_config = bridge.get_mountain_configuration().await?;
643
644 let config_data = serde_json::json!({
646 "application": mountain_config.clone(),
647 "workspace": mountain_config.clone(),
648 "profile": mountain_config.clone()
649 });
650
651 debug!("[ConfigurationBridge] Configuration data retrieved successfully");
652 Ok(config_data)
653 } else {
654 Err("ApplicationRunTime not found".to_string())
655 }
656}
657
658#[tauri::command]
660pub async fn save_configuration_data(app_handle:tauri::AppHandle, config_data:serde_json::Value) -> Result<(), String> {
661 debug!("[ConfigurationBridge] Tauri command: save_configuration_data");
662
663 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
664 let bridge = ConfigurationBridge::new(runtime.inner().clone());
665
666 bridge.update_mountain_configuration(config_data).await?;
668
669 debug!("[ConfigurationBridge] Configuration data saved successfully");
670 Ok(())
671 } else {
672 Err("ApplicationRunTime not found".to_string())
673 }
674}
675
676#[tauri::command]
678pub async fn mountain_update_configuration_from_wind(
679 app_handle:tauri::AppHandle,
680 config:serde_json::Value,
681) -> Result<(), String> {
682 debug!("[ConfigurationBridge] Tauri command: update_configuration_from_wind");
683
684 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
685 let bridge = ConfigurationBridge::new(runtime.inner().clone());
686 bridge.handle_wind_configuration_change(config).await
687 } else {
688 Err("ApplicationRunTime not found".to_string())
689 }
690}
691
692#[tauri::command]
694pub async fn mountain_synchronize_configuration(app_handle:tauri::AppHandle) -> Result<serde_json::Value, String> {
695 debug!("[ConfigurationBridge] Tauri command: synchronize_configuration");
696
697 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
698 let bridge = ConfigurationBridge::new(runtime.inner().clone());
699 bridge
700 .synchronize_configuration()
701 .await
702 .map(|_| serde_json::json!({ "status": "success" }))
703 } else {
704 Err("ApplicationRunTime not found".to_string())
705 }
706}
707
708#[tauri::command]
710pub async fn mountain_get_configuration_status(app_handle:tauri::AppHandle) -> Result<serde_json::Value, String> {
711 debug!("[ConfigurationBridge] Tauri command: get_configuration_status");
712
713 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
714 let bridge = ConfigurationBridge::new(runtime.inner().clone());
715 bridge
716 .get_configuration_status()
717 .await
718 .map(|status| serde_json::to_value(status).unwrap_or(serde_json::Value::Null))
719 } else {
720 Err("ApplicationRunTime not found".to_string())
721 }
722}