tuwunel_service/account_data/
mod.rs1mod direct;
7mod push_rules;
8mod room_tags;
9
10use std::sync::Arc;
11
12use futures::{Stream, StreamExt, TryFutureExt, pin_mut};
13use ruma::{
14 RoomId, UserId,
15 events::{
16 AnyGlobalAccountDataEvent, AnyRawAccountDataEvent, AnyRoomAccountDataEvent,
17 GlobalAccountDataEventType, RoomAccountDataEventType,
18 },
19 push::{RuleKind, Ruleset},
20 serde::Raw,
21};
22use serde::Deserialize;
23use serde_json::json;
24use tuwunel_core::{
25 Err, Result, at, err, implement,
26 utils::{ReadyExt, TryReadyExt, result::LogErr, stream::TryIgnore},
27};
28use tuwunel_database::{Deserialized, Handle, Ignore, Interfix, Json, Map};
29
30pub const MAX_RULES: usize = 10_000;
36
37pub const MAX_RULE_ID_BYTES: usize = 300;
42
43pub const MAX_RULE_BYTES: usize = 1024;
47
48pub struct Service {
54 services: Arc<crate::services::OnceServices>,
55 db: Data,
56}
57
58struct Data {
59 roomuserdataid_accountdata: Arc<Map>,
60 roomusertype_roomuserdataid: Arc<Map>,
61}
62
63impl crate::Service for Service {
64 fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
65 Ok(Arc::new(Self {
66 services: args.services.clone(),
67 db: Data {
68 roomuserdataid_accountdata: args.db["roomuserdataid_accountdata"].clone(),
69 roomusertype_roomuserdataid: args.db["roomusertype_roomuserdataid"].clone(),
70 },
71 }))
72 }
73
74 fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
75}
76
77#[must_use]
84pub fn admits_rule(ruleset: &Ruleset, kind: RuleKind, rule_id: &str) -> bool {
85 rule_id.len() <= MAX_RULE_ID_BYTES
86 && (ruleset.get(kind, rule_id).is_some()
87 || ruleset.iter().take(MAX_RULES).count() < MAX_RULES)
88}
89
90#[implement(Service)]
99pub async fn update(
100 &self,
101 room_id: Option<&RoomId>,
102 user_id: &UserId,
103 event_type: RoomAccountDataEventType,
104 data: &serde_json::Value,
105) -> Result {
106 if data.get("type").is_none() || data.get("content").is_none() {
107 return Err!(Request(InvalidParam("Account data doesn't have all required fields.")));
108 }
109
110 let count = self.services.globals.next_count();
111 let roomuserdataid = (room_id, user_id, *count, &event_type);
112 let key = (room_id, user_id, &event_type);
113 let prev = self
114 .db
115 .roomusertype_roomuserdataid
116 .qry(&key)
117 .await;
118
119 let mut txn = self.services.db.txn();
120
121 txn.put(&self.db.roomuserdataid_accountdata, roomuserdataid, Json(data));
122 txn.put(&self.db.roomusertype_roomuserdataid, key, roomuserdataid);
123
124 if let Ok(prev) = prev {
125 txn.del_raw(&self.db.roomuserdataid_accountdata, prev);
126 }
127
128 txn.execute();
129
130 Ok(())
131}
132
133#[implement(Service)]
142pub async fn delete(
143 &self,
144 room_id: Option<&RoomId>,
145 user_id: &UserId,
146 event_type: RoomAccountDataEventType,
147) -> Result {
148 let tombstone = json!({
149 "type": event_type.to_string(),
150 "content": {},
151 });
152
153 self.update(room_id, user_id, event_type, &tombstone)
154 .await
155}
156
157#[implement(Service)]
163pub async fn get_global<T>(&self, user_id: &UserId, kind: GlobalAccountDataEventType) -> Result<T>
164where
165 T: for<'de> Deserialize<'de>,
166{
167 self.get_raw(None, user_id, &kind.to_string())
168 .await
169 .deserialized()
170}
171
172#[implement(Service)]
178pub async fn get_room<T>(
179 &self,
180 room_id: &RoomId,
181 user_id: &UserId,
182 kind: RoomAccountDataEventType,
183) -> Result<T>
184where
185 T: for<'de> Deserialize<'de>,
186{
187 self.get_raw(Some(room_id), user_id, &kind.to_string())
188 .await
189 .deserialized()
190}
191
192#[implement(Service)]
197pub async fn get_raw(
198 &self,
199 room_id: Option<&RoomId>,
200 user_id: &UserId,
201 kind: &str,
202) -> Result<Handle<'_>> {
203 let key = (room_id, user_id, kind.to_owned());
204 self.db
205 .roomusertype_roomuserdataid
206 .qry(&key)
207 .and_then(|roomuserdataid| {
208 self.db
209 .roomuserdataid_accountdata
210 .get(&roomuserdataid)
211 })
212 .await
213}
214
215#[implement(Service)]
220pub fn changes_since<'a>(
221 &'a self,
222 room_id: Option<&'a RoomId>,
223 user_id: &'a UserId,
224 since: u64,
225 to: Option<u64>,
226) -> impl Stream<Item = AnyRawAccountDataEvent> + Send + 'a {
227 self.changes_since_fallible(room_id, user_id, since, to)
228 .map(LogErr::log_err)
229 .ignore_err()
230}
231
232#[implement(Service)]
238pub fn changes_since_fallible<'a>(
239 &'a self,
240 room_id: Option<&'a RoomId>,
241 user_id: &'a UserId,
242 since: u64,
243 to: Option<u64>,
244) -> impl Stream<Item = Result<AnyRawAccountDataEvent>> + Send + 'a {
245 type Key<'a> = (Option<&'a RoomId>, &'a UserId, u64, Ignore);
246
247 let first_possible = (room_id, user_id, since.saturating_add(1));
249
250 self.db
251 .roomuserdataid_accountdata
252 .stream_from(&first_possible)
253 .ready_try_take_while(move |((room_id_, user_id_, count, _), _): &(Key<'_>, _)| {
254 Ok(room_id == *room_id_ && user_id == *user_id_ && to.is_none_or(|to| *count <= to))
255 })
256 .ready_and_then(move |(_, v)| {
257 match room_id {
258 | Some(_) => serde_json::from_slice::<Raw<AnyRoomAccountDataEvent>>(v)
259 .map(AnyRawAccountDataEvent::Room),
260 | None => serde_json::from_slice::<Raw<AnyGlobalAccountDataEvent>>(v)
261 .map(AnyRawAccountDataEvent::Global),
262 }
263 .map_err(|e| err!(Database("Database contains invalid account data: {e}")))
264 })
265}
266
267#[implement(Service)]
272pub async fn erase_user(&self, user_id: &UserId, room_id: Option<&RoomId>) {
273 let prefix = (room_id, user_id, Interfix);
274 let mut txn = self.services.db.txn();
275
276 self.db
277 .roomuserdataid_accountdata
278 .keys_prefix_raw(&prefix)
279 .ignore_err()
280 .ready_for_each(|key| txn.del_raw(&self.db.roomuserdataid_accountdata, key))
281 .await;
282
283 self.db
284 .roomusertype_roomuserdataid
285 .keys_prefix_raw(&prefix)
286 .ignore_err()
287 .ready_for_each(|key| txn.del_raw(&self.db.roomusertype_roomuserdataid, key))
288 .await;
289
290 txn.execute();
291}
292
293#[implement(Service)]
298pub async fn last_count<'a>(
299 &'a self,
300 room_id: Option<&'a RoomId>,
301 user_id: &'a UserId,
302 upper: Option<u64>,
303) -> Result<u64> {
304 type Key<'a> = (Option<&'a RoomId>, &'a UserId, u64, Ignore);
305
306 let upper = upper.unwrap_or(u64::MAX);
307 let key = (room_id, user_id, upper, Interfix);
308 let keys = self
309 .db
310 .roomuserdataid_accountdata
311 .rev_keys_from(&key)
312 .ignore_err()
313 .ready_take_while(move |(room_id_, user_id_, ..): &Key<'_>| {
314 room_id == *room_id_ && user_id == *user_id_
315 })
316 .map(at!(2));
317
318 pin_mut!(keys);
319 keys.next()
320 .await
321 .ok_or_else(|| err!(Request(NotFound("No account data found."))))
322}