use activitypub_federation::{ config::Data, fetch::object_id::ObjectId, http_signatures::generate_actor_keypair, kinds::actor::PersonType, protocol::public_key::PublicKey, traits::{Actor, Object}, }; use async_trait::async_trait; use serde::{Deserialize, Serialize}; use tracing::trace; use url::Url; use crate::{error::AppError, state::AppHandle}; #[derive(PartialEq, Clone, Debug)] pub(crate) struct User { pub username: String, pub ap_id: ObjectId, pub private_key: Option, pub public_key: String, pub inbox: Url, } impl User { pub fn new(username: &str) -> Result { trace!("creating a new user"); let keys = generate_actor_keypair()?; let stub = &format!("http://localhost/users/{username}"); Ok(Self { username: username.to_owned(), ap_id: Url::parse(stub)?.into(), private_key: Some(keys.private_key), public_key: keys.public_key, inbox: Url::parse(&format!("{stub}/inbox"))?, }) } } #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct Person { #[serde(rename = "type")] kind: PersonType, preferred_username: String, id: ObjectId, inbox: Url, public_key: PublicKey, } #[async_trait] impl Object for User { #[doc = " App data type passed to handlers. Must be identical to"] #[doc = " [crate::config::FederationConfigBuilder::app_data] type."] type DataType = AppHandle; #[doc = " The type of protocol struct which gets sent over network to federate this database struct."] type Kind = Person; #[doc = " Error type returned by handler methods"] type Error = AppError; #[doc = " `id` field of the object"] fn id(&self) -> &Url { self.ap_id.inner() } #[doc = " Try to read the object with given `id` from local database."] #[doc = " Should return `Ok(None)` if not found."] async fn read_from_id( object_id: Url, data: &Data, ) -> Result, Self::Error> { todo!() } #[doc = " Convert database type to Activitypub type."] #[doc = " Called when a local object gets fetched by another instance over HTTP, or when an object"] #[doc = " gets sent in an activity."] async fn into_json(self, data: &Data) -> Result { Ok(Person { preferred_username: self.username.clone(), kind: Default::default(), id: self.ap_id.clone(), inbox: self.inbox.clone(), public_key: self.public_key(), }) } #[doc = " Verifies that the received object is valid."] #[doc = " You should check here that the domain of id matches `expected_domain`. Additionally you"] #[doc = " should perform any application specific checks."] #[doc = " It is necessary to use a separate method for this, because it might be used for activities"] #[doc = " like `Delete/Note`, which shouldn\'t perform any database write for the inner `Note`."] async fn verify( json: &Self::Kind, expected_domain: &Url, data: &Data, ) -> Result<(), Self::Error> { todo!() } #[doc = " Convert object from ActivityPub type to database type."] #[doc = " Called when an object is received from HTTP fetch or as part of an activity. This method"] #[doc = " should write the received object to database. Note that there is no distinction between"] #[doc = " create and update, so an `upsert` operation should be used."] async fn from_json(json: Self::Kind, data: &Data) -> Result { todo!() } } impl Actor for User { fn public_key_pem(&self) -> &str { &self.public_key } fn private_key_pem(&self) -> Option { self.private_key.clone() } fn inbox(&self) -> Url { self.inbox.clone() } }