1use axum::{routing::get, Router};
19use axum_server::acme::{caches::DirCache, Acceptor, AcmeConfig};
20use futures_util::StreamExt;
21use std::{env, net::SocketAddr};
22
23#[tokio::main]
24async fn main() {
25 let domains = env::var("ACME_DOMAINS")
26 .expect("set ACME_DOMAINS to a comma-separated list of domains")
27 .split(',')
28 .map(str::to_owned)
29 .collect::<Vec<_>>();
30
31 let cache = env::var("ACME_CACHE").unwrap_or_else(|_| "./acme-cache".to_owned());
32 let port = env::var("ACME_PORT")
33 .ok()
34 .and_then(|port| port.parse().ok())
35 .unwrap_or(443);
36 let production = env::var("ACME_PRODUCTION").is_ok_and(|value| value == "1");
37
38 let mut config = AcmeConfig::new(domains)
39 .cache(DirCache::new(cache))
40 .directory_lets_encrypt(production);
41
42 if let Ok(contact) = env::var("ACME_CONTACT") {
43 config = config.contact([format!("mailto:{contact}")]);
44 }
45
46 let mut state = config.state();
47 let acceptor = Acceptor::new(&state);
48
49 tokio::spawn(async move {
51 while let Some(event) = state.next().await {
52 match event {
53 Ok(event) => println!("acme: {event:?}"),
54 Err(error) => eprintln!("acme error: {error:?}"),
55 }
56 }
57 });
58
59 let app = Router::new().route("/", get(|| async { "Hello, world!" }));
60 let addr = SocketAddr::from(([0, 0, 0, 0], port));
61
62 println!("listening on https://{addr}");
63 if !production {
64 println!("using the Let's Encrypt staging directory");
65 }
66
67 axum_server::bind(addr)
68 .acceptor(acceptor)
69 .serve(app.into_make_service())
70 .await
71 .unwrap();
72}