- account: admin 权限守卫 (list_accounts/get_account/update_status/list_logs) - relay: SSRF 防护 (禁止内网地址、限制 http scheme、30s 超时) - config: 生产环境强制 ZCLAW_SAAS_JWT_SECRET 环境变量 - error: 500 错误不再泄露内部细节给客户端 - main: CORS 支持配置白名单 origins - 全部 21 个测试通过 (7 unit + 14 integration)
75 lines
2.3 KiB
Rust
75 lines
2.3 KiB
Rust
//! ZCLAW SaaS 服务入口
|
|
|
|
use tracing::info;
|
|
use zclaw_saas::{config::SaaSConfig, db::init_db, state::AppState};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> anyhow::Result<()> {
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(
|
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
|
.unwrap_or_else(|_| "zclaw_saas=debug,tower_http=debug".into()),
|
|
)
|
|
.init();
|
|
|
|
let config = SaaSConfig::load()?;
|
|
info!("SaaS config loaded: {}:{}", config.server.host, config.server.port);
|
|
|
|
let db = init_db(&config.database.url).await?;
|
|
info!("Database initialized");
|
|
|
|
let state = AppState::new(db, config.clone())?;
|
|
let app = build_router(state);
|
|
|
|
let listener = tokio::net::TcpListener::bind(format!("{}:{}", config.server.host, config.server.port))
|
|
.await?;
|
|
info!("SaaS server listening on {}:{}", config.server.host, config.server.port);
|
|
|
|
axum::serve(listener, app).await?;
|
|
Ok(())
|
|
}
|
|
|
|
fn build_router(state: AppState) -> axum::Router {
|
|
use axum::middleware;
|
|
use tower_http::cors::{Any, CorsLayer};
|
|
use tower_http::trace::TraceLayer;
|
|
|
|
use axum::http::HeaderValue;
|
|
let cors = {
|
|
let config = state.config.blocking_read();
|
|
if config.server.cors_origins.is_empty() {
|
|
CorsLayer::new()
|
|
.allow_origin(Any)
|
|
.allow_methods(Any)
|
|
.allow_headers(Any)
|
|
} else {
|
|
let origins: Vec<HeaderValue> = config.server.cors_origins.iter()
|
|
.filter_map(|o: &String| o.parse::<HeaderValue>().ok())
|
|
.collect();
|
|
CorsLayer::new()
|
|
.allow_origin(origins)
|
|
.allow_methods(Any)
|
|
.allow_headers(Any)
|
|
}
|
|
};
|
|
|
|
let public_routes = zclaw_saas::auth::routes();
|
|
|
|
let protected_routes = zclaw_saas::auth::protected_routes()
|
|
.merge(zclaw_saas::account::routes())
|
|
.merge(zclaw_saas::model_config::routes())
|
|
.merge(zclaw_saas::relay::routes())
|
|
.merge(zclaw_saas::migration::routes())
|
|
.layer(middleware::from_fn_with_state(
|
|
state.clone(),
|
|
zclaw_saas::auth::auth_middleware,
|
|
));
|
|
|
|
axum::Router::new()
|
|
.merge(public_routes)
|
|
.merge(protected_routes)
|
|
.layer(TraceLayer::new_for_http())
|
|
.layer(cors)
|
|
.with_state(state)
|
|
}
|