fedimint_server_ui/dashboard/modules/
lnv2.rs

1use axum::extract::{Form, State};
2use axum::response::{IntoResponse, Redirect};
3use fedimint_core::util::SafeUrl;
4use fedimint_server_core::dashboard_ui::{DashboardApiModuleExt, DynDashboardApi};
5use fedimint_ui_common::auth::UserAuth;
6use fedimint_ui_common::{ROOT_ROUTE, UiState};
7use maud::{Markup, html};
8
9// LNv2 route constants
10pub const LNV2_ADD_ROUTE: &str = "/lnv2/add";
11pub const LNV2_REMOVE_ROUTE: &str = "/lnv2/remove";
12
13// Form for gateway management
14#[derive(serde::Deserialize)]
15pub struct GatewayForm {
16    pub gateway_url: SafeUrl,
17}
18
19// Function to render the Lightning V2 module UI section
20pub async fn render(lightning: &fedimint_lnv2_server::Lightning) -> Markup {
21    let gateways = lightning.gateways_ui().await;
22    let consensus_block_count = lightning.consensus_block_count_ui().await;
23    let consensus_unix_time = lightning.consensus_unix_time_ui().await;
24    let formatted_unix_time = chrono::DateTime::from_timestamp(consensus_unix_time as i64, 0)
25        .map(|dt| dt.to_rfc2822())
26        .unwrap_or("Invalid time".to_string());
27
28    html! {
29        div class="card h-100" {
30            div class="card-header dashboard-header" { "Lightning V2" }
31            div class="card-body" {
32                // Consensus status information
33                div class="mb-4" {
34                    table
35                        class="table"
36                        id="lnv2-module-timers" hx-swap-oob=(true)
37                    {
38                        tr {
39                            th { "Consensus Block Count" }
40                            td { (consensus_block_count) }
41                        }
42                        tr {
43                            th { "Consensus Unix Time" }
44                            td { (formatted_unix_time) }
45                        }
46                    }
47                }
48
49                // Gateway management
50                div {
51                    div class="row" {
52                        // Left tile - Gateway list or message
53                        div class="col-lg-6 pe-lg-4 position-relative" {
54                            div class="h-100" {
55                                @if gateways.is_empty() {
56                                    div class="text-center p-4" {
57                                        p { "You need a Lightning gateway to connect to your federation and then add its URL here in the dashboard to enable V2 Lightning payments for your users. You can either run your own gateway or reach out to the Fedimint team on " a href="https://chat.fedimint.org/" { "Discord" } " - we are running our own gateway and are happy to get you started." }
58                                    }
59                                } @else {
60                                    div class="table-responsive" {
61                                        table class="table table-hover" {
62                                            tbody {
63                                                @for gateway in &gateways {
64                                                    tr {
65                                                        td { (gateway.to_string()) }
66                                                        td class="text-end" {
67                                                            form action=(LNV2_REMOVE_ROUTE) method="post" style="display: inline;" {
68                                                                input type="hidden" name="gateway_url" value=(gateway.to_string());
69                                                                button type="submit" class="btn btn-sm btn-danger" {
70                                                                    "Remove"
71                                                                }
72                                                            }
73                                                        }
74                                                    }
75                                                }
76                                            }
77                                        }
78                                    }
79                                }
80                            }
81                            // Add vertical divider
82                            div class="position-absolute end-0 top-0 bottom-0 d-none d-lg-block" style="width: 1px; background-color: #dee2e6;" {}
83                        }
84
85                        // Right tile - Add gateway form
86                        div class="col-lg-6 ps-lg-4" {
87                            div class="d-flex flex-column align-items-center h-100" {
88                                form action=(LNV2_ADD_ROUTE) method="post" class="w-100" style="max-width: 400px;" {
89                                    div class="mb-3" {
90                                        input
91                                            type="url"
92                                            class="form-control"
93                                            id="gateway-url"
94                                            name="gateway_url"
95                                            placeholder="Enter gateway URL"
96                                            required;
97                                    }
98                                    div class="text-muted mb-3 text-center" style="font-size: 0.875em;" {
99                                        "Please enter a valid URL starting with http:// or https://"
100                                    }
101                                    div class="text-center" {
102                                        button type="submit" class="btn btn-primary" style="min-width: 150px;" {
103                                            "Add Gateway"
104                                        }
105                                    }
106                                }
107                            }
108                        }
109                    }
110                }
111            }
112        }
113    }
114}
115
116// Handler for adding a new gateway
117pub async fn post_add(
118    State(state): State<UiState<DynDashboardApi>>,
119    _auth: UserAuth,
120    Form(form): Form<GatewayForm>,
121) -> impl IntoResponse {
122    state
123        .api
124        .get_module::<fedimint_lnv2_server::Lightning>()
125        .expect("Route only mounted when Lightning V2 module exists")
126        .add_gateway_ui(form.gateway_url)
127        .await;
128
129    Redirect::to(ROOT_ROUTE).into_response()
130}
131
132// Handler for removing a gateway
133pub async fn post_remove(
134    State(state): State<UiState<DynDashboardApi>>,
135    _auth: UserAuth,
136    Form(form): Form<GatewayForm>,
137) -> impl IntoResponse {
138    state
139        .api
140        .get_module::<fedimint_lnv2_server::Lightning>()
141        .expect("Route only mounted when Lightning V2 module exists")
142        .remove_gateway_ui(form.gateway_url)
143        .await;
144
145    Redirect::to(ROOT_ROUTE).into_response()
146}