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 = Config::from_env().unwrap_or_else(|_| {
33 eprintln!("警告: 无法从环境变量加载配置,使用默认配置");
34 Config::default()
35 });
36
37 init_logging(&config).map_err(|e| -> Box<dyn std::error::Error> { format!("{}", e).into() })?;
39
40 tracing::info!(
41 "启动 {} v{}",
42 env!("CARGO_PKG_NAME"),
43 env!("CARGO_PKG_VERSION")
44 );
45
46 #[cfg(feature = "database")]
48 if let Some(ref db_config) = config.database {
49 database::init_database(
50 &db_config.url,
51 db_config.max_connections,
52 db_config.min_connections,
53 )
54 .await
55 .map_err(|e| -> Box<dyn std::error::Error> {
56 format!("数据库初始化失败: {}", e).into()
57 })?;
58 }
59
60 #[cfg(feature = "redis")]
62 if let Some(ref redis_config) = config.redis {
63 redis::init_redis(
64 &redis_config.url,
65 redis_config.password.as_deref(),
66 redis_config.pool_size,
67 )
68 .await
69 .map_err(|e| -> Box<dyn std::error::Error> {
70 format!("Redis 初始化失败: {}", e).into()
71 })?;
72 }
73
74 let server = Server::new(config)
76 .await
77 .map_err(|e| -> Box<dyn std::error::Error> { format!("{}", e).into() })?;
78 server
79 .start()
80 .await
81 .map_err(|e| -> Box<dyn std::error::Error> { format!("{}", e).into() })
82}
83
84pub async fn start_with_routes(
110 custom_routes: Option<axum::Router>,
111) -> Result<(), Box<dyn std::error::Error>> {
112 let config = Config::from_env().unwrap_or_else(|_| {
114 eprintln!("警告: 无法从环境变量加载配置,使用默认配置");
115 Config::default()
116 });
117
118 init_logging(&config).map_err(|e| -> Box<dyn std::error::Error> { format!("{}", e).into() })?;
120
121 tracing::info!(
122 "启动 {} v{}",
123 env!("CARGO_PKG_NAME"),
124 env!("CARGO_PKG_VERSION")
125 );
126
127 #[cfg(feature = "database")]
129 if let Some(ref db_config) = config.database {
130 database::init_database(
131 &db_config.url,
132 db_config.max_connections,
133 db_config.min_connections,
134 )
135 .await
136 .map_err(|e| -> Box<dyn std::error::Error> {
137 format!("数据库初始化失败: {}", e).into()
138 })?;
139 }
140
141 #[cfg(feature = "redis")]
143 if let Some(ref redis_config) = config.redis {
144 redis::init_redis(
145 &redis_config.url,
146 redis_config.password.as_deref(),
147 redis_config.pool_size,
148 )
149 .await
150 .map_err(|e| -> Box<dyn std::error::Error> {
151 format!("Redis 初始化失败: {}", e).into()
152 })?;
153 }
154
155 let server = Server::new(config)
157 .await
158 .map_err(|e| -> Box<dyn std::error::Error> { format!("{}", e).into() })?;
159 server
160 .start_with_routes(custom_routes)
161 .await
162 .map_err(|e| -> Box<dyn std::error::Error> { format!("{}", e).into() })
163}
164
165pub async fn start_with_config(config: Config) -> Result<(), anyhow::Error> {
169 let server = Server::new(config).await?;
170 server.start().await
171}
172
173fn init_logging(config: &Config) -> Result<(), anyhow::Error> {
177 use time::UtcOffset;
178 use tracing_subscriber::{
179 fmt::time::OffsetTime, layer::SubscriberExt, util::SubscriberInitExt,
180 };
181
182 let offset =
184 UtcOffset::from_hms(8, 0, 0).map_err(|e| anyhow::anyhow!("无效的时区偏移: {}", e))?;
185 let timer = OffsetTime::new(
186 offset,
187 time::format_description::parse(
188 "[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]",
189 )
190 .map_err(|e| anyhow::anyhow!("时间格式解析失败: {}", e))?,
191 );
192
193 let subscriber = tracing_subscriber::registry().with(
195 tracing_subscriber::EnvFilter::try_from_default_env()
196 .unwrap_or_else(|_| format!("{}={}", env!("CARGO_PKG_NAME"), config.log.level).into()),
197 );
198
199 if config.log.json {
200 subscriber
201 .with(tracing_subscriber::fmt::layer().json().with_timer(timer))
202 .init();
203 } else {
204 subscriber
206 .with(
207 tracing_subscriber::fmt::layer()
208 .compact()
209 .with_timer(timer)
210 .with_target(true)
211 .with_file(true)
212 .with_line_number(true),
213 )
214 .init();
215 }
216
217 Ok(())
218}