char/dns-ez
cloudflare dns management via plaintext zonefiles
git clone https://git.t4t.associates/char/dns-ez
c6d896d
main
1use std:: fmt; 2 3use anyhow::{ Context , Result }; 4use clap:: ValueEnum ; 5 6use crate :: dns::{ ForeignRecord , Name , Record }; 7use crate :: reconcile:: Action ; 8use crate ::{ cloudflare, wix}; 9 10/// Where a zone is hosted. 11# [ derive ( Clone , Copy , Debug , PartialEq , Eq , ValueEnum )] 12pub enum Kind { 13Cloudflare , 14Wix , 15} 16 17impl Kind { 18pub fn connect ( self ) ->Result < Box < dyn Provider >> { 19let var = |name : & str , hint : & str |{ 20 std:: env:: var ( name). with_context ( ||format! ( "set {name} to {hint}" )) 21}; 22Ok ( match self { 23Kind :: Cloudflare =>Box :: new ( cloudflare:: Client :: new ( 24var ( "CF_API_TOKEN" , "a Cloudflare API token" ) 25. or_else ( |e| std:: env:: var ( "CLOUDFLARE_API_TOKEN" ). map_err ( |_| e)) ?, 26)), 27Kind :: Wix =>Box :: new ( wix:: Client :: new ( 28var ( "WIX_API_TOKEN" , "a Wix account-level API token" ) ?, 29var ( 30"WIX_ACCOUNT_ID" , 31"the Wix account ID shown beside your API keys" , 32) ?, 33)), 34}) 35} 36} 37 38impl fmt:: Display for Kind { 39fn fmt ( & self , f : & mut fmt:: Formatter < ' _ >) -> fmt:: Result { 40 f. write_str ( 41self . to_possible_value () 42. expect ( "no skipped variants" ) 43. get_name (), 44) 45} 46} 47 48/// A DNS host. Providers address records differently (Cloudflare by ID, Wix 49/// by whole RRset), so `apply` takes plain record changes and resolves them 50/// against the zone as it stands at apply time. 51pub trait Provider { 52fn list_zones ( & self ) ->Result < Vec < String >>; 53fn records ( & self , zone : & Name ) ->Result < Vec < Listed >>; 54fn apply ( & self , zone : & Name , actions : & [ Action ]) ->Result <()>; 55} 56 57/// A record at the provider: managed records are reconciled, everything else 58/// is kept only for display. 59pub enum Listed { 60Managed ( Record ), 61Foreign ( ForeignRecord ), 62} 63 64/// Split the provider's records into the ones we manage and the ones we 65/// leave alone. Apex NS records belong to the provider's nameservers and 66/// are untouchable, so they count as foreign too. 67pub fn split_listed ( listed : Vec < Listed >, apex : & Name ) ->( Vec < Record >, Vec < ForeignRecord >) { 68let ( mut managed, mut foreign) =( Vec :: new (), Vec :: new ()); 69for lin listed{ 70match l{ 71Listed :: Managed ( r) if r. is_apex_ns ( apex) =>{ 72 foreign. push ( ForeignRecord { 73name : r. name . to_string (), 74rtype : r. rtype (). to_string (), 75ttl : r. ttl , 76content : r. data . to_string (), 77}); 78} 79Listed :: Managed ( r) => managed. push ( r), 80Listed :: Foreign ( f) => foreign. push ( f), 81} 82} 83( managed, foreign) 84}