use crate::database::user::{User, UserForm}; use crate::AppState; use actix_web::{delete, post, web, HttpResponse, Scope}; use chrono::{Duration, Utc}; use jsonwebtoken::{encode, EncodingKey, Header}; use serde::{Deserialize, Serialize}; pub fn auth_scope() -> Scope { web::scope("/auth") .service(register) .service(login) .service(delete_user) } #[derive(Serialize, Deserialize)] pub struct Claims { pub id: usize, pub exp: usize, } #[derive(Serialize, Deserialize)] struct Response { message: String, } #[derive(Serialize, Deserialize)] struct EncodeResponse { message: String, token: String, } #[derive(Serialize, Deserialize)] struct DecodeResponse { message: String, id: usize, } #[derive(Serialize, Deserialize)] struct DecodeBody { token: String, } #[post("/register")] pub async fn register( app_state: web::Data, request_data: web::Json, ) -> HttpResponse { let query = app_state.database.register(request_data.into_inner()).await; match query { Ok(_) => HttpResponse::Ok().json(Response { message: "Registration executed with no errors".to_owned(), }), Err(e) => HttpResponse::BadRequest().json(Response { message: format!("There was an issue in the request: {}", e).to_owned(), }), } } #[post("/login")] pub async fn login( app_state: web::Data, request_data: web::Json, ) -> HttpResponse { let query = app_state.database.login(request_data.into_inner()).await; let result = match query { Ok(res) => res, Err(e) => { return HttpResponse::BadRequest().json(Response { message: format!("There was an issue in the request: {}", e).to_owned(), }) } }; let user: User = match result { Some(user) => user, None => { return HttpResponse::BadRequest().json(Response { message: "Username/Password incorrect!".to_owned(), }) } }; let id: usize = match user.id { Some(res) => res as usize, None => { return HttpResponse::BadRequest().json(Response { message: "Internal error: user id not found".to_owned(), }) } }; return match encode_token(id, &app_state.secret).await { Ok(token) => HttpResponse::Ok().json(EncodeResponse { message: format!("Successfully logged in as {}", user.name.unwrap()).to_owned(), token: token.to_owned(), }), Err(response) => response, }; } async fn encode_token(id: usize, secret: &String) -> Result { let exp: usize = (Utc::now() + Duration::days(365)).timestamp() as usize; let claims: Claims = Claims { id, exp }; match encode( &Header::default(), &claims, &EncodingKey::from_secret(secret.as_str().as_ref()), ) { Ok(token) => return Ok(token), Err(_) => return Err(HttpResponse::Ok().body("Token encoding didn't work\n")), }; } // todo! tell if the user has been deleted or not #[delete("/user")] pub async fn delete_user( app_state: web::Data, request_data: web::Json, ) -> HttpResponse { let query = app_state .database .delete_user(request_data.into_inner()) .await; match query { Ok(_) => HttpResponse::Ok().json(Response { message: "Deletion executed with no errors".to_owned(), }), Err(e) => { return HttpResponse::BadRequest().json(Response { message: format!("There was an issue in the request: {}", e).to_owned(), }) } } }