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