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 serde_json::json; 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(state: web::Data>) -> HttpResponse { let nodes = state.get_nodes().await; HttpResponse::Ok() .json(nodes.into_iter().map(Into::::into).collect::>()) } #[derive(Deserialize)] struct MintReq { address: String, } #[post("/mint")] async fn mint( state: web::Data>, sol_client: web::Data>, req: web::Query, ) -> impl Responder { let recipient = req.into_inner().address; state.increase_mint_requests().await; if state.is_minting() { return HttpResponse::TooManyRequests().json(json!({ "error": "already mint processing" })); } let mint_res = sol_client.mint(&recipient).await; state.release_minting(); match mint_res { Ok(s) => { state.increase_mints().await; HttpResponse::Ok().json(json!({"signature": s})) } Err(e) => HttpResponse::InternalServerError().json(json!({ "error": e.to_string() })), } } #[get("/metrics")] async fn metrics(state: web::Data>) -> HttpResponse { let mut metrics = String::new(); for (ip, node) in state.get_nodes().await { 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(); }