mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-01 20:22:19 +08:00
fix(enhance): replace DNS fields in merge overrides
Replace DNS map fields and hosts instead of retaining old entries, while preserving unspecified DNS fields. Keep DNS override priority and explicit empty values intact. Refs #7993
This commit is contained in:
+5
-3
@@ -6,14 +6,16 @@
|
||||
- 修复不同来源的订阅或规则集共用缓存路径时,服务模式无法启动内核的问题
|
||||
- 优化文件错误日志,显示具体文件路径和失败原因
|
||||
- 修复 DNS 覆写:空内容报错、初始值覆盖已有设置、已填写字段残留原配置、空字段在高级编辑器中缺失
|
||||
- 修复 Merge 覆写 DNS 策略、过滤条件和 hosts 时,旧条目仍被保留的问题
|
||||
- 修复首次以管理员身份启动后,系统服务因数据目录所有权不符而拒绝启动内核的问题
|
||||
|
||||
**🖥️ Windows**
|
||||
|
||||
- 修复 Windows 服务因路径或目录权限问题无法启动时,缺少重新安装服务的提示
|
||||
|
||||
**🐧 Linux**
|
||||
|
||||
- Linux 修复安装或修复服务后仍无法启动内核、开启 TUN 模式的问题
|
||||
**🖥️ Windows**
|
||||
|
||||
- 修复 Windows 服务因路径或目录权限问题无法启动时,缺少重新安装服务的提示
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use clash_verge_logging::{Type, logging};
|
||||
|
||||
use super::field::use_lowercase;
|
||||
use serde_yaml_ng::{self, Mapping, Value};
|
||||
|
||||
@@ -18,20 +16,19 @@ fn deep_merge(a: &mut Value, b: Value) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_merge(merge: &Mapping, config: Mapping) -> Mapping {
|
||||
let mut config = Value::from(config);
|
||||
let merge = use_lowercase(merge);
|
||||
|
||||
deep_merge(&mut config, Value::from(merge));
|
||||
|
||||
config.as_mapping().cloned().unwrap_or_else(|| {
|
||||
logging!(
|
||||
error,
|
||||
Type::Core,
|
||||
"Failed to convert merged config to mapping, using empty mapping"
|
||||
);
|
||||
Mapping::new()
|
||||
})
|
||||
pub fn use_merge(merge: &Mapping, mut config: Mapping) -> Mapping {
|
||||
for (key, value) in use_lowercase(merge) {
|
||||
match (config.get_mut(&key), value) {
|
||||
(Some(Value::Mapping(existing)), Value::Mapping(overlay)) if key.as_str() == Some("dns") => {
|
||||
existing.extend(overlay);
|
||||
}
|
||||
(Some(existing), value) if key.as_str() != Some("hosts") => deep_merge(existing, value),
|
||||
(_, value) => {
|
||||
config.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -54,18 +51,49 @@ fn test_merge() -> anyhow::Result<()> {
|
||||
enable: true
|
||||
dns:
|
||||
enable: true
|
||||
nameserver-policy:
|
||||
new.example: 1.1.1.1
|
||||
fallback-filter:
|
||||
geoip: false
|
||||
hosts:
|
||||
new.example: 192.0.2.2
|
||||
";
|
||||
|
||||
let config = r"
|
||||
rules:
|
||||
- aaaaa
|
||||
script1: test
|
||||
tun:
|
||||
mtu: 1500
|
||||
dns:
|
||||
nameserver: [9.9.9.9]
|
||||
nameserver-policy:
|
||||
old.example: 8.8.8.8
|
||||
fallback-filter:
|
||||
geoip: true
|
||||
domain: [old.example]
|
||||
hosts:
|
||||
old.example: 192.0.2.1
|
||||
";
|
||||
|
||||
let merge = serde_yaml_ng::from_str::<Mapping>(merge)?;
|
||||
let config = serde_yaml_ng::from_str::<Mapping>(config)?;
|
||||
|
||||
let _ = serde_yaml_ng::to_string(&use_merge(&merge, config))?;
|
||||
let result = use_merge(&merge, config);
|
||||
assert_eq!(result["dns"]["nameserver-policy"], merge["dns"]["nameserver-policy"]);
|
||||
assert_eq!(result["dns"]["fallback-filter"], merge["dns"]["fallback-filter"]);
|
||||
assert_eq!(result["hosts"], merge["hosts"]);
|
||||
assert_eq!(result["dns"]["nameserver"], serde_yaml_ng::to_value(["9.9.9.9"])?);
|
||||
assert_eq!(result["tun"]["mtu"], Value::from(1500));
|
||||
assert_eq!(result["rules"], merge["rules"]);
|
||||
assert_eq!(result["script1"], Value::from("test"));
|
||||
|
||||
let cleared = serde_yaml_ng::from_str::<Mapping>(
|
||||
"dns: {enable: false, nameserver-policy: {}, fallback-filter: {}, nameserver: []}\nhosts: {}",
|
||||
)?;
|
||||
let result = use_merge(&cleared, result);
|
||||
assert_eq!(result["dns"], cleared["dns"]);
|
||||
assert_eq!(result["hosts"], cleared["hosts"]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1433,6 +1433,38 @@ mod tests {
|
||||
serde_yaml_ng::from_str(yaml).expect("test config should be valid")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_replaces_dns_fields_with_or_without_dns_settings() {
|
||||
let profile = mapping(
|
||||
"dns: {nameserver: [9.9.9.9], nameserver-policy: {profile.example: 9.9.9.9}}\n\
|
||||
hosts: {profile.example: 192.0.2.1}",
|
||||
);
|
||||
let settings = mapping(
|
||||
"dns: {ipv6: true, nameserver: [8.8.8.8], nameserver-policy: {settings.example: 8.8.8.8}}\n\
|
||||
hosts: {settings.example: 192.0.2.2}",
|
||||
);
|
||||
let merge = mapping(
|
||||
"dns: {ipv6: false, nameserver-policy: {merge.example: 1.1.1.1}}\n\
|
||||
hosts: {merge.example: 192.0.2.3}",
|
||||
);
|
||||
|
||||
for enabled in [false, true] {
|
||||
let (config, owns_ipv6) = if enabled {
|
||||
super::merge_dns_config(profile.clone(), settings.clone())
|
||||
} else {
|
||||
(profile.clone(), false)
|
||||
};
|
||||
let authoritative = AuthoritativeFields::capture(&config, &[], owns_ipv6);
|
||||
let result = authoritative.enforce(super::use_merge(&merge, config));
|
||||
|
||||
assert_eq!(result["dns"]["nameserver-policy"], merge["dns"]["nameserver-policy"]);
|
||||
assert_eq!(result["hosts"], merge["hosts"]);
|
||||
let source = if enabled { &settings } else { &profile };
|
||||
assert_eq!(result["dns"]["nameserver"], source["dns"]["nameserver"]);
|
||||
assert_eq!(result["dns"]["ipv6"], serde_yaml_ng::Value::from(enabled));
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn manual_overrides_follow_expected_priority() {
|
||||
let mut config = mapping(
|
||||
|
||||
Reference in New Issue
Block a user