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