Refactor Tauri crate into a library and add OS version header
This commit is contained in:
@@ -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";
|
||||
|
||||
+37
-21
@@ -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<ApplyProfilesResponse, CommandError> {
|
||||
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<DetectedProxyfier>,
|
||||
detected_singbox: Option<DetectedSingBox>,
|
||||
) -> Result<ApplyProfilesResponse, CommandError> {
|
||||
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<Vec<ComponentStatus>, CommandError> {
|
||||
components_or_defaults_with_detection(
|
||||
storage,
|
||||
detect_proxyfier_install(),
|
||||
detect_singbox_install(),
|
||||
)
|
||||
}
|
||||
|
||||
fn components_or_defaults_with_detection(
|
||||
storage: &JsonStorage,
|
||||
detected_proxyfier: Option<DetectedProxyfier>,
|
||||
detected_singbox: Option<DetectedSingBox>,
|
||||
) -> Result<Vec<ComponentStatus>, 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",
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
+1
-71
@@ -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();
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ pub struct SubscriptionFetchIdentity {
|
||||
pub app_name: String,
|
||||
pub user_agent: String,
|
||||
pub device_os: String,
|
||||
pub device_os_version: Option<String>,
|
||||
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<String> {
|
||||
.map(|(_, value)| value.into_owned())
|
||||
}
|
||||
|
||||
fn sanitize_header_value(value: &str) -> String {
|
||||
value
|
||||
.chars()
|
||||
.filter(|ch| ch.is_ascii_graphic() || *ch == ' ')
|
||||
.collect::<String>()
|
||||
}
|
||||
|
||||
fn detect_device_os_version() -> Option<String> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
windows_device_os_version()
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn windows_device_os_version() -> Option<String> {
|
||||
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::<String, _>("ProductName")
|
||||
.ok()
|
||||
.map(|value| normalize_windows_product_name(&value, ¤t_version))
|
||||
.filter(|value| !value.trim().is_empty());
|
||||
let display_version = current_version
|
||||
.get_value::<String, _>("DisplayVersion")
|
||||
.ok()
|
||||
.or_else(|| current_version.get_value::<String, _>("ReleaseId").ok())
|
||||
.filter(|value| !value.trim().is_empty());
|
||||
let build = current_version
|
||||
.get_value::<String, _>("CurrentBuildNumber")
|
||||
.ok()
|
||||
.or_else(|| current_version.get_value::<String, _>("CurrentBuild").ok())
|
||||
.filter(|value| !value.trim().is_empty());
|
||||
let ubr = current_version.get_value::<u32, _>("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::<String, _>("CurrentBuildNumber")
|
||||
.ok()
|
||||
.or_else(|| current_version.get_value::<String, _>("CurrentBuild").ok())
|
||||
.and_then(|value| value.parse::<u32>().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)
|
||||
|
||||
Reference in New Issue
Block a user