use axum::{ extract::State, middleware, response::{IntoResponse, Redirect}, routing::{get, post}, Router, }; use axum_extra::extract::CookieJar; use crate::api::{auth, bots, events, mode, positions}; use crate::AppState; pub fn build(state: AppState) -> Router { let protected_api = Router::new() .route("/symbols", get(bots::list_symbols)) .route("/bots", get(bots::list_bots).post(bots::create_bot)) .route("/bots/:id", get(bots::get_bot).delete(bots::delete_bot).put(bots::update_bot)) .route("/bots/:id/start", post(bots::start_bot)) .route("/bots/:id/stop", post(bots::stop_bot)) .route("/bots/:id/logs", get(bots::get_bot_logs)) .route("/bots/:id/positions", get(bots::get_bot_positions)) .route("/bots/:id/positions/closed", get(bots::get_bot_closed)) .route("/bots/:id/log-stream", get(events::bot_log_sse)) .route("/positions", get(positions::open_positions)) .route("/positions/closed", get(positions::closed_positions)) .route("/mode", get(mode::get_mode).post(mode::set_mode)) .route("/events", get(events::sse_handler)) .layer(middleware::from_fn_with_state(state.clone(), auth::require_auth)); let api = Router::new() .route("/auth/login", post(auth::login)) .route("/auth/logout", post(auth::logout)) .merge(protected_api); Router::new() .nest("/api", api) .route("/", get(login_handler)) .route("/dashboard", get(dashboard_handler)) .route("/bots", get(bots_handler)) .route("/bots/:id", get(bot_detail_handler)) .route("/positions", get(positions_handler)) .with_state(state) } async fn login_handler( State(state): State, jar: CookieJar, ) -> impl IntoResponse { let token = jar.get("mse_session").map(|c| c.value().to_string()); let authed = match token { Some(t) => { let db = state.db.lock().await; db.session_exists(&t).unwrap_or(false) } None => false, }; if authed { Redirect::to("/dashboard").into_response() } else { axum::response::Html(include_str!("../web/login.html")).into_response() } } async fn bots_handler( State(state): State, jar: CookieJar, ) -> impl IntoResponse { let token = jar.get("mse_session").map(|c| c.value().to_string()); let authed = match token { Some(t) => { let db = state.db.lock().await; db.session_exists(&t).unwrap_or(false) } None => false, }; if authed { axum::response::Html(include_str!("../web/bots.html")).into_response() } else { Redirect::to("/").into_response() } } async fn positions_handler( State(state): State, jar: CookieJar, ) -> impl IntoResponse { let token = jar.get("mse_session").map(|c| c.value().to_string()); let authed = match token { Some(t) => { let db = state.db.lock().await; db.session_exists(&t).unwrap_or(false) } None => false, }; if authed { axum::response::Html(include_str!("../web/positions.html")).into_response() } else { Redirect::to("/").into_response() } } async fn bot_detail_handler( State(state): State, jar: CookieJar, ) -> impl IntoResponse { let token = jar.get("mse_session").map(|c| c.value().to_string()); let authed = match token { Some(t) => { let db = state.db.lock().await; db.session_exists(&t).unwrap_or(false) } None => false, }; if authed { axum::response::Html(include_str!("../web/bot.html")).into_response() } else { Redirect::to("/").into_response() } } async fn dashboard_handler( State(state): State, jar: CookieJar, ) -> impl IntoResponse { let token = jar.get("mse_session").map(|c| c.value().to_string()); let authed = match token { Some(t) => { let db = state.db.lock().await; db.session_exists(&t).unwrap_or(false) } None => false, }; if authed { axum::response::Html(include_str!("../web/index.html")).into_response() } else { Redirect::to("/").into_response() } }