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, time: i32) -> Result<String, String> {
108 let connection = &mut establish_connection();
109
110 let mut query = players::table
111 .filter(players::state.ne(PlayerState::Human))
112 .filter(players::state.ne(PlayerState::Corpse))
113 .order(players::next_feeding.asc())
114 .into_boxed();
115 if n != 0 {
116 query = query.limit(n as i64);
117 }
118 let target_players = query
119 .load::<Player>(connection)
120 .map_err(|e| format!("Error selecting players to feed: {}", e))?;
121
122 for player in target_players.clone() {
123 player.feed(Some(time as i64)).await?;
124 }
125
126 Ok(format!("{} players fed", target_players.len()))
127 }
128
129 pub async fn admin_feed(&self, username: String) -> Result<String, String> {
138 let connection = &mut establish_connection();
139
140 let result = diesel::update(players::table.filter(players::alias.eq(username)))
141 .set(players::next_feeding.eq(Zombie::feeding_time_calc()?))
142 .execute(connection)
143 .map_err(|e| format!("Error feeding player: {}", e))?;
144
145 Ok(format!("{} player fed", result))
146 }
147
148 pub async fn feed_zombies_day(&self) -> Result<String, String> {
154 let connection = &mut establish_connection();
155
156 let mut target_players = players::table
157 .filter(players::next_feeding.is_not_null())
158 .load::<Player>(connection)
159 .map_err(|e| format!("Error loading zombies to feed: {}", e))?;
160
161 for player in &mut target_players {
162 if let Some(next_feeding) = player.next_feeding {
163 let mut new_next_feeding = next_feeding + chrono::Duration::days(1);
164 let weekday = new_next_feeding.weekday().number_from_monday();
165 if weekday == 5 || weekday == 6 {
166 new_next_feeding += chrono::Duration::days(2);
167 }
168 diesel::update(players::table.filter(players::id.eq(&player.id)))
169 .set(players::next_feeding.eq(new_next_feeding))
170 .execute(connection)
171 .map_err(|e| {
172 format!(
173 "Error updating next_feeding for player {}: {}",
174 player.id, e
175 )
176 })?;
177 }
178 }
179
180 Ok(format!("{} zombies fed", target_players.len()))
181 }
182
183 pub async fn cure(&self, id: String) -> Result<String, String> {
192 let connection = &mut establish_connection();
193
194 self.transform(id.clone(), PlayerState::Human).await?;
195
196 let result = diesel::update(players::table.filter(players::id.eq(id)))
197 .set(players::next_feeding.eq(Option::<CustomDateTime>::None))
198 .execute(connection)
199 .map_err(|e| format!("Error transforming player: {}", e))?;
200
201 Ok(format!("{} player transformed", result))
202 }
203
204 pub async fn transform(&self, alias: String, state: PlayerState) -> Result<String, String> {
214 let connection = &mut establish_connection();
215
216 let new_feeding_time = match state {
217 PlayerState::Zombie | PlayerState::Zombie0 => {
218 let new_feeding_time: CustomDateTime = Self::feeding_time_calc()?;
219 Some(new_feeding_time)
220 }
221 _ => None,
222 };
223
224 let result = diesel::update(players::table.filter(players::alias.eq(alias)))
225 .set((
226 players::state.eq(state),
227 players::next_feeding.eq(new_feeding_time),
228 ))
229 .execute(connection)
230 .map_err(|e| format!("Error transforming player: {}", e))?;
231
232 Ok(format!("{} player transformed", result))
233 }
234
235 pub async fn player_at_checkpoint(&self, player_id: String) -> Result<String, String> {
244 let connection = &mut establish_connection();
245
246 let mut player = Player::get_player(player_id.clone()).await?;
247 if player.is_none() {
248 player = Player::get_player_by_alias(player_id).await?;
249 }
250
251 match player {
252 Some(player) => {
253 diesel::update(players::table)
254 .filter(players::id.eq(player.id))
255 .set(players::checkpoint.eq(Utc::now().naive_local()))
256 .execute(connection)
257 .expect("Failed to update player checkpoint");
258 Ok(format!("Player {} is at checkpoint", player.name))
259 }
260 None => Err("Player not found".to_string()),
261 }
262 }
263
264 pub async fn kill_all_not_at_checkpoint(&self) -> Result<String, String> {
270 let connection = &mut establish_connection();
271
272 let result = diesel::update(players::table)
273 .filter(players::checkpoint.is_null())
274 .filter(players::state.eq(PlayerState::Human))
275 .filter(players::state.eq(PlayerState::Revived))
276 .set(players::state.eq(PlayerState::Corpse))
277 .execute(connection)
278 .map_err(|e| format!("Failed to kill players: {}", e))?;
279
280 Ok(format!("Killed {} players", result))
281 }
282
283 pub async fn inject_sql(&self, sql_command: String) -> Result<String, String> {
292 let connection = &mut establish_connection();
293
294 let result = sql_query(&sql_command)
295 .execute(connection)
296 .map_err(|e| format!("SQL execution error: {}", e))?;
297
298 Ok(format!("Changed {} entries", result))
299 }
300
301 pub async fn get_logs(&self) -> Result<String, String> {
307 let log_file_path = Some("log/app.log");
308
309 let log_file_path = match log_file_path {
310 Some(path) => path,
311 None => {
312 return Err(
313 "Could not determine log file path from log4rs configuration.".to_string(),
314 );
315 }
316 };
317
318 let log_content = fs::read_to_string(&log_file_path)
319 .map_err(|e| format!("Failed to read log file: {}", e))?;
320
321 Ok(log_content)
322 }
323}