use axum::{ extract::{Json, State}, http::StatusCode, response::{IntoResponse, Response}, }; use p256::ecdsa::VerifyingKey; use web_push::SubscriptionInfo; use crate::{ error::Internal, push::{app, app::Push}, token::extract::Identity, }; #[cfg(test)] mod test; #[derive(Clone, serde::Deserialize)] pub struct Request { subscription: Subscription, #[serde(with = "crate::vapid::ser::key")] vapid: VerifyingKey, } // This structure is described in . #[derive(Clone, serde::Deserialize)] pub struct Subscription { endpoint: String, keys: Keys, } // This structure is described in . #[derive(Clone, serde::Deserialize)] pub struct Keys { p256dh: String, auth: String, } pub async fn handler

( State(push): State>, identity: Identity, Json(request): Json, ) -> Result { let Request { subscription, vapid, } = request; push.subscribe(&identity, &subscription.into(), &vapid) .await?; Ok(StatusCode::CREATED) } impl From for SubscriptionInfo { fn from(request: Subscription) -> Self { let Subscription { endpoint, keys: Keys { p256dh, auth }, } = request; SubscriptionInfo::new(endpoint, p256dh, auth) } } #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct Error(#[from] app::SubscribeError); impl IntoResponse for Error { fn into_response(self) -> Response { let Self(err) = self; match err { app::SubscribeError::StaleVapidKey(key) => { let body = StaleVapidKey { message: err.to_string(), key, }; (StatusCode::BAD_REQUEST, Json(body)).into_response() } app::SubscribeError::Duplicate => { (StatusCode::CONFLICT, err.to_string()).into_response() } app::SubscribeError::Failed(_) => Internal::from(err).into_response(), } } } #[derive(serde::Serialize)] struct StaleVapidKey { message: String, #[serde(with = "crate::vapid::ser::key")] key: VerifyingKey, }