fedimint_server_ui/
lnv2.rs
1use axum::extract::{Form, State};
2use axum::response::{IntoResponse, Redirect};
3use axum_extra::extract::cookie::CookieJar;
4use fedimint_core::util::SafeUrl;
5use fedimint_server_core::dashboard_ui::{DashboardApiModuleExt, DynDashboardApi};
6use maud::{Markup, html};
7
8use crate::{AuthState, check_auth};
9
10#[derive(serde::Deserialize)]
12pub struct GatewayForm {
13 pub gateway_url: SafeUrl,
14}
15
16pub async fn render(lightning: &fedimint_lnv2_server::Lightning) -> Markup {
18 let gateways = lightning.gateways_ui().await;
19 let consensus_block_count = lightning.consensus_block_count_ui().await;
20 let consensus_unix_time = lightning.consensus_unix_time_ui().await;
21 let formatted_unix_time = chrono::DateTime::from_timestamp(consensus_unix_time as i64, 0)
22 .map(|dt| dt.to_rfc2822())
23 .unwrap_or("Invalid time".to_string());
24
25 html! {
26 div class="row gy-4 mt-2" {
27 div class="col-12" {
28 div class="card h-100" {
29 div class="card-header dashboard-header" { "Lightning V2" }
30 div class="card-body" {
31 div class="mb-4" {
33 table
34 class="table"
35 id="lnv2-module-timers" hx-swap-oob=(true)
36 {
37 tr {
38 th { "Consensus Block Count" }
39 td { (consensus_block_count) }
40 }
41 tr {
42 th { "Consensus Unix Time" }
43 td { (formatted_unix_time) }
44 }
45 }
46 }
47
48 div {
50 h5 { "Gateway Management" }
51 div class="mb-4" {
53 form action="/lnv2_gateway_add" method="post" class="row g-3" {
54 div class="col-md-9" {
55 div class="form-group" {
56 div class="text-muted mb-1" style="font-size: 0.875em;" {
57 "Please enter a valid URL starting with http:// or https://"
58 }
59 input
60 type="url"
61 class="form-control"
62 id="gateway-url"
63 name="gateway_url"
64 placeholder="Enter gateway URL"
65 required;
66 }
67 }
68 div class="col-md-3" {
69 div style="margin-top: 24px;" {
70 button type="submit" class="btn btn-primary w-100 form-control" { "Add Gateway" }
71 }
72 }
73 }
74 }
75
76 @if gateways.is_empty() {
78 div class="alert alert-info" { "No gateways configured yet." }
79 } @else {
80 div class="table-responsive" {
81 table class="table table-hover" {
82 thead {
83 tr {
84 th { "Gateway URL" }
85 th class="text-end" { "Actions" }
86 }
87 }
88 tbody {
89 @for gateway in &gateways {
90 tr {
91 td {
92 a href=(gateway.to_string()) target="_blank" { (gateway.to_string()) }
93 }
94 td class="text-end" {
95 form action="/lnv2_gateway_remove" method="post" style="display: inline;" {
96 input type="hidden" name="gateway_url" value=(gateway.to_string());
97 button type="submit" class="btn btn-sm btn-danger" {
98 "Remove"
99 }
100 }
101 }
102 }
103 }
104 }
105 }
106 }
107 }
108 }
109 }
110 }
111 }
112 }
113 }
114}
115
116pub async fn add_gateway(
118 State(state): State<AuthState<DynDashboardApi>>,
119 jar: CookieJar,
120 Form(form): Form<GatewayForm>,
121) -> impl IntoResponse {
122 if !check_auth(&state.auth_cookie_name, &state.auth_cookie_value, &jar).await {
123 return Redirect::to("/login").into_response();
124 }
125
126 state
127 .api
128 .get_module::<fedimint_lnv2_server::Lightning>()
129 .expect("Route only mounted when Lightning V2 module exists")
130 .add_gateway_ui(form.gateway_url)
131 .await;
132
133 Redirect::to("/").into_response()
134}
135
136pub async fn remove_gateway(
138 State(state): State<AuthState<DynDashboardApi>>,
139 jar: CookieJar,
140 Form(form): Form<GatewayForm>,
141) -> impl IntoResponse {
142 if !check_auth(&state.auth_cookie_name, &state.auth_cookie_value, &jar).await {
143 return Redirect::to("/login").into_response();
144 }
145
146 state
147 .api
148 .get_module::<fedimint_lnv2_server::Lightning>()
149 .expect("Route only mounted when Lightning V2 module exists")
150 .remove_gateway_ui(form.gateway_url)
151 .await;
152
153 Redirect::to("/").into_response()
154}