Save extra fields from remote actors
This commit is contained in:
parent
6dec1a5da1
commit
7fad429a8c
8 changed files with 60 additions and 16 deletions
|
@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::{json, Value};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::errors::HttpError;
|
||||
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;
|
||||
|
@ -14,7 +14,7 @@ use super::views::{
|
|||
get_followers_url,
|
||||
get_following_url,
|
||||
};
|
||||
use super::vocabulary::{PERSON, IMAGE};
|
||||
use super::vocabulary::{PERSON, IMAGE, PROPERTY_VALUE};
|
||||
|
||||
const W3ID_CONTEXT: &str = "https://w3id.org/security/v1";
|
||||
|
||||
|
@ -40,6 +40,14 @@ pub struct ActorCapabilities {
|
|||
accepts_chat_messages: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct ActorProperty {
|
||||
name: String,
|
||||
#[serde(rename = "type")]
|
||||
object_type: String,
|
||||
value: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Actor {
|
||||
|
@ -71,12 +79,31 @@ pub struct Actor {
|
|||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub summary: Option<String>,
|
||||
|
||||
pub attachment: Option<Vec<ActorProperty>>,
|
||||
}
|
||||
|
||||
impl Actor {
|
||||
/// Parse 'attachment' into ExtraField vector
|
||||
pub fn extra_fields(&self) -> Vec<ExtraField> {
|
||||
match &self.attachment {
|
||||
Some(properties) => {
|
||||
properties.iter()
|
||||
.map(|prop| ExtraField {
|
||||
name: prop.name.clone(),
|
||||
value: prop.value.clone(),
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
None => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_actor_object(
|
||||
config: &Config,
|
||||
user: &User,
|
||||
) -> Result<Actor, HttpError> {
|
||||
) -> Result<Actor, rsa::pkcs8::Error> {
|
||||
let username = &user.profile.username;
|
||||
let id = get_actor_url(&config.instance_url(), &username);
|
||||
let inbox = get_inbox_url(&config.instance_url(), &username);
|
||||
|
@ -84,15 +111,16 @@ pub fn get_actor_object(
|
|||
let followers = get_followers_url(&config.instance_url(), &username);
|
||||
let following = get_following_url(&config.instance_url(), &username);
|
||||
|
||||
let private_key = deserialize_private_key(&user.private_key)
|
||||
.map_err(|_| HttpError::InternalError)?;
|
||||
let public_key_pem = get_public_key_pem(&private_key)
|
||||
.map_err(|_| HttpError::InternalError)?;
|
||||
let private_key = deserialize_private_key(&user.private_key)?;
|
||||
let public_key_pem = get_public_key_pem(&private_key)?;
|
||||
let public_key = PublicKey {
|
||||
id: format!("{}#main-key", id),
|
||||
owner: id.clone(),
|
||||
public_key_pem: public_key_pem,
|
||||
};
|
||||
let capabilities = ActorCapabilities {
|
||||
accepts_chat_messages: Some(false),
|
||||
};
|
||||
let avatar = match &user.profile.avatar_file_name {
|
||||
Some(file_name) => {
|
||||
let image = Image {
|
||||
|
@ -113,9 +141,15 @@ pub fn get_actor_object(
|
|||
},
|
||||
None => None,
|
||||
};
|
||||
let capabilities = ActorCapabilities {
|
||||
accepts_chat_messages: Some(false),
|
||||
};
|
||||
let properties = user.profile.extra_fields.clone()
|
||||
.unpack().into_iter()
|
||||
.map(|field| {
|
||||
ActorProperty {
|
||||
object_type: PROPERTY_VALUE.to_string(),
|
||||
name: field.name,
|
||||
value: field.value,
|
||||
}
|
||||
}).collect();
|
||||
let actor = Actor {
|
||||
context: Some(json!([
|
||||
AP_CONTEXT.to_string(),
|
||||
|
@ -134,6 +168,7 @@ pub fn get_actor_object(
|
|||
icon: avatar,
|
||||
image: banner,
|
||||
summary: None,
|
||||
attachment: Some(properties),
|
||||
};
|
||||
Ok(actor)
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ pub async fn fetch_profile_by_actor_id(
|
|||
let actor_value: Value = serde_json::from_str(&actor_json)?;
|
||||
let actor: Actor = serde_json::from_value(actor_value.clone())?;
|
||||
let (avatar, banner) = fetch_avatar_and_banner(&actor, media_dir).await?;
|
||||
let extra_fields = actor.extra_fields();
|
||||
let actor_address = format!(
|
||||
"{}@{}",
|
||||
actor.preferred_username,
|
||||
|
@ -102,8 +103,9 @@ pub async fn fetch_profile_by_actor_id(
|
|||
display_name: Some(actor.name),
|
||||
acct: actor_address,
|
||||
bio: actor.summary,
|
||||
avatar: avatar,
|
||||
banner: banner,
|
||||
avatar,
|
||||
banner,
|
||||
extra_fields,
|
||||
actor: Some(actor_value),
|
||||
};
|
||||
Ok(profile_data)
|
||||
|
|
|
@ -146,13 +146,14 @@ pub async fn receive_activity(
|
|||
let profile = get_profile_by_actor_id(db_client, &actor.id).await?;
|
||||
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 mut profile_data = ProfileUpdateData {
|
||||
display_name: Some(actor.name),
|
||||
bio: actor.summary.clone(),
|
||||
bio_source: actor.summary,
|
||||
avatar,
|
||||
banner,
|
||||
extra_fields: vec![],
|
||||
extra_fields,
|
||||
};
|
||||
profile_data.clean()?;
|
||||
update_profile(db_client, &profile.id, profile_data).await?;
|
||||
|
|
|
@ -47,7 +47,8 @@ async fn get_actor(
|
|||
) -> Result<HttpResponse, HttpError> {
|
||||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let user = get_user_by_name(db_client, &username).await?;
|
||||
let actor = get_actor_object(&config, &user)?;
|
||||
let actor = get_actor_object(&config, &user)
|
||||
.map_err(|_| HttpError::InternalError)?;
|
||||
let response = HttpResponse::Ok()
|
||||
.content_type(ACTIVITY_CONTENT_TYPE)
|
||||
.json(actor);
|
||||
|
|
|
@ -12,3 +12,4 @@ pub const PERSON: &str = "Person";
|
|||
pub const DOCUMENT: &str = "Document";
|
||||
pub const IMAGE: &str = "Image";
|
||||
pub const NOTE: &str = "Note";
|
||||
pub const PROPERTY_VALUE: &str = "PropertyValue";
|
||||
|
|
|
@ -15,14 +15,15 @@ pub async fn create_profile(
|
|||
profile_data: &ProfileCreateData,
|
||||
) -> Result<DbActorProfile, DatabaseError> {
|
||||
let profile_id = Uuid::new_v4();
|
||||
let extra_fields = ExtraFields(profile_data.extra_fields.clone());
|
||||
let result = db_client.query_one(
|
||||
"
|
||||
INSERT INTO actor_profile (
|
||||
id, username, display_name, acct, bio, bio_source,
|
||||
avatar_file_name, banner_file_name,
|
||||
avatar_file_name, banner_file_name, extra_fields,
|
||||
actor_json
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
RETURNING actor_profile
|
||||
",
|
||||
&[
|
||||
|
@ -34,6 +35,7 @@ pub async fn create_profile(
|
|||
&profile_data.bio,
|
||||
&profile_data.avatar,
|
||||
&profile_data.banner,
|
||||
&extra_fields,
|
||||
&profile_data.actor,
|
||||
],
|
||||
).await;
|
||||
|
|
|
@ -74,6 +74,7 @@ pub struct ProfileCreateData {
|
|||
pub bio: Option<String>,
|
||||
pub avatar: Option<String>,
|
||||
pub banner: Option<String>,
|
||||
pub extra_fields: Vec<ExtraField>,
|
||||
pub actor: Option<Value>,
|
||||
}
|
||||
|
||||
|
|
|
@ -145,6 +145,7 @@ pub async fn create_user(
|
|||
bio: None,
|
||||
avatar: None,
|
||||
banner: None,
|
||||
extra_fields: vec![],
|
||||
actor: None,
|
||||
};
|
||||
let profile = create_profile(&transaction, &profile_data).await?;
|
||||
|
|
Loading…
Reference in a new issue