Get replaceable events
This commit is contained in:
parent
02bc00754c
commit
64375c23c5
14
README.md
Normal file
14
README.md
Normal 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)
|
@ -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
|
||||||
}
|
}
|
||||||
|
47
src/store.rs
47
src/store.rs
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user