mirror of
https://github.com/damus-io/notecrumbs.git
synced 2024-10-01 11:00:46 +00:00
fetch notes from relays if we don't have them
If we don't have a cache hit, try to find the note on other relays. Once we find it, add it to nostrdb.
This commit is contained in:
parent
7a7a04da39
commit
dec66e4a8a
@ -13,8 +13,8 @@ hyper-util = { version = "0.1", features = ["full"] }
|
||||
http-body-util = "0.1"
|
||||
log = "0.4.20"
|
||||
env_logger = "0.10.1"
|
||||
nostrdb = "0.1.3"
|
||||
nostr-sdk = { git = "https://github.com/damus-io/nostr-sdk.git", rev = "bfd6ac4e111720dcecf8fc09d4ea76da4d971cf4" }
|
||||
nostrdb = { git = "https://github.com/damus-io/nostrdb-rs.git", rev = "0545571827bf64e06250f094d65775acd2a1165e" }
|
||||
nostr-sdk = { git = "https://github.com/damus-io/nostr-sdk.git", rev = "fc0dc7b38f5060f171228b976b9700c0135245d3" }
|
||||
hex = "0.4.3"
|
||||
egui = "0.21.0"
|
||||
egui_skia = { version = "0.4.0", features = ["cpu_fix"] }
|
||||
|
@ -17,7 +17,7 @@ WIP!
|
||||
|
||||
- [x] Local note fetching with nostrdb
|
||||
- [x] Basic note rendering
|
||||
- [ ] Fetch notes from relays
|
||||
- [x] Fetch notes from relays
|
||||
- [ ] Render profile pictures
|
||||
- [ ] Cache profile pictures
|
||||
- [ ] HTML note page
|
||||
|
104
src/main.rs
104
src/main.rs
@ -7,20 +7,26 @@ use hyper::server::conn::http1;
|
||||
use hyper::service::service_fn;
|
||||
use hyper::{Request, Response, StatusCode};
|
||||
use hyper_util::rt::TokioIo;
|
||||
use log::info;
|
||||
use log::{error, info, warn};
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
use crate::error::Error;
|
||||
use nostr_sdk::nips::nip19::Nip19;
|
||||
use nostr_sdk::prelude::*;
|
||||
use nostrdb::{Config, Ndb, Note, Transaction};
|
||||
use nostrdb::{Config, Ndb, Transaction};
|
||||
use std::time::Duration;
|
||||
|
||||
use nostr_sdk::Kind;
|
||||
|
||||
mod error;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Context {
|
||||
ndb: Ndb,
|
||||
//font_data: egui::FontData,
|
||||
keys: Keys,
|
||||
|
||||
/// How long do we wait for remote note requests
|
||||
timeout: Duration,
|
||||
}
|
||||
|
||||
fn nip19_evid(nip19: &Nip19) -> Option<EventId> {
|
||||
@ -31,8 +37,7 @@ fn nip19_evid(nip19: &Nip19) -> Option<EventId> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn render_note<'a>(_app_ctx: &Context, note: &'a Note) -> Vec<u8> {
|
||||
fn render_note<'a>(_app_ctx: &Context, content: &'a str) -> Vec<u8> {
|
||||
use egui::{FontId, RichText};
|
||||
use egui_skia::{rasterize, RasterizeOptions};
|
||||
use skia_safe::EncodedImageFormat;
|
||||
@ -46,7 +51,7 @@ fn render_note<'a>(_app_ctx: &Context, note: &'a Note) -> Vec<u8> {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(RichText::new("✏").font(FontId::proportional(120.0)));
|
||||
ui.vertical(|ui| {
|
||||
ui.label(RichText::new(note.content()).font(FontId::proportional(40.0)));
|
||||
ui.label(RichText::new(content).font(FontId::proportional(40.0)));
|
||||
});
|
||||
})
|
||||
});
|
||||
@ -97,11 +102,57 @@ fn nip19_relays(nip19: &Nip19) -> Vec<String> {
|
||||
relays
|
||||
}
|
||||
|
||||
async fn find_note(ctx: &Context, nip19: &Nip19) -> Result<nostr_sdk::Event, Error> {
|
||||
let opts = Options::new().shutdown_on_drop(true);
|
||||
let client = Client::with_opts(&ctx.keys, opts);
|
||||
|
||||
let _ = client.add_relay("wss://relay.damus.io").await;
|
||||
|
||||
let other_relays = nip19_relays(nip19);
|
||||
for relay in other_relays {
|
||||
let _ = client.add_relay(relay).await;
|
||||
}
|
||||
|
||||
client.connect().await;
|
||||
|
||||
let filters = nip19_to_filters(nip19)?;
|
||||
|
||||
client
|
||||
.req_events_of(filters.clone(), Some(ctx.timeout))
|
||||
.await;
|
||||
|
||||
loop {
|
||||
match client.notifications().recv().await? {
|
||||
RelayPoolNotification::Event(_url, ev) => {
|
||||
info!("got ev: {:?}", ev);
|
||||
return Ok(ev);
|
||||
}
|
||||
RelayPoolNotification::RelayStatus { .. } => continue,
|
||||
RelayPoolNotification::Message(_url, msg) => match msg {
|
||||
RelayMessage::Event { event, .. } => return Ok(*event),
|
||||
RelayMessage::EndOfStoredEvents(_) => return Err(Error::NotFound),
|
||||
_ => continue,
|
||||
},
|
||||
RelayPoolNotification::Stop | RelayPoolNotification::Shutdown => {
|
||||
return Err(Error::NotFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn serve(
|
||||
ctx: &Context,
|
||||
r: Request<hyper::body::Incoming>,
|
||||
) -> Result<Response<Full<Bytes>>, Error> {
|
||||
let nip19 = Nip19::from_bech32(&r.uri().to_string()[1..])?;
|
||||
let nip19 = match Nip19::from_bech32(&r.uri().to_string()[1..]) {
|
||||
Ok(nip19) => nip19,
|
||||
Err(_) => {
|
||||
return Ok(Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(Full::new(Bytes::from("Invalid url\n")))?);
|
||||
}
|
||||
};
|
||||
|
||||
let evid = match nip19_evid(&nip19) {
|
||||
Some(evid) => evid,
|
||||
None => {
|
||||
@ -111,14 +162,25 @@ async fn serve(
|
||||
}
|
||||
};
|
||||
|
||||
let content = {
|
||||
let mut txn = Transaction::new(&ctx.ndb)?;
|
||||
let note = match ctx
|
||||
.ndb
|
||||
ctx.ndb
|
||||
.get_note_by_id(&mut txn, evid.as_bytes().try_into()?)
|
||||
{
|
||||
Ok(note) => note,
|
||||
Err(nostrdb::Error::NotFound) => {
|
||||
// query relays
|
||||
.map(|n| {
|
||||
info!("cache hit {:?}", nip19);
|
||||
n.content().to_string()
|
||||
})
|
||||
};
|
||||
|
||||
let content = match content {
|
||||
Ok(content) => content,
|
||||
Err(nostrdb::Error::NotFound) => match find_note(ctx, &nip19).await {
|
||||
Ok(note) => {
|
||||
ctx.ndb
|
||||
.process_event(&json!(["EVENT", "s", note]).to_string());
|
||||
note.content
|
||||
}
|
||||
Err(err) => {
|
||||
return Ok(Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(Full::new(Bytes::from(format!(
|
||||
@ -126,6 +188,7 @@ async fn serve(
|
||||
::hex::encode(evid)
|
||||
))))?);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
return Ok(Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
@ -133,13 +196,20 @@ async fn serve(
|
||||
}
|
||||
};
|
||||
|
||||
let data = render_note(&ctx, ¬e);
|
||||
let data = render_note(&ctx, &content);
|
||||
|
||||
Ok(Response::builder()
|
||||
.header(header::CONTENT_TYPE, "image/png")
|
||||
.status(StatusCode::OK)
|
||||
.body(Full::new(Bytes::from(data)))?)
|
||||
}
|
||||
|
||||
fn get_env_timeout() -> Duration {
|
||||
let timeout_env = std::env::var("TIMEOUT_MS").unwrap_or("2000".to_string());
|
||||
let timeout_ms: u64 = timeout_env.parse().unwrap_or(2000);
|
||||
Duration::from_millis(timeout_ms)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
env_logger::init();
|
||||
@ -153,9 +223,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let cfg = Config::new();
|
||||
let ndb = Ndb::new(".", &cfg).expect("ndb failed to open");
|
||||
//let font_data = egui::FontData::from_static(include_bytes!("../fonts/NotoSans-Regular.ttf"));
|
||||
let ctx = Context {
|
||||
ndb, /*, font_data */
|
||||
};
|
||||
let keys = Keys::generate();
|
||||
let timeout = get_env_timeout();
|
||||
let ctx = Context { ndb, keys, timeout };
|
||||
|
||||
// We start a loop to continuously accept incoming connections
|
||||
loop {
|
||||
|
Loading…
Reference in New Issue
Block a user