Make actor and object endpoints compatible with Mastodon fetcher

This commit is contained in:
silverpill 2021-12-29 18:36:49 +00:00
parent f9fca604a9
commit 580ba6dfba

View file

@ -1,6 +1,7 @@
use actix_web::{ use actix_web::{
get, post, web, get, post, web,
HttpRequest, HttpResponse, Scope, HttpRequest, HttpResponse, Scope,
http::HeaderMap,
}; };
use serde::Deserialize; use serde::Deserialize;
use uuid::Uuid; use uuid::Uuid;
@ -51,16 +52,18 @@ pub fn get_object_url(instance_url: &str, internal_object_id: &Uuid) -> String {
format!("{}/objects/{}", instance_url, internal_object_id) format!("{}/objects/{}", instance_url, internal_object_id)
} }
fn is_activitypub_request(request: &HttpRequest) -> bool { fn is_activitypub_request(headers: &HeaderMap) -> bool {
const CONTENT_TYPES: [&str; 5] = [ const CONTENT_TYPES: [&str; 4] = [
ACTIVITY_CONTENT_TYPE, ACTIVITY_CONTENT_TYPE,
"application/activity+json, application/ld+json", // Mastodon
"application/activity+json", "application/activity+json",
"application/ld+json", "application/ld+json",
"application/json", "application/json",
]; ];
if let Some(content_type) = request.headers().get("Accept") { if let Some(content_type) = headers.get("Accept") {
let content_type_str = content_type.to_str().unwrap_or(""); let content_type_str = content_type.to_str().ok()
// Take first content type if there are many
.and_then(|value| value.split(",").next())
.unwrap_or("");
return CONTENT_TYPES.contains(&content_type_str); return CONTENT_TYPES.contains(&content_type_str);
}; };
false false
@ -75,7 +78,7 @@ async fn actor_view(
) -> Result<HttpResponse, HttpError> { ) -> Result<HttpResponse, HttpError> {
let db_client = &**get_database_client(&db_pool).await?; let db_client = &**get_database_client(&db_pool).await?;
let user = get_user_by_name(db_client, &username).await?; let user = get_user_by_name(db_client, &username).await?;
if !is_activitypub_request(&request) { if !is_activitypub_request(&request.headers()) {
let page_url = get_profile_page_url(&config.instance_url(), &user.id); let page_url = get_profile_page_url(&config.instance_url(), &user.id);
let response = HttpResponse::Found() let response = HttpResponse::Found()
.header("Location", page_url) .header("Location", page_url)
@ -263,7 +266,7 @@ pub async fn object_view(
let post = thread.iter() let post = thread.iter()
.find(|post| post.id == internal_object_id && post.author.is_local()) .find(|post| post.id == internal_object_id && post.author.is_local())
.ok_or(HttpError::NotFoundError("post"))?; .ok_or(HttpError::NotFoundError("post"))?;
if !is_activitypub_request(&request) { if !is_activitypub_request(&request.headers()) {
let page_url = get_post_page_url(&config.instance_url(), &post.id); let page_url = get_post_page_url(&config.instance_url(), &post.id);
let response = HttpResponse::Found() let response = HttpResponse::Found()
.header("Location", page_url) .header("Location", page_url)
@ -287,3 +290,42 @@ pub async fn object_view(
.json(object); .json(object);
Ok(response) Ok(response)
} }
#[cfg(test)]
mod tests {
use actix_web::http::{header, HeaderMap, HeaderValue};
use super::*;
#[test]
fn test_is_activitypub_request_mastodon() {
let mut request_headers = HeaderMap::new();
request_headers.insert(
header::ACCEPT,
HeaderValue::from_static(r#"application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams", text/html;q=0.1"#),
);
let result = is_activitypub_request(&request_headers);
assert_eq!(result, true);
}
#[test]
fn test_is_activitypub_request_pleroma() {
let mut request_headers = HeaderMap::new();
request_headers.insert(
header::ACCEPT,
HeaderValue::from_static("application/activity+json"),
);
let result = is_activitypub_request(&request_headers);
assert_eq!(result, true);
}
#[test]
fn test_is_activitypub_request_browser() {
let mut request_headers = HeaderMap::new();
request_headers.insert(
header::ACCEPT,
HeaderValue::from_static("text/html"),
);
let result = is_activitypub_request(&request_headers);
assert_eq!(result, false);
}
}