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::errors::ValidationError;
use crate::ethereum::identity::{ use crate::ethereum::identity::{
ETHEREUM_EIP191_PROOF, ETHEREUM_EIP191_PROOF,
DidPkh, DidPkh,
verify_identity_proof, 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; use super::types::ActorAttachment;
pub fn attach_identity_proof( pub fn attach_identity_proof(
@ -15,6 +27,7 @@ pub fn attach_identity_proof(
object_type: IDENTITY_PROOF.to_string(), object_type: IDENTITY_PROOF.to_string(),
name: proof.issuer.to_string(), name: proof.issuer.to_string(),
value: None, value: None,
href: None,
signature_algorithm: Some(proof.proof_type), signature_algorithm: Some(proof.proof_type),
signature_value: Some(proof.value), signature_value: Some(proof.value),
} }
@ -49,6 +62,43 @@ pub fn parse_identity_proof(
Ok(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( pub fn attach_extra_field(
field: ExtraField, field: ExtraField,
) -> ActorAttachment { ) -> ActorAttachment {
@ -56,6 +106,7 @@ pub fn attach_extra_field(
object_type: PROPERTY_VALUE.to_string(), object_type: PROPERTY_VALUE.to_string(),
name: field.name, name: field.name,
value: Some(field.value), value: Some(field.value),
href: None,
signature_algorithm: None, signature_algorithm: None,
signature_value: None, signature_value: None,
} }
@ -79,8 +130,11 @@ pub fn parse_extra_field(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::utils::id::new_uuid;
use super::*; use super::*;
const INSTANCE_URL: &str = "https://example.com";
#[test] #[test]
fn test_extra_field() { fn test_extra_field() {
let field = ExtraField { let field = ExtraField {
@ -90,8 +144,30 @@ mod tests {
}; };
let attachment = attach_extra_field(field.clone()); let attachment = attach_extra_field(field.clone());
assert_eq!(attachment.object_type, PROPERTY_VALUE); assert_eq!(attachment.object_type, PROPERTY_VALUE);
let parsed_field = parse_extra_field(&attachment).unwrap(); let parsed_field = parse_extra_field(&attachment).unwrap();
assert_eq!(parsed_field.name, field.name); assert_eq!(parsed_field.name, field.name);
assert_eq!(parsed_field.value, field.value); 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::{ use crate::activitypub::{
constants::{ACTOR_KEY_SUFFIX, AP_CONTEXT}, constants::{ACTOR_KEY_SUFFIX, AP_CONTEXT},
identifiers::{local_actor_id, LocalActorCollection}, 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::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::models::users::types::User;
use crate::utils::crypto::{deserialize_private_key, get_public_key_pem}; use crate::utils::crypto::{deserialize_private_key, get_public_key_pem};
use crate::utils::files::get_file_url; use crate::utils::files::get_file_url;
use super::attachments::{ use super::attachments::{
attach_extra_field, attach_extra_field,
attach_identity_proof, attach_identity_proof,
attach_payment_option,
parse_extra_field, parse_extra_field,
parse_identity_proof, parse_identity_proof,
parse_payment_option,
}; };
const W3ID_CONTEXT: &str = "https://w3id.org/security/v1"; const W3ID_CONTEXT: &str = "https://w3id.org/security/v1";
@ -48,6 +55,9 @@ pub struct ActorAttachment {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<String>, pub value: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub href: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub signature_algorithm: Option<String>, pub signature_algorithm: Option<String>,
@ -119,46 +129,49 @@ impl Actor {
Ok(actor_address) 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 identity_proofs = vec![];
let mut payment_options = vec![];
let mut extra_fields = 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 { if let Some(attachments) = &self.attachment {
for attachment in attachments { for attachment in attachments {
match attachment.object_type.as_str() { match attachment.object_type.as_str() {
IDENTITY_PROOF => { IDENTITY_PROOF => {
match parse_identity_proof(&self.id, attachment) { match parse_identity_proof(&self.id, attachment) {
Ok(proof) => identity_proofs.push(proof), Ok(proof) => identity_proofs.push(proof),
Err(error) => { Err(error) => log_error(attachment, error),
log::warn!( };
"ignoring actor attachment of type {}: {}",
attachment.object_type,
error,
);
}, },
LINK => {
match parse_payment_option(attachment) {
Ok(option) => payment_options.push(option),
Err(error) => log_error(attachment, error),
}; };
}, },
PROPERTY_VALUE => { PROPERTY_VALUE => {
match parse_extra_field(attachment) { match parse_extra_field(attachment) {
Ok(field) => extra_fields.push(field), Ok(field) => extra_fields.push(field),
Err(error) => { Err(error) => log_error(attachment, error),
log::warn!(
"ignoring actor attachment of type {}: {}",
attachment.object_type,
error,
);
},
}; };
}, },
_ => { _ => {
log::warn!( log_error(attachment, ValidationError("unsupported type"));
"ignoring actor attachment of type {}",
attachment.object_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); let attachment = attach_identity_proof(proof);
attachments.push(attachment); 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() { for field in user.profile.extra_fields.clone().into_inner() {
let attachment = attach_extra_field(field); let attachment = attach_extra_field(field);
attachments.push(attachment); 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 avatar = fetch_actor_avatar(&actor, media_dir, None).await;
let banner = fetch_actor_banner(&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 { let profile_data = ProfileCreateData {
username: actor.preferred_username.clone(), username: actor.preferred_username.clone(),
display_name: actor.name.clone(), display_name: actor.name.clone(),
@ -78,7 +79,7 @@ async fn prepare_remote_profile_data(
avatar, avatar,
banner, banner,
identity_proofs, identity_proofs,
payment_options: vec![], payment_options,
extra_fields, extra_fields,
actor_json: Some(actor), 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 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 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 { let mut profile_data = ProfileUpdateData {
display_name: actor.name.clone(), display_name: actor.name.clone(),
bio: actor.summary.clone(), bio: actor.summary.clone(),
@ -64,7 +65,7 @@ pub async fn update_remote_profile(
avatar, avatar,
banner, banner,
identity_proofs, identity_proofs,
payment_options: vec![], payment_options,
extra_fields, extra_fields,
actor_json: Some(actor), actor_json: Some(actor),
}; };

View file

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

View file

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