@@ -11,8 +11,10 @@ use crate::cloudflare::{Auth, CloudflareHandle};
1111use crate :: config:: { AppConfig , CronSchedule } ;
1212use crate :: notifier:: { CompositeNotifier , Heartbeat , Message } ;
1313use crate :: pp:: PP ;
14+ use std:: collections:: HashSet ;
1415use std:: sync:: atomic:: { AtomicBool , Ordering } ;
1516use std:: sync:: Arc ;
17+ use reqwest:: Client ;
1618use tokio:: signal;
1719use tokio:: time:: { sleep, Duration } ;
1820
@@ -117,13 +119,17 @@ async fn main() {
117119 heartbeat. start ( ) . await ;
118120
119121 let mut cf_cache = cf_ip_filter:: CachedCloudflareFilter :: new ( ) ;
122+ let detection_client = Client :: builder ( )
123+ . timeout ( app_config. detection_timeout )
124+ . build ( )
125+ . unwrap_or_default ( ) ;
120126
121127 if app_config. legacy_mode {
122128 // --- Legacy mode (original cloudflare-ddns behavior) ---
123- run_legacy_mode ( & app_config, & handle, & notifier, & heartbeat, & ppfmt, running, & mut cf_cache) . await ;
129+ run_legacy_mode ( & app_config, & handle, & notifier, & heartbeat, & ppfmt, running, & mut cf_cache, & detection_client ) . await ;
124130 } else {
125131 // --- Env var mode (cf-ddns behavior) ---
126- run_env_mode ( & app_config, & handle, & notifier, & heartbeat, & ppfmt, running, & mut cf_cache) . await ;
132+ run_env_mode ( & app_config, & handle, & notifier, & heartbeat, & ppfmt, running, & mut cf_cache, & detection_client ) . await ;
127133 }
128134
129135 // On shutdown: delete records if configured
@@ -146,12 +152,15 @@ async fn run_legacy_mode(
146152 ppfmt : & PP ,
147153 running : Arc < AtomicBool > ,
148154 cf_cache : & mut cf_ip_filter:: CachedCloudflareFilter ,
155+ detection_client : & Client ,
149156) {
150157 let legacy = match & config. legacy_config {
151158 Some ( l) => l,
152159 None => return ,
153160 } ;
154161
162+ let mut noop_reported = HashSet :: new ( ) ;
163+
155164 if config. repeat {
156165 match ( legacy. a , legacy. aaaa ) {
157166 ( true , true ) => println ! (
@@ -168,7 +177,7 @@ async fn run_legacy_mode(
168177 }
169178
170179 while running. load ( Ordering :: SeqCst ) {
171- updater:: update_once ( config, handle, notifier, heartbeat, cf_cache, ppfmt) . await ;
180+ updater:: update_once ( config, handle, notifier, heartbeat, cf_cache, ppfmt, & mut noop_reported , detection_client ) . await ;
172181
173182 for _ in 0 ..legacy. ttl {
174183 if !running. load ( Ordering :: SeqCst ) {
@@ -178,7 +187,7 @@ async fn run_legacy_mode(
178187 }
179188 }
180189 } else {
181- updater:: update_once ( config, handle, notifier, heartbeat, cf_cache, ppfmt) . await ;
190+ updater:: update_once ( config, handle, notifier, heartbeat, cf_cache, ppfmt, & mut noop_reported , detection_client ) . await ;
182191 }
183192}
184193
@@ -190,11 +199,14 @@ async fn run_env_mode(
190199 ppfmt : & PP ,
191200 running : Arc < AtomicBool > ,
192201 cf_cache : & mut cf_ip_filter:: CachedCloudflareFilter ,
202+ detection_client : & Client ,
193203) {
204+ let mut noop_reported = HashSet :: new ( ) ;
205+
194206 match & config. update_cron {
195207 CronSchedule :: Once => {
196208 if config. update_on_start {
197- updater:: update_once ( config, handle, notifier, heartbeat, cf_cache, ppfmt) . await ;
209+ updater:: update_once ( config, handle, notifier, heartbeat, cf_cache, ppfmt, & mut noop_reported , detection_client ) . await ;
198210 }
199211 }
200212 schedule => {
@@ -210,7 +222,7 @@ async fn run_env_mode(
210222
211223 // Update on start if configured
212224 if config. update_on_start {
213- updater:: update_once ( config, handle, notifier, heartbeat, cf_cache, ppfmt) . await ;
225+ updater:: update_once ( config, handle, notifier, heartbeat, cf_cache, ppfmt, & mut noop_reported , detection_client ) . await ;
214226 }
215227
216228 // Main loop
@@ -237,7 +249,7 @@ async fn run_env_mode(
237249 return ;
238250 }
239251
240- updater:: update_once ( config, handle, notifier, heartbeat, cf_cache, ppfmt) . await ;
252+ updater:: update_once ( config, handle, notifier, heartbeat, cf_cache, ppfmt, & mut noop_reported , detection_client ) . await ;
241253 }
242254 }
243255 }
@@ -386,6 +398,7 @@ mod tests {
386398 config : & [ LegacyCloudflareEntry ] ,
387399 ttl : i64 ,
388400 purge_unknown_records : bool ,
401+ noop_reported : & mut std:: collections:: HashSet < String > ,
389402 ) {
390403 for entry in config {
391404 #[ derive( serde:: Deserialize ) ]
@@ -487,8 +500,10 @@ mod tests {
487500 }
488501 }
489502
503+ let noop_key = format ! ( "{fqdn}:{record_type}" ) ;
490504 if let Some ( ref id) = identifier {
491505 if modified {
506+ noop_reported. remove ( & noop_key) ;
492507 if self . dry_run {
493508 println ! ( "[DRY RUN] Would update record {fqdn} -> {ip}" ) ;
494509 } else {
@@ -504,23 +519,30 @@ mod tests {
504519 )
505520 . await ;
506521 }
507- } else if self . dry_run {
508- println ! ( "[DRY RUN] Record {fqdn} is up to date ({ip})" ) ;
522+ } else if noop_reported. insert ( noop_key) {
523+ if self . dry_run {
524+ println ! ( "[DRY RUN] Record {fqdn} is up to date" ) ;
525+ } else {
526+ println ! ( "Record {fqdn} is up to date" ) ;
527+ }
509528 }
510- } else if self . dry_run {
511- println ! ( "[DRY RUN] Would add new record {fqdn} -> {ip}" ) ;
512529 } else {
513- println ! ( "Adding new record {fqdn} -> {ip}" ) ;
514- let create_endpoint =
515- format ! ( "zones/{}/dns_records" , entry. zone_id) ;
516- let _: Option < serde_json:: Value > = self
517- . cf_api (
518- & create_endpoint,
519- "POST" ,
520- & entry. authentication . api_token ,
521- Some ( & record) ,
522- )
523- . await ;
530+ noop_reported. remove ( & noop_key) ;
531+ if self . dry_run {
532+ println ! ( "[DRY RUN] Would add new record {fqdn} -> {ip}" ) ;
533+ } else {
534+ println ! ( "Adding new record {fqdn} -> {ip}" ) ;
535+ let create_endpoint =
536+ format ! ( "zones/{}/dns_records" , entry. zone_id) ;
537+ let _: Option < serde_json:: Value > = self
538+ . cf_api (
539+ & create_endpoint,
540+ "POST" ,
541+ & entry. authentication . api_token ,
542+ Some ( & record) ,
543+ )
544+ . await ;
545+ }
524546 }
525547
526548 if purge_unknown_records {
@@ -640,7 +662,7 @@ mod tests {
640662
641663 let ddns = TestDdnsClient :: new ( & mock_server. uri ( ) ) ;
642664 let config = test_config ( zone_id) ;
643- ddns. commit_record ( "198.51.100.7" , "A" , & config. cloudflare , 300 , false )
665+ ddns. commit_record ( "198.51.100.7" , "A" , & config. cloudflare , 300 , false , & mut std :: collections :: HashSet :: new ( ) )
644666 . await ;
645667 }
646668
@@ -689,7 +711,7 @@ mod tests {
689711
690712 let ddns = TestDdnsClient :: new ( & mock_server. uri ( ) ) ;
691713 let config = test_config ( zone_id) ;
692- ddns. commit_record ( "198.51.100.7" , "A" , & config. cloudflare , 300 , false )
714+ ddns. commit_record ( "198.51.100.7" , "A" , & config. cloudflare , 300 , false , & mut std :: collections :: HashSet :: new ( ) )
693715 . await ;
694716 }
695717
@@ -732,7 +754,7 @@ mod tests {
732754
733755 let ddns = TestDdnsClient :: new ( & mock_server. uri ( ) ) ;
734756 let config = test_config ( zone_id) ;
735- ddns. commit_record ( "198.51.100.7" , "A" , & config. cloudflare , 300 , false )
757+ ddns. commit_record ( "198.51.100.7" , "A" , & config. cloudflare , 300 , false , & mut std :: collections :: HashSet :: new ( ) )
736758 . await ;
737759 }
738760
@@ -766,7 +788,7 @@ mod tests {
766788
767789 let ddns = TestDdnsClient :: new ( & mock_server. uri ( ) ) . dry_run ( ) ;
768790 let config = test_config ( zone_id) ;
769- ddns. commit_record ( "198.51.100.7" , "A" , & config. cloudflare , 300 , false )
791+ ddns. commit_record ( "198.51.100.7" , "A" , & config. cloudflare , 300 , false , & mut std :: collections :: HashSet :: new ( ) )
770792 . await ;
771793 }
772794
@@ -823,7 +845,7 @@ mod tests {
823845 ip4_provider : None ,
824846 ip6_provider : None ,
825847 } ;
826- ddns. commit_record ( "198.51.100.7" , "A" , & config. cloudflare , 300 , true )
848+ ddns. commit_record ( "198.51.100.7" , "A" , & config. cloudflare , 300 , true , & mut std :: collections :: HashSet :: new ( ) )
827849 . await ;
828850 }
829851
@@ -925,7 +947,7 @@ mod tests {
925947 ip6_provider : None ,
926948 } ;
927949
928- ddns. commit_record ( "203.0.113.99" , "A" , & config. cloudflare , 300 , false )
950+ ddns. commit_record ( "203.0.113.99" , "A" , & config. cloudflare , 300 , false , & mut std :: collections :: HashSet :: new ( ) )
929951 . await ;
930952 }
931953}
0 commit comments