Files
clash-verge-rev/src-tauri/src/feat/dns.rs
T
Tunglies 6e08a77a84 refactor(backend): specta ipc bindings (#8195)
* 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
2026-10-12 06:16:05 +00:00

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)
}