Attach subscription page link to actor object

This commit is contained in:
silverpill 2022-07-23 20:21:23 +00:00
parent 1554780b35
commit 33a711b91c
6 changed files with 129 additions and 29 deletions

View file

@ -1,11 +1,23 @@
use crate::activitypub::vocabulary::{IDENTITY_PROOF, PROPERTY_VALUE};
use uuid::Uuid;
use crate::activitypub::vocabulary::{
IDENTITY_PROOF,
LINK,
PROPERTY_VALUE,
};
use crate::errors::ValidationError;
use crate::ethereum::identity::{
ETHEREUM_EIP191_PROOF,
DidPkh,
verify_identity_proof,
};
use crate::models::profiles::types::{ExtraField, IdentityProof};
use crate::frontend::get_subscription_page_url;
use crate::models::profiles::types::{
ExtraField,
IdentityProof,
PaymentOption,
PaymentType,
};
use super::types::ActorAttachment;
pub fn attach_identity_proof(
@ -15,6 +27,7 @@ pub fn attach_identity_proof(
object_type: IDENTITY_PROOF.to_string(),
name: proof.issuer.to_string(),
value: None,
href: None,
signature_algorithm: Some(proof.proof_type),
signature_value: Some(proof.value),
}
@ -49,6 +62,43 @@ pub fn parse_identity_proof(
Ok(proof)
}
pub fn attach_payment_option(
instance_url: &str,
user_id: &Uuid,
payment_option: PaymentOption,
) -> ActorAttachment {
match payment_option.payment_type {
PaymentType::Link => unimplemented!(),
PaymentType::EthereumSubscription => {
let name = format!("{:?}", payment_option.payment_type);
let subscription_page_url =
get_subscription_page_url(instance_url, user_id);
ActorAttachment {
object_type: LINK.to_string(),
name: name,
value: None,
href: Some(subscription_page_url),
signature_algorithm: None,
signature_value: None,
}
},
}
}
pub fn parse_payment_option(
attachment: &ActorAttachment,
) -> Result<PaymentOption, ValidationError> {
if attachment.object_type != LINK {
return Err(ValidationError("invalid attachment type"));
};
let payment_option = PaymentOption {
payment_type: PaymentType::Link,
name: Some(attachment.name.clone()),
href: attachment.href.clone(),
};
Ok(payment_option)
}
pub fn attach_extra_field(
field: ExtraField,
) -> ActorAttachment {
@ -56,6 +106,7 @@ pub fn attach_extra_field(
object_type: PROPERTY_VALUE.to_string(),
name: field.name,
value: Some(field.value),
href: None,
signature_algorithm: None,
signature_value: None,
}
@ -79,8 +130,11 @@ pub fn parse_extra_field(
#[cfg(test)]
mod tests {
use crate::utils::id::new_uuid;
use super::*;
const INSTANCE_URL: &str = "https://example.com";
#[test]
fn test_extra_field() {
let field = ExtraField {
@ -90,8 +144,30 @@ mod tests {
};
let attachment = attach_extra_field(field.clone());
assert_eq!(attachment.object_type, PROPERTY_VALUE);
let parsed_field = parse_extra_field(&attachment).unwrap();
assert_eq!(parsed_field.name, field.name);
assert_eq!(parsed_field.value, field.value);
}
#[test]
fn test_payment_option() {
let user_id = new_uuid();
let payment_option = PaymentOption::subscription();
let subscription_page_url =
format!("https://example.com/profile/{}/subscription", user_id);
let attachment = attach_payment_option(
INSTANCE_URL,
&user_id,
payment_option,
);
assert_eq!(attachment.object_type, LINK);
assert_eq!(attachment.name, "EthereumSubscription");
assert_eq!(attachment.href.as_deref().unwrap(), subscription_page_url);
let parsed_option = parse_payment_option(&attachment).unwrap();
assert!(matches!(parsed_option.payment_type, PaymentType::Link));
assert_eq!(parsed_option.name.unwrap(), "EthereumSubscription");
assert_eq!(parsed_option.href.unwrap(), subscription_page_url);
}
}

View file

@ -4,18 +4,25 @@ use serde_json::{json, Value};
use crate::activitypub::{
constants::{ACTOR_KEY_SUFFIX, AP_CONTEXT},
identifiers::{local_actor_id, LocalActorCollection},
vocabulary::{IDENTITY_PROOF, IMAGE, PERSON, PROPERTY_VALUE, SERVICE},
vocabulary::{IDENTITY_PROOF, IMAGE, LINK, PERSON, PROPERTY_VALUE, SERVICE},
};
use crate::config::Instance;
use crate::models::profiles::types::{ExtraField, IdentityProof};
use crate::errors::ValidationError;
use crate::models::profiles::types::{
ExtraField,
IdentityProof,
PaymentOption,
};
use crate::models::users::types::User;
use crate::utils::crypto::{deserialize_private_key, get_public_key_pem};
use crate::utils::files::get_file_url;
use super::attachments::{
attach_extra_field,
attach_identity_proof,
attach_payment_option,
parse_extra_field,
parse_identity_proof,
parse_payment_option,
};
const W3ID_CONTEXT: &str = "https://w3id.org/security/v1";
@ -48,6 +55,9 @@ pub struct ActorAttachment {
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub href: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub signature_algorithm: Option<String>,
@ -119,46 +129,49 @@ impl Actor {
Ok(actor_address)
}
pub fn parse_attachments(&self) -> (Vec<IdentityProof>, Vec<ExtraField>) {
pub fn parse_attachments(&self) -> (
Vec<IdentityProof>,
Vec<PaymentOption>,
Vec<ExtraField>,
) {
let mut identity_proofs = vec![];
let mut payment_options = vec![];
let mut extra_fields = vec![];
let log_error = |attachment: &ActorAttachment, error| {
log::warn!(
"ignoring actor attachment of type {}: {}",
attachment.object_type,
error,
);
};
if let Some(attachments) = &self.attachment {
for attachment in attachments {
match attachment.object_type.as_str() {
IDENTITY_PROOF => {
match parse_identity_proof(&self.id, attachment) {
Ok(proof) => identity_proofs.push(proof),
Err(error) => {
log::warn!(
"ignoring actor attachment of type {}: {}",
attachment.object_type,
error,
);
},
Err(error) => log_error(attachment, error),
};
},
LINK => {
match parse_payment_option(attachment) {
Ok(option) => payment_options.push(option),
Err(error) => log_error(attachment, error),
};
},
PROPERTY_VALUE => {
match parse_extra_field(attachment) {
Ok(field) => extra_fields.push(field),
Err(error) => {
log::warn!(
"ignoring actor attachment of type {}: {}",
attachment.object_type,
error,
);
},
Err(error) => log_error(attachment, error),
};
},
_ => {
log::warn!(
"ignoring actor attachment of type {}",
attachment.object_type,
);
log_error(attachment, ValidationError("unsupported type"));
},
};
};
};
(identity_proofs, extra_fields)
(identity_proofs, payment_options, extra_fields)
}
}
@ -231,6 +244,14 @@ pub fn get_local_actor(
let attachment = attach_identity_proof(proof);
attachments.push(attachment);
};
for payment_option in user.profile.payment_options.clone().into_inner() {
let attachment = attach_payment_option(
instance_url,
&user.id,
payment_option,
);
attachments.push(attachment);
};
for field in user.profile.extra_fields.clone().into_inner() {
let attachment = attach_extra_field(field);
attachments.push(attachment);

View file

@ -69,7 +69,8 @@ async fn prepare_remote_profile_data(
};
let avatar = fetch_actor_avatar(&actor, media_dir, None).await;
let banner = fetch_actor_banner(&actor, media_dir, None).await;
let (identity_proofs, extra_fields) = actor.parse_attachments();
let (identity_proofs, payment_options, extra_fields) =
actor.parse_attachments();
let profile_data = ProfileCreateData {
username: actor.preferred_username.clone(),
display_name: actor.name.clone(),
@ -78,7 +79,7 @@ async fn prepare_remote_profile_data(
avatar,
banner,
identity_proofs,
payment_options: vec![],
payment_options,
extra_fields,
actor_json: Some(actor),
};

View file

@ -56,7 +56,8 @@ pub async fn update_remote_profile(
};
let avatar = fetch_actor_avatar(&actor, media_dir, profile.avatar_file_name).await;
let banner = fetch_actor_banner(&actor, media_dir, profile.banner_file_name).await;
let (identity_proofs, extra_fields) = actor.parse_attachments();
let (identity_proofs, payment_options, extra_fields) =
actor.parse_attachments();
let mut profile_data = ProfileUpdateData {
display_name: actor.name.clone(),
bio: actor.summary.clone(),
@ -64,7 +65,7 @@ pub async fn update_remote_profile(
avatar,
banner,
identity_proofs,
payment_options: vec![],
payment_options,
extra_fields,
actor_json: Some(actor),
};

View file

@ -34,4 +34,5 @@ pub const ORDERED_COLLECTION_PAGE: &str = "OrderedCollectionPage";
// Misc
pub const HASHTAG: &str = "Hashtag";
pub const IDENTITY_PROOF: &str = "IdentityProof";
pub const LINK: &str = "Link";
pub const PROPERTY_VALUE: &str = "PropertyValue";

View file

@ -34,7 +34,7 @@ impl From<&EventType> for i16 {
EventType::Mention => 5,
EventType::Repost => 6,
EventType::Subscription => 7,
EventType::SubscriptionStart => panic!("not supported"),
EventType::SubscriptionStart => unimplemented!("not supported"),
EventType::SubscriptionExpiration => 9,
}
}