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