use axum::http::{HeaderMap, Response, StatusCode}; use axum_extra::extract::Query; use serde::Deserialize; use acrate_core::diesel_async::{AsyncConnection, AsyncPgConnection}; use acrate_hostmeta::jrd::ResourceDescriptorLinkJRD; use acrate_hostmeta::xrd::{ResourceDescriptorLinkXRD, ResourceDescriptorPropertyXRD}; 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 = acrate_core::meta::MetaAlias::query_matching(&mut conn, WEBFINGER_DOC, &resource) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let properties = acrate_core::meta::MetaProperty::query_matching(&mut conn, WEBFINGER_DOC, &resource) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let links = acrate_core::meta::MetaLink::query_matching(&mut conn, WEBFINGER_DOC, &resource) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; for mime in accept.split(",") { { let headers = response.headers_mut(); headers.insert("Content-Type", mime.parse().unwrap()); } 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 .into_iter() .map(|link| ResourceDescriptorLinkJRD { rel: link.rel, r#type: link.type_, href: link.href, titles: Default::default(), // TODO: Titles properties: Default::default(), // TODO: Link properties template: None, // TODO: Template }) .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 = properties .into_iter() .map(|prop| ResourceDescriptorPropertyXRD { rel: prop.rel, value: prop.value, }) .collect(); let links = links .into_iter() .map(|link| ResourceDescriptorLinkXRD { rel: link.rel, r#type: link.type_, href: link.href, titles: Default::default(), // TODO: Titles properties: Default::default(), // TODO: Link properties template: None, // TODO: Template }) .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) }