1mod append;
2mod backfill;
3mod build;
4mod create;
5mod pdus;
6mod purge;
7mod redact;
8
9use std::{fmt::Write, sync::Arc};
10
11use async_trait::async_trait;
12use futures::{
13 TryFutureExt, TryStreamExt,
14 future::{
15 Either::{Left, Right},
16 select_ok,
17 },
18 pin_mut,
19};
20use ruma::{
21 CanonicalJsonObject, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, RoomId,
22 UserId, api::Direction, events::room::encrypted::Relation,
23};
24use serde::Deserialize;
25pub use tuwunel_core::matrix::pdu::{PduId, RawPduId};
26use tuwunel_core::{
27 Err, Result, at, err, implement,
28 matrix::{
29 ShortEventId,
30 pdu::{PduCount, PduEvent},
31 },
32 utils::{
33 MutexMap, MutexMapGuard,
34 result::{LogErr, NotFound},
35 stream::TryReadyExt,
36 },
37 warn,
38};
39use tuwunel_database::{Database, Deserialized, Json, Map};
40
41pub use self::pdus::{PdusIterItem, bias_count};
42use crate::rooms::short::{ShortRoomId, ShortStateHash};
43
44pub struct Service {
45 services: Arc<crate::services::OnceServices>,
46 db: Data,
47 pub mutex_insert: RoomMutexMap,
52}
53
54struct Data {
55 eventid_outlierpdu: Arc<Map>,
56 eventid_pduid: Arc<Map>,
57 pduid_pdu: Arc<Map>,
58 roomid_tscount_pducount: Arc<Map>,
59 db: Arc<Database>,
60}
61
62#[derive(Deserialize)]
64struct ExtractRelatesTo {
65 #[serde(rename = "m.relates_to")]
66 relates_to: Relation,
67}
68
69#[derive(Clone, Debug, Deserialize)]
70struct ExtractEventId {
71 event_id: OwnedEventId,
72}
73#[derive(Clone, Debug, Deserialize)]
74struct ExtractRelatesToEventId {
75 #[serde(rename = "m.relates_to")]
76 relates_to: ExtractEventId,
77}
78
79#[derive(Deserialize)]
80struct ExtractBody {
81 body: Option<String>,
82}
83
84type RoomMutexMap = MutexMap<OwnedRoomId, ()>;
85pub type RoomMutexGuard = MutexMapGuard<OwnedRoomId, ()>;
86
87#[async_trait]
88impl crate::Service for Service {
89 fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
90 Ok(Arc::new(Self {
91 services: args.services.clone(),
92 db: Data {
93 eventid_outlierpdu: args.db["eventid_outlierpdu"].clone(),
94 eventid_pduid: args.db["eventid_pduid"].clone(),
95 pduid_pdu: args.db["pduid_pdu"].clone(),
96 roomid_tscount_pducount: args.db["roomid_tscount_pducount"].clone(),
97 db: args.db.clone(),
98 },
99 mutex_insert: RoomMutexMap::new(),
100 }))
101 }
102
103 async fn memory_usage(&self, out: &mut (dyn Write + Send)) -> Result {
104 let mutex_insert = self.mutex_insert.len();
105 writeln!(out, "- insert_mutex: {mutex_insert}")?;
106
107 Ok(())
108 }
109
110 fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
111}
112
113#[implement(Service)]
115#[tracing::instrument(skip(self), level = "debug")]
116pub async fn replace_pdu(&self, pdu_id: &RawPduId, pdu_json: &CanonicalJsonObject) -> Result {
117 if self.db.pduid_pdu.get(pdu_id).await.is_not_found() {
118 return Err!(Request(NotFound("PDU does not exist.")));
119 }
120
121 self.db.pduid_pdu.raw_put(pdu_id, Json(pdu_json));
122
123 Ok(())
124}
125
126#[implement(Service)]
127#[tracing::instrument(skip(self, pdu), level = "debug")]
128pub fn add_pdu_outlier(&self, event_id: &EventId, pdu: &CanonicalJsonObject) {
129 self.db
130 .eventid_outlierpdu
131 .raw_put(event_id, Json(pdu));
132}
133
134#[implement(Service)]
135#[tracing::instrument(skip(self), level = "debug")]
136pub async fn first_pdu_in_room(&self, room_id: &RoomId) -> Result<PduEvent> {
137 self.first_item_in_room(room_id).await.map(at!(1))
138}
139
140#[implement(Service)]
141#[tracing::instrument(skip(self), level = "debug")]
142#[inline]
143pub async fn latest_pdu_in_room(&self, room_id: &RoomId) -> Result<PduEvent> {
144 self.latest_item_in_room(None, room_id).await
145}
146
147#[implement(Service)]
148#[tracing::instrument(skip(self), level = "debug")]
149pub async fn first_item_in_room(&self, room_id: &RoomId) -> Result<(PduCount, PduEvent)> {
150 let pdus = self.pdus(None, room_id, None);
151
152 pin_mut!(pdus);
153 pdus.try_next()
154 .await?
155 .ok_or_else(|| err!(Request(NotFound("No PDU found in room"))))
156}
157
158#[implement(Service)]
159#[tracing::instrument(skip(self), level = "debug")]
160pub async fn latest_item_in_room(
161 &self,
162 sender_user: Option<&UserId>,
163 room_id: &RoomId,
164) -> Result<PduEvent> {
165 let pdus_rev = self.pdus_rev(sender_user, room_id, None);
166
167 pin_mut!(pdus_rev);
168 pdus_rev
169 .try_next()
170 .await?
171 .map(at!(1))
172 .ok_or_else(|| err!(Request(NotFound("No PDU's found in room"))))
173}
174
175#[implement(Service)]
179#[tracing::instrument(skip(self), level = "debug")]
180pub async fn prev_shortstatehash(
181 &self,
182 room_id: &RoomId,
183 before: PduCount,
184) -> Result<ShortStateHash> {
185 let shortroomid: ShortRoomId = self
186 .services
187 .short
188 .get_shortroomid(room_id)
189 .await
190 .map_err(|e| err!(Request(NotFound("Room {room_id:?} not found: {e:?}"))))?;
191
192 let before = PduId { shortroomid, count: before };
193
194 let prev = PduId {
195 shortroomid,
196 count: self.prev_timeline_count(&before).await?,
197 };
198
199 let shorteventid = self.get_shorteventid_from_pdu_id(&prev).await?;
200
201 self.services
202 .state
203 .get_shortstatehash(shorteventid)
204 .await
205}
206
207#[implement(Service)]
211#[tracing::instrument(skip(self), level = "debug")]
212pub async fn next_shortstatehash(
213 &self,
214 room_id: &RoomId,
215 after: PduCount,
216) -> Result<ShortStateHash> {
217 let shortroomid: ShortRoomId = self
218 .services
219 .short
220 .get_shortroomid(room_id)
221 .await
222 .map_err(|e| err!(Request(NotFound("Room {room_id:?} not found: {e:?}"))))?;
223
224 let after = PduId { shortroomid, count: after };
225
226 let next = PduId {
227 shortroomid,
228 count: self.next_timeline_count(&after).await?,
229 };
230
231 let shorteventid = self.get_shorteventid_from_pdu_id(&next).await?;
232
233 self.services
234 .state
235 .get_shortstatehash(shorteventid)
236 .await
237}
238
239#[implement(Service)]
241#[tracing::instrument(skip(self), level = "debug")]
242pub async fn get_shortstatehash(
243 &self,
244 room_id: &RoomId,
245 count: PduCount,
246) -> Result<ShortStateHash> {
247 let shortroomid: ShortRoomId = self
248 .services
249 .short
250 .get_shortroomid(room_id)
251 .await
252 .map_err(|e| err!(Request(NotFound("Room {room_id:?} not found: {e:?}"))))?;
253
254 let pdu_id = PduId { shortroomid, count };
255
256 let shorteventid = self.get_shorteventid_from_pdu_id(&pdu_id).await?;
257
258 self.services
259 .state
260 .get_shortstatehash(shorteventid)
261 .await
262}
263
264#[implement(Service)]
267#[tracing::instrument(skip(self), level = "debug")]
268pub async fn prev_timeline_count(&self, before: &PduId) -> Result<PduCount> {
269 let before = Self::pdu_count_to_id(before.shortroomid, before.count, Direction::Backward);
270
271 let pdu_ids = self
272 .db
273 .pduid_pdu
274 .rev_keys_raw_from(&before)
275 .ready_try_take_while(|pdu_id: &RawPduId| Ok(pdu_id.is_room_eq(before)))
276 .ready_and_then(|pdu_id: RawPduId| Ok(pdu_id.pdu_count()));
277
278 pin_mut!(pdu_ids);
279 pdu_ids
280 .try_next()
281 .await
282 .log_err()?
283 .ok_or_else(|| err!(Request(NotFound("No earlier PDU's found in room"))))
284}
285
286#[implement(Service)]
289#[tracing::instrument(skip(self), level = "debug")]
290pub async fn next_timeline_count(&self, after: &PduId) -> Result<PduCount> {
291 let after = Self::pdu_count_to_id(after.shortroomid, after.count, Direction::Forward);
292
293 let pdu_ids = self
294 .db
295 .pduid_pdu
296 .keys_raw_from(&after)
297 .ready_try_take_while(|pdu_id: &RawPduId| Ok(pdu_id.is_room_eq(after)))
298 .ready_and_then(|pdu_id: RawPduId| Ok(pdu_id.pdu_count()));
299
300 pin_mut!(pdu_ids);
301 pdu_ids
302 .try_next()
303 .await
304 .log_err()?
305 .ok_or(err!(Request(NotFound("No more PDU's found in room"))))
306}
307
308#[implement(Service)]
309#[tracing::instrument(skip(self), level = "debug")]
310pub async fn last_timeline_count(
311 &self,
312 sender_user: Option<&UserId>,
313 room_id: &RoomId,
314 upper_bound: Option<PduCount>,
315) -> Result<PduCount> {
316 let upper_bound = upper_bound.unwrap_or_else(PduCount::max);
317 let pdus_rev = self.pdus_rev(sender_user, room_id, None);
318
319 pin_mut!(pdus_rev);
320 let last_count = pdus_rev
321 .ready_try_skip_while(|&(pducount, _)| Ok(pducount > upper_bound))
322 .try_next()
323 .await?
324 .map(at!(0))
325 .filter(|&count| matches!(count, PduCount::Normal(_)))
326 .unwrap_or_else(PduCount::max);
327
328 Ok(last_count)
329}
330
331#[implement(Service)]
332pub async fn get_event_id_near_ts(
333 &self,
334 room_id: &RoomId,
335 ts: MilliSecondsSinceUnixEpoch,
336 dir: Direction,
337) -> Result<(MilliSecondsSinceUnixEpoch, OwnedEventId)> {
338 self.get_pdu_id_near_ts(room_id, ts, dir)
339 .and_then(async |(ts, pdu_id)| {
340 self.get_event_id_from_pdu_id(&pdu_id)
341 .map_ok(|event_id| (ts, event_id))
342 .await
343 })
344 .await
345}
346
347#[implement(Service)]
348pub async fn get_pdu_id_near_ts(
349 &self,
350 room_id: &RoomId,
351 ts: MilliSecondsSinceUnixEpoch,
352 dir: Direction,
353) -> Result<(MilliSecondsSinceUnixEpoch, PduId)> {
354 let pdu_ids = self.pdu_ids_near_ts(room_id, ts, dir);
355
356 pin_mut!(pdu_ids);
357 pdu_ids
358 .try_next()
359 .await?
360 .ok_or_else(|| err!(Request(NotFound("No event found near this timestamp."))))
361}
362
363#[implement(Service)]
364pub async fn get_pdu_near_ts(
365 &self,
366 _user_id: Option<&UserId>,
367 room_id: &RoomId,
368 ts: MilliSecondsSinceUnixEpoch,
369 dir: Direction,
370) -> Result<PdusIterItem> {
371 let pdus = self
372 .pdu_ids_near_ts(room_id, ts, dir)
373 .map_ok(|(ts, pdu_id)| (ts, pdu_id.into()))
374 .and_then(async |(_, pdu_id): (_, RawPduId)| {
375 self.get_pdu_from_id(&pdu_id)
376 .map_ok(|pdu| (pdu_id.pdu_count(), pdu))
377 .await
378 });
379
380 pin_mut!(pdus);
381 pdus.try_next()
382 .await?
383 .ok_or_else(|| err!(Request(NotFound("No event found near this timestamp."))))
384}
385
386#[implement(Service)]
387async fn count_to_id(
388 &self,
389 room_id: &RoomId,
390 count: PduCount,
391 dir: Direction,
392) -> Result<RawPduId> {
393 let shortroomid: ShortRoomId = self
394 .services
395 .short
396 .get_shortroomid(room_id)
397 .await
398 .map_err(|e| err!(Request(NotFound("Room {room_id:?} not found: {e:?}"))))?;
399
400 Ok(Self::pdu_count_to_id(shortroomid, count, dir))
401}
402
403#[implement(Service)]
404fn pdu_count_to_id(shortroomid: ShortRoomId, count: PduCount, dir: Direction) -> RawPduId {
405 let pdu_id = PduId {
407 shortroomid,
408 count: count.saturating_inc(dir),
409 };
410
411 pdu_id.into()
412}
413
414#[implement(Service)]
417pub async fn get_pdu_from_shorteventid(&self, shorteventid: ShortEventId) -> Result<PduEvent> {
418 let event_id: OwnedEventId = self
419 .services
420 .short
421 .get_eventid_from_short(shorteventid)
422 .await?;
423
424 self.get_pdu(&event_id).await
425}
426
427#[implement(Service)]
430pub async fn get_pdu(&self, event_id: &EventId) -> Result<PduEvent> { self.get(event_id).await }
431
432#[implement(Service)]
435pub async fn get_outlier_pdu(&self, event_id: &EventId) -> Result<PduEvent> {
436 self.get_outlier(event_id).await
437}
438
439#[implement(Service)]
442pub async fn get_non_outlier_pdu(&self, event_id: &EventId) -> Result<PduEvent> {
443 self.get_non_outlier(event_id).await
444}
445
446#[implement(Service)]
449pub async fn get_pdu_from_id(&self, pdu_id: &RawPduId) -> Result<PduEvent> {
450 self.get_from_id(pdu_id).await
451}
452
453#[implement(Service)]
456pub async fn get_pdu_json(&self, event_id: &EventId) -> Result<CanonicalJsonObject> {
457 self.get(event_id).await
458}
459
460#[implement(Service)]
463pub async fn get_outlier_pdu_json(&self, event_id: &EventId) -> Result<CanonicalJsonObject> {
464 self.get_outlier(event_id).await
465}
466
467#[implement(Service)]
470pub async fn get_non_outlier_pdu_json(&self, event_id: &EventId) -> Result<CanonicalJsonObject> {
471 self.get_non_outlier(event_id).await
472}
473
474#[implement(Service)]
477pub async fn get_pdu_json_from_id(&self, pdu_id: &RawPduId) -> Result<CanonicalJsonObject> {
478 self.get_from_id(pdu_id).await
479}
480
481#[implement(Service)]
484#[inline]
485pub async fn get<T>(&self, event_id: &EventId) -> Result<T>
486where
487 T: for<'de> Deserialize<'de>,
488{
489 let accepted = self.get_non_outlier(event_id);
490 let outlier = self.get_outlier(event_id);
491
492 pin_mut!(accepted, outlier);
493 select_ok([Left(accepted), Right(outlier)])
494 .await
495 .map(at!(0))
496}
497
498#[implement(Service)]
501#[inline]
502pub async fn get_outlier<T>(&self, event_id: &EventId) -> Result<T>
503where
504 T: for<'de> Deserialize<'de>,
505{
506 self.db
507 .eventid_outlierpdu
508 .get(event_id)
509 .await
510 .deserialized()
511}
512
513#[implement(Service)]
516#[inline]
517pub async fn get_non_outlier<T>(&self, event_id: &EventId) -> Result<T>
518where
519 T: for<'de> Deserialize<'de>,
520{
521 let pdu_id = self.get_pdu_id(event_id).await?;
522
523 self.get_from_id(&pdu_id).await
524}
525
526#[implement(Service)]
529#[inline]
530pub async fn get_from_id<T>(&self, pdu_id: &RawPduId) -> Result<T>
531where
532 T: for<'de> Deserialize<'de>,
533{
534 self.db.pduid_pdu.get(pdu_id).await.deserialized()
535}
536
537#[implement(Service)]
540pub async fn pdu_exists<'a>(&'a self, event_id: &'a EventId) -> bool {
541 let non_outlier = self.non_outlier_pdu_exists(event_id);
542 let outlier = self.outlier_pdu_exists(event_id);
543
544 pin_mut!(non_outlier, outlier);
545 select_ok([Left(non_outlier), Right(outlier)])
546 .await
547 .map(at!(0))
548 .is_ok()
549}
550
551#[implement(Service)]
556pub fn watch_event<'a>(&'a self, event_id: &EventId) -> impl Future<Output = ()> + Send + 'a {
557 self.db
558 .eventid_pduid
559 .watch_raw_prefix_once(event_id)
560}
561
562#[implement(Service)]
565pub async fn non_outlier_pdu_exists(&self, event_id: &EventId) -> Result {
566 let pduid = self.get_pdu_id(event_id).await?;
567
568 self.db.pduid_pdu.exists(&pduid).await
569}
570
571#[implement(Service)]
574#[inline]
575pub async fn outlier_pdu_exists(&self, event_id: &EventId) -> Result {
576 self.db.eventid_outlierpdu.exists(event_id).await
577}
578
579#[implement(Service)]
581pub async fn get_pdu_count(&self, event_id: &EventId) -> Result<PduCount> {
582 self.get_pdu_id(event_id)
583 .await
584 .map(RawPduId::pdu_count)
585}
586
587#[implement(Service)]
589pub async fn get_shorteventid_from_pdu_id(&self, pdu_id: &PduId) -> Result<ShortEventId> {
590 let event_id = self.get_event_id_from_pdu_id(pdu_id).await?;
591
592 self.services
593 .short
594 .get_shorteventid(&event_id)
595 .await
596}
597
598#[implement(Service)]
600pub async fn get_event_id_from_pdu_id(&self, pdu_id: &PduId) -> Result<OwnedEventId> {
601 let pdu_id: RawPduId = (*pdu_id).into();
602
603 self.get_pdu_from_id(&pdu_id)
604 .map_ok(|pdu| pdu.event_id)
605 .await
606}
607
608#[implement(Service)]
610pub async fn get_pdu_id_from_shorteventid(&self, shorteventid: ShortEventId) -> Result<RawPduId> {
611 let event_id: OwnedEventId = self
612 .services
613 .short
614 .get_eventid_from_short(shorteventid)
615 .await?;
616
617 self.get_pdu_id(&event_id).await
618}
619
620#[implement(Service)]
622pub async fn get_pdu_id(&self, event_id: &EventId) -> Result<RawPduId> {
623 self.db
624 .eventid_pduid
625 .get(event_id)
626 .await
627 .map(|handle| RawPduId::from(&*handle))
628}