pub mod api;
pub mod dashboard;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::net::TcpListener;
use tracing::info;
use crate::trace_store::TraceStore;
use crate::web::api::AuthConfig;
pub struct WebServer {
store: Arc<dyn TraceStore>,
port: u16,
bind_ip: [u8; 4],
auth: AuthConfig,
}
impl std::fmt::Debug for WebServer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WebServer")
.field("port", &self.port)
.finish_non_exhaustive()
}
}
impl WebServer {
#[must_use]
pub fn new(store: Arc<dyn TraceStore>, port: u16) -> Self {
Self {
store,
port,
bind_ip: [127, 0, 0, 1],
auth: AuthConfig::default(),
}
}
#[must_use]
pub const fn with_bind_addr(mut self, ip: [u8; 4]) -> Self {
self.bind_ip = ip;
self
}
#[must_use]
pub fn with_auth(mut self, public_key: String, secret_key: String) -> Self {
self.auth = AuthConfig {
public_key: Some(public_key),
secret_key: Some(secret_key),
};
self
}
pub async fn start(self) -> Result<WebServerHandle, std::io::Error> {
let router = api::create_router_with_auth(self.store, self.auth);
let addr = SocketAddr::from((self.bind_ip, self.port));
let listener = TcpListener::bind(addr).await?;
let actual_addr = listener.local_addr()?;
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>();
tokio::spawn(async move {
axum::serve(listener, router)
.with_graceful_shutdown(async {
let _ = shutdown_rx.await;
})
.await
.unwrap_or_else(|e| {
tracing::error!("web server error: {e}");
});
});
for _ in 0..50 {
if tokio::net::TcpStream::connect(actual_addr).await.is_ok() {
break;
}
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
}
info!(addr = %actual_addr, "telemetry web server started");
Ok(WebServerHandle {
addr: actual_addr,
shutdown_tx: Some(shutdown_tx),
})
}
}
#[derive(Debug)]
pub struct WebServerHandle {
pub addr: SocketAddr,
shutdown_tx: Option<tokio::sync::oneshot::Sender<()>>,
}
impl WebServerHandle {
pub fn stop(&mut self) {
if let Some(tx) = self.shutdown_tx.take() {
let _ = tx.send(());
info!(addr = %self.addr, "telemetry web server stopped");
}
}
#[must_use]
pub fn base_url(&self) -> String {
format!("http://{}", self.addr)
}
}