mirror of
https://github.com/timothymiller/cloudflare-ddns.git
synced 2026-09-20 14:39:04 -03:00
Compare commits
12 Commits
4ef6ba1b74
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f2b543b75 | ||
|
|
7c6d5b43c1 | ||
|
|
9f4e37f175 | ||
|
|
848da5acd1 | ||
|
|
5289d2067e | ||
|
|
6d7e4d644e | ||
|
|
1faef32f1e | ||
|
|
ee46eb9c4c | ||
|
|
da4b20e252 | ||
|
|
3307adaede | ||
|
|
65f5629157 | ||
|
|
da3b90ff93 |
2
.github/workflows/image.yml
vendored
2
.github/workflows/image.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/login-action@v4
|
uses: docker/login-action@v4.6.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -8,3 +8,6 @@ debug/
|
|||||||
|
|
||||||
# Git History
|
# Git History
|
||||||
**/.history/*
|
**/.history/*
|
||||||
|
|
||||||
|
# JetBrains IDE
|
||||||
|
.idea/
|
||||||
24
Cargo.lock
generated
24
Cargo.lock
generated
@@ -39,6 +39,12 @@ version = "0.22.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.23.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.11.0"
|
version = "2.11.0"
|
||||||
@@ -469,7 +475,7 @@ version = "0.1.20"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -882,11 +888,11 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.13.4"
|
version = "0.13.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
|
checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64 0.23.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"http",
|
"http",
|
||||||
@@ -946,9 +952,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.42"
|
version = "0.23.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
|
checksum = "6725596c3f2c3a0aef021139e145d4eafe314a6623e4680ca83852b2c67ab2ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ring",
|
"ring",
|
||||||
@@ -1008,9 +1014,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.103.13"
|
version = "0.103.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
|
checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
@@ -1747,7 +1753,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "08db1edfb05d9b3c1542e521aea074442088292f00b5f28e435c714a98f85031"
|
checksum = "08db1edfb05d9b3c1542e521aea074442088292f00b5f28e435c714a98f85031"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert-json-diff",
|
"assert-json-diff",
|
||||||
"base64",
|
"base64 0.22.1",
|
||||||
"deadpool",
|
"deadpool",
|
||||||
"futures",
|
"futures",
|
||||||
"http",
|
"http",
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ pub struct LegacyConfig {
|
|||||||
pub ip4_provider: Option<String>,
|
pub ip4_provider: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub ip6_provider: Option<String>,
|
pub ip6_provider: Option<String>,
|
||||||
|
#[serde(rename = "recordComment", alias = "record_comment", default)]
|
||||||
|
pub record_comment: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_true() -> bool {
|
fn default_true() -> bool {
|
||||||
@@ -476,7 +478,7 @@ fn legacy_to_app_config(
|
|||||||
delete_on_failure: false,
|
delete_on_failure: false,
|
||||||
ttl,
|
ttl,
|
||||||
proxied_expression: None,
|
proxied_expression: None,
|
||||||
record_comment: None,
|
record_comment: legacy.record_comment.clone(),
|
||||||
managed_comment_regex: None,
|
managed_comment_regex: None,
|
||||||
waf_list_description: None,
|
waf_list_description: None,
|
||||||
waf_list_item_comment: None,
|
waf_list_item_comment: None,
|
||||||
@@ -1063,6 +1065,7 @@ mod tests {
|
|||||||
ttl: 300,
|
ttl: 300,
|
||||||
ip4_provider: None,
|
ip4_provider: None,
|
||||||
ip6_provider: None,
|
ip6_provider: None,
|
||||||
|
record_comment: None,
|
||||||
};
|
};
|
||||||
let config = legacy_to_app_config(legacy, false, false).unwrap();
|
let config = legacy_to_app_config(legacy, false, false).unwrap();
|
||||||
assert!(config.legacy_mode);
|
assert!(config.legacy_mode);
|
||||||
@@ -1091,6 +1094,7 @@ mod tests {
|
|||||||
ttl: 120,
|
ttl: 120,
|
||||||
ip4_provider: None,
|
ip4_provider: None,
|
||||||
ip6_provider: None,
|
ip6_provider: None,
|
||||||
|
record_comment: None,
|
||||||
};
|
};
|
||||||
let config = legacy_to_app_config(legacy, true, true).unwrap();
|
let config = legacy_to_app_config(legacy, true, true).unwrap();
|
||||||
assert!(
|
assert!(
|
||||||
@@ -1121,6 +1125,7 @@ mod tests {
|
|||||||
ttl: 300,
|
ttl: 300,
|
||||||
ip4_provider: None,
|
ip4_provider: None,
|
||||||
ip6_provider: None,
|
ip6_provider: None,
|
||||||
|
record_comment: None,
|
||||||
};
|
};
|
||||||
let config = legacy_to_app_config(legacy, false, false).unwrap();
|
let config = legacy_to_app_config(legacy, false, false).unwrap();
|
||||||
assert!(matches!(config.auth, Auth::Key { ref api_key, ref email }
|
assert!(matches!(config.auth, Auth::Key { ref api_key, ref email }
|
||||||
@@ -1145,6 +1150,7 @@ mod tests {
|
|||||||
ttl: 300,
|
ttl: 300,
|
||||||
ip4_provider: Some("ipify".to_string()),
|
ip4_provider: Some("ipify".to_string()),
|
||||||
ip6_provider: Some("cloudflare.doh".to_string()),
|
ip6_provider: Some("cloudflare.doh".to_string()),
|
||||||
|
record_comment: None,
|
||||||
};
|
};
|
||||||
let config = legacy_to_app_config(legacy, false, false).unwrap();
|
let config = legacy_to_app_config(legacy, false, false).unwrap();
|
||||||
assert!(matches!(config.providers[&IpType::V4], ProviderType::Ipify));
|
assert!(matches!(config.providers[&IpType::V4], ProviderType::Ipify));
|
||||||
@@ -1172,6 +1178,7 @@ mod tests {
|
|||||||
ttl: 300,
|
ttl: 300,
|
||||||
ip4_provider: Some("none".to_string()),
|
ip4_provider: Some("none".to_string()),
|
||||||
ip6_provider: None,
|
ip6_provider: None,
|
||||||
|
record_comment: None,
|
||||||
};
|
};
|
||||||
let config = legacy_to_app_config(legacy, false, false).unwrap();
|
let config = legacy_to_app_config(legacy, false, false).unwrap();
|
||||||
// ip4_provider=none should exclude V4 even though a=true
|
// ip4_provider=none should exclude V4 even though a=true
|
||||||
@@ -1197,6 +1204,7 @@ mod tests {
|
|||||||
ttl: 300,
|
ttl: 300,
|
||||||
ip4_provider: Some("totally_invalid".to_string()),
|
ip4_provider: Some("totally_invalid".to_string()),
|
||||||
ip6_provider: None,
|
ip6_provider: None,
|
||||||
|
record_comment: None,
|
||||||
};
|
};
|
||||||
let result = legacy_to_app_config(legacy, false, false);
|
let result = legacy_to_app_config(legacy, false, false);
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
@@ -1204,6 +1212,30 @@ mod tests {
|
|||||||
assert!(err.contains("ip4_provider"));
|
assert!(err.contains("ip4_provider"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_legacy_to_app_config_with_record_comment() {
|
||||||
|
let legacy = LegacyConfig {
|
||||||
|
cloudflare: vec![LegacyCloudflareEntry {
|
||||||
|
authentication: LegacyAuthentication {
|
||||||
|
api_token: "tok".to_string(),
|
||||||
|
api_key: None,
|
||||||
|
},
|
||||||
|
zone_id: "z".to_string(),
|
||||||
|
subdomains: vec![],
|
||||||
|
proxied: false,
|
||||||
|
}],
|
||||||
|
a: true,
|
||||||
|
aaaa: false,
|
||||||
|
purge_unknown_records: false,
|
||||||
|
ttl: 300,
|
||||||
|
ip4_provider: None,
|
||||||
|
ip6_provider: None,
|
||||||
|
record_comment: Some("managed by cloudflare-ddns".to_string()),
|
||||||
|
};
|
||||||
|
let config = legacy_to_app_config(legacy, false, false).unwrap();
|
||||||
|
assert_eq!(config.record_comment, Some("managed by cloudflare-ddns".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_legacy_config_deserializes_providers() {
|
fn test_legacy_config_deserializes_providers() {
|
||||||
let json = r#"{
|
let json = r#"{
|
||||||
@@ -1220,6 +1252,20 @@ mod tests {
|
|||||||
assert_eq!(config.ip6_provider, Some("none".to_string()));
|
assert_eq!(config.ip6_provider, Some("none".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_legacy_config_deserializes_record_comment() {
|
||||||
|
let json = r#"{
|
||||||
|
"cloudflare": [{
|
||||||
|
"authentication": { "api_token": "tok" },
|
||||||
|
"zone_id": "z",
|
||||||
|
"subdomains": ["@"]
|
||||||
|
}],
|
||||||
|
"recordComment": "managed by cloudflare-ddns"
|
||||||
|
}"#;
|
||||||
|
let config = parse_legacy_config(json).unwrap();
|
||||||
|
assert_eq!(config.record_comment, Some("managed by cloudflare-ddns".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_legacy_config_deserializes_without_providers() {
|
fn test_legacy_config_deserializes_without_providers() {
|
||||||
let json = r#"{
|
let json = r#"{
|
||||||
|
|||||||
@@ -421,6 +421,7 @@ mod tests {
|
|||||||
ttl: 300,
|
ttl: 300,
|
||||||
ip4_provider: None,
|
ip4_provider: None,
|
||||||
ip6_provider: None,
|
ip6_provider: None,
|
||||||
|
record_comment: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,6 +974,7 @@ mod tests {
|
|||||||
ttl: 300,
|
ttl: 300,
|
||||||
ip4_provider: None,
|
ip4_provider: None,
|
||||||
ip6_provider: None,
|
ip6_provider: None,
|
||||||
|
record_comment: None,
|
||||||
};
|
};
|
||||||
ddns.commit_record(
|
ddns.commit_record(
|
||||||
"198.51.100.7",
|
"198.51.100.7",
|
||||||
@@ -1104,6 +1106,7 @@ mod tests {
|
|||||||
ttl: 300,
|
ttl: 300,
|
||||||
ip4_provider: None,
|
ip4_provider: None,
|
||||||
ip6_provider: None,
|
ip6_provider: None,
|
||||||
|
record_comment: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
ddns.commit_record(
|
ddns.commit_record(
|
||||||
|
|||||||
113
src/updater.rs
113
src/updater.rs
@@ -409,7 +409,7 @@ async fn update_legacy(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
ips.retain(|key, ip_info| {
|
ips.retain(|key, ip_info| {
|
||||||
if let Ok(addr) = ip_info.ip.parse::<std::net::IpAddr>() {
|
if let Ok(addr) = ip_info.ip.parse::<IpAddr>() {
|
||||||
if cf_filter.contains(&addr) {
|
if cf_filter.contains(&addr) {
|
||||||
ppfmt.warningf(
|
ppfmt.warningf(
|
||||||
pp::EMOJI_WARNING,
|
pp::EMOJI_WARNING,
|
||||||
@@ -445,6 +445,7 @@ async fn update_legacy(
|
|||||||
legacy.ttl,
|
legacy.ttl,
|
||||||
legacy.purge_unknown_records,
|
legacy.purge_unknown_records,
|
||||||
noop_reported,
|
noop_reported,
|
||||||
|
config.record_comment.as_deref(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@@ -599,12 +600,20 @@ impl LegacyDdnsClient {
|
|||||||
ttl: i64,
|
ttl: i64,
|
||||||
purge_unknown_records: bool,
|
purge_unknown_records: bool,
|
||||||
noop_reported: &mut HashSet<String>,
|
noop_reported: &mut HashSet<String>,
|
||||||
|
comment: Option<&str>,
|
||||||
) -> (Vec<Message>, bool) {
|
) -> (Vec<Message>, bool) {
|
||||||
let mut messages = Vec::new();
|
let mut messages = Vec::new();
|
||||||
let mut notify = false;
|
let mut notify = false;
|
||||||
for ip in ips.values() {
|
for ip in ips.values() {
|
||||||
let (msgs, changed) = self
|
let (msgs, changed) = self
|
||||||
.commit_record(ip, config, ttl, purge_unknown_records, noop_reported)
|
.commit_record(
|
||||||
|
ip,
|
||||||
|
config,
|
||||||
|
ttl,
|
||||||
|
purge_unknown_records,
|
||||||
|
noop_reported,
|
||||||
|
comment,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
messages.extend(msgs);
|
messages.extend(msgs);
|
||||||
if changed {
|
if changed {
|
||||||
@@ -621,6 +630,7 @@ impl LegacyDdnsClient {
|
|||||||
ttl: i64,
|
ttl: i64,
|
||||||
purge_unknown_records: bool,
|
purge_unknown_records: bool,
|
||||||
noop_reported: &mut HashSet<String>,
|
noop_reported: &mut HashSet<String>,
|
||||||
|
comment: Option<&str>,
|
||||||
) -> (Vec<Message>, bool) {
|
) -> (Vec<Message>, bool) {
|
||||||
let mut messages = Vec::new();
|
let mut messages = Vec::new();
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
@@ -660,6 +670,7 @@ impl LegacyDdnsClient {
|
|||||||
content: ip.ip.clone(),
|
content: ip.ip.clone(),
|
||||||
proxied,
|
proxied,
|
||||||
ttl,
|
ttl,
|
||||||
|
comment: comment.map(|s| s.to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let dns_endpoint = format!(
|
let dns_endpoint = format!(
|
||||||
@@ -687,7 +698,10 @@ impl LegacyDdnsClient {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
identifier = Some(r.id.clone());
|
identifier = Some(r.id.clone());
|
||||||
if r.content != record.content || r.proxied != record.proxied {
|
if r.content != record.content
|
||||||
|
|| r.proxied != record.proxied
|
||||||
|
|| r.comment != record.comment
|
||||||
|
{
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1249,7 +1263,7 @@ mod tests {
|
|||||||
assert!(ok, "skip on detection failure should not be an error");
|
assert!(ok, "skip on detection failure should not be an error");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A definitive "no address of this family" (e.g. provider `none`) with
|
/// A definitive "no address of this family" (e.g., provider `none`) with
|
||||||
/// delete_on_failure enabled deletes the managed records (documented behavior).
|
/// delete_on_failure enabled deletes the managed records (documented behavior).
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_update_once_no_ip_deletes_records_with_delete_on_failure() {
|
async fn test_update_once_no_ip_deletes_records_with_delete_on_failure() {
|
||||||
@@ -1595,7 +1609,7 @@ mod tests {
|
|||||||
assert!(ok);
|
assert!(ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// update_once with WAF lists: IPs are detected and WAF list is updated.
|
/// update_once with WAF lists: IPs are detected and the WAF list is updated.
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_update_once_with_waf_list() {
|
async fn test_update_once_with_waf_list() {
|
||||||
let server = MockServer::start().await;
|
let server = MockServer::start().await;
|
||||||
@@ -2001,7 +2015,7 @@ mod tests {
|
|||||||
final_delete(&config, &cf, ¬ifier, &heartbeat, &ppfmt).await;
|
final_delete(&config, &cf, ¬ifier, &heartbeat, &ppfmt).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// final_delete skips DNS deletion when zone is not found.
|
/// final_delete skips DNS deletion when the zone is not found.
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_final_delete_skips_when_zone_not_found() {
|
async fn test_final_delete_skips_when_zone_not_found() {
|
||||||
let server = MockServer::start().await;
|
let server = MockServer::start().await;
|
||||||
@@ -2239,7 +2253,7 @@ mod tests {
|
|||||||
let server = MockServer::start().await;
|
let server = MockServer::start().await;
|
||||||
let zone_id = "zone-abc";
|
let zone_id = "zone-abc";
|
||||||
let domain_v6 = "v6only.example.com";
|
let domain_v6 = "v6only.example.com";
|
||||||
// Only a V4 literal provider is configured but domain is V6
|
// Only a V4 literal provider is configured, but domain is V6
|
||||||
let ip_v4 = "198.51.100.1";
|
let ip_v4 = "198.51.100.1";
|
||||||
|
|
||||||
// Zone lookup for V6 domain
|
// Zone lookup for V6 domain
|
||||||
@@ -2494,7 +2508,7 @@ mod tests {
|
|||||||
subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())],
|
subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())],
|
||||||
proxied: false,
|
proxied: false,
|
||||||
}];
|
}];
|
||||||
ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new())
|
ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new(), None)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2551,7 +2565,7 @@ mod tests {
|
|||||||
subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())],
|
subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())],
|
||||||
proxied: false,
|
proxied: false,
|
||||||
}];
|
}];
|
||||||
ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new())
|
ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new(), None)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2595,7 +2609,7 @@ mod tests {
|
|||||||
proxied: false,
|
proxied: false,
|
||||||
}];
|
}];
|
||||||
// Should not POST
|
// Should not POST
|
||||||
ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new())
|
ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new(), None)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2649,7 +2663,7 @@ mod tests {
|
|||||||
}],
|
}],
|
||||||
proxied: false,
|
proxied: false,
|
||||||
}];
|
}];
|
||||||
ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new())
|
ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new(), None)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2702,7 +2716,7 @@ mod tests {
|
|||||||
subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())],
|
subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())],
|
||||||
proxied: false,
|
proxied: false,
|
||||||
}];
|
}];
|
||||||
ddns.commit_record(&ip, &config, 300, true, &mut HashSet::new())
|
ddns.commit_record(&ip, &config, 300, true, &mut HashSet::new(), None)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2756,7 +2770,7 @@ mod tests {
|
|||||||
subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())],
|
subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())],
|
||||||
proxied: false,
|
proxied: false,
|
||||||
}];
|
}];
|
||||||
ddns.update_ips(&ips, &config, 300, false, &mut HashSet::new())
|
ddns.update_ips(&ips, &config, 300, false, &mut HashSet::new(), None)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3098,6 +3112,75 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
assert!(ok, "Should succeed with both detections");
|
assert!(ok, "Should succeed with both detections");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// update_once passes record_comment to the API when creating a new record.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_update_once_with_record_comment() {
|
||||||
|
let server = MockServer::start().await;
|
||||||
|
let zone_id = "zone-abc";
|
||||||
|
let domain = "home.example.com";
|
||||||
|
let ip = "198.51.100.42";
|
||||||
|
let comment = "managed by cf-ddns";
|
||||||
|
|
||||||
|
// Zone lookup
|
||||||
|
Mock::given(method("GET"))
|
||||||
|
.and(path("/zones"))
|
||||||
|
.and(query_param("name", domain))
|
||||||
|
.respond_with(
|
||||||
|
ResponseTemplate::new(200).set_body_json(zones_response(zone_id, "example.com")),
|
||||||
|
)
|
||||||
|
.mount(&server)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// List existing records (empty)
|
||||||
|
Mock::given(method("GET"))
|
||||||
|
.and(path_regex(format!("/zones/{zone_id}/dns_records")))
|
||||||
|
.respond_with(ResponseTemplate::new(200).set_body_json(dns_records_empty()))
|
||||||
|
.mount(&server)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Create record: POST should receive the comment in the body
|
||||||
|
Mock::given(method("POST"))
|
||||||
|
.and(path(format!("/zones/{zone_id}/dns_records")))
|
||||||
|
.respond_with(
|
||||||
|
ResponseTemplate::new(200).set_body_json(dns_record_created("rec-1", domain, ip)),
|
||||||
|
)
|
||||||
|
.expect(1)
|
||||||
|
.mount(&server)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut providers = HashMap::new();
|
||||||
|
providers.insert(
|
||||||
|
IpType::V4,
|
||||||
|
ProviderType::Literal {
|
||||||
|
ips: vec![ip.parse::<IpAddr>().unwrap()],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let mut domains = HashMap::new();
|
||||||
|
domains.insert(IpType::V4, vec![domain.to_string()]);
|
||||||
|
|
||||||
|
let mut config = make_config(providers, domains, vec![], false);
|
||||||
|
config.record_comment = Some(comment.to_string());
|
||||||
|
|
||||||
|
let cf = handle(&server.uri());
|
||||||
|
let notifier = empty_notifier();
|
||||||
|
let heartbeat = empty_heartbeat();
|
||||||
|
let ppfmt = pp();
|
||||||
|
|
||||||
|
let mut cf_cache = CachedCloudflareFilter::new();
|
||||||
|
let ok = update_once(
|
||||||
|
&config,
|
||||||
|
&cf,
|
||||||
|
¬ifier,
|
||||||
|
&heartbeat,
|
||||||
|
&mut cf_cache,
|
||||||
|
&ppfmt,
|
||||||
|
&mut HashSet::new(),
|
||||||
|
&crate::test_client(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(ok, "Should succeed with record_comment set");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy types for backwards compatibility
|
// Legacy types for backwards compatibility
|
||||||
@@ -3117,6 +3200,8 @@ struct LegacyDnsRecord {
|
|||||||
name: String,
|
name: String,
|
||||||
content: String,
|
content: String,
|
||||||
proxied: bool,
|
proxied: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
comment: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
@@ -3127,4 +3212,6 @@ struct LegacyDnsRecordPayload {
|
|||||||
content: String,
|
content: String,
|
||||||
proxied: bool,
|
proxied: bool,
|
||||||
ttl: i64,
|
ttl: i64,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
comment: Option<String>,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user