flams_router_dashboard/
settings.rs1use flams_backend_types::ManagerCacheSize;
2use flams_router_base::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}
41
42#[server(
43 prefix="/api",
44 endpoint="memory",
45 output=server_fn::codec::Json
46)]
47#[allow(clippy::unused_async)]
48pub async fn get_memory() -> Result<Memory, ServerFnError<String>> {
49 use flams_math_archives::backend::GlobalBackend;
50 use flams_router_base::LoginState;
51 match LoginState::get_server() {
52 LoginState::Admin | LoginState::NoAccounts | LoginState::User { is_admin: true, .. } => {
53 tokio::task::spawn_blocking(|| {
54 let backend = GlobalBackend.memory();
55 let uris = ftml_uris::get_memory_state();
56 let terms = ftml_ontology::terms::get_cache_size();
57 Ok(Memory {
58 backend,
59 uris,
60 terms,
61 })
62 })
63 .await
64 .map_err(|e| e.to_string())?
65 }
66 _ => Err("Not logged in".to_string().into()),
67 }
68}
69
70#[server(
71 prefix="/api",
72 endpoint="reload",
73 output=server_fn::codec::Json
74)]
75pub async fn reload() -> Result<(), ServerFnError<String>> {
76 use flams_math_archives::backend::GlobalBackend;
77 use flams_router_base::LoginState;
78 match LoginState::get_server() {
79 LoginState::Admin | LoginState::NoAccounts | LoginState::User { is_admin: true, .. } => {
80 let _ = tokio::task::spawn_blocking(move || {
81 GlobalBackend.reset::<flams_system::TokioEngine>();
82 ftml_uris::clear_memory();
83 ftml_ontology::terms::clear_term_cache();
84 })
85 .await;
86 Ok(())
87 }
88 _ => Err("Not logged in".to_string().into()),
89 }
90}
91
92#[component]
93pub(super) fn Settings() -> AnyView {
94 use thaw::Table;
95 inject_css("flams-settings", include_str!("settings.css"));
96 require_login(Box::new(|| {
97 wait_and_then_fn(
98 || async {
99 Ok::<_, ServerFnError<String>>((get_settings().await?, get_memory().await?))
100 },
101 |((settings, gl), mem)| {
102 let loading = RwSignal::new(false);
103 let reload_act = flams_web_utils::components::message_action(
104 move |()| {
105 loading.set(true);
106 reload()
107 },
108 move |()| {
109 loading.set(false);
110 "success".to_string()
111 },
112 );
113 view!(
114 <Table class="flams-settings-table"><thead/><tbody>
115 <tr><td><h2>"Status"</h2></td><td/></tr>
116 {do_memory(mem)}
117 <tr>
118 <td></td>
119 <td>{move || if loading.get() {
120 leptos::either::Either::Left(view!(<flams_web_utils::components::Spinner/>))
121 } else {
122 leptos::either::Either::Right(view!(<button on:click=move |_| {reload_act.dispatch(());}>"Reload"</button>))
123 }
124 }</td>
125 </tr>
126 <tr><td><h2>"Settings"</h2></td><td/></tr>
127 <tr><td><h3>"General"</h3></td><td/></tr>
128 <tr>
129 <td class="flams-settings-col"><b>"MathHub"</b></td>
130 <td class="flams-settings-col">{settings.mathhubs.into_iter().map(|m| m.display().to_string() + " ").collect::<Vec<_>>()}</td>
131 </tr>
132 <tr>
133 <td class="flams-settings-col"><b>"Debug Mode"</b></td>
134 <td class="flams-settings-col">{settings.debug}</td>
135 </tr>
136 <tr>
137 <td class="flams-settings-col"><b>"Log Directory"</b></td>
138 <td class="flams-settings-col">{settings.log_dir.unwrap_or_else(|| unreachable!()).display().to_string()}</td>
139 </tr>
140 <tr>
141 <td class="flams-settings-col"><b>"Database Path"</b></td>
142 <td class="flams-settings-col">{settings.database.unwrap_or_else(|| unreachable!()).display().to_string()}</td>
143 </tr>
144 <tr>
145 <td class="flams-settings-col"><b>"Temp Directory"</b></td>
146 <td class="flams-settings-col">{settings.temp_dir.unwrap_or_else(|| unreachable!()).display().to_string()}</td>
147 </tr>
148 <tr>
149 <td class="flams-settings-col"><b>"Stack Size"</b></td>
150 <td class="flams-settings-col">{(settings.stack_size)}{if settings.stack_size.is_some() {"MB"} else {"(System default)"}}</td>
151 </tr>
152 <tr><td><h3>"Server"</h3></td><td/></tr>
153 <tr>
154 <td class="flams-settings-col"><b>"IP/Port"</b></td>
155 <td class="flams-settings-col">{settings.server.ip.unwrap_or_else(|| unreachable!())}":"{settings.server.port}</td>
156 </tr>
157 <tr>
158 <td class="flams-settings-col"><b>"Gitlab URL"</b></td>
159 <td class="flams-settings-col">{settings.gitlab.url.map_or_else(|| leptos::either::Either::Left("(None)".to_string()),|s|
160 leptos::either::Either::Right(view!({s.to_string()}{
161 if gl {
162 leptos::either::Either::Left(view!(" "<div style="color:green;display:inline;"><thaw::Icon icon=icondata_ai::AiCheckOutlined/></div>))
163 } else {
164 leptos::either::Either::Right(view!(" "<div style="color:red;display:inline;"><thaw::Icon icon=icondata_ai::AiCloseOutlined/></div>))
165 }
166 })
167 ))}</td>
168 </tr>
169 <tr><td><h3>"Build Queue"</h3></td><td/></tr>
170 <tr>
171 <td class="flams-settings-col"><b>"Threads:"</b></td>
172 <td class="flams-settings-col">{settings.buildqueue.num_threads}</td>
173 </tr>
174 </tbody></Table>
175 ).into_any()
176 },
177 )
178 }))
179}
180
181fn do_memory(mem: Memory) -> impl IntoView {
182 let total = mem.terms.total_bytes() + mem.uris.total_bytes() + mem.backend.total_bytes();
183 macro_rules! disp {
184 ($name:literal = $num:expr;$bytes:expr) => {
185 view!(<tr>
186 <td class="flams-settings-col">{$name}</td>
187 <td class="flams-settings-col">{$num}" ("{disp!($bytes)}")"</td>
188 </tr>)
189 };
190 ($val:expr) => {
191 bytesize::ByteSize::b($val as u64)
192 .display()
193 .iec_short()
194 .to_string()
195 };
196 }
197 view! {
198 <tr>
199 <td class="flams-settings-col"><b>"Relations"</b></td>
200 <td class="flams-settings-col">{mem.backend.relations}</td>
201 </tr>
202 <tr><td/><td/></tr>
203 <tr><td><b>"Backend"</b></td></tr>
204 {disp!("Modules" = mem.backend.num_modules;mem.backend.modules_bytes)}
205 {disp!("Documents" = mem.backend.num_documents;mem.backend.documents_bytes)}
206 <tr>
207 <td class="flams-settings-col">"Total"</td>
208 <td class="flams-settings-col">{disp!(mem.backend.total_bytes())}</td>
209 </tr>
210 <tr><td><b>"URIs"</b></td></tr>
211 {disp!("Base URIs" = mem.uris.num_base_uris;mem.uris.base_uris_bytes)}
212 {disp!("IDs" = mem.uris.num_ids;mem.uris.ids_bytes)}
213 {disp!("Archive IDs" = mem.uris.num_archives;mem.uris.archives_bytes)}
214 {disp!("URI names" = mem.uris.num_uri_names;mem.uris.uri_names_bytes)}
215 {disp!("URI paths" = mem.uris.num_uri_paths;mem.uris.uri_paths_bytes)}
216 <tr>
217 <td class="flams-settings-col">"Total"</td>
218 <td class="flams-settings-col">{disp!(mem.uris.total_bytes())}</td>
219 </tr>
220 <tr><td><b>"Terms"</b></td></tr>
221 {disp!("Applications" = mem.terms.num_applications;mem.terms.applications_bytes)}
222 {disp!("Bindings" = mem.terms.num_bindings;mem.terms.bindings_bytes)}
223 {disp!("Records" = mem.terms.num_records;mem.terms.records_bytes)}
224 {disp!("Opaques" = mem.terms.num_opaques;mem.terms.opaques_bytes)}
225 <tr>
226 <td class="flams-settings-col">"Total"</td>
227 <td class="flams-settings-col">{disp!(mem.terms.total_bytes())}</td>
228 </tr>
229
230
231 <tr><td/><td/></tr>
232 <tr>
233 <td class="flams-settings-col"><b>"Total"</b></td>
234 <td class="flams-settings-col">{disp!(total)}</td>
235 </tr>
236 }
237}