mirror of
https://github.com/scsibug/nostr-rs-relay.git
synced 2024-11-23 09:29:06 -05:00
refactor: format
This commit is contained in:
parent
6df92f9580
commit
c1c25a22f5
5
build.rs
5
build.rs
|
@ -2,9 +2,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
tonic_build::configure()
|
tonic_build::configure()
|
||||||
.build_server(false)
|
.build_server(false)
|
||||||
.protoc_arg("--experimental_allow_proto3_optional")
|
.protoc_arg("--experimental_allow_proto3_optional")
|
||||||
.compile(
|
.compile(&["proto/nauthz.proto"], &["proto"])?;
|
||||||
&["proto/nauthz.proto"],
|
|
||||||
&["proto"],
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ pub struct Limits {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub struct Authorization {
|
pub struct Authorization {
|
||||||
pub pubkey_whitelist: Option<Vec<String>>, // If present, only allow these pubkeys to publish events
|
pub pubkey_whitelist: Option<Vec<String>>, // If present, only allow these pubkeys to publish events
|
||||||
pub nip42_auth: bool, // if true enables NIP-42 authentication
|
pub nip42_auth: bool, // if true enables NIP-42 authentication
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
@ -257,7 +257,7 @@ impl Default for Settings {
|
||||||
},
|
},
|
||||||
authorization: Authorization {
|
authorization: Authorization {
|
||||||
pubkey_whitelist: None, // Allow any address to publish
|
pubkey_whitelist: None, // Allow any address to publish
|
||||||
nip42_auth: false, // Disable NIP-42 authentication
|
nip42_auth: false, // Disable NIP-42 authentication
|
||||||
},
|
},
|
||||||
verified_users: VerifiedUsers {
|
verified_users: VerifiedUsers {
|
||||||
mode: VerifiedUsersMode::Disabled,
|
mode: VerifiedUsersMode::Disabled,
|
||||||
|
|
|
@ -161,12 +161,12 @@ impl ClientConn {
|
||||||
Challenge(_) => (),
|
Challenge(_) => (),
|
||||||
AuthPubkey(_) => {
|
AuthPubkey(_) => {
|
||||||
// already authenticated
|
// already authenticated
|
||||||
return Ok(())
|
return Ok(());
|
||||||
},
|
}
|
||||||
NoAuth => {
|
NoAuth => {
|
||||||
// unexpected AUTH request
|
// unexpected AUTH request
|
||||||
return Err(Error::AuthFailure);
|
return Err(Error::AuthFailure);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
match event.validate() {
|
match event.validate() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
|
49
src/db.rs
49
src/db.rs
|
@ -2,12 +2,12 @@
|
||||||
use crate::config::Settings;
|
use crate::config::Settings;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
|
use crate::nauthz;
|
||||||
use crate::notice::Notice;
|
use crate::notice::Notice;
|
||||||
use crate::repo::postgres::{PostgresPool, PostgresRepo};
|
use crate::repo::postgres::{PostgresPool, PostgresRepo};
|
||||||
use crate::repo::sqlite::SqliteRepo;
|
use crate::repo::sqlite::SqliteRepo;
|
||||||
use crate::repo::NostrRepo;
|
use crate::repo::NostrRepo;
|
||||||
use crate::server::NostrMetrics;
|
use crate::server::NostrMetrics;
|
||||||
use crate::nauthz;
|
|
||||||
use governor::clock::Clock;
|
use governor::clock::Clock;
|
||||||
use governor::{Quota, RateLimiter};
|
use governor::{Quota, RateLimiter};
|
||||||
use r2d2;
|
use r2d2;
|
||||||
|
@ -116,8 +116,8 @@ pub async fn db_writer(
|
||||||
};
|
};
|
||||||
|
|
||||||
//let gprc_client = settings.grpc.event_admission_server.map(|s| {
|
//let gprc_client = settings.grpc.event_admission_server.map(|s| {
|
||||||
// event_admitter_connect(&s);
|
// event_admitter_connect(&s);
|
||||||
// });
|
// });
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if shutdown.try_recv().is_ok() {
|
if shutdown.try_recv().is_ok() {
|
||||||
|
@ -181,10 +181,7 @@ pub async fn db_writer(
|
||||||
&event.kind
|
&event.kind
|
||||||
);
|
);
|
||||||
notice_tx
|
notice_tx
|
||||||
.try_send(Notice::blocked(
|
.try_send(Notice::blocked(event.id, "event kind is blocked by relay"))
|
||||||
event.id,
|
|
||||||
"event kind is blocked by relay"
|
|
||||||
))
|
|
||||||
.ok();
|
.ok();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -216,7 +213,6 @@ pub async fn db_writer(
|
||||||
uv.name.to_string(),
|
uv.name.to_string(),
|
||||||
event.get_author_prefix()
|
event.get_author_prefix()
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
info!(
|
info!(
|
||||||
"rejecting event, author ({:?} / {:?}) verification invalid (expired/wrong domain)",
|
"rejecting event, author ({:?} / {:?}) verification invalid (expired/wrong domain)",
|
||||||
|
@ -253,27 +249,44 @@ pub async fn db_writer(
|
||||||
}
|
}
|
||||||
|
|
||||||
// nip05 address
|
// nip05 address
|
||||||
let nip05_address : Option<crate::nip05::Nip05Name> = validation.and_then(|x| x.ok().map(|y| y.name));
|
let nip05_address: Option<crate::nip05::Nip05Name> =
|
||||||
|
validation.and_then(|x| x.ok().map(|y| y.name));
|
||||||
|
|
||||||
// GRPC check
|
// GRPC check
|
||||||
if let Some(ref mut c) = grpc_client {
|
if let Some(ref mut c) = grpc_client {
|
||||||
trace!("checking if grpc permits");
|
trace!("checking if grpc permits");
|
||||||
let grpc_start = Instant::now();
|
let grpc_start = Instant::now();
|
||||||
let decision_res = c.admit_event(&event, &subm_event.source_ip, subm_event.origin, subm_event.user_agent, nip05_address, subm_event.auth_pubkey).await;
|
let decision_res = c
|
||||||
|
.admit_event(
|
||||||
|
&event,
|
||||||
|
&subm_event.source_ip,
|
||||||
|
subm_event.origin,
|
||||||
|
subm_event.user_agent,
|
||||||
|
nip05_address,
|
||||||
|
subm_event.auth_pubkey,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
match decision_res {
|
match decision_res {
|
||||||
Ok(decision) => {
|
Ok(decision) => {
|
||||||
if !decision.permitted() {
|
if !decision.permitted() {
|
||||||
// GPRC returned a decision to reject this event
|
// GPRC returned a decision to reject this event
|
||||||
info!("GRPC rejected event: {:?} (kind: {}) from: {:?} in: {:?} (IP: {:?})",
|
info!(
|
||||||
event.get_event_id_prefix(),
|
"GRPC rejected event: {:?} (kind: {}) from: {:?} in: {:?} (IP: {:?})",
|
||||||
event.kind,
|
event.get_event_id_prefix(),
|
||||||
event.get_author_prefix(),
|
event.kind,
|
||||||
grpc_start.elapsed(),
|
event.get_author_prefix(),
|
||||||
subm_event.source_ip);
|
grpc_start.elapsed(),
|
||||||
notice_tx.try_send(Notice::blocked(event.id, &decision.message().unwrap_or_else(|| "".to_string()))).ok();
|
subm_event.source_ip
|
||||||
|
);
|
||||||
|
notice_tx
|
||||||
|
.try_send(Notice::blocked(
|
||||||
|
event.id,
|
||||||
|
&decision.message().unwrap_or_else(|| "".to_string()),
|
||||||
|
))
|
||||||
|
.ok();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("GRPC server error: {:?}", e);
|
warn!("GRPC server error: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
45
src/event.rs
45
src/event.rs
|
@ -5,6 +5,8 @@ use crate::error::Error::{
|
||||||
EventMalformedPubkey,
|
EventMalformedPubkey,
|
||||||
};
|
};
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
|
use crate::event::EventWrapper::WrappedAuth;
|
||||||
|
use crate::event::EventWrapper::WrappedEvent;
|
||||||
use crate::nip05;
|
use crate::nip05;
|
||||||
use crate::utils::unix_time;
|
use crate::utils::unix_time;
|
||||||
use bitcoin_hashes::{sha256, Hash};
|
use bitcoin_hashes::{sha256, Hash};
|
||||||
|
@ -17,8 +19,6 @@ use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
use crate::event::EventWrapper::WrappedEvent;
|
|
||||||
use crate::event::EventWrapper::WrappedAuth;
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Secp256k1 verification instance.
|
/// Secp256k1 verification instance.
|
||||||
|
@ -90,10 +90,9 @@ pub fn single_char_tagname(tagname: &str) -> Option<char> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub enum EventWrapper {
|
pub enum EventWrapper {
|
||||||
WrappedEvent(Event),
|
WrappedEvent(Event),
|
||||||
WrappedAuth(Event)
|
WrappedAuth(Event),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert network event to parsed/validated event.
|
/// Convert network event to parsed/validated event.
|
||||||
|
@ -157,11 +156,13 @@ impl Event {
|
||||||
/// Determine the time at which this event should expire
|
/// Determine the time at which this event should expire
|
||||||
pub fn expiration(&self) -> Option<u64> {
|
pub fn expiration(&self) -> Option<u64> {
|
||||||
let default = "".to_string();
|
let default = "".to_string();
|
||||||
let dvals:Vec<&String> = self.tags
|
let dvals: Vec<&String> = self
|
||||||
|
.tags
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| !x.is_empty())
|
.filter(|x| !x.is_empty())
|
||||||
.filter(|x| x.get(0).unwrap() == "expiration")
|
.filter(|x| x.get(0).unwrap() == "expiration")
|
||||||
.map(|x| x.get(1).unwrap_or(&default)).take(1)
|
.map(|x| x.get(1).unwrap_or(&default))
|
||||||
|
.take(1)
|
||||||
.collect();
|
.collect();
|
||||||
let val_first = dvals.get(0);
|
let val_first = dvals.get(0);
|
||||||
val_first.and_then(|t| t.parse::<u64>().ok())
|
val_first.and_then(|t| t.parse::<u64>().ok())
|
||||||
|
@ -711,9 +712,7 @@ mod tests {
|
||||||
// regular events do not expire
|
// regular events do not expire
|
||||||
let mut event = Event::simple_event();
|
let mut event = Event::simple_event();
|
||||||
event.kind = 7;
|
event.kind = 7;
|
||||||
event.tags = vec![
|
event.tags = vec![vec!["test".to_string(), "foo".to_string()]];
|
||||||
vec!["test".to_string(), "foo".to_string()],
|
|
||||||
];
|
|
||||||
assert_eq!(event.expiration(), None);
|
assert_eq!(event.expiration(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,57 +721,47 @@ mod tests {
|
||||||
// regular events do not expire
|
// regular events do not expire
|
||||||
let mut event = Event::simple_event();
|
let mut event = Event::simple_event();
|
||||||
event.kind = 7;
|
event.kind = 7;
|
||||||
event.tags = vec![
|
event.tags = vec![vec!["expiration".to_string()]];
|
||||||
vec!["expiration".to_string()],
|
|
||||||
];
|
|
||||||
assert_eq!(event.expiration(), None);
|
assert_eq!(event.expiration(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expiring_event_future() {
|
fn expiring_event_future() {
|
||||||
// a normal expiring event
|
// a normal expiring event
|
||||||
let exp:u64 = 1676264138;
|
let exp: u64 = 1676264138;
|
||||||
let mut event = Event::simple_event();
|
let mut event = Event::simple_event();
|
||||||
event.kind = 1;
|
event.kind = 1;
|
||||||
event.tags = vec![
|
event.tags = vec![vec!["expiration".to_string(), exp.to_string()]];
|
||||||
vec!["expiration".to_string(), exp.to_string()],
|
|
||||||
];
|
|
||||||
assert_eq!(event.expiration(), Some(exp));
|
assert_eq!(event.expiration(), Some(exp));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expiring_event_negative() {
|
fn expiring_event_negative() {
|
||||||
// expiration set to a negative value (invalid)
|
// expiration set to a negative value (invalid)
|
||||||
let exp:i64 = -90;
|
let exp: i64 = -90;
|
||||||
let mut event = Event::simple_event();
|
let mut event = Event::simple_event();
|
||||||
event.kind = 1;
|
event.kind = 1;
|
||||||
event.tags = vec![
|
event.tags = vec![vec!["expiration".to_string(), exp.to_string()]];
|
||||||
vec!["expiration".to_string(), exp.to_string()],
|
|
||||||
];
|
|
||||||
assert_eq!(event.expiration(), None);
|
assert_eq!(event.expiration(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expiring_event_zero() {
|
fn expiring_event_zero() {
|
||||||
// a normal expiring event set to zero
|
// a normal expiring event set to zero
|
||||||
let exp:i64 = 0;
|
let exp: i64 = 0;
|
||||||
let mut event = Event::simple_event();
|
let mut event = Event::simple_event();
|
||||||
event.kind = 1;
|
event.kind = 1;
|
||||||
event.tags = vec![
|
event.tags = vec![vec!["expiration".to_string(), exp.to_string()]];
|
||||||
vec!["expiration".to_string(), exp.to_string()],
|
|
||||||
];
|
|
||||||
assert_eq!(event.expiration(), Some(0));
|
assert_eq!(event.expiration(), Some(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expiring_event_fraction() {
|
fn expiring_event_fraction() {
|
||||||
// expiration is fractional (invalid)
|
// expiration is fractional (invalid)
|
||||||
let exp:f64 = 23.334;
|
let exp: f64 = 23.334;
|
||||||
let mut event = Event::simple_event();
|
let mut event = Event::simple_event();
|
||||||
event.kind = 1;
|
event.kind = 1;
|
||||||
event.tags = vec![
|
event.tags = vec![vec!["expiration".to_string(), exp.to_string()]];
|
||||||
vec!["expiration".to_string(), exp.to_string()],
|
|
||||||
];
|
|
||||||
assert_eq!(event.expiration(), None);
|
assert_eq!(event.expiration(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ pub mod error;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod hexrange;
|
pub mod hexrange;
|
||||||
pub mod info;
|
pub mod info;
|
||||||
pub mod nip05;
|
|
||||||
pub mod nauthz;
|
pub mod nauthz;
|
||||||
|
pub mod nip05;
|
||||||
pub mod notice;
|
pub mod notice;
|
||||||
pub mod repo;
|
pub mod repo;
|
||||||
pub mod subscription;
|
pub mod subscription;
|
||||||
|
|
|
@ -76,7 +76,7 @@ impl EventAuthzService {
|
||||||
origin: Option<String>,
|
origin: Option<String>,
|
||||||
user_agent: Option<String>,
|
user_agent: Option<String>,
|
||||||
nip05: Option<Nip05Name>,
|
nip05: Option<Nip05Name>,
|
||||||
auth_pubkey: Option<Vec<u8>>
|
auth_pubkey: Option<Vec<u8>>,
|
||||||
) -> Result<Box<dyn AuthzDecision>> {
|
) -> Result<Box<dyn AuthzDecision>> {
|
||||||
self.ready_connection().await;
|
self.ready_connection().await;
|
||||||
let id_blob = hex::decode(&event.id)?;
|
let id_blob = hex::decode(&event.id)?;
|
||||||
|
|
|
@ -265,10 +265,10 @@ impl Verifier {
|
||||||
Err(Error::ChannelClosed) => {
|
Err(Error::ChannelClosed) => {
|
||||||
// channel was closed, we are shutting down
|
// channel was closed, we are shutting down
|
||||||
return;
|
return;
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
info!("error in verifier: {:?}", e);
|
info!("error in verifier: {:?}", e);
|
||||||
},
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub struct EventResult {
|
||||||
pub enum Notice {
|
pub enum Notice {
|
||||||
Message(String),
|
Message(String),
|
||||||
EventResult(EventResult),
|
EventResult(EventResult),
|
||||||
AuthChallenge(String)
|
AuthChallenge(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventResultStatus {
|
impl EventResultStatus {
|
||||||
|
|
|
@ -16,11 +16,11 @@ use crate::error;
|
||||||
use crate::hexrange::{hex_range, HexSearch};
|
use crate::hexrange::{hex_range, HexSearch};
|
||||||
use crate::repo::postgres_migration::run_migrations;
|
use crate::repo::postgres_migration::run_migrations;
|
||||||
use crate::server::NostrMetrics;
|
use crate::server::NostrMetrics;
|
||||||
use crate::utils::{is_hex, is_lower_hex, self};
|
use crate::utils::{self, is_hex, is_lower_hex};
|
||||||
use tokio::sync::mpsc::Sender;
|
use tokio::sync::mpsc::Sender;
|
||||||
use tokio::sync::oneshot::Receiver;
|
use tokio::sync::oneshot::Receiver;
|
||||||
use tracing::log::trace;
|
use tracing::log::trace;
|
||||||
use tracing::{debug, info, warn, error};
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
pub type PostgresPool = sqlx::pool::Pool<Postgres>;
|
pub type PostgresPool = sqlx::pool::Pool<Postgres>;
|
||||||
|
|
||||||
|
@ -36,10 +36,8 @@ impl PostgresRepo {
|
||||||
metrics: m,
|
metrics: m,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Cleanup expired events on a regular basis
|
/// Cleanup expired events on a regular basis
|
||||||
async fn cleanup_expired(conn: PostgresPool, frequency: Duration) -> Result<()> {
|
async fn cleanup_expired(conn: PostgresPool, frequency: Duration) -> Result<()> {
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
|
@ -66,12 +64,13 @@ async fn cleanup_expired(conn: PostgresPool, frequency: Duration) -> Result<()>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One-time deletion of all expired events
|
/// One-time deletion of all expired events
|
||||||
async fn delete_expired(conn:PostgresPool) -> Result<u64> {
|
async fn delete_expired(conn: PostgresPool) -> Result<u64> {
|
||||||
let mut tx = conn.begin().await?;
|
let mut tx = conn.begin().await?;
|
||||||
let update_count = sqlx::query("DELETE FROM \"event\" WHERE expires_at <= $1;")
|
let update_count = sqlx::query("DELETE FROM \"event\" WHERE expires_at <= $1;")
|
||||||
.bind(Utc.timestamp_opt(utils::unix_time() as i64, 0).unwrap())
|
.bind(Utc.timestamp_opt(utils::unix_time() as i64, 0).unwrap())
|
||||||
.execute(&mut tx)
|
.execute(&mut tx)
|
||||||
.await?.rows_affected();
|
.await?
|
||||||
|
.rows_affected();
|
||||||
tx.commit().await?;
|
tx.commit().await?;
|
||||||
Ok(update_count)
|
Ok(update_count)
|
||||||
}
|
}
|
||||||
|
@ -81,7 +80,7 @@ impl NostrRepo for PostgresRepo {
|
||||||
async fn start(&self) -> Result<()> {
|
async fn start(&self) -> Result<()> {
|
||||||
// begin a cleanup task for expired events.
|
// begin a cleanup task for expired events.
|
||||||
cleanup_expired(self.conn.clone(), Duration::from_secs(600)).await?;
|
cleanup_expired(self.conn.clone(), Duration::from_secs(600)).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn migrate_up(&self) -> Result<usize> {
|
async fn migrate_up(&self) -> Result<usize> {
|
||||||
|
@ -148,16 +147,19 @@ impl NostrRepo for PostgresRepo {
|
||||||
VALUES($1, $2, $3, $4, $5, $6, $7)
|
VALUES($1, $2, $3, $4, $5, $6, $7)
|
||||||
ON CONFLICT (id) DO NOTHING"#,
|
ON CONFLICT (id) DO NOTHING"#,
|
||||||
)
|
)
|
||||||
.bind(&id_blob)
|
.bind(&id_blob)
|
||||||
.bind(&pubkey_blob)
|
.bind(&pubkey_blob)
|
||||||
.bind(Utc.timestamp_opt(e.created_at as i64, 0).unwrap())
|
.bind(Utc.timestamp_opt(e.created_at as i64, 0).unwrap())
|
||||||
.bind(e.expiration().and_then(|x| Utc.timestamp_opt(x as i64, 0).latest()))
|
.bind(
|
||||||
.bind(e.kind as i64)
|
e.expiration()
|
||||||
.bind(event_str.into_bytes())
|
.and_then(|x| Utc.timestamp_opt(x as i64, 0).latest()),
|
||||||
.bind(delegator_blob)
|
)
|
||||||
.execute(&mut tx)
|
.bind(e.kind as i64)
|
||||||
.await?
|
.bind(event_str.into_bytes())
|
||||||
.rows_affected();
|
.bind(delegator_blob)
|
||||||
|
.execute(&mut tx)
|
||||||
|
.await?
|
||||||
|
.rows_affected();
|
||||||
if ins_count == 0 {
|
if ins_count == 0 {
|
||||||
// if the event was a duplicate, no need to insert event or
|
// if the event was a duplicate, no need to insert event or
|
||||||
// pubkey references. This will abort the txn.
|
// pubkey references. This will abort the txn.
|
||||||
|
@ -753,7 +755,8 @@ fn query_from_filter(f: &ReqFilter) -> Option<QueryBuilder<Postgres>> {
|
||||||
// never display expired events
|
// never display expired events
|
||||||
query
|
query
|
||||||
.push(" AND (e.expires_at IS NULL OR e.expires_at > ")
|
.push(" AND (e.expires_at IS NULL OR e.expires_at > ")
|
||||||
.push_bind(Utc.timestamp_opt(utils::unix_time() as i64, 0).unwrap()).push(")");
|
.push_bind(Utc.timestamp_opt(utils::unix_time() as i64, 0).unwrap())
|
||||||
|
.push(")");
|
||||||
|
|
||||||
// Apply per-filter limit to this query.
|
// Apply per-filter limit to this query.
|
||||||
// The use of a LIMIT implies a DESC order, to capture only the most recent events.
|
// The use of a LIMIT implies a DESC order, to capture only the most recent events.
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
//use crate::config::SETTINGS;
|
//use crate::config::SETTINGS;
|
||||||
use crate::config::Settings;
|
use crate::config::Settings;
|
||||||
use crate::db::QueryResult;
|
use crate::db::QueryResult;
|
||||||
use crate::error::Result;
|
|
||||||
use crate::error::Error::SqlError;
|
use crate::error::Error::SqlError;
|
||||||
|
use crate::error::Result;
|
||||||
use crate::event::{single_char_tagname, Event};
|
use crate::event::{single_char_tagname, Event};
|
||||||
use crate::hexrange::hex_range;
|
use crate::hexrange::hex_range;
|
||||||
use crate::hexrange::HexSearch;
|
use crate::hexrange::HexSearch;
|
||||||
|
@ -257,13 +257,15 @@ impl NostrRepo for SqliteRepo {
|
||||||
self.maint_pool.clone(),
|
self.maint_pool.clone(),
|
||||||
Duration::from_secs(60),
|
Duration::from_secs(60),
|
||||||
self.write_in_progress.clone(),
|
self.write_in_progress.clone(),
|
||||||
self.checkpoint_in_progress.clone()
|
self.checkpoint_in_progress.clone(),
|
||||||
).await?;
|
)
|
||||||
|
.await?;
|
||||||
cleanup_expired(
|
cleanup_expired(
|
||||||
self.maint_pool.clone(),
|
self.maint_pool.clone(),
|
||||||
Duration::from_secs(600),
|
Duration::from_secs(600),
|
||||||
self.write_in_progress.clone()
|
self.write_in_progress.clone(),
|
||||||
).await
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn migrate_up(&self) -> Result<usize> {
|
async fn migrate_up(&self) -> Result<usize> {
|
||||||
|
@ -286,24 +288,29 @@ impl NostrRepo for SqliteRepo {
|
||||||
// this could fail because the database was busy; try
|
// this could fail because the database was busy; try
|
||||||
// multiple times before giving up.
|
// multiple times before giving up.
|
||||||
loop {
|
loop {
|
||||||
attempts+=1;
|
attempts += 1;
|
||||||
let wr = SqliteRepo::persist_event(&mut conn, &e);
|
let wr = SqliteRepo::persist_event(&mut conn, &e);
|
||||||
match wr {
|
match wr {
|
||||||
Err(SqlError(rusqlite::Error::SqliteFailure(e,_))) => {
|
Err(SqlError(rusqlite::Error::SqliteFailure(e, _))) => {
|
||||||
// this basically means that NIP-05 or another
|
// this basically means that NIP-05 or another
|
||||||
// writer was using the database between us
|
// writer was using the database between us
|
||||||
// reading and promoting the connection to a
|
// reading and promoting the connection to a
|
||||||
// write lock.
|
// write lock.
|
||||||
info!("event write failed, DB locked (attempt: {}); sqlite err: {}",
|
info!(
|
||||||
attempts, e.extended_code);
|
"event write failed, DB locked (attempt: {}); sqlite err: {}",
|
||||||
},
|
attempts, e.extended_code
|
||||||
_ => {return wr;},
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return wr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if attempts >= max_write_attempts {
|
if attempts >= max_write_attempts {
|
||||||
return wr;
|
return wr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).await?;
|
})
|
||||||
|
.await?;
|
||||||
self.metrics
|
self.metrics
|
||||||
.write_events
|
.write_events
|
||||||
.observe(start.elapsed().as_secs_f64());
|
.observe(start.elapsed().as_secs_f64());
|
||||||
|
@ -865,7 +872,8 @@ fn query_from_filter(f: &ReqFilter) -> (String, Vec<Box<dyn ToSql>>, Option<Stri
|
||||||
let until_clause;
|
let until_clause;
|
||||||
if let Some(ks) = &f.kinds {
|
if let Some(ks) = &f.kinds {
|
||||||
// kind is number, no escaping needed
|
// kind is number, no escaping needed
|
||||||
let str_kinds: Vec<String> = ks.iter().map(std::string::ToString::to_string).collect();
|
let str_kinds: Vec<String> =
|
||||||
|
ks.iter().map(std::string::ToString::to_string).collect();
|
||||||
kind_clause = format!("AND kind IN ({})", str_kinds.join(", "));
|
kind_clause = format!("AND kind IN ({})", str_kinds.join(", "));
|
||||||
} else {
|
} else {
|
||||||
kind_clause = format!("");
|
kind_clause = format!("");
|
||||||
|
@ -998,7 +1006,11 @@ pub fn build_pool(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cleanup expired events on a regular basis
|
/// Cleanup expired events on a regular basis
|
||||||
async fn cleanup_expired(pool: SqlitePool, frequency: Duration, write_in_progress: Arc<Mutex<u64>>) -> Result<()> {
|
async fn cleanup_expired(
|
||||||
|
pool: SqlitePool,
|
||||||
|
frequency: Duration,
|
||||||
|
write_in_progress: Arc<Mutex<u64>>,
|
||||||
|
) -> Result<()> {
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
|
@ -1050,7 +1062,8 @@ pub async fn db_checkpoint_task(
|
||||||
pool: SqlitePool,
|
pool: SqlitePool,
|
||||||
frequency: Duration,
|
frequency: Duration,
|
||||||
write_in_progress: Arc<Mutex<u64>>,
|
write_in_progress: Arc<Mutex<u64>>,
|
||||||
checkpoint_in_progress: Arc<Mutex<u64>>) -> Result<()> {
|
checkpoint_in_progress: Arc<Mutex<u64>>,
|
||||||
|
) -> Result<()> {
|
||||||
// TODO; use acquire_many on the reader semaphore to stop them from interrupting this.
|
// TODO; use acquire_many on the reader semaphore to stop them from interrupting this.
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
// WAL size in pages.
|
// WAL size in pages.
|
||||||
|
|
|
@ -4,13 +4,13 @@ use crate::error::Result;
|
||||||
use crate::event::{single_char_tagname, Event};
|
use crate::event::{single_char_tagname, Event};
|
||||||
use crate::utils::is_lower_hex;
|
use crate::utils::is_lower_hex;
|
||||||
use const_format::formatcp;
|
use const_format::formatcp;
|
||||||
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
use rusqlite::limits::Limit;
|
use rusqlite::limits::Limit;
|
||||||
use rusqlite::params;
|
use rusqlite::params;
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
|
||||||
|
|
||||||
/// Startup DB Pragmas
|
/// Startup DB Pragmas
|
||||||
pub const STARTUP_SQL: &str = r##"
|
pub const STARTUP_SQL: &str = r##"
|
||||||
|
@ -692,22 +692,22 @@ CREATE INDEX IF NOT EXISTS tag_covering_index ON tag(name,kind,value,created_at,
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let tx = conn.transaction()?;
|
let tx = conn.transaction()?;
|
||||||
|
|
||||||
let bar = ProgressBar::new(count.try_into().unwrap())
|
let bar = ProgressBar::new(count.try_into().unwrap()).with_message("rebuilding tags table");
|
||||||
.with_message("rebuilding tags table");
|
|
||||||
bar.set_style(
|
bar.set_style(
|
||||||
ProgressStyle::with_template(
|
ProgressStyle::with_template(
|
||||||
"[{elapsed_precise}] {bar:40.white/blue} {pos:>7}/{len:7} [{percent}%] {msg}",
|
"[{elapsed_precise}] {bar:40.white/blue} {pos:>7}/{len:7} [{percent}%] {msg}",
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
{
|
{
|
||||||
tx.execute_batch(upgrade_sql)?;
|
tx.execute_batch(upgrade_sql)?;
|
||||||
let mut stmt = tx.prepare("select id, kind, created_at, content from event order by id;")?;
|
let mut stmt =
|
||||||
|
tx.prepare("select id, kind, created_at, content from event order by id;")?;
|
||||||
let mut tag_rows = stmt.query([])?;
|
let mut tag_rows = stmt.query([])?;
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
while let Some(row) = tag_rows.next()? {
|
while let Some(row) = tag_rows.next()? {
|
||||||
count += 1;
|
count += 1;
|
||||||
if count%10==0 {
|
if count % 10 == 0 {
|
||||||
bar.inc(10);
|
bar.inc(10);
|
||||||
}
|
}
|
||||||
let event_id: u64 = row.get(0)?;
|
let event_id: u64 = row.get(0)?;
|
||||||
|
@ -735,7 +735,10 @@ CREATE INDEX IF NOT EXISTS tag_covering_index ON tag(name,kind,value,created_at,
|
||||||
}
|
}
|
||||||
bar.finish();
|
bar.finish();
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
info!("database schema upgraded v15 -> v16 in {:?}", start.elapsed());
|
info!(
|
||||||
|
"database schema upgraded v15 -> v16 in {:?}",
|
||||||
|
start.elapsed()
|
||||||
|
);
|
||||||
Ok(16)
|
Ok(16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,15 @@ use crate::conn;
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::db::SubmittedEvent;
|
use crate::db::SubmittedEvent;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::event::EventWrapper;
|
|
||||||
use crate::server::EventWrapper::{WrappedAuth, WrappedEvent};
|
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use crate::event::EventCmd;
|
use crate::event::EventCmd;
|
||||||
|
use crate::event::EventWrapper;
|
||||||
use crate::info::RelayInfo;
|
use crate::info::RelayInfo;
|
||||||
use crate::nip05;
|
use crate::nip05;
|
||||||
use crate::notice::Notice;
|
use crate::notice::Notice;
|
||||||
use crate::repo::NostrRepo;
|
use crate::repo::NostrRepo;
|
||||||
|
use crate::server::Error::CommandUnknownError;
|
||||||
|
use crate::server::EventWrapper::{WrappedAuth, WrappedEvent};
|
||||||
use crate::subscription::Subscription;
|
use crate::subscription::Subscription;
|
||||||
use futures::SinkExt;
|
use futures::SinkExt;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
@ -53,7 +54,6 @@ use tungstenite::error::Error as WsError;
|
||||||
use tungstenite::handshake;
|
use tungstenite::handshake;
|
||||||
use tungstenite::protocol::Message;
|
use tungstenite::protocol::Message;
|
||||||
use tungstenite::protocol::WebSocketConfig;
|
use tungstenite::protocol::WebSocketConfig;
|
||||||
use crate::server::Error::CommandUnknownError;
|
|
||||||
|
|
||||||
/// Handle arbitrary HTTP requests, including for `WebSocket` upgrades.
|
/// Handle arbitrary HTTP requests, including for `WebSocket` upgrades.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
@ -197,17 +197,17 @@ async fn handle_web_request(
|
||||||
if let Some(favicon_bytes) = favicon {
|
if let Some(favicon_bytes) = favicon {
|
||||||
info!("returning favicon");
|
info!("returning favicon");
|
||||||
Ok(Response::builder()
|
Ok(Response::builder()
|
||||||
.status(StatusCode::OK)
|
.status(StatusCode::OK)
|
||||||
.header("Content-Type", "image/x-icon")
|
.header("Content-Type", "image/x-icon")
|
||||||
// 1 month cache
|
// 1 month cache
|
||||||
.header("Cache-Control", "public, max-age=2419200")
|
.header("Cache-Control", "public, max-age=2419200")
|
||||||
.body(Body::from(favicon_bytes))
|
.body(Body::from(favicon_bytes))
|
||||||
.unwrap())
|
.unwrap())
|
||||||
} else {
|
} else {
|
||||||
Ok(Response::builder()
|
Ok(Response::builder()
|
||||||
.status(StatusCode::NOT_FOUND)
|
.status(StatusCode::NOT_FOUND)
|
||||||
.body(Body::from(""))
|
.body(Body::from(""))
|
||||||
.unwrap())
|
.unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, _) => {
|
(_, _) => {
|
||||||
|
@ -283,15 +283,20 @@ fn create_metrics() -> (Registry, NostrMetrics) {
|
||||||
let query_aborts = IntCounterVec::new(
|
let query_aborts = IntCounterVec::new(
|
||||||
Opts::new("nostr_query_abort_total", "Aborted queries"),
|
Opts::new("nostr_query_abort_total", "Aborted queries"),
|
||||||
vec!["reason"].as_slice(),
|
vec!["reason"].as_slice(),
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
let cmd_req = IntCounter::with_opts(Opts::new("nostr_cmd_req_total", "REQ commands")).unwrap();
|
let cmd_req = IntCounter::with_opts(Opts::new("nostr_cmd_req_total", "REQ commands")).unwrap();
|
||||||
let cmd_event =
|
let cmd_event =
|
||||||
IntCounter::with_opts(Opts::new("nostr_cmd_event_total", "EVENT commands")).unwrap();
|
IntCounter::with_opts(Opts::new("nostr_cmd_event_total", "EVENT commands")).unwrap();
|
||||||
let cmd_close =
|
let cmd_close =
|
||||||
IntCounter::with_opts(Opts::new("nostr_cmd_close_total", "CLOSE commands")).unwrap();
|
IntCounter::with_opts(Opts::new("nostr_cmd_close_total", "CLOSE commands")).unwrap();
|
||||||
let cmd_auth = IntCounter::with_opts(Opts::new("nostr_cmd_auth_total", "AUTH commands")).unwrap();
|
let cmd_auth =
|
||||||
let disconnects = IntCounterVec::new(Opts::new("nostr_disconnects_total", "Client disconnects"),
|
IntCounter::with_opts(Opts::new("nostr_cmd_auth_total", "AUTH commands")).unwrap();
|
||||||
vec!["reason"].as_slice()).unwrap();
|
let disconnects = IntCounterVec::new(
|
||||||
|
Opts::new("nostr_disconnects_total", "Client disconnects"),
|
||||||
|
vec!["reason"].as_slice(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
registry.register(Box::new(query_sub.clone())).unwrap();
|
registry.register(Box::new(query_sub.clone())).unwrap();
|
||||||
registry.register(Box::new(query_db.clone())).unwrap();
|
registry.register(Box::new(query_db.clone())).unwrap();
|
||||||
registry.register(Box::new(write_events.clone())).unwrap();
|
registry.register(Box::new(write_events.clone())).unwrap();
|
||||||
|
@ -313,9 +318,9 @@ fn create_metrics() -> (Registry, NostrMetrics) {
|
||||||
db_connections,
|
db_connections,
|
||||||
disconnects,
|
disconnects,
|
||||||
query_aborts,
|
query_aborts,
|
||||||
cmd_req,
|
cmd_req,
|
||||||
cmd_event,
|
cmd_event,
|
||||||
cmd_close,
|
cmd_close,
|
||||||
cmd_auth,
|
cmd_auth,
|
||||||
};
|
};
|
||||||
(registry, metrics)
|
(registry, metrics)
|
||||||
|
@ -650,9 +655,7 @@ async fn nostr_server(
|
||||||
let unspec = "<unspecified>".to_string();
|
let unspec = "<unspecified>".to_string();
|
||||||
info!("new client connection (cid: {}, ip: {:?})", cid, conn.ip());
|
info!("new client connection (cid: {}, ip: {:?})", cid, conn.ip());
|
||||||
let origin = client_info.origin.as_ref().unwrap_or_else(|| &unspec);
|
let origin = client_info.origin.as_ref().unwrap_or_else(|| &unspec);
|
||||||
let user_agent = client_info
|
let user_agent = client_info.user_agent.as_ref().unwrap_or_else(|| &unspec);
|
||||||
.user_agent.as_ref()
|
|
||||||
.unwrap_or_else(|| &unspec);
|
|
||||||
info!(
|
info!(
|
||||||
"cid: {}, origin: {:?}, user-agent: {:?}",
|
"cid: {}, origin: {:?}, user-agent: {:?}",
|
||||||
cid, origin, user_agent
|
cid, origin, user_agent
|
||||||
|
@ -664,8 +667,12 @@ async fn nostr_server(
|
||||||
if settings.authorization.nip42_auth {
|
if settings.authorization.nip42_auth {
|
||||||
conn.generate_auth_challenge();
|
conn.generate_auth_challenge();
|
||||||
if let Some(challenge) = conn.auth_challenge() {
|
if let Some(challenge) = conn.auth_challenge() {
|
||||||
ws_stream.send(
|
ws_stream
|
||||||
make_notice_message(&Notice::AuthChallenge(challenge.to_string()))).await.ok();
|
.send(make_notice_message(&Notice::AuthChallenge(
|
||||||
|
challenge.to_string(),
|
||||||
|
)))
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,9 @@ pub fn is_lower_hex(s: &str) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn host_str(url: &String) -> Option<String> {
|
pub fn host_str(url: &String) -> Option<String> {
|
||||||
Url::parse(url).ok().and_then(|u| u.host_str().map(|s| s.to_string()))
|
Url::parse(url)
|
||||||
|
.ok()
|
||||||
|
.and_then(|u| u.host_str().map(|s| s.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user