1use crate::cache::Cache;
2use crate::config::{Config, ServerConfig};
3use crate::container::App;
4use crate::http::{HttpResponse, Request};
5use crate::middleware::{Middleware, MiddlewareChain, MiddlewareRegistry};
6use crate::routing::Router;
7use crate::websocket::handle_ws_upgrade;
8use bytes::Bytes;
9use http_body_util::Full;
10use hyper::server::conn::http1;
11use hyper::service::service_fn;
12use hyper_util::rt::TokioIo;
13use std::convert::Infallible;
14use std::net::SocketAddr;
15use std::sync::Arc;
16use tokio::net::TcpListener;
17
18pub struct Server {
19 router: Arc<Router>,
20 middleware: MiddlewareRegistry,
21 host: String,
22 port: u16,
23}
24
25impl Server {
26 pub fn new(router: impl Into<Router>) -> Self {
27 Self {
28 router: Arc::new(router.into()),
29 middleware: MiddlewareRegistry::new(),
30 host: "127.0.0.1".to_string(),
31 port: 8080,
32 }
33 }
34
35 pub fn from_config(router: impl Into<Router>) -> Self {
36 App::init();
38
39 App::boot_services();
41
42 let config = Config::get::<ServerConfig>().unwrap_or_else(ServerConfig::from_env);
43 Self {
44 router: Arc::new(router.into()),
45 middleware: MiddlewareRegistry::from_global(),
47 host: config.host,
48 port: config.port,
49 }
50 }
51
52 pub fn middleware<M: Middleware + 'static>(mut self, middleware: M) -> Self {
66 self.middleware = self.middleware.append(middleware);
67 self
68 }
69
70 pub fn host(mut self, host: &str) -> Self {
71 self.host = host.to_string();
72 self
73 }
74
75 pub fn port(mut self, port: u16) -> Self {
76 self.port = port;
77 self
78 }
79
80 fn get_addr(&self) -> SocketAddr {
81 SocketAddr::new(self.host.parse().unwrap(), self.port)
82 }
83
84 pub async fn run(self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
85 Cache::bootstrap().await;
87
88 let addr: SocketAddr = self.get_addr();
89 let listener = TcpListener::bind(addr).await?;
90
91 println!("Ferro server running on http://{addr}");
92
93 let router = self.router;
94 let middleware = Arc::new(self.middleware);
95
96 loop {
97 let (stream, _) = listener.accept().await?;
98 let io = TokioIo::new(stream);
99 let router = router.clone();
100 let middleware = middleware.clone();
101
102 tokio::spawn(async move {
103 let service = service_fn(move |req: hyper::Request<hyper::body::Incoming>| {
104 let router = router.clone();
105 let middleware = middleware.clone();
106 async move { Ok::<_, Infallible>(handle_request(router, middleware, req).await) }
107 });
108
109 if let Err(err) = http1::Builder::new()
110 .serve_connection(io, service)
111 .with_upgrades()
112 .await
113 {
114 eprintln!("Error serving connection: {err:?}");
115 }
116 });
117 }
118 }
119}
120
121async fn handle_request(
122 router: Arc<Router>,
123 middleware_registry: Arc<MiddlewareRegistry>,
124 req: hyper::Request<hyper::body::Incoming>,
125) -> hyper::Response<Full<Bytes>> {
126 let method = req.method().clone();
127 let path = req.uri().path().to_string();
128 let query = req.uri().query().unwrap_or("");
129
130 if path == "/_ferro/ws" && hyper_tungstenite::is_upgrade_request(&req) {
132 return handle_ws_upgrade(req);
133 }
134
135 if path.starts_with("/_ferro/") && method == hyper::Method::GET {
138 return match path.as_str() {
139 "/_ferro/health" => health_response(query).await,
140 "/_ferro/routes" => crate::debug::handle_routes(),
141 "/_ferro/middleware" => crate::debug::handle_middleware(),
142 "/_ferro/services" => crate::debug::handle_services(),
143 "/_ferro/metrics" => crate::debug::handle_metrics(),
144 "/_ferro/queue/jobs" => crate::debug::handle_queue_jobs().await,
145 "/_ferro/queue/stats" => crate::debug::handle_queue_stats().await,
146 _ => HttpResponse::text("404 Not Found").status(404).into_hyper(),
147 };
148 }
149
150 let response = match router.match_route(&method, &path) {
155 Some((handler, params, route_pattern)) => {
156 let request = Request::new(req)
157 .with_params(params)
158 .with_route_pattern(route_pattern.clone());
159
160 let mut chain = MiddlewareChain::new();
162
163 chain.extend(middleware_registry.global_middleware().iter().cloned());
165
166 let route_middleware = router.get_route_middleware(&route_pattern);
168 chain.extend(route_middleware);
169
170 let response = chain.execute(request, handler).await;
172
173 let http_response = response.unwrap_or_else(|e| e);
175 http_response.into_hyper()
176 }
177 None => {
178 if method == hyper::Method::GET || method == hyper::Method::HEAD {
180 if let Some(response) = crate::static_files::try_serve_static_file(&path).await {
181 return response;
182 }
183 }
184
185 if let Some((fallback_handler, fallback_middleware)) = router.get_fallback() {
187 let request = Request::new(req).with_params(std::collections::HashMap::new());
188
189 let mut chain = MiddlewareChain::new();
191
192 chain.extend(middleware_registry.global_middleware().iter().cloned());
194
195 chain.extend(fallback_middleware);
197
198 let response = chain.execute(request, fallback_handler).await;
200
201 let http_response = response.unwrap_or_else(|e| e);
203 http_response.into_hyper()
204 } else {
205 HttpResponse::text("404 Not Found").status(404).into_hyper()
207 }
208 }
209 };
210
211 response
212}
213
214async fn health_response(query: &str) -> hyper::Response<Full<Bytes>> {
218 use chrono::Utc;
219 use serde_json::json;
220
221 let timestamp = Utc::now().to_rfc3339();
222 let check_db = query.contains("db=true");
223
224 let mut response = json!({
225 "status": "ok",
226 "timestamp": timestamp
227 });
228
229 if check_db {
230 match check_database_health().await {
232 Ok(_) => {
233 response["database"] = json!("connected");
234 }
235 Err(e) => {
236 response["database"] = json!("error");
237 response["database_error"] = json!(e);
238 }
239 }
240 }
241
242 let body =
243 serde_json::to_string(&response).unwrap_or_else(|_| r#"{"status":"ok"}"#.to_string());
244
245 hyper::Response::builder()
246 .status(200)
247 .header("Content-Type", "application/json")
248 .body(Full::new(Bytes::from(body)))
249 .unwrap()
250}
251
252async fn check_database_health() -> Result<(), String> {
254 use crate::database::DB;
255 use sea_orm::ConnectionTrait;
256
257 if !DB::is_connected() {
258 return Err("Database not initialized".to_string());
259 }
260
261 let conn = DB::connection().map_err(|e| e.to_string())?;
262
263 conn.inner()
265 .execute_unprepared("SELECT 1")
266 .await
267 .map_err(|e| format!("Database query failed: {e}"))?;
268
269 Ok(())
270}