use dtmrs::server::api::Api;
use dtmrs::server::driver::Driver;
use dtmrs::server::http::{router, App};
use dtmrs::Store;
use std::time::Duration;
use tracing::info;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()),
)
.init();
let db = std::env::var("DTMRS_DB").unwrap_or_else(|_| "sqlite:dtmrs.db".into());
let addr = std::env::var("DTMRS_ADDR").unwrap_or_else(|_| "0.0.0.0:36789".into());
let grpc_addr = std::env::var("DTMRS_GRPC_ADDR").unwrap_or_else(|_| "0.0.0.0:36790".into());
let owner =
std::env::var("DTMRS_OWNER").unwrap_or_else(|_| format!("tc-{}", std::process::id()));
let tick_ms: u64 = std::env::var("DTMRS_TICK_MS")
.ok()
.and_then(|v| v.parse().ok())
.filter(|v| *v > 0)
.unwrap_or(1000);
let store = Store::open(&db).await?;
let store_for_auth = store.clone();
let driver = Driver::from_env(store.clone(), owner.clone());
info!(db = %db, http = %addr, grpc = %grpc_addr, owner = %owner,
branch_timeout = driver.http_timeout_secs(), lease = driver.lease,
retry_initial = driver.retry.initial, retry_max = driver.retry.max, tick_ms,
"dtmrs 启动");
tokio::spawn(driver.clone().run_forever(Duration::from_millis(tick_ms)));
let inline = !matches!(
std::env::var("DTMRS_INLINE_SUBMIT").as_deref(),
Ok("0") | Ok("false")
);
let api = if inline {
Api::new(store).with_inline_driver(driver.clone())
} else {
Api::new(store)
};
info!(inline_submit = inline, "提交后是否直接开推");
let auth = dtmrs::server::auth::Auth::from_env().map(|a| a.with_store(store_for_auth.clone()));
if let Some(a) = &auth {
info!(
登录页 = a.has_login(),
"认证已开启(业务端用 Authorization: Bearer <DTMRS_AUTH_TOKEN>)"
);
}
let grpc = serve_grpc(api.clone(), grpc_addr, auth.clone());
let http = serve_http(api, addr, auth, store_for_auth);
tokio::select! {
r = grpc => r?,
r = http => r?,
}
Ok(())
}
async fn serve_http(
api: Api,
addr: String,
auth: Option<std::sync::Arc<dtmrs::server::auth::Auth>>,
store: dtmrs::Store,
) -> anyhow::Result<()> {
let listener = tokio::net::TcpListener::bind(&addr).await?;
let app = App::new(api);
let router = match auth {
Some(auth) => dtmrs::server::http::router_with_auth(app, auth, store),
None => {
let public = !addr.starts_with("127.") && !addr.starts_with("localhost");
if public {
tracing::warn!(
%addr,
"⚠ 监听在非回环地址但既没设 DTMRS_AUTH_TOKEN 也没设 \
DTMRS_ADMIN_PASSWORD —— 管理台和全部接口(含 abort/retry/submit)\
对任何能连上的人开放"
);
}
router(app)
}
};
axum::serve(listener, router).await?;
Ok(())
}
#[cfg(feature = "grpc")]
async fn serve_grpc(
api: Api,
addr: String,
auth: Option<std::sync::Arc<dtmrs::server::auth::Auth>>,
) -> anyhow::Result<()> {
use dtmrs::server::grpc::server::TcService;
let sock = addr.parse()?;
let svc = TcService::new(api);
match auth {
Some(a) => {
tonic::transport::Server::builder()
.add_service(svc.into_server_with_auth(a))
.serve(sock)
.await?
}
None => {
tonic::transport::Server::builder()
.add_service(svc.into_server())
.serve(sock)
.await?
}
}
Ok(())
}
#[cfg(not(feature = "grpc"))]
async fn serve_grpc(
_api: Api,
_addr: String,
_auth: Option<std::sync::Arc<dtmrs::server::auth::Auth>>,
) -> anyhow::Result<()> {
std::future::pending::<()>().await;
Ok(())
}