use axum::{ extract::{Json, Path, State}, http::StatusCode, response::{IntoResponse, Response}, routing::{get, post}, Router, }; use super::{app, Id, Invite, Summary}; use crate::{ app::App, clock::RequestedAt, error::{Internal, NotFound}, login::{Login, Password}, token::extract::IdentityToken, }; pub fn router() -> Router { Router::new() .route("/api/invite", post(on_invite)) .route("/api/invite/:invite", get(invite)) .route("/api/invite/:invite", post(on_accept)) } #[derive(serde::Deserialize)] struct InviteRequest {} async fn on_invite( State(app): State, RequestedAt(issued_at): RequestedAt, login: Login, // Require `{}` as the only valid request for this endpoint. _: Json, ) -> Result, Internal> { let invite = app.invites().create(&login, &issued_at).await?; Ok(Json(invite)) } async fn invite( State(app): State, Path(invite): Path, ) -> Result, InviteError> { app.invites() .get(&invite) .await .map(Json) .map_err(InviteError) } struct InviteError(app::Error); impl IntoResponse for InviteError { fn into_response(self) -> Response { let Self(error) = self; match error { error @ app::Error::NotFound(_) => NotFound(error).into_response(), other => Internal::from(other).into_response(), } } } #[derive(serde::Deserialize)] struct AcceptRequest { name: String, password: Password, } async fn on_accept( State(app): State, RequestedAt(accepted_at): RequestedAt, identity: IdentityToken, Path(invite): Path, Json(request): Json, ) -> Result<(IdentityToken, StatusCode), AcceptError> { let secret = app .invites() .accept(&invite, &request.name, &request.password, &accepted_at) .await .map_err(AcceptError)?; let identity = identity.set(secret); Ok((identity, StatusCode::NO_CONTENT)) } struct AcceptError(app::AcceptError); impl IntoResponse for AcceptError { fn into_response(self) -> Response { let Self(error) = self; match error { error @ app::AcceptError::NotFound(_) => NotFound(error).into_response(), other => Internal::from(other).into_response(), } } }