use axum::http::{HeaderMap, Response, StatusCode}; use axum_extra::extract::Query; use serde::Deserialize; use acrate_core::diesel::GroupedBy; use acrate_core::diesel_async::{AsyncConnection, AsyncPgConnection}; use acrate_core::meta::{MetaAlias, MetaLink, MetaLinkProperty, MetaLinkTitle, MetaProperty}; use acrate_hostmeta::jrd::ResourceDescriptorLinkJRD; use acrate_hostmeta::xrd::{ResourceDescriptorLinkXRD, ResourceDescriptorPropertyXRD, ResourceDescriptorTitleXRD}; use crate::config; #[derive(Debug, Clone, Deserialize)] pub struct WebfingerQuery { pub resource: Option, #[serde(default)] pub rel: Vec, } const WEBFINGER_DOC: &str = "/.well-known/webfinger"; #[axum::debug_handler] pub async fn webfinger_handler( Query(WebfingerQuery {resource, rel}): Query, headers: HeaderMap, ) -> Result, StatusCode> { log::info!("Handling a WebFinger request!"); let resource = resource.unwrap_or_else(|| "".to_string()); log::debug!("Resource is: {resource:#?}"); log::debug!("Rel is: {rel:#?}"); let accept = headers.get("Accept") .map(|v| v.to_str()) .filter(Result::is_ok) .map(|v| v.unwrap()) .unwrap_or("application/json") .to_string(); log::debug!("Accept is: {accept:#?}"); let mut response = Response::new("".to_string()); let mut conn = AsyncPgConnection::establish(config::ACRATE_WEBFINGER_DATABASE_URL()) .await .map_err(|_| StatusCode::BAD_GATEWAY)?; let aliases = MetaAlias::query_matching(&mut conn, WEBFINGER_DOC, &resource) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let properties = MetaProperty::query_matching(&mut conn, WEBFINGER_DOC, &resource) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let links = MetaLink::query_matching(&mut conn, WEBFINGER_DOC, &resource) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let link_properties = MetaLinkProperty::query_by_link(&mut conn, &links) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? .grouped_by(&links); let link_titles = MetaLinkTitle::query_by_link(&mut conn, &links) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? .grouped_by(&links); let links_full: Vec<(MetaLink, Vec, Vec)> = links .into_iter() .zip(link_properties) .zip(link_titles) .map(|((link, properties), titles)| (link, properties, titles)) .collect(); { let headers = response.headers_mut(); headers.insert( "Access-Control-Allow-Origin", "*".parse().unwrap() ); } for mime in accept.split(",") { { let headers = response.headers_mut(); headers.insert( "Content-Type", mime.parse().map_err(|_| StatusCode::BAD_REQUEST)? ); } let (mime, _params) = match mime.trim().split_once(";") { Some((mime, params)) => (mime, Some(params)), None => (mime, None), }; match mime { "*/*" | "application/json" | "application/jrd+json" => { let subject = Some(resource); let aliases = aliases .into_iter() .map(|alias| alias.alias) .collect(); let properties = properties .into_iter() .map(|prop| (prop.rel, prop.value)) .collect(); let links = links_full .into_iter() .map(|(link, properties, titles)| ResourceDescriptorLinkJRD { rel: link.rel, r#type: link.type_, href: link.href, template: link.template, properties: properties .into_iter() .map(|property| (property.rel, property.value)) .collect(), titles: titles .into_iter() .map(|title| (title.language, title.value)) .collect(), }) .collect::>(); let rd = acrate_hostmeta::jrd::ResourceDescriptorJRD { subject, aliases, properties, links, }; let json = serde_json::to_string_pretty(&rd) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; { let body = response.body_mut(); body.push_str(&json); } return Ok(response) }, "application/xml" | "application/xrd+xml" => { let subject = Some(resource); let aliases = aliases .into_iter() .map(|alias| alias.alias) .collect(); let properties: Vec = properties .into_iter() .map(|prop| ResourceDescriptorPropertyXRD { rel: prop.rel, value: prop.value, }) .collect(); let links = links_full .into_iter() .map(|(link, properties, titles)| ResourceDescriptorLinkXRD { rel: link.rel, r#type: link.type_, href: link.href, template: link.template, properties: properties .into_iter() .map(|property| ResourceDescriptorPropertyXRD { rel: property.rel, value: property.value, }) .collect(), titles: titles .into_iter() .map(|title| ResourceDescriptorTitleXRD { language: title.language, value: title.value, }) .collect(), }) .collect::>(); let rd = acrate_hostmeta::xrd::ResourceDescriptorXRD { subject, aliases, properties, links, }; let xml = quick_xml::se::to_string(&rd) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; { let body = response.body_mut(); body.push_str(""); body.push_str(&xml); } return Ok(response) }, _ => { continue; } } } Err(StatusCode::NOT_ACCEPTABLE) }