Add support for search by Note url
This commit is contained in:
parent
284840463c
commit
81d6cf3daf
5 changed files with 65 additions and 13 deletions
|
@ -3,6 +3,6 @@ pub mod actor;
|
|||
pub mod constants;
|
||||
pub mod deliverer;
|
||||
pub mod fetcher;
|
||||
mod receiver;
|
||||
pub mod receiver;
|
||||
pub mod views;
|
||||
mod vocabulary;
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::config::Config;
|
|||
use crate::database::{Pool, get_database_client};
|
||||
use crate::errors::{DatabaseError, HttpError, ValidationError};
|
||||
use crate::models::attachments::queries::create_attachment;
|
||||
use crate::models::posts::types::PostCreateData;
|
||||
use crate::models::posts::types::{Post, PostCreateData};
|
||||
use crate::models::posts::queries::{
|
||||
create_post,
|
||||
get_post_by_id,
|
||||
|
@ -101,9 +101,18 @@ pub async fn process_note(
|
|||
config: &Config,
|
||||
db_client: &mut impl GenericClient,
|
||||
object: Object,
|
||||
) -> Result<(), HttpError> {
|
||||
) -> Result<Post, HttpError> {
|
||||
match get_post_by_object_id(db_client, &object.id).await {
|
||||
Ok(post) => return Ok(post), // post already exists
|
||||
Err(DatabaseError::NotFound(_)) => (), // continue processing
|
||||
Err(other_error) => return Err(other_error.into()),
|
||||
};
|
||||
|
||||
let initial_object_id = object.id.clone();
|
||||
let mut maybe_parent_object_id = object.in_reply_to.clone();
|
||||
let mut objects = vec![object];
|
||||
let mut posts = vec![];
|
||||
|
||||
// Fetch ancestors by going through inReplyTo references
|
||||
// TODO: fetch replies too
|
||||
loop {
|
||||
|
@ -133,6 +142,7 @@ pub async fn process_note(
|
|||
maybe_parent_object_id = object.in_reply_to.clone();
|
||||
objects.push(object);
|
||||
}
|
||||
|
||||
// Objects are ordered according to their place in reply tree,
|
||||
// starting with the root
|
||||
objects.reverse();
|
||||
|
@ -189,9 +199,14 @@ pub async fn process_note(
|
|||
object_id: Some(object.id),
|
||||
created_at: object.published,
|
||||
};
|
||||
create_post(db_client, &author.id, post_data).await?;
|
||||
let post = create_post(db_client, &author.id, post_data).await?;
|
||||
posts.push(post);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
let initial_post = posts.into_iter()
|
||||
.find(|post| post.object_id.as_ref() == Some(&initial_object_id))
|
||||
.unwrap();
|
||||
Ok(initial_post)
|
||||
}
|
||||
|
||||
pub async fn receive_activity(
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
use regex::Regex;
|
||||
use tokio_postgres::GenericClient;
|
||||
|
||||
use crate::activitypub::fetcher::fetch_profile;
|
||||
use crate::activitypub::fetcher::{fetch_object, fetch_profile};
|
||||
use crate::activitypub::receiver::process_note;
|
||||
use crate::config::Config;
|
||||
use crate::errors::{ValidationError, HttpError};
|
||||
use crate::mastodon_api::accounts::types::Account;
|
||||
use crate::mastodon_api::statuses::types::Status;
|
||||
use crate::models::posts::types::Post;
|
||||
use crate::models::profiles::queries::{create_profile, search_profile};
|
||||
use crate::models::profiles::types::DbActorProfile;
|
||||
use super::types::SearchResults;
|
||||
|
||||
fn parse_search_query(query: &str) ->
|
||||
fn parse_profile_query(query: &str) ->
|
||||
Result<(String, Option<String>), ValidationError>
|
||||
{
|
||||
let acct_regexp = Regex::new(r"^@?(?P<user>\w+)(@(?P<instance>[\w\.-]+))?").unwrap();
|
||||
let acct_regexp = Regex::new(r"^@?(?P<user>\w+)(@(?P<instance>[\w\.-]+))?$").unwrap();
|
||||
let acct_caps = acct_regexp.captures(query)
|
||||
.ok_or(ValidationError("invalid search query"))?;
|
||||
let username = acct_caps.name("user")
|
||||
|
@ -28,7 +31,13 @@ async fn search_profiles(
|
|||
db_client: &impl GenericClient,
|
||||
search_query: &str,
|
||||
) -> Result<Vec<DbActorProfile>, HttpError> {
|
||||
let (username, instance) = parse_search_query(search_query)?;
|
||||
let (username, instance) = match parse_profile_query(search_query) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(_) => {
|
||||
// Not an 'acct' query
|
||||
return Ok(vec![]);
|
||||
},
|
||||
};
|
||||
let mut profiles = search_profile(db_client, &username, instance.as_ref()).await?;
|
||||
if profiles.len() == 0 && instance.is_some() {
|
||||
let instance_uri = instance.unwrap();
|
||||
|
@ -50,14 +59,40 @@ async fn search_profiles(
|
|||
Ok(profiles)
|
||||
}
|
||||
|
||||
async fn search_note(
|
||||
config: &Config,
|
||||
db_client: &mut impl GenericClient,
|
||||
search_query: &str,
|
||||
) -> Result<Option<Post>, HttpError> {
|
||||
if url::Url::parse(search_query).is_err() {
|
||||
// Not a valid URL
|
||||
return Ok(None);
|
||||
}
|
||||
let maybe_post = if let Ok(object) = fetch_object(search_query).await {
|
||||
let post = process_note(config, db_client, object).await?;
|
||||
Some(post)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(maybe_post)
|
||||
}
|
||||
|
||||
pub async fn search(
|
||||
config: &Config,
|
||||
db_client: &impl GenericClient,
|
||||
db_client: &mut impl GenericClient,
|
||||
search_query: &str,
|
||||
) -> Result<SearchResults, HttpError> {
|
||||
let profiles = search_profiles(config, db_client, search_query).await?;
|
||||
let accounts: Vec<Account> = profiles.into_iter()
|
||||
.map(|profile| Account::from_profile(profile, &config.instance_url()))
|
||||
.collect();
|
||||
Ok(SearchResults { accounts })
|
||||
let maybe_post = search_note(config, db_client, search_query).await?;
|
||||
let statuses = match maybe_post {
|
||||
Some(post) => {
|
||||
let status = Status::from_post(post, &config.instance_url());
|
||||
vec![status]
|
||||
},
|
||||
None => vec![],
|
||||
};
|
||||
Ok(SearchResults { accounts, statuses })
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::mastodon_api::accounts::types::Account;
|
||||
use crate::mastodon_api::statuses::types::Status;
|
||||
|
||||
/// https://docs.joinmastodon.org/methods/search/
|
||||
#[derive(Deserialize)]
|
||||
|
@ -11,4 +12,5 @@ pub struct SearchQueryParams {
|
|||
#[derive(Serialize)]
|
||||
pub struct SearchResults {
|
||||
pub accounts: Vec<Account>,
|
||||
pub statuses: Vec<Status>,
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@ async fn search_view(
|
|||
db_pool: web::Data<Pool>,
|
||||
query_params: web::Query<SearchQueryParams>,
|
||||
) -> Result<HttpResponse, HttpError> {
|
||||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||
get_current_user(db_client, auth.token()).await?;
|
||||
let results = search(&config, db_client, &query_params.q).await?;
|
||||
let results = search(&config, db_client, &query_params.q.trim()).await?;
|
||||
Ok(HttpResponse::Ok().json(results))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue