use std::sync::Arc;
use headgate_api::{ApiConfig, router};
use headgate_core::Inspect;
use headgate_mysql::MysqlStore;
use headgate_postgres::PgStore;
use headgate_redis::RedisStore;
#[tokio::main]
async fn main() {
let addr = std::env::var("HG_API_ADDR").unwrap_or_else(|_| "127.0.0.1:8091".into());
let backend = std::env::var("HG_STORE").unwrap_or_else(|_| "pg".into());
let meta_backend: &'static str = match backend.as_str() {
"redis" => "redis",
"mysql" => "mysql",
_ => "postgres",
};
let inspect: Arc<dyn Inspect> = match backend.as_str() {
"pg" => {
let conninfo = std::env::var("HG_PG")
.unwrap_or_else(|_| "host=/tmp port=5432 user=postgres dbname=hg".into());
Arc::new(PgStore::connect(&conninfo, 8).expect("connect pg"))
}
"redis" => {
let url = std::env::var("HG_REDIS").unwrap_or_else(|_| "redis://127.0.0.1:6380".into());
let prefix = std::env::var("HG_REDIS_PREFIX").unwrap_or_else(|_| "hg".into());
Arc::new(
RedisStore::connect(&url, prefix)
.await
.expect("connect redis"),
)
}
"mysql" => {
let url = std::env::var("HG_MYSQL")
.unwrap_or_else(|_| "mysql://root:hg@127.0.0.1:3307/hg".into());
Arc::new(MysqlStore::connect(&url).expect("connect mysql"))
}
other => panic!("HG_STORE must be pg, redis, or mysql, got `{other}`"),
};
let read_only = std::env::var("HG_READ_ONLY").ok().as_deref() == Some("1");
let loopback =
addr.starts_with("127.") || addr.starts_with("localhost") || addr.starts_with("[::1]");
if !loopback && std::env::var("HG_API_ALLOW_REMOTE").ok().as_deref() != Some("1") {
panic!(
"refusing to bind {addr}: no authentication ships with this binary (authorization boundary). \
Put it behind your own auth and set HG_API_ALLOW_REMOTE=1, or bind loopback."
);
}
let app = router(
inspect,
ApiConfig {
read_only,
backend: meta_backend,
..ApiConfig::default()
},
)
.nest_service(
"/admin",
headgate_ui::router(headgate_ui::Config {
api_base: "/api/v1".into(),
read_only,
}),
);
let listener = tokio::net::TcpListener::bind(&addr).await.expect("bind");
eprintln!("hg-api ({backend}) listening on {addr} — console at http://{addr}/admin");
axum::serve(listener, app).await.expect("serve");
}