1pub mod config;
2#[cfg(feature = "database")]
3pub mod database;
4pub mod error;
5pub mod handlers;
6pub mod middleware;
7#[cfg(feature = "redis")]
8pub mod redis;
9pub mod routing;
10pub mod server;
11
12pub use config::Config;
13#[cfg(feature = "database")]
14pub use database::{
15 get_database, get_mysql_pool, get_postgres_pool, get_sqlite_pool, init_database, DatabasePool,
16};
17pub use error::{AppError, AppResult};
18pub use handlers::AppState;
19#[cfg(feature = "redis")]
20pub use redis::{get_redis_client, get_redis_connection, init_redis};
21pub use routing::{registry, RouteGroup, RouteRegistry};
22pub use server::Server;
23
24pub async fn start() -> Result<(), Box<dyn std::error::Error>> {
31 let config = load_and_init_config(true)?;
32 let server = create_server(config).await?;
33 server
34 .start()
35 .await
36 .map_err(|e| -> Box<dyn std::error::Error> { format!("{}", e).into() })
37}
38
39pub async fn start_with_routes(
65 custom_routes: Option<axum::Router>,
66) -> Result<(), Box<dyn std::error::Error>> {
67 let config = load_and_init_config(true)?;
68 let server = create_server(config).await?;
69 server
70 .start_with_routes(custom_routes)
71 .await
72 .map_err(|e| -> Box<dyn std::error::Error> { format!("{}", e).into() })
73}
74
75pub async fn start_with_config(config: Config) -> Result<(), anyhow::Error> {
79 let server = Server::new(config).await?;
80 server.start().await
81}
82
83fn load_and_init_config(verbose: bool) -> Result<Config, Box<dyn std::error::Error>> {
88 if verbose {
89 eprintln!("正在加载配置...");
90 }
91
92 let config = match Config::from_env() {
94 Ok(cfg) => {
95 if verbose {
96 eprintln!("✓ 配置加载成功, 日志级别: {}", cfg.log.level);
97 }
98 cfg
99 }
100 Err(e) => {
101 if verbose {
102 eprintln!("⚠ 警告: 无法从环境变量加载配置: {}, 使用默认配置", e);
103 } else {
104 eprintln!("警告: 无法从环境变量加载配置,使用默认配置");
105 }
106 Config::default()
107 }
108 };
109
110 if verbose {
112 eprintln!("正在初始化日志系统...");
113 }
114 init_logging(&config).map_err(|e| -> Box<dyn std::error::Error> { format!("{}", e).into() })?;
115 if verbose {
116 eprintln!("✓ 日志系统初始化完成");
117 }
118
119 tracing::info!(
120 "启动 {} v{}",
121 env!("CARGO_PKG_NAME"),
122 env!("CARGO_PKG_VERSION")
123 );
124
125 tracing::info!("服务器配置: {}:{}", config.server.addr, config.server.port);
126 if let Some(ref context_path) = config.server.context_path {
127 tracing::info!("上下文路径: {}", context_path);
128 }
129
130 Ok(config)
131}
132
133async fn create_server(config: Config) -> Result<Server, Box<dyn std::error::Error>> {
135 #[cfg(feature = "database")]
137 if let Some(ref db_config) = config.database {
138 database::init_database(
139 &db_config.url,
140 db_config.max_connections,
141 db_config.min_connections,
142 )
143 .await
144 .map_err(|e| -> Box<dyn std::error::Error> {
145 format!("数据库初始化失败: {}", e).into()
146 })?;
147 }
148
149 #[cfg(feature = "redis")]
151 if let Some(ref redis_config) = config.redis {
152 redis::init_redis(
153 &redis_config.url,
154 redis_config.password.as_deref(),
155 redis_config.pool_size,
156 )
157 .await
158 .map_err(|e| -> Box<dyn std::error::Error> {
159 format!("Redis 初始化失败: {}", e).into()
160 })?;
161 }
162
163 Server::new(config)
165 .await
166 .map_err(|e| -> Box<dyn std::error::Error> { format!("{}", e).into() })
167}
168
169fn init_logging(config: &Config) -> Result<(), anyhow::Error> {
173 use time::UtcOffset;
174 use tracing_subscriber::{
175 fmt::time::OffsetTime, layer::SubscriberExt, util::SubscriberInitExt,
176 };
177
178 let offset =
180 UtcOffset::from_hms(8, 0, 0).map_err(|e| anyhow::anyhow!("无效的时区偏移: {}", e))?;
181 let timer = OffsetTime::new(
182 offset,
183 time::format_description::parse(
184 "[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]",
185 )
186 .map_err(|e| anyhow::anyhow!("时间格式解析失败: {}", e))?,
187 );
188
189 let env_filter = tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
193 config.log.level.as_str().into()
196 });
197
198 let subscriber = tracing_subscriber::registry().with(env_filter);
199
200 if config.log.json {
201 subscriber
202 .with(tracing_subscriber::fmt::layer().json().with_timer(timer))
203 .init();
204 } else {
205 subscriber
207 .with(
208 tracing_subscriber::fmt::layer()
209 .compact()
210 .with_timer(timer)
211 .with_target(true)
212 .with_file(true)
213 .with_line_number(true),
214 )
215 .init();
216 }
217
218 Ok(())
219}