use axum::{Json, extract::State, response::IntoResponse}; use conduwuit::{Error, Result}; use futures::StreamExt; use ruma::api::client::{ discovery::{ discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo}, discover_support::{self, Contact}, }, error::ErrorKind, }; use crate::Ruma; /// # `GET /.well-known/matrix/client` /// /// Returns the .well-known URL if it is configured, otherwise returns 404. pub(crate) async fn well_known_client( State(services): State, _body: Ruma, ) -> Result { let client_url = match services.config.well_known.client.as_ref() { | Some(url) => url.to_string(), | None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")), }; Ok(discover_homeserver::Response { homeserver: HomeserverInfo { base_url: client_url.clone() }, identity_server: None, sliding_sync_proxy: Some(SlidingSyncProxyInfo { url: client_url }), tile_server: None, }) } /// # `GET /.well-known/matrix/support` /// /// Server support contact and support page of a homeserver's domain. /// Implements MSC1929 for server discovery. /// If no configuration is set, uses admin users as contacts. pub(crate) async fn well_known_support( State(services): State, _body: Ruma, ) -> Result { let support_page = services .config .well_known .support_page .as_ref() .map(ToString::to_string); let email_address = services.config.well_known.support_email.clone(); let matrix_id = services.config.well_known.support_mxid.clone(); // TODO: support defining multiple contacts in the config let mut contacts: Vec = vec![]; let role_value = services .config .well_known .support_role .clone() .unwrap_or_else(|| "m.role.admin".to_owned().into()); // Add configured contact if at least one contact method is specified if email_address.is_some() || matrix_id.is_some() { contacts.push(Contact { role: role_value.clone(), email_address: email_address.clone(), matrix_id: matrix_id.clone(), }); } // Try to add admin users as contacts if no contacts are configured if contacts.is_empty() { if let Ok(admin_room) = services.admin.get_admin_room().await { let admin_users = services.rooms.state_cache.room_members(&admin_room); let mut stream = admin_users; while let Some(user_id) = stream.next().await { // Skip server user if *user_id == services.globals.server_user { break; } contacts.push(Contact { role: role_value.clone(), email_address: None, matrix_id: Some(user_id.to_owned()), }); } } } if contacts.is_empty() && support_page.is_none() { // No admin room, no configured contacts, and no support page return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); } Ok(discover_support::Response { contacts, support_page }) } /// # `GET /client/server.json` /// /// Endpoint provided by sliding sync proxy used by some clients such as Element /// Web as a non-standard health check. pub(crate) async fn syncv3_client_server_json( State(services): State, ) -> Result { let server_url = match services.config.well_known.client.as_ref() { | Some(url) => url.to_string(), | None => match services.config.well_known.server.as_ref() { | Some(url) => url.to_string(), | None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")), }, }; Ok(Json(serde_json::json!({ "server": server_url, "version": conduwuit::version(), }))) }