use std::sync::Arc;
use clap::Parser;
use log::{info, warn};
mod config;
mod i2pcontrol;
mod metrics;
mod server;
pub mod version;
use config::{Cli, Config};
use i2pcontrol::I2pControlClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let cli = Cli::parse();
let cfg = Config::try_from(cli)?;
env_logger::init();
info!(
"Starting I2PControl exporter on {} (target: {})",
cfg.listen_addr, cfg.i2p_addr
);
let tls_insecure_env = cfg.tls_insecure;
let host_is_loopback = reqwest::Url::parse(&cfg.i2p_addr)
.ok()
.and_then(|u| u.host_str().map(|h| h.to_string()))
.map(|host| {
host.eq_ignore_ascii_case("localhost")
|| host
.parse::<std::net::IpAddr>()
.map(|ip| ip.is_loopback())
.unwrap_or(false)
})
.unwrap_or(false);
let allow_insecure = tls_insecure_env || host_is_loopback;
if tls_insecure_env {
warn!("I2PCONTROL_TLS_INSECURE=1 set; accepting invalid TLS certificates");
} else if host_is_loopback {
info!("Loopback target detected; allowing self-signed certificate");
}
let api_client = reqwest::Client::builder()
.http1_only()
.danger_accept_invalid_certs(allow_insecure)
.user_agent(format!("i2pd-exporter/{}", version::VERSION))
.build()?;
let state = Arc::new(I2pControlClient::new(
api_client,
format!("{}/jsonrpc", cfg.i2p_addr.trim_end_matches('/')),
cfg.max_scrape_timeout,
));
let routes = server::routes(state.clone());
info!("Listening on http://{}", cfg.listen_addr);
warp::serve(routes).run(cfg.listen_addr).await;
Ok(())
}