use std::{error::Error, fmt::Display, time::Duration}; use serde::{Deserialize, Serialize}; use crate::api::FastInsidersApi; #[cfg(client)] #[derive(Serialize)] pub struct UserLoginBody { pub name: String, pub password: String, } #[cfg(client)] #[derive(Serialize)] pub struct UserRegisterBody { pub name: String, pub email: String, pub password: String, } #[cfg(client)] type Response = reqwasm::http::Response; #[cfg(client)] #[derive(Deserialize)] struct LoginResponse { pub token: String, } #[cfg(client)] #[derive(Debug, Deserialize)] pub enum LoginError { Unknown, InvalidCredentials, InternalServer(String), Server(String), UserNotFound(String), } #[cfg(client)] #[derive(Deserialize)] struct ErrorBody { error: String, } #[cfg(client)] impl Display for LoginError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Unknown => { write!(f, "There was an unknown error while trying to log you in.").to_owned() } Self::InternalServer(e) => write!(f, "Internal server error: {}", e), Self::Server(e) => write!(f, "There was an error completing the request {}", e), Self::InvalidCredentials => write!(f, "Invalid username or password"), Self::UserNotFound(e) => write!(f, "{e}"), } } } #[cfg(client)] impl Error for LoginError {} #[cfg(client)] #[derive(Debug, Deserialize)] pub enum RegisterError { Unknown, InternalServer(String), Server(String), Conflict(String), } #[cfg(client)] impl Display for RegisterError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Unknown => { write!(f, "There was an unknown error while trying to log you in.").to_owned() } Self::InternalServer(e) => write!(f, "Internal server error: {}", e), Self::Server(e) => write!(f, "There was an error completing the request {}", e), Self::Conflict(e) => write!(f, "{e}"), } } } #[cfg(client)] impl Error for RegisterError {} #[cfg(client)] pub fn set_token_cookie(token: &str) { use wasm_cookies::CookieOptions; let cookie_options = CookieOptions::default() .secure() .with_same_site(wasm_cookies::SameSite::Strict) .expires_after(Duration::from_secs(60 * 60 * 24 * 5)); // 5 days wasm_cookies::set("token", token, &cookie_options); } #[cfg(client)] impl FastInsidersApi { pub async fn login(&self, body: &UserLoginBody) -> Result<(), LoginError> { let route = &format!("{}/user/login", self.url); let resp = reqwasm::http::Request::post(route) .header("Content-type", "application/json") .body(serde_json::to_string(&body).unwrap()) .send() .await .map_err(|e| LoginError::Server(e.to_string()))?; if resp.status() == 200 { if let Ok(data) = resp.json::().await { set_token_cookie(&data.token); return Ok(()); } else { panic!(); } } match resp.status() { 401 => return Err(LoginError::InvalidCredentials), 500 => { let error = resp.json::().await.unwrap().error; return Err(LoginError::InternalServer(error)); } 404 => { let error = resp.json::().await.unwrap().error; return Err(LoginError::UserNotFound(error)); } _ => return Err(LoginError::Unknown), } } pub async fn register(&self, body: &UserRegisterBody) -> Result<(), RegisterError> { let route = &format!("{}/user/register", self.url); use wasm_cookies::CookieOptions; let resp = reqwasm::http::Request::post(route) .header("Content-type", "application/json") .body(serde_json::to_string(&body).unwrap()) .send() .await .map_err(|e| RegisterError::Server(e.to_string()))?; if resp.status() == 200 { return Ok(()); } if resp.status() == 500 { let error = resp.json::().await.unwrap().error; return Err(RegisterError::InternalServer(error)); } if resp.status() == 409 { let error = resp.json::().await.unwrap().error; return Err(RegisterError::Conflict(error)); } Err(RegisterError::Unknown) } }