|
| 1 | +use actix_web::{post, web, HttpRequest, HttpResponse, Responder}; |
| 2 | +use serde::{Deserialize, Serialize}; |
| 3 | +use utoipa::ToSchema; |
| 4 | + |
| 5 | +use crate::{ |
| 6 | + error::Error, |
| 7 | + rate_limit::ChargerRateLimiter, |
| 8 | + routes::charger::add::{get_charger_from_db, password_matches}, |
| 9 | + utils::{parse_uuid, send_email_with_attachment}, |
| 10 | + AppState, |
| 11 | +}; |
| 12 | + |
| 13 | +#[derive(Serialize, Deserialize, ToSchema)] |
| 14 | +pub struct SendChargelogSchema { |
| 15 | + pub charger_uuid: String, |
| 16 | + pub password: String, |
| 17 | + pub user_email: String, |
| 18 | + pub chargelog: Vec<u8>, // binary data |
| 19 | +} |
| 20 | + |
| 21 | +#[utoipa::path( |
| 22 | + request_body = SendChargelogSchema, |
| 23 | + responses( |
| 24 | + (status = 200, description = "Chargelog sent via email"), |
| 25 | + (status = 401, description = "Invalid charger credentials or rate limit exceeded"), |
| 26 | + (status = 500, description = "Internal server error"), |
| 27 | + ) |
| 28 | +)] |
| 29 | +#[post("/send_chargelog_to_user")] |
| 30 | +pub async fn send_chargelog( |
| 31 | + req: HttpRequest, |
| 32 | + state: web::Data<AppState>, |
| 33 | + rate_limiter: web::Data<ChargerRateLimiter>, |
| 34 | + payload: web::Json<SendChargelogSchema>, |
| 35 | +) -> actix_web::Result<impl Responder> { |
| 36 | + rate_limiter.check(payload.charger_uuid.clone(), &req)?; |
| 37 | + |
| 38 | + let charger_id = parse_uuid(&payload.charger_uuid)?; |
| 39 | + let charger = get_charger_from_db(charger_id, &state).await?; |
| 40 | + if !password_matches(&payload.password, &charger.password)? { |
| 41 | + return Err(Error::ChargerCredentialsWrong.into()); |
| 42 | + } |
| 43 | + |
| 44 | + let subject = "Your Charger Log"; |
| 45 | + let body = "Attached is your requested chargelog.".to_string(); |
| 46 | + send_email_with_attachment( |
| 47 | + &payload.user_email, |
| 48 | + subject, |
| 49 | + body, |
| 50 | + payload.chargelog.clone(), |
| 51 | + "chargelog.bin", |
| 52 | + &state, |
| 53 | + ); |
| 54 | + |
| 55 | + Ok(HttpResponse::Ok()) |
| 56 | +} |
| 57 | + |
| 58 | +#[cfg(test)] |
| 59 | +mod tests { |
| 60 | + use super::*; |
| 61 | + use actix_web::{test, App}; |
| 62 | + use crate::{routes::user::tests::TestUser, tests::configure}; |
| 63 | + |
| 64 | + #[actix_web::test] |
| 65 | + async fn test_send_chargelog_success() { |
| 66 | + let (mut user, _mail) = TestUser::random().await; |
| 67 | + user.login().await; |
| 68 | + let charger = user.add_random_charger().await; |
| 69 | + |
| 70 | + let app = App::new().configure(configure).service(send_chargelog); |
| 71 | + let app = test::init_service(app).await; |
| 72 | + |
| 73 | + let payload = SendChargelogSchema { |
| 74 | + charger_uuid: charger.uuid.clone(), |
| 75 | + password: charger.password.clone(), |
| 76 | + user_email: user.mail.clone(), |
| 77 | + chargelog: vec![1, 2, 3, 4, 5], |
| 78 | + }; |
| 79 | + |
| 80 | + let req = test::TestRequest::post() |
| 81 | + .uri("/send_chargelog_to_user") |
| 82 | + .append_header(("X-Forwarded-For", "123.123.123.3")) |
| 83 | + .set_json(payload) |
| 84 | + .to_request(); |
| 85 | + let resp = test::call_service(&app, req).await; |
| 86 | + assert_eq!(resp.status(), 200); |
| 87 | + } |
| 88 | + |
| 89 | + #[actix_web::test] |
| 90 | + async fn test_send_chargelog_invalid_password() { |
| 91 | + let (mut user, _mail) = TestUser::random().await; |
| 92 | + user.login().await; |
| 93 | + let charger = user.add_random_charger().await; |
| 94 | + |
| 95 | + let app = App::new().configure(configure).service(send_chargelog); |
| 96 | + let app = test::init_service(app).await; |
| 97 | + |
| 98 | + let payload = SendChargelogSchema { |
| 99 | + charger_uuid: charger.uuid.clone(), |
| 100 | + password: "wrongpassword".to_string(), |
| 101 | + user_email: user.mail.clone(), |
| 102 | + chargelog: vec![1, 2, 3, 4, 5], |
| 103 | + }; |
| 104 | + |
| 105 | + let req = test::TestRequest::post() |
| 106 | + .uri("/send_chargelog_to_user") |
| 107 | + .append_header(("X-Forwarded-For", "123.123.123.3")) |
| 108 | + .set_json(payload) |
| 109 | + .to_request(); |
| 110 | + let resp = test::call_service(&app, req).await; |
| 111 | + assert_eq!(resp.status(), 401); |
| 112 | + } |
| 113 | +} |
0 commit comments