Skip to main content

pingap_acme/
lib.rs

1// Copyright 2024-2025 Tree xie.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use async_trait::async_trait;
16use pingap_certificate::rcgen;
17use snafu::Snafu;
18use substring::Substring;
19
20/// Category name for ACME-related logging
21pub static LOG_TARGET: &str = "pingap::acme";
22
23/// Errors that can occur during ACME operations
24#[derive(Debug, Snafu)]
25pub enum Error {
26    /// Error from the instant-acme library
27    #[snafu(display("ACME instant error: {source}, category: {category}"))]
28    Instant {
29        category: String,
30        source: instant_acme::Error,
31    },
32
33    /// Error from certificate generation
34    #[snafu(display(
35        "Certificate generation error: {source}, category: {category}"
36    ))]
37    Rcgen {
38        category: String,
39        source: rcgen::Error,
40    },
41
42    /// Challenge not found during verification
43    #[snafu(display("ACME challenge not found: {message}"))]
44    NotFound { message: String },
45
46    /// General Let's Encrypt operation failure
47    #[snafu(display(
48        "Let's Encrypt operation failed: {message}, category: {category}"
49    ))]
50    Fail { category: String, message: String },
51}
52
53/// Convenience type alias for Results with our Error type
54pub type Result<T, E = Error> = std::result::Result<T, E>;
55
56fn get_value_from_env(value: &str) -> String {
57    if value.is_empty() {
58        return value.to_string();
59    }
60    let key_prefix = "$ENV:";
61    if value.starts_with(key_prefix) {
62        std::env::var(value.substring(key_prefix.len(), value.len()))
63            .unwrap_or(value.to_string())
64    } else {
65        value.to_string()
66    }
67}
68
69/// Acme DNS task
70#[async_trait]
71pub trait AcmeDnsTask: Sync + Send {
72    /// Add a DNS TXT record
73    async fn add_txt_record(&self, domain: &str, value: &str) -> Result<()>;
74    /// Task done, it will clean up the added dns txt record
75    async fn done(&self) -> Result<()>;
76}
77
78mod dns_ali;
79mod dns_cf;
80mod dns_huawei;
81mod dns_manual;
82mod dns_tencent;
83mod lets_encrypt;
84
85pub use lets_encrypt::{handle_lets_encrypt, new_lets_encrypt_service};