about summary refs log tree commit diff
path: root/kittybox-rs/src/database/file
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2023-07-22 12:24:08 +0300
committerVika <vika@fireburn.ru>2023-07-22 12:24:08 +0300
commit39ddd3689aa4ef38580ea90087e1e204b55fcfc7 (patch)
treedfdef357c0e97543d8957c4d700da670081fe018 /kittybox-rs/src/database/file
parent6a88707be1651f457df0960ec0ddc78dadf8ed3a (diff)
downloadkittybox-39ddd3689aa4ef38580ea90087e1e204b55fcfc7.tar.zst
database: add "add_or_update_webmention" operation
This is an operation that atomically adds or updates a webmention cite
attached to a post. This is used so a database backend can optimize
for it (for example, using a transaction or shifting the JSON
modification operation to the database)
Diffstat (limited to 'kittybox-rs/src/database/file')
-rw-r--r--kittybox-rs/src/database/file/mod.rs50
1 files changed, 50 insertions, 0 deletions
diff --git a/kittybox-rs/src/database/file/mod.rs b/kittybox-rs/src/database/file/mod.rs
index fe53ea5..ca8e2ac 100644
--- a/kittybox-rs/src/database/file/mod.rs
+++ b/kittybox-rs/src/database/file/mod.rs
@@ -3,6 +3,7 @@ use crate::database::{ErrorKind, Result, settings, Storage, StorageError};
 use crate::micropub::{MicropubUpdate, MicropubPropertyDeletion};
 use async_trait::async_trait;
 use futures::{stream, StreamExt, TryStreamExt};
+use kittybox_util::MentionType;
 use serde_json::json;
 use std::borrow::Cow;
 use std::collections::HashMap;
@@ -680,4 +681,53 @@ impl Storage for FileStorage {
         tokio::fs::File::open(path.parent().unwrap()).await?.sync_all().await?;
         Ok(())
     }
+
+    #[tracing::instrument(skip(self))]
+    async fn add_or_update_webmention(&self, target: &str, mention_type: MentionType, mention: serde_json::Value) -> Result<()> {
+        let path = url_to_path(&self.root_dir, target);
+        let tempfilename = path.with_extension("tmp");
+
+        let mut temp = OpenOptions::new()
+                .write(true)
+                .create_new(true)
+                .open(&tempfilename)
+                .await?;
+        let mut file = OpenOptions::new().read(true).open(&path).await?;
+
+        let mut post: serde_json::Value = {
+            let mut content = String::new();
+            file.read_to_string(&mut content).await?;
+            drop(file);
+
+            serde_json::from_str(&content)?
+        };
+
+        let key: &'static str = match mention_type {
+            MentionType::Reply => "reply",
+            MentionType::Like => "like",
+            MentionType::Repost => "repost",
+            MentionType::Bookmark => "bookmark",
+            MentionType::Mention => "mention",
+        };
+        let mention_uid = mention["properties"]["uid"][0].clone();
+        if let Some(values) = post["properties"][key].as_array_mut() {
+            for value in values.iter_mut() {
+                if value["properties"]["uid"][0] == mention_uid {
+                    *value = mention;
+                    break;
+                }
+            }
+        } else {
+            post["properties"][key] = serde_json::Value::Array(vec![mention]);
+        }
+
+        temp.write_all(post.to_string().as_bytes()).await?;
+        temp.flush().await?;
+        temp.sync_all().await?;
+        drop(temp);
+        tokio::fs::rename(tempfilename, &path).await?;
+        tokio::fs::File::open(path.parent().unwrap()).await?.sync_all().await?;
+
+        Ok(())
+    }
 }