65 lines
1.8 KiB
Rust
65 lines
1.8 KiB
Rust
mod api;
|
||
mod auth;
|
||
mod config;
|
||
mod error;
|
||
mod services;
|
||
mod state;
|
||
mod worker;
|
||
|
||
use crate::config::Config;
|
||
use crate::error::{AppError, ErrorCode};
|
||
use crate::services::mail::Mailer;
|
||
use crate::state::AppState;
|
||
|
||
use sqlx::postgres::PgPoolOptions;
|
||
|
||
#[tokio::main]
|
||
async fn main() -> Result<(), AppError> {
|
||
dotenvy::dotenv().ok();
|
||
init_tracing();
|
||
|
||
let config = Config::from_env()?;
|
||
let mailer = Mailer::new(&config)?;
|
||
|
||
let db = PgPoolOptions::new()
|
||
.max_connections(config.database_max_connections)
|
||
.connect(&config.database_url)
|
||
.await
|
||
.map_err(|err| AppError::new(ErrorCode::Internal, "数据库连接失败").with_source(err))?;
|
||
|
||
let redis = redis::Client::open(config.redis_url.clone())
|
||
.map_err(|err| AppError::new(ErrorCode::Internal, "Redis 配置错误").with_source(err))?
|
||
.get_connection_manager()
|
||
.await
|
||
.map_err(|err| AppError::new(ErrorCode::Internal, "Redis 连接失败").with_source(err))?;
|
||
|
||
let image_processing_semaphore = std::sync::Arc::new(tokio::sync::Semaphore::new(
|
||
config.image_processing_concurrency as usize,
|
||
));
|
||
|
||
let state = AppState {
|
||
config,
|
||
db,
|
||
redis,
|
||
mailer: std::sync::Arc::new(mailer),
|
||
image_processing_semaphore,
|
||
};
|
||
|
||
match state.config.role.as_str() {
|
||
"api" => api::run(state).await,
|
||
"worker" => worker::run(state).await,
|
||
other => Err(AppError::new(
|
||
ErrorCode::InvalidRequest,
|
||
format!("未知 IMAGEFORGE_ROLE: {other}(仅支持 api/worker)"),
|
||
)),
|
||
}
|
||
}
|
||
|
||
fn init_tracing() {
|
||
let env_filter = tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
||
tracing_subscriber::EnvFilter::new("info,tower_http=info,imageforge=info")
|
||
});
|
||
|
||
tracing_subscriber::fmt().with_env_filter(env_filter).init();
|
||
}
|