Convert actor_json value into Actor type when reading from database
This commit is contained in:
parent
97d798eeed
commit
9156dec5bb
10 changed files with 70 additions and 83 deletions
|
@ -179,11 +179,11 @@ pub fn create_note(
|
|||
let mut recipients = vec![AP_PUBLIC.to_string()];
|
||||
let mut tags = vec![];
|
||||
for profile in &post.mentions {
|
||||
let actor_id = profile.actor_id(instance_url).unwrap();
|
||||
let actor_id = profile.actor_id(instance_url);
|
||||
if !profile.is_local() {
|
||||
recipients.push(actor_id);
|
||||
};
|
||||
let actor_url = profile.actor_url(instance_url).unwrap();
|
||||
let actor_url = profile.actor_url(instance_url);
|
||||
let tag = Tag {
|
||||
name: format!("@{}", profile.actor_address(instance_host)),
|
||||
tag_type: MENTION.to_string(),
|
||||
|
@ -208,7 +208,7 @@ pub fn create_note(
|
|||
Some(get_object_url(instance_url, &post.id))
|
||||
} else {
|
||||
// Replying to remote post
|
||||
let remote_actor_id = post.author.actor_id(instance_url).unwrap();
|
||||
let remote_actor_id = post.author.actor_id(instance_url);
|
||||
if !recipients.contains(&remote_actor_id) {
|
||||
recipients.push(remote_actor_id);
|
||||
};
|
||||
|
@ -294,7 +294,7 @@ pub fn create_activity_announce(
|
|||
repost_id: &Uuid,
|
||||
) -> Activity {
|
||||
let object_id = post.get_object_id(instance_url);
|
||||
let recipient_id = post.author.actor_id(instance_url).unwrap();
|
||||
let recipient_id = post.author.actor_id(instance_url);
|
||||
let activity = create_activity(
|
||||
instance_url,
|
||||
&actor_profile.username,
|
||||
|
@ -345,7 +345,7 @@ pub fn create_activity_delete_note(
|
|||
};
|
||||
let mut recipients = vec![AP_PUBLIC.to_string()];
|
||||
for profile in &post.mentions {
|
||||
let actor_id = profile.actor_id(instance_url).unwrap();
|
||||
let actor_id = profile.actor_id(instance_url);
|
||||
if !profile.is_local() {
|
||||
recipients.push(actor_id);
|
||||
};
|
||||
|
@ -460,6 +460,7 @@ pub fn create_activity_update_person(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::activitypub::actor::Actor;
|
||||
use super::*;
|
||||
|
||||
const INSTANCE_HOST: &str = "example.com";
|
||||
|
@ -511,10 +512,11 @@ mod tests {
|
|||
let parent_author_actor_url = "https://test.net/@test";
|
||||
let parent_author = DbActorProfile {
|
||||
acct: parent_author_acct.to_string(),
|
||||
actor_json: Some(json!({
|
||||
"id": parent_author_actor_id,
|
||||
"url": parent_author_actor_url,
|
||||
})),
|
||||
actor_json: Some(Actor {
|
||||
id: parent_author_actor_id.to_string(),
|
||||
url: Some(parent_author_actor_url.to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
let parent = Post {
|
||||
|
|
|
@ -2,8 +2,7 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::{json, Value};
|
||||
|
||||
use crate::config::Instance;
|
||||
use crate::errors::ConversionError;
|
||||
use crate::models::profiles::types::{DbActorProfile, ExtraField};
|
||||
use crate::models::profiles::types::ExtraField;
|
||||
use crate::models::users::types::User;
|
||||
use crate::utils::crypto::{deserialize_private_key, get_public_key_pem};
|
||||
use crate::utils::files::get_file_url;
|
||||
|
@ -19,7 +18,8 @@ use super::vocabulary::{IMAGE, PERSON, PROPERTY_VALUE, SERVICE};
|
|||
|
||||
const W3ID_CONTEXT: &str = "https://w3id.org/security/v1";
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(test, derive(Default))]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PublicKey {
|
||||
id: String,
|
||||
|
@ -27,7 +27,7 @@ pub struct PublicKey {
|
|||
pub public_key_pem: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Image {
|
||||
#[serde(rename = "type")]
|
||||
|
@ -35,13 +35,13 @@ pub struct Image {
|
|||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ActorCapabilities {
|
||||
accepts_chat_messages: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct ActorProperty {
|
||||
name: String,
|
||||
#[serde(rename = "type")]
|
||||
|
@ -49,16 +49,18 @@ pub struct ActorProperty {
|
|||
value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
// Clone trait is required by FromSql
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(test, derive(Default))]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Actor {
|
||||
#[serde(rename = "@context")]
|
||||
context: Option<Value>,
|
||||
pub context: Option<Value>,
|
||||
|
||||
pub id: String,
|
||||
|
||||
#[serde(rename = "type")]
|
||||
object_type: String,
|
||||
pub object_type: String,
|
||||
|
||||
pub name: Option<String>,
|
||||
|
||||
|
@ -116,20 +118,6 @@ impl Actor {
|
|||
}
|
||||
}
|
||||
|
||||
impl DbActorProfile {
|
||||
pub fn remote_actor(&self) -> Result<Option<Actor>, ConversionError> {
|
||||
let actor = match self.actor_json {
|
||||
Some(ref value) => {
|
||||
let actor: Actor = serde_json::from_value(value.clone())
|
||||
.map_err(|_| ConversionError)?;
|
||||
Some(actor)
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
Ok(actor)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ActorAddress {
|
||||
pub username: String,
|
||||
pub instance: String,
|
||||
|
|
|
@ -53,7 +53,7 @@ async fn send_activity(
|
|||
.body(activity_json.to_owned());
|
||||
|
||||
if instance.is_private {
|
||||
log::debug!(
|
||||
log::info!(
|
||||
"private mode: not sending activity to {}",
|
||||
inbox_url,
|
||||
);
|
||||
|
|
|
@ -472,7 +472,7 @@ pub async fn receive_activity(
|
|||
&config.media_dir(),
|
||||
&activity.actor,
|
||||
).await?;
|
||||
let source_actor = source_profile.remote_actor().ok().flatten()
|
||||
let source_actor = source_profile.actor_json
|
||||
.ok_or(HttpError::InternalError)?;
|
||||
let target_actor_id = get_object_id(activity.object)?;
|
||||
let target_username = parse_actor_id(&config.instance_url(), &target_actor_id)?;
|
||||
|
@ -543,8 +543,7 @@ pub async fn receive_activity(
|
|||
let (avatar, banner) = fetch_avatar_and_banner(&actor, &config.media_dir()).await
|
||||
.map_err(|_| ValidationError("failed to fetch image"))?;
|
||||
let extra_fields = actor.extra_fields();
|
||||
let actor_old = profile.remote_actor()
|
||||
.map_err(|_| HttpError::InternalError)?.unwrap();
|
||||
let actor_old = profile.actor_json.unwrap();
|
||||
if actor_old.id != actor.id {
|
||||
log::warn!(
|
||||
"actor ID changed from {} to {}",
|
||||
|
|
|
@ -134,7 +134,7 @@ pub async fn verify_http_signature(
|
|||
return Err(VerificationError::ActorError(other_error.to_string()));
|
||||
},
|
||||
};
|
||||
let actor = actor_profile.remote_actor().ok().flatten()
|
||||
let actor = actor_profile.actor_json.as_ref()
|
||||
.ok_or(VerificationError::ActorError("invalid profile".to_string()))?;
|
||||
|
||||
let public_key = deserialize_public_key(&actor.public_key.public_key_pem)?;
|
||||
|
@ -146,8 +146,7 @@ pub async fn verify_http_signature(
|
|||
if !is_valid_signature {
|
||||
return Err(VerificationError::InvalidSignature);
|
||||
}
|
||||
let signer_id = actor_profile.actor_id(&config.instance_url())
|
||||
.map_err(|_| VerificationError::ActorError("invalid profile".to_string()))?;
|
||||
let signer_id = actor_profile.actor_id(&config.instance_url());
|
||||
Ok(signer_id)
|
||||
}
|
||||
|
||||
|
|
|
@ -157,9 +157,7 @@ async fn update_credentials(
|
|||
let followers = get_followers(db_client, ¤t_user.id).await?;
|
||||
let mut recipients: Vec<Actor> = Vec::new();
|
||||
for follower in followers {
|
||||
let maybe_remote_actor = follower.remote_actor()
|
||||
.map_err(|_| HttpError::InternalError)?;
|
||||
if let Some(remote_actor) = maybe_remote_actor {
|
||||
if let Some(remote_actor) = follower.actor_json {
|
||||
recipients.push(remote_actor);
|
||||
};
|
||||
};
|
||||
|
@ -203,9 +201,7 @@ async fn follow_account(
|
|||
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||
let target = get_profile_by_id(db_client, &account_id).await?;
|
||||
let maybe_remote_actor = target.remote_actor()
|
||||
.map_err(|_| HttpError::InternalError)?;
|
||||
if let Some(remote_actor) = maybe_remote_actor {
|
||||
if let Some(remote_actor) = target.actor_json {
|
||||
// Remote follow
|
||||
let request = create_follow_request(db_client, ¤t_user.id, &target.id).await?;
|
||||
let activity = create_activity_follow(
|
||||
|
@ -236,9 +232,7 @@ async fn unfollow_account(
|
|||
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||
let target = get_profile_by_id(db_client, &account_id).await?;
|
||||
let maybe_remote_actor = target.remote_actor()
|
||||
.map_err(|_| HttpError::InternalError)?;
|
||||
if let Some(remote_actor) = maybe_remote_actor {
|
||||
if let Some(remote_actor) = target.actor_json {
|
||||
// Remote follow
|
||||
let follow_request = get_follow_request_by_path(
|
||||
db_client,
|
||||
|
|
|
@ -21,8 +21,7 @@ pub async fn get_note_audience(
|
|||
audience.extend(post.mentions.clone());
|
||||
let mut recipients: Vec<Actor> = Vec::new();
|
||||
for profile in audience {
|
||||
let maybe_remote_actor = profile.remote_actor()?;
|
||||
if let Some(remote_actor) = maybe_remote_actor {
|
||||
if let Some(remote_actor) = profile.actor_json {
|
||||
recipients.push(remote_actor);
|
||||
};
|
||||
};
|
||||
|
@ -40,10 +39,9 @@ pub async fn get_like_audience(
|
|||
) -> Result<Audience, DatabaseError> {
|
||||
let mut recipients: Vec<Actor> = Vec::new();
|
||||
let mut primary_recipient = None;
|
||||
let maybe_remote_author = post.author.remote_actor()?;
|
||||
if let Some(remote_actor) = maybe_remote_author {
|
||||
if let Some(remote_actor) = post.author.actor_json.as_ref() {
|
||||
primary_recipient = Some(remote_actor.id.clone());
|
||||
recipients.push(remote_actor);
|
||||
recipients.push(remote_actor.clone());
|
||||
};
|
||||
Ok(Audience { recipients, primary_recipient })
|
||||
}
|
||||
|
@ -56,16 +54,14 @@ pub async fn get_announce_audience(
|
|||
let followers = get_followers(db_client, ¤t_user.id).await?;
|
||||
let mut recipients: Vec<Actor> = Vec::new();
|
||||
for profile in followers {
|
||||
let maybe_remote_actor = profile.remote_actor()?;
|
||||
if let Some(remote_actor) = maybe_remote_actor {
|
||||
if let Some(remote_actor) = profile.actor_json {
|
||||
recipients.push(remote_actor);
|
||||
};
|
||||
};
|
||||
let mut primary_recipient = None;
|
||||
let maybe_remote_author = post.author.remote_actor()?;
|
||||
if let Some(remote_actor) = maybe_remote_author {
|
||||
if let Some(remote_actor) = post.author.actor_json.as_ref() {
|
||||
primary_recipient = Some(remote_actor.id.clone());
|
||||
recipients.push(remote_actor);
|
||||
recipients.push(remote_actor.clone());
|
||||
};
|
||||
Ok(Audience { recipients, primary_recipient })
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ impl Mention {
|
|||
id: profile.id.to_string(),
|
||||
username: profile.username.clone(),
|
||||
acct: profile.acct.clone(),
|
||||
url: profile.actor_id(instance_url).unwrap(),
|
||||
url: profile.actor_id(instance_url),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ pub fn replace_mentions(
|
|||
if let Some(profile) = mention_map.get(&acct) {
|
||||
// Replace with a link to profile.
|
||||
// Actor URL may differ from actor ID.
|
||||
let url = profile.actor_url(instance_url).unwrap();
|
||||
let url = profile.actor_url(instance_url);
|
||||
return format!(
|
||||
// https://microformats.org/wiki/h-card
|
||||
r#"{}<span class="h-card"><a class="u-url mention" href="{}">@{}</a></span>{}"#,
|
||||
|
@ -103,7 +103,7 @@ pub fn mention_to_address(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
use crate::activitypub::actor::Actor;
|
||||
use super::*;
|
||||
|
||||
const INSTANCE_HOST: &str = "server1.com";
|
||||
|
@ -137,10 +137,11 @@ mod tests {
|
|||
// Remote actor
|
||||
let profile_2 = DbActorProfile {
|
||||
username: "user2".to_string(),
|
||||
actor_json: Some(json!({
|
||||
"id": "https://server2.com/actors/user2",
|
||||
"url": "https://server2.com/@user2",
|
||||
})),
|
||||
actor_json: Some(Actor {
|
||||
id: "https://server2.com/actors/user2".to_string(),
|
||||
url: Some("https://server2.com/@user2".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
let text = concat!(
|
||||
|
|
|
@ -8,8 +8,9 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::Value;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::activitypub::actor::Actor;
|
||||
use crate::activitypub::views::get_actor_url;
|
||||
use crate::errors::{ConversionError, ValidationError};
|
||||
use crate::errors::ValidationError;
|
||||
use crate::utils::html::clean_html;
|
||||
use super::validators::{
|
||||
validate_username,
|
||||
|
@ -41,7 +42,7 @@ impl<'a> FromSql<'a> for ExtraFields {
|
|||
let fields: Self = serde_json::from_value(json_value)?;
|
||||
Ok(fields)
|
||||
}
|
||||
accepts!(JSON,JSONB);
|
||||
accepts!(JSON, JSONB);
|
||||
}
|
||||
|
||||
impl ToSql for ExtraFields {
|
||||
|
@ -54,6 +55,15 @@ impl ToSql for ExtraFields {
|
|||
to_sql_checked!();
|
||||
}
|
||||
|
||||
impl<'a> FromSql<'a> for Actor {
|
||||
fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, SqlError> {
|
||||
let Json(json_value) = Json::<Value>::from_sql(ty, raw)?;
|
||||
let actor: Self = serde_json::from_value(json_value)?;
|
||||
Ok(actor)
|
||||
}
|
||||
accepts!(JSON, JSONB);
|
||||
}
|
||||
|
||||
#[derive(Clone, FromSql)]
|
||||
#[postgres(name = "actor_profile")]
|
||||
pub struct DbActorProfile {
|
||||
|
@ -70,7 +80,7 @@ pub struct DbActorProfile {
|
|||
pub following_count: i32,
|
||||
pub post_count: i32,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub actor_json: Option<Value>,
|
||||
pub actor_json: Option<Actor>,
|
||||
}
|
||||
|
||||
impl DbActorProfile {
|
||||
|
@ -78,22 +88,17 @@ impl DbActorProfile {
|
|||
self.actor_json.is_none()
|
||||
}
|
||||
|
||||
pub fn actor_id(&self, instance_url: &str) -> Result<String, ConversionError> {
|
||||
let actor_id = match self.actor_json {
|
||||
Some(ref actor_value) => {
|
||||
actor_value["id"].as_str()
|
||||
.ok_or(ConversionError)?
|
||||
.to_string()
|
||||
},
|
||||
pub fn actor_id(&self, instance_url: &str) -> String {
|
||||
match self.actor_json {
|
||||
Some(ref actor) => actor.id.clone(),
|
||||
None => get_actor_url(instance_url, &self.username),
|
||||
};
|
||||
Ok(actor_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn actor_url(&self, instance_url: &str) -> Result<String, ConversionError> {
|
||||
if let Some(ref actor_value) = self.actor_json {
|
||||
if let Some(actor_url) = actor_value["url"].as_str() {
|
||||
return Ok(actor_url.to_string());
|
||||
pub fn actor_url(&self, instance_url: &str) -> String {
|
||||
if let Some(ref actor) = self.actor_json {
|
||||
if let Some(ref actor_url) = actor.url {
|
||||
return actor_url.to_string();
|
||||
};
|
||||
};
|
||||
self.actor_id(instance_url)
|
||||
|
@ -189,7 +194,7 @@ impl ProfileUpdateData {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
use crate::activitypub::actor::Actor;
|
||||
use super::*;
|
||||
|
||||
const INSTANCE_HOST: &str = "example.com";
|
||||
|
@ -211,7 +216,10 @@ mod tests {
|
|||
fn test_remote_actor_address() {
|
||||
let remote_profile = DbActorProfile {
|
||||
acct: "test@remote.com".to_string(),
|
||||
actor_json: Some(json!({"id": "https://test"})),
|
||||
actor_json: Some(Actor {
|
||||
id: "https://test".to_string(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
Loading…
Reference in a new issue