1pub mod auth;
2pub mod config;
3pub mod error;
4pub mod file_store;
5pub mod mcp;
6pub mod openapi;
7pub mod routes;
8pub mod service_pool;
9pub mod state;
10pub mod streaming;
11
12use std::net::SocketAddr;
13use std::sync::Arc;
14
15use config::ProxyConfig;
16use state::AppState;
17
18pub struct ProxyServer {
23 config: ProxyConfig,
24}
25
26impl ProxyServer {
27 pub fn new(config: ProxyConfig) -> Self {
29 Self { config }
30 }
31
32 pub async fn serve(self) -> Result<(), String> {
34 let service_pool = service_pool::ServicePool::from_config(&self.config)?;
35 let key_store = auth::KeyStore::from_config(self.config.general.master_key.clone(), &self.config.keys);
36 let file_store = file_store::FileStore::from_config(self.config.files.as_ref().unwrap_or(&Default::default()))?;
37
38 let state = AppState {
39 key_store: Arc::new(key_store),
40 service_pool: Arc::new(service_pool),
41 file_store: Arc::new(file_store),
42 config: Arc::new(self.config),
43 };
44
45 let addr: SocketAddr = format!("{}:{}", state.config.server.host, state.config.server.port)
46 .parse()
47 .map_err(|e| format!("invalid listen address: {e}"))?;
48
49 let router = routes::build_router(state);
50
51 tracing::info!("liter-llm proxy listening on {addr}");
52
53 let listener = tokio::net::TcpListener::bind(addr)
54 .await
55 .map_err(|e| format!("failed to bind {addr}: {e}"))?;
56
57 let shutdown = async {
58 let _ = tokio::signal::ctrl_c().await;
59 tracing::info!("shutdown signal received");
60 };
61
62 axum::serve(listener, router)
63 .with_graceful_shutdown(shutdown)
64 .await
65 .map_err(|e| format!("server error: {e}"))?;
66
67 Ok(())
68 }
69}