1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
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 <https://w3c.github.io/push-api/#dom-pushsubscription-tojson>.
#[derive(Clone, serde::Deserialize)]
pub struct Subscription {
endpoint: String,
keys: Keys,
}
// This structure is described in <https://w3c.github.io/push-api/#dom-pushsubscription-tojson>.
#[derive(Clone, serde::Deserialize)]
pub struct Keys {
p256dh: String,
auth: String,
}
pub async fn handler<P>(
State(push): State<Push<P>>,
identity: Identity,
Json(request): Json<Request>,
) -> Result<StatusCode, Error> {
let Request {
subscription,
vapid,
} = request;
push.subscribe(&identity, &subscription.into(), &vapid)
.await?;
Ok(StatusCode::CREATED)
}
impl From<Subscription> 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,
}
|