mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-22 02:22:08 +08:00
* refactor(ipc): generate bindings with Specta * fix(profile): project IPC inputs to consumed fields * fix(events): remove unused refresh payloads * fix(webdav): return only consumed backup metadata * fix(network): trim unused interface metadata * fix(sysinfo): return only displayed system fields * chore(knip): check generated runtime exports * fix(proxy): confirm runtime chain updates before UI changes * fix(clipboard): propagate proxy environment copy failures * refactor(dns): remove ignored runtime patch * refactor(core): trim unused upgrade response fields * refactor(ipc): avoid redundant response copies * refactor(proxy): clarify automatic proxy query naming * chore(tests): remove added IPC regression tests * refactor(ipc): enforce typed command boundaries * chore(contract): check generated bindings before push * refactor(ipc): remove unused proxy liveness payloads
88 lines
2.8 KiB
Rust
88 lines
2.8 KiB
Rust
use crate::core::notify::{Refresh, announce};
|
|
use anyhow::{Result, ensure};
|
|
use clash_verge_draft::DraftTransaction;
|
|
use serde::Serialize;
|
|
use smartstring::alias::String;
|
|
|
|
use crate::{
|
|
config::{
|
|
Config,
|
|
dns::{ProfileDnsSettings, dns_override_source},
|
|
profiles::PROFILE_WRITE_LOCK,
|
|
},
|
|
constants::files::DNS_CONFIG,
|
|
core::CoreManager,
|
|
utils::dirs,
|
|
};
|
|
|
|
#[derive(Serialize, specta::Type)]
|
|
#[serde(tag = "status", rename_all = "snake_case")]
|
|
pub enum DnsOverrideOutcome {
|
|
Applied,
|
|
ConfirmationRequired {
|
|
#[specta(type = std::string::String)]
|
|
source: String,
|
|
},
|
|
}
|
|
|
|
pub async fn set_dns_override(
|
|
profile_uid: String,
|
|
enabled: bool,
|
|
confirmation: Option<String>,
|
|
) -> Result<DnsOverrideOutcome> {
|
|
let _profile_write = PROFILE_WRITE_LOCK.lock().await;
|
|
let _config_write = Config::lock_config_write().await;
|
|
let profiles = Config::profiles().await.data_arc();
|
|
ensure!(
|
|
profiles.current.as_ref() == Some(&profile_uid),
|
|
"The active profile changed; retry the DNS setting"
|
|
);
|
|
let source = dns_override_source(
|
|
profiles.current.as_deref().unwrap_or_default(),
|
|
&profiles.current_mapping().await?,
|
|
)?;
|
|
if enabled && let Some(source) = source.as_ref().filter(|source| Some(*source) != confirmation.as_ref()) {
|
|
return Ok(DnsOverrideOutcome::ConfirmationRequired { source: source.clone() });
|
|
}
|
|
|
|
if enabled {
|
|
let content = tokio::fs::read_to_string(dirs::app_home_dir()?.join(DNS_CONFIG)).await?;
|
|
serde_yaml_ng::from_str::<serde_yaml_ng::Mapping>(&content)?;
|
|
}
|
|
|
|
let verge = Config::verge().await;
|
|
let transaction = DraftTransaction::begin(vec![&verge])?;
|
|
verge.edit_draft(|draft| {
|
|
draft.profile_dns_settings.insert(
|
|
profile_uid.clone(),
|
|
ProfileDnsSettings {
|
|
enabled,
|
|
confirmation: if enabled { source } else { None },
|
|
},
|
|
);
|
|
});
|
|
CoreManager::global().update_config_checked().await?;
|
|
|
|
let state = Config::runtime().await.data_arc().dns_override.clone();
|
|
let mut outcome = DnsOverrideOutcome::Applied;
|
|
// A subscription update can replace the file while Mihomo is validating it.
|
|
if enabled && let Some(state) = state.filter(|state| !state.enabled) {
|
|
verge.edit_draft(|draft| {
|
|
draft
|
|
.profile_dns_settings
|
|
.insert(profile_uid, ProfileDnsSettings::default());
|
|
});
|
|
if let Some(source) = state.source {
|
|
outcome = DnsOverrideOutcome::ConfirmationRequired { source };
|
|
}
|
|
}
|
|
|
|
transaction.commit();
|
|
// Core and memory state remain applied even when persistence fails.
|
|
let result = verge.data_arc().save_file().await;
|
|
announce(Refresh::Verge);
|
|
announce(Refresh::Clash);
|
|
result?;
|
|
Ok(outcome)
|
|
}
|