zero4rs/utils/
mod.rs

1use crate::prelude2::*;
2
3use std::time::Duration;
4
5use actix_web::dev;
6use actix_web::http::header::LOCATION;
7
8use actix_extensible_rate_limit::backend::memory::InMemoryBackend;
9use actix_extensible_rate_limit::backend::SimpleInput;
10use actix_extensible_rate_limit::backend::SimpleInputFunctionBuilder;
11use actix_extensible_rate_limit::backend::SimpleOutput;
12use actix_extensible_rate_limit::RateLimiter;
13
14// Return an opaque 400 while preserving the error's root cause for logging.
15pub fn e400<T>(e: T) -> actix_web::Error
16where
17    T: std::fmt::Debug + std::fmt::Display + 'static,
18{
19    log::error!("e400: error={:?}", e);
20    actix_web::error::ErrorBadRequest(e)
21}
22
23// Return an opaque 500 while preserving the error's root cause for logging.
24pub fn e500<T>(e: T) -> actix_web::Error
25where
26    T: std::fmt::Debug + std::fmt::Display + 'static,
27{
28    log::error!("e500: error={:?}", e);
29    actix_web::error::ErrorInternalServerError(e)
30}
31
32pub fn see_other(location: &str) -> HttpResponse {
33    HttpResponse::SeeOther()
34        .insert_header((LOCATION, location))
35        .finish()
36}
37
38pub fn found(location: &str) -> HttpResponse {
39    HttpResponse::Found()
40        .insert_header((LOCATION, location))
41        .finish()
42}
43
44pub fn send_redirect(location: &str) -> HttpResponse {
45    HttpResponse::Found()
46        .insert_header((LOCATION, location))
47        .finish()
48}
49
50pub fn send_forward(location: &str) -> HttpResponse {
51    HttpResponse::SeeOther()
52        .insert_header((LOCATION, location))
53        .finish()
54}
55
56pub fn normalize_path_config() -> actix_web::middleware::NormalizePath {
57    actix_web::middleware::NormalizePath::new(actix_web::middleware::TrailingSlash::Trim)
58}
59
60#[rustfmt::skip]
61pub fn cors() -> actix_cors::Cors {
62
63    let h1 = actix_web::http::header::HeaderName::from_lowercase(b"csrf-token").unwrap();
64    let h3 = actix_web::http::header::HeaderName::from_lowercase(b"x-csrf-token").unwrap();
65    let h4 = actix_web::http::header::HeaderName::from_lowercase(b"cookie").unwrap();
66    let h5 = actix_web::http::header::HeaderName::from_lowercase(b"x-requested-with").unwrap();
67    let h6 = actix_web::http::header::HeaderName::from_lowercase(b"etag").unwrap();
68
69    actix_cors::Cors // ..
70        // ::permissive()
71        ::default()
72        // .allowed_origin("https://www.rust-lang.org")
73        .allow_any_origin()
74        // .allowed_origin_fn(|origin, _req_head| origin.as_bytes().ends_with(b".rust-lang.org"))
75        .allowed_methods(vec!["GET", "POST", "DELETE", "PUT"])
76        .allowed_headers(vec![
77            actix_web::http::header::CONTENT_TYPE,
78            actix_web::http::header::AUTHORIZATION,
79            actix_web::http::header::ACCEPT,
80            actix_web::http::header::COOKIE,
81            h1.clone(),
82            h3.clone(),
83        ])
84        // Access-Control-Expose-Headers: x-csrf-token, csrf-token
85        .expose_headers(vec![h1.clone(), h3.clone(), h4.clone(), h5.clone(), h6.clone()])
86        .supports_credentials() // Access-Control-Allow-Credentials: true
87        .max_age(3600)
88}
89
90#[rustfmt::skip]
91pub fn default_headers() -> actix_web::middleware::DefaultHeaders {
92
93    let mut default_headers = actix_web::middleware::DefaultHeaders::new().add(("X-Version", "1.0"));
94
95    // armor::armor(&mut secure_headers);
96    default_headers = default_headers.add(("X-DNS-Prefetch-Control", "on"));
97    default_headers = default_headers.add(("X-Content-Type-Options", "nosniff"));
98    default_headers = default_headers.add(("X-Frame-Options", "sameorigin"));
99    default_headers = default_headers.add(("Strict-Transport-Security", "max-age=5184000"));
100    default_headers = default_headers.add(("X-XSS-Protection", "1; mode=block"));
101    // default_headers = default_headers.add(("Connection", "keep-alive"));
102    // default_headers = default_headers.add(("Keep-Alive", "timeout=60, max=100"));
103    // default_headers = default_headers.remove("X-Powered-By");
104
105    default_headers
106}
107
108#[rustfmt::skip]
109pub fn access_limiter() -> RateLimiter<
110    InMemoryBackend,
111    SimpleOutput,
112    impl Fn(&dev::ServiceRequest) -> std::future::Ready<std::result::Result<SimpleInput, actix_web::Error>>,
113> {
114    RateLimiter::builder(
115        InMemoryBackend::builder().build(),
116        SimpleInputFunctionBuilder::new(Duration::from_secs(1), 100)
117            .real_ip_key()
118            .build(),
119    )
120    .add_headers()
121    .build()
122}
123
124// QueryString 参数验证
125pub fn query_error_handler() -> actix_web::web::QueryConfig {
126    actix_web::web::QueryConfig::default().error_handler(|err, _| {
127        let r = R::failed(4137, err.to_string());
128        let r = actix_web::HttpResponse::Ok().json(r);
129        actix_web::error::InternalError::from_response(err, r).into()
130    })
131}
132
133// Customize your error in the handler
134pub fn path_variable_error_handler() -> actix_web::web::PathConfig {
135    actix_web::web::PathConfig::default().error_handler(|err, _| {
136        let r = R::failed(4134, err.to_string());
137        let r = actix_web::HttpResponse::Ok().json(r);
138        actix_web::error::InternalError::from_response(err, r).into()
139    })
140}
141
142pub fn post_json_error_handler() -> actix_web::web::JsonConfig {
143    actix_web::web::JsonConfig::default()
144        .limit(2_097_152 * 100) // 200m
145        .error_handler(|err, _| {
146            let r = R::failed(4133, err.to_string());
147            let r = actix_web::HttpResponse::Ok().json(r);
148            actix_web::error::InternalError::from_response(err, r).into()
149        })
150}
151
152pub fn post_form_error_handler() -> actix_web::web::FormConfig {
153    actix_web::web::FormConfig::default().error_handler(|err, _| {
154        let r = R::failed(4132, err.to_string());
155        let r = actix_web::HttpResponse::Ok().json(r);
156        actix_web::error::InternalError::from_response(err, r).into()
157    })
158}
159
160pub fn multipart_form_error_handler() -> actix_multipart::form::MultipartFormConfig {
161    actix_multipart::form::MultipartFormConfig::default().error_handler(|err, _| {
162        let r = R::failed(4131, err.to_string());
163        let r = actix_web::HttpResponse::Ok().json(r);
164        actix_web::error::InternalError::from_response(err, r).into()
165    })
166}
167
168pub fn get_header_value(request: &HttpRequest, key: &str) -> String {
169    match request.headers().get(key) {
170        Some(val) => val.to_str().unwrap_or_default().to_string(),
171        _ => "".to_string(),
172    }
173}