Get replaceable events

This commit is contained in:
kieran 2024-08-08 22:13:28 +01:00
parent 02bc00754c
commit 64375c23c5
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
3 changed files with 75 additions and 15 deletions

14
README.md Normal file
View File

@ -0,0 +1,14 @@
# nostr-services-rs
Simple API for serving nostr applications
## API
### `POST /event`
Save event in database
### `GET /event/<event-id>`
Get event by ID as `hex/nevent/naddr`
### `GET /event/<kind>/<pubkey>`
Get regular replaceable event for pubkey (non-parameterized)

View File

@ -1,13 +1,14 @@
use anyhow::Error; use anyhow::Error;
use nostr::{Event, EventId, JsonUtil, Kind}; use nostr::{Event, EventId, JsonUtil, Kind, PublicKey};
use nostr_database::{DatabaseError, DynNostrDatabase, NostrDatabase}; use nostr_database::{DatabaseError, DynNostrDatabase, NostrDatabase};
use rocket::{Data, Route, State}; use rocket::{Route, State};
use rocket::http::Status; use rocket::http::Status;
use rocket::serde::json::Json; use rocket::serde::json::Json;
use crate::store::SledDatabase; use crate::store::SledDatabase;
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![import_event, get_event] routes![import_event, get_event, get_event_by_kind]
} }
#[rocket::post("/event", data = "<data>")] #[rocket::post("/event", data = "<data>")]
@ -29,7 +30,7 @@ async fn import_event(
} }
#[rocket::get("/event/<id>")] #[rocket::get("/event/<id>")]
async fn get_event( fn get_event(
db: &State<SledDatabase>, db: &State<SledDatabase>,
id: &str, id: &str,
) -> Option<Json<Event>> { ) -> Option<Json<Event>> {
@ -37,7 +38,23 @@ async fn get_event(
Ok(i) => i, Ok(i) => i,
_ => return None _ => 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/<kind>/<pubkey>")]
fn get_event_by_kind(
db: &State<SledDatabase>,
kind: u32,
pubkey: &str,
) -> Option<Json<Event>> {
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)), Ok(ev) => Some(Json::from(ev)),
_ => None _ => None
} }

View File

@ -1,7 +1,8 @@
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
use nostr::{Event, EventId}; use nostr::{Event, EventId, PublicKey};
use nostr::util::hex;
use nostr_database::{FlatBufferBuilder, FlatBufferDecode, FlatBufferEncode}; use nostr_database::{FlatBufferBuilder, FlatBufferDecode, FlatBufferEncode};
use sled::{Db, IVec}; use sled::{Db, IVec};
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -34,31 +35,46 @@ impl SledDatabase {
} }
fn write_replaceable_index(&self, event: &Event) -> Result<bool, anyhow::Error> { fn write_replaceable_index(&self, event: &Event) -> Result<bool, anyhow::Error> {
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 { if let Some(prev) = prev {
let timestamp: u64 = u64::from_be_bytes(prev[..8].try_into().unwrap()); let timestamp: u64 = u64::from_be_bytes(prev[..8].try_into().unwrap());
if timestamp < event.created_at.as_u64() { if timestamp < event.created_at.as_u64() {
let new_val = Self::replaceable_index_value(event); let new_val = Self::replaceable_index_value(event);
Some(IVec::from(new_val.as_slice())) Some(IVec::from(new_val.as_slice()))
} else { } else {
None Some(IVec::from(prev))
} }
} else { } else {
let new_val = Self::replaceable_index_value(event); let new_val = Self::replaceable_index_value(event);
Some(IVec::from(new_val.as_slice())) 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 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(&kind.to_be_bytes());
rpk[4..].copy_from_slice(&event.pubkey.to_bytes()); rpk[4..].copy_from_slice(&pubkey.to_bytes());
rpk rpk
} }
@ -69,7 +85,7 @@ impl SledDatabase {
new_val new_val
} }
pub async fn event_by_id(&self, event_id: EventId) -> Result<Event, anyhow::Error> { pub fn event_by_id(&self, event_id: EventId) -> Result<Event, anyhow::Error> {
match self.db.get(event_id.as_bytes()) { match self.db.get(event_id.as_bytes()) {
Ok(v) => match v { Ok(v) => match v {
Some(v) => match Event::decode(&v) { Some(v) => match Event::decode(&v) {
@ -81,4 +97,17 @@ impl SledDatabase {
Err(e) => Err(anyhow::Error::new(e)) Err(e) => Err(anyhow::Error::new(e))
} }
} }
pub fn event_by_kind_pubkey(&self, kind: u32, pubkey: &PublicKey) -> Result<Event, anyhow::Error> {
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))
}
}
} }