diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 6328d5d..7527af8 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2326,6 +2326,7 @@ dependencies = [ "tauri-plugin-dialog", "url", "uuid", + "winreg", ] [[package]] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 19d36f5..92e8600 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -22,3 +22,6 @@ reqwest = { version = "0.12", default-features = false, features = ["blocking", percent-encoding = "2" url = "2" uuid = { version = "1", features = ["v4"] } + +[target.'cfg(windows)'.dependencies] +winreg = "0.55" diff --git a/src-tauri/src/adapters/proxifyre.rs b/src-tauri/src/adapters/proxifyre.rs index 2839bbc..fdd29d2 100644 --- a/src-tauri/src/adapters/proxifyre.rs +++ b/src-tauri/src/adapters/proxifyre.rs @@ -1,4 +1,3 @@ -#[cfg(not(test))] use crate::adapters::proxy_router::{ ProxyRouterAdapter, ProxyRouterError, ProxyRouterErrorKind, ProxyRouterGeneratedConfig, ProxyRouterRequest, @@ -7,11 +6,6 @@ use crate::models::{ ComponentId, ComponentState, ComponentStatus, Profile, ProfileItemType, Protocol, ProxyProtocol, Target, }; -#[cfg(test)] -use crate::proxy_router::{ - ProxyRouterAdapter, ProxyRouterError, ProxyRouterErrorKind, ProxyRouterGeneratedConfig, - ProxyRouterRequest, -}; use serde::{Deserialize, Serialize}; pub const PROXIFYRE_ADAPTER_ID: &str = "proxifyre"; diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index d7286ff..f02b425 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1,11 +1,8 @@ -#[cfg(not(test))] use crate::adapters::proxifyre::{ProxiFyreAdapter, ProxiFyreConfig, ProxiFyreProxy}; -#[cfg(not(test))] use crate::adapters::proxy_router::{ ProxyRouterAdapter, ProxyRouterError, ProxyRouterErrorKind, ProxyRouterGeneratedConfig, ProxyRouterRequest, }; -#[cfg(not(test))] use crate::adapters::singbox::{ SingBoxAdapter, SingBoxCheckResult, SingBoxCommandChecker, SingBoxConfigChecker, SingBoxConfigError, SingBoxConfigErrorKind, SingBoxGeneratedConfig, SingBoxGenerationRequest, @@ -21,18 +18,6 @@ use crate::models::{ SubscriptionCache, SubscriptionServer, Target, TargetInput, TargetKind, }; use crate::process::command_no_window; -#[cfg(test)] -use crate::proxifyre::{ProxiFyreAdapter, ProxiFyreConfig, ProxiFyreProxy}; -#[cfg(test)] -use crate::proxy_router::{ - ProxyRouterAdapter, ProxyRouterError, ProxyRouterErrorKind, ProxyRouterGeneratedConfig, - ProxyRouterRequest, -}; -#[cfg(test)] -use crate::singbox::{ - SingBoxAdapter, SingBoxCheckResult, SingBoxCommandChecker, SingBoxConfigChecker, - SingBoxConfigError, SingBoxConfigErrorKind, SingBoxGeneratedConfig, SingBoxGenerationRequest, -}; use crate::singbox_service::{ build_singbox_setup_status, ensure_safe_singbox_install_dir, parse_service_command_output as parse_singbox_service_command_output, service_control_script, @@ -1327,10 +1312,29 @@ pub fn apply_profiles_with_services( adapter: &impl ProxyRouterAdapter, helper: &impl ProxyApplyHelper, clock: &impl Clock, +) -> Result { + apply_profiles_with_services_and_detection( + storage, + adapter, + helper, + clock, + detect_proxyfier_install(), + detect_singbox_install(), + ) +} + +pub fn apply_profiles_with_services_and_detection( + storage: &JsonStorage, + adapter: &impl ProxyRouterAdapter, + helper: &impl ProxyApplyHelper, + clock: &impl Clock, + detected_proxyfier: Option, + detected_singbox: Option, ) -> Result { let profiles = storage.read_profiles().map_err(storage_error)?; let targets = storage.read_targets().map_err(storage_error)?; - let components = components_or_defaults(storage)?; + let components = + components_or_defaults_with_detection(storage, detected_proxyfier, detected_singbox)?; let generated = match adapter.generate_config(ProxyRouterRequest::new(&profiles, &targets, &components)) { Ok(generated) => generated, @@ -2441,7 +2445,7 @@ try {{ } } -pub(crate) fn singbox_installer_runner_script( +pub fn singbox_installer_runner_script( installer_path: &Path, result_path: &Path, installer_args: &[String], @@ -2560,11 +2564,23 @@ fn elevated_singbox_service_failed_message( } fn components_or_defaults(storage: &JsonStorage) -> Result, CommandError> { + components_or_defaults_with_detection( + storage, + detect_proxyfier_install(), + detect_singbox_install(), + ) +} + +fn components_or_defaults_with_detection( + storage: &JsonStorage, + detected_proxyfier: Option, + detected_singbox: Option, +) -> Result, CommandError> { let components = storage.read_components().map_err(storage_error)?; Ok(resolve_component_statuses( components, - detect_proxyfier_install(), - detect_singbox_install(), + detected_proxyfier, + detected_singbox, )) } @@ -3443,7 +3459,7 @@ try {{ } } -pub(crate) fn wrap_elevated_package_script(body: &str, result_path: &Path) -> String { +pub fn wrap_elevated_package_script(body: &str, result_path: &Path) -> String { let mut script = String::new(); script.push_str("$ErrorActionPreference = 'Stop'\n"); script.push_str(&format!( @@ -3530,7 +3546,7 @@ fn powershell_output_message(output: &Output, fallback: &str) -> String { fallback.to_string() } -pub(crate) fn install_proxifyre_script(generated_config_path: &Path) -> String { +pub fn install_proxifyre_script(generated_config_path: &Path) -> String { let mut script = String::new(); script.push_str(&format!( "$targetDir = '{}'\n", diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 008d525..f2865a9 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,5 +1,59 @@ +pub mod activity; +pub mod commands; +pub mod component_detection; +pub mod helper; +pub mod models; +pub mod process; +pub mod singbox_service; +pub mod storage; +pub mod subscription; +pub mod validation; + +pub mod adapters { + pub mod proxifyre; + pub mod proxy_router; + pub mod singbox; +} + pub fn run() { tauri::Builder::default() + .plugin(tauri_plugin_dialog::init()) + .manage(commands::CommandState::default()) + .invoke_handler(tauri::generate_handler![ + commands::get_status, + commands::get_admin_status, + commands::restart_as_admin, + commands::get_startup_snapshot, + commands::get_profiles, + commands::get_saved_state, + commands::save_profile, + commands::get_targets, + commands::save_target, + commands::get_components, + commands::get_proxifyre_setup_status, + commands::get_singbox_status, + commands::get_singbox_setup_status, + commands::resolve_profile_preview, + commands::save_singbox_subscription, + commands::fetch_singbox_subscription, + commands::forget_singbox_subscription, + commands::select_singbox_server, + commands::ping_singbox_server, + commands::ping_all_singbox_servers, + commands::ping_proxy_target, + commands::generate_singbox_config, + commands::apply_profiles, + commands::get_logs, + commands::open_config_location, + commands::start_proxifyre_service, + commands::stop_proxifyre_service, + commands::install_proxifyre, + commands::uninstall_proxifyre, + commands::start_singbox_service, + commands::stop_singbox_service, + commands::install_singbox, + commands::uninstall_singbox + ]) .run(tauri::generate_context!()) .expect("не удалось запустить клиент ProxyWarden"); } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index aff92f6..737f2ce 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,75 +1,5 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -mod activity; -mod commands; -mod component_detection; -mod models; -mod process; -mod singbox_service; -mod storage; -mod subscription; -mod validation; - -mod adapters { - pub mod proxifyre; - pub mod proxy_router; - pub mod singbox; -} - -#[cfg(test)] -pub(crate) mod proxifyre { - pub use crate::adapters::proxifyre::*; -} - -#[cfg(test)] -pub(crate) mod proxy_router { - pub use crate::adapters::proxy_router::*; -} - -#[cfg(test)] -pub(crate) mod singbox { - pub use crate::adapters::singbox::*; -} - fn main() { - tauri::Builder::default() - .plugin(tauri_plugin_dialog::init()) - .manage(commands::CommandState::default()) - .invoke_handler(tauri::generate_handler![ - commands::get_status, - commands::get_admin_status, - commands::restart_as_admin, - commands::get_startup_snapshot, - commands::get_profiles, - commands::get_saved_state, - commands::save_profile, - commands::get_targets, - commands::save_target, - commands::get_components, - commands::get_proxifyre_setup_status, - commands::get_singbox_status, - commands::get_singbox_setup_status, - commands::resolve_profile_preview, - commands::save_singbox_subscription, - commands::fetch_singbox_subscription, - commands::forget_singbox_subscription, - commands::select_singbox_server, - commands::ping_singbox_server, - commands::ping_all_singbox_servers, - commands::ping_proxy_target, - commands::generate_singbox_config, - commands::apply_profiles, - commands::get_logs, - commands::open_config_location, - commands::start_proxifyre_service, - commands::stop_proxifyre_service, - commands::install_proxifyre, - commands::uninstall_proxifyre, - commands::start_singbox_service, - commands::stop_singbox_service, - commands::install_singbox, - commands::uninstall_singbox - ]) - .run(tauri::generate_context!()) - .expect("не удалось запустить клиент ProxyWarden"); + proxywarden_lib::run(); } diff --git a/src-tauri/src/subscription.rs b/src-tauri/src/subscription.rs index b153ee3..0f1915d 100644 --- a/src-tauri/src/subscription.rs +++ b/src-tauri/src/subscription.rs @@ -40,6 +40,7 @@ pub struct SubscriptionFetchIdentity { pub app_name: String, pub user_agent: String, pub device_os: String, + pub device_os_version: Option, pub device_model: String, } @@ -64,6 +65,7 @@ impl Default for SubscriptionFetchIdentity { app_name: DEFAULT_APP_NAME.to_string(), user_agent: format!("{DEFAULT_APP_NAME}/{device_os}"), device_os, + device_os_version: detect_device_os_version(), device_model: DEFAULT_APP_NAME.to_string(), } } @@ -124,6 +126,18 @@ pub fn fetch_subscription_with_identity( .header("x-device-os", identity.device_os.as_str()) .header("x-device-model", identity.device_model.as_str()); + if let Some(device_os_version) = identity + .device_os_version + .as_deref() + .map(str::trim) + .filter(|value| !value.is_empty()) + { + let header_value = sanitize_header_value(device_os_version); + if !header_value.is_empty() { + request = request.header("x-device-os-version", header_value); + } + } + if let Some(device_hwid) = identity .device_hwid .as_deref() @@ -318,6 +332,86 @@ fn query_value(url: &Url, key: &str) -> Option { .map(|(_, value)| value.into_owned()) } +fn sanitize_header_value(value: &str) -> String { + value + .chars() + .filter(|ch| ch.is_ascii_graphic() || *ch == ' ') + .collect::() +} + +fn detect_device_os_version() -> Option { + #[cfg(windows)] + { + windows_device_os_version() + } + + #[cfg(not(windows))] + { + None + } +} + +#[cfg(windows)] +fn windows_device_os_version() -> Option { + use winreg::{enums::HKEY_LOCAL_MACHINE, RegKey}; + + let current_version = RegKey::predef(HKEY_LOCAL_MACHINE) + .open_subkey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion") + .ok()?; + + let product_name = current_version + .get_value::("ProductName") + .ok() + .map(|value| normalize_windows_product_name(&value, ¤t_version)) + .filter(|value| !value.trim().is_empty()); + let display_version = current_version + .get_value::("DisplayVersion") + .ok() + .or_else(|| current_version.get_value::("ReleaseId").ok()) + .filter(|value| !value.trim().is_empty()); + let build = current_version + .get_value::("CurrentBuildNumber") + .ok() + .or_else(|| current_version.get_value::("CurrentBuild").ok()) + .filter(|value| !value.trim().is_empty()); + let ubr = current_version.get_value::("UBR").ok(); + let build = match (build, ubr) { + (Some(build), Some(ubr)) => Some(format!("{build}.{ubr}")), + (build, _) => build, + }; + + let mut parts = Vec::new(); + if let Some(product_name) = product_name { + parts.push(product_name); + } + if let Some(display_version) = display_version { + parts.push(display_version); + } + if let Some(build) = build { + parts.push(format!("build {build}")); + } + + let version = parts.join(" | "); + (!version.is_empty()).then_some(version) +} + +#[cfg(windows)] +fn normalize_windows_product_name(value: &str, current_version: &winreg::RegKey) -> String { + let trimmed = value.trim(); + let build_number = current_version + .get_value::("CurrentBuildNumber") + .ok() + .or_else(|| current_version.get_value::("CurrentBuild").ok()) + .and_then(|value| value.parse::().ok()) + .unwrap_or_default(); + + if build_number >= 22000 && trimmed.starts_with("Windows 10") { + return trimmed.replacen("Windows 10", "Windows 11", 1); + } + + trimmed.to_string() +} + fn now_timestamp() -> String { let seconds = SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/src-tauri/tests/command_tests.rs b/src-tauri/tests/command_tests.rs index 47f19ad..e931152 100644 --- a/src-tauri/tests/command_tests.rs +++ b/src-tauri/tests/command_tests.rs @@ -1,42 +1,19 @@ -#[path = "../src/activity.rs"] -mod activity; -#[path = "../src/commands.rs"] -mod commands; -#[path = "../src/component_detection.rs"] -mod component_detection; -#[path = "../src/models.rs"] -mod models; -#[path = "../src/process.rs"] -mod process; -#[path = "../src/adapters/proxifyre.rs"] -mod proxifyre; -#[path = "../src/adapters/proxy_router.rs"] -mod proxy_router; -#[path = "../src/adapters/singbox.rs"] -mod singbox; -#[path = "../src/singbox_service.rs"] -mod singbox_service; -#[path = "../src/storage.rs"] -mod storage; -#[path = "../src/subscription.rs"] -mod subscription; -#[path = "../src/validation.rs"] -mod validation; - -use commands::{ - apply_profiles_with_services, build_status, read_saved_state_with_proxifyre_config, - resolve_component_statuses, resolve_preview, save_profile_to_storage, save_target_to_storage, - Clock, CommandError, DetectedProxyApplyHelper, HelperApplyRequest, HelperApplyResult, - ProfileInputDto, ProfileItemInputDto, ProxyApplyHelper, TargetInputDto, +use proxywarden_lib::adapters::proxifyre::ProxiFyreAdapter; +use proxywarden_lib::commands::{ + self, apply_profiles_with_services, apply_profiles_with_services_and_detection, build_status, + read_saved_state_with_proxifyre_config, resolve_component_statuses, resolve_preview, + save_profile_to_storage, save_target_to_storage, Clock, CommandError, DetectedProxyApplyHelper, + HelperApplyRequest, HelperApplyResult, ProfileInputDto, ProfileItemInputDto, ProxyApplyHelper, + TargetInputDto, }; -use component_detection::{ +use proxywarden_lib::component_detection::{ DetectedProxyfier, ProxyfierDetectionHost, ProxyfierEngine, RegistryInstallEntry, }; -use models::{ - ComponentId, ComponentState, ComponentStatus, Profile, ProfileItem, ProfileItemType, Protocol, - ProxyProtocol, Target, TargetKind, +use proxywarden_lib::models::{ + self, ComponentId, ComponentState, ComponentStatus, Profile, ProfileItem, ProfileItemType, + Protocol, ProxyProtocol, Target, TargetKind, }; -use proxifyre::ProxiFyreAdapter; +use proxywarden_lib::storage::JsonStorage; use std::collections::HashSet; use std::fs; use std::net::TcpListener; @@ -44,7 +21,6 @@ use std::path::{Path, PathBuf}; #[cfg(windows)] use std::process::Command as ProcessCommand; use std::time::{SystemTime, UNIX_EPOCH}; -use storage::JsonStorage; #[test] fn save_commands_normalize_and_persist_profile_and_target() { @@ -354,11 +330,13 @@ fn apply_blocks_local_singbox_target_when_component_is_missing() { .expect("write targets"); write_json(&storage.paths().components_file, &[singbox_missing()]); - let error = apply_profiles_with_services( + let error = apply_profiles_with_services_and_detection( &storage, &ProxiFyreAdapter::default(), &MockApplyHelper, &FixedClock, + None, + None, ) .expect_err("missing sing-box should block local target apply"); let activity = storage.read_activity().expect("read blocked activity"); diff --git a/src-tauri/tests/component_detection_tests.rs b/src-tauri/tests/component_detection_tests.rs index ed73dc8..30efbe2 100644 --- a/src-tauri/tests/component_detection_tests.rs +++ b/src-tauri/tests/component_detection_tests.rs @@ -1,16 +1,9 @@ -#[path = "../src/component_detection.rs"] -mod component_detection; -#[path = "../src/models.rs"] -mod models; -#[path = "../src/process.rs"] -mod process; - -use component_detection::{ +use proxywarden_lib::component_detection::{ detect_proxyfier_install_with_host, detect_singbox_install_with_host, proxyfier_component_from_detection, singbox_component_from_detection, ProxyfierDetectionHost, ProxyfierEngine, RegistryInstallEntry, }; -use models::ComponentState; +use proxywarden_lib::models::ComponentState; use std::{ collections::{HashMap, HashSet}, path::{Path, PathBuf}, diff --git a/src-tauri/tests/domain_tests.rs b/src-tauri/tests/domain_tests.rs index a06428f..0d6e424 100644 --- a/src-tauri/tests/domain_tests.rs +++ b/src-tauri/tests/domain_tests.rs @@ -1,13 +1,8 @@ -#[path = "../src/models.rs"] -mod models; -#[path = "../src/validation.rs"] -mod validation; - -use models::{ +use proxywarden_lib::models::{ ComponentId, ProfileInput, ProfileItemInput, ProfileItemType, Protocol, ProxyProtocol, TargetInput, TargetKind, }; -use validation::{normalize_profile, normalize_target}; +use proxywarden_lib::validation::{normalize_profile, normalize_target}; #[test] fn normalizes_profile_source_items() { diff --git a/src-tauri/tests/helper_tests.rs b/src-tauri/tests/helper_tests.rs index 36d1744..091518c 100644 --- a/src-tauri/tests/helper_tests.rs +++ b/src-tauri/tests/helper_tests.rs @@ -1,14 +1,9 @@ -#[path = "../src/helper.rs"] -mod helper; -#[path = "../src/models.rs"] -mod models; - -use helper::{ +use proxywarden_lib::helper::{ helper_action_requires_elevation, install_request, parse_helper_response, proxifyre_apply_request, service_request, HelperAction, HelperCommandOutput, HelperCommandRunner, HelperCommandSpec, HelperError, HelperResponse, StructuredHelper, }; -use models::ComponentId; +use proxywarden_lib::models::ComponentId; use serde_json::json; use std::cell::RefCell; use std::path::PathBuf; diff --git a/src-tauri/tests/proxifyre_adapter_tests.rs b/src-tauri/tests/proxifyre_adapter_tests.rs index fe70682..6e03c94 100644 --- a/src-tauri/tests/proxifyre_adapter_tests.rs +++ b/src-tauri/tests/proxifyre_adapter_tests.rs @@ -1,16 +1,13 @@ -#[path = "../src/models.rs"] -mod models; -#[path = "../src/adapters/proxifyre.rs"] -mod proxifyre; -#[path = "../src/adapters/proxy_router.rs"] -mod proxy_router; - -use models::{ +use proxywarden_lib::adapters::proxifyre::{ + ProxiFyreAdapter, ProxiFyreConfig, PROXIFYRE_OUTPUT_FILE, +}; +use proxywarden_lib::adapters::proxy_router::{ + ProxyRouterAdapter, ProxyRouterErrorKind, ProxyRouterRequest, +}; +use proxywarden_lib::models::{ ComponentId, ComponentState, ComponentStatus, Profile, ProfileItem, ProfileItemType, Protocol, ProxyProtocol, Target, TargetKind, }; -use proxifyre::{ProxiFyreAdapter, ProxiFyreConfig, PROXIFYRE_OUTPUT_FILE}; -use proxy_router::{ProxyRouterAdapter, ProxyRouterErrorKind, ProxyRouterRequest}; #[test] fn generates_proxifyre_config_for_discord_external_socks5_target() { diff --git a/src-tauri/tests/singbox_adapter_tests.rs b/src-tauri/tests/singbox_adapter_tests.rs index b3f5e15..ade9e36 100644 --- a/src-tauri/tests/singbox_adapter_tests.rs +++ b/src-tauri/tests/singbox_adapter_tests.rs @@ -1,26 +1,15 @@ -#[path = "../src/models.rs"] -mod models; -#[path = "../src/process.rs"] -mod process; -#[path = "../src/adapters/proxifyre.rs"] -mod proxifyre; -#[path = "../src/adapters/proxy_router.rs"] -mod proxy_router; -#[path = "../src/adapters/singbox.rs"] -mod singbox; - -use models::{ - ComponentId, ComponentState, ComponentStatus, LocalSingBoxConfig, Profile, ProfileItem, - ProfileItemType, Protocol, ProxyProtocol, SubscriptionCache, SubscriptionServer, Target, - TargetKind, -}; -use proxifyre::{ProxiFyreAdapter, ProxiFyreConfig}; -use proxy_router::{ProxyRouterAdapter, ProxyRouterRequest}; -use singbox::{ +use proxywarden_lib::adapters::proxifyre::{ProxiFyreAdapter, ProxiFyreConfig}; +use proxywarden_lib::adapters::proxy_router::{ProxyRouterAdapter, ProxyRouterRequest}; +use proxywarden_lib::adapters::singbox::{ SingBoxAdapter, SingBoxCheckResult, SingBoxConfigChecker, SingBoxConfigError, SingBoxConfigErrorKind, SingBoxGenerationRequest, DEFAULT_VPN_OUTBOUND_TAG, SINGBOX_OUTPUT_FILE, }; +use proxywarden_lib::models::{ + ComponentId, ComponentState, ComponentStatus, LocalSingBoxConfig, Profile, ProfileItem, + ProfileItemType, Protocol, ProxyProtocol, SubscriptionCache, SubscriptionServer, Target, + TargetKind, +}; use std::{ cell::RefCell, path::{Path, PathBuf}, diff --git a/src-tauri/tests/singbox_command_tests.rs b/src-tauri/tests/singbox_command_tests.rs index e87ef09..5716137 100644 --- a/src-tauri/tests/singbox_command_tests.rs +++ b/src-tauri/tests/singbox_command_tests.rs @@ -1,44 +1,22 @@ -#[path = "../src/activity.rs"] -mod activity; -#[path = "../src/commands.rs"] -mod commands; -#[path = "../src/component_detection.rs"] -mod component_detection; -#[path = "../src/models.rs"] -mod models; -#[path = "../src/process.rs"] -mod process; -#[path = "../src/adapters/proxifyre.rs"] -mod proxifyre; -#[path = "../src/adapters/proxy_router.rs"] -mod proxy_router; -#[path = "../src/adapters/singbox.rs"] -mod singbox; -#[path = "../src/singbox_service.rs"] -mod singbox_service; -#[path = "../src/storage.rs"] -mod storage; -#[path = "../src/subscription.rs"] -mod subscription; -#[path = "../src/validation.rs"] -mod validation; - -use commands::{ +use proxywarden_lib::adapters::singbox::{ + SingBoxAdapter, SingBoxCheckResult, SingBoxConfigChecker, SingBoxConfigError, +}; +use proxywarden_lib::commands::{ fetch_singbox_subscription_with_fetcher, forget_singbox_subscription_in_storage, generate_singbox_config_with_services, save_singbox_subscription_to_storage, select_singbox_server_in_storage, Clock, SaveSingBoxSubscriptionInputDto, SelectSingBoxServerInputDto, SubscriptionFetcher, }; -use models::{ +use proxywarden_lib::models::{ ActivityLevel, ComponentId, LocalSingBoxConfig, ProxyProtocol, SubscriptionCache, SubscriptionServer, TargetKind, }; +use proxywarden_lib::storage::JsonStorage; +use proxywarden_lib::subscription; use serde_json::{json, Map}; -use singbox::{SingBoxAdapter, SingBoxCheckResult, SingBoxConfigChecker, SingBoxConfigError}; use std::fs; use std::path::{Path, PathBuf}; use std::time::{SystemTime, UNIX_EPOCH}; -use storage::JsonStorage; #[test] fn saves_subscription_url_without_exposing_secret_query() { diff --git a/src-tauri/tests/singbox_service_tests.rs b/src-tauri/tests/singbox_service_tests.rs index 125756d..13ce026 100644 --- a/src-tauri/tests/singbox_service_tests.rs +++ b/src-tauri/tests/singbox_service_tests.rs @@ -1,14 +1,5 @@ -#[path = "../src/component_detection.rs"] -mod component_detection; -#[path = "../src/models.rs"] -mod models; -#[path = "../src/process.rs"] -mod process; -#[path = "../src/singbox_service.rs"] -mod singbox_service; - -use component_detection::DetectedSingBox; -use singbox_service::{ +use proxywarden_lib::component_detection::DetectedSingBox; +use proxywarden_lib::singbox_service::{ build_singbox_setup_status, ensure_safe_singbox_install_dir, parse_service_command_output, service_control_script, SingBoxServiceAction, }; diff --git a/src-tauri/tests/storage_tests.rs b/src-tauri/tests/storage_tests.rs index 78e6a98..a23d44f 100644 --- a/src-tauri/tests/storage_tests.rs +++ b/src-tauri/tests/storage_tests.rs @@ -1,19 +1,12 @@ -#[path = "../src/activity.rs"] -mod activity; -#[path = "../src/models.rs"] -mod models; -#[path = "../src/storage.rs"] -mod storage; - -use models::{ +use proxywarden_lib::models::{ ActivityEntry, ActivityLevel, ComponentId, ComponentState, ComponentStatus, LocalSingBoxConfig, Profile, ProfileItem, ProfileItemType, Protocol, ProxyProtocol, SubscriptionCache, SubscriptionServer, Target, TargetKind, }; +use proxywarden_lib::storage::{backup_path, default_config_root, JsonStorage, StoragePaths}; use std::fs; use std::path::{Path, PathBuf}; use std::time::{SystemTime, UNIX_EPOCH}; -use storage::{backup_path, default_config_root, JsonStorage, StoragePaths}; #[test] fn storage_defaults_to_programdata_root() { diff --git a/src-tauri/tests/subscription_tests.rs b/src-tauri/tests/subscription_tests.rs index aac2f32..e92d105 100644 --- a/src-tauri/tests/subscription_tests.rs +++ b/src-tauri/tests/subscription_tests.rs @@ -1,14 +1,11 @@ -#[path = "../src/models.rs"] -mod models; -#[path = "../src/subscription.rs"] -mod subscription; - use base64::{engine::general_purpose, Engine}; -use models::redact_subscription_url; +use proxywarden_lib::models::redact_subscription_url; +use proxywarden_lib::subscription::{ + self, parse_subscription_body, parse_user_info, SubscriptionFetchIdentity, +}; use std::io::{Read, Write}; use std::net::TcpListener; use std::time::Duration; -use subscription::{parse_subscription_body, parse_user_info, SubscriptionFetchIdentity}; #[test] fn parses_singbox_json_config_servers() { @@ -130,7 +127,8 @@ fn fetch_subscription_sends_device_hwid_header_when_identity_is_set() { String::from_utf8_lossy(&request).to_ascii_lowercase() }); - let identity = SubscriptionFetchIdentity::with_device_hwid(Some("hwid-abc123")); + let mut identity = SubscriptionFetchIdentity::with_device_hwid(Some("hwid-abc123")); + identity.device_os_version = Some("Windows 11 Pro | 25H2 | build 26200.8655".to_string()); let cache = subscription::fetch_subscription_with_identity(&url, &identity) .expect("fetch subscription through local test server"); let request = request_thread.join().expect("request thread"); @@ -140,6 +138,7 @@ fn fetch_subscription_sends_device_hwid_header_when_identity_is_set() { assert!(request.contains("user-agent: proxywarden/")); assert!(request.contains("x-app-name: proxywarden")); assert!(request.contains("x-device-os:")); + assert!(request.contains("x-device-os-version: windows 11 pro | 25h2 | build 26200.8655")); assert!(request.contains("x-device-model: proxywarden")); }