summaryrefslogtreecommitdiffstats
path: root/src/routes/players.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/routes/players.rs131
1 files changed, 131 insertions, 0 deletions
diff --git a/src/routes/players.rs b/src/routes/players.rs
new file mode 100644
index 0000000..b98a39d
--- /dev/null
+++ b/src/routes/players.rs
@@ -0,0 +1,131 @@
1use askama::Template;
2use axum::{
3 Form, Router,
4 extract::State,
5 response::{Html, Redirect},
6 routing::{get, post},
7};
8use serde::Deserialize;
9
10use crate::auth::CurrentUser;
11use crate::routes::AppState;
12
13#[derive(Template)]
14#[template(path = "players/manage.html")]
15struct PlayerManagementTemplate {
16 username: String,
17 is_superuser: bool,
18 password_change_required: bool,
19 active_instance_name: Option<String>,
20 whitelist: Vec<String>,
21 ops: Vec<String>,
22 bans: Vec<String>,
23 message: Option<String>,
24}
25
26#[derive(Debug, Deserialize)]
27pub struct PlayerAction {
28 pub username: String,
29}
30
31pub fn router() -> Router<AppState> {
32 Router::new()
33 .route("/players", get(manage_players))
34 .route("/players/whitelist/add", post(add_whitelist))
35 .route("/players/whitelist/remove", post(remove_whitelist))
36 .route("/players/op/add", post(add_op))
37 .route("/players/op/remove", post(remove_op))
38 .route("/players/ban/add", post(add_ban))
39 .route("/players/ban/remove", post(remove_ban))
40}
41
42async fn manage_players(
43 State(state): State<AppState>,
44 CurrentUser(account): CurrentUser,
45) -> Result<Html<String>, axum::response::Redirect> {
46 let active = get_active_instance(&state.db);
47
48 let (instance_name, whitelist, ops, bans) =
49 if let Some((_id, inst_name, _rcon_pw, _container_state)) = active {
50 let data_dir = state
51 .config
52 .server_directory
53 .join("instances")
54 .join(&inst_name)
55 .join("data");
56
57 let w = read_json_list(&data_dir.join("whitelist.json"));
58 let o = read_json_list(&data_dir.join("ops.json"));
59 let b = read_json_list(&data_dir.join("banned-players.json"));
60 (Some(inst_name), w, o, b)
61 } else {
62 (None, vec![], vec![], vec![])
63 };
64
65 let template = PlayerManagementTemplate {
66 username: account.username,
67 is_superuser: account.is_superuser,
68 password_change_required: account.password_change_required,
69 active_instance_name: instance_name,
70 whitelist,
71 ops,
72 bans,
73 message: None,
74 };
75
76 Ok(Html(template.render().unwrap_or_default()))
77}
78
79fn read_json_list(path: &std::path::Path) -> Vec<String> {
80 if let Ok(contents) = std::fs::read_to_string(path) {
81 if let Ok(entries) = serde_json::from_str::<Vec<serde_json::Value>>(&contents) {
82 return entries
83 .iter()
84 .filter_map(|e| e["name"].as_str().map(String::from))
85 .collect();
86 }
87 }
88 vec![]
89}
90
91macro_rules! player_action {
92 ($name:ident, $command:expr) => {
93 async fn $name(
94 State(state): State<AppState>,
95 CurrentUser(_user): CurrentUser,
96 Form(form): Form<PlayerAction>,
97 ) -> Result<Redirect, axum::response::Redirect> {
98 if let Some((_, _, rcon_pw, _)) = get_active_instance(&state.db) {
99 let client = crate::rcon::RconClient::new(rcon_pw);
100 let cmd = format!($command, form.username);
101 match client.send_command_async(cmd.clone()).await {
102 Ok(resp) => tracing::info!("RCON '{}' -> {}", cmd, resp.trim()),
103 Err(e) => tracing::error!("RCON '{}' failed: {}", cmd, e),
104 }
105 } else {
106 tracing::warn!(
107 "Player action '{}' called but no active instance",
108 stringify!($name)
109 );
110 }
111 Ok(Redirect::to("/players"))
112 }
113 };
114}
115
116player_action!(add_whitelist, "whitelist add {}");
117player_action!(remove_whitelist, "whitelist remove {}");
118player_action!(add_op, "op {}");
119player_action!(remove_op, "deop {}");
120player_action!(add_ban, "ban {}");
121player_action!(remove_ban, "pardon {}");
122
123fn get_active_instance(db: &crate::db::Database) -> Option<(i64, String, String, String)> {
124 db.conn()
125 .query_row(
126 "SELECT id, name, rcon_password, container_state FROM instances WHERE is_active = 1",
127 [],
128 |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)),
129 )
130 .ok()
131}