rustical_caldav/calendar/methods/
post.rs1use crate::Error;
2use crate::calendar::CalendarResourceService;
3use crate::calendar::resource::CalendarResource;
4use axum::extract::{Path, State};
5use axum::response::{IntoResponse, Response};
6use http::{HeaderMap, HeaderValue, StatusCode, header};
7use rustical_dav::privileges::UserPrivilege;
8use rustical_dav::resource::Resource;
9use rustical_dav_push::register::PushRegister;
10use rustical_dav_push::{DavPushStore, Subscription};
11use rustical_store::CalendarStore;
12use rustical_store::auth::Principal;
13use rustical_xml::XmlDocument;
14use tracing::instrument;
15
16#[instrument(skip(resource_service))]
17pub async fn route_post<C: CalendarStore, DP: DavPushStore>(
18 Path((principal, cal_id)): Path<(String, String)>,
19 user: Principal,
20 State(resource_service): State<CalendarResourceService<C, DP>>,
21 body: String,
22) -> Result<Response, Error> {
23 if !user.is_principal(&principal) {
24 return Err(Error::Unauthorized);
25 }
26
27 let calendar = resource_service
28 .cal_store
29 .get_calendar(&principal, &cal_id, false)
30 .await?;
31 let vapid_pubkey = resource_service
32 .dav_push_store
33 .get_vapid_pubkey_b64()
34 .await?
35 .clone();
36 let calendar_resource = CalendarResource {
37 cal: calendar,
38 read_only: true,
39 vapid_pubkey,
40 };
41
42 if !calendar_resource
43 .get_user_privileges(&user)?
44 .has(&UserPrivilege::Read)
45 {
46 return Err(Error::Unauthorized);
47 }
48
49 let request = PushRegister::parse_str(&body)?;
50 let sub_id = uuid::Uuid::new_v4().to_string();
51
52 let expires = if let Some(expires) = request.expires {
53 chrono::DateTime::parse_from_rfc2822(&expires).map_err(Error::from)?
54 } else {
55 chrono::Utc::now().fixed_offset() + chrono::Duration::weeks(1)
56 };
57
58 let subscription = Subscription {
59 id: sub_id.clone(),
60 push_resource: request
61 .subscription
62 .web_push_subscription
63 .push_resource
64 .clone(),
65 topic: calendar_resource.cal.push_topic,
66 expiration: expires.naive_local(),
67 public_key: request
68 .subscription
69 .web_push_subscription
70 .subscription_public_key
71 .key,
72 public_key_type: request
73 .subscription
74 .web_push_subscription
75 .subscription_public_key
76 .ty,
77 auth_secret: request.subscription.web_push_subscription.auth_secret,
78 };
79 resource_service
80 .dav_push_store
81 .upsert_subscription(subscription)
82 .await?;
83
84 let location = format!("/push_subscription/{sub_id}");
86 Ok((
87 StatusCode::CREATED,
88 HeaderMap::from_iter([
89 (header::LOCATION, HeaderValue::from_str(&location).unwrap()),
90 (
91 header::EXPIRES,
92 HeaderValue::from_str(&expires.to_rfc2822()).unwrap(),
93 ),
94 ]),
95 )
96 .into_response())
97}