use axum::body::Body; use axum::http::{HeaderMap, HeaderValue, 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: String, #[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<(Body, HeaderMap), StatusCode> { log::info!("Handling a WebFinger request!"); 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_headers = HeaderMap::new(); 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(", ") { response_headers.insert("Content-Type", mime.parse().unwrap()); 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.r#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 body = rd. return Ok(rd, response_headers) }, "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 { r#type: prop.rel, // TODO: Ah si chiama type? value: prop.value, }) .collect(); let links = links .into_iter() .map(|link| ResourceDescriptorLinkXRD { rel: link.rel, r#type: link.r#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, }; return Ok(StatusCode::OK) }, _ => { continue; } } } Err(StatusCode::NOT_ACCEPTABLE) }