use std::borrow::Cow;
use std::collections::HashSet;
use std::fmt;
use std::future::Future;
use std::net::{IpAddr, SocketAddr};
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use nostr::message::MachineReadablePrefix;
use nostr_database::prelude::*;
use super::local::LocalRelay;
pub(super) const DEFAULT_MAX_CONNECTIONS: usize = 128;
pub(super) const DEFAULT_MAX_FILTERS_PER_REQ: usize = 20;
pub(super) const DEFAULT_MAX_EVENT_SIZE: usize = 64 * 1024;
pub(super) const DEFAULT_MAX_QUERY_RESULTS: usize = 500;
pub(super) const DEFAULT_MAX_NEGENTROPY_ITEMS: usize = 50_000;
pub(super) const DEFAULT_MAX_SUBSCRIPTION_BYTES: usize = 1024 * 1024;
pub(super) const DEFAULT_MAX_WEBSOCKET_MESSAGE_SIZE: usize = 5 * 1024 * 1024;
pub(super) const DEFAULT_WEBSOCKET_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(10);
pub(super) const DEFAULT_QUERIES_PER_MINUTE: u32 = 120;
pub(super) const DEFAULT_AUTH_EVENTS_PER_MINUTE: u32 = 30;
pub(super) const DEFAULT_MESSAGES_PER_MINUTE: u32 = 300;
#[derive(Debug, Clone)]
pub struct RateLimit {
pub max_reqs: usize,
pub notes_per_minute: u32,
}
impl Default for RateLimit {
fn default() -> Self {
Self {
max_reqs: 500,
notes_per_minute: 60,
}
}
}
#[allow(missing_docs)]
#[deprecated(since = "0.45.0", note = "Use `LocalRelayBuilderMode` instead")]
pub type RelayBuilderMode = LocalRelayBuilderMode;
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LocalRelayBuilderMode {
#[default]
Generic,
PublicKey(PublicKey),
}
pub enum WritePolicyResult {
Accept,
Reject {
prefix: MachineReadablePrefix,
message: Cow<'static, str>,
status: bool,
},
}
impl WritePolicyResult {
#[inline]
pub fn ok_msg<S>(prefix: MachineReadablePrefix, msg: S) -> Self
where
S: Into<Cow<'static, str>>,
{
Self::Reject {
message: msg.into(),
status: true,
prefix,
}
}
#[inline]
pub fn reject<S>(prefix: MachineReadablePrefix, msg: S) -> Self
where
S: Into<Cow<'static, str>>,
{
Self::Reject {
message: msg.into(),
status: false,
prefix,
}
}
#[inline]
pub fn is_accept(&self) -> bool {
matches!(self, Self::Accept)
}
#[inline]
pub fn is_reject(&self) -> bool {
matches!(self, Self::Reject { .. })
}
}
pub enum QueryPolicyResult {
Accept,
Reject {
prefix: MachineReadablePrefix,
message: Cow<'static, str>,
},
}
impl QueryPolicyResult {
#[inline]
pub fn reject<S>(prefix: MachineReadablePrefix, msg: S) -> Self
where
S: Into<Cow<'static, str>>,
{
Self::Reject {
prefix,
message: msg.into(),
}
}
#[inline]
pub fn is_accept(&self) -> bool {
matches!(self, Self::Accept)
}
#[inline]
pub fn is_reject(&self) -> bool {
matches!(self, Self::Reject { .. })
}
}
pub trait WritePolicy: fmt::Debug + Send + Sync {
fn admit_event<'a>(
&'a self,
event: &'a Event,
addr: &'a SocketAddr,
) -> Pin<Box<dyn Future<Output = WritePolicyResult> + Send + 'a>>;
}
pub trait QueryPolicy: fmt::Debug + Send + Sync {
fn admit_query<'a>(
&'a self,
query: &'a mut Filter,
addr: &'a SocketAddr,
) -> Pin<Box<dyn Future<Output = QueryPolicyResult> + Send + 'a>>;
}
#[allow(missing_docs)]
#[deprecated(since = "0.45.0", note = "Use `LocalRelayTestOptions` instead")]
pub type RelayTestOptions = LocalRelayTestOptions;
#[derive(Debug, Clone, Default)]
pub struct LocalRelayTestOptions {
pub unresponsive_connection: Option<Duration>,
pub send_random_events: bool,
}
#[allow(missing_docs)]
#[deprecated(since = "0.45.0", note = "Use `LocalRelayBuilderNip42Mode` instead")]
pub type RelayBuilderNip42Mode = LocalRelayBuilderNip42Mode;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LocalRelayBuilderNip42Mode {
Write,
Read,
#[default]
Both,
}
impl LocalRelayBuilderNip42Mode {
#[inline]
pub fn is_read(&self) -> bool {
matches!(self, Self::Read | Self::Both)
}
#[inline]
pub fn is_write(&self) -> bool {
matches!(self, Self::Write | Self::Both)
}
}
#[allow(missing_docs)]
#[deprecated(since = "0.45.0", note = "Use `LocalRelayBuilderNip42` instead")]
pub type RelayBuilderNip42 = LocalRelayBuilderNip42;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct LocalRelayBuilderNip42 {
pub mode: LocalRelayBuilderNip42Mode,
}
impl LocalRelayBuilderNip42 {
#[inline]
pub fn write() -> Self {
Self {
mode: LocalRelayBuilderNip42Mode::Write,
}
}
#[inline]
pub fn read() -> Self {
Self {
mode: LocalRelayBuilderNip42Mode::Read,
}
}
#[inline]
pub fn read_and_write() -> Self {
Self {
mode: LocalRelayBuilderNip42Mode::Both,
}
}
}
#[allow(missing_docs)]
#[deprecated(since = "0.45.0", note = "Use `LocalRelayBuilder` instead")]
pub type RelayBuilder = LocalRelayBuilder;
#[derive(Debug, Clone)]
pub struct LocalRelayBuilder {
pub(crate) addr: Option<IpAddr>,
pub(crate) port: Option<u16>,
pub(crate) database: Option<Arc<dyn NostrDatabase>>,
pub(crate) mode: LocalRelayBuilderMode,
pub(crate) rate_limit: RateLimit,
pub(crate) queries_per_minute: u32,
pub(crate) auth_events_per_minute: u32,
pub(crate) messages_per_minute: u32,
pub(crate) nip42: Option<LocalRelayBuilderNip42>,
pub(crate) max_connections: usize,
pub(crate) max_websocket_message_size: usize,
pub(crate) max_event_size: usize,
pub(crate) websocket_handshake_timeout: Duration,
pub(crate) max_subid_length: usize,
pub(crate) max_filters_per_req: usize,
pub(crate) max_subscription_bytes: usize,
pub(crate) max_negentropy_subscriptions: usize,
pub(crate) max_negentropy_items: usize,
pub(crate) max_filter_limit: Option<usize>,
pub(crate) max_query_results: usize,
pub(crate) default_filter_limit: usize,
pub(crate) auth_dm: bool,
pub(crate) min_pow: Option<u8>,
pub(crate) kinds_blacklist: HashSet<Kind>,
pub(crate) write_policy: Option<Arc<dyn WritePolicy>>,
pub(crate) query_policy: Option<Arc<dyn QueryPolicy>>,
pub(crate) test: LocalRelayTestOptions,
}
impl Default for LocalRelayBuilder {
fn default() -> Self {
const BLACKLISTED_KINDS: [Kind; 5] = [
Kind::Seal, Kind::ZapRequest, Kind::Authentication, Kind::BlossomAuth, Kind::HttpAuth, ];
Self {
addr: None,
port: None,
database: None,
mode: LocalRelayBuilderMode::default(),
rate_limit: RateLimit::default(),
queries_per_minute: DEFAULT_QUERIES_PER_MINUTE,
auth_events_per_minute: DEFAULT_AUTH_EVENTS_PER_MINUTE,
messages_per_minute: DEFAULT_MESSAGES_PER_MINUTE,
nip42: None,
max_connections: DEFAULT_MAX_CONNECTIONS,
max_websocket_message_size: DEFAULT_MAX_WEBSOCKET_MESSAGE_SIZE,
max_event_size: DEFAULT_MAX_EVENT_SIZE,
websocket_handshake_timeout: DEFAULT_WEBSOCKET_HANDSHAKE_TIMEOUT,
max_subid_length: 250,
max_filters_per_req: DEFAULT_MAX_FILTERS_PER_REQ,
max_subscription_bytes: DEFAULT_MAX_SUBSCRIPTION_BYTES,
max_negentropy_subscriptions: 10,
max_negentropy_items: DEFAULT_MAX_NEGENTROPY_ITEMS,
max_filter_limit: Some(DEFAULT_MAX_QUERY_RESULTS),
max_query_results: DEFAULT_MAX_QUERY_RESULTS,
default_filter_limit: 500,
auth_dm: false,
min_pow: None,
kinds_blacklist: HashSet::from(BLACKLISTED_KINDS),
write_policy: None,
query_policy: None,
test: LocalRelayTestOptions::default(),
}
}
}
impl LocalRelayBuilder {
#[inline]
pub fn addr(mut self, ip: IpAddr) -> Self {
self.addr = Some(ip);
self
}
#[inline]
pub fn port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
#[inline]
pub fn database<D>(mut self, database: D) -> Self
where
D: IntoNostrDatabase,
{
self.database = Some(database.into_nostr_database());
self
}
#[inline]
pub fn mode(mut self, mode: LocalRelayBuilderMode) -> Self {
self.mode = mode;
self
}
#[inline]
pub fn rate_limit(mut self, limit: RateLimit) -> Self {
self.rate_limit = limit;
self
}
#[inline]
pub fn queries_per_minute(mut self, max: u32) -> Self {
self.queries_per_minute = max;
self
}
#[inline]
pub fn auth_events_per_minute(mut self, max: u32) -> Self {
self.auth_events_per_minute = max;
self
}
#[inline]
pub fn messages_per_minute(mut self, max: u32) -> Self {
self.messages_per_minute = max;
self
}
#[inline]
pub fn nip42(mut self, opts: LocalRelayBuilderNip42) -> Self {
self.nip42 = Some(opts);
self
}
#[inline]
pub fn max_connections(mut self, max: usize) -> Self {
self.max_connections = max;
self
}
#[inline]
pub fn max_websocket_message_size(mut self, max: usize) -> Self {
self.max_websocket_message_size = max;
self
}
#[inline]
pub fn max_event_size(mut self, max: usize) -> Self {
self.max_event_size = max;
self
}
#[inline]
pub fn websocket_handshake_timeout(mut self, timeout: Duration) -> Self {
self.websocket_handshake_timeout = timeout;
self
}
#[inline]
pub fn max_subid_length(mut self, max: usize) -> Self {
self.max_subid_length = max;
self
}
#[inline]
pub fn max_filters_per_req(mut self, max: usize) -> Self {
self.max_filters_per_req = max;
self
}
#[inline]
pub fn max_subscription_bytes(mut self, max: usize) -> Self {
self.max_subscription_bytes = max;
self
}
#[inline]
pub fn max_negentropy_subscriptions(mut self, max: usize) -> Self {
self.max_negentropy_subscriptions = max;
self
}
#[inline]
pub fn max_negentropy_items(mut self, max: usize) -> Self {
self.max_negentropy_items = max;
self
}
#[inline]
pub fn max_filter_limit(mut self, max: usize) -> Self {
self.max_filter_limit = Some(max);
self
}
#[inline]
pub fn max_query_results(mut self, max: usize) -> Self {
self.max_query_results = max;
self
}
#[inline]
pub fn default_filter_limit(mut self, limit: usize) -> Self {
self.default_filter_limit = limit;
self
}
#[inline]
pub fn auth_dm(mut self, enable: bool) -> Self {
self.auth_dm = enable;
self
}
#[inline]
pub fn min_pow(mut self, difficulty: u8) -> Self {
if difficulty > 0 {
self.min_pow = Some(difficulty);
}
self
}
#[inline]
pub fn blacklist_kinds(mut self, kinds: &[Kind]) -> Self {
self.kinds_blacklist.extend(kinds);
self
}
#[inline]
pub fn write_policy<T>(mut self, policy: T) -> Self
where
T: WritePolicy + 'static,
{
self.write_policy = Some(Arc::new(policy));
self
}
#[inline]
pub fn query_policy<T>(mut self, policy: T) -> Self
where
T: QueryPolicy + 'static,
{
self.query_policy = Some(Arc::new(policy));
self
}
#[inline]
pub(crate) fn test(mut self, test: LocalRelayTestOptions) -> Self {
self.test = test;
self
}
#[inline]
pub fn build(self) -> LocalRelay {
LocalRelay::from_builder(self)
}
}