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