1use std::fs;
2
3use chrono::{Datelike, Utc};
4use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, dsl::count_star, sql_query};
5use password_auth::generate_hash;
6
7use crate::{
8 establish_connection,
9 player::{Player, PlayerState},
10 schema::players,
11 settings::{CHECKPOINT, Settings},
12 time::CustomDateTime,
13 zombie::Zombie,
14};
15
16pub type Admin = Player;
18
19impl Admin {
20 pub async fn recover_password(&self, username: String, password: String) -> Result<(), String> {
30 let connection = &mut establish_connection();
31
32 diesel::update(players::table.filter(players::alias.eq(username)))
33 .set(players::password.eq(generate_hash(password)))
34 .execute(connection)
35 .expect("Failed to update player");
36
37 Ok(())
38 }
39
40 pub async fn count_player(&self) -> Result<String, String> {
46 let connection = &mut establish_connection();
47
48 let total = players::table
49 .count()
50 .get_result::<i64>(connection)
51 .expect("Error counting the players.");
52 let count = players::table
53 .group_by(players::state)
54 .select((players::state, count_star()))
55 .load::<(PlayerState, i64)>(connection)
56 .expect("Error loading the roles");
57
58 let mut result = format!("Total players: {}", total.to_string());
59
60 let state_string = count
61 .iter()
62 .map(|state| {
63 format!(
64 "{}: {}, ",
65 match state.0 {
66 PlayerState::Human => "Humans",
67 PlayerState::Zombie => "Zombies",
68 PlayerState::Zombie0 => "Zombie0",
69 PlayerState::Corpse => "Corpse",
70 _ => "Others",
71 },
72 state.1
73 )
74 })
75 .collect::<String>();
76
77 result.push_str(&state_string[..state_string.len() - 2]);
78
79 Ok(result)
80 }
81
82 pub async fn admin_get_players(&self) -> Result<Vec<Player>, String> {
88 let connection = &mut establish_connection();
89
90 let players = players::table
91 .order(players::player_number.asc())
92 .load::<Player>(connection)
93 .map_err(|e| format!("Error selecting players: {}", e))?;
94
95 Ok(players)
96 }
97
98 pub async fn feed_players(&self, n: i32) -> Result<String, String> {
107 let connection = &mut establish_connection();
108
109 let target_ids = players::table
110 .filter(players::state.ne(PlayerState::Human))
111 .order(players::next_feeding.asc())
112 .select(players::id)
113 .limit(n as i64)
114 .load::<String>(connection)
115 .map_err(|e| format!("Error selecting players to feed: {}", e))?;
116
117 let result = diesel::update(players::table.filter(players::id.eq_any(&target_ids)))
118 .set(players::next_feeding.eq(Zombie::feeding_time_calc()?))
119 .execute(connection)
120 .map_err(|e| format!("Error feeding players: {}", e))?;
121
122 Ok(format!("{} players fed", result))
123 }
124
125 pub async fn admin_feed(&self, id: String) -> Result<String, String> {
134 let connection = &mut establish_connection();
135
136 let result = diesel::update(players::table.filter(players::id.eq(id)))
137 .set(players::next_feeding.eq(Zombie::feeding_time_calc()?))
138 .execute(connection)
139 .map_err(|e| format!("Error feeding player: {}", e))?;
140
141 Ok(format!("{} player fed", result))
142 }
143
144 pub async fn feed_zombies_day(&self) -> Result<String, String> {
150 let connection = &mut establish_connection();
151
152 let mut target_players = players::table
153 .filter(players::next_feeding.is_not_null())
154 .load::<Player>(connection)
155 .map_err(|e| format!("Error loading zombies to feed: {}", e))?;
156
157 for player in &mut target_players {
158 if let Some(next_feeding) = player.next_feeding {
159 let mut new_next_feeding = next_feeding + chrono::Duration::days(1);
160 let weekday = new_next_feeding.weekday().number_from_monday();
161 if weekday == 5 || weekday == 6 {
162 new_next_feeding += chrono::Duration::days(2);
163 }
164 diesel::update(players::table.filter(players::id.eq(&player.id)))
165 .set(players::next_feeding.eq(new_next_feeding))
166 .execute(connection)
167 .map_err(|e| {
168 format!(
169 "Error updating next_feeding for player {}: {}",
170 player.id, e
171 )
172 })?;
173 }
174 }
175
176 Ok(format!("{} zombies fed", target_players.len()))
177 }
178
179 pub async fn cure(&self, id: String) -> Result<String, String> {
188 let connection = &mut establish_connection();
189
190 self.transform(id.clone(), PlayerState::Human).await?;
191
192 let result = diesel::update(players::table.filter(players::id.eq(id)))
193 .set(players::next_feeding.eq(Option::<CustomDateTime>::None))
194 .execute(connection)
195 .map_err(|e| format!("Error transforming player: {}", e))?;
196
197 Ok(format!("{} player transformed", result))
198 }
199
200 pub async fn transform(&self, alias: String, state: PlayerState) -> Result<String, String> {
210 let connection = &mut establish_connection();
211
212 let result = diesel::update(players::table.filter(players::alias.eq(alias)))
213 .set(players::state.eq(state))
214 .execute(connection)
215 .map_err(|e| format!("Error transforming player: {}", e))?;
216
217 Ok(format!("{} player transformed", result))
218 }
219
220 pub async fn set_checkpoint(&self, x: String, y: String) -> Result<String, String> {
229 let coordinates = format!("{} {}", x, y);
230
231 match Settings::set_settings(CHECKPOINT, coordinates.clone()).await {
232 Ok(_) => Ok(coordinates),
233 Err(_) => Err("Error setting the coordinates!".to_string()),
234 }
235 }
236
237 pub async fn player_at_checkpoint(&self, player_id: String) -> Result<String, String> {
246 let connection = &mut establish_connection();
247
248 let player = Player::get_player(player_id).await?;
249
250 match player {
251 Some(player) => {
252 diesel::update(players::table)
253 .filter(players::id.eq(player.id))
254 .set(players::checkpoint.eq(Utc::now().naive_local()))
255 .execute(connection)
256 .expect("Failed to update player checkpoint");
257 Ok(format!("Player {} is at checkpoint", player.name))
258 }
259 None => Err("Player not found".to_string()),
260 }
261 }
262
263 pub async fn kill_all_not_at_checkpoint(&self) -> Result<String, String> {
269 let connection = &mut establish_connection();
270
271 let result = diesel::update(players::table)
272 .filter(players::checkpoint.is_null())
273 .filter(players::state.eq(PlayerState::Human))
274 .filter(players::state.eq(PlayerState::Revived))
275 .set(players::state.eq(PlayerState::Corpse))
276 .execute(connection)
277 .map_err(|e| format!("Failed to kill players: {}", e))?;
278
279 Ok(format!("Killed {} players", result))
280 }
281
282 pub async fn inject_sql(&self, sql_command: String) -> Result<String, String> {
291 let connection = &mut establish_connection();
292
293 let result = sql_query(&sql_command)
294 .execute(connection)
295 .map_err(|e| format!("SQL execution error: {}", e))?;
296
297 Ok(format!("Changed {} entries", result))
298 }
299
300 pub async fn get_logs(&self) -> Result<String, String> {
306 let log_file_path = Some("log/app.log");
307
308 let log_file_path = match log_file_path {
309 Some(path) => path,
310 None => {
311 return Err(
312 "Could not determine log file path from log4rs configuration.".to_string(),
313 );
314 }
315 };
316
317 let log_content = fs::read_to_string(&log_file_path)
318 .map_err(|e| format!("Failed to read log file: {}", e))?;
319
320 Ok(log_content)
321 }
322}