#![warn(missing_docs)]
use std::net::SocketAddr;
use std::path::PathBuf;
use adler_core::{Client, Site};
use tokio::net::TcpListener;
use tokio::signal;
mod api;
mod assets;
mod error;
mod persist;
mod scan;
mod state;
pub use api::router;
pub use error::{Error, Result};
pub use persist::{PersistedScan, default_dir as default_scans_dir};
pub use scan::{FinishedScan, ScanHandle, ScanId, Summary};
pub use state::AppState;
#[derive(Debug, Clone)]
pub struct AppConfig {
pub bind: SocketAddr,
pub scan_capacity: usize,
pub scans_dir: Option<PathBuf>,
}
impl Default for AppConfig {
fn default() -> Self {
Self {
bind: SocketAddr::from(([127, 0, 0, 1], 8765)),
scan_capacity: 32,
scans_dir: None,
}
}
}
pub async fn serve(sites: Vec<Site>, client: Client, config: AppConfig) -> Result<()> {
let mut state = AppState::new(sites, client, config.scan_capacity);
if let Some(dir) = config.scans_dir.clone() {
state = state.with_scans_dir(dir);
}
let app = assets::attach(router(state));
let listener = TcpListener::bind(config.bind)
.await
.map_err(|source| Error::Bind {
addr: config.bind.to_string(),
source,
})?;
tracing::debug!(bind = %config.bind, "adler-server listening");
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.map_err(Error::Server)?;
Ok(())
}
async fn shutdown_signal() {
let ctrl_c = async {
if let Err(err) = signal::ctrl_c().await {
tracing::warn!(error = %err, "failed to install Ctrl-C handler");
}
};
#[cfg(unix)]
let terminate = async {
match signal::unix::signal(signal::unix::SignalKind::terminate()) {
Ok(mut sig) => {
sig.recv().await;
}
Err(err) => tracing::warn!(error = %err, "failed to install SIGTERM handler"),
}
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
() = ctrl_c => {}
() = terminate => {}
}
tracing::info!("shutdown signal received");
}