Skip to main content

this_env/middleware/actix/
service.rs

1//this.env/crate/src/middleware/actix/service.rs
2// by suiGn
3use std::rc::Rc;
4use std::task::{Context, Poll};
5use actix_service::Service;
6use actix_web::dev::{ServiceRequest, ServiceResponse};
7use actix_web::Error;
8use actix_web::body::{BoxBody, EitherBody};
9use futures_util::future::LocalBoxFuture;
10use crate::middleware::actix::ActixMwConfig;
11use serde_json::json;
12use crate::middleware::actix::env_request_parser::parse_env_request;
13use crate::middleware::env_request::EnvRequest;
14use actix_web::{HttpResponse, http::StatusCode};
15use actix_web::HttpMessage;
16/// The middleware service implementation that intercepts the request,
17/// builds an `EnvRequest`, and invokes the handler pipeline.
18pub struct ActixMiddlewareService<S> {
19    pub(crate) service: Rc<S>,
20    pub(crate) config: ActixMwConfig,
21    pub(crate) conn: std::sync::Arc<rusqlite::Connection>,
22}
23
24impl<S, B> Service<ServiceRequest> for ActixMiddlewareService<S>
25where
26    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
27    S::Future: 'static,
28    B: 'static,
29{
30    type Response = ServiceResponse<EitherBody<B, BoxBody>>;
31    type Error = Error;
32    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
33    fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
34        self.service.poll_ready(ctx)
35    }
36
37    fn call(&self, req: ServiceRequest) -> Self::Future {
38        use crate::middleware::actix::mw_router::intercept_internal_routes;
39        use crate::env::{Env, EnvStatus};
40        let port = req.connection_info().host().split(':').nth(1).unwrap_or("unknown").to_string();
41        if self.config.manual_mode {
42            log::info!("this.env [{}] ActixMiddleware (manual): {} {}", port, req.method(), req.path());
43        } else {
44            log::info!("this.env [{}] ActixMiddleware: {} {}", port, req.method(), req.path());
45        }
46
47        let conn = self.conn.clone();
48        req.extensions_mut().insert(conn.clone());
49
50        if let Some(resp) = intercept_internal_routes(&req) {
51            return Box::pin(async {
52                Ok(req.into_response(resp.map_into_right_body()))
53            });
54        }
55
56        let svc = Rc::clone(&self.service);
57        let config = self.config.clone();
58        let env_request_result = parse_env_request(req.request());
59        let accepts_html = req
60            .headers()
61            .get("Accept")
62            .and_then(|v| v.to_str().ok())
63            .map(|s| s.contains("text/html"))
64            .unwrap_or(false);
65
66        if config.manual_mode {
67            if let Some(env_request) = &env_request_result {
68                match Env::resolve(env_request, &conn) {
69                    status => log::info!("this.env status (manual mode): {:?}", status),
70                }
71            }
72            return Box::pin(async move {
73                let res = svc.call(req).await?;
74                Ok(res.map_into_left_body())
75            });
76        }
77
78        let decision_status = match &env_request_result {
79            Some(env_request) => {
80                if let Some(EnvRequest::Http(http)) = &env_request_result {
81                    log::debug!("this.env request: [{}] {} {}", http.host, http.method, http.path);
82                }
83                // Always approve for now, bypassing pending and blocked logic
84                EnvStatus::Approved {
85                    env_request: crate::middleware::env_request::EnvRequestInfo::from(env_request),
86                }
87            }
88            None => {
89                // If no env_request, still return Approved with a dummy request
90                EnvStatus::Approved {
91                    env_request: crate::middleware::env_request::EnvRequestInfo::from(
92                        &EnvRequest::Cli(Default::default())
93                    ),
94                }
95            }
96        };
97
98    log::info!("this.env decision: {:?}", match &decision_status {
99        EnvStatus::Approved { .. } => "Approved",
100        EnvStatus::PendingApproval { .. } => "PendingApproval",
101        EnvStatus::Blocked { .. } => "Blocked",
102    });
103        let svc_clone = Rc::clone(&svc);
104        let req_clone = req;
105        // let req = &env_req.req; // (line to delete)
106        Box::pin(async move {
107            if let Some(_env_request) = env_request_result {
108                match decision_status {
109/* ▗▄▖ ▗▄▄▖ ▗▄▄▖ ▗▄▄▖  ▗▄▖ ▗▖  ▗▖▗▄▄▄▖▗▄▄▄ 
110  ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌  ▐▌▐▌   ▐▌  █
111  ▐▛▀▜▌▐▛▀▘ ▐▛▀▘ ▐▛▀▚▖▐▌ ▐▌▐▌  ▐▌▐▛▀▀▘▐▌  █
112  ▐▌ ▐▌▐▌   ▐▌   ▐▌ ▐▌▝▚▄▞▘ ▝▚▞▘ ▐▙▄▄▖▐▙▄▄*/
113                    EnvStatus::Approved { env_request: _ } => {
114                        // Always insert EnvStatus for handlers to read
115                        req_clone.extensions_mut().insert(decision_status.clone());
116                        let res = svc_clone.call(req_clone).await?;
117                        return Ok(res.map_into_left_body());
118                    }
119/*▗▄▄▖ ▗▄▄▄▖▗▖  ▗▖▗▄▄▄ ▗▄▄▄▖▗▖  ▗▖ ▗▄▄▖
120  ▐▌ ▐▌▐▌   ▐▛▚▖▐▌▐▌  █  █  ▐▛▚▖▐▌▐▌   
121  ▐▛▀▘ ▐▛▀▀▘▐▌ ▝▜▌▐▌  █  █  ▐▌ ▝▜▌▐▌▝▜▌
122  ▐▌   ▐▙▄▄▖▐▌  ▐▌▐▙▄▄▀▗▄█▄▖▐▌  ▐▌▝▚▄▞▘*/
123                    EnvStatus::PendingApproval { env_request: env_req, reason: _ } => {
124                        // Use EnvRequestInfo::from(env_req) for serialization.
125                        let info = crate::middleware::env_request::EnvRequestInfo::from(env_req);
126                        let json_body = json!({
127                            "status": "pending",
128                            "message": "Awaiting user approval",
129                            "env_request": info
130                        }).to_string();
131                        let resp = HttpResponse::build(StatusCode::UNAUTHORIZED)
132                            .content_type("application/json")
133                            .body(json_body);
134                        return Ok(req_clone.into_response(resp.map_into_right_body()));
135                    }
136/*▗▄▄▖ ▗▖    ▗▄▖  ▗▄▄▖▗▖ ▗▖▗▄▄▄▖▗▄▄▄ 
137  ▐▌ ▐▌▐▌   ▐▌ ▐▌▐▌   ▐▌▗▞▘▐▌   ▐▌  █
138  ▐▛▀▚▖▐▌   ▐▌ ▐▌▐▌   ▐▛▚▖ ▐▛▀▀▘▐▌  █
139  ▐▙▄▞▘▐▙▄▄▖▝▚▄▞▘▝▚▄▄▖▐▌ ▐▌▐▙▄▄▖▐▙▄▄▀*/ 
140                    EnvStatus::Blocked { env_request: _, reason: _ } => {
141                        let wants_html = accepts_html || config.prefer_html;
142                        if wants_html {
143                            let resp = HttpResponse::build(StatusCode::FORBIDDEN)
144                                .content_type("text/html")
145                                .body(include_str!("../../html/blocked.html"));
146                            return Ok(req_clone.into_response(resp.map_into_right_body()));
147                        } else {
148                            let resp = HttpResponse::build(StatusCode::FORBIDDEN)
149                                .content_type("text/plain")
150                                .body("Blocked");
151                            return Ok(req_clone.into_response(resp.map_into_right_body()));
152                        }
153                    }
154                }
155            }
156            // For cases without env_request, still insert Approved
157            req_clone.extensions_mut().insert(decision_status.clone());
158            let res = svc_clone.call(req_clone).await?;
159            Ok(res.map_into_left_body())
160        })
161    }
162}