use std::env::var;
use std::process::ExitCode;
use std::time::Duration;
use tokio::net::TcpListener;
use tracing::{error, info};
use tracing_subscriber::EnvFilter;
use tracing_subscriber::fmt;
use ironflow_auth_proxy::{
AuthProxyConfig, AuthProxyState, DATABASE_URL_ENV, MIN_ADMIN_KEY_LEN, registry_from_config,
serve, spawn_purge,
};
use ironflow_core::auth_proxy::{ADMIN_KEY_ENV, DEFAULT_UPSTREAM};
use ironflow_store::crypto::KeyRing;
const LISTEN_ENV: &str = "IRONFLOW_AUTH_PROXY_LISTEN";
const DEFAULT_LISTEN: &str = "0.0.0.0:8080";
const PURGE_INTERVAL: Duration = Duration::from_secs(60);
#[tokio::main]
async fn main() -> ExitCode {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
fmt().json().with_env_filter(filter).init();
let listen = var(LISTEN_ENV)
.ok()
.filter(|value| !value.is_empty())
.unwrap_or_else(|| DEFAULT_LISTEN.to_string());
let admin_key = var(ADMIN_KEY_ENV)
.map(|key| key.trim().to_string())
.unwrap_or_default();
if admin_key.len() < MIN_ADMIN_KEY_LEN {
error!("{ADMIN_KEY_ENV} must be set to at least {MIN_ADMIN_KEY_LEN} characters");
return ExitCode::FAILURE;
}
let database_url = var(DATABASE_URL_ENV)
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty());
let key_ring = match &database_url {
Some(_) => match KeyRing::from_env() {
Ok(ring) => ring,
Err(e) => {
error!(error = %e, "invalid encryption key configuration");
return ExitCode::FAILURE;
}
},
None => None,
};
let backend = if database_url.is_some() {
"postgres"
} else {
"memory"
};
let registry = match registry_from_config(database_url.as_deref(), key_ring).await {
Ok(registry) => registry,
Err(e) => {
error!(error = %e, backend, "cannot build the token registry");
return ExitCode::FAILURE;
}
};
let state = match AuthProxyState::with_registry(AuthProxyConfig::new(&admin_key), registry) {
Ok(state) => state,
Err(e) => {
error!(error = %e, "cannot build the upstream HTTP client");
return ExitCode::FAILURE;
}
};
let listener = match TcpListener::bind(&listen).await {
Ok(listener) => listener,
Err(e) => {
error!(listen = %listen, error = %e, "cannot bind the listen address");
return ExitCode::FAILURE;
}
};
info!(
listen = %listen,
upstream = DEFAULT_UPSTREAM,
backend,
"ironflow-auth-proxy listening"
);
let purge = spawn_purge(state.registry().clone(), PURGE_INTERVAL);
let result = serve(listener, state).await;
purge.abort();
match result {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
error!(error = %e, "server stopped");
ExitCode::FAILURE
}
}
}