Skip to main content

tuwunel_api/client/room/
initial_sync.rs

1use axum::extract::State;
2use futures::{FutureExt, StreamExt, TryFutureExt, TryStreamExt, future::try_join5};
3use ruma::{
4	api::client::room::initial_sync::v3::{PaginationChunk, Request, Response},
5	events::AnyRawAccountDataEvent,
6};
7use tuwunel_core::{
8	Err, Event, Result, at, extract_variant,
9	matrix::PduCount,
10	utils::stream::{ReadyExt, TryTools},
11};
12
13use crate::Ruma;
14
15const LIMIT_MAX: usize = 50;
16
17/// GET `/_matrix/client/v3/rooms/{roomId}/initialSync`
18pub(crate) async fn room_initial_sync_route(
19	State(services): State<crate::State>,
20	body: Ruma<Request>,
21) -> Result<Response> {
22	let room_id = &body.room_id;
23
24	if !services
25		.state_accessor
26		.user_can_see_state_events(body.sender_user(), room_id)
27		.await
28	{
29		return Err!(Request(Forbidden("No room preview available.")));
30	}
31
32	let next_batch = services.globals.current_count();
33
34	let visibility = services.directory.visibility(room_id).map(Ok);
35
36	let membership = services
37		.state_cache
38		.user_membership(body.sender_user(), room_id)
39		.map(Ok);
40
41	let state = services
42		.state_accessor
43		.room_state_full_pdus(room_id)
44		.map_ok(Event::into_format)
45		.try_collect::<Vec<_>>();
46
47	let limit = LIMIT_MAX;
48	let events = services
49		.timeline
50		.pdus_rev(None, room_id, Some(PduCount::Normal(next_batch).saturating_add(1)))
51		.try_take(limit)
52		.try_collect()
53		.map_ok(|mut vec: Vec<_>| {
54			vec.reverse();
55			vec
56		});
57
58	let account_data = services
59		.account_data
60		.changes_since(Some(room_id), body.sender_user(), 0, Some(next_batch))
61		.ready_filter_map(|e| extract_variant!(e, AnyRawAccountDataEvent::Room))
62		.collect::<Vec<_>>()
63		.map(Ok);
64
65	let (membership, visibility, state, events, account_data) =
66		try_join5(membership, visibility, state, events, account_data)
67			.boxed()
68			.await?;
69
70	Ok(Response {
71		room_id: room_id.to_owned(),
72		membership,
73		visibility: visibility.into(),
74		account_data: Some(account_data),
75		state: state.into(),
76		messages: PaginationChunk {
77			start: events
78				.first()
79				.map(at!(0))
80				.as_ref()
81				.map(ToString::to_string),
82
83			end: events
84				.last()
85				.map(at!(0))
86				.as_ref()
87				.map(ToString::to_string)
88				.unwrap_or_default(),
89
90			chunk: events
91				.into_iter()
92				.map(at!(1))
93				.map(Event::into_format)
94				.collect(),
95		}
96		.into(),
97	})
98}