use std::sync::Arc;
use salvo::prelude::*;
use salvo::affix_state;
use salvo::oapi::OpenApi;
use genies::context::CONTEXT;
use genies::k8s::k8s_health_check;
use genies_auth::{
LocalAuthConfig, EnforcerManager,
casbin_auth, auth_router, extract_and_sync_schemas,
};
#[tokio::main]
async fn main() {
genies::config::log_config::init_log();
log::info!(
"[auth-admin] 服务启动: http://{}",
CONTEXT.config.server_url.replace("0.0.0.0", "127.0.0.1")
);
CONTEXT.init_database().await;
genies_auth::models::run_migrations().await;
genies_auth_admin::infrastructure::migration::run_migrations().await;
let auth_config = Arc::new(LocalAuthConfig::new(CONTEXT.config.jwt_secret.clone()));
let mgr = Arc::new(
match EnforcerManager::new().await {
Ok(m) => m,
Err(e) => {
log::warn!("[auth-admin] EnforcerManager 初始化失败: {}, 使用空策略降级", e);
EnforcerManager::empty().await
}
}
);
let public_router = Router::new()
.push(k8s_health_check())
.push(genies::dapr_event_router())
.hoop(affix_state::inject(auth_config.clone()))
.push(genies_auth_admin::interfaces::router::public_routes());
let internal_router = Router::new()
.push(genies_auth_admin::interfaces::router::internal_routes());
let protected_router = Router::new()
.hoop(affix_state::inject(auth_config))
.hoop(genies_auth::local_auth)
.hoop(affix_state::inject(mgr.clone()))
.hoop(casbin_auth)
.push(genies_auth_admin::interfaces::router::protected_routes())
.push(auth_router());
let router = Router::new()
.push(public_router)
.push(internal_router)
.push(protected_router);
let doc = OpenApi::new("auth-admin", "1.0.0").merge_router(&router);
extract_and_sync_schemas(&doc).await.ok();
let instance_id: i64 = genies::core::id_gen::next_id().parse().expect("snowflake id should be valid i64");
let now = rbdc::DateTime::now();
let instance = genies_auth_admin::domain::entity::app_instance_entity::AppInstanceEntity {
id: None,
app_name: Some(CONTEXT.config.server_name.clone()),
instance_id: Some(instance_id),
base_url: Some(format!("http://{}", CONTEXT.config.server_url)),
version: Some("1.0.0".to_string()),
status: Some(1),
last_heartbeat_at: Some(now.clone()),
registered_at: Some(now),
metadata: None,
};
if let Err(e) = genies_auth_admin::domain::service::AppInstanceDomainService::register_or_update(&instance).await {
log::warn!("[auth-admin] 自注册失败: {}, 不影响启动", e);
} else {
log::info!("[auth-admin] 自注册成功, instance_id={}", instance_id);
}
let heartbeat_interval = CONTEXT.config.heartbeat_interval;
tokio::spawn(async move {
let mut interval = tokio::time::interval(std::time::Duration::from_secs(heartbeat_interval));
loop {
interval.tick().await;
if let Err(e) = genies_auth_admin::domain::service::AppInstanceDomainService::heartbeat(instance_id).await {
log::warn!("[auth-admin] self heartbeat failed: {}", e);
}
}
});
tokio::spawn(async {
let mut interval = tokio::time::interval(std::time::Duration::from_secs(60));
loop {
interval.tick().await;
if let Err(e) = genies_auth_admin::domain::service::AppInstanceDomainService::cleanup_stale(90).await {
log::warn!("[auth-admin] Failed to cleanup stale instances: {}", e);
}
if let Err(e) = genies_auth_admin::domain::service::AppInstanceDomainService::delete_stale_instances(3600).await {
log::warn!("[auth-admin] Failed to delete stale instances: {}", e);
}
}
});
let acceptor = TcpListener::new(&CONTEXT.config.server_url).bind().await;
Server::new(acceptor).serve(router).await;
}