diff --git a/src/tls/acme/dns/digitalocean.rs b/src/tls/acme/dns/digitalocean.rs new file mode 100644 index 0000000..0f173b0 --- /dev/null +++ b/src/tls/acme/dns/digitalocean.rs @@ -0,0 +1,70 @@ +use crate::tls::acme::lookup::lookup_wait; +use crate::tls::acme::types::{DnsBackendPlugin, DnsProvider}; +use async_trait::async_trait; +use log::info; +use serde_json::json; +use std::env; + +pub struct DigitalOceanProvider { + token: String, + client: reqwest::Client, +} + +impl DigitalOceanProvider { + pub fn new() -> Self { + let token = env::var("DIGITALOCEAN_TOKEN").expect("Environment variable DIGITALOCEAN_TOKEN is required"); + + Self { + token, + client: reqwest::Client::new(), + } + } +} + +#[async_trait] +impl DnsProvider for DigitalOceanProvider { + async fn create_txt_record(&self, domain: &str, name: &str, value: &str) -> Result> { + let relative_name = name.strip_suffix(&format!(".{}", domain)).unwrap_or(name); + let url = format!("https://api.digitalocean.com/v2/domains/{}/records", domain); + let res = self + .client + .post(&url) + .bearer_auth(&self.token) + .json(&json!({ + "type": "TXT", + "name": relative_name, + "data": value, + "ttl": 30 + })) + .send() + .await?; + + if !res.status().is_success() { + let err_text = res.text().await?; + return Err(format!("DigitalOcean API error: {}", err_text).into()); + } + + let json: serde_json::Value = res.json().await?; + let record_id = json["domain_record"]["id"].as_i64().ok_or("Missing record ID in DigitalOcean response")?.to_string(); + info!("Created TXT record name: {}, id: {}", name, record_id); + lookup_wait(name, Some("1.1.1.1"), 60, value).await; + Ok(record_id) + } + + async fn delete_txt_record(&self, record_id: &str, domain: &str) -> Result<(), Box> { + let url = format!("https://api.digitalocean.com/v2/domains/{}/records/{}", domain, record_id); + let res = self.client.delete(&url).bearer_auth(&self.token).send().await?; + if !res.status().is_success() { + let err_text = res.text().await?; + return Err(format!("Failed to delete DigitalOcean record: {}", err_text).into()); + } + Ok(()) + } +} + +inventory::submit! { + DnsBackendPlugin { + name: "digitalocean", + factory: || Box::new(DigitalOceanProvider::new()), + } +} diff --git a/src/tls/acme/dns/mod.rs b/src/tls/acme/dns/mod.rs index 4dfee2b..d57d291 100644 --- a/src/tls/acme/dns/mod.rs +++ b/src/tls/acme/dns/mod.rs @@ -1,2 +1,3 @@ pub mod cloudflare; +pub mod digitalocean; pub mod route53;