Skip to main content

hvz/endpoints/
player.rs

1use actix_files::NamedFile;
2use actix_multipart::Multipart;
3use actix_web::{HttpRequest, HttpResponse, Responder, delete, get, post, web};
4use chrono::NaiveDate;
5use futures::future::join_all;
6use serde::{Deserialize, Serialize};
7
8use crate::{
9    player::{Player, PlayerRole, PlayerState},
10    time::CustomDateTime,
11    token::Token,
12};
13
14/// Struct representing a new player to be created.
15#[derive(Debug, Serialize, Deserialize)]
16pub struct NewPlayer {
17    pub name: String,
18    pub surname: String,
19    pub pronouns: String,
20    pub alias: String,
21    pub niu: String,
22    pub email: String,
23    pub phone: String,
24    pub faculty: String,
25    pub isuab: bool,
26    pub how_did_you_hear_about_us: String,
27    pub social_media: Option<String>,
28    pub password: String,
29    pub birth_date: NaiveDate,
30    pub accept_info_other_editions: bool,
31    pub wants_to_be_zombie: i32,
32}
33
34/// Struct representing a player to be edited.
35#[derive(Debug, Serialize, Deserialize)]
36pub struct EditPlayer {
37    pub bio: String,
38    pub pronouns: String,
39    pub alias: String,
40    pub email: String,
41    pub phone: String,
42    pub social_media: Option<String>,
43    pub accept_info_other_editions: bool,
44}
45
46/// Struct representing a player to be logged in.
47#[derive(Debug, Serialize, Deserialize)]
48pub struct Login {
49    pub username: String,
50    pub password: String,
51}
52
53/// Struct returned from a successful login.
54#[derive(Debug, Serialize, Deserialize)]
55pub struct LoginReturn {
56    pub uuid: String,
57    pub token: String,
58}
59
60/// Struct representing a player to change their password.
61#[derive(Debug, Serialize, Deserialize)]
62pub struct ChangePassword {
63    pub old_password: String,
64    pub new_password: String,
65}
66
67/// Struct representing a player to be returned.
68#[derive(Debug, Serialize, Deserialize)]
69pub struct PlayerReturnInfo {
70    pub name: String,
71    pub surname: String,
72    pub bio: String,
73    pub pronouns: String,
74    pub alias: String,
75    pub social_media: Option<String>,
76    pub player_number: i32,
77    pub state: PlayerState,
78    pub role: PlayerRole,
79    pub next_feeding: Option<CustomDateTime>,
80}
81
82/// **POST /new** - Create a new player
83#[post("/new")]
84pub async fn new_player(form: web::Json<NewPlayer>) -> impl Responder {
85    let player = form.into_inner();
86    let new_db_player = Player::new(
87        player.name,
88        player.surname,
89        player.pronouns,
90        player.alias,
91        player.niu,
92        player.email,
93        player.phone,
94        player.faculty,
95        player.isuab,
96        player.how_did_you_hear_about_us,
97        player.social_media,
98        player.password,
99        player.birth_date,
100        player.accept_info_other_editions,
101        player.wants_to_be_zombie,
102    )
103    .await;
104
105    let insert_result = new_db_player.signup().await;
106
107    match insert_result {
108        Ok(player) => {
109            let token: Token = match Token::generate(&player.id) {
110                Ok(t) => t,
111                Err(e) => return HttpResponse::InternalServerError().body(e),
112            };
113            HttpResponse::Ok().json((token.token, player.id))
114        }
115        Err(_) => HttpResponse::InternalServerError().body("Failed to create player\n"),
116    }
117}
118
119/// **POST /login** - Logs in a player
120#[post("/login")]
121pub async fn login(form: web::Json<Login>) -> impl Responder {
122    let Login { username, password } = form.into_inner();
123
124    let result = Player::login(&username, password).await;
125
126    match result {
127        Ok(player) => {
128            let token: Token = match Token::generate(&player.id) {
129                Ok(t) => t,
130                Err(e) => return HttpResponse::InternalServerError().body(e),
131            };
132            HttpResponse::Ok().json(LoginReturn {
133                uuid: player.id,
134                token: token.token,
135            })
136        }
137        Err(_) => HttpResponse::InternalServerError().body("Player not found\n"),
138    }
139}
140
141/// **POST /logout** - Logs out a player
142#[post("/logout/{url_id}")]
143pub async fn logout(url_id: web::Path<String>, req: HttpRequest) -> impl Responder {
144    if let Err(e) = Token::check_token(&req, &url_id).await {
145        return HttpResponse::Forbidden().body(e);
146    }
147
148    let player = match Player::get_player(url_id.into_inner()).await {
149        Ok(Some(player)) => player,
150        Ok(None) => return HttpResponse::NotFound().body("Player not found"),
151        Err(err) => return HttpResponse::InternalServerError().body(err),
152    };
153
154    let result = Token::clear_player_tokens(&player);
155
156    match result {
157        Ok(_) => HttpResponse::Ok().finish(),
158        Err(err) => HttpResponse::InternalServerError().body(err),
159    }
160}
161
162/// **POST /change_password** - Changes a player's password
163#[post("/change_password/{url_id}")]
164pub async fn change_password(
165    url_id: web::Path<String>,
166    req: HttpRequest,
167    form: web::Json<ChangePassword>,
168) -> impl Responder {
169    if let Err(e) = Token::check_token(&req, &url_id).await {
170        return HttpResponse::Forbidden().body(e);
171    }
172
173    let player = match Player::get_player(url_id.into_inner()).await {
174        Ok(Some(player)) => player,
175        Ok(None) => return HttpResponse::NotFound().body("Player not found"),
176        Err(err) => return HttpResponse::InternalServerError().body(err),
177    };
178
179    let ChangePassword {
180        old_password,
181        new_password,
182    } = form.into_inner();
183
184    if let Err(e) = Token::clear_player_tokens(&player) {
185        return HttpResponse::InternalServerError().body(e);
186    };
187    let token = match Token::generate(&player.id) {
188        Ok(t) => t,
189        Err(e) => return HttpResponse::InternalServerError().body(e),
190    };
191
192    let result = player.change_password(old_password, new_password).await;
193
194    match result {
195        Ok(_) => HttpResponse::Ok().json((token.token, player.id)),
196        Err(e) => HttpResponse::InternalServerError().body(e),
197    }
198}
199
200/// **POST /edit_player/{url_id}** - Edits a player's information
201#[post("/edit_player/{url_id}")]
202pub async fn edit_player(
203    url_id: web::Path<String>,
204    req: HttpRequest,
205    form: web::Json<EditPlayer>,
206) -> impl Responder {
207    if let Err(e) = Token::check_token(&req, &url_id).await {
208        return HttpResponse::Forbidden().body(e);
209    }
210
211    let player = match Player::get_player(url_id.into_inner()).await {
212        Ok(Some(player)) => player,
213        Ok(None) => return HttpResponse::NotFound().body("Player not found"),
214        Err(err) => return HttpResponse::InternalServerError().body(err),
215    };
216
217    let edit_data = form.into_inner();
218    let result = player
219        .edit(
220            edit_data.bio,
221            edit_data.pronouns,
222            edit_data.alias,
223            edit_data.email,
224            edit_data.phone,
225            edit_data.social_media,
226            edit_data.accept_info_other_editions,
227        )
228        .await;
229
230    match result {
231        Ok(_) => HttpResponse::Ok().body("Player edited\n"),
232        Err(_) => HttpResponse::InternalServerError().body("Failed to edit player\n"),
233    }
234}
235
236/// **POST /set_pfp/{url_id}** - Sets a player's profile picture
237#[post("/set_pfp/{url_id}")]
238pub async fn set_pfp(
239    url_id: web::Path<String>,
240    req: HttpRequest,
241    mut img: Multipart,
242) -> impl Responder {
243    if let Err(e) = Token::check_token(&req, &url_id).await {
244        return HttpResponse::Forbidden().body(e);
245    }
246
247    let player = match Player::get_player(url_id.into_inner()).await {
248        Ok(Some(player)) => player,
249        Ok(None) => return HttpResponse::NotFound().body("Player not found"),
250        Err(err) => return HttpResponse::InternalServerError().body(err),
251    };
252
253    let result = player.set_pfp(&mut img).await;
254
255    match result {
256        Ok(_) => HttpResponse::Ok().body("Profile picture set\n"),
257        Err(err) => HttpResponse::InternalServerError()
258            .body(format!("Failed to set profile picture: {}", err)),
259    }
260}
261
262/// **GET /pfp/{url_id}** - Gets a player's profile picture
263#[get("/pfp/{url_id}")]
264pub async fn pfp(url_id: web::Path<String>, req: HttpRequest) -> impl Responder {
265    if let Err(e) = Token::check_token(&req, &url_id).await {
266        return HttpResponse::Forbidden().body(e);
267    }
268
269    let player = match Player::get_player(url_id.into_inner()).await {
270        Ok(Some(player)) => player,
271        Ok(None) => return HttpResponse::NotFound().body("Player not found"),
272        Err(err) => return HttpResponse::InternalServerError().body(err),
273    };
274
275    let result = player.get_pfp().await;
276
277    match result {
278        Ok(named_file) => NamedFile::into_response(named_file, &req),
279        Err(_) => HttpResponse::InternalServerError().body("Failed to get profile picture\n"),
280    }
281}
282
283/// **POST /get_pfp/{url_id}** - Get someone else's profile picture
284#[post("/get_pfp/{url_id}")]
285pub async fn get_pfp(
286    url_id: web::Path<String>,
287    req: HttpRequest,
288    form: web::Json<String>,
289) -> impl Responder {
290    if let Err(e) = Token::check_token(&req, &url_id).await {
291        return HttpResponse::Forbidden().body(e);
292    }
293
294    match Player::get_player(url_id.into_inner()).await {
295        Ok(Some(player)) => player,
296        Ok(None) => return HttpResponse::NotFound().body("Player not found"),
297        Err(err) => return HttpResponse::InternalServerError().body(err),
298    };
299
300    let other = match Player::get_player_by_alias(form.into_inner()).await {
301        Ok(Some(player)) => player,
302        Ok(None) => return HttpResponse::NotFound().body("Player not found"),
303        Err(err) => return HttpResponse::InternalServerError().body(err),
304    };
305
306    let result = other.get_pfp().await;
307
308    match result {
309        Ok(named_file) => NamedFile::into_response(named_file, &req),
310        Err(_) => HttpResponse::InternalServerError().body("Failed to get profile picture\n"),
311    }
312}
313
314/// **GET /checkpoint/{url_id}** - Gets a player's checkpoint
315#[get("/checkpoint/{url_id}")]
316pub async fn get_checkpoint(url_id: web::Path<String>, req: HttpRequest) -> impl Responder {
317    if let Err(e) = Token::check_token(&req, &url_id).await {
318        return HttpResponse::Forbidden().body(e);
319    }
320
321    let player = match Player::get_player(url_id.into_inner()).await {
322        Ok(Some(player)) => player,
323        Ok(None) => return HttpResponse::NotFound().body("Player not found"),
324        Err(err) => return HttpResponse::InternalServerError().body(err),
325    };
326
327    let result = player.get_checkpoint().await;
328
329    match result {
330        Ok(checkpoint) => HttpResponse::Ok().json(checkpoint),
331        Err(_) => HttpResponse::InternalServerError().body("Failed to get checkpoint\n"),
332    }
333}
334
335/// **GET /player/{url_id}** - Gets the current player by id
336#[get("/player/{url_id}")]
337pub async fn get_player(url_id: web::Path<String>, req: HttpRequest) -> impl Responder {
338    if let Err(e) = Token::check_token(&req, &url_id).await {
339        return HttpResponse::Forbidden().body(e);
340    }
341
342    match Player::get_player(url_id.into_inner()).await {
343        Ok(Some(player)) => HttpResponse::Ok().json(player),
344        Ok(None) => HttpResponse::NotFound().body("Player not found"),
345        Err(err) => HttpResponse::InternalServerError().body(err),
346    }
347}
348
349/// **POST /get_player/{url_id}** - Get another player by alias
350#[post("/get_player/{url_id}")]
351pub async fn get_other_player(
352    url_id: web::Path<String>,
353    req: HttpRequest,
354    form: web::Json<String>,
355) -> impl Responder {
356    if let Err(e) = Token::check_token(&req, &url_id).await {
357        return HttpResponse::Forbidden().body(e);
358    }
359
360    let player = match Player::get_player(url_id.into_inner()).await {
361        Ok(Some(player)) => player,
362        Ok(None) => return HttpResponse::NotFound().body("Player not found"),
363        Err(err) => return HttpResponse::InternalServerError().body(err),
364    };
365
366    let other = Player::get_player_by_alias(form.into_inner()).await;
367
368    match other {
369        Ok(Some(p)) => {
370            let player_response = PlayerReturnInfo {
371                name: p.name,
372                surname: p.surname,
373                bio: p.bio,
374                pronouns: p.pronouns,
375                alias: p.alias,
376                social_media: p.social_media,
377                player_number: p.player_number,
378                state: if p.state == PlayerState::Zombie0 {
379                    PlayerState::Human
380                } else {
381                    p.state
382                },
383                role: p.role,
384                next_feeding: if player.is_zombie().await {
385                    p.next_feeding
386                } else {
387                    None
388                },
389            };
390            HttpResponse::Ok().json(player_response)
391        }
392        Ok(None) => HttpResponse::NotFound().body("Player not found"),
393        Err(err) => HttpResponse::InternalServerError().body(err),
394    }
395}
396
397/// **GET /users/{url_id}** - Get all users
398#[get("/users/{url_id}")]
399pub async fn get_users(url_id: web::Path<String>, req: HttpRequest) -> impl Responder {
400    if let Err(e) = Token::check_token(&req, &url_id).await {
401        return HttpResponse::Forbidden().body(e);
402    }
403
404    let player = match Player::get_player(url_id.into_inner()).await {
405        Ok(Some(player)) => player,
406        Ok(None) => return HttpResponse::NotFound().body("Player not found"),
407        Err(err) => return HttpResponse::InternalServerError().body(err),
408    };
409    let is_zombie = player.is_zombie().await;
410
411    let result = Player::get_all_players().await;
412
413    match result {
414        Ok(players) => {
415            let futures = players.into_iter().map(|p| async move {
416                PlayerReturnInfo {
417                    name: p.name,
418                    surname: p.surname,
419                    bio: p.bio,
420                    pronouns: p.pronouns,
421                    alias: p.alias,
422                    social_media: p.social_media,
423                    player_number: p.player_number,
424                    state: if p.state == PlayerState::Zombie0 {
425                        PlayerState::Human
426                    } else {
427                        p.state
428                    },
429                    role: p.role,
430                    next_feeding: if is_zombie { p.next_feeding } else { None },
431                }
432            });
433
434            let return_players: Vec<PlayerReturnInfo> = join_all(futures).await;
435            HttpResponse::Ok().json(return_players)
436        }
437        Err(e) => {
438            eprintln!("Failed to get players: {:?}\n", e);
439            HttpResponse::InternalServerError().body("Failed to get players\n")
440        }
441    }
442}
443
444/// **DELETE /delete_player/{url_id}** - Deletes a player by UUID
445#[delete("/delete_player/{url_id}")]
446pub async fn delete_player(url_id: web::Path<String>, req: HttpRequest) -> impl Responder {
447    if let Err(e) = Token::check_token(&req, &url_id).await {
448        return HttpResponse::Forbidden().body(e);
449    }
450
451    let player = match Player::get_player(url_id.into_inner()).await {
452        Ok(Some(player)) => player,
453        Ok(None) => return HttpResponse::NotFound().body("Player not found"),
454        Err(err) => return HttpResponse::InternalServerError().body(err),
455    };
456
457    if let Err(e) = Token::clear_player_tokens(&player) {
458        return HttpResponse::InternalServerError().body(e);
459    }
460
461    let result = Player::delete_player(player.id).await;
462
463    match result {
464        Ok(_) => HttpResponse::Ok().body("Player deleted\n"),
465        Err(_) => HttpResponse::InternalServerError().body("Failed to delete player\n"),
466    }
467}