use actix_web::{get, web, HttpResponse}; use regex::Regex; use tokio_postgres::GenericClient; use crate::activitypub::views::get_actor_url; use crate::activitypub::constants::ACTIVITY_CONTENT_TYPE; use crate::config::{Config, Instance}; use crate::database::{Pool, get_database_client}; use crate::errors::HttpError; use crate::models::users::queries::is_registered_user; use super::types::{ JRD_CONTENT_TYPE, WebfingerQueryParams, Link, JsonResourceDescriptor, }; async fn get_user_info( db_client: &impl GenericClient, instance: Instance, query_params: WebfingerQueryParams, ) -> Result { // Parse 'acct' URI // https://datatracker.ietf.org/doc/html/rfc7565#section-7 let uri_regexp = Regex::new(r"acct:(?P\w+)@(?P.+)").unwrap(); let uri_caps = uri_regexp.captures(&query_params.resource) .ok_or(HttpError::ValidationError("invalid query target".into()))?; let username = uri_caps.name("user") .ok_or(HttpError::ValidationError("invalid query target".into()))? .as_str(); let instance_host = uri_caps.name("instance") .ok_or(HttpError::ValidationError("invalid query target".into()))? .as_str(); if instance_host != instance.host() { // Wrong instance return Err(HttpError::NotFoundError("user")); } if !is_registered_user(db_client, &username).await? { return Err(HttpError::NotFoundError("user")); } let actor_url = get_actor_url(&instance.url(), &username); let link = Link { rel: "self".to_string(), link_type: Some(ACTIVITY_CONTENT_TYPE.to_string()), href: Some(actor_url), }; let jrd = JsonResourceDescriptor { subject: query_params.resource, links: vec![link], }; Ok(jrd) } #[get("/.well-known/webfinger")] pub async fn get_descriptor( config: web::Data, db_pool: web::Data, query_params: web::Query, ) -> Result { let db_client = &**get_database_client(&db_pool).await?; let jrd = get_user_info( db_client, config.instance(), query_params.into_inner(), ).await?; let response = HttpResponse::Ok() .content_type(JRD_CONTENT_TYPE) .json(jrd); Ok(response) }