1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
mod endpoints;

use hyper::body::Buf;

pub struct WebService {
    backend: tokio::sync::mpsc::Sender<cargolifter_core::BackendCommand>,
    storage: tokio::sync::mpsc::Sender<cargolifter_core::StorageCommand>,
    config: cargolifter_core::config::WebServiceConfig,
}

pub struct RequestExtractor(cargolifter_core::models::PublishRequest);

#[axum::async_trait]
impl axum::extract::FromRequest for RequestExtractor {
    type Rejection = axum::http::StatusCode;

    async fn from_request(req: &mut axum::extract::RequestParts) -> Result<Self, Self::Rejection> {
        let mut data = hyper::body::to_bytes(req.body_mut().unwrap())
            .await
            .unwrap();
        let json_length = data.get_u32_le() as usize;
        let json_data = &data[0..json_length].to_vec();
        data.advance(json_length);
        let data_length = data.get_u32_le() as usize;

        Ok(Self {
            0: cargolifter_core::models::PublishRequest {
                meta: serde_json::from_slice(json_data).unwrap(),
                data: data[0..data_length].to_vec(),
            },
        })
    }
}

impl WebService {
    pub fn new(
        backend: tokio::sync::mpsc::Sender<cargolifter_core::BackendCommand>,
        storage: tokio::sync::mpsc::Sender<cargolifter_core::StorageCommand>,
        config: cargolifter_core::config::WebServiceConfig,
    ) -> Self {
        Self {
            backend,
            storage,
            config,
        }
    }

    pub async fn run(&self) {
        let host = format!("0.0.0.0:{}", self.config.port); // TODO: confiure port
        tracing::info!("starting web service at: {}", host);

        let app = axum::Router::new()
            .route(
                "/api/v1/crates/:crate_name/:crate_version/download",
                axum::handler::get(endpoints::download),
            )
            .route("/api/v1/crates/new", axum::handler::put(endpoints::publish))
            .route(
                "/api/v1/crates/:name/:version/yank",
                axum::handler::delete(endpoints::yank),
            )
            .route(
                "/api/v1/crates/:name/:version/unyank",
                axum::handler::put(endpoints::unyank),
            )
            .layer(axum::AddExtensionLayer::new(self.backend.clone()))
            .layer(axum::AddExtensionLayer::new(self.storage.clone()));

        axum::Server::bind(&host.parse().unwrap())
            .serve(app.into_make_service())
            .await
            .unwrap();
    }
}