Skip to main content

tuwunel_service/rooms/delete/
mod.rs

1use std::sync::Arc;
2
3use futures::{FutureExt, StreamExt};
4use ruma::{OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomId};
5use serde::{Deserialize, Serialize};
6use tuwunel_core::{Result, debug, result::LogErr, trace, utils::future::BoolExt, warn};
7
8use crate::rooms::timeline::RoomMutexGuard;
9
10pub struct Service {
11	services: Arc<crate::services::OnceServices>,
12}
13
14/// Records local-user eviction results and aliases targeted for removal.
15///
16/// Its serialized layout matches Synapse's `ShutdownRoom`.
17#[derive(Clone, Debug, Default, Serialize, Deserialize)]
18pub struct ShutdownRoom {
19	pub kicked_users: Vec<OwnedUserId>,
20	pub failed_to_kick_users: Vec<OwnedUserId>,
21	pub local_aliases: Vec<OwnedRoomAliasId>,
22	pub new_room_id: Option<OwnedRoomId>,
23}
24
25impl crate::Service for Service {
26	fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
27		Ok(Arc::new(Self { services: args.services.clone() }))
28	}
29
30	fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
31}
32
33impl Service {
34	pub async fn delete_if_empty_local(&self, room_id: &RoomId, state_lock: RoomMutexGuard) {
35		debug_assert!(
36			self.services.config.delete_rooms_after_leave,
37			"Caller must checking if delete_rooms_after_leave configured."
38		);
39
40		let has_local_users = self
41			.services
42			.state_cache
43			.local_users_in_room(room_id)
44			.boxed()
45			.into_future()
46			.map(|(next, ..)| next.as_ref().is_some());
47
48		let has_local_invites = self
49			.services
50			.state_cache
51			.local_users_invited_to_room(room_id)
52			.boxed()
53			.into_future()
54			.map(|(next, ..)| next.as_ref().is_some());
55
56		if has_local_users.or(has_local_invites).await {
57			trace!(?room_id, "Not deleting with local joined or invited");
58			return;
59		}
60
61		debug!(?room_id, "Preparing to delete room...");
62
63		self.services
64			.delete
65			.delete_room(room_id, false, state_lock)
66			.boxed()
67			.await
68			.expect("unhandled error during room deletion");
69	}
70
71	pub async fn delete_room(
72		&self,
73		room_id: &RoomId,
74		force: bool,
75		state_lock: RoomMutexGuard,
76	) -> Result<ShutdownRoom> {
77		let summary = self.shutdown_room(room_id, &state_lock).await;
78
79		self.purge_room(room_id, force, &state_lock).await;
80
81		debug!(?room_id, "Successfully deleted room from our database");
82
83		Ok(summary)
84	}
85
86	/// Evicts every local user, strips the room's local aliases, and
87	/// unpublishes it from the directory, returning the shutdown summary. The
88	/// admin delete runs this phase alone when `purge` is false.
89	pub async fn shutdown_room(
90		&self,
91		room_id: &RoomId,
92		state_lock: &RoomMutexGuard,
93	) -> ShutdownRoom {
94		debug!(?room_id, "Making all local users leave the room and forgetting it");
95		let (kicked_users, failed_to_kick_users) = self
96			.services
97			.state_cache
98			.local_users_in_room(room_id)
99			.map(ToOwned::to_owned)
100			.fold((Vec::new(), Vec::new()), async |(mut kicked, mut failed), user_id| {
101				match self
102					.services
103					.membership
104					.leave(&user_id, room_id, Some("Room Deleted".into()), true, state_lock)
105					.await
106				{
107					| Ok(()) => kicked.push(user_id),
108					| Err(e) => {
109						warn!(%e, "Failed to leave room");
110						failed.push(user_id);
111					},
112				}
113
114				(kicked, failed)
115			})
116			.await;
117
118		debug!("Deleting all our room aliases for the room");
119		let local_aliases = self
120			.services
121			.alias
122			.local_aliases_for_room(room_id)
123			.map(ToOwned::to_owned)
124			.collect::<Vec<_>>()
125			.await;
126
127		for alias in &local_aliases {
128			self.services
129				.alias
130				.remove_alias(alias)
131				.await
132				.log_err()
133				.ok();
134		}
135
136		debug!("Removing/unpublishing room from our room directory");
137		self.services.directory.set_not_public(room_id);
138
139		ShutdownRoom {
140			kicked_users,
141			failed_to_kick_users,
142			local_aliases,
143			new_room_id: None,
144		}
145	}
146
147	/// Wipes the room's storage. `force` widens the erasure of local users'
148	/// left-state (it is not Synapse's `force_purge`).
149	async fn purge_room(&self, room_id: &RoomId, force: bool, state_lock: &RoomMutexGuard) {
150		debug!("Deleting room's threads from database");
151		self.services
152			.threads
153			.delete_all_rooms_threads(room_id)
154			.await
155			.log_err()
156			.ok();
157
158		debug!("Deleting all the room's search token IDs from our database");
159		self.services
160			.search
161			.delete_all_search_tokenids_for_room(room_id)
162			.await
163			.log_err()
164			.ok();
165
166		debug!("Deleting all room's forward extremities from our database");
167		self.services
168			.state
169			.delete_all_rooms_forward_extremities(room_id)
170			.await
171			.log_err()
172			.ok();
173
174		debug!("Deleting all the room's event (PDU) references");
175		self.services
176			.pdu_metadata
177			.delete_all_referenced_for_room(room_id)
178			.await
179			.log_err()
180			.ok();
181
182		debug!("Deleting all the room's typed relation index entries");
183		self.services
184			.pdu_metadata
185			.delete_all_relatesto_typed_for_room(room_id)
186			.await
187			.log_err()
188			.ok();
189
190		debug!("Deleting all the room's member counts");
191		self.services
192			.state_cache
193			.delete_room_join_counts(room_id, force)
194			.await
195			.log_err()
196			.ok();
197
198		debug!("Deleting all the room's private read receipts");
199		self.services
200			.read_receipt
201			.delete_all_read_receipts(room_id)
202			.await
203			.log_err()
204			.ok();
205
206		debug!("Deleting the room's last notifications read.");
207		self.services
208			.pusher
209			.delete_room_notification_read(room_id)
210			.await
211			.log_err()
212			.ok();
213
214		debug!("Deleting room state hash from our database");
215		self.services
216			.state
217			.delete_room_shortstatehash(room_id, state_lock)
218			.log_err()
219			.ok();
220
221		debug!("Deleting PDUs");
222		self.services
223			.timeline
224			.delete_pdus(room_id)
225			.await
226			.log_err()
227			.ok();
228
229		debug!("Deleting internal room ID from our database");
230		self.services
231			.short
232			.delete_shortroomid(room_id)
233			.await
234			.log_err()
235			.ok();
236	}
237}