From 86f172076b5f6239b293b846026f43b3472e0945 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 17 Apr 2020 15:46:08 +0200 Subject: [PATCH] Implement search for activitypub IDs --- docker/federation/docker-compose.yml | 2 +- server/src/api/site.rs | 23 ++++-- server/src/apub/fetcher.rs | 113 +++++++++++++++++++-------- server/src/routes/federation.rs | 2 +- 4 files changed, 96 insertions(+), 44 deletions(-) diff --git a/docker/federation/docker-compose.yml b/docker/federation/docker-compose.yml index 216ac9a1d..a4e5c3074 100644 --- a/docker/federation/docker-compose.yml +++ b/docker/federation/docker-compose.yml @@ -31,7 +31,7 @@ services: - LEMMY_SETUP__ADMIN_PASSWORD=lemmy - LEMMY_SETUP__SITE_NAME=lemmy_alpha - RUST_BACKTRACE=1 - - RUST_LOG=actix_web=debug + - RUST_LOG=debug restart: always depends_on: - postgres_alpha diff --git a/server/src/api/site.rs b/server/src/api/site.rs index 4202fea06..4e1431a57 100644 --- a/server/src/api/site.rs +++ b/server/src/api/site.rs @@ -1,9 +1,10 @@ use super::*; use crate::api::user::Register; use crate::api::{Oper, Perform}; +use crate::apub::fetcher::search_by_apub_id; use crate::settings::Settings; use diesel::PgConnection; -use log::info; +use log::{debug, info}; use std::str::FromStr; #[derive(Serialize, Deserialize)] @@ -14,7 +15,7 @@ pub struct ListCategoriesResponse { categories: Vec, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct Search { q: String, type_: String, @@ -25,13 +26,13 @@ pub struct Search { auth: Option, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct SearchResponse { - type_: String, - comments: Vec, - posts: Vec, - communities: Vec, - users: Vec, + pub type_: String, + pub comments: Vec, + pub posts: Vec, + pub communities: Vec, + pub users: Vec, } #[derive(Serialize, Deserialize)] @@ -354,6 +355,12 @@ impl Perform for Oper { fn perform(&self, conn: &PgConnection) -> Result { let data: &Search = &self.data; + dbg!(&data); + match search_by_apub_id(&data.q, conn) { + Ok(r) => return Ok(r), + Err(e) => debug!("Failed to resolve search query as activitypub ID: {}", e), + } + let user_id: Option = match &data.auth { Some(auth) => match Claims::decode(&auth) { Ok(claims) => { diff --git a/server/src/apub/fetcher.rs b/server/src/apub/fetcher.rs index 53c97b690..f29d9f19f 100644 --- a/server/src/apub/fetcher.rs +++ b/server/src/apub/fetcher.rs @@ -1,8 +1,12 @@ +use crate::api::site::SearchResponse; use crate::apub::*; use crate::db::community::{Community, CommunityForm}; +use crate::db::community_view::CommunityView; use crate::db::post::{Post, PostForm}; +use crate::db::post_view::PostView; use crate::db::user::{UserForm, User_}; -use crate::db::Crud; +use crate::db::user_view::UserView; +use crate::db::{Crud, SearchType}; use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown}; use crate::settings::Settings; use activitystreams::collection::{OrderedCollection, UnorderedCollection}; @@ -27,6 +31,36 @@ fn fetch_node_info(instance: &Instance) -> Result { Ok(fetch_remote_object::(&well_known.links.href)?) } +// TODO: move these to db +fn upsert_community( + community_form: &CommunityForm, + conn: &PgConnection, +) -> Result { + let existing = Community::read_from_actor_id(conn, &community_form.actor_id); + match existing { + Err(NotFound {}) => Ok(Community::create(conn, &community_form)?), + Ok(c) => Ok(Community::update(conn, c.id, &community_form)?), + Err(e) => Err(Error::from(e)), + } +} +fn upsert_user(user_form: &UserForm, conn: &PgConnection) -> Result { + let existing = User_::read_from_apub_id(conn, &user_form.actor_id); + Ok(match existing { + Err(NotFound {}) => User_::create(conn, &user_form)?, + Ok(u) => User_::update(conn, u.id, &user_form)?, + Err(e) => return Err(Error::from(e)), + }) +} + +fn upsert_post(post_form: &PostForm, conn: &PgConnection) -> Result { + let existing = Post::read_from_apub_id(conn, &post_form.ap_id); + match existing { + Err(NotFound {}) => Ok(Post::create(conn, &post_form)?), + Ok(p) => Ok(Post::update(conn, p.id, &post_form)?), + Err(e) => Err(Error::from(e)), + } +} + fn fetch_communities_from_instance( community_list: &Url, conn: &PgConnection, @@ -39,17 +73,7 @@ fn fetch_communities_from_instance( let group = b.to_owned().to_concrete::()?; Ok(CommunityForm::from_group(&group, conn)?) }) - .map( - |cf: Result| -> Result { - let cf2 = cf?; - let existing = Community::read_from_actor_id(conn, &cf2.actor_id); - match existing { - Err(NotFound {}) => Ok(Community::create(conn, &cf2)?), - Ok(c) => Ok(Community::update(conn, c.id, &cf2)?), - Err(e) => Err(Error::from(e)), - } - }, - ) + .map(|cf| upsert_community(&cf?, conn)) .collect() } @@ -74,6 +98,45 @@ where Ok(res) } +#[serde(untagged)] +#[derive(serde::Deserialize)] +pub enum SearchAcceptedObjects { + Person(Box), + Group(Box), + Page(Box), +} + +pub fn search_by_apub_id(query: &str, conn: &PgConnection) -> Result { + let query_url = Url::parse(&query)?; + let mut response = SearchResponse { + type_: SearchType::All.to_string(), + comments: vec![], + posts: vec![], + communities: vec![], + users: vec![], + }; + // test with: + // http://lemmy_alpha:8540/federation/c/main + // http://lemmy_alpha:8540/federation/u/lemmy_alpha + // http://lemmy_alpha:8540/federation/p/3 + match fetch_remote_object::(&query_url)? { + SearchAcceptedObjects::Person(p) => { + let u = upsert_user(&UserForm::from_person(&p)?, conn)?; + response.users = vec![UserView::read(conn, u.id)?]; + } + SearchAcceptedObjects::Group(g) => { + let c = upsert_community(&CommunityForm::from_group(&g, conn)?, conn)?; + response.communities = vec![CommunityView::read(conn, c.id, None)?]; + } + SearchAcceptedObjects::Page(p) => { + let p = upsert_post(&PostForm::from_page(&p, conn)?, conn)?; + response.posts = vec![PostView::read(conn, p.id, None)?]; + } + } + dbg!(&response); + Ok(response) +} + fn fetch_remote_community_posts( community: &Community, conn: &PgConnection, @@ -89,39 +152,21 @@ fn fetch_remote_community_posts( let page = obox.clone().to_concrete::()?; PostForm::from_page(&page, conn) }) - .map(|pf: Result| -> Result { - let pf2 = pf?; - let existing = Post::read_from_apub_id(conn, &pf2.ap_id); - match existing { - Err(NotFound {}) => Ok(Post::create(conn, &pf2)?), - Ok(p) => Ok(Post::update(conn, p.id, &pf2)?), - Err(e) => Err(Error::from(e)), - } - }) + .map(|pf| upsert_post(&pf?, conn)) .collect::, Error>>()?, ) } -// TODO: can probably merge these two methods? pub fn fetch_remote_user(apub_id: &Url, conn: &PgConnection) -> Result { let person = fetch_remote_object::(apub_id)?; let uf = UserForm::from_person(&person)?; - let existing = User_::read_from_apub_id(conn, &uf.actor_id); - Ok(match existing { - Err(NotFound {}) => User_::create(conn, &uf)?, - Ok(u) => User_::update(conn, u.id, &uf)?, - Err(e) => return Err(Error::from(e)), - }) + upsert_user(&uf, conn) } + pub fn fetch_remote_community(apub_id: &Url, conn: &PgConnection) -> Result { let group = fetch_remote_object::(apub_id)?; let cf = CommunityForm::from_group(&group, conn)?; - let existing = Community::read_from_actor_id(conn, &cf.actor_id); - Ok(match existing { - Err(NotFound {}) => Community::create(conn, &cf)?, - Ok(u) => Community::update(conn, u.id, &cf)?, - Err(e) => return Err(Error::from(e)), - }) + upsert_community(&cf, conn) } // TODO: in the future, this should only be done when an instance is followed for the first time diff --git a/server/src/routes/federation.rs b/server/src/routes/federation.rs index 6c7cce8a8..4be4e54b5 100644 --- a/server/src/routes/federation.rs +++ b/server/src/routes/federation.rs @@ -37,7 +37,7 @@ pub fn config(cfg: &mut web::ServiceConfig) { ) .route( "/federation/p/{post_id}", - web::get().to(apub::user::get_apub_user), + web::get().to(apub::post::get_apub_post), ); } }