diff --git a/reject_filters/.gitignore b/reject_filters/.gitignore new file mode 100644 index 0000000..c3a29c2 --- /dev/null +++ b/reject_filters/.gitignore @@ -0,0 +1,10 @@ +/target +target +Cargo.lock +.cache +.temp +.env +*.log +.DS_Store +logs +tmp diff --git a/reject_filters/Cargo.toml b/reject_filters/Cargo.toml new file mode 100644 index 0000000..41981cd --- /dev/null +++ b/reject_filters/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "reject_filters" +version = "0.1.0" +authors = ["JesusPerez "] +edition = "2018" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_derive = "1.0.125" +warp = { version = "0.3", features = ["default","websocket","tls","compression"] } +app_env = { version = "0.1.0", path = "../../defs/app_env" } \ No newline at end of file diff --git a/reject_filters/README.md b/reject_filters/README.md new file mode 100644 index 0000000..7845b6f --- /dev/null +++ b/reject_filters/README.md @@ -0,0 +1,5 @@ +# App reject filters library + +Filters as configure routes and webservices for [warp](https://github.com/seanmonstar/warp) + +As default fallback for **warp routes** diff --git a/reject_filters/TODO.md b/reject_filters/TODO.md new file mode 100644 index 0000000..6819106 --- /dev/null +++ b/reject_filters/TODO.md @@ -0,0 +1,5 @@ +# App reject filters library + +- [ ] Conditional **boxed** with cargo build for production + +[Warp and compile times](https://rust-classes.com/chapter_6_3.html) diff --git a/reject_filters/src/lib.rs b/reject_filters/src/lib.rs new file mode 100644 index 0000000..f014424 --- /dev/null +++ b/reject_filters/src/lib.rs @@ -0,0 +1,201 @@ + + +use serde_derive::{Serialize}; +//use std::convert::Infallible; +use std::error::Error; + +use warp::{ + http::{StatusCode}, +// http::{method::Method, HeaderMap, HeaderValue}, +// http::{HeaderMap, HeaderValue}, +// filters::header::headers_cloned, +// filters::method::method, +// filters::BoxedFilter, + Filter, + Rejection, Reply, +}; + +use app_env::AppStore; + +/// An API error serializable to JSON. +#[derive(Serialize)] +struct ErrorMessage { + code: u16, + message: String, +} + +/* +#[must_use] +#[allow(clippy::missing_errors_doc)] +pub fn static_filter( + static_path: &String +) -> BoxedFilter<(impl warp::Reply,)> { + warp::fs::dir(&static_path).with(warp::compression::gzip()) + .boxed() +} +*/ +/* +#[allow(clippy::missing_errors_doc)] +pub fn rejection( + err: Rejection, + db: AppStore, +) -> impl Filter + Clone { + headers_cloned() + .and(method()) + .and(handle_rejection) + .and(with_db(db)) + .and_then(handle_rejection_filter) +} +*/ +// This function receives a `Rejection` and tries to return a custom +// value, otherwise simply passes the rejection along. +#[allow(clippy::missing_errors_doc)] +pub async fn handle_rejection_filter( + reply: impl warp::Reply, + // full_path: warp::path::FullPath, + // header: HeaderMap, + db: AppStore, +) -> Result { +// dbg!(&db); + // dbg!(&header); + // dbg!(&full_path); + let response = reply.into_response(); + let status_code = &response.status(); + //if let Some(message) = response.headers().get("x-message") { + let message = match &response.headers().get("x-message") { + Some(msg) => format!("{:?}",&msg), + //warp::http::HeaderValue::from("NOT_FOUND") => println!("No encontrado"), + None => String::from("Unknown"), + }; + let result =format!("StatusCode: {} -> Message: {}", &status_code, &message); + + let app = db.app_data.read(); + let fallback = |_: &str| { + Ok(warp::http::Response::builder() + .body(result.to_string()) + .into_response()) + // Ok(warp::reply::with_header( + // warp::http::Response::new(result), + // "Access-Control-Allow-Origin", + // "http://localhost:3333")) + }; + let tera = app.tera.to_owned(); + let mut context = app.ctx.to_owned(); + context.insert("status_code", &status_code.to_string()); + let template = match status_code.as_str() { + "404" => "error404.html", + _ => "error.html", + }; + match tera.render(&template, &context) { + Ok(page) => + Ok(warp::http::Response::builder() + .body(page.to_string()) + .into_response()), + // Ok(warp::reply::with_header( + // warp::http::Response::new(page), + // "Access-Control-Allow-Origin", + // "http://localhost:3333")), + Err(_) => fallback("Page not found"), + } +} + +#[allow(clippy::missing_errors_doc)] +pub async fn handle_rejection( + err: Rejection, +// header: HeaderMap, + db: AppStore, + // full_path: warp::path::FullPath, +) -> Result { +// ) -> Result { + // dbg!(&header); + // dbg!(&err); + // dbg!(&full_path); + let code; + let message; + + + if err.is_not_found() { + code = StatusCode::NOT_FOUND; + message = "NOT_FOUND"; + } else if let Some(e) = err.find::() { + // This error happens if the body could not be deserialized correctly + // We can use the cause to analyze the error and customize the error message + message = match e.source() { + Some(cause) => { + if cause.to_string().contains("denom") { + "FIELD_ERROR: denom" + } else { + "BAD_REQUEST" + } + } + None => "BAD_REQUEST", + }; + code = StatusCode::BAD_REQUEST; + } else if let Some(_) = err.find::() { + // We can handle a specific error, here METHOD_NOT_ALLOWED, + // and render it however we want + code = StatusCode::METHOD_NOT_ALLOWED; + message = "METHOD_NOT_ALLOWED"; + } else { + // We should have expected this... Just log and say its a 500 + eprintln!("unhandled rejection: {:?}", err); + code = StatusCode::INTERNAL_SERVER_ERROR; + message = "UNHANDLED_REJECTION"; + } + + // let json = warp::reply::json(&ErrorMessage { + // code: code.as_u16(), + // message: message.into(), + // }); + + let result =format!("StatusCode: {} -> Message: {}", &code, &message); + + let app = db.app_data.read(); + // let fallback = |e: &str| { + // warp::reply::with_header( + // warp::http::Response::new(result), + // "Access-Control-Allow-Origin", + // "http://localhost:3333") + // }; + let tera = app.tera.to_owned(); + let mut context = app.ctx.to_owned(); + context.insert("status_code", &code.to_string()); + let template = match code.as_str() { + "404" => "error404.html", + _ => "error.html", + }; + let reply = match tera.render(&template, &context) { + Ok(page) => + warp::http::Response::builder() + .status(code) + .header("X-Message", message) + .body(page) + .into_response(), +/* + Ok(warp::reply::with_header( + warp::http::Response::new(page), + "Access-Control-Allow-Origin", + "http://localhost:3333")), +*/ + Err(_) => + warp::http::Response::builder() + .status(code) + .header("X-Message", message) + .body(result) + .into_response(), + }; + Ok(warp::reply::with_status(reply,code)) +/* + let res = warp::http::Response::builder() + .status(code) + .header("X-Message", message) + .body(message.to_owned()) + .into_response(); + Ok(warp::reply::with_status(res, code)) +*/ +} + +#[must_use] +pub fn with_app_db(db: AppStore) -> impl Filter + Clone { + warp::any().map(move || db.clone()) +} \ No newline at end of file