mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-22 12:41:10 +00:00
generate edit on article update (without federation)
This commit is contained in:
parent
afbb81c0d1
commit
5f7837d843
6 changed files with 50 additions and 11 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -625,6 +625,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
|
"sha2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
|
|
|
@ -17,6 +17,7 @@ futures = "0.3.29"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
serde = "1.0.192"
|
serde = "1.0.192"
|
||||||
serde_json = "1.0.108"
|
serde_json = "1.0.108"
|
||||||
|
sha2 = "0.10.8"
|
||||||
tokio = { version = "1.34.0", features = ["full"] }
|
tokio = { version = "1.34.0", features = ["full"] }
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
url = "2.4.1"
|
url = "2.4.1"
|
||||||
|
|
14
src/api.rs
14
src/api.rs
|
@ -15,6 +15,7 @@ use axum::{Form, Json, Router};
|
||||||
use axum_macros::debug_handler;
|
use axum_macros::debug_handler;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use crate::federation::objects::edit::DbEdit;
|
||||||
|
|
||||||
pub fn api_routes() -> Router {
|
pub fn api_routes() -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
|
@ -82,21 +83,28 @@ async fn edit_article(
|
||||||
data: Data<DatabaseHandle>,
|
data: Data<DatabaseHandle>,
|
||||||
Form(edit_article): Form<EditArticle>,
|
Form(edit_article): Form<EditArticle>,
|
||||||
) -> MyResult<Json<DbArticle>> {
|
) -> MyResult<Json<DbArticle>> {
|
||||||
let article = {
|
let original_article = {
|
||||||
|
let mut lock = data.articles.lock().unwrap();
|
||||||
|
let article = lock.get_mut(edit_article.ap_id.inner()).unwrap();
|
||||||
|
article.clone()
|
||||||
|
};
|
||||||
|
let edit = DbEdit::new(&original_article, &edit_article.new_text)?;
|
||||||
|
let updated_article = {
|
||||||
let mut lock = data.articles.lock().unwrap();
|
let mut lock = data.articles.lock().unwrap();
|
||||||
let article = lock.get_mut(edit_article.ap_id.inner()).unwrap();
|
let article = lock.get_mut(edit_article.ap_id.inner()).unwrap();
|
||||||
article.text = edit_article.new_text;
|
article.text = edit_article.new_text;
|
||||||
|
article.edits.push(edit);
|
||||||
article.clone()
|
article.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
CreateOrUpdateArticle::send_to_local_followers(
|
CreateOrUpdateArticle::send_to_local_followers(
|
||||||
article.clone(),
|
updated_article.clone(),
|
||||||
CreateOrUpdateType::Update,
|
CreateOrUpdateType::Update,
|
||||||
&data,
|
&data,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Json(article))
|
Ok(Json(updated_article))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use crate::database::DatabaseHandle;
|
use crate::database::DatabaseHandle;
|
||||||
use crate::error::Error;
|
use crate::error::{Error, MyResult};
|
||||||
use crate::federation::objects::article::DbArticle;
|
use crate::federation::objects::article::DbArticle;
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
use activitypub_federation::fetch::object_id::ObjectId;
|
use activitypub_federation::fetch::object_id::ObjectId;
|
||||||
use activitypub_federation::traits::Object;
|
use activitypub_federation::traits::Object;
|
||||||
|
use diffy::create_patch;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha2::Sha224;
|
||||||
|
use sha2::{Digest};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
/// Represents a single change to the article.
|
/// Represents a single change to the article.
|
||||||
|
@ -15,6 +18,21 @@ pub struct DbEdit {
|
||||||
pub local: bool,
|
pub local: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DbEdit {
|
||||||
|
pub fn new(original_article: &DbArticle, updated_text: &str) -> MyResult<Self> {
|
||||||
|
let diff = create_patch(&original_article.text, updated_text);
|
||||||
|
let mut sha224 = Sha224::new();
|
||||||
|
sha224.update(diff.to_bytes());
|
||||||
|
let hash = format!("{:X}", sha224.finalize());
|
||||||
|
let edit_id = ObjectId::parse(&format!("{}/{}", original_article.ap_id, hash))?;
|
||||||
|
Ok(DbEdit {
|
||||||
|
id: edit_id,
|
||||||
|
diff: diff.to_string(),
|
||||||
|
local: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum EditType {
|
pub enum EditType {
|
||||||
Edit,
|
Edit,
|
||||||
|
@ -26,7 +44,6 @@ pub struct ApubEdit {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: EditType,
|
kind: EditType,
|
||||||
id: ObjectId<DbEdit>,
|
id: ObjectId<DbEdit>,
|
||||||
article_id: ObjectId<DbArticle>,
|
|
||||||
diff: String,
|
diff: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +61,11 @@ impl Object for DbEdit {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
|
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
|
||||||
todo!()
|
Ok(ApubEdit {
|
||||||
|
kind: EditType::Edit,
|
||||||
|
id: self.id,
|
||||||
|
diff: self.diff,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verify(
|
async fn verify(
|
||||||
|
@ -52,13 +73,17 @@ impl Object for DbEdit {
|
||||||
_expected_domain: &Url,
|
_expected_domain: &Url,
|
||||||
_data: &Data<Self::DataType>,
|
_data: &Data<Self::DataType>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
todo!()
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn from_json(
|
async fn from_json(
|
||||||
_json: Self::Kind,
|
json: Self::Kind,
|
||||||
_data: &Data<Self::DataType>,
|
_data: &Data<Self::DataType>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
todo!()
|
Ok(Self {
|
||||||
|
id: json.id,
|
||||||
|
diff: json.diff,
|
||||||
|
local: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,9 +75,11 @@ impl Collection for DbEditCollection {
|
||||||
try_join_all(apub.items.into_iter().map(|i| DbEdit::from_json(i, data))).await?;
|
try_join_all(apub.items.into_iter().map(|i| DbEdit::from_json(i, data))).await?;
|
||||||
let mut articles = data.articles.lock().unwrap();
|
let mut articles = data.articles.lock().unwrap();
|
||||||
let article = articles.get_mut(owner.ap_id.inner()).unwrap();
|
let article = articles.get_mut(owner.ap_id.inner()).unwrap();
|
||||||
|
let edit_ids = article.edits.iter().map(|e| e.id.clone()).collect::<Vec<_>>();
|
||||||
for e in edits.clone() {
|
for e in edits.clone() {
|
||||||
// TODO: edits need a unique id to avoid pushing duplicates
|
if !edit_ids.contains(&&e.id) {
|
||||||
article.edits.push(e);
|
article.edits.push(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// TODO: return value propably not needed
|
// TODO: return value propably not needed
|
||||||
Ok(DbEditCollection(edits))
|
Ok(DbEditCollection(edits))
|
||||||
|
|
|
@ -153,6 +153,8 @@ async fn test_federate_article_changes() -> MyResult<()> {
|
||||||
};
|
};
|
||||||
let edit_res: DbArticle = patch(data.hostname_beta, "article", &edit_form).await?;
|
let edit_res: DbArticle = patch(data.hostname_beta, "article", &edit_form).await?;
|
||||||
assert_eq!(edit_res.text, edit_form.new_text);
|
assert_eq!(edit_res.text, edit_form.new_text);
|
||||||
|
assert_eq!(edit_res.edits.len(), 1);
|
||||||
|
assert!(edit_res.edits[0].id.to_string().starts_with(&edit_res.ap_id.to_string()));
|
||||||
|
|
||||||
// edit should be federated to alpha
|
// edit should be federated to alpha
|
||||||
let get_article = GetArticle {
|
let get_article = GetArticle {
|
||||||
|
|
Loading…
Reference in a new issue