fedimint_server_ui/dashboard/
mod.rs
1pub mod audit;
2pub mod bitcoin;
3pub(crate) mod consensus_explorer;
4pub mod general;
5pub mod invite;
6pub mod latency;
7pub mod modules;
8
9use axum::Router;
10use axum::extract::{Form, State};
11use axum::response::{Html, IntoResponse};
12use axum::routing::{get, post};
13use axum_extra::extract::cookie::CookieJar;
14use consensus_explorer::consensus_explorer_view;
15use fedimint_server_core::dashboard_ui::{DashboardApiModuleExt, DynDashboardApi};
16use maud::{DOCTYPE, Markup, html};
17use {fedimint_lnv2_server, fedimint_meta_server, fedimint_wallet_server};
18
19use crate::assets::WithStaticRoutesExt as _;
20use crate::auth::UserAuth;
21use crate::dashboard::modules::{lnv2, meta, wallet};
22use crate::{
23 EXPLORER_IDX_ROUTE, EXPLORER_ROUTE, LOGIN_ROUTE, LoginInput, ROOT_ROUTE, UiState, common_head,
24 login_form_response, login_submit_response,
25};
26
27pub fn dashboard_layout(content: Markup) -> Markup {
28 html! {
29 (DOCTYPE)
30 html {
31 head {
32 (common_head("Dashboard"))
33 }
34 body {
35 div class="container" {
36 header class="text-center" {
37 h1 class="header-title" { "Fedimint Guardian UI" }
38 }
39
40 (content)
41 }
42 script src="/assets/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous" {}
43 }
44 }
45 }
46}
47
48async fn login_form(State(_state): State<UiState<DynDashboardApi>>) -> impl IntoResponse {
50 login_form_response()
51}
52
53async fn login_submit(
55 State(state): State<UiState<DynDashboardApi>>,
56 jar: CookieJar,
57 Form(input): Form<LoginInput>,
58) -> impl IntoResponse {
59 login_submit_response(
60 state.api.auth().await,
61 state.auth_cookie_name,
62 state.auth_cookie_value,
63 jar,
64 input,
65 )
66 .into_response()
67}
68
69async fn dashboard_view(
71 State(state): State<UiState<DynDashboardApi>>,
72 _auth: UserAuth,
73) -> impl IntoResponse {
74 let guardian_names = state.api.guardian_names().await;
75 let federation_name = state.api.federation_name().await;
76 let session_count = state.api.session_count().await;
77 let consensus_ord_latency = state.api.consensus_ord_latency().await;
78 let p2p_connection_status = state.api.p2p_connection_status().await;
79 let invite_code = state.api.federation_invite_code().await;
80 let audit_summary = state.api.federation_audit().await;
81 let bitcoin_rpc_url = state.api.bitcoin_rpc_url().await;
82 let bitcoin_rpc_status = state.api.bitcoin_rpc_status().await;
83
84 let content = html! {
85 div class="row gy-4" {
86 div class="col-md-6" {
87 (general::render(&federation_name, session_count, &guardian_names))
88 }
89
90 div class="col-md-6" {
91 (invite::render(&invite_code))
92 }
93 }
94
95 div class="row gy-4 mt-2" {
96 div class="col-lg-6" {
97 (audit::render(&audit_summary))
98 }
99
100 div class="col-lg-6" {
101 (latency::render(consensus_ord_latency, &p2p_connection_status))
102 }
103 }
104
105 div class="row gy-4 mt-2" {
106 div class="col-12" {
107 (bitcoin::render(bitcoin_rpc_url, &bitcoin_rpc_status))
108 }
109 }
110
111 @if let Some(lightning) = state.api.get_module::<fedimint_lnv2_server::Lightning>() {
113 div class="row gy-4 mt-2" {
114 div class="col-12" {
115 (lnv2::render(lightning).await)
116 }
117 }
118 }
119
120 @if let Some(wallet_module) = state.api.get_module::<fedimint_wallet_server::Wallet>() {
122 div class="row gy-4 mt-2" {
123 div class="col-12" {
124 (wallet::render(wallet_module).await)
125 }
126 }
127 }
128
129 @if let Some(meta_module) = state.api.get_module::<fedimint_meta_server::Meta>() {
131 div class="row gy-4 mt-2" {
132 div class="col-12" {
133 (meta::render(meta_module).await)
134 }
135 }
136 }
137 };
138
139 Html(dashboard_layout(content).into_string()).into_response()
140}
141
142pub fn router(api: DynDashboardApi) -> Router {
143 let mut app = Router::new()
144 .route(ROOT_ROUTE, get(dashboard_view))
145 .route(LOGIN_ROUTE, get(login_form).post(login_submit))
146 .route(EXPLORER_ROUTE, get(consensus_explorer_view))
147 .route(EXPLORER_IDX_ROUTE, get(consensus_explorer_view))
148 .with_static_routes();
149
150 if api
152 .get_module::<fedimint_lnv2_server::Lightning>()
153 .is_some()
154 {
155 app = app
156 .route(lnv2::LNV2_ADD_ROUTE, post(lnv2::post_add))
157 .route(lnv2::LNV2_REMOVE_ROUTE, post(lnv2::post_remove));
158 }
159
160 if api.get_module::<fedimint_meta_server::Meta>().is_some() {
162 app = app
163 .route(meta::META_SUBMIT_ROUTE, post(meta::post_submit))
164 .route(meta::META_SET_ROUTE, post(meta::post_set))
165 .route(meta::META_RESET_ROUTE, post(meta::post_reset))
166 .route(meta::META_DELETE_ROUTE, post(meta::post_delete));
167 }
168
169 app.with_state(UiState::new(api))
171}