feat: partially add wayfern

This commit is contained in:
zhom
2026-01-09 09:50:07 +04:00
parent fdd921c6bb
commit e9c084d6a4
22 changed files with 2313 additions and 127 deletions
+282
View File
@@ -4,6 +4,7 @@ use crate::camoufox_manager::CamoufoxConfig;
use crate::downloaded_browsers_registry::DownloadedBrowsersRegistry;
use crate::profile::types::BrowserProfile;
use crate::proxy_manager::PROXY_MANAGER;
use crate::wayfern_manager::WayfernConfig;
use directories::BaseDirs;
use std::fs::{self, create_dir_all};
use std::path::{Path, PathBuf};
@@ -13,6 +14,7 @@ use tauri::Emitter;
pub struct ProfileManager {
base_dirs: BaseDirs,
camoufox_manager: &'static crate::camoufox_manager::CamoufoxManager,
wayfern_manager: &'static crate::wayfern_manager::WayfernManager,
}
impl ProfileManager {
@@ -20,6 +22,7 @@ impl ProfileManager {
Self {
base_dirs: BaseDirs::new().expect("Failed to get base directories"),
camoufox_manager: crate::camoufox_manager::CamoufoxManager::instance(),
wayfern_manager: crate::wayfern_manager::WayfernManager::instance(),
}
}
@@ -59,6 +62,7 @@ impl ProfileManager {
release_type: &str,
proxy_id: Option<String>,
camoufox_config: Option<CamoufoxConfig>,
wayfern_config: Option<WayfernConfig>,
group_id: Option<String>,
) -> Result<BrowserProfile, Box<dyn std::error::Error>> {
log::info!("Attempting to create profile: {name}");
@@ -163,6 +167,7 @@ impl ProfileManager {
last_launch: None,
release_type: release_type.to_string(),
camoufox_config: None,
wayfern_config: None,
group_id: group_id.clone(),
tags: Vec::new(),
note: None,
@@ -198,6 +203,118 @@ impl ProfileManager {
camoufox_config.clone()
};
// For Wayfern profiles, generate fingerprint during creation
let final_wayfern_config = if browser == "wayfern" {
let mut config = wayfern_config.unwrap_or_else(|| {
log::info!("Creating default Wayfern config for profile: {name}");
crate::wayfern_manager::WayfernConfig::default()
});
// Always ensure executable_path is set to the user's binary location
if config.executable_path.is_none() {
let mut browser_dir = self.get_binaries_dir();
browser_dir.push(browser);
browser_dir.push(version);
#[cfg(target_os = "macos")]
let binary_path = browser_dir
.join("Chromium.app")
.join("Contents")
.join("MacOS")
.join("Chromium");
#[cfg(target_os = "windows")]
let binary_path = browser_dir.join("chrome.exe");
#[cfg(target_os = "linux")]
let binary_path = browser_dir.join("chrome");
config.executable_path = Some(binary_path.to_string_lossy().to_string());
log::info!("Set Wayfern executable path: {:?}", config.executable_path);
}
// Pass upstream proxy information to config for fingerprint generation
if let Some(proxy_id_ref) = &proxy_id {
if let Some(proxy_settings) = PROXY_MANAGER.get_proxy_settings_by_id(proxy_id_ref) {
let proxy_url = if let (Some(username), Some(password)) =
(&proxy_settings.username, &proxy_settings.password)
{
format!(
"{}://{}:{}@{}:{}",
proxy_settings.proxy_type.to_lowercase(),
username,
password,
proxy_settings.host,
proxy_settings.port
)
} else {
format!(
"{}://{}:{}",
proxy_settings.proxy_type.to_lowercase(),
proxy_settings.host,
proxy_settings.port
)
};
config.proxy = Some(proxy_url);
log::info!(
"Using upstream proxy for Wayfern fingerprint generation: {}://{}:{}",
proxy_settings.proxy_type.to_lowercase(),
proxy_settings.host,
proxy_settings.port
);
}
}
// Generate fingerprint if not already provided
if config.fingerprint.is_none() {
log::info!("Generating fingerprint for Wayfern profile: {name}");
// Create a temporary profile for fingerprint generation
let temp_profile = BrowserProfile {
id: uuid::Uuid::new_v4(),
name: name.to_string(),
browser: browser.to_string(),
version: version.to_string(),
proxy_id: proxy_id.clone(),
process_id: None,
last_launch: None,
release_type: release_type.to_string(),
camoufox_config: None,
wayfern_config: None,
group_id: group_id.clone(),
tags: Vec::new(),
note: None,
sync_enabled: false,
last_sync: None,
};
match self
.wayfern_manager
.generate_fingerprint_config(app_handle, &temp_profile, &config)
.await
{
Ok(generated_fingerprint) => {
config.fingerprint = Some(generated_fingerprint);
log::info!("Successfully generated fingerprint for Wayfern profile: {name}");
}
Err(e) => {
return Err(
format!("Failed to generate fingerprint for Wayfern profile '{name}': {e}").into(),
);
}
}
} else {
log::info!("Using provided fingerprint for Wayfern profile: {name}");
}
// Clear the proxy from config after fingerprint generation
config.proxy = None;
Some(config)
} else {
wayfern_config.clone()
};
let profile = BrowserProfile {
id: profile_id,
name: name.to_string(),
@@ -208,6 +325,7 @@ impl ProfileManager {
last_launch: None,
release_type: release_type.to_string(),
camoufox_config: final_camoufox_config,
wayfern_config: final_wayfern_config,
group_id: group_id.clone(),
tags: Vec::new(),
note: None,
@@ -722,6 +840,66 @@ impl ProfileManager {
Ok(())
}
pub async fn update_wayfern_config(
&self,
app_handle: tauri::AppHandle,
profile_id: &str,
config: WayfernConfig,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Find the profile by ID
let profile_uuid = uuid::Uuid::parse_str(profile_id).map_err(
|_| -> Box<dyn std::error::Error + Send + Sync> {
format!("Invalid profile ID: {profile_id}").into()
},
)?;
let profiles =
self
.list_profiles()
.map_err(|e| -> Box<dyn std::error::Error + Send + Sync> {
format!("Failed to list profiles: {e}").into()
})?;
let mut profile = profiles
.into_iter()
.find(|p| p.id == profile_uuid)
.ok_or_else(|| -> Box<dyn std::error::Error + Send + Sync> {
format!("Profile with ID '{profile_id}' not found").into()
})?;
// Check if the browser is currently running using the comprehensive status check
let is_running = self
.check_browser_status(app_handle.clone(), &profile)
.await?;
if is_running {
return Err(
"Cannot update Wayfern configuration while browser is running. Please stop the browser first.".into(),
);
}
// Update the Wayfern configuration
profile.wayfern_config = Some(config);
// Save the updated profile
self
.save_profile(&profile)
.map_err(|e| -> Box<dyn std::error::Error + Send + Sync> {
format!("Failed to save profile: {e}").into()
})?;
log::info!(
"Wayfern configuration updated for profile '{}' (ID: {}).",
profile.name,
profile_id
);
// Emit profile config update event
if let Err(e) = app_handle.emit("profiles-changed", ()) {
log::warn!("Warning: Failed to emit profiles-changed event: {e}");
}
Ok(())
}
pub async fn update_profile_proxy(
&self,
app_handle: tauri::AppHandle,
@@ -825,6 +1003,11 @@ impl ProfileManager {
return self.check_camoufox_status(&app_handle, profile).await;
}
// Handle Wayfern profiles using WayfernManager-based status checking
if profile.browser == "wayfern" {
return self.check_wayfern_status(&app_handle, profile).await;
}
// For non-camoufox browsers, use the existing PID-based logic
let inner_profile = profile.clone();
let mut system = System::new();
@@ -1094,6 +1277,88 @@ impl ProfileManager {
}
}
// Check Wayfern status using WayfernManager
async fn check_wayfern_status(
&self,
app_handle: &tauri::AppHandle,
profile: &BrowserProfile,
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
let manager = self.wayfern_manager;
let profiles_dir = self.get_profiles_dir();
let profile_data_path = profile.get_profile_data_path(&profiles_dir);
let profile_path_str = profile_data_path.to_string_lossy();
// Check if there's a running Wayfern instance for this profile
match manager.find_wayfern_by_profile(&profile_path_str).await {
Some(wayfern_process) => {
// Found a running instance, update profile with process info if changed
let profiles_dir = self.get_profiles_dir();
let profile_uuid_dir = profiles_dir.join(profile.id.to_string());
let metadata_file = profile_uuid_dir.join("metadata.json");
let metadata_exists = metadata_file.exists();
if metadata_exists {
// Load latest to avoid overwriting other fields
let mut latest: BrowserProfile = match std::fs::read_to_string(&metadata_file)
.ok()
.and_then(|s| serde_json::from_str(&s).ok())
{
Some(p) => p,
None => profile.clone(),
};
if latest.process_id != wayfern_process.processId {
latest.process_id = wayfern_process.processId;
if let Err(e) = self.save_profile(&latest) {
log::warn!("Warning: Failed to update Wayfern profile with process info: {e}");
}
// Emit profile update event to frontend
if let Err(e) = app_handle.emit("profile-updated", &latest) {
log::warn!("Warning: Failed to emit profile update event: {e}");
}
log::info!(
"Wayfern process has started for profile '{}' with PID: {:?}",
profile.name,
wayfern_process.processId
);
}
}
Ok(true)
}
None => {
// No running instance found, clear process ID if set
let profiles_dir = self.get_profiles_dir();
let profile_uuid_dir = profiles_dir.join(profile.id.to_string());
let metadata_file = profile_uuid_dir.join("metadata.json");
let metadata_exists = metadata_file.exists();
if metadata_exists {
let mut latest: BrowserProfile = match std::fs::read_to_string(&metadata_file)
.ok()
.and_then(|s| serde_json::from_str(&s).ok())
{
Some(p) => p,
None => profile.clone(),
};
if latest.process_id.is_some() {
latest.process_id = None;
if let Err(e) = self.save_profile(&latest) {
log::warn!("Warning: Failed to clear Wayfern profile process info: {e}");
}
if let Err(e) = app_handle.emit("profile-updated", &latest) {
log::warn!("Warning: Failed to emit profile update event: {e}");
}
}
}
Ok(false)
}
}
}
fn get_common_firefox_preferences(&self) -> Vec<String> {
vec![
// Disable default browser check
@@ -1453,6 +1718,7 @@ pub async fn create_browser_profile_with_group(
release_type: String,
proxy_id: Option<String>,
camoufox_config: Option<CamoufoxConfig>,
wayfern_config: Option<WayfernConfig>,
group_id: Option<String>,
) -> Result<BrowserProfile, String> {
let profile_manager = ProfileManager::instance();
@@ -1465,6 +1731,7 @@ pub async fn create_browser_profile_with_group(
&release_type,
proxy_id,
camoufox_config,
wayfern_config,
group_id,
)
.await
@@ -1550,6 +1817,7 @@ pub async fn create_browser_profile_new(
release_type: String,
proxy_id: Option<String>,
camoufox_config: Option<CamoufoxConfig>,
wayfern_config: Option<WayfernConfig>,
group_id: Option<String>,
) -> Result<BrowserProfile, String> {
let browser_type =
@@ -1562,6 +1830,7 @@ pub async fn create_browser_profile_new(
release_type,
proxy_id,
camoufox_config,
wayfern_config,
group_id,
)
.await
@@ -1580,6 +1849,19 @@ pub async fn update_camoufox_config(
.map_err(|e| format!("Failed to update Camoufox config: {e}"))
}
#[tauri::command]
pub async fn update_wayfern_config(
app_handle: tauri::AppHandle,
profile_id: String,
config: WayfernConfig,
) -> Result<(), String> {
let profile_manager = ProfileManager::instance();
profile_manager
.update_wayfern_config(app_handle, &profile_id, config)
.await
.map_err(|e| format!("Failed to update Wayfern config: {e}"))
}
// Global singleton instance
#[tauri::command]
pub fn delete_profile(app_handle: tauri::AppHandle, profile_id: String) -> Result<(), String> {
+3
View File
@@ -1,4 +1,5 @@
use crate::camoufox_manager::CamoufoxConfig;
use crate::wayfern_manager::WayfernConfig;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
@@ -29,6 +30,8 @@ pub struct BrowserProfile {
#[serde(default)]
pub camoufox_config: Option<CamoufoxConfig>, // Camoufox configuration
#[serde(default)]
pub wayfern_config: Option<WayfernConfig>, // Wayfern configuration
#[serde(default)]
pub group_id: Option<String>, // Reference to profile group
#[serde(default)]
pub tags: Vec<String>, // Free-form tags