tuwunel_admin/server/
commands.rs1use std::{fmt::Write, path::PathBuf, sync::Arc};
2
3use futures::TryStreamExt;
4use tuwunel_core::{
5 Err, Result, info,
6 utils::{stream::IterStream, time},
7 warn,
8};
9
10use crate::admin_command;
11
12#[admin_command]
13pub(super) async fn uptime(&self) -> Result {
14 let elapsed = self
15 .services
16 .server
17 .started
18 .elapsed()
19 .expect("standard duration");
20
21 let result = time::pretty(elapsed);
22 self.write_str(&format!("{result}.")).await
23}
24
25#[admin_command]
26pub(super) async fn show_config(&self) -> Result {
27 self.write_str(&format!("{}", *self.services.server.config))
28 .await
29}
30
31#[admin_command]
32pub(super) async fn reload_config(&self, path: Option<PathBuf>) -> Result {
33 let path = path.as_deref().into_iter();
34 self.services.config.reload(path)?;
35
36 self.write_str("Successfully reconfigured.").await
37}
38
39#[admin_command]
40pub(super) async fn list_features(&self, available: bool, enabled: bool, comma: bool) -> Result {
41 let delim = if comma { "," } else { " " };
42 if enabled && !available {
43 let features = info::rustc::features().join(delim);
44 let out = format!("`\n{features}\n`");
45 return self.write_str(&out).await;
46 }
47
48 if available && !enabled {
49 let features = info::cargo::features().join(delim);
50 let out = format!("`\n{features}\n`");
51 return self.write_str(&out).await;
52 }
53
54 let mut features = String::new();
55 let enabled = info::rustc::features();
56 let available = info::cargo::features();
57 for feature in available {
58 let active = enabled.contains(&feature.as_str());
59 let emoji = if active { "✅" } else { "❌" };
60 let remark = if active { "[enabled]" } else { "" };
61 writeln!(features, "{emoji} {feature} {remark}")?;
62 }
63
64 self.write_str(&features).await
65}
66
67#[admin_command]
68pub(super) async fn memory_usage(&self) -> Result {
69 let services_usage = self.services.memory_usage().await?;
70 let database_usage = self.services.db.engine.memory_usage()?;
71 let allocator_usage = tuwunel_core::alloc::memory_usage()
72 .map_or(String::new(), |s| format!("\nAllocator:\n{s}"));
73
74 self.write_str(&format!(
75 "Services:\n{services_usage}\nDatabase:\n{database_usage}{allocator_usage}",
76 ))
77 .await
78}
79
80#[admin_command]
81pub(super) async fn clear_caches(&self) -> Result {
82 self.services.clear_cache().await;
83
84 self.write_str("Done.").await
85}
86
87#[admin_command]
88pub(super) async fn list_backups(&self) -> Result {
89 self.services
90 .db
91 .engine
92 .backup_list()?
93 .try_stream()
94 .try_for_each(|result| write!(self, "{result}"))
95 .await
96}
97
98#[admin_command]
99pub(super) async fn backup_database(&self) -> Result {
100 let db = Arc::clone(&self.services.db);
101 let result = self
102 .services
103 .server
104 .runtime()
105 .spawn_blocking(move || match db.engine.backup() {
106 | Ok(()) => "Done".to_owned(),
107 | Err(e) => format!("Failed: {e}"),
108 })
109 .await?;
110
111 let count = self.services.db.engine.backup_count()?;
112 self.write_str(&format!("{result}. Currently have {count} backups."))
113 .await
114}
115
116#[admin_command]
117pub(super) async fn admin_notice(&self, message: Vec<String>) -> Result {
118 let message = message.join(" ");
119 self.services.admin.send_text(&message).await;
120
121 self.write_str("Notice was sent to #admins").await
122}
123
124#[admin_command]
125pub(super) async fn reload_mods(&self) -> Result {
126 self.services.server.reload()?;
127
128 self.write_str("Reloading server...").await
129}
130
131#[admin_command]
132#[cfg(unix)]
133pub(super) async fn restart(&self, force: bool) -> Result {
134 use tuwunel_core::utils::sys::current_exe_deleted;
135
136 if !force && current_exe_deleted() {
137 return Err!(
138 "The server cannot be restarted because the executable changed. If this is expected \
139 use --force to override."
140 );
141 }
142
143 self.services.server.restart()?;
144
145 self.write_str("Restarting server...").await
146}
147
148#[admin_command]
149pub(super) async fn shutdown(&self) -> Result {
150 warn!("shutdown command");
151 self.services.server.shutdown()?;
152
153 self.write_str("Shutting down server...").await
154}