tuwunel_api_federation/
query.rs1use axum::extract::State;
2use futures::StreamExt;
3use rand::seq::SliceRandom;
4use ruma::{
5 OwnedServerName,
6 api::federation::query::{get_profile_information, get_room_information},
7 profile::ProfileFieldValue,
8};
9use tuwunel_core::{Err, Result, err};
10
11use crate::Ruma;
12
13pub(crate) async fn get_room_information_route(
17 State(services): State<crate::State>,
18 body: Ruma<get_room_information::v1::Request>,
19) -> Result<get_room_information::v1::Response> {
20 let room_id = services
21 .alias
22 .resolve_local_alias(&body.room_alias)
23 .await
24 .map_err(|_| err!(Request(NotFound("Room alias not found."))))?;
25
26 let mut servers: Vec<OwnedServerName> = services
27 .state_cache
28 .room_servers(&room_id)
29 .map(ToOwned::to_owned)
30 .collect()
31 .await;
32
33 servers.sort_unstable();
34 servers.dedup();
35
36 servers.shuffle(&mut rand::rng());
37
38 if let Some(server_index) = servers
40 .iter()
41 .position(|server| server == services.globals.server_name())
42 {
43 servers.swap_remove(server_index);
44 servers.insert(0, services.globals.server_name().to_owned());
45 }
46
47 Ok(get_room_information::v1::Response { room_id, servers })
48}
49
50pub(crate) async fn get_profile_information_route(
55 State(services): State<crate::State>,
56 body: Ruma<get_profile_information::v1::Request>,
57) -> Result<get_profile_information::v1::Response> {
58 if !services
59 .server
60 .config
61 .profile
62 .allow_inbound_federation_lookups
63 {
64 return Err!(Request(Forbidden(
65 "Profile lookup over federation is not allowed on this homeserver.",
66 )));
67 }
68
69 if !services.globals.user_is_local(&body.user_id) {
70 return Err!(Request(InvalidParam("User does not belong to this server.",)));
71 }
72
73 if !services.users.exists(&body.user_id).await {
74 return Err!(Request(NotFound("Profile was not found.")));
75 }
76
77 match &body.field {
78 | Some(field) => {
79 let value = services
80 .profile
81 .profile_key(&body.user_id, field)
82 .await
83 .ok();
84
85 let response = value
86 .map(|value| ProfileFieldValue::new(field.as_str(), value))
87 .transpose()
88 .map_err(|_| {
89 err!(Database(
90 error!(user_id = %body.user_id, key = %field, "Invalid json in database profile value")
91 ))
92 })?
93 .into_iter()
94 .collect();
95
96 Ok(response)
97 },
98 | None => {
99 let response = services
100 .profile
101 .all_profile_keys(&body.user_id)
102 .collect::<_>()
103 .await;
104
105 Ok(response)
106 },
107 }
108}