use anyhow::Result;
use axum::Router;
use tracing::{error, info};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[cfg(target_env = "musl")]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
#[tokio::main]
async fn main() -> Result<()> {
let filter = std::env::var("RUST_LOG").unwrap_or_else(|_| {
let level = std::env::var("PGRST_LOG_LEVEL")
.ok()
.and_then(|v| postrust_core::LogLevel::parse_config(&v))
.unwrap_or_else(|| postrust_core::AppConfig::default().log_level);
format!("postrust={}", level.to_tracing())
});
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(filter))
.with(tracing_subscriber::fmt::layer())
.init();
let config = postrust_core::AppConfig::try_from_env()?;
info!("Starting Postrust server");
let postrust_server::App {
router: app, state, ..
} = postrust_server::build(config.clone(), postrust_server::Host::SERVER).await?;
if let Some(admin_port) = config.admin_server_port {
let admin_addr = format!("{}:{}", config.server_host, admin_port);
let admin_listener = tokio::net::TcpListener::bind(&admin_addr).await?;
info!("Admin server listening on http://{}", admin_addr);
let admin_app = postrust_server::custom::admin_router().with_state(state.clone());
tokio::spawn(async move {
if let Err(e) = axum::serve(admin_listener, admin_app).await {
error!("Admin server stopped: {}", e);
}
});
}
match &config.server_unix_socket {
Some(path) => serve_unix_socket(path, app).await?,
None => {
let addr = format!("{}:{}", config.server_host, config.server_port);
let listener = tokio::net::TcpListener::bind(&addr).await?;
info!("Listening on http://{}", addr);
axum::serve(postrust_server::lenient_uri::LenientListener(listener), app).await?;
}
}
Ok(())
}
#[cfg(unix)]
async fn serve_unix_socket(path: &str, app: Router) -> Result<()> {
remove_stale_socket(path).await?;
let listener = tokio::net::UnixListener::bind(path)
.map_err(|e| anyhow::anyhow!("could not bind unix socket {path}: {e}"))?;
info!("Listening on unix:{}", path);
axum::serve(
postrust_server::lenient_uri::LenientUnixListener(listener),
app,
)
.await?;
Ok(())
}
#[cfg(not(unix))]
async fn serve_unix_socket(path: &str, _app: Router) -> Result<()> {
Err(anyhow::anyhow!(
"server_unix_socket ({path}) is not supported on this platform"
))
}
#[cfg(unix)]
async fn remove_stale_socket(path: &str) -> Result<()> {
use std::os::unix::fs::FileTypeExt;
let metadata = match tokio::fs::symlink_metadata(path).await {
Ok(m) => m,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()),
Err(e) => return Err(anyhow::anyhow!("could not stat {path}: {e}")),
};
if !metadata.file_type().is_socket() {
return Err(anyhow::anyhow!(
"{path} exists and is not a socket; refusing to remove it"
));
}
tokio::fs::remove_file(path)
.await
.map_err(|e| anyhow::anyhow!("could not remove stale socket {path}: {e}"))?;
info!("Removed stale socket at {}", path);
Ok(())
}