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