safety(web-gateway): use configured CORS origins, add health/readiness probes

- Replace CorsLayer::allow_origin(Any) with configured cors_origins whitelist
- Add GET /health liveness probe (returns {"status":"ok"})
- Add GET /ready readiness probe (reports gRPC channel availability)
- Fix unused import warning in auth middleware tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-02-22 07:53:02 +01:00
parent 356bd39810
commit 65ca5372f2
3 changed files with 36 additions and 7 deletions

View File

@@ -41,7 +41,6 @@ mod tests {
use axum::body::Body;
use axum::http::{Request as HttpRequest, StatusCode};
use axum::middleware;
use axum::response::IntoResponse;
use axum::routing::get;
use axum::Router;
use jsonwebtoken::{encode, EncodingKey, Header};

View File

@@ -1,5 +1,6 @@
use anyhow::Result;
use tower_http::cors::{Any, CorsLayer};
use axum::http::HeaderValue;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;
use tracing::info;
@@ -19,16 +20,23 @@ async fn main() -> Result<()> {
info!(" Trading service: {}", config.trading_service_url);
info!(" Backtesting service: {}", config.backtesting_service_url);
info!(" ML Training service: {}", config.ml_training_service_url);
info!(" CORS origins: {:?}", config.cors_origins);
let state = AppState::new(config).await?;
let state = AppState::new(config.clone()).await?;
// Start gRPC stream bridge tasks (forward gRPC streams to WebSocket broadcast)
start_grpc_stream_bridges(state.trading_channel.clone(), state.ws_broadcast.clone());
// Build CORS from configured origins
let origins: Vec<HeaderValue> = config
.cors_origins
.iter()
.filter_map(|o| o.parse().ok())
.collect();
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any);
.allow_origin(origins)
.allow_methods(tower_http::cors::Any)
.allow_headers(tower_http::cors::Any);
let app = create_router(state)
.layer(cors)

View File

@@ -1,4 +1,5 @@
use axum::{middleware, routing, Router};
use axum::{middleware, routing, Json, Router};
use serde_json::json;
use crate::auth::middleware::auth_middleware;
use crate::state::AppState;
@@ -36,9 +37,30 @@ pub fn create_router(state: AppState) -> Router {
// WebSocket endpoint validates JWT via query param (no middleware layer)
let ws_route = Router::new().route("/ws", routing::get(ws_handler));
// Health/readiness probes (public, no auth)
let health = Router::new()
.route("/health", routing::get(health_check))
.route("/ready", routing::get(readiness_check));
Router::new()
.nest("/api", rest_api)
.nest("/api", public_api)
.nest("/api", ws_route)
.merge(health)
.with_state(state)
}
async fn health_check() -> Json<serde_json::Value> {
Json(json!({ "status": "ok" }))
}
async fn readiness_check(
axum::extract::State(state): axum::extract::State<AppState>,
) -> Json<serde_json::Value> {
Json(json!({
"status": "ok",
"trading_service": state.trading_channel.is_some(),
"backtesting_service": state.backtesting_channel.is_some(),
"ml_training_service": state.ml_training_channel.is_some(),
}))
}