resymo_agent/uplink/
http_server.rs1use crate::{common::http, manager::Manager};
2use actix_web::{get, middleware::Logger, web, App, HttpResponse, Responder};
3use actix_web_extras::middleware::Condition;
4use actix_web_httpauth::{
5 extractors::{bearer, AuthenticationError},
6 middleware::HttpAuthentication,
7};
8use anyhow::bail;
9use std::{
10 net::{IpAddr, Ipv6Addr},
11 sync::Arc,
12};
13
14const DEFAULT_BIND_PORT: u16 = 4242;
15const DEFAULT_BIND_HOST: IpAddr = IpAddr::V6(Ipv6Addr::LOCALHOST);
16
17#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
18#[serde(rename_all = "camelCase")]
19pub struct Options {
20 #[serde(default, skip_serializing_if = "Option::is_none")]
22 token: Option<String>,
23
24 #[serde(default)]
26 disable_authentication: bool,
27
28 #[serde(flatten)]
29 http: http::Options,
30}
31
32#[get("/")]
33async fn index() -> impl Responder {
34 ""
35}
36
37#[get("/api/v1/collect")]
38async fn collect_all(manager: web::Data<Manager>) -> actix_web::Result<HttpResponse> {
39 Ok(HttpResponse::Ok().json(manager.collect_all().await?))
40}
41
42#[get("/api/v1/collect/{collector}")]
43async fn collect(
44 path: web::Path<String>,
45 manager: web::Data<Manager>,
46) -> actix_web::Result<HttpResponse> {
47 let collector = path.into_inner();
48
49 log::info!("Collecting: {collector}");
50
51 Ok(match manager.collect_one(&collector).await? {
52 Some(result) => HttpResponse::Ok().json(result),
53 None => HttpResponse::NotFound().finish(),
54 })
55}
56
57pub async fn run(options: Options, manager: Arc<Manager>) -> anyhow::Result<()> {
58 let manager = web::Data::from(manager);
59
60 let auth = options.token.map(|token| {
61 let token = Arc::new(token);
62 HttpAuthentication::bearer(move |req, credentials| {
63 let token = token.clone();
64 async move {
65 if credentials.token() == *token {
66 Ok(req)
67 } else {
68 let config = req
69 .app_data::<bearer::Config>()
70 .cloned()
71 .unwrap_or_default()
72 .scope("api");
73
74 Err((AuthenticationError::from(config).into(), req))
75 }
76 }
77 })
78 });
79
80 if auth.is_none() {
81 if options.disable_authentication {
82 log::warn!("Running without access token. This is discouraged as it may compromise your system.");
83 } else {
84 bail!("Running without access token. This is discouraged as it may compromise your system. If you really want to do it, use --disable-authentication");
85 }
86 }
87
88 http::run_server(
89 options.http,
90 http::Defaults {
91 port: DEFAULT_BIND_PORT,
92 host: DEFAULT_BIND_HOST,
93 },
94 move || {
95 App::new()
96 .app_data(manager.clone())
97 .wrap(Condition::from_option(auth.clone()))
98 .wrap(Logger::default())
99 .service(index)
100 .service(collect)
101 .service(collect_all)
102 },
103 )
104 .await?;
105
106 Ok(())
107}