devimint/
faucet.rs
1use std::str::FromStr;
2
3use axum::Router;
4use axum::extract::State;
5use axum::http::StatusCode;
6use axum::routing::{get, post};
7use fedimint_gateway_common::V1_API_ENDPOINT;
8use fedimint_ln_server::common::lightning_invoice::Bolt11Invoice;
9use tokio::net::TcpListener;
10use tower_http::cors::CorsLayer;
11
12use crate::federation::Federation;
13use crate::{DevFed, Gatewayd};
14
15#[derive(Clone)]
16pub struct Faucet {
17 gw_ldk: Gatewayd,
18 fed: Federation,
19}
20
21impl Faucet {
22 pub fn new(dev_fed: &DevFed) -> Self {
23 let gw_ldk = dev_fed.gw_ldk.clone();
24 let fed = dev_fed.fed.clone();
25 Faucet { gw_ldk, fed }
26 }
27
28 async fn pay_invoice(&self, invoice: String) -> anyhow::Result<()> {
29 self.gw_ldk
30 .pay_invoice(Bolt11Invoice::from_str(&invoice).expect("Could not parse invoice"))
31 .await?;
32 Ok(())
33 }
34
35 async fn generate_invoice(&self, amount: u64) -> anyhow::Result<String> {
36 Ok(self.gw_ldk.create_invoice(amount).await?.to_string())
37 }
38
39 fn get_invite_code(&self) -> anyhow::Result<String> {
40 self.fed.invite_code()
41 }
42}
43
44pub async fn run(
45 dev_fed: &DevFed,
46 fauct_bind_addr: String,
47 gw_lnd_port: u16,
48) -> anyhow::Result<()> {
49 let faucet = Faucet::new(dev_fed);
50 let router = Router::new()
51 .route(
52 "/connect-string",
53 get(|State(faucet): State<Faucet>| async move {
54 faucet
55 .get_invite_code()
56 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")))
57 }),
58 )
59 .route(
60 "/pay",
61 post(|State(faucet): State<Faucet>, invoice: String| async move {
62 faucet
63 .pay_invoice(invoice)
64 .await
65 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")))
66 }),
67 )
68 .route(
69 "/invoice",
70 post(|State(faucet): State<Faucet>, amt: String| async move {
71 let amt = amt
72 .parse::<u64>()
73 .map_err(|e| (StatusCode::BAD_REQUEST, format!("{e:?}")))?;
74 faucet
75 .generate_invoice(amt)
76 .await
77 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")))
78 }),
79 )
80 .route(
81 "/gateway-api",
82 get(move || async move { format!("http://127.0.0.1:{gw_lnd_port}/{V1_API_ENDPOINT}") }),
83 )
84 .layer(CorsLayer::permissive())
85 .with_state(faucet);
86
87 let listener = TcpListener::bind(fauct_bind_addr).await?;
88 axum::serve(listener, router.into_make_service()).await?;
89 Ok(())
90}