Fix Shoutrrr notifications not sending in legacy config.json mode and

bump to 2.0.10

  Legacy mode (config.json) never set the notify flag or generated
  notification messages, so Shoutrrr services (Telegram, Discord, Slack,
  etc.) were silently skipped even when DNS records were created or
  updated. Propagate messages and the notify flag from the legacy update
  path back to update_once() so notifications fire correctly.
This commit is contained in:
Timothy Miller
2026-03-23 20:01:29 -04:00
parent 697089b43d
commit 2913ce379c
4 changed files with 44 additions and 18 deletions

2
Cargo.lock generated
View File

@@ -139,7 +139,7 @@ dependencies = [
[[package]] [[package]]
name = "cloudflare-ddns" name = "cloudflare-ddns"
version = "2.0.9" version = "2.0.10"
dependencies = [ dependencies = [
"chrono", "chrono",
"idna", "idna",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "cloudflare-ddns" name = "cloudflare-ddns"
version = "2.0.9" version = "2.0.10"
edition = "2021" edition = "2021"
description = "Access your home network remotely via a custom domain name without a static IP" description = "Access your home network remotely via a custom domain name without a static IP"
license = "GPL-3.0" license = "GPL-3.0"

View File

@@ -47,7 +47,7 @@ This project handles **Cloudflare API tokens** that grant DNS editing privileges
- The Docker image runs as a **static binary from scratch** with zero runtime dependencies, which minimizes the attack surface. - The Docker image runs as a **static binary from scratch** with zero runtime dependencies, which minimizes the attack surface.
- Use `security_opt: no-new-privileges:true` in Docker Compose deployments. - Use `security_opt: no-new-privileges:true` in Docker Compose deployments.
- Pin image tags to a specific version (e.g., `timothyjmiller/cloudflare-ddns:v2.0.9`) rather than using `latest` in production. - Pin image tags to a specific version (e.g., `timothyjmiller/cloudflare-ddns:v2.0.10`) rather than using `latest` in production.
### Network Security ### Network Security

View File

@@ -26,7 +26,11 @@ pub async fn update_once(
let mut notify = false; // NEW: track meaningful events let mut notify = false; // NEW: track meaningful events
if config.legacy_mode { if config.legacy_mode {
all_ok = update_legacy(config, cf_cache, ppfmt, noop_reported, detection_client).await; let (ok, legacy_msgs, legacy_notify) =
update_legacy(config, cf_cache, ppfmt, noop_reported, detection_client).await;
all_ok = ok;
messages = legacy_msgs;
notify = legacy_notify;
} else { } else {
// Detect IPs for each provider // Detect IPs for each provider
let mut detected_ips: HashMap<IpType, Vec<IpAddr>> = HashMap::new(); let mut detected_ips: HashMap<IpType, Vec<IpAddr>> = HashMap::new();
@@ -251,10 +255,10 @@ async fn update_legacy(
ppfmt: &PP, ppfmt: &PP,
noop_reported: &mut HashSet<String>, noop_reported: &mut HashSet<String>,
detection_client: &Client, detection_client: &Client,
) -> bool { ) -> (bool, Vec<Message>, bool) {
let legacy = match &config.legacy_config { let legacy = match &config.legacy_config {
Some(l) => l, Some(l) => l,
None => return false, None => return (false, Vec::new(), false),
}; };
let ddns = LegacyDdnsClient { let ddns = LegacyDdnsClient {
@@ -341,7 +345,8 @@ async fn update_legacy(
} }
} }
ddns.update_ips( let (msgs, should_notify) = ddns
.update_ips(
&ips, &ips,
&legacy.cloudflare, &legacy.cloudflare,
legacy.ttl, legacy.ttl,
@@ -350,7 +355,7 @@ async fn update_legacy(
) )
.await; .await;
true (true, msgs, should_notify)
} }
/// Delete records on stop (for env var mode). /// Delete records on stop (for env var mode).
@@ -499,12 +504,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>,
) { ) -> (Vec<Message>, bool) {
let mut messages = Vec::new();
let mut notify = false;
for ip in ips.values() { for ip in ips.values() {
self.commit_record(ip, config, ttl, purge_unknown_records, noop_reported) let (msgs, changed) = self
.commit_record(ip, config, ttl, purge_unknown_records, noop_reported)
.await; .await;
messages.extend(msgs);
if changed {
notify = true;
} }
} }
(messages, notify)
}
async fn commit_record( async fn commit_record(
&self, &self,
@@ -513,7 +526,9 @@ impl LegacyDdnsClient {
ttl: i64, ttl: i64,
purge_unknown_records: bool, purge_unknown_records: bool,
noop_reported: &mut HashSet<String>, noop_reported: &mut HashSet<String>,
) { ) -> (Vec<Message>, bool) {
let mut messages = Vec::new();
let mut changed = false;
for entry in config { for entry in config {
let zone_resp: Option<LegacyCfResponse<LegacyZoneResult>> = self let zone_resp: Option<LegacyCfResponse<LegacyZoneResult>> = self
.cf_api( .cf_api(
@@ -592,6 +607,7 @@ impl LegacyDdnsClient {
if let Some(ref id) = identifier { if let Some(ref id) = identifier {
if modified { if modified {
noop_reported.remove(&noop_key); noop_reported.remove(&noop_key);
changed = true;
if self.dry_run { if self.dry_run {
println!("[DRY RUN] Would update record {fqdn} -> {}", ip.ip); println!("[DRY RUN] Would update record {fqdn} -> {}", ip.ip);
} else { } else {
@@ -602,6 +618,10 @@ impl LegacyDdnsClient {
.cf_api(&update_endpoint, "PUT", entry, Some(&record)) .cf_api(&update_endpoint, "PUT", entry, Some(&record))
.await; .await;
} }
messages.push(Message::new_ok(&format!(
"Updated {fqdn} -> {}",
ip.ip
)));
} else if noop_reported.insert(noop_key) { } else if noop_reported.insert(noop_key) {
if self.dry_run { if self.dry_run {
println!("[DRY RUN] Record {fqdn} is up to date"); println!("[DRY RUN] Record {fqdn} is up to date");
@@ -611,6 +631,7 @@ impl LegacyDdnsClient {
} }
} else { } else {
noop_reported.remove(&noop_key); noop_reported.remove(&noop_key);
changed = true;
if self.dry_run { if self.dry_run {
println!("[DRY RUN] Would add new record {fqdn} -> {}", ip.ip); println!("[DRY RUN] Would add new record {fqdn} -> {}", ip.ip);
} else { } else {
@@ -620,6 +641,10 @@ impl LegacyDdnsClient {
.cf_api(&create_endpoint, "POST", entry, Some(&record)) .cf_api(&create_endpoint, "POST", entry, Some(&record))
.await; .await;
} }
messages.push(Message::new_ok(&format!(
"Created {fqdn} -> {}",
ip.ip
)));
} }
if purge_unknown_records { if purge_unknown_records {
@@ -638,6 +663,7 @@ impl LegacyDdnsClient {
} }
} }
} }
(messages, changed)
} }
} }