bestool_alertd/
http_server.rs1use std::sync::Arc;
4
5use axum::{
6 Router,
7 routing::{get, post},
8};
9use jiff::Timestamp;
10use tokio::sync::mpsc;
11use tower_http::trace::{DefaultMakeSpan, DefaultOnResponse, TraceLayer};
12use tracing::{Level, error, info, warn};
13
14use crate::{EmailConfig, alert::InternalContext, events::EventManager, scheduler::Scheduler};
15
16mod endpoints;
17mod state;
18#[cfg(test)]
19mod test_utils;
20mod types;
21
22pub use endpoints::*;
23pub use state::ServerState;
24pub use types::*;
25
26pub async fn start_server(
27 reload_tx: mpsc::Sender<()>,
28 event_manager: Option<Arc<EventManager>>,
29 internal_context: Arc<InternalContext>,
30 email_config: Option<EmailConfig>,
31 dry_run: bool,
32 addrs: Vec<std::net::SocketAddr>,
33 scheduler: Arc<Scheduler>,
34) {
35 let started_at = Timestamp::now();
36 let pid = std::process::id();
37
38 let state = ServerState {
39 reload_tx,
40 started_at,
41 pid,
42 event_manager,
43 internal_context,
44 email_config,
45 dry_run,
46 scheduler,
47 };
48
49 let app = Router::new()
50 .route("/", get(handle_index))
51 .route("/reload", post(handle_reload))
52 .route("/alert", post(handle_alert))
53 .route("/alerts", get(handle_alerts).delete(handle_pause_alert))
54 .route("/targets", get(handle_targets))
55 .route("/validate", post(handle_validate))
56 .route("/metrics", get(handle_metrics))
57 .route("/status", get(handle_status))
58 .layer(
59 TraceLayer::new_for_http()
60 .make_span_with(
61 DefaultMakeSpan::new()
62 .level(Level::INFO)
63 .include_headers(false),
64 )
65 .on_request(|request: &axum::http::Request<_>, _span: &tracing::Span| {
66 info!(
67 method = %request.method(),
68 uri = %request.uri(),
69 "HTTP request"
70 );
71 })
72 .on_response(
73 DefaultOnResponse::new()
74 .level(Level::INFO)
75 .include_headers(false),
76 ),
77 )
78 .with_state(Arc::new(state));
79
80 let addrs_to_try = if addrs.is_empty() {
82 vec![
83 "[::1]:8271".parse().unwrap(),
84 "127.0.0.1:8271".parse().unwrap(),
85 ]
86 } else {
87 addrs
88 };
89
90 let mut listener = None;
91 let mut last_error = None;
92
93 for addr in &addrs_to_try {
95 match tokio::net::TcpListener::bind(addr).await {
96 Ok(l) => {
97 info!("HTTP server listening on http://{}", addr);
98 listener = Some(l);
99 break;
100 }
101 Err(e) => {
102 warn!("failed to bind HTTP server to {}: {}", addr, e);
103 last_error = Some(e);
104 }
105 }
106 }
107
108 let listener = match listener {
109 Some(l) => l,
110 None => {
111 if let Some(e) = last_error {
112 warn!("failed to bind HTTP server to any address: {}", e);
113 } else {
114 warn!("no addresses provided for HTTP server");
115 }
116 warn!("waiting 10 seconds before continuing without");
117 warn!("use --no-server to disable the HTTP server and this warning");
118
119 tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
120
121 info!("continuing without HTTP server");
122 return;
123 }
124 };
125
126 if let Err(e) = axum::serve(listener, app).await {
127 error!("HTTP server error: {}", e);
128 }
129}