1use crate::handlers::AppState;
7use crate::routes::create_router;
8use axum::serve;
9use redact_core::AnalyzerEngine;
10use std::net::SocketAddr;
11use std::sync::Arc;
12use tokio::net::TcpListener;
13use tower_http::trace::TraceLayer;
14use tracing::info;
15
16#[derive(Debug, Clone)]
18pub struct ServerConfig {
19 pub host: String,
21
22 pub port: u16,
24
25 pub enable_tracing: bool,
27}
28
29impl Default for ServerConfig {
30 fn default() -> Self {
31 Self {
32 host: "0.0.0.0".to_string(),
33 port: 8080,
34 enable_tracing: true,
35 }
36 }
37}
38
39pub struct ApiServer {
41 config: ServerConfig,
42 engine: Arc<AnalyzerEngine>,
43}
44
45impl ApiServer {
46 pub fn new(config: ServerConfig) -> Self {
48 Self {
49 config,
50 engine: Arc::new(AnalyzerEngine::new()),
51 }
52 }
53
54 pub fn with_engine(config: ServerConfig, engine: AnalyzerEngine) -> Self {
56 Self {
57 config,
58 engine: Arc::new(engine),
59 }
60 }
61
62 pub fn bind_address(&self) -> String {
64 format!("{}:{}", self.config.host, self.config.port)
65 }
66
67 pub async fn run(self) -> anyhow::Result<()> {
69 let bind_addr = self.bind_address();
71 let enable_tracing = self.config.enable_tracing;
72
73 let state = AppState {
75 engine: self.engine,
76 };
77
78 let mut app = create_router(state);
80
81 if enable_tracing {
83 app = app.layer(TraceLayer::new_for_http());
84 }
85
86 let addr: SocketAddr = bind_addr.parse()?;
88 let listener = TcpListener::bind(addr).await?;
89
90 info!("Redact API server listening on {}", addr);
91 info!("Endpoints:");
92 info!(" GET /health - Health check");
93 info!(" POST /api/v1/analyze - Analyze text for PII");
94 info!(" POST /api/v1/anonymize - Anonymize detected PII");
95
96 serve(listener, app).await?;
98
99 Ok(())
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn test_server_config() {
109 let config = ServerConfig::default();
110 assert_eq!(config.host, "0.0.0.0");
111 assert_eq!(config.port, 8080);
112 assert!(config.enable_tracing);
113 }
114
115 #[test]
116 fn test_bind_address() {
117 let config = ServerConfig {
118 host: "127.0.0.1".to_string(),
119 port: 3000,
120 enable_tracing: false,
121 };
122 let server = ApiServer::new(config);
123 assert_eq!(server.bind_address(), "127.0.0.1:3000");
124 }
125}