diff --git a/src/atproto/lexicon/mod.rs b/src/atproto/lexicon/mod.rs index 2897d7a..fb64cc9 100644 --- a/src/atproto/lexicon/mod.rs +++ b/src/atproto/lexicon/mod.rs @@ -92,12 +92,53 @@ impl From for AppError { } } -/// A record resolved from a route, along with the identity of its owner. +/// A record resolved from a route. pub struct Found { pub did: Did, pub handle: Handle, pub rkey: Rkey, pub record: T, + session: Option, + state: AppState, +} + +impl Found { + pub fn new( + did: Did, + handle: Handle, + rkey: Rkey, + record: T, + session: Option, + state: &AppState, + ) -> Self { + Self { + did, + handle, + rkey, + record, + session, + state: state.clone(), + } + } + + /// Whether the current requester owns this record. + pub fn is_owner(&self) -> bool { + self.session.as_ref().is_some_and(|s| s.did == self.did) + } +} + +impl Found { + /// Writes `self.record` back locally and to the PDS. + /// Fails with [`AppError::Forbidden`] if they're not signed in as the owner. + pub async fn save(&self) -> Result<(), AppError> { + let session = self + .session + .as_ref() + .filter(|s| s.did == self.did) + .ok_or(AppError::Forbidden)?; + self.record.update(&self.state, session, &self.rkey).await?; + Ok(()) + } } /// Lexicon `maxLength` counts UTF-8 bytes. diff --git a/src/mods.rs b/src/mods.rs index 0ff293a..fb78f7e 100644 --- a/src/mods.rs +++ b/src/mods.rs @@ -43,12 +43,9 @@ impl FromRequestParts for Found { .map_err(AppError::Internal)? .ok_or(AppError::NotFound)?; - Ok(Self { - did, - handle, - rkey, - record, - }) + let session = Option::::from_request_parts(parts, state).await?; + + Ok(Found::new(did, handle, rkey, record, session, state)) } } @@ -158,8 +155,8 @@ async fn create( Ok(Redirect::to(&format!("/{handle}/{}", listing.slug)).into_response()) } -async fn show(existing: Found, session: Option) -> Response { - let is_owner = session.is_some_and(|session| session.did == existing.did); +async fn show(existing: Found) -> Response { + let is_owner = existing.is_owner(); let listing = &existing.record; layout(html! { @@ -234,30 +231,18 @@ struct EditListingForm { } async fn save( - State(state): State, - existing: Found, - session: SessionCookie, + mut existing: Found, Form(form): Form, ) -> Result { - if session.did != existing.did { - return Err(AppError::Forbidden); - } - - let game: Game = form + existing.record.title = form.title; + existing.record.description = form.description; + existing.record.details = form.details; + existing.record.game = form .game .parse() .map_err(|e: anyhow::Error| AppError::BadRequest(e.to_string()))?; - - let mut listing = existing.record; - listing.title = form.title; - listing.description = form.description; - listing.details = form.details; - listing.game = game; - listing.category = form.category; - listing.license = form.license; - - let redirect = format!("/{}/{}", existing.handle, listing.slug); - listing.update(&state, &session, &existing.rkey).await?; - - Ok(Redirect::to(&redirect).into_response()) + existing.record.category = form.category; + existing.record.license = form.license; + existing.save().await?; + Ok(Redirect::to(&format!("/{}/{}", existing.handle, existing.record.slug)).into_response()) }