diff --git a/config.toml b/config.toml index ef5f68d..6a78aba 100644 --- a/config.toml +++ b/config.toml @@ -20,6 +20,9 @@ description = "A newly created nostr-rs-relay.\n\nCustomize this with your own i # ICO format. #favicon = "favicon.ico" +# URL of Relay's icon. +#relay_icon = "https://example.test/img.png" + [diagnostics] # Enable tokio tracing (for use with tokio-console) #tracing = false diff --git a/docs/database-maintenance.md b/docs/database-maintenance.md index e475918..f68cc90 100644 --- a/docs/database-maintenance.md +++ b/docs/database-maintenance.md @@ -112,7 +112,8 @@ seen" policy. ```console PRAGMA foreign_keys = ON; -TODO! + +DELETE FROM event WHERE first_seen < CAST(strftime('%s', date('now', '-30 day')) AS INT); ``` ### Delete Profile Events with No Recent Events diff --git a/src/bin/bulkloader.rs b/src/bin/bulkloader.rs index af276ea..77d9e74 100644 --- a/src/bin/bulkloader.rs +++ b/src/bin/bulkloader.rs @@ -20,7 +20,7 @@ pub fn main() -> Result<()> { let _trace_sub = tracing_subscriber::fmt::try_init(); println!("Nostr-rs-relay Bulk Loader"); // check for a database file, or create one. - let settings = config::Settings::new(&None); + let settings = config::Settings::new(&None)?; if !Path::new(&settings.database.data_directory).is_dir() { info!("Database directory does not exist"); return Err(Error::DatabaseDirError); diff --git a/src/config.rs b/src/config.rs index 03cc243..9222821 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,10 +1,8 @@ //! Configuration file and settings management +use crate::payment::Processor; use config::{Config, ConfigError, File}; use serde::{Deserialize, Serialize}; use std::time::Duration; -use tracing::warn; - -use crate::payment::Processor; #[derive(Debug, Serialize, Deserialize, Clone)] #[allow(unused)] @@ -15,6 +13,7 @@ pub struct Info { pub pubkey: Option, pub contact: Option, pub favicon: Option, + pub relay_icon: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -192,17 +191,23 @@ pub struct Settings { } impl Settings { - #[must_use] - pub fn new(config_file_name: &Option) -> Self { + pub fn new(config_file_name: &Option) -> Result { let default_settings = Self::default(); // attempt to construct settings with file let from_file = Self::new_from_default(&default_settings, config_file_name); match from_file { - Ok(f) => f, Err(e) => { - warn!("Error reading config file ({:?})", e); - default_settings + // pass up the parse error if the config file was specified, + // otherwise use the default config (with a warning). + if config_file_name.is_some() { + Err(e) + } else { + eprintln!("Error reading config file ({:?})", e); + eprintln!("WARNING: Default configuration settings will be used"); + Ok(default_settings) + } } + ok => ok, } } @@ -268,6 +273,7 @@ impl Default for Settings { pubkey: None, contact: None, favicon: None, + relay_icon: None, }, diagnostics: Diagnostics { tracing: false }, database: Database { diff --git a/src/info.rs b/src/info.rs index d2233ca..35f6430 100644 --- a/src/info.rs +++ b/src/info.rs @@ -45,6 +45,8 @@ pub struct RelayInfo { #[serde(skip_serializing_if = "Option::is_none")] pub contact: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub icon: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub supported_nips: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub software: Option, @@ -124,6 +126,7 @@ impl From for RelayInfo { limitation: Some(limitations), payment_url, fees, + icon: i.relay_icon, } } } diff --git a/src/main.rs b/src/main.rs index 6e8994e..ee92534 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,9 @@ use console_subscriber::ConsoleLayer; use nostr_rs_relay::cli::CLIArgs; use nostr_rs_relay::config; use nostr_rs_relay::server::start_server; +use std::fs; +use std::path::Path; +use std::process; use std::sync::mpsc as syncmpsc; use std::sync::mpsc::{Receiver as MpscReceiver, Sender as MpscSender}; use std::thread; @@ -24,11 +27,35 @@ fn main() { // get config file name from args let config_file_arg = args.config; + // Ensure the config file is readable if it was explicitly set + if let Some(config_path) = config_file_arg.as_ref() { + let path = Path::new(&config_path); + if !path.exists() { + eprintln!("Config file not found: {}", &config_path); + process::exit(1); + } + if !path.is_file() { + eprintln!("Invalid config file path: {}", &config_path); + process::exit(1); + } + if let Err(err) = fs::metadata(path) { + eprintln!("Error while accessing file metadata: {}", err); + process::exit(1); + } + if let Err(err) = fs::File::open(path) { + eprintln!("Config file is not readable: {}", err); + process::exit(1); + } + } + let mut _log_guard: Option = None; // configure settings from the config file (defaults to config.toml) // replace default settings with those read from the config file - let mut settings = config::Settings::new(&config_file_arg); + let mut settings = config::Settings::new(&config_file_arg).unwrap_or_else(|e| { + eprintln!("Error reading config file ({:?})", e); + process::exit(1); + }); // setup tracing if settings.diagnostics.tracing { diff --git a/src/repo/postgres.rs b/src/repo/postgres.rs index 8fc46eb..cc899e8 100644 --- a/src/repo/postgres.rs +++ b/src/repo/postgres.rs @@ -904,7 +904,7 @@ fn query_from_filter(f: &ReqFilter) -> Option> { } push_and = true; query - .push("e.created_at > ") + .push("e.created_at >= ") .push_bind(Utc.timestamp_opt(f.since.unwrap() as i64, 0).unwrap()); } @@ -915,7 +915,7 @@ fn query_from_filter(f: &ReqFilter) -> Option> { } push_and = true; query - .push("e.created_at < ") + .push("e.created_at <= ") .push_bind(Utc.timestamp_opt(f.until.unwrap() as i64, 0).unwrap()); } diff --git a/src/repo/sqlite.rs b/src/repo/sqlite.rs index 87d8556..8f5b798 100644 --- a/src/repo/sqlite.rs +++ b/src/repo/sqlite.rs @@ -1083,13 +1083,13 @@ fn query_from_filter(f: &ReqFilter) -> (String, Vec>, Option {}", f.since.unwrap()); + since_clause = format!("AND created_at >= {}", f.since.unwrap()); } else { since_clause = String::new(); }; // Query for timestamp if f.until.is_some() { - until_clause = format!("AND created_at < {}", f.until.unwrap()); + until_clause = format!("AND created_at <= {}", f.until.unwrap()); } else { until_clause = String::new(); }; @@ -1107,12 +1107,12 @@ fn query_from_filter(f: &ReqFilter) -> (String, Vec>, Option {}", f.since.unwrap()); + let created_clause = format!("created_at >= {}", f.since.unwrap()); filter_components.push(created_clause); } // Query for timestamp if f.until.is_some() { - let until_clause = format!("created_at < {}", f.until.unwrap()); + let until_clause = format!("created_at <= {}", f.until.unwrap()); filter_components.push(until_clause); } // never display hidden events diff --git a/src/subscription.rs b/src/subscription.rs index 822df44..17aaceb 100644 --- a/src/subscription.rs +++ b/src/subscription.rs @@ -319,8 +319,8 @@ impl ReqFilter { pub fn interested_in_event(&self, event: &Event) -> bool { // self.id.as_ref().map(|v| v == &event.id).unwrap_or(true) self.ids_match(event) - && self.since.map_or(true, |t| event.created_at > t) - && self.until.map_or(true, |t| event.created_at < t) + && self.since.map_or(true, |t| event.created_at >= t) + && self.until.map_or(true, |t| event.created_at <= t) && self.kind_match(event.kind) && (self.authors_match(event) || self.delegated_authors_match(event)) && self.tag_match(event)