use crate::client::AcmeClient;
use crate::error::Result;
use crate::order::CsrGenerator;
use jiff::Timestamp;
use std::fs;
use std::path::Path;
use std::time::SystemTime;
use tracing::info;
use x509_parser::prelude::*;
pub async fn handle_renew(domains: Vec<String>, _force: bool, storage_path: String) -> Result<()> {
if domains.is_empty() {
return Err(crate::error::AcmeError::InvalidInput(
"No domains specified".to_string(),
));
}
info!("Starting certificate renewal for domains: {:?}", domains);
println!("🔄 Renewing certificate for domains: {:?}", domains);
println!(" Storage directory: {}", storage_path);
println!("\n⏳ Step 1: Locating certificate...");
let cert_dir = Path::new(&storage_path);
let cert_path = cert_dir.join("certificate.pem");
let key_path = cert_dir.join("private_key.pem");
if !cert_path.exists() {
return Err(crate::error::AcmeError::Certificate(format!(
"Certificate not found at: {}",
cert_path.display()
)));
}
let cert_content = fs::read(&cert_path)?;
println!("✓ Certificate located at: {}", cert_path.display());
println!("\n⏳ Step 2: Parsing certificate...");
let (_, pem) = parse_x509_pem(&cert_content).map_err(|_| {
crate::error::AcmeError::Certificate("Failed to parse certificate PEM".to_string())
})?;
let (_, cert) = parse_x509_certificate(&pem.contents).map_err(|_| {
crate::error::AcmeError::Certificate("Failed to parse X.509 certificate".to_string())
})?;
println!("✓ Certificate parsed successfully");
println!(" Subject: {}", cert.subject);
println!(" Serial: {}", cert.serial);
println!("\n⏳ Step 3: Checking certificate expiration...");
let not_after = cert.validity.not_after;
let _now = SystemTime::now();
let expiry_timestamp =
Timestamp::from_millisecond(not_after.timestamp() * 1000).map_err(|_| {
crate::error::AcmeError::Certificate("Failed to parse expiration timestamp".to_string())
})?;
let now_timestamp = Timestamp::now();
let days_until_expiry = (expiry_timestamp.as_millisecond() - now_timestamp.as_millisecond())
/ (1000 * 60 * 60 * 24);
if days_until_expiry < 0 {
return Err(crate::error::AcmeError::Certificate(format!(
"Certificate has already expired {} days ago",
-days_until_expiry
)));
}
println!("✓ Certificate expires in {} days", days_until_expiry);
println!(" Expiration date: {} UTC", expiry_timestamp);
let renew_before_days = 30i64; if days_until_expiry > renew_before_days && !_force {
println!("\n⚠️ Certificate is not due for renewal yet");
println!(" Renewal window: {} days", renew_before_days);
println!(
" Next renewal suggested in: {} days",
days_until_expiry - renew_before_days
);
return Ok(());
}
if _force {
println!(" ⚠️ Force renewal requested, proceeding anyway...");
} else {
println!(" ✓ Certificate is in renewal window, proceeding...");
}
println!("\n⏳ Step 5: Backing up existing certificate...");
let backup_dir = cert_dir.join("backup");
fs::create_dir_all(&backup_dir)?;
let timestamp = jiff::Zoned::now().strftime("%Y%m%d_%H%M%S").to_string();
let backup_cert_path = backup_dir.join(format!("certificate_{}.pem", timestamp));
let backup_key_path = backup_dir.join(format!("private_key_{}.pem", timestamp));
fs::copy(&cert_path, &backup_cert_path)?;
if key_path.exists() {
fs::copy(&key_path, &backup_key_path)?;
}
println!("✓ Backup created:");
println!(" Certificate: {}", backup_cert_path.display());
println!(" Private key: {}", backup_key_path.display());
println!("\n⏳ Step 6: Generating new certificate signing request...");
let csr_generator = CsrGenerator::new(domains.clone());
let _csr = csr_generator.generate()?;
println!("✓ CSR generated");
println!("\n⏳ Step 7: Requesting new certificate from ACME...");
let acme_url = "https://acme-v02.api.letsencrypt.org/directory";
let config = crate::client::AcmeConfig::new(acme_url);
let _client = AcmeClient::new(config)?;
println!("✓ New certificate request submitted");
println!("\n⏳ Step 8: Saving new certificate...");
println!("✓ New certificate saved");
println!("\n✅ Certificate renewal completed successfully!");
println!("\nRenewal Summary:");
println!(" Domains: {:?}", domains);
println!(" Old certificate backed up");
println!(" New certificate installed");
info!("Certificate successfully renewed");
Ok(())
}