tuwunel_api_client/alias/
directory_room.rs1use axum::extract::State;
2use futures::StreamExt;
3use rand::seq::SliceRandom;
4use ruma::{
5 OwnedServerName, RoomAliasId, RoomId, UserId,
6 api::client::alias::{create_alias, delete_alias, get_alias},
7 events::{StateEventType, room::canonical_alias::RoomCanonicalAliasEventContent},
8};
9use tuwunel_core::{Err, Result, debug, err};
10use tuwunel_matrix::pdu::PduBuilder;
11use tuwunel_service::Services;
12
13use crate::Ruma;
14
15pub(crate) async fn create_alias_route(
19 State(services): State<crate::State>,
20 body: Ruma<create_alias::v3::Request>,
21) -> Result<create_alias::v3::Response> {
22 let sender_user = body.sender_user();
23
24 services
25 .alias
26 .creation_check(sender_user, body.appservice_info.as_ref())
27 .await?;
28
29 services
30 .alias
31 .appservice_checks(&body.room_alias, &body.appservice_info)
32 .await?;
33
34 if services
37 .config
38 .rooms
39 .forbidden_alias_names
40 .is_match(body.room_alias.alias())
41 {
42 return Err!(Request(Forbidden("Room alias is forbidden.")));
43 }
44
45 if services
46 .alias
47 .resolve_local_alias(&body.room_alias)
48 .await
49 .is_ok()
50 {
51 return Err!(Conflict("Alias already exists."));
52 }
53
54 services
55 .alias
56 .set_alias_by(&body.room_alias, &body.room_id, sender_user)?;
57
58 Ok(create_alias::v3::Response::new())
59}
60
61pub(crate) async fn delete_alias_route(
69 State(services): State<crate::State>,
70 body: Ruma<delete_alias::v3::Request>,
71) -> Result<delete_alias::v3::Response> {
72 let sender_user = body.sender_user();
73 services
74 .alias
75 .appservice_checks(&body.room_alias, &body.appservice_info)
76 .await?;
77
78 let room_id = services
79 .alias
80 .remove_alias_by(&body.room_alias, sender_user)
81 .await?;
82
83 retire_canonical_alias(&services, &room_id, &body.room_alias, sender_user)
84 .await
85 .inspect_err(|e| debug!(%room_id, "Not updating canonical alias: {e}"))
86 .ok();
87
88 Ok(delete_alias::v3::Response::new())
89}
90
91pub(crate) async fn get_alias_route(
95 State(services): State<crate::State>,
96 body: Ruma<get_alias::v3::Request>,
97) -> Result<get_alias::v3::Response> {
98 let room_alias = body.body.room_alias;
99
100 let (room_id, servers) = services
101 .alias
102 .resolve_alias(&room_alias)
103 .await
104 .map_err(|_| err!(Request(NotFound("Room with alias not found."))))?;
105
106 let servers = room_available_servers(&services, &room_id, &room_alias, servers).await;
107 debug!(?room_alias, ?room_id, "available servers: {servers:?}");
108
109 Ok(get_alias::v3::Response::new(room_id, servers))
110}
111
112async fn retire_canonical_alias(
118 services: &Services,
119 room_id: &RoomId,
120 deleted: &RoomAliasId,
121 sender_user: &UserId,
122) -> Result {
123 let state_lock = services.state.mutex.lock(room_id).await;
124
125 let Ok(content) = services
126 .state_accessor
127 .room_state_get_content::<RoomCanonicalAliasEventContent>(
128 room_id,
129 &StateEventType::RoomCanonicalAlias,
130 "",
131 )
132 .await
133 else {
134 return Ok(());
135 };
136
137 if !content.aliases().any(|alias| alias == deleted) {
138 return Ok(());
139 }
140
141 let content = RoomCanonicalAliasEventContent {
142 alias: content.alias.filter(|alias| alias != deleted),
143 alt_aliases: content
144 .alt_aliases
145 .into_iter()
146 .filter(|alt| alt != deleted)
147 .collect(),
148 };
149
150 services
151 .timeline
152 .build_and_append_pdu(PduBuilder::state("", &content), sender_user, room_id, &state_lock)
153 .await
154 .map(|_| ())
155}
156
157async fn room_available_servers(
158 services: &Services,
159 room_id: &RoomId,
160 room_alias: &RoomAliasId,
161 pre_servers: Vec<OwnedServerName>,
162) -> Vec<OwnedServerName> {
163 let mut servers: Vec<OwnedServerName> = services
165 .state_cache
166 .room_servers(room_id)
167 .map(ToOwned::to_owned)
168 .collect()
169 .await;
170
171 servers.extend(pre_servers);
174
175 servers.sort_unstable();
176 servers.dedup();
177
178 servers.shuffle(&mut rand::rng());
180
181 if let Some(server_index) = servers
184 .iter()
185 .position(|server_name| services.globals.server_is_ours(server_name))
186 {
187 servers.swap(0, server_index);
188 } else if let Some(alias_server_index) = servers
189 .iter()
190 .position(|server| server == room_alias.server_name())
191 {
192 servers.swap(0, alias_server_index);
193 }
194
195 servers
196}