Files
zclaw_openfang/crates/zclaw-saas/src/main.rs
iven 94bf387aee fix(saas): 安全修复 — IDOR防护、SSRF防护、JWT密钥强制、错误信息脱敏、CORS配置化
- 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)
2026-03-27 13:07:20 +08:00

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)
}