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
403 "wind-machine-fallback".to_string()
404 });
405
406 let session_id = self.generate_session_id().await.unwrap_or_else(|e| {
407 dev_log!("config", "warn: [ConfigurationBridge] Failed to generate session ID: {}", e);
408
409 "wind-session-fallback".to_string()
410 });
411
412 let mountain_config = serde_json::json!({
413 "window_id": wind_config.window_id.to_string(),
414 "machine_id": machine_id,
415 "session_id": session_id,
416 "log_level": wind_config.log_level,
417 "app_root": wind_config.app_root,
418 "user_data_dir": wind_config.user_data_path,
419 "tmp_dir": wind_config.temp_path,
420 "platform": wind_config.platform,
421 "arch": wind_config.arch,
422 "zoom_level": wind_config.zoom_level.unwrap_or(0.0),
423 "backup_path": wind_config.backup_path.unwrap_or_default(),
424 "home_dir": wind_config.profiles.home,
425 "is_packaged": wind_config.is_packaged,
426 });
427
428 Ok(mountain_config)
429 }
430
431 pub async fn synchronize_configuration(&self) -> Result<(), String> {
433 dev_log!("config", "[ConfigurationBridge] Synchronizing configuration");
434
435 let mountain_config = self.get_mountain_configuration().await?;
437
438 let service_adapter = WindServiceAdapter::new(self.runtime.clone());
440
441 let wind_config = service_adapter.convert_to_wind_configuration(mountain_config).await?;
442
443 self.send_configuration_to_wind(wind_config).await?;
445
446 dev_log!("config", "[ConfigurationBridge] Configuration synchronized");
447
448 Ok(())
449 }
450
451 async fn send_configuration_to_wind(&self, config:WindDesktopConfiguration) -> Result<(), String> {
453 dev_log!("config", "[ConfigurationBridge] Sending configuration to Wind");
454
455 if let Some(ipc_server) = self
457 .runtime
458 .Environment
459 .ApplicationHandle
460 .try_state::<crate::IPC::TauriIPCServer_Old::TauriIPCServer>()
461 {
462 let config_json =
463 serde_json::to_value(config).map_err(|e| format!("Failed to serialize configuration: {}", e))?;
464
465 ipc_server
466 .send("configuration:update", config_json)
467 .await
468 .map_err(|e| format!("Failed to send configuration to Wind: {}", e))?;
469 } else {
470 return Err("IPC Server not found".to_string());
471 }
472
473 Ok(())
474 }
475
476 pub async fn WindConfigurationChange(&self, new_config:serde_json::Value) -> Result<(), String> {
478 dev_log!("config", "[ConfigurationBridge] Handling Wind configuration change");
479
480 let wind_config:WindDesktopConfiguration =
482 serde_json::from_value(new_config).map_err(|e| format!("Failed to parse Wind configuration: {}", e))?;
483
484 self.update_configuration_from_wind(wind_config).await?;
486
487 dev_log!("config", "[ConfigurationBridge] Wind configuration change handled");
488
489 Ok(())
490 }
491
492 pub async fn get_configuration_status(&self) -> Result<ConfigurationStatus, String> {
494 dev_log!("config", "[ConfigurationBridge] Getting configuration status");
495
496 let mountain_config = self.get_mountain_configuration().await?;
497
498 let is_valid = !mountain_config.is_null();
499
500 let status = ConfigurationStatus {
501 is_valid,
502
503 last_sync:std::time::SystemTime::now()
504 .duration_since(std::time::UNIX_EPOCH)
505 .unwrap_or_default()
506 .as_millis() as u64,
507
508 configuration_keys:if let Some(obj) = mountain_config.as_object() {
509 obj.keys().map(|k| k.clone()).collect()
510 } else {
511 Vec::new()
512 },
513 };
514
515 Ok(status)
516 }
517
518 async fn generate_machine_id(&self) -> Result<String, String> {
520 #[cfg(target_os = "macos")]
522 {
523 use std::process::Command;
524
525 let result = Command::new("system_profiler")
527 .arg("SPHardwareDataType")
528 .arg("-json")
529 .output()
530 .map_err(|e| format!("Failed to execute system_profiler: {}", e))?;
531
532 if result.status.success() {
533 let output_str = String::from_utf8_lossy(&result.stdout);
534
535 if let Ok(json) = serde_json::from_str::<serde_json::Value>(&output_str) {
536 if let Some(serial) = json["SPHardwareDataType"][0]["serial_number"].as_str() {
537 return Ok(format!("mac-{}", serial));
538 }
539 }
540 }
541 }
542
543 #[cfg(target_os = "windows")]
544 {
545 use std::process::Command;
546
547 let result = Command::new("wmic")
549 .arg("csproduct")
550 .arg("get")
551 .arg("UUID")
552 .output()
553 .map_err(|e| format!("Failed to execute wmic: {}", e))?;
554
555 if result.status.success() {
556 let output_str = String::from_utf8_lossy(&result.stdout);
557
558 let lines:Vec<&str> = output_str.lines().collect();
559
560 if lines.len() > 1 {
561 let uuid = lines[1].trim();
562
563 if !uuid.is_empty() {
564 return Ok(format!("win-{}", uuid));
565 }
566 }
567 }
568 }
569
570 #[cfg(target_os = "linux")]
571 {
572 use std::fs;
573
574 if let Ok(content) = fs::read_to_string("/etc/machine-id") {
576 let machine_id = content.trim();
577
578 if !machine_id.is_empty() {
579 return Ok(format!("linux-{}", machine_id));
580 }
581 }
582
583 if let Ok(content) = fs::read_to_string("/var/lib/dbus/machine-id") {
585 let machine_id = content.trim();
586
587 if !machine_id.is_empty() {
588 return Ok(format!("linux-{}", machine_id));
589 }
590 }
591 }
592
593 let hostname = hostname::get()
595 .map_err(|e| format!("Failed to get hostname: {}", e))?
596 .to_string_lossy()
597 .to_string();
598
599 let timestamp = std::time::SystemTime::now()
600 .duration_since(std::time::UNIX_EPOCH)
601 .unwrap_or_default()
602 .as_millis();
603
604 Ok(format!("fallback-{}-{}", hostname, timestamp))
605 }
606
607 async fn generate_session_id(&self) -> Result<String, String> {
609 use std::time::{SystemTime, UNIX_EPOCH};
610
611 let random_part:u64 = rand::random();
613
614 let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_millis();
615
616 let process_id = std::process::id();
618
619 let session_data = format!("{}:{}:{}", timestamp, random_part, process_id);
621
622 let mut hasher = sha2::Sha256::new();
623
624 hasher.update(session_data.as_bytes());
625
626 let result = hasher.finalize();
627
628 let hex_string = hex::encode(result);
633
634 let session_id = hex_string.chars().take(16).collect::<String>();
635
636 dev_log!("config", "[ConfigurationBridge] Generated session ID: {}", session_id);
637
638 Ok(format!("session-{}", session_id))
639 }
640}
641
642#[derive(Debug, Clone, Serialize, Deserialize)]
644pub struct ConfigurationStatus {
645 pub is_valid:bool,
646
647 pub last_sync:u64,
648
649 pub configuration_keys:Vec<String>,
650}
651
652#[tauri::command]
654pub async fn mountain_get_wind_desktop_configuration(
655 app_handle:tauri::AppHandle,
656) -> Result<WindDesktopConfiguration, String> {
657 dev_log!("config", "[ConfigurationBridge] Tauri command: get_wind_desktop_configuration");
658
659 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
660 let bridge = ConfigurationBridge::new(runtime.inner().clone());
661
662 bridge.get_wind_desktop_configuration().await
663 } else {
664 Err("ApplicationRunTime not found".to_string())
665 }
666}
667
668#[tauri::command]
670pub async fn get_configuration_data(app_handle:tauri::AppHandle) -> Result<serde_json::Value, String> {
671 dev_log!("config", "[ConfigurationBridge] Tauri command: get_configuration_data");
672
673 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
674 let bridge = ConfigurationBridge::new(runtime.inner().clone());
675
676 let mountain_config = bridge.get_mountain_configuration().await?;
678
679 let config_data = serde_json::json!({
681 "application": mountain_config.clone(),
682 "workspace": mountain_config.clone(),
683 "profile": mountain_config.clone()
684 });
685
686 dev_log!("config", "[ConfigurationBridge] Configuration data retrieved successfully");
687
688 Ok(config_data)
689 } else {
690 Err("ApplicationRunTime not found".to_string())
691 }
692}
693
694#[tauri::command]
696pub async fn save_configuration_data(app_handle:tauri::AppHandle, config_data:serde_json::Value) -> Result<(), String> {
697 dev_log!("config", "[ConfigurationBridge] Tauri command: save_configuration_data");
698
699 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
700 let bridge = ConfigurationBridge::new(runtime.inner().clone());
701
702 bridge.update_mountain_configuration(config_data).await?;
704
705 dev_log!("config", "[ConfigurationBridge] Configuration data saved successfully");
706
707 Ok(())
708 } else {
709 Err("ApplicationRunTime not found".to_string())
710 }
711}
712
713#[tauri::command]
715pub async fn mountain_update_configuration_from_wind(
716 app_handle:tauri::AppHandle,
717
718 config:serde_json::Value,
719) -> Result<(), String> {
720 dev_log!("config", "[ConfigurationBridge] Tauri command: update_configuration_from_wind");
721
722 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
723 let bridge = ConfigurationBridge::new(runtime.inner().clone());
724
725 bridge.WindConfigurationChange(config).await
726 } else {
727 Err("ApplicationRunTime not found".to_string())
728 }
729}
730
731#[tauri::command]
733pub async fn mountain_synchronize_configuration(app_handle:tauri::AppHandle) -> Result<serde_json::Value, String> {
734 dev_log!("config", "[ConfigurationBridge] Tauri command: synchronize_configuration");
735
736 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
737 let bridge = ConfigurationBridge::new(runtime.inner().clone());
738
739 bridge
740 .synchronize_configuration()
741 .await
742 .map(|_| serde_json::json!({ "status": "success" }))
743 } else {
744 Err("ApplicationRunTime not found".to_string())
745 }
746}
747
748#[tauri::command]
750pub async fn mountain_get_configuration_status(app_handle:tauri::AppHandle) -> Result<serde_json::Value, String> {
751 dev_log!("config", "[ConfigurationBridge] Tauri command: get_configuration_status");
752
753 if let Some(runtime) = app_handle.try_state::<Arc<ApplicationRunTime>>() {
754 let bridge = ConfigurationBridge::new(runtime.inner().clone());
755
756 bridge
757 .get_configuration_status()
758 .await
759 .map(|status| serde_json::to_value(status).unwrap_or(serde_json::Value::Null))
760 } else {
761 Err("ApplicationRunTime not found".to_string())
762 }
763}