diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-10-11 22:40:03 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-10-11 22:40:03 -0400 |
| commit | 812f1cafe3f8a68bf45b677fade7417c30d92eac (patch) | |
| tree | e2633472a31bd86bf08bcc16ddb4296df89064b4 /src/invite/routes.rs | |
| parent | a0abed5ea08b2fc5b9ac4abdade1199f62cd5da7 (diff) | |
Create APIs for inviting users.
Diffstat (limited to 'src/invite/routes.rs')
| -rw-r--r-- | src/invite/routes.rs | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/src/invite/routes.rs b/src/invite/routes.rs new file mode 100644 index 0000000..3384e10 --- /dev/null +++ b/src/invite/routes.rs @@ -0,0 +1,94 @@ +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<App> { + 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<App>, + RequestedAt(issued_at): RequestedAt, + login: Login, + // Require `{}` as the only valid request for this endpoint. + _: Json<InviteRequest>, +) -> Result<Json<Invite>, Internal> { + let invite = app.invites().create(&login, &issued_at).await?; + Ok(Json(invite)) +} + +async fn invite( + State(app): State<App>, + Path(invite): Path<Id>, +) -> Result<Json<Summary>, 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<App>, + RequestedAt(accepted_at): RequestedAt, + identity: IdentityToken, + Path(invite): Path<Id>, + Json(request): Json<AcceptRequest>, +) -> 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(), + } + } +} |
