use super::LOG_TARGET;
use crate::CertificateProvider;
use async_trait::async_trait;
use pingap_core::Error as ServiceError;
use pingap_core::{
BackgroundTask, NotificationData, NotificationLevel, NotificationSender,
};
use std::sync::Arc;
use tracing::error;
const SECONDS_PER_DAY: i64 = 24 * 3600;
const DEFAULT_EXPIRATION_WARNING_DAYS: u16 = 7;
const CHECK_INTERVAL_MINUTES: u32 = 24 * 60;
async fn do_validity_check(
count: u32,
provider: Arc<dyn CertificateProvider>,
sender: Option<Arc<NotificationSender>>,
) -> Result<bool, ServiceError> {
if count % CHECK_INTERVAL_MINUTES != 0 {
return Ok(false);
}
let now = pingap_core::now_sec() as i64;
let mut name_list = vec![];
for (name, cert) in provider.list().iter() {
let Some(info) = &cert.info else {
continue;
};
if info.acme.is_some() {
continue;
}
let mut buffer_days = cert.buffer_days;
if buffer_days == 0 {
buffer_days = DEFAULT_EXPIRATION_WARNING_DAYS;
}
let time_offset = (buffer_days as i64) * SECONDS_PER_DAY;
if now > info.not_after - time_offset {
error!(
target: LOG_TARGET,
expired_date = info.not_after.to_string(),
name,
"certificate will be expired",
);
name_list.push(name.clone());
continue;
}
if now < info.not_before {
error!(
target: LOG_TARGET,
valid_date = info.not_before.to_string(),
name,
"certificate is not valid",
);
name_list.push(name.clone());
continue;
}
}
if !name_list.is_empty() {
if let Some(sender) = &sender {
sender
.notify(NotificationData {
level: NotificationLevel::Warn,
category: "tls_validity".to_string(),
message: format!(
"certificate {} will be expired",
name_list.join(",")
),
..Default::default()
})
.await;
}
}
Ok(true)
}
struct CertificateValidityTask {
provider: Arc<dyn CertificateProvider>,
sender: Option<Arc<NotificationSender>>,
}
#[async_trait]
impl BackgroundTask for CertificateValidityTask {
async fn execute(&self, count: u32) -> Result<bool, ServiceError> {
do_validity_check(count, self.provider.clone(), self.sender.clone())
.await?;
Ok(true)
}
}
pub fn new_certificate_validity_service(
provider: Arc<dyn CertificateProvider>,
sender: Option<Arc<NotificationSender>>,
) -> Box<dyn BackgroundTask> {
Box::new(CertificateValidityTask { provider, sender })
}