mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-25 21:01:08 +00:00
wip: separate activities for editing local/remote article
This commit is contained in:
parent
8a11cfed20
commit
c22df74b99
7 changed files with 117 additions and 61 deletions
|
@ -1,7 +1,7 @@
|
|||
use crate::database::DatabaseHandle;
|
||||
use crate::error::MyResult;
|
||||
use crate::federation::activities::create_article::CreateArticle;
|
||||
use crate::federation::activities::update_article::UpdateArticle;
|
||||
use crate::federation::activities::update_remote_article::UpdateRemoteArticle;
|
||||
use crate::federation::objects::article::DbArticle;
|
||||
use crate::federation::objects::edit::{ApubEdit, DbEdit, EditVersion};
|
||||
use crate::federation::objects::instance::DbInstance;
|
||||
|
@ -10,6 +10,7 @@ use activitypub_federation::fetch::object_id::ObjectId;
|
|||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use crate::federation::activities::update_local_article::UpdateLocalArticle;
|
||||
use axum::extract::Query;
|
||||
use axum::routing::{get, post};
|
||||
use axum::{Form, Json, Router};
|
||||
|
@ -93,9 +94,9 @@ async fn edit_article(
|
|||
article.clone()
|
||||
};
|
||||
|
||||
UpdateArticle::send_to_followers(edit, updated_article.clone(), &data).await?;
|
||||
UpdateLocalArticle::send(updated_article, &data).await?;
|
||||
} else {
|
||||
UpdateArticle::send_to_origin(
|
||||
UpdateRemoteArticle::send(
|
||||
edit,
|
||||
original_article.instance.dereference(&data).await?,
|
||||
&data,
|
||||
|
|
|
@ -2,4 +2,5 @@ pub mod accept;
|
|||
pub mod create_article;
|
||||
pub mod follow;
|
||||
pub mod reject;
|
||||
pub mod update_article;
|
||||
pub mod update_local_article;
|
||||
pub mod update_remote_article;
|
||||
|
|
71
src/federation/activities/update_local_article.rs
Normal file
71
src/federation/activities/update_local_article.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use crate::database::DatabaseHandle;
|
||||
use crate::error::MyResult;
|
||||
use crate::federation::objects::article::{ApubArticle, DbArticle};
|
||||
|
||||
use crate::federation::objects::instance::DbInstance;
|
||||
use crate::utils::generate_activity_id;
|
||||
use activitypub_federation::kinds::activity::UpdateType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
traits::{ActivityHandler, Object},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateLocalArticle {
|
||||
pub actor: ObjectId<DbInstance>,
|
||||
#[serde(deserialize_with = "deserialize_one_or_many")]
|
||||
pub to: Vec<Url>,
|
||||
pub object: ApubArticle,
|
||||
#[serde(rename = "type")]
|
||||
pub kind: UpdateType,
|
||||
pub id: Url,
|
||||
}
|
||||
|
||||
impl UpdateLocalArticle {
|
||||
/// Sent from article origin instance
|
||||
pub async fn send(article: DbArticle, data: &Data<DatabaseHandle>) -> MyResult<()> {
|
||||
debug_assert!(article.local);
|
||||
let local_instance = data.local_instance();
|
||||
let id = generate_activity_id(local_instance.ap_id.inner())?;
|
||||
let update = UpdateLocalArticle {
|
||||
actor: local_instance.ap_id.clone(),
|
||||
to: local_instance.follower_ids(),
|
||||
object: article.into_json(data).await?,
|
||||
kind: Default::default(),
|
||||
id,
|
||||
};
|
||||
local_instance.send_to_followers(update, data).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for UpdateLocalArticle {
|
||||
type DataType = DatabaseHandle;
|
||||
type Error = crate::error::Error;
|
||||
|
||||
fn id(&self) -> &Url {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn actor(&self) -> &Url {
|
||||
self.actor.inner()
|
||||
}
|
||||
|
||||
async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Received on article follower instances (where article is always remote)
|
||||
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||
DbArticle::from_json(self.object, data).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,61 +1,45 @@
|
|||
use crate::database::DatabaseHandle;
|
||||
use crate::error::MyResult;
|
||||
use crate::federation::objects::article::DbArticle;
|
||||
|
||||
use crate::federation::objects::edit::{ApubEdit, DbEdit};
|
||||
use crate::federation::objects::instance::DbInstance;
|
||||
use crate::utils::generate_activity_id;
|
||||
use activitypub_federation::kinds::activity::CreateType;
|
||||
use activitypub_federation::kinds::activity::UpdateType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
traits::{ActivityHandler, Object},
|
||||
};
|
||||
use diffy::{apply, Patch};
|
||||
|
||||
use crate::federation::activities::reject::RejectEdit;
|
||||
use crate::federation::activities::update_local_article::UpdateLocalArticle;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateArticle {
|
||||
pub struct UpdateRemoteArticle {
|
||||
pub actor: ObjectId<DbInstance>,
|
||||
#[serde(deserialize_with = "deserialize_one_or_many")]
|
||||
pub to: Vec<Url>,
|
||||
pub object: ApubEdit,
|
||||
#[serde(rename = "type")]
|
||||
pub kind: CreateType,
|
||||
pub kind: UpdateType,
|
||||
pub id: Url,
|
||||
}
|
||||
|
||||
impl UpdateArticle {
|
||||
pub async fn send_to_followers(
|
||||
edit: DbEdit,
|
||||
article: DbArticle,
|
||||
data: &Data<DatabaseHandle>,
|
||||
) -> MyResult<()> {
|
||||
debug_assert!(article.local);
|
||||
let local_instance = data.local_instance();
|
||||
let id = generate_activity_id(local_instance.ap_id.inner())?;
|
||||
let update = UpdateArticle {
|
||||
actor: local_instance.ap_id.clone(),
|
||||
to: local_instance.follower_ids(),
|
||||
object: edit.into_json(data).await?,
|
||||
kind: Default::default(),
|
||||
id,
|
||||
};
|
||||
local_instance.send_to_followers(update, data).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_to_origin(
|
||||
impl UpdateRemoteArticle {
|
||||
/// Sent by a follower instance
|
||||
pub async fn send(
|
||||
edit: DbEdit,
|
||||
article_instance: DbInstance,
|
||||
data: &Data<DatabaseHandle>,
|
||||
) -> MyResult<()> {
|
||||
let local_instance = data.local_instance();
|
||||
let id = generate_activity_id(local_instance.ap_id.inner())?;
|
||||
let update = UpdateArticle {
|
||||
let update = UpdateRemoteArticle {
|
||||
actor: local_instance.ap_id.clone(),
|
||||
to: vec![article_instance.ap_id.into_inner()],
|
||||
object: edit.into_json(data).await?,
|
||||
|
@ -68,8 +52,9 @@ impl UpdateArticle {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for UpdateArticle {
|
||||
impl ActivityHandler for UpdateRemoteArticle {
|
||||
type DataType = DatabaseHandle;
|
||||
type Error = crate::error::Error;
|
||||
|
||||
|
@ -85,23 +70,30 @@ impl ActivityHandler for UpdateArticle {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Received on article origin instances
|
||||
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||
if DbEdit::from_json(self.object.clone(), data).await.is_ok() {
|
||||
let article_local = {
|
||||
let lock = data.articles.lock().unwrap();
|
||||
let article = lock.get(self.object.object.inner()).unwrap();
|
||||
article.local
|
||||
};
|
||||
match DbEdit::from_json(self.object.clone(), data).await {
|
||||
Ok(edit) => {
|
||||
let article = {
|
||||
let lock = data.articles.lock().unwrap();
|
||||
let article = lock.get(self.object.object.inner()).unwrap();
|
||||
article.clone()
|
||||
};
|
||||
{
|
||||
let patch = Patch::from_str(&edit.diff)?;
|
||||
let applied = apply(&article.text, &patch)?;
|
||||
let mut lock = data.articles.lock().unwrap();
|
||||
let article = lock.get_mut(edit.article_id.inner()).unwrap();
|
||||
article.edits.push(edit.clone());
|
||||
article.text = applied;
|
||||
}
|
||||
|
||||
if article_local {
|
||||
// No need to wrap in announce, we can construct a new activity as all important info
|
||||
// is in the object and result fields.
|
||||
let local_instance = data.local_instance();
|
||||
let id = generate_activity_id(local_instance.ap_id.inner())?;
|
||||
let update = UpdateArticle {
|
||||
let update = UpdateLocalArticle {
|
||||
actor: local_instance.ap_id.clone(),
|
||||
to: local_instance.follower_ids(),
|
||||
object: self.object,
|
||||
object: article.clone().into_json(data).await?,
|
||||
kind: Default::default(),
|
||||
id,
|
||||
};
|
||||
|
@ -109,9 +101,10 @@ impl ActivityHandler for UpdateArticle {
|
|||
.send_to_followers(update, data)
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
let user_instance = self.actor.dereference(data).await?;
|
||||
RejectEdit::send(self.object.clone(), user_instance, data).await?;
|
||||
Err(_e) => {
|
||||
let user_instance = self.actor.dereference(data).await?;
|
||||
RejectEdit::send(self.object.clone(), user_instance, data).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
|
@ -42,7 +42,7 @@ pub struct ApubArticle {
|
|||
pub(crate) attributed_to: ObjectId<DbInstance>,
|
||||
#[serde(deserialize_with = "deserialize_one_or_many")]
|
||||
pub(crate) to: Vec<Url>,
|
||||
edits: CollectionId<DbEditCollection>,
|
||||
pub edits: CollectionId<DbEditCollection>,
|
||||
latest_version: EditVersion,
|
||||
content: String,
|
||||
name: String,
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::federation::objects::article::DbArticle;
|
|||
use activitypub_federation::config::Data;
|
||||
use activitypub_federation::fetch::object_id::ObjectId;
|
||||
use activitypub_federation::traits::Object;
|
||||
use diffy::{apply, create_patch, Patch};
|
||||
use diffy::create_patch;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Digest;
|
||||
use sha2::Sha224;
|
||||
|
@ -104,21 +104,9 @@ impl Object for DbEdit {
|
|||
version: json.version,
|
||||
local: false,
|
||||
};
|
||||
let article_read = {
|
||||
let lock = data.articles.lock().unwrap();
|
||||
lock.get(edit.article_id.inner()).unwrap().clone()
|
||||
};
|
||||
let patch = Patch::from_str(&edit.diff)?;
|
||||
// Dont apply the edit if we already fetched an update Article version.
|
||||
// TODO: this assumes that we always receive edits in the correct order, probably need to
|
||||
// include the parent for each edit
|
||||
//if article_read.latest_version != edit.version {
|
||||
let applied = apply(&article_read.text, &patch)?;
|
||||
let mut lock = data.articles.lock().unwrap();
|
||||
let article = lock.get_mut(edit.article_id.inner()).unwrap();
|
||||
article.edits.push(edit.clone());
|
||||
article.text = applied;
|
||||
Ok(edit)
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ use axum::extract::Path;
|
|||
|
||||
use crate::federation::activities::create_article::CreateArticle;
|
||||
use crate::federation::activities::reject::RejectEdit;
|
||||
use crate::federation::activities::update_article::UpdateArticle;
|
||||
use crate::federation::activities::update_local_article::UpdateLocalArticle;
|
||||
use crate::federation::activities::update_remote_article::UpdateRemoteArticle;
|
||||
use crate::federation::objects::article::ApubArticle;
|
||||
use crate::federation::objects::articles_collection::{ArticleCollection, DbArticleCollection};
|
||||
use crate::federation::objects::edits_collection::{ApubEditCollection, DbEditCollection};
|
||||
|
@ -85,7 +86,8 @@ pub enum InboxActivities {
|
|||
Follow(Follow),
|
||||
Accept(Accept),
|
||||
CreateArticle(CreateArticle),
|
||||
UpdateArticle(UpdateArticle),
|
||||
UpdateLocalArticle(UpdateLocalArticle),
|
||||
UpdateRemoteArticle(UpdateRemoteArticle),
|
||||
RejectEdit(RejectEdit),
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue