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