Add get_person route

This commit is contained in:
Ben Grant 2023-05-13 16:30:16 +10:00
parent 7770ab958a
commit d2a94b078c
8 changed files with 164 additions and 12 deletions

View file

@ -4,7 +4,7 @@ use common::adaptor::Adaptor;
pub enum ApiError<A: Adaptor> {
AdaptorError(A::Error),
NotFound,
// NotAuthorized,
NotAuthorized,
}
// Define what the error types above should return
@ -16,7 +16,7 @@ impl<A: Adaptor> IntoResponse for ApiError<A> {
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
ApiError::NotFound => StatusCode::NOT_FOUND.into_response(),
// ApiError::NotAuthorized => StatusCode::UNAUTHORIZED.into_response(),
ApiError::NotAuthorized => StatusCode::UNAUTHORIZED.into_response(),
}
}
}

View file

@ -39,9 +39,10 @@ async fn main() {
let app = Router::new()
.route("/", get(get_root))
.route("/stats", get(get_stats))
.route("/event/:event_id", get(get_event))
.route("/event", post(create_event))
.route("/event/:event_id", get(get_event))
.route("/event/:event_id/people", get(get_people))
.route("/event/:event_id/people/:person_name", get(get_person))
.with_state(shared_state);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

View file

@ -58,3 +58,8 @@ impl From<Person> for PersonResponse {
}
}
}
#[derive(Deserialize)]
pub struct PersonInput {
pub password: Option<String>,
}

View file

@ -0,0 +1,87 @@
use axum::{
extract::{self, Path},
Json,
};
use common::{adaptor::Adaptor, person::Person};
use crate::{
errors::ApiError,
payloads::{ApiResult, PersonInput, PersonResponse},
State,
};
pub async fn get_person<A: Adaptor>(
extract::State(state): State<A>,
Path((event_id, person_name)): Path<(String, String)>,
input: Option<Json<PersonInput>>,
) -> ApiResult<PersonResponse, A> {
let adaptor = &state.lock().await.adaptor;
// Get inputted password
let password = match input {
Some(Json(i)) => i.password,
None => None,
};
let existing_people = adaptor
.get_people(event_id.clone())
.await
.map_err(ApiError::AdaptorError)?;
// Event not found
if existing_people.is_none() {
return Err(ApiError::NotFound);
}
// Check if the user already exists
let existing_person = existing_people
.unwrap()
.into_iter()
.find(|p| p.name == person_name);
match existing_person {
// Login
Some(p) => {
// Verify password (if set)
if verify_password(&p, password) {
Ok(Json(p.into()))
} else {
Err(ApiError::NotAuthorized)
}
}
// Signup
None => {
// Update stats
adaptor
.increment_stat_person_count()
.await
.map_err(ApiError::AdaptorError)?;
Ok(Json(
adaptor
.upsert_person(
event_id,
Person {
name: person_name,
password_hash: password
.map(|raw| bcrypt::hash(raw, 10).unwrap_or(String::from(""))),
created_at: chrono::offset::Utc::now(),
availability: vec![],
},
)
.await
.map_err(ApiError::AdaptorError)?
.into(),
))
}
}
}
fn verify_password(person: &Person, raw: Option<String>) -> bool {
match &person.password_hash {
Some(hash) => bcrypt::verify(raw.unwrap_or(String::from("")), hash).unwrap_or(false),
// Specifically allow a user who doesn't have a password
// set to log in with or without any password input
None => true,
}
}

View file

@ -9,3 +9,6 @@ pub use create_event::create_event;
mod get_people;
pub use get_people::get_people;
mod get_person;
pub use get_person::get_person;