fix: persist note_extensions on AP Update activity
Some checks failed
lint / lint (push) Has been cancelled
test / unit (push) Has been cancelled

on_update was discarding custom fields (posterUrl, movieTitle, etc),
so remote notes from movies-diary lost posters after Update delivery.
This commit is contained in:
2026-06-04 23:28:58 +02:00
parent 90d13c883b
commit 6dbd4dafdc
7 changed files with 22 additions and 9 deletions

1
Cargo.lock generated
View File

@@ -280,6 +280,7 @@ dependencies = [
"domain",
"futures",
"hex",
"serde_json",
"sha2",
"thiserror 2.0.18",
"tokio",

View File

@@ -210,11 +210,11 @@ impl ApObjectHandler for ThoughtsObjectHandler {
let obj_type = object.get("type").and_then(|v| v.as_str()).unwrap_or("");
match obj_type {
"Note" | "Article" | "Page" => {
let Some((note, _)) = ThoughtNote::try_from_ap(object) else {
let Some((note, note_extensions)) = ThoughtNote::try_from_ap(object) else {
return Ok(());
};
self.repo
.apply_note_update(ap_id.as_str(), &note.content)
.apply_note_update(ap_id.as_str(), &note.content, note_extensions)
.await
.map_err(|e| anyhow!("{e}"))
}

View File

@@ -76,7 +76,12 @@ pub trait ActivityPubRepository: Send + Sync {
async fn accept_note(&self, input: AcceptNoteInput<'_>) -> Result<ThoughtId, DomainError>;
/// Apply an Update to a previously accepted remote Note.
async fn apply_note_update(&self, ap_id: &str, new_content: &str) -> Result<(), DomainError>;
async fn apply_note_update(
&self,
ap_id: &str,
new_content: &str,
note_extensions: Option<serde_json::Value>,
) -> Result<(), DomainError>;
/// Remove a specific remote Note (Delete activity). Only touches
/// remotely-originated thoughts.

View File

@@ -270,13 +270,19 @@ impl ActivityPubRepository for PgActivityPubRepository {
Ok(ThoughtId::from_uuid(row.0))
}
async fn apply_note_update(&self, ap_id: &str, new_content: &str) -> Result<(), DomainError> {
async fn apply_note_update(
&self,
ap_id: &str,
new_content: &str,
note_extensions: Option<serde_json::Value>,
) -> Result<(), DomainError> {
let capped: String = new_content.chars().take(MAX_REMOTE_CONTENT_CHARS).collect();
sqlx::query(
"UPDATE thoughts SET content=$2,updated_at=NOW() WHERE ap_id=$1 AND local=false",
"UPDATE thoughts SET content=$2,note_extensions=$3,updated_at=NOW() WHERE ap_id=$1 AND local=false",
)
.bind(ap_id)
.bind(&capped)
.bind(&note_extensions)
.execute(&self.pool)
.await
.into_domain()

View File

@@ -19,5 +19,6 @@ bytes = { workspace = true }
futures = { workspace = true }
[dev-dependencies]
tokio = { workspace = true, features = ["full"] }
domain = { workspace = true, features = ["test-helpers"] }
tokio = { workspace = true, features = ["full"] }
domain = { workspace = true, features = ["test-helpers"] }
serde_json = { workspace = true }

View File

@@ -90,7 +90,7 @@ impl ActivityPubRepository for TestApRepo {
) -> Result<ThoughtId, DomainError> {
Ok(ThoughtId::from_uuid(uuid::Uuid::new_v4()))
}
async fn apply_note_update(&self, _ap_id: &str, _new_content: &str) -> Result<(), DomainError> {
async fn apply_note_update(&self, _ap_id: &str, _new_content: &str, _: Option<serde_json::Value>) -> Result<(), DomainError> {
Ok(())
}
async fn retract_note(&self, _ap_id: &str) -> Result<(), DomainError> {

View File

@@ -66,7 +66,7 @@ impl ActivityPubRepository for NoOpApRepo {
) -> Result<ThoughtId, DomainError> {
Ok(ThoughtId::from_uuid(uuid::Uuid::new_v4()))
}
async fn apply_note_update(&self, _: &str, _: &str) -> Result<(), DomainError> {
async fn apply_note_update(&self, _: &str, _: &str, _: Option<serde_json::Value>) -> Result<(), DomainError> {
Ok(())
}
async fn retract_note(&self, _: &str) -> Result<(), DomainError> {