1use axum::{serve, Router};
4use std::net::SocketAddr;
5use std::time::Instant;
6use tokio::net::TcpListener;
7use tracing::{info, error};
8
9use crate::{routes::create_router, handlers::AppState};
10use fukurow_engine::ReasonerEngine;
11use fukurow_domain_cyber::threat_intelligence::ThreatProcessor;
12
13#[derive(Debug, Clone)]
15pub struct ServerConfig {
16 pub host: String,
17 pub port: u16,
18 pub max_connections: usize,
19}
20
21impl Default for ServerConfig {
22 fn default() -> Self {
23 Self {
24 host: "0.0.0.0".to_string(),
25 port: 3000,
26 max_connections: 100,
27 }
28 }
29}
30
31pub struct ReasonerServer {
33 config: ServerConfig,
34 app_state: AppState,
35}
36
37impl ReasonerServer {
38 pub fn new() -> Self {
40 Self::with_config(ServerConfig::default())
41 }
42
43 pub fn with_config(config: ServerConfig) -> Self {
45 let reasoner = ReasonerEngine::new();
46 let threat_processor = ThreatProcessor::new();
47
48 let app_state = AppState {
52 reasoner: std::sync::Arc::new(reasoner),
53 threat_processor: std::sync::Arc::new(tokio::sync::RwLock::new(threat_processor)),
54 start_time: Instant::now(),
55 };
56
57 Self { config, app_state }
58 }
59
60 pub fn address(&self) -> SocketAddr {
62 format!("{}:{}", self.config.host, self.config.port)
63 .parse()
64 .expect("Invalid server address")
65 }
66
67 pub fn create_app(&self) -> Router {
69 create_router(self.app_state.clone())
70 }
71
72 pub async fn serve(self) -> anyhow::Result<()> {
74 let addr = self.address();
75 let app = self.create_app();
76
77 info!("Starting Reasoner API server on {}", addr);
78
79 let listener = TcpListener::bind(addr).await?;
80 info!("Server listening on {}", addr);
81
82 serve(listener, app)
83 .await
84 .map_err(|e| {
85 error!("Server error: {}", e);
86 e.into()
87 })
88 }
89
90 pub async fn run_with_shutdown(self, shutdown_signal: impl std::future::Future<Output = ()> + Send + 'static) -> anyhow::Result<()> {
92 let addr = self.address();
93 let app = self.create_app();
94
95 info!("Starting Reasoner API server on {} with graceful shutdown", addr);
96
97 let listener = TcpListener::bind(addr).await?;
98 info!("Server listening on {}", addr);
99
100 let server = serve(listener, app);
101 let graceful = server.with_graceful_shutdown(shutdown_signal);
102
103 graceful.await.map_err(|e| {
106 error!("Server error: {}", e);
107 e.into()
108 })
109 }
110}
111
112impl Default for ReasonerServer {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118pub fn create_server_with_reasoner(reasoner: ReasonerEngine, config: ServerConfig) -> ReasonerServer {
120 let threat_processor = ThreatProcessor::new();
121
122 let app_state = AppState {
123 reasoner: std::sync::Arc::new(reasoner),
124 threat_processor: std::sync::Arc::new(tokio::sync::RwLock::new(threat_processor)),
125 start_time: Instant::now(),
126 };
127
128 ReasonerServer { config, app_state }
129}
130
131pub async fn shutdown_signal() {
133 use tokio::signal;
134
135 let ctrl_c = async {
136 signal::ctrl_c()
137 .await
138 .expect("failed to install Ctrl+C handler");
139 };
140
141 #[cfg(unix)]
142 let terminate = async {
143 signal::unix::signal(signal::unix::SignalKind::terminate())
144 .expect("failed to install signal handler")
145 .recv()
146 .await;
147 };
148
149 #[cfg(not(unix))]
150 let terminate = std::future::pending::<()>();
151
152 tokio::select! {
153 _ = ctrl_c => {},
154 _ = terminate => {},
155 }
156
157 info!("Shutdown signal received, starting graceful shutdown...");
158}