From 64375c23c5cc4a063282c1ee0edd9e58d3d87c38 Mon Sep 17 00:00:00 2001 From: kieran Date: Thu, 8 Aug 2024 22:13:28 +0100 Subject: [PATCH] Get replaceable events --- README.md | 14 ++++++++++++++ src/events.rs | 27 ++++++++++++++++++++++----- src/store.rs | 49 +++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..28c0c19 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# nostr-services-rs + +Simple API for serving nostr applications + +## API + +### `POST /event` +Save event in database + +### `GET /event/` +Get event by ID as `hex/nevent/naddr` + +### `GET /event//` +Get regular replaceable event for pubkey (non-parameterized) \ No newline at end of file diff --git a/src/events.rs b/src/events.rs index 9b5747d..b8f3cfc 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,13 +1,14 @@ use anyhow::Error; -use nostr::{Event, EventId, JsonUtil, Kind}; +use nostr::{Event, EventId, JsonUtil, Kind, PublicKey}; use nostr_database::{DatabaseError, DynNostrDatabase, NostrDatabase}; -use rocket::{Data, Route, State}; +use rocket::{Route, State}; use rocket::http::Status; use rocket::serde::json::Json; + use crate::store::SledDatabase; pub fn routes() -> Vec { - routes![import_event, get_event] + routes![import_event, get_event, get_event_by_kind] } #[rocket::post("/event", data = "")] @@ -29,7 +30,7 @@ async fn import_event( } #[rocket::get("/event/")] -async fn get_event( +fn get_event( db: &State, id: &str, ) -> Option> { @@ -37,7 +38,23 @@ async fn get_event( Ok(i) => i, _ => return None }; - match db.event_by_id(id).await { + match db.event_by_id(id) { + Ok(ev) => Some(Json::from(ev)), + _ => None + } +} + +#[rocket::get("/event//")] +fn get_event_by_kind( + db: &State, + kind: u32, + pubkey: &str, +) -> Option> { + let pk = match PublicKey::parse(pubkey) { + Ok(i) => i, + _ => return None + }; + match db.event_by_kind_pubkey(kind, &pk) { Ok(ev) => Some(Json::from(ev)), _ => None } diff --git a/src/store.rs b/src/store.rs index bfacad3..45c0743 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,7 +1,8 @@ use std::fmt::Debug; use std::sync::Arc; -use nostr::{Event, EventId}; +use nostr::{Event, EventId, PublicKey}; +use nostr::util::hex; use nostr_database::{FlatBufferBuilder, FlatBufferDecode, FlatBufferEncode}; use sled::{Db, IVec}; use tokio::sync::Mutex; @@ -34,31 +35,46 @@ impl SledDatabase { } fn write_replaceable_index(&self, event: &Event) -> Result { - let rpk = Self::replaceable_index_key(event); + let rpk = Self::replaceable_index_key_of_event(event); - if let Err(e) = self.db.update_and_fetch(rpk, |prev| { + match self.db.update_and_fetch(rpk, |prev| { if let Some(prev) = prev { let timestamp: u64 = u64::from_be_bytes(prev[..8].try_into().unwrap()); if timestamp < event.created_at.as_u64() { let new_val = Self::replaceable_index_value(event); Some(IVec::from(new_val.as_slice())) } else { - None + Some(IVec::from(prev)) } } else { let new_val = Self::replaceable_index_value(event); Some(IVec::from(new_val.as_slice())) } }) { - return Err(anyhow::Error::new(e)); + Err(e) => Err(anyhow::Error::new(e)), + Ok(v) => { + match v { + Some(v) => { + info!("Wrote index {} = {}", hex::encode(rpk), hex::encode(v.as_ref())); + Ok(true) + } + None => { + info!("Duplicate or older index {}", hex::encode(rpk)); + Ok(false) + } + } + } } - Ok(false) } - fn replaceable_index_key(event: &Event) -> [u8; 36] { + fn replaceable_index_key_of_event(event: &Event) -> [u8; 36] { + Self::replaceable_index_key(event.kind.as_u32(), &event.pubkey) + } + + fn replaceable_index_key(kind: u32, pubkey: &PublicKey) -> [u8; 36] { let mut rpk = [0; 4 + 32]; // kind:pubkey - rpk[..4].copy_from_slice(&event.kind.as_u32().to_be_bytes()); - rpk[4..].copy_from_slice(&event.pubkey.to_bytes()); + rpk[..4].copy_from_slice(&kind.to_be_bytes()); + rpk[4..].copy_from_slice(&pubkey.to_bytes()); rpk } @@ -69,7 +85,7 @@ impl SledDatabase { new_val } - pub async fn event_by_id(&self, event_id: EventId) -> Result { + pub fn event_by_id(&self, event_id: EventId) -> Result { match self.db.get(event_id.as_bytes()) { Ok(v) => match v { Some(v) => match Event::decode(&v) { @@ -81,4 +97,17 @@ impl SledDatabase { Err(e) => Err(anyhow::Error::new(e)) } } + + pub fn event_by_kind_pubkey(&self, kind: u32, pubkey: &PublicKey) -> Result { + let rpk = Self::replaceable_index_key(kind, pubkey); + match self.db.get(rpk) { + Ok(v) => match v { + Some(v) => { + self.event_by_id(EventId::from_slice(v[8..].as_ref())?) + } + None => Err(anyhow::Error::msg("Not Found")) + } + Err(e) => Err(anyhow::Error::new(e)) + } + } } \ No newline at end of file