1pub mod background_tasks;
2mod consts;
3mod http_utils;
4mod jwt;
5mod logger;
6pub mod pipeline;
7mod schema_state;
8mod shared_state;
9mod supergraph;
10mod utils;
11
12use std::sync::Arc;
13
14use crate::{
15 background_tasks::BackgroundTasksManager,
16 consts::ROUTER_VERSION,
17 http_utils::{
18 landing_page::landing_page_handler,
19 probes::{health_check_handler, readiness_check_handler},
20 },
21 jwt::JwtAuthRuntime,
22 logger::configure_logging,
23 pipeline::graphql_request_handler,
24};
25
26pub use crate::{schema_state::SchemaState, shared_state::RouterSharedState};
27
28use hive_router_config::{load_config, HiveRouterConfig};
29use http::header::RETRY_AFTER;
30use ntex::{
31 util::Bytes,
32 web::{self, HttpRequest},
33};
34use tracing::{info, warn};
35
36async fn graphql_endpoint_handler(
37 mut request: HttpRequest,
38 body_bytes: Bytes,
39 schema_state: web::types::State<Arc<SchemaState>>,
40 app_state: web::types::State<Arc<RouterSharedState>>,
41) -> impl web::Responder {
42 let maybe_supergraph = schema_state.current_supergraph();
43
44 if let Some(supergraph) = maybe_supergraph.as_ref() {
45 if let Some(early_response) = app_state
47 .cors_runtime
48 .as_ref()
49 .and_then(|cors| cors.get_early_response(&request))
50 {
51 return early_response;
52 }
53
54 let mut res = graphql_request_handler(
55 &mut request,
56 body_bytes,
57 supergraph,
58 app_state.get_ref(),
59 schema_state.get_ref(),
60 )
61 .await;
62
63 if let Some(cors) = app_state.cors_runtime.as_ref() {
65 cors.set_headers(&request, res.headers_mut());
66 }
67
68 res
69 } else {
70 warn!("No supergraph available yet, unable to process request");
71
72 web::HttpResponse::ServiceUnavailable()
73 .header(RETRY_AFTER, 10)
74 .finish()
75 }
76}
77
78pub async fn router_entrypoint() -> Result<(), Box<dyn std::error::Error>> {
79 let config_path = std::env::var("ROUTER_CONFIG_FILE_PATH").ok();
80 let router_config = load_config(config_path)?;
81 configure_logging(&router_config.log);
82 info!("hive-router@{} starting...", ROUTER_VERSION);
83 let addr = router_config.http.address();
84 let mut bg_tasks_manager = BackgroundTasksManager::new();
85 let (shared_state, schema_state) =
86 configure_app_from_config(router_config, &mut bg_tasks_manager).await?;
87
88 let maybe_error = web::HttpServer::new(move || {
89 web::App::new()
90 .state(shared_state.clone())
91 .state(schema_state.clone())
92 .configure(configure_ntex_app)
93 .default_service(web::to(landing_page_handler))
94 })
95 .bind(addr)?
96 .run()
97 .await
98 .map_err(|err| err.into());
99
100 info!("server stopped, clearning background tasks");
101 bg_tasks_manager.shutdown();
102
103 maybe_error
104}
105
106pub async fn configure_app_from_config(
107 router_config: HiveRouterConfig,
108 bg_tasks_manager: &mut BackgroundTasksManager,
109) -> Result<(Arc<RouterSharedState>, Arc<SchemaState>), Box<dyn std::error::Error>> {
110 let jwt_runtime = match router_config.jwt.is_jwt_auth_enabled() {
111 true => Some(JwtAuthRuntime::init(bg_tasks_manager, &router_config.jwt).await?),
112 false => None,
113 };
114
115 let router_config_arc = Arc::new(router_config);
116 let schema_state =
117 SchemaState::new_from_config(bg_tasks_manager, router_config_arc.clone()).await?;
118 let schema_state_arc = Arc::new(schema_state);
119 let shared_state = Arc::new(RouterSharedState::new(router_config_arc, jwt_runtime)?);
120
121 Ok((shared_state, schema_state_arc))
122}
123
124pub fn configure_ntex_app(cfg: &mut web::ServiceConfig) {
125 cfg.route("/graphql", web::to(graphql_endpoint_handler))
126 .route("/health", web::to(health_check_handler))
127 .route("/readiness", web::to(readiness_check_handler));
128}