Refactor application structure and simplify implementation

This commit is contained in:
2026-07-22 00:08:09 +03:00
parent dbba3806cc
commit 90b2eb507c
74 changed files with 11362 additions and 6814 deletions
+4 -1
View File
@@ -205,7 +205,10 @@ fn app_names_for_profile(profile: &Profile) -> Vec<String> {
ProfileItemType::Process | ProfileItemType::Folder | ProfileItemType::Exe => value,
};
if !names.iter().any(|existing| existing == app_name) {
if !names
.iter()
.any(|existing: &String| existing.eq_ignore_ascii_case(app_name))
{
names.push(app_name.to_string());
}
}
+74 -39
View File
@@ -1,12 +1,8 @@
use crate::models::{LocalSingBoxConfig, SubscriptionCache};
use crate::models::{LocalSingBoxConfig, SubscriptionCache, SubscriptionServer};
use crate::process::command_no_window;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::{
env, fs,
path::Path,
time::{SystemTime, UNIX_EPOCH},
};
use std::{env, fs, fs::OpenOptions, io::Write, path::Path};
pub const SINGBOX_ADAPTER_ID: &str = "singbox";
pub const SINGBOX_OUTPUT_FILE: &str = "sing-box-config.json";
@@ -43,23 +39,36 @@ impl SingBoxAdapter {
checker: &C,
) -> Result<SingBoxGeneratedConfig, SingBoxConfigError>
where
C: SingBoxConfigChecker,
C: SingBoxConfigChecker + ?Sized,
{
let selected_server_tag = request
let selected_server = request
.config
.selected_server_tag
.selected_server_id
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty())
.and_then(|id| {
request
.subscription_cache
.servers
.iter()
.find(|server| server.id == id)
})
.or_else(|| {
let tag = request.config.selected_server_tag.as_deref()?;
request
.subscription_cache
.servers
.iter()
.find(|server| server.tag == tag)
})
.ok_or_else(|| {
SingBoxConfigError::new(
SingBoxConfigErrorKind::MissingSelectedServer,
"Сервер Local sing-box не выбран",
"Сервер Local sing-box не выбран или отсутствует в текущей подписке",
)
})?;
let vpn_outbound = selected_outbound(
&request.subscription_cache.config,
selected_server_tag,
selected_server,
&self.vpn_outbound_tag,
)?;
let generated_config = json!({
@@ -105,7 +114,7 @@ impl SingBoxAdapter {
adapter_id: SINGBOX_ADAPTER_ID.to_string(),
output_file_name: SINGBOX_OUTPUT_FILE.to_string(),
contents,
selected_server_tag: selected_server_tag.to_string(),
selected_server_tag: selected_server.tag.clone(),
listen: request.config.listen_host.clone(),
listen_port: request.config.listen_port,
check,
@@ -200,20 +209,37 @@ impl SingBoxConfigChecker for SingBoxCommandChecker {
config_json: &str,
) -> Result<SingBoxCheckResult, SingBoxConfigError> {
let config_path = env::temp_dir().join(format!(
"proxywarden-sing-box-{}-{}.json",
std::process::id(),
now_millis()
"proxywarden-sing-box-{}.json",
uuid::Uuid::new_v4().hyphenated()
));
fs::write(&config_path, config_json).map_err(|error| {
SingBoxConfigError::new(
SingBoxConfigErrorKind::CheckFailed,
format!(
"Не удалось записать временный конфиг sing-box '{}': {error}",
config_path.display()
),
)
})?;
{
let mut config_file = OpenOptions::new()
.write(true)
.create_new(true)
.open(&config_path)
.map_err(|error| {
SingBoxConfigError::new(
SingBoxConfigErrorKind::CheckFailed,
format!(
"Не удалось создать временный конфиг sing-box '{}': {error}",
config_path.display()
),
)
})?;
let write_result = config_file.write_all(config_json.as_bytes());
drop(config_file);
if let Err(error) = write_result {
let _ = fs::remove_file(&config_path);
return Err(SingBoxConfigError::new(
SingBoxConfigErrorKind::CheckFailed,
format!(
"Не удалось записать временный конфиг sing-box '{}': {error}",
config_path.display()
),
));
}
}
let output = command_no_window(binary_path)
.arg("check")
@@ -257,7 +283,7 @@ impl SingBoxConfigChecker for SingBoxCommandChecker {
fn selected_outbound(
subscription_config: &Value,
selected_server_tag: &str,
selected_server: &SubscriptionServer,
vpn_outbound_tag: &str,
) -> Result<Value, SingBoxConfigError> {
let outbounds = subscription_config
@@ -272,15 +298,27 @@ fn selected_outbound(
let outbound = outbounds
.iter()
.find(|outbound| {
outbound
let tag_matches = outbound
.get("tag")
.and_then(Value::as_str)
.is_some_and(|tag| tag.trim() == selected_server_tag)
.is_some_and(|tag| tag.trim() == selected_server.tag);
let server_matches = outbound
.get("server")
.and_then(Value::as_str)
.is_some_and(|server| server.eq_ignore_ascii_case(&selected_server.server));
let port_matches = outbound
.get("server_port")
.and_then(Value::as_u64)
.is_some_and(|port| port == u64::from(selected_server.server_port));
tag_matches && server_matches && port_matches
})
.ok_or_else(|| {
SingBoxConfigError::new(
SingBoxConfigErrorKind::MissingSelectedOutbound,
format!("Outbound не найден: {selected_server_tag}"),
format!(
"Outbound не найден: {} ({}:{})",
selected_server.tag, selected_server.server, selected_server.server_port
),
)
})?;
let outbound_type = outbound
@@ -292,7 +330,8 @@ fn selected_outbound(
return Err(SingBoxConfigError::new(
SingBoxConfigErrorKind::UnsupportedSelectedOutbound,
format!(
"Outbound '{selected_server_tag}' имеет неподдерживаемый тип '{outbound_type}'"
"Outbound '{}' имеет неподдерживаемый тип '{outbound_type}'",
selected_server.tag
),
));
}
@@ -301,7 +340,10 @@ fn selected_outbound(
let object = outbound.as_object_mut().ok_or_else(|| {
SingBoxConfigError::new(
SingBoxConfigErrorKind::UnsupportedSelectedOutbound,
format!("Outbound '{selected_server_tag}' должен быть JSON-объектом"),
format!(
"Outbound '{}' должен быть JSON-объектом",
selected_server.tag
),
)
})?;
object.insert(
@@ -318,13 +360,6 @@ fn selected_outbound(
Ok(outbound)
}
fn now_millis() -> u128 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_millis())
.unwrap_or_default()
}
fn command_message(stdout: &str, stderr: &str) -> String {
let stdout = stdout.trim();
let stderr = stderr.trim();