Skip to main content

flams_router_dashboard/
settings.rs

1use flams_backend_types::ManagerCacheSize;
2use flams_router_base::{maybe_lazy, require_login};
3use flams_utils::settings::SettingsSpec;
4use flams_web_utils::components::wait_and_then_fn;
5use ftml_dom::utils::css::inject_css;
6use leptos::prelude::*;
7
8#[server(
9  prefix="/api",
10  endpoint="settings",
11  output=server_fn::codec::Json
12)]
13#[allow(clippy::unused_async)]
14pub async fn get_settings() -> Result<(SettingsSpec, bool), ServerFnError<String>> {
15    use flams_router_base::LoginState;
16    use flams_system::settings::Settings;
17    match LoginState::get_server() {
18        LoginState::Admin | LoginState::NoAccounts | LoginState::User { is_admin: true, .. } => {
19            let mut spec = Settings::get().as_spec();
20            if let Some(pw) = spec.server.admin_pwd.as_mut() {
21                *pw = "********".to_string();
22            }
23            if let Some(tk) = spec.gitlab.token.as_mut() {
24                *tk = "********".to_string().into_boxed_str();
25            }
26            if let Some(secret) = spec.gitlab.app_secret.as_mut() {
27                *secret = "********".to_string().into_boxed_str();
28            }
29            Ok((spec, flams_git::gl::GLInstance::global().has_loaded()))
30        }
31        _ => Err("Not logged in".to_string().into()),
32    }
33}
34
35#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
36pub struct Memory {
37    uris: ftml_uris::MemoryState,
38    terms: ftml_ontology::terms::TermCacheSize,
39    backend: ManagerCacheSize,
40    search: (usize, usize),
41}
42
43#[server(
44  prefix="/api",
45  endpoint="memory",
46  output=server_fn::codec::Json
47)]
48#[allow(clippy::unused_async)]
49pub async fn get_memory() -> Result<Memory, ServerFnError<String>> {
50    use flams_math_archives::backend::GlobalBackend;
51    use flams_router_base::LoginState;
52    match LoginState::get_server() {
53        LoginState::Admin | LoginState::NoAccounts | LoginState::User { is_admin: true, .. } => {
54            tokio::task::spawn_blocking(|| {
55                let backend = GlobalBackend.memory();
56                let uris = ftml_uris::get_memory_state();
57                let terms = ftml_ontology::terms::get_cache_size();
58                Ok(Memory {
59                    backend,
60                    uris,
61                    terms,
62                    search: flams_search::Searcher::get().size(),
63                })
64            })
65            .await
66            .map_err(|e| e.to_string())?
67        }
68        _ => Err("Not logged in".to_string().into()),
69    }
70}
71
72#[server(
73  prefix="/api",
74  endpoint="reload",
75  output=server_fn::codec::Json
76)]
77pub async fn reload() -> Result<(), ServerFnError<String>> {
78    use flams_math_archives::backend::GlobalBackend;
79    use flams_router_base::LoginState;
80    match LoginState::get_server() {
81        LoginState::Admin | LoginState::NoAccounts | LoginState::User { is_admin: true, .. } => {
82            let _ = tokio::task::spawn_blocking(move || {
83                GlobalBackend.reset::<flams_system::TokioEngine>();
84                ftml_uris::clear_memory();
85                ftml_ontology::terms::clear_term_cache();
86            })
87            .await;
88            let _ = tokio::task::spawn_blocking(|| {
89                for e in flams_system::iter::<flams_system::FlamsExtension>() {
90                    (e.on_reload)();
91                }
92            });
93            Ok(())
94        }
95        _ => Err("Not logged in".to_string().into()),
96    }
97}
98
99maybe_lazy!(Settings = settings());
100
101//#[component]
102fn settings() -> AnyView {
103    use ftml_component_utils::{Table, TableCell, TableRow};
104    require_login(Box::new(|| {
105        wait_and_then_fn(
106            || async {
107                Ok::<_, ServerFnError<String>>((get_settings().await?, get_memory().await?))
108            },
109            |((settings, gl), mem)| {
110                let loading = RwSignal::new(false);
111                let reload_act = flams_web_utils::components::message_action(
112                    move |()| {
113                        loading.set(true);
114                        reload()
115                    },
116                    move |()| {
117                        loading.set(false);
118                        "success".to_string()
119                    },
120                );
121                let r = view!(
122                  <Table class="flams-settings-table">
123                    <TableRow><TableCell><h2>"Status"</h2></TableCell><td/></TableRow>
124                    {do_memory(mem)}
125                    <TableRow>
126                        <TableCell>""</TableCell>
127                        <TableCell>{move || if loading.get() {
128                          leptos::either::Either::Left(view!(<ftml_component_utils::Spinner/>))
129                        } else {
130                          leptos::either::Either::Right(view!(<button on:click=move |_| {reload_act.dispatch(());}>"Reload"</button>))
131                        }
132                      }</TableCell>
133                    </TableRow>
134                    <TableRow><TableCell><h2>"Settings"</h2></TableCell><td/></TableRow>
135                    <TableRow><TableCell><h3>"General"</h3></TableCell><td/></TableRow>
136                    <TableRow>
137                          <TableCell class="flams-settings-col"><b>"MathHub"</b></TableCell>
138                          <TableCell class="flams-settings-col">{settings.mathhubs.into_iter().map(|m| m.display().to_string() + " ").collect::<Vec<_>>()}</TableCell>
139                    </TableRow>
140                    <TableRow>
141                          <TableCell class="flams-settings-col"><b>"Debug Mode"</b></TableCell>
142                          <TableCell class="flams-settings-col">{settings.debug}</TableCell>
143                    </TableRow>
144                    <TableRow>
145                          <TableCell class="flams-settings-col"><b>"Log Directory"</b></TableCell>
146                          <TableCell class="flams-settings-col">{settings.log_dir.unwrap_or_else(|| unreachable!()).display().to_string()}</TableCell>
147                    </TableRow>
148                    <TableRow>
149                          <TableCell class="flams-settings-col"><b>"Database Path"</b></TableCell>
150                          <TableCell class="flams-settings-col">{settings.database.unwrap_or_else(|| unreachable!()).display().to_string()}</TableCell>
151                    </TableRow>
152                    <TableRow>
153                          <TableCell class="flams-settings-col"><b>"Temp Directory"</b></TableCell>
154                          <TableCell class="flams-settings-col">{settings.temp_dir.unwrap_or_else(|| unreachable!()).display().to_string()}</TableCell>
155                    </TableRow>
156                    <TableRow>
157                          <TableCell class="flams-settings-col"><b>"Stack Size"</b></TableCell>
158                          <TableCell class="flams-settings-col">{settings.stack_size}{if settings.stack_size.is_some() {"MB"} else {"(System default)"}}</TableCell>
159                    </TableRow>
160                    <TableRow><TableCell><h3>"Server"</h3></TableCell><td/></TableRow>
161                    <TableRow>
162                          <TableCell class="flams-settings-col"><b>"IP/Port"</b></TableCell>
163                          <TableCell class="flams-settings-col">{settings.server.ip.unwrap_or_else(|| unreachable!())}":"{settings.server.port}</TableCell>
164                    </TableRow>
165                    <TableRow>
166                          <TableCell class="flams-settings-col"><b>"Gitlab URL"</b></TableCell>
167                          <TableCell class="flams-settings-col">{settings.gitlab.url.map_or_else(|| leptos::either::Either::Left("(None)".to_string()),|s|
168                            leptos::either::Either::Right(view!({s.to_string()}{
169                              if gl {
170                                leptos::either::Either::Left(view!(" "<div style="color:green;display:inline;"><ftml_component_utils::icons::CheckmarkIcon/></div>))
171                              } else {
172                                leptos::either::Either::Right(view!(" "<div style="color:red;display:inline;"><ftml_component_utils::icons::XMarkIcon/></div>))
173                              }
174                            })
175                          ))}</TableCell>
176                    </TableRow>
177                    <TableRow><TableCell><h3>"Build Queue"</h3></TableCell><td/></TableRow>
178                    <TableRow>
179                          <TableCell class="flams-settings-col"><b>"Threads:"</b></TableCell>
180                          <TableCell class="flams-settings-col">{settings.buildqueue.num_threads}</TableCell>
181                    </TableRow>
182                    </Table>
183                ).into_any();
184                inject_css("flams-settings", include_str!("settings.css"));
185                r
186            },
187        )
188    }))
189}
190
191fn do_memory(mem: Memory) -> impl IntoView {
192    let total = mem.terms.total_bytes() + mem.uris.total_bytes() + mem.backend.total_bytes();
193
194    let total = total + mem.search.1;
195    macro_rules! disp {
196        ($name:literal = $num:expr;$bytes:expr) => {
197            view!(<tr>
198                <td class="flams-settings-col">{$name}</td>
199                <td class="flams-settings-col">{$num}" ("{disp!($bytes)}")"</td>
200            </tr>)
201        };
202        ($val:expr) => {
203            bytesize::ByteSize::b($val as u64)
204                .display()
205                .iec_short()
206                .to_string()
207        };
208    }
209
210    let search = disp!("Search Index" = mem.search.0;mem.search.1);
211    view! {
212        <tr>
213            <td class="flams-settings-col"><b>"Relations"</b></td>
214            <td class="flams-settings-col">{mem.backend.relations}</td>
215        </tr>
216        {search}
217        <tr><td/><td/></tr>
218        <tr><td><b>"Backend"</b></td></tr>
219            {disp!("Modules" = mem.backend.num_modules;mem.backend.modules_bytes)}
220            {disp!("Documents" = mem.backend.num_documents;mem.backend.documents_bytes)}
221            <tr>
222                <td class="flams-settings-col">"Total"</td>
223                <td class="flams-settings-col">{disp!(mem.backend.total_bytes())}</td>
224            </tr>
225        <tr><td><b>"URIs"</b></td></tr>
226            {disp!("Base URIs" = mem.uris.num_base_uris;mem.uris.base_uris_bytes)}
227            {disp!("IDs" = mem.uris.num_ids;mem.uris.ids_bytes)}
228            {disp!("Archive IDs" = mem.uris.num_archives;mem.uris.archives_bytes)}
229            {disp!("URI names" = mem.uris.num_uri_names;mem.uris.uri_names_bytes)}
230            {disp!("URI paths" = mem.uris.num_uri_paths;mem.uris.uri_paths_bytes)}
231            <tr>
232                <td class="flams-settings-col">"Total"</td>
233                <td class="flams-settings-col">{disp!(mem.uris.total_bytes())}</td>
234            </tr>
235        <tr><td><b>"Terms"</b></td></tr>
236            {disp!("Applications" = mem.terms.num_applications;mem.terms.applications_bytes)}
237            {disp!("Bindings" = mem.terms.num_bindings;mem.terms.bindings_bytes)}
238            {disp!("Records" = mem.terms.num_records;mem.terms.records_bytes)}
239            {disp!("Opaques" = mem.terms.num_opaques;mem.terms.opaques_bytes)}
240            <tr>
241                <td class="flams-settings-col">"Total"</td>
242                <td class="flams-settings-col">{disp!(mem.terms.total_bytes())}</td>
243            </tr>
244
245
246        <tr><td/><td/></tr>
247        <tr>
248            <td class="flams-settings-col"><b>"Total"</b></td>
249            <td class="flams-settings-col">{disp!(total)}</td>
250        </tr>
251    }
252}