mod template; mod route; use std::sync::Arc; use crate::app::State; use warp::{Filter, Rejection, Reply}; use std::convert::Infallible; use warp::http::StatusCode; use std::borrow::Cow; use self::route::*; pub async fn start_web(state: Arc) { let cookie_state = Arc::clone(&state); let authed = warp::cookie("access_token") .or(warp::header("x-api-key")) .unify() .and_then(move |access_token: String| { let state = Arc::clone(&cookie_state); async move { if access_token == state.user_config.bot.access_token { Ok(()) } else { Err(warp::reject::custom(CustomRejection::InvalidAccessToken)) } } }) .untuple_one() .and( commands_routes(Arc::clone(&state)) .or(redemptions_routes(Arc::clone(&state))) .or(livesplit_routes(Arc::clone(&state))) ); let unauthed = access_token_routes(); let routes = authed.or(unauthed).recover(handle_rejection); warp::serve(routes) .run(([0, 0, 0, 0], 8000)) .await; } #[derive(Debug)] enum CustomRejection { InvalidAccessToken, InvalidForm, TwitchError, } impl warp::reject::Reject for CustomRejection {} async fn handle_rejection(err: Rejection) -> Result { let (code, message) = if err.is_not_found() { (StatusCode::NOT_FOUND, Cow::from("404 - Not Found")) } else if let Some(custom) = err.find::() { match custom { CustomRejection::InvalidAccessToken => (StatusCode::UNAUTHORIZED, Cow::from("invalid access token")), CustomRejection::InvalidForm => (StatusCode::BAD_REQUEST, Cow::from("invalid form submission")), CustomRejection::TwitchError => (StatusCode::INTERNAL_SERVER_ERROR, Cow::from("twitch error")), // CustomRejection::Askama(e) => (StatusCode::INTERNAL_SERVER_ERROR, Cow::from(format!("templating error: {:#?}", e))), } } else if let Some(e) = err.find::() { (StatusCode::BAD_REQUEST, Cow::from(format!("missing `{}` cookie", e.name()))) } else if let Some(e) = err.find::() { (StatusCode::BAD_REQUEST, Cow::from(format!("missing `{}` header", e.name()))) } else { (StatusCode::INTERNAL_SERVER_ERROR, Cow::from(format!("unhandled error: {:#?}", err))) }; Ok(warp::reply::with_status(message, code)) }