use crate::{datastore, datastore::State, solana::SolClient}; use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::sync::Arc; const HOMEPAGE: &str = include_str!("HOMEPAGE.md"); #[get("/")] async fn homepage(sol_client: web::Data>) -> impl Responder { let text = HOMEPAGE .to_string() .replace("TOKEN_ADDRESS", &sol_client.get_token_address()) .replace("MINT_AUTHORITY", &sol_client.get_wallet_pubkey()); HttpResponse::Ok().body(text) } #[derive(Serialize)] pub struct NodesResp { pub ip: String, pub joined_at: String, pub last_keepalive: String, pub mint_requests: u64, pub mints: u64, pub ratls_attacks: u64, pub total_ratls_conns: u64, pub public: bool, } impl From<(String, datastore::NodeInfo)> for NodesResp { fn from((ip, node_info): (String, datastore::NodeInfo)) -> Self { let joined_at: DateTime = node_info.started_at.into(); let last_keepalive: DateTime = node_info.keepalive.into(); let joined_at = joined_at.format("%Y-%m-%d %H:%M:%S").to_string(); let last_keepalive = last_keepalive.format("%Y-%m-%d %H:%M:%S").to_string(); NodesResp { ip, joined_at, last_keepalive, mints: node_info.mints, total_ratls_conns: node_info.mratls_conns, ratls_attacks: node_info.net_attacks, public: node_info.public, mint_requests: node_info.mint_requests, } } } #[get("/nodes")] async fn get_nodes(ds: web::Data>) -> HttpResponse { HttpResponse::Ok() .json(ds.get_nodes().into_iter().map(Into::::into).collect::>()) } #[derive(Deserialize)] struct MintReq { wallet: String, } #[post("/mint")] async fn mint( state: web::Data>, sol_client: web::Data>, req: web::Json, ) -> impl Responder { let recipient = req.into_inner().wallet; state.increase_mint_requests(); let result = web::block(move || sol_client.mint(&recipient).map_err(|e| e.to_string())).await.unwrap(); // TODO: check if this can get a BlockingError match result { Ok(s) => { state.increase_mints(); HttpResponse::Ok().body(format!(r#"{{" signature": "{s} "}}"#)) } Err(e) => HttpResponse::InternalServerError().body(format!(r#"{{ "error": "{e}" }}"#)), } } #[get("/metrics")] async fn metrics(ds: web::Data>) -> HttpResponse { let mut metrics = String::new(); for (ip, node) in ds.get_nodes() { metrics.push_str(node.to_metrics(&ip).as_str()); } HttpResponse::Ok().content_type("text/plain; version=0.0.4; charset=utf-8").body(metrics) } pub async fn http_new_server(state: Arc, sol_client: Arc) { HttpServer::new(move || { App::new() .app_data(web::Data::new(state.clone())) .app_data(web::Data::new(sol_client.clone())) .service(homepage) .service(get_nodes) .service(mint) .service(metrics) }) .bind("0.0.0.0:31372") .unwrap() .run() .await .unwrap(); }