tuwunel_api/client/
profile.rs1use axum::extract::State;
2use futures::StreamExt;
3use ruma::{
4 api::client::profile::{
5 PropagateTo, delete_profile_field, get_profile, get_profile_field, set_profile_field,
6 },
7 profile::ProfileFieldValue,
8};
9use tuwunel_core::{Err, Result, err};
10use tuwunel_service::{presence::Ping, profile::Propagation};
11
12use crate::{ClientIp, Ruma};
13
14pub(super) fn resolve_propagation(propagate_to: &PropagateTo) -> Propagation {
19 match propagate_to {
20 | PropagateTo::Unchanged => Propagation::Unchanged,
21 | PropagateTo::None => Propagation::None,
22 | _ => Propagation::All,
23 }
24}
25
26pub(crate) async fn get_profile_route(
33 State(services): State<crate::State>,
34 body: Ruma<get_profile::v3::Request>,
35) -> Result<get_profile::v3::Response> {
36 if !services.globals.user_is_local(&body.user_id) {
37 services
38 .profile
39 .fetch_remote_profile(&body.user_id)
40 .await?;
41 }
42
43 if !services.users.exists(&body.user_id).await {
44 return Err!(Request(NotFound("Profile was not found.")));
47 }
48
49 let response = services
50 .profile
51 .all_profile_keys(&body.user_id)
52 .collect()
53 .await;
54
55 Ok(response)
56}
57
58pub(crate) async fn get_profile_field_route(
65 State(services): State<crate::State>,
66 body: Ruma<get_profile_field::v3::Request>,
67) -> Result<get_profile_field::v3::Response> {
68 if !services.globals.user_is_local(&body.user_id) {
69 services
70 .profile
71 .fetch_remote_profile(&body.user_id)
72 .await?;
73 }
74
75 if !services.users.exists(&body.user_id).await {
76 return Err!(Request(NotFound("Profile was not found.")));
79 }
80
81 let value = services
82 .profile
83 .profile_key(&body.user_id, &body.field)
84 .await?;
85
86 let profile_value = ProfileFieldValue::new(body.field.as_str(), value).map_err(|_| {
87 err!(Database(
88 error!(user_id = %body.user_id, key = %body.field, "Invalid json in database profile value")
89 ))
90 })?;
91
92 Ok(get_profile_field::v3::Response { value: Some(profile_value) })
93}
94
95pub(crate) async fn set_profile_field_route(
103 State(services): State<crate::State>,
104 ClientIp(client): ClientIp,
105 body: Ruma<set_profile_field::v3::Request>,
106) -> Result<set_profile_field::v3::Response> {
107 let sender_user = body.sender_user();
108
109 if *sender_user != body.user_id
110 && !body
111 .appservice_info
112 .as_ref()
113 .is_some_and(|registration| registration.is_user_match(&body.user_id))
114 {
115 return Err!(Request(Forbidden("You cannot update the profile of another user")));
116 }
117
118 let propagation = resolve_propagation(&body.propagate_to);
119
120 services
121 .profile
122 .set_profile_keys(
123 &body.user_id,
124 &[(body.value.field_name(), Some(body.value.value().into_owned()))],
125 Some(propagation),
126 )
127 .await?;
128
129 let ping = Ping {
131 device_id: body.sender_device.as_deref(),
132 client_ip: Some(client),
133 appservice: body.appservice_info.as_ref(),
134 ..Default::default()
135 };
136
137 services
138 .presence
139 .maybe_ping_presence(&body.user_id, ping)
140 .await?;
141
142 Ok(set_profile_field::v3::Response {})
143}
144
145pub(crate) async fn delete_profile_field_route(
151 State(services): State<crate::State>,
152 ClientIp(client): ClientIp,
153 body: Ruma<delete_profile_field::v3::Request>,
154) -> Result<delete_profile_field::v3::Response> {
155 let sender_user = body.sender_user();
156
157 if *sender_user != body.user_id
158 && !body
159 .appservice_info
160 .as_ref()
161 .is_some_and(|registration| registration.is_user_match(&body.user_id))
162 {
163 return Err!(Request(Forbidden("You cannot update the profile of another user")));
164 }
165
166 let propagation = resolve_propagation(&body.propagate_to);
167
168 services
169 .profile
170 .set_profile_keys(&body.user_id, &[(body.field.clone(), None)], Some(propagation))
171 .await?;
172
173 let ping = Ping {
175 device_id: body.sender_device.as_deref(),
176 client_ip: Some(client),
177 appservice: body.appservice_info.as_ref(),
178 ..Default::default()
179 };
180
181 services
182 .presence
183 .maybe_ping_presence(&body.user_id, ping)
184 .await?;
185
186 Ok(delete_profile_field::v3::Response {})
187}