From ff1ce92facfd17ef66d4e8d150b224a94741c80a Mon Sep 17 00:00:00 2001 From: wonfen Date: Tue, 22 Sep 2026 10:54:55 +0800 Subject: [PATCH] feat(dns): remember override state per profile Save the DNS override switch and session confirmation per profile so automatic protection only disables the affected profile. Preserve the legacy switch as the initial preference and show the profile scope beside the settings gear and in the DNS editor. Refs #7993 --- Changelog.md | 1 + src-tauri/src/cmd/clash.rs | 8 +- src-tauri/src/config/dns.rs | 154 ++++++++++++++++----- src-tauri/src/config/verge.rs | 7 +- src-tauri/src/enhance/mod.rs | 14 +- src-tauri/src/feat/dns.rs | 32 ++++- src/components/setting/mods/dns-viewer.tsx | 13 +- src/components/setting/setting-clash.tsx | 48 +++++-- src/locales/ar/settings.json | 1 + src/locales/de/settings.json | 1 + src/locales/en/settings.json | 1 + src/locales/es/settings.json | 1 + src/locales/fa/settings.json | 1 + src/locales/id/settings.json | 1 + src/locales/jp/settings.json | 1 + src/locales/ko/settings.json | 1 + src/locales/ru/settings.json | 1 + src/locales/tr/settings.json | 1 + src/locales/tt/settings.json | 1 + src/locales/zh/settings.json | 1 + src/locales/zhtw/settings.json | 1 + src/services/cmds.ts | 8 +- src/types/generated/i18n-keys.ts | 1 + src/types/generated/i18n-resources.ts | 1 + src/types/global.d.ts | 1 + 25 files changed, 237 insertions(+), 64 deletions(-) diff --git a/Changelog.md b/Changelog.md index 8a8b94ee9..87fca5b5f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,7 @@
🚀 优化改进 +- 优化 DNS 覆写开关:按订阅独立记忆,自动关闭仅影响当前订阅,并提示作用范围 - 优化系统服务版本检查:版本与当前应用要求不一致时,提示卸载后重新安装 - 优化预构建资源,补充 ASN 数据库文件 - 扩展配置或脚本写入由应用设置接管的字段时,弹出提示并在扩展日志中记录该值已被丢弃 diff --git a/src-tauri/src/cmd/clash.rs b/src-tauri/src/cmd/clash.rs index 9e273d290..76f059516 100644 --- a/src-tauri/src/cmd/clash.rs +++ b/src-tauri/src/cmd/clash.rs @@ -133,8 +133,12 @@ pub fn take_dns_override_notice() -> bool { } #[tauri::command] -pub async fn set_dns_override(enabled: bool, confirmation: Option) -> CmdResult { - feat::set_dns_override(enabled, confirmation) +pub async fn set_dns_override( + profile_uid: String, + enabled: bool, + confirmation: Option, +) -> CmdResult { + feat::set_dns_override(profile_uid, enabled, confirmation) .await .map_err(|error| proxy_aware_coded_error(&error, "DNS_OVERRIDE_UPDATE_FAILED")) } diff --git a/src-tauri/src/config/dns.rs b/src-tauri/src/config/dns.rs index f752f14d9..8c7efaf19 100644 --- a/src-tauri/src/config/dns.rs +++ b/src-tauri/src/config/dns.rs @@ -1,5 +1,6 @@ use anyhow::Result; use clash_verge_draft::DraftTransaction; +use serde::{Deserialize, Serialize}; use serde_yaml_ng::{Mapping, Value}; use sha2::{Digest as _, Sha256}; use smartstring::alias::String; @@ -54,6 +55,7 @@ pub(crate) fn dns_override_source(profile_uid: &str, config: &Mapping) -> Result #[derive(Debug, Clone)] pub(crate) struct DnsOverrideState { + profile_uid: String, pub source: Option, pub enabled: bool, requested: bool, @@ -61,9 +63,10 @@ pub(crate) struct DnsOverrideState { } impl DnsOverrideState { - pub fn new(source: Option, requested: bool, confirmation: Option) -> Self { + pub fn new(profile_uid: &str, source: Option, requested: bool, confirmation: Option) -> Self { let enabled = requested && (source.is_none() || source == confirmation); Self { + profile_uid: profile_uid.into(), source, enabled, requested, @@ -72,24 +75,56 @@ impl DnsOverrideState { } fn apply_to(&self, verge: &mut IVerge) -> bool { + if self.profile_uid.is_empty() { + return false; + } + let settings = verge.dns_settings_for(&self.profile_uid); // A later settings write must not be overwritten by an earlier runtime generation. - if verge.enable_dns_settings.unwrap_or(false) != self.requested - || verge.dns_override_confirmation != self.confirmation - { + if (settings.enabled, settings.confirmation.as_ref()) != (self.requested, self.confirmation.as_ref()) { return false; } let clear_confirmation = self.confirmation.is_some() && self.source != self.confirmation; - if self.enabled == self.requested && !clear_confirmation { + if self.enabled == self.requested + && !clear_confirmation + && verge.profile_dns_settings.contains_key(&self.profile_uid) + { return false; } - verge.enable_dns_settings = Some(self.enabled); - if clear_confirmation { - verge.dns_override_confirmation = None; - } + verge.profile_dns_settings.insert( + self.profile_uid.clone(), + ProfileDnsSettings { + enabled: self.enabled, + confirmation: if clear_confirmation { + None + } else { + self.confirmation.clone() + }, + }, + ); true } } +#[derive(Debug, Clone, Default, Deserialize, Serialize)] +pub struct ProfileDnsSettings { + pub enabled: bool, + // Force-enable confirmation is valid only for the current app session. + #[serde(skip)] + pub confirmation: Option, +} + +impl IVerge { + pub(crate) fn dns_settings_for(&self, profile_uid: &str) -> ProfileDnsSettings { + self.profile_dns_settings + .get(profile_uid) + .cloned() + .unwrap_or(ProfileDnsSettings { + enabled: self.enable_dns_settings.unwrap_or(false), + confirmation: None, + }) + } +} + impl Config { pub(crate) async fn sync_dns_override() -> Result<()> { let _config_write = Self::lock_config_write().await; @@ -116,7 +151,7 @@ impl Config { #[cfg(test)] #[allow(clippy::expect_used, reason = "tests assert by panicking")] mod tests { - use super::{DnsOverrideState, dns_override_source}; + use super::{DnsOverrideState, ProfileDnsSettings, dns_override_source}; use crate::config::IVerge; use anyhow::Result; use serde_yaml_ng::Mapping; @@ -152,10 +187,10 @@ mod tests { let reordered = mapping("dns:\n nameserver-policy:\n b.example: [8.8.8.8]\n a.example: 1.1.1.1\nport: 7890"); assert_eq!(source, dns_override_source("one", &reordered)?); - assert!(DnsOverrideState::new(source.clone(), true, source.clone()).enabled); - assert!(!DnsOverrideState::new(dns_override_source("two", &original)?, true, source.clone()).enabled); + assert!(DnsOverrideState::new("one", source.clone(), true, source.clone()).enabled); + assert!(!DnsOverrideState::new("two", dns_override_source("two", &original)?, true, source.clone()).enabled); let changed = mapping("dns: {nameserver-policy: {a.example: 9.9.9.9, b.example: [8.8.8.8]}}"); - assert!(!DnsOverrideState::new(dns_override_source("one", &changed)?, true, source).enabled); + assert!(!DnsOverrideState::new("one", dns_override_source("one", &changed)?, true, source).enabled); Ok(()) } @@ -163,24 +198,28 @@ mod tests { fn dns_override_confirmation_expires_on_restart() -> Result<()> { let source = Some("provider-dns".into()); let confirmed = IVerge { - enable_dns_settings: Some(true), - dns_override_confirmation: source.clone(), + profile_dns_settings: [( + "one".into(), + ProfileDnsSettings { + enabled: true, + confirmation: source.clone(), + }, + )] + .into(), ..IVerge::default() }; - assert!(DnsOverrideState::new(source.clone(), true, confirmed.dns_override_confirmation.clone()).enabled); + let settings = confirmed.dns_settings_for("one"); + assert!(DnsOverrideState::new("one", source.clone(), settings.enabled, settings.confirmation).enabled); for saved in [ serde_yaml_ng::to_string(&confirmed)?, "enable_dns_settings: true\ndns_override_confirmation: provider-dns".to_owned(), ] { let mut restarted: IVerge = serde_yaml_ng::from_str(&saved)?; - let state = DnsOverrideState::new( - source.clone(), - restarted.enable_dns_settings.unwrap_or(false), - restarted.dns_override_confirmation.clone(), - ); + let settings = restarted.dns_settings_for("one"); + let state = DnsOverrideState::new("one", source.clone(), settings.enabled, settings.confirmation); assert!(!state.enabled, "a previous session must not bypass startup protection"); assert!(state.apply_to(&mut restarted)); - assert_eq!(restarted.enable_dns_settings, Some(false)); + assert!(!restarted.dns_settings_for("one").enabled); } Ok(()) } @@ -191,21 +230,74 @@ mod tests { enable_dns_settings: Some(true), ..IVerge::default() }; - let state = DnsOverrideState::new(Some("provider-dns".into()), true, None); + let state = DnsOverrideState::new("one", Some("provider-dns".into()), true, None); assert!(state.apply_to(&mut verge)); - assert_eq!(verge.enable_dns_settings, Some(false)); + assert!(!verge.dns_settings_for("one").enabled); assert!(!state.apply_to(&mut verge)); - let disabled = DnsOverrideState::new(None, false, None); + let disabled = DnsOverrideState::new("one", None, false, None); assert!(!disabled.apply_to(&mut verge)); assert!(!disabled.enabled); - verge.enable_dns_settings = Some(true); - verge.dns_override_confirmation = Some("provider-dns".into()); + verge.profile_dns_settings.insert( + "one".into(), + ProfileDnsSettings { + enabled: true, + confirmation: Some("provider-dns".into()), + }, + ); assert!(!state.apply_to(&mut verge)); - assert_eq!(verge.enable_dns_settings, Some(true)); - assert_eq!(verge.dns_override_confirmation.as_deref(), Some("provider-dns")); - let leaving = DnsOverrideState::new(None, true, verge.dns_override_confirmation.clone()); + let settings = verge.dns_settings_for("one"); + assert!(settings.enabled); + assert_eq!(settings.confirmation.as_deref(), Some("provider-dns")); + let leaving = DnsOverrideState::new("one", None, true, settings.confirmation); assert!(leaving.apply_to(&mut verge)); - assert!(verge.dns_override_confirmation.is_none()); + assert!(verge.dns_settings_for("one").confirmation.is_none()); + } + + #[test] + fn automatic_disable_is_saved_only_for_the_affected_profile() -> Result<()> { + let mut verge = IVerge { + enable_dns_settings: Some(true), + ..IVerge::default() + }; + let first = DnsOverrideState::new("one", None, true, None); + assert!(first.apply_to(&mut verge)); + let protected = DnsOverrideState::new("two", Some("provider-dns".into()), true, None); + assert!(protected.apply_to(&mut verge)); + assert!(verge.dns_settings_for("one").enabled); + assert!(!verge.dns_settings_for("two").enabled); + assert!(verge.dns_settings_for("unvisited").enabled); + assert_eq!(verge.enable_dns_settings, Some(true)); + let restarted: IVerge = serde_yaml_ng::from_str(&serde_yaml_ng::to_string(&verge)?)?; + assert!(restarted.dns_settings_for("one").enabled); + assert!(!restarted.dns_settings_for("two").enabled); + Ok(()) + } + + #[test] + fn switching_profiles_preserves_each_confirmation_until_its_source_changes() { + let mut verge = IVerge::default(); + for uid in ["one", "two"] { + verge.profile_dns_settings.insert( + uid.into(), + ProfileDnsSettings { + enabled: true, + confirmation: Some(uid.into()), + }, + ); + } + for uid in ["one", "two", "one"] { + let settings = verge.dns_settings_for(uid); + let state = DnsOverrideState::new(uid, Some(uid.into()), settings.enabled, settings.confirmation); + assert!(state.enabled); + assert!(!state.apply_to(&mut verge)); + } + let settings = verge.dns_settings_for("two"); + let updated = DnsOverrideState::new("two", Some("updated".into()), settings.enabled, settings.confirmation); + assert!(updated.apply_to(&mut verge)); + assert!(!verge.dns_settings_for("two").enabled); + assert!(verge.dns_settings_for("two").confirmation.is_none()); + assert!(verge.dns_settings_for("one").enabled); + assert_eq!(verge.dns_settings_for("one").confirmation.as_deref(), Some("one")); } } diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index 0b04635c0..d6d6b8dfe 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -80,12 +80,11 @@ pub struct IVerge { pub enable_bypass_check: Option, - /// enable dns settings - this controls whether dns_config.yaml is applied + /// Initial DNS override preference for profiles without a saved setting. pub enable_dns_settings: Option, - // Force-enable confirmation is valid only for the current app session. - #[serde(skip)] - pub dns_override_confirmation: Option, + #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")] + pub profile_dns_settings: std::collections::BTreeMap, pub use_default_bypass: Option, diff --git a/src-tauri/src/enhance/mod.rs b/src-tauri/src/enhance/mod.rs index 9807fe489..93994ae0d 100644 --- a/src-tauri/src/enhance/mod.rs +++ b/src-tauri/src/enhance/mod.rs @@ -105,7 +105,7 @@ async fn chain_item_or_default(item: Option<&PrfItem>, default_item: impl FnOnce } } -async fn get_config_values() -> ConfigValues { +async fn get_config_values(profile_uid: &str) -> ConfigValues { let clash = Config::clash().await; let clash_arc = clash.latest_arc(); let clash_config = clash_arc.0.clone(); @@ -120,12 +120,12 @@ async fn get_config_values() -> ConfigValues { ref enable_builtin_enhanced, ref verge_socks_enabled, ref verge_http_enabled, - ref enable_dns_settings, ref enable_external_controller, .. } = **verge_arc; let enable_external_controller = enable_external_controller.unwrap_or(false); - let dns_override_confirmation = verge_arc.dns_override_confirmation.clone(); + let dns_settings = verge_arc.dns_settings_for(profile_uid); + let dns_override_confirmation = dns_settings.confirmation; let (clash_core, enable_tun, enable_builtin, socks_enabled, http_enabled, enable_dns_settings) = ( Some(verge_arc.get_valid_clash_core()), @@ -133,7 +133,7 @@ async fn get_config_values() -> ConfigValues { enable_builtin_enhanced.unwrap_or(true), verge_socks_enabled.unwrap_or(false), verge_http_enabled.unwrap_or(false), - enable_dns_settings.unwrap_or(false), + dns_settings.enabled, ); #[cfg(not(target_os = "windows"))] @@ -873,7 +873,8 @@ async fn apply_dns_settings(config: Mapping, enable_dns_settings: bool) -> (Mapp pub async fn enhance( profiles: &IProfiles, ) -> Result<(Mapping, HashSet, HashMap, DnsOverrideState)> { - let cfg_vals = get_config_values().await; + let profile_uid = profiles.current.as_deref().unwrap_or_default(); + let cfg_vals = get_config_values(profile_uid).await; let ConfigValues { clash_config, clash_core, @@ -892,7 +893,8 @@ pub async fn enhance( let profile = collect_profile_items(profiles).await?; let dns_override = DnsOverrideState::new( - dns_override_source(profiles.current.as_deref().unwrap_or_default(), &profile.config)?, + profile_uid, + dns_override_source(profile_uid, &profile.config)?, enable_dns_settings, dns_override_confirmation, ); diff --git a/src-tauri/src/feat/dns.rs b/src-tauri/src/feat/dns.rs index d711511fb..92ea7041b 100644 --- a/src-tauri/src/feat/dns.rs +++ b/src-tauri/src/feat/dns.rs @@ -1,10 +1,14 @@ -use anyhow::Result; +use anyhow::{Result, ensure}; use clash_verge_draft::DraftTransaction; use serde::Serialize; use smartstring::alias::String; use crate::{ - config::{Config, dns::dns_override_source, profiles::PROFILE_WRITE_LOCK}, + config::{ + Config, + dns::{ProfileDnsSettings, dns_override_source}, + profiles::PROFILE_WRITE_LOCK, + }, constants::files::DNS_CONFIG, core::{CoreManager, handle::Handle}, utils::dirs, @@ -17,10 +21,18 @@ pub enum DnsOverrideOutcome { ConfirmationRequired { source: String }, } -pub async fn set_dns_override(enabled: bool, confirmation: Option) -> Result { +pub async fn set_dns_override( + profile_uid: String, + enabled: bool, + confirmation: Option, +) -> Result { 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?, @@ -37,8 +49,13 @@ pub async fn set_dns_override(enabled: bool, confirmation: Option) -> Re let verge = Config::verge().await; let transaction = DraftTransaction::begin(vec![&verge])?; verge.edit_draft(|draft| { - draft.enable_dns_settings = Some(enabled); - draft.dns_override_confirmation = if enabled { source } else { None }; + draft.profile_dns_settings.insert( + profile_uid.clone(), + ProfileDnsSettings { + enabled, + confirmation: if enabled { source } else { None }, + }, + ); }); CoreManager::global().update_config_checked().await?; @@ -47,8 +64,9 @@ pub async fn set_dns_override(enabled: bool, confirmation: Option) -> Re // 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.enable_dns_settings = Some(false); - draft.dns_override_confirmation = None; + draft + .profile_dns_settings + .insert(profile_uid, ProfileDnsSettings::default()); }); if let Some(source) = state.source { outcome = DnsOverrideOutcome::ConfirmationRequired { source }; diff --git a/src/components/setting/mods/dns-viewer.tsx b/src/components/setting/mods/dns-viewer.tsx index c4cd75951..2ecdf5c46 100644 --- a/src/components/setting/mods/dns-viewer.tsx +++ b/src/components/setting/mods/dns-viewer.tsx @@ -1,5 +1,6 @@ import { RestartAltRounded } from '@mui/icons-material' import { + Alert, Box, Button, FormControl, @@ -33,6 +34,7 @@ import { Switch, } from '@/components/base' import { useClash } from '@/hooks/use-clash' +import { useProfiles } from '@/hooks/use-profiles' import { useVerge } from '@/hooks/use-verge' import { showNotice } from '@/services/notice-service' import { useThemeMode } from '@/services/states' @@ -186,6 +188,12 @@ export function DnsViewer({ ref }: { ref?: Ref }) { const { t } = useTranslation() const { mutateClash } = useClash() const { verge } = useVerge() + const { current: currentProfile } = useProfiles() + const dnsEnabled = currentProfile + ? (verge?.profile_dns_settings?.[currentProfile.uid]?.enabled ?? + verge?.enable_dns_settings ?? + false) + : false const themeMode = useThemeMode() const [open, setOpen] = useState(false) @@ -550,7 +558,7 @@ export function DnsViewer({ ref }: { ref?: Ref }) { return } - if (verge?.enable_dns_settings) { + if (dnsEnabled) { await invoke('apply_dns_config', { apply: true }) mutateClash() } @@ -638,6 +646,9 @@ export function DnsViewer({ ref }: { ref?: Ref }) { onCancel={() => setOpen(false)} onOk={onSave} > + + {t('settings.modals.dns.dialog.profileScope')} + { const { clash, version, mutateClash, patchClash } = useClash() const { verge, mutateVerge } = useVerge() + const { current: currentProfile } = useProfiles() + const dnsEnabled = currentProfile + ? (verge?.profile_dns_settings?.[currentProfile.uid]?.enabled ?? + verge?.enable_dns_settings ?? + false) + : false const displayedMixedPort = useDisplayedMixedPort() const [, setClashLog] = useClashLog() @@ -46,7 +53,10 @@ const SettingClash = ({ onError }: Props) => { 'unified-delay': unifiedDelay, } = clash ?? {} - const [dnsConfirmation, setDnsConfirmation] = useState(null) + const [dnsConfirmation, setDnsConfirmation] = useState<{ + profileUid: string + source: string + } | null>(null) const [dnsUpdating, setDnsUpdating] = useState(false) const webRef = useRef(null) @@ -72,12 +82,17 @@ const SettingClash = ({ onError }: Props) => { } const handleDnsToggle = useLockFn( - async (enable: boolean, confirmation?: string) => { + async ( + enable: boolean, + confirmation?: string, + profileUid = currentProfile?.uid, + ) => { + if (!profileUid) return setDnsUpdating(true) try { - const outcome = await setDnsOverride(enable, confirmation) + const outcome = await setDnsOverride(profileUid, enable, confirmation) if (outcome.status === 'confirmation_required') { - setDnsConfirmation(outcome.source) + setDnsConfirmation({ profileUid, source: outcome.source }) } else { setDnsConfirmation(null) } @@ -114,7 +129,12 @@ const SettingClash = ({ onError }: Props) => { onCancel={closeDnsConfirmation} onClose={closeDnsConfirmation} onOk={() => { - if (dnsConfirmation) void handleDnsToggle(true, dnsConfirmation) + if (dnsConfirmation) + void handleDnsToggle( + true, + dnsConfirmation.source, + dnsConfirmation.profileUid, + ) }} > @@ -151,16 +171,22 @@ const SettingClash = ({ onError }: Props) => { dnsRef.current?.open()} - /> + <> + dnsRef.current?.open()} + /> + + } > handleDnsToggle(checked)} /> diff --git a/src/locales/ar/settings.json b/src/locales/ar/settings.json index 412421dcb..631d1527f 100644 --- a/src/locales/ar/settings.json +++ b/src/locales/ar/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "DNS Overwrite", + "profileScope": "تُحفظ حالة مفتاح تجاوز DNS بشكل مستقل لكل ملف تعريف، وتُطبَّق على ذلك الملف فقط.", "warning": "If you are not familiar with these settings, please do not modify them and keep DNS Overwrite disabled" }, "protection": { diff --git a/src/locales/de/settings.json b/src/locales/de/settings.json index 42d2352b2..f783e0751 100644 --- a/src/locales/de/settings.json +++ b/src/locales/de/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "DNS-Überschreibung", + "profileScope": "Der DNS-Überschreibungsschalter wird für jedes Profil separat gespeichert und gilt nur für dieses Profil.", "warning": "Wenn Sie sich nicht mit diesen Einstellungen auskennen, ändern Sie sie nicht und lassen Sie die DNS-Überschreibung deaktiviert." }, "protection": { diff --git a/src/locales/en/settings.json b/src/locales/en/settings.json index 6b480e6d4..5d79df27c 100644 --- a/src/locales/en/settings.json +++ b/src/locales/en/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "DNS Overwrite", + "profileScope": "The DNS override switch is saved separately for each profile and applies only to that profile.", "warning": "If you are not familiar with these settings, please do not modify them and keep DNS Overwrite disabled" }, "protection": { diff --git a/src/locales/es/settings.json b/src/locales/es/settings.json index 3124e24d3..9cc6be44a 100644 --- a/src/locales/es/settings.json +++ b/src/locales/es/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "Sobrescritura de DNS", + "profileScope": "El estado del interruptor de sobrescritura de DNS se guarda por separado para cada perfil y solo se aplica a ese perfil.", "warning": "Si no está seguro de cómo configurar esto, no realice cambios y mantenga deshabilitada la sobrescritura de DNS." }, "protection": { diff --git a/src/locales/fa/settings.json b/src/locales/fa/settings.json index ebab6eba0..8033ef072 100644 --- a/src/locales/fa/settings.json +++ b/src/locales/fa/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "بازنویسی DNS", + "profileScope": "وضعیت کلید بازنویسی DNS برای هر پروفایل جداگانه ذخیره می‌شود و فقط بر همان پروفایل اعمال می‌شود.", "warning": "اگر با این تنظیمات آشنا نیستید، لطفاً آنها را تغییر ندهید و DNS Overwrite را غیرفعال نگه دارید." }, "protection": { diff --git a/src/locales/id/settings.json b/src/locales/id/settings.json index a9fc55815..e544fb63a 100644 --- a/src/locales/id/settings.json +++ b/src/locales/id/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "DNS Overwrite", + "profileScope": "Status sakelar penimpaan DNS disimpan secara terpisah untuk setiap profil dan hanya berlaku untuk profil tersebut.", "warning": "If you are not familiar with these settings, please do not modify them and keep DNS Overwrite disabled" }, "protection": { diff --git a/src/locales/jp/settings.json b/src/locales/jp/settings.json index ac871770f..eb5ecee34 100644 --- a/src/locales/jp/settings.json +++ b/src/locales/jp/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "DNS上書き", + "profileScope": "DNS 上書きスイッチの状態はプロファイルごとに保存され、そのプロファイルにのみ適用されます。", "warning": "ここの設定がわからない場合は、変更しないでください。DNS上書きを無効にしたままにしてください。" }, "protection": { diff --git a/src/locales/ko/settings.json b/src/locales/ko/settings.json index b43047280..35055eb98 100644 --- a/src/locales/ko/settings.json +++ b/src/locales/ko/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "DNS 덮어쓰기", + "profileScope": "DNS 덮어쓰기 스위치 상태는 프로필별로 저장되며 해당 프로필에만 적용됩니다.", "warning": "이 설정에 익숙하지 않다면 수정하지 말고 DNS 덮어쓰기를 비활성화 상태로 유지하세요" }, "protection": { diff --git a/src/locales/ru/settings.json b/src/locales/ru/settings.json index 0699c13c3..9c80fb9f7 100644 --- a/src/locales/ru/settings.json +++ b/src/locales/ru/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "Переопределение настроек DNS", + "profileScope": "Состояние переключателя переопределения DNS сохраняется отдельно для каждого профиля и действует только для него.", "warning": "Если вы не знакомы с этими настройками, пожалуйста, не изменяйте их и оставьте переопределение DNS отключённым" }, "protection": { diff --git a/src/locales/tr/settings.json b/src/locales/tr/settings.json index 0c8a12ebf..5c6d5078c 100644 --- a/src/locales/tr/settings.json +++ b/src/locales/tr/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "DNS Üzerine Yazma", + "profileScope": "DNS geçersiz kılma anahtarının durumu her profil için ayrı kaydedilir ve yalnızca o profile uygulanır.", "warning": "Bu ayarlarla ilgili bilginiz yoksa, lütfen bunları değiştirmeyin ve DNS Üzerine Yazma'yı devre dışı tutun" }, "protection": { diff --git a/src/locales/tt/settings.json b/src/locales/tt/settings.json index cb0078d81..6181ae668 100644 --- a/src/locales/tt/settings.json +++ b/src/locales/tt/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "DNS Overwrite", + "profileScope": "DNS көйләүләрен алыштыру күчергеченең хәле һәр профиль өчен аерым саклана һәм шул профильгә генә кагыла.", "warning": "If you are not familiar with these settings, please do not modify them and keep DNS Overwrite disabled" }, "protection": { diff --git a/src/locales/zh/settings.json b/src/locales/zh/settings.json index d96c10705..909cb91a3 100644 --- a/src/locales/zh/settings.json +++ b/src/locales/zh/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "DNS 覆写", + "profileScope": "DNS 覆写开关按订阅独立保存,仅对对应订阅生效。", "warning": "如果你不清楚这里的设置请不要修改,并保持 DNS 覆写关闭" }, "protection": { diff --git a/src/locales/zhtw/settings.json b/src/locales/zhtw/settings.json index b66e52f10..58baf8a58 100644 --- a/src/locales/zhtw/settings.json +++ b/src/locales/zhtw/settings.json @@ -508,6 +508,7 @@ "dns": { "dialog": { "title": "DNS 覆寫", + "profileScope": "DNS 覆寫開關依訂閱分別儲存,僅對對應訂閱生效。", "warning": "如果你不清楚這裡的設定請不要修改,並保持 DNS 覆寫關閉" }, "protection": { diff --git a/src/services/cmds.ts b/src/services/cmds.ts index 07968e9b4..a40f98650 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -172,10 +172,14 @@ export async function patchVergeConfig(payload: IVergeConfig) { return invoke('patch_verge_config', { payload }) } -export async function setDnsOverride(enabled: boolean, confirmation?: string) { +export async function setDnsOverride( + profileUid: string, + enabled: boolean, + confirmation?: string, +) { return invoke< { status: 'applied' } | { status: 'confirmation_required'; source: string } - >('set_dns_override', { enabled, confirmation }) + >('set_dns_override', { profileUid, enabled, confirmation }) } export async function takeDnsOverrideNotice() { diff --git a/src/types/generated/i18n-keys.ts b/src/types/generated/i18n-keys.ts index 44ee22b36..972a2b33d 100644 --- a/src/types/generated/i18n-keys.ts +++ b/src/types/generated/i18n-keys.ts @@ -699,6 +699,7 @@ export const translationKeys = [ 'settings.modals.tun.messages.invalidRouteExcludeAddress', 'settings.modals.tun.messages.routeExcludeAddressHint', 'settings.modals.dns.dialog.title', + 'settings.modals.dns.dialog.profileScope', 'settings.modals.dns.dialog.warning', 'settings.modals.dns.protection.title', 'settings.modals.dns.protection.message', diff --git a/src/types/generated/i18n-resources.ts b/src/types/generated/i18n-resources.ts index f90c3326d..076b98c6e 100644 --- a/src/types/generated/i18n-resources.ts +++ b/src/types/generated/i18n-resources.ts @@ -955,6 +955,7 @@ export interface TranslationResources { } dns: { dialog: { + profileScope: string title: string warning: string } diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 33ee5c50b..13da2141e 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -890,6 +890,7 @@ interface IVergeConfig { enable_system_proxy?: boolean enable_global_hotkey?: boolean enable_dns_settings?: boolean + profile_dns_settings?: Record proxy_auto_config?: boolean pac_file_content?: string proxy_host?: string