From a985fbb01ab7336039be09cfa52ee75ee2e597ba Mon Sep 17 00:00:00 2001 From: Ronin Beks Date: Mon, 3 Nov 2025 15:18:01 +0100 Subject: [PATCH 1/7] feat: custom hostname endpoints --- .../custom_hostnames/custom_hostname.rs | 586 ++++++++++++++++++ .../src/endpoints/custom_hostnames/mod.rs | 1 + cloudflare/src/endpoints/mod.rs | 1 + 3 files changed, 588 insertions(+) create mode 100644 cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs create mode 100644 cloudflare/src/endpoints/custom_hostnames/mod.rs diff --git a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs new file mode 100644 index 00000000..ce4d55ea --- /dev/null +++ b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs @@ -0,0 +1,586 @@ +use crate::framework::endpoint::{serialize_query, EndpointSpec, Method, RequestBody}; +use crate::framework::response::{ApiResult, ApiSuccess}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Clone, Debug)] +#[serde(rename_all = "lowercase")] +pub enum OrderDirection { + Asc, + Desc, +} + +/// List Custom Hostnames +/// List, search, sort, and filter all of your custom hostnames. +/// +#[derive(Debug)] +pub struct ListCustomHostnames<'a> { + pub zone_id: &'a str, + pub params: ListCustomHostnamesParams, +} + +impl EndpointSpec for ListCustomHostnames<'_> { + type JsonResponse = Vec; + type ResponseType = ApiSuccess; + + fn method(&self) -> Method { + Method::GET + } + + fn path(&self) -> String { + format!("zones/{}/custom_hostnames", self.zone_id) + } + + #[inline] + fn query(&self) -> Option { + serialize_query(&self.params) + } +} + +/// Create Custom Hostname +/// Add a new custom hostname and request that an SSL certificate be issued for it. +/// +#[derive(Debug)] +pub struct CreateCustomHostname<'a> { + pub zone_id: &'a str, + pub params: CreateCustomHostnameParams<'a>, +} + +impl EndpointSpec for CreateCustomHostname<'_> { + type JsonResponse = CustomHostname; + type ResponseType = ApiSuccess; + + fn method(&self) -> Method { + Method::POST + } + + fn path(&self) -> String { + format!("zones/{}/custom_hostnames", self.zone_id) + } + + #[inline] + fn body(&self) -> Option { + let body = serde_json::to_string(&self.params).unwrap(); + Some(RequestBody::Json(body)) + } +} + +/// Update Custom Hostname +/// Modify SSL configuration for a custom hostname. +/// +#[derive(Debug)] +pub struct UpdateCustomHostname<'a> { + pub zone_id: &'a str, + pub identifier: &'a str, + pub params: UpdateCustomHostnameParams<'a>, +} + +impl EndpointSpec for UpdateCustomHostname<'_> { + type JsonResponse = CustomHostname; + type ResponseType = ApiSuccess; + + fn method(&self) -> Method { + Method::PATCH + } + + fn path(&self) -> String { + format!( + "zones/{}/custom_hostnames/{}", + self.zone_id, self.identifier + ) + } + + #[inline] + fn body(&self) -> Option { + let body = serde_json::to_string(&self.params).unwrap(); + Some(RequestBody::Json(body)) + } +} + +/// Delete Custom Hostname +/// Delete Custom Hostname (and any issued SSL certificates) +/// +#[derive(Debug)] +pub struct DeleteCustomHostname<'a> { + pub zone_id: &'a str, + pub identifier: &'a str, +} + +impl EndpointSpec for DeleteCustomHostname<'_> { + type JsonResponse = DeleteCustomHostnameResponse; + type ResponseType = ApiSuccess; + + fn method(&self) -> Method { + Method::DELETE + } + + fn path(&self) -> String { + format!( + "zones/{}/custom_hostnames/{}", + self.zone_id, self.identifier + ) + } +} + +#[derive(Serialize, Clone, Debug, Default)] +pub struct ListCustomHostnamesParams { + /// Hostname ID to match against + pub id: Option, + /// Fully qualified domain name to match against + pub hostname: Option, + /// Page number of paginated results + pub page: Option, + /// Number of hostnames per page + pub per_page: Option, + /// Field to order hostnames by + pub order: Option, + /// Direction to order hostnames + pub direction: Option, + /// Whether to filter hostnames based on if they have SSL enabled + pub ssl: Option, +} + +#[derive(Serialize, Clone, Debug)] +#[serde(rename_all = "lowercase")] +pub enum ListCustomHostnamesOrder { + Ssl, + SslStatus, +} + +#[derive(Serialize, Clone, Debug)] +pub struct CreateCustomHostnameParams<'a> { + /// The custom hostname that will point to your hostname via CNAME + pub hostname: &'a str, + /// SSL properties for the custom hostname + pub ssl: SslParams<'a>, + /// A hostname that will be sent to your custom origin server as SNI + pub custom_origin_server: Option<&'a str>, + /// A hostname that will be sent to your custom origin SNI + pub custom_origin_sni: Option<&'a str>, + /// Unique key/value metadata for this hostname + pub custom_metadata: Option, +} + +#[derive(Serialize, Clone, Debug)] +pub struct UpdateCustomHostnameParams<'a> { + /// SSL properties for the custom hostname + pub ssl: Option>, + /// A hostname that will be sent to your custom origin server as SNI + pub custom_origin_server: Option<&'a str>, + /// A hostname that will be sent to your custom origin SNI + pub custom_origin_sni: Option<&'a str>, + /// Unique key/value metadata for this hostname + pub custom_metadata: Option, +} + +#[derive(Serialize, Clone, Debug)] +pub struct SslParams<'a> { + /// Domain control validation method + pub method: Option, + /// Level of validation to be used for this hostname + #[serde(rename = "type")] + pub validation_type: Option, + /// The certificate authority that will issue the certificate + pub certificate_authority: Option, + /// Indicates whether the certificate covers a wildcard + pub wildcard: Option, + /// Custom certificate used for this hostname + pub custom_certificate: Option<&'a str>, + /// Custom CSR ID + pub custom_csr_id: Option<&'a str>, + /// Custom private key + pub custom_key: Option<&'a str>, + /// Bundle method + pub bundle_method: Option, + /// SSL certificate settings + pub settings: Option, +} + +#[derive(Deserialize, Debug)] +pub struct DeleteCustomHostnameResponse { + pub id: String, +} + +/// A Custom Hostname +/// +#[derive(Deserialize, Debug)] +pub struct CustomHostname { + /// Custom hostname identifier tag + pub id: String, + /// The custom hostname that will point to your hostname + pub hostname: String, + /// SSL properties for the custom hostname + pub ssl: Ssl, + /// Status of the hostname + pub status: CustomHostnameStatus, + /// These are errors that were encountered while trying to activate a hostname + pub verification_errors: Option>, + /// This is a record which can be placed to activate a hostname + pub ownership_verification: Option, + /// This presents the token to be served by the given http url to activate a hostname + pub ownership_verification_http: Option, + /// A hostname that will be sent to your custom origin server + pub custom_origin_server: Option, + /// A hostname that will be sent to your custom origin SNI for TLS handshake + pub custom_origin_sni: Option, + /// Unique key/value metadata for this hostname + pub custom_metadata: Option, + /// This is the time the hostname was created + pub created_at: DateTime, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "lowercase")] +pub enum CustomHostnameStatus { + Active, + Pending, + ActiveRedeploying, + Moved, + PendingDeletion, + Deleted, + PendingBlocked, + PendingMigration, + PendingProvisioned, + TestPending, + TestActive, + TestActiveApex, + TestBlocked, + TestFailed, + Provisioned, + Blocked, +} + +#[derive(Deserialize, Debug)] +pub struct Ssl { + /// Custom hostname SSL identifier tag + pub id: Option, + /// Status of the hostname's SSL certificates + pub status: Option, + /// Domain control validation method + pub method: Option, + /// Level of validation to be used for this hostname + #[serde(rename = "type")] + pub validation_type: Option, + /// The certificate authority that will issue the certificate + pub certificate_authority: Option, + /// Indicates whether the certificate covers a wildcard + pub wildcard: Option, + /// SSL specific errors + pub validation_errors: Option>, + /// Custom certificate used for this hostname + pub custom_certificate: Option, + /// Custom CSR ID + pub custom_csr_id: Option, + /// Custom private key + pub custom_key: Option, + /// Certificate pack identifier + pub certificate_pack_id: Option, + /// Bundle method + pub bundle_method: Option, + /// SSL certificate settings + pub settings: Option, + /// CNAME target for validation + pub cname_target: Option, + /// CNAME for validation + pub cname: Option, + /// HTTP URL for validation + pub http_url: Option, + /// HTTP body for validation + pub http_body: Option, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] +pub enum SslStatus { + Initializing, + PendingValidation, + Deleted, + PendingIssuance, + PendingDeployment, + PendingDeletion, + PendingExpiration, + Expired, + Active, + InitializingTimedOut, + ValidationTimedOut, + IssuanceTimedOut, + DeploymentTimedOut, + DeletionTimedOut, + PendingCleanup, + StagingDeployment, + StagingActive, + DeactivatingTimedOut, + InvalidRequestSsl, + ProjectSsl, + ProjectSslTimedOut, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(rename_all = "lowercase")] +pub enum ValidationMethod { + Http, + Txt, + Email, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(rename_all = "lowercase")] +pub enum ValidationType { + Dv, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(rename_all = "lowercase")] +pub enum CertificateAuthority { + Digicert, + Google, + #[serde(rename = "lets_encrypt")] + LetsEncrypt, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(rename_all = "lowercase")] +pub enum BundleMethod { + Ubiquitous, + Optimal, + Force, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct ValidationError { + pub message: Option, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct SslSettings { + pub http2: Option, + pub http3: Option, + pub min_tls_version: Option, + pub tls_1_3: Option, + pub ciphers: Option>, + pub early_hints: Option, +} + +#[derive(Deserialize, Debug)] +pub struct OwnershipVerification { + #[serde(rename = "type")] + pub verification_type: Option, + pub name: Option, + pub value: Option, +} + +#[derive(Deserialize, Debug)] +pub struct OwnershipVerificationHttp { + pub http_body: Option, + pub http_url: Option, +} + +impl ApiResult for CustomHostname {} +impl ApiResult for Vec {} +impl ApiResult for DeleteCustomHostnameResponse {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::framework::endpoint::{EndpointSpec, Method}; + + #[test] + fn test_list_custom_hostnames_endpoint() { + let endpoint = ListCustomHostnames { + zone_id: "test_zone_123", + params: ListCustomHostnamesParams { + hostname: Some("example.com".to_string()), + page: Some(1), + per_page: Some(50), + ..Default::default() + }, + }; + + assert_eq!(endpoint.method(), Method::GET); + assert_eq!(endpoint.path(), "zones/test_zone_123/custom_hostnames"); + assert!(endpoint.query().is_some()); + } + + #[test] + fn test_create_custom_hostname_endpoint() { + let ssl_params = SslParams { + method: Some(ValidationMethod::Http), + validation_type: Some(ValidationType::Dv), + certificate_authority: Some(CertificateAuthority::LetsEncrypt), + wildcard: Some(false), + custom_certificate: None, + custom_csr_id: None, + custom_key: None, + bundle_method: Some(BundleMethod::Ubiquitous), + settings: None, + }; + + let params = CreateCustomHostnameParams { + hostname: "app.example.com", + ssl: ssl_params, + custom_origin_server: Some("origin.example.com"), + custom_origin_sni: None, + custom_metadata: None, + }; + + let endpoint = CreateCustomHostname { + zone_id: "test_zone_123", + params, + }; + + assert_eq!(endpoint.method(), Method::POST); + assert_eq!(endpoint.path(), "zones/test_zone_123/custom_hostnames"); + assert!(endpoint.body().is_some()); + } + + #[test] + fn test_update_custom_hostname_endpoint() { + let ssl_params = SslParams { + method: Some(ValidationMethod::Txt), + validation_type: Some(ValidationType::Dv), + certificate_authority: Some(CertificateAuthority::Google), + wildcard: None, + custom_certificate: None, + custom_csr_id: None, + custom_key: None, + bundle_method: None, + settings: None, + }; + + let params = UpdateCustomHostnameParams { + ssl: Some(ssl_params), + custom_origin_server: Some("new-origin.example.com"), + custom_origin_sni: None, + custom_metadata: None, + }; + + let endpoint = UpdateCustomHostname { + zone_id: "test_zone_123", + identifier: "hostname_456", + params, + }; + + assert_eq!(endpoint.method(), Method::PATCH); + assert_eq!( + endpoint.path(), + "zones/test_zone_123/custom_hostnames/hostname_456" + ); + assert!(endpoint.body().is_some()); + } + + #[test] + fn test_delete_custom_hostname_endpoint() { + let endpoint = DeleteCustomHostname { + zone_id: "test_zone_123", + identifier: "hostname_789", + }; + + assert_eq!(endpoint.method(), Method::DELETE); + assert_eq!( + endpoint.path(), + "zones/test_zone_123/custom_hostnames/hostname_789" + ); + } + + #[test] + fn test_ssl_params_serialization() { + let ssl_params = SslParams { + method: Some(ValidationMethod::Http), + validation_type: Some(ValidationType::Dv), + certificate_authority: Some(CertificateAuthority::Digicert), + wildcard: Some(true), + custom_certificate: Some("cert_data"), + custom_csr_id: Some("csr_123"), + custom_key: Some("key_data"), + bundle_method: Some(BundleMethod::Optimal), + settings: None, + }; + + let json = serde_json::to_string(&ssl_params).unwrap(); + assert!(json.contains("\"method\":\"http\"")); + assert!(json.contains("\"type\":\"dv\"")); + assert!(json.contains("\"certificate_authority\":\"digicert\"")); + } + + #[test] + fn test_custom_hostname_status_deserialization() { + let json = r#""active""#; + let status: CustomHostnameStatus = serde_json::from_str(json).unwrap(); + assert!(matches!(status, CustomHostnameStatus::Active)); + + let json = r#""pending""#; + let status: CustomHostnameStatus = serde_json::from_str(json).unwrap(); + assert!(matches!(status, CustomHostnameStatus::Pending)); + } + + #[test] + fn test_ssl_status_deserialization() { + let json = r#""active""#; + let status: SslStatus = serde_json::from_str(json).unwrap(); + assert!(matches!(status, SslStatus::Active)); + + let json = r#""pending_validation""#; + let status: SslStatus = serde_json::from_str(json).unwrap(); + assert!(matches!(status, SslStatus::PendingValidation)); + } + + #[test] + fn test_validation_method_serialization() { + let method = ValidationMethod::Http; + let json = serde_json::to_string(&method).unwrap(); + assert_eq!(json, r#""http""#); + + let method = ValidationMethod::Txt; + let json = serde_json::to_string(&method).unwrap(); + assert_eq!(json, r#""txt""#); + } + + #[test] + fn test_certificate_authority_variants() { + let ca = CertificateAuthority::LetsEncrypt; + let json = serde_json::to_string(&ca).unwrap(); + assert_eq!(json, r#""lets_encrypt""#); + + let ca = CertificateAuthority::Google; + let json = serde_json::to_string(&ca).unwrap(); + assert_eq!(json, r#""google""#); + } + + #[test] + fn test_list_params_default() { + let params = ListCustomHostnamesParams::default(); + assert!(params.id.is_none()); + assert!(params.hostname.is_none()); + assert!(params.page.is_none()); + } + + #[test] + fn test_custom_metadata_json() { + let metadata = serde_json::json!({ + "app_id": "12345", + "environment": "production" + }); + + let params = CreateCustomHostnameParams { + hostname: "test.example.com", + ssl: SslParams { + method: Some(ValidationMethod::Http), + validation_type: Some(ValidationType::Dv), + certificate_authority: None, + wildcard: None, + custom_certificate: None, + custom_csr_id: None, + custom_key: None, + bundle_method: None, + settings: None, + }, + custom_origin_server: None, + custom_origin_sni: None, + custom_metadata: Some(metadata), + }; + + let json = serde_json::to_string(¶ms).unwrap(); + assert!(json.contains("app_id")); + assert!(json.contains("production")); + } +} diff --git a/cloudflare/src/endpoints/custom_hostnames/mod.rs b/cloudflare/src/endpoints/custom_hostnames/mod.rs new file mode 100644 index 00000000..4a700213 --- /dev/null +++ b/cloudflare/src/endpoints/custom_hostnames/mod.rs @@ -0,0 +1 @@ +pub mod custom_hostname; diff --git a/cloudflare/src/endpoints/mod.rs b/cloudflare/src/endpoints/mod.rs index f590cc90..1162ca35 100644 --- a/cloudflare/src/endpoints/mod.rs +++ b/cloudflare/src/endpoints/mod.rs @@ -7,6 +7,7 @@ pub mod account; pub mod ai; pub mod argo_tunnel; pub mod cfd_tunnel; +pub mod custom_hostnames; pub mod dns; pub mod load_balancing; pub mod r2; From d275e4e746913bbed5c11b5bdd770b944852c0d1 Mon Sep 17 00:00:00 2001 From: Dillen Meijboom Date: Sun, 9 Nov 2025 17:22:31 +0100 Subject: [PATCH 2/7] feat: implement default for tls settings and add switch for on/off --- .../custom_hostnames/custom_hostname.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs index ce4d55ea..53a1fb45 100644 --- a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs +++ b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs @@ -173,7 +173,7 @@ pub struct UpdateCustomHostnameParams<'a> { pub custom_metadata: Option, } -#[derive(Serialize, Clone, Debug)] +#[derive(Serialize, Clone, Debug, Default)] pub struct SslParams<'a> { /// Domain control validation method pub method: Option, @@ -352,13 +352,19 @@ pub struct ValidationError { } #[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(rename_all = "lowercase")] +pub enum Switch { + On, + Off, +} + +#[derive(Deserialize, Serialize, Debug, Clone, Default)] pub struct SslSettings { - pub http2: Option, - pub http3: Option, + pub http2: Option, pub min_tls_version: Option, - pub tls_1_3: Option, + pub tls_1_3: Option, pub ciphers: Option>, - pub early_hints: Option, + pub early_hints: Option, } #[derive(Deserialize, Debug)] From 4e30b0790adfb83e694fe608af96eedd3adf9b29 Mon Sep 17 00:00:00 2001 From: Dillen Meijboom Date: Sun, 9 Nov 2025 23:46:55 +0100 Subject: [PATCH 3/7] fix: remove unknown parameters in custom hostname creation --- cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs index 53a1fb45..fe133722 100644 --- a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs +++ b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs @@ -153,10 +153,6 @@ pub struct CreateCustomHostnameParams<'a> { pub hostname: &'a str, /// SSL properties for the custom hostname pub ssl: SslParams<'a>, - /// A hostname that will be sent to your custom origin server as SNI - pub custom_origin_server: Option<&'a str>, - /// A hostname that will be sent to your custom origin SNI - pub custom_origin_sni: Option<&'a str>, /// Unique key/value metadata for this hostname pub custom_metadata: Option, } From 2c64ae2d7353a2359086ad2aa3d25e2523a897ea Mon Sep 17 00:00:00 2001 From: Dillen Meijboom Date: Mon, 10 Nov 2025 00:22:54 +0100 Subject: [PATCH 4/7] fix: skip null in create custom hostname endpoint --- cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs index fe133722..6eae0ef6 100644 --- a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs +++ b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs @@ -147,6 +147,7 @@ pub enum ListCustomHostnamesOrder { SslStatus, } +#[serde_with::skip_serializing_none] #[derive(Serialize, Clone, Debug)] pub struct CreateCustomHostnameParams<'a> { /// The custom hostname that will point to your hostname via CNAME @@ -169,6 +170,7 @@ pub struct UpdateCustomHostnameParams<'a> { pub custom_metadata: Option, } +#[serde_with::skip_serializing_none] #[derive(Serialize, Clone, Debug, Default)] pub struct SslParams<'a> { /// Domain control validation method @@ -354,6 +356,7 @@ pub enum Switch { Off, } +#[serde_with::skip_serializing_none] #[derive(Deserialize, Serialize, Debug, Clone, Default)] pub struct SslSettings { pub http2: Option, From fca5d3be093b3dae3413ea5bc9c2292901eb015e Mon Sep 17 00:00:00 2001 From: Ronin Beks Date: Tue, 11 Nov 2025 15:33:43 +0100 Subject: [PATCH 5/7] feat: add GetCustomHostname endpoint for direct lookups --- .../custom_hostnames/custom_hostname.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs index 6eae0ef6..c4e43352 100644 --- a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs +++ b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs @@ -37,6 +37,30 @@ impl EndpointSpec for ListCustomHostnames<'_> { } } +/// Custom Hostname Details +/// +#[derive(Debug)] +pub struct GetCustomHostname<'a> { + pub zone_id: &'a str, + pub identifier: &'a str, +} + +impl EndpointSpec for GetCustomHostname<'_> { + type JsonResponse = CustomHostname; + type ResponseType = ApiSuccess; + + fn method(&self) -> Method { + Method::GET + } + + fn path(&self) -> String { + format!( + "zones/{}/custom_hostnames/{}", + self.zone_id, self.identifier + ) + } +} + /// Create Custom Hostname /// Add a new custom hostname and request that an SSL certificate be issued for it. /// From d8d4ccb552170996c8405ba0f4a43b7c34cd4cf4 Mon Sep 17 00:00:00 2001 From: Dillen Meijboom Date: Thu, 13 Nov 2025 15:41:00 +0100 Subject: [PATCH 6/7] fix: add missing validation_records to SslParams --- .../endpoints/custom_hostnames/custom_hostname.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs index c4e43352..2e9d7543 100644 --- a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs +++ b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs @@ -289,6 +289,8 @@ pub struct Ssl { pub wildcard: Option, /// SSL specific errors pub validation_errors: Option>, + /// Validation records + pub validation_records: Option>, /// Custom certificate used for this hostname pub custom_certificate: Option, /// Custom CSR ID @@ -368,6 +370,15 @@ pub enum BundleMethod { Force, } +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct ValidationRecord { + pub emails: Option>, + pub http_body: Option, + pub http_url: Option, + pub txt_name: Option, + pub txt_value: Option, +} + #[derive(Deserialize, Serialize, Debug, Clone)] pub struct ValidationError { pub message: Option, @@ -447,8 +458,6 @@ mod tests { let params = CreateCustomHostnameParams { hostname: "app.example.com", ssl: ssl_params, - custom_origin_server: Some("origin.example.com"), - custom_origin_sni: None, custom_metadata: None, }; @@ -603,8 +612,6 @@ mod tests { bundle_method: None, settings: None, }, - custom_origin_server: None, - custom_origin_sni: None, custom_metadata: Some(metadata), }; From d4283fff1c311379011603eff94ba39f5dd29f13 Mon Sep 17 00:00:00 2001 From: Dillen Meijboom Date: Thu, 13 Nov 2025 15:51:42 +0100 Subject: [PATCH 7/7] fix: skip none in update custom hostname --- cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs index 2e9d7543..902492a3 100644 --- a/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs +++ b/cloudflare/src/endpoints/custom_hostnames/custom_hostname.rs @@ -182,6 +182,7 @@ pub struct CreateCustomHostnameParams<'a> { pub custom_metadata: Option, } +#[serde_with::skip_serializing_none] #[derive(Serialize, Clone, Debug)] pub struct UpdateCustomHostnameParams<'a> { /// SSL properties for the custom hostname