Compare commits

..

No commits in common. "61fcbcf25747eee0699f3e158b9c8f883fed292b" and "72c874ff3708c35ab7ba1fbfdf393bdc38d724ad" have entirely different histories.

21 changed files with 712 additions and 956 deletions

1284
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "chorus" name = "chorus"
version = "2.0.1" version = "2.0.0"
description = "A personal relay for nostr" description = "A personal relay for nostr"
authors = ["Mike Dilger <mike@mikedilger.com>"] authors = ["Mike Dilger <mike@mikedilger.com>"]
license = "MIT" license = "MIT"
@ -9,7 +9,7 @@ edition = "2021"
[dependencies] [dependencies]
base64 = "0.22" base64 = "0.22"
bitcoin_hashes = "0.19" bitcoin_hashes = { version = "0.16", features = [ "bitcoin-io" ] }
dashmap = "6" dashmap = "6"
env_logger = "0.11" env_logger = "0.11"
futures = "0.3" futures = "0.3"
@ -24,12 +24,12 @@ log = "0.4"
mime-sniffer = "0.1" mime-sniffer = "0.1"
mime2ext = "0.1" mime2ext = "0.1"
negentropy = "0.5" negentropy = "0.5"
pocket-types = { git = "https://github.com/mikedilger/pocket", ref = "43d35015f7caf1db48eb846a1d6916a5716048da" } pocket-types = { git = "https://github.com/mikedilger/pocket", branch = "master" }
pocket-db = { git = "https://github.com/mikedilger/pocket", ref = "43d35015f7caf1db48eb846a1d6916a5716048da" } pocket-db = { git = "https://github.com/mikedilger/pocket", branch = "master" }
parking_lot = "0.12" parking_lot = "0.12"
rustls-pki-types = "1.11" rustls-pki-types = "1.11"
rustls-pemfile = "2.2" rustls-pemfile = "2.2"
secp256k1 = { version = "0.31", features = [ "hashes", "global-context" ] } secp256k1 = { version = "0.30", features = [ "hashes", "global-context" ] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
speedy = "0.8" speedy = "0.8"

View File

@ -97,14 +97,6 @@ key_pem_path = "/opt/chorus/etc/tls/privkey.pem"
# description = "A default config of the Chorus relay" # description = "A default config of the Chorus relay"
# This is a banner URL pointing to an image representing your relay, displayed in the NIP-11
# response.
#
# Default is not set
#
# banner_url =
# This is an icon URL pointing to an image representing your relay, displayed in the NIP-11 # This is an icon URL pointing to an image representing your relay, displayed in the NIP-11
# response. # response.
# #
@ -113,20 +105,6 @@ key_pem_path = "/opt/chorus/etc/tls/privkey.pem"
# icon_url = # icon_url =
# This is an optional privacy policy as a blob of text (not a URL, not HTML).
#
# Default is not set
#
# privacy_policy = ""
# This is an optional terms of service document as a blob of text (not a URL, not HTML).
#
# Default is not set
#
# terms_of_service = ""
# This is an optional contact for your relay, displayed in the NIP-11 response. # This is an optional contact for your relay, displayed in the NIP-11 response.
# #
# Default is not set # Default is not set

View File

@ -94,22 +94,10 @@ This is an optional description for your relay, displayed in the NIP-11 response
Default is "A default config of the Chorus relay". Default is "A default config of the Chorus relay".
### banner_url
This is an optional URL for an graphical banner representing your relay, displayed in the NIP-11 response.
### icon_url ### icon_url
This is an optional URL for an graphical icon representing your relay, displayed in the NIP-11 response. This is an optional URL for an graphical icon representing your relay, displayed in the NIP-11 response.
### privacy_policy
This is an optional privacy policy as a blob of text (not a URL, not HTML).
### terms_of_service
This is an optional terms of service document as a blob of text (not a URL, not HTML).
### contact ### contact
This is an optional administrative contact for your relay, displayed in the NIP-11 response. This is an optional administrative contact for your relay, displayed in the NIP-11 response.

View File

@ -83,8 +83,7 @@ Go ahead and edit that file to your liking. In particular:
- Change the `ip_address` to your internet-accessible IP address (if you are running directly) - Change the `ip_address` to your internet-accessible IP address (if you are running directly)
or to 127.0.0.1 with a local port like 8080 (if you are proxying behind nginx) or to 127.0.0.1 with a local port like 8080 (if you are proxying behind nginx)
- Change the port if necessary - Change the port if necessary
- Change the name, description, banner_url, icon_url, privacy_policy, terms_of_service and - Change the name, description, icon_url and contact (e.g. your email address) as desired
contact (e.g. your email address) as desired
- Set your contact_public_key_hex (it is an option, so use `Some()`) - Set your contact_public_key_hex (it is an option, so use `Some()`)
- Set hex keys of users for which this relay will act as a personal relay - Set hex keys of users for which this relay will act as a personal relay

View File

@ -10,10 +10,10 @@
2) Users and moderators are now dynamically configured in the database. Use `chorus_cmd` to 2) Users and moderators are now dynamically configured in the database. Use `chorus_cmd` to
manage them from the command line: manage them from the command line:
* Adding a user: `chorus_cmd <chorus.toml> add_user <pubkey> 0` * Adding a user: `chorus_cmd add_user <pubkey> 0`
* Adding a moderator: `chorus_cmd <chorus.toml> add_user <pubkey> 1` * Adding a moderator: `chorus_cmd add_user <pubkey> 1`
* Removing a user or moderator: `chorus_cmd <chorus.toml> rm_user <pubkey>` * Removing a user or moderator: `chorus_cmd rm_user <pubkey>`
* Listing users and moderators: `chorus_cmd <chorus.toml> dump_users` * Listing users and moderators: `chorus_cmd dump_users`
3) Remove the following from your config file as these are no longer used: 3) Remove the following from your config file as these are no longer used:

View File

@ -10,8 +10,6 @@ certchain_pem_path = "tls/fullchain.pem"
key_pem_path = "tls/privkey.pem" key_pem_path = "tls/privkey.pem"
name = "Chorus Sample" name = "Chorus Sample"
description = "A sample run of the Chorus relay" description = "A sample run of the Chorus relay"
privacy_policy = "This relay guarantees nothing. Privacy is your concern, not ours."
terms_of_service = "This relay guarantees nothing. Use at your own risk."
# icon_url = # icon_url =
open_relay = false open_relay = false
admin_hex_keys = [ admin_hex_keys = [

View File

@ -10,7 +10,7 @@ fn main() -> Result<(), Error> {
// Get args (config path) // Get args (config path)
let mut args = env::args(); let mut args = env::args();
if args.len() <= 1 { if args.len() <= 1 {
panic!("USAGE: chorus_cmd <config_path> <command> [args...]"); panic!("USAGE: chorus_moderate <config_path>");
} }
let _ = args.next(); // ignore program name let _ = args.next(); // ignore program name

View File

@ -1,27 +0,0 @@
use chorus::error::Error;
use chorus::globals::GLOBALS;
use std::env;
#[tokio::main]
async fn main() -> Result<(), Error> {
// Get args (config path)
let mut args = env::args();
if args.len() <= 1 {
panic!("USAGE: chorus <config_path>");
}
let _ = args.next(); // ignore program name
let config_path = args.next().unwrap();
let config = chorus::load_config(&config_path)?;
chorus::setup_logging(&config);
// Log host name
log::info!(target: "Server", "HOSTNAME = {}", config.hostname);
chorus::setup_store(&config)?;
let _ = GLOBALS.store.get().unwrap().sync();
Ok(())
}

View File

@ -19,10 +19,7 @@ pub struct FriendlyConfig {
pub key_pem_path: String, pub key_pem_path: String,
pub name: Option<String>, pub name: Option<String>,
pub description: Option<String>, pub description: Option<String>,
pub banner_url: Option<String>,
pub icon_url: Option<String>, pub icon_url: Option<String>,
pub privacy_policy: Option<String>,
pub terms_of_service: Option<String>,
pub contact: Option<String>, pub contact: Option<String>,
#[serde(alias = "public_key_hex")] #[serde(alias = "public_key_hex")]
pub contact_public_key_hex: Option<String>, pub contact_public_key_hex: Option<String>,
@ -63,10 +60,7 @@ impl Default for FriendlyConfig {
key_pem_path: "/opt/chorus/etc/tls/privkey.pem".to_string(), key_pem_path: "/opt/chorus/etc/tls/privkey.pem".to_string(),
name: Some("Chorus Default".to_string()), name: Some("Chorus Default".to_string()),
description: Some("A default config of the Chorus relay".to_string()), description: Some("A default config of the Chorus relay".to_string()),
banner_url: None,
icon_url: None, icon_url: None,
privacy_policy: None,
terms_of_service: None,
contact: None, contact: None,
contact_public_key_hex: None, contact_public_key_hex: None,
open_relay: false, open_relay: false,
@ -108,10 +102,7 @@ impl FriendlyConfig {
key_pem_path, key_pem_path,
name, name,
description, description,
banner_url,
icon_url, icon_url,
privacy_policy,
terms_of_service,
contact, contact,
contact_public_key_hex, contact_public_key_hex,
open_relay, open_relay,
@ -168,10 +159,7 @@ impl FriendlyConfig {
key_pem_path, key_pem_path,
name, name,
description, description,
banner_url,
icon_url, icon_url,
privacy_policy,
terms_of_service,
contact, contact,
contact_public_key, contact_public_key,
open_relay, open_relay,
@ -213,10 +201,7 @@ pub struct Config {
pub key_pem_path: String, pub key_pem_path: String,
pub name: Option<String>, pub name: Option<String>,
pub description: Option<String>, pub description: Option<String>,
pub banner_url: Option<String>,
pub icon_url: Option<String>, pub icon_url: Option<String>,
pub privacy_policy: Option<String>,
pub terms_of_service: Option<String>,
pub contact: Option<String>, pub contact: Option<String>,
pub contact_public_key: Option<Pubkey>, pub contact_public_key: Option<Pubkey>,
pub open_relay: bool, pub open_relay: bool,
@ -253,8 +238,6 @@ impl Default for Config {
} }
impl Config { impl Config {
/// Get the URI for our server matching the inner Uri, overridden with either
/// our base_url parts or our hostname/port.
pub fn uri_parts(&self, inner: Uri, http: bool) -> Result<http::uri::Parts, Error> { pub fn uri_parts(&self, inner: Uri, http: bool) -> Result<http::uri::Parts, Error> {
let mut uri_parts = inner.into_parts(); let mut uri_parts = inner.into_parts();

View File

@ -45,7 +45,7 @@ pub enum ChorusError {
BannedUser, BannedUser,
// Base64 Decode Error // Base64 Decode Error
Base64Decode(base64::DecodeError), // 24b Base64Decode(base64::DecodeError),
// Blocked IP // Blocked IP
BlockedIp, BlockedIp,
@ -54,16 +54,16 @@ pub enum ChorusError {
BlossomAuthFailure(String), BlossomAuthFailure(String),
// Channel Recv // Channel Recv
ChannelRecv(tokio::sync::broadcast::error::RecvError), // 24b ChannelRecv(tokio::sync::broadcast::error::RecvError),
// Channel Send // Channel Send
ChannelSend(tokio::sync::broadcast::error::SendError<u64>), // 16b ChannelSend(tokio::sync::broadcast::error::SendError<u64>),
// Config // Config
Config(Box<toml::de::Error>), // 16b Config(toml::de::Error),
// Crypto // Crypto
Crypto(secp256k1::Error), // 16b Crypto(secp256k1::Error),
// Closing on error(s) // Closing on error(s)
ErrorClose, ErrorClose,
@ -72,16 +72,16 @@ pub enum ChorusError {
EventIsInvalid(String), EventIsInvalid(String),
// From hex // From hex
FromHex(hex::FromHexError), // 24b FromHex(hex::FromHexError),
// From UTF8 // From UTF8
FromUtf8(Box<std::string::FromUtf8Error>), FromUtf8(std::string::FromUtf8Error),
// General // General
General(String), General(String),
// Http // Http
Http(Box<hyper::http::Error>), Http(hyper::http::Error),
// Hyper // Hyper
Hyper(hyper::Error), Hyper(hyper::Error),
@ -126,7 +126,7 @@ pub enum ChorusError {
PocketDb(pocket_db::Error), PocketDb(pocket_db::Error),
// Pocket Db Heed Error // Pocket Db Heed Error
PocketDbHeed(Box<pocket_db::heed::Error>), PocketDbHeed(pocket_db::heed::Error),
// Pocket Types Error // Pocket Types Error
PocketType(pocket_types::Error), PocketType(pocket_types::Error),
@ -141,7 +141,7 @@ pub enum ChorusError {
Restricted, Restricted,
// Rustls // Rustls
Rustls(Box<tokio_rustls::rustls::Error>), Rustls(tokio_rustls::rustls::Error),
// Filter is underspecified // Filter is underspecified
Scraper, Scraper,
@ -165,7 +165,7 @@ pub enum ChorusError {
TooManySubscriptions, TooManySubscriptions,
// Tungstenite // Tungstenite
Tungstenite(Box<hyper_tungstenite::tungstenite::error::Error>), Tungstenite(hyper_tungstenite::tungstenite::error::Error),
// URL Parse // URL Parse
UrlParse(url::ParseError), UrlParse(url::ParseError),
@ -382,7 +382,7 @@ impl From<toml::de::Error> for Error {
#[track_caller] #[track_caller]
fn from(err: toml::de::Error) -> Self { fn from(err: toml::de::Error) -> Self {
Error { Error {
inner: ChorusError::Config(Box::new(err)), inner: ChorusError::Config(err),
location: std::panic::Location::caller(), location: std::panic::Location::caller(),
} }
} }
@ -402,7 +402,7 @@ impl From<hyper::http::Error> for Error {
#[track_caller] #[track_caller]
fn from(err: hyper::http::Error) -> Self { fn from(err: hyper::http::Error) -> Self {
Error { Error {
inner: ChorusError::Http(Box::new(err)), inner: ChorusError::Http(err),
location: std::panic::Location::caller(), location: std::panic::Location::caller(),
} }
} }
@ -472,7 +472,7 @@ impl From<pocket_db::heed::Error> for Error {
#[track_caller] #[track_caller]
fn from(err: pocket_db::heed::Error) -> Self { fn from(err: pocket_db::heed::Error) -> Self {
Error { Error {
inner: ChorusError::PocketDbHeed(Box::new(err)), inner: ChorusError::PocketDbHeed(err),
location: std::panic::Location::caller(), location: std::panic::Location::caller(),
} }
} }
@ -492,7 +492,7 @@ impl From<tokio_rustls::rustls::Error> for Error {
#[track_caller] #[track_caller]
fn from(err: tokio_rustls::rustls::Error) -> Self { fn from(err: tokio_rustls::rustls::Error) -> Self {
Error { Error {
inner: ChorusError::Rustls(Box::new(err)), inner: ChorusError::Rustls(err),
location: std::panic::Location::caller(), location: std::panic::Location::caller(),
} }
} }
@ -502,7 +502,7 @@ impl From<hyper_tungstenite::tungstenite::error::Error> for Error {
#[track_caller] #[track_caller]
fn from(err: hyper_tungstenite::tungstenite::error::Error) -> Self { fn from(err: hyper_tungstenite::tungstenite::error::Error) -> Self {
Error { Error {
inner: ChorusError::Tungstenite(Box::new(err)), inner: ChorusError::Tungstenite(err),
location: std::panic::Location::caller(), location: std::panic::Location::caller(),
} }
} }
@ -572,7 +572,7 @@ impl From<std::string::FromUtf8Error> for Error {
#[track_caller] #[track_caller]
fn from(err: std::string::FromUtf8Error) -> Self { fn from(err: std::string::FromUtf8Error) -> Self {
Error { Error {
inner: ChorusError::FromUtf8(Box::new(err)), inner: ChorusError::FromUtf8(err),
location: std::panic::Location::caller(), location: std::panic::Location::caller(),
} }
} }

View File

@ -220,7 +220,7 @@ async fn websocket_thread(peer: HashedPeer, websocket: HyperWebsocket, origin: S
last_message: Instant::now(), last_message: Instant::now(),
burst_tokens: GLOBALS.config.read().throttling_burst, burst_tokens: GLOBALS.config.read().throttling_burst,
challenge: TextNonce::new().into_string(), challenge: TextNonce::new().into_string(),
authed_as: Vec::new(), user: None,
error_punishment: 0.0, error_punishment: 0.0,
replied: false, replied: false,
negentropy_sub: None, negentropy_sub: None,
@ -254,16 +254,14 @@ async fn websocket_thread(peer: HashedPeer, websocket: HyperWebsocket, origin: S
// Handle the websocket // Handle the websocket
if let Err(e) = ws_service.handle_websocket_stream().await { if let Err(e) = ws_service.handle_websocket_stream().await {
match e.inner { match e.inner {
ChorusError::Tungstenite(ref t) => { ChorusError::Tungstenite(tungstenite::error::Error::Protocol(
match *t.as_ref() {
tungstenite::error::Error::Protocol(
tungstenite::error::ProtocolError::ResetWithoutClosingHandshake, tungstenite::error::ProtocolError::ResetWithoutClosingHandshake,
) => { )) => {
// So they disconnected ungracefully. // So they disconnected ungracefully.
// No big deal, still SessionExit::Ok // No big deal, still SessionExit::Ok
msg = "Reset"; msg = "Reset";
} }
tungstenite::error::Error::Io(ref ioerror) => { ChorusError::Tungstenite(tungstenite::error::Error::Io(ref ioerror)) => {
match ioerror.kind() { match ioerror.kind() {
std::io::ErrorKind::ConnectionReset std::io::ErrorKind::ConnectionReset
| std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::ConnectionAborted
@ -278,13 +276,6 @@ async fn websocket_thread(peer: HashedPeer, websocket: HyperWebsocket, origin: S
} }
} }
} }
_ => {
log::error!(target: "Client", "{}: {}", peer, e);
session_exit = SessionExit::ErrorExit;
msg = "Error Exited";
}
}
}
ChorusError::ErrorClose => { ChorusError::ErrorClose => {
session_exit = SessionExit::TooManyErrors; session_exit = SessionExit::TooManyErrors;
msg = "Errored Out"; msg = "Errored Out";
@ -365,7 +356,7 @@ struct WebSocketService {
pub last_message: Instant, pub last_message: Instant,
pub burst_tokens: usize, pub burst_tokens: usize,
pub challenge: String, pub challenge: String,
pub authed_as: Vec<Pubkey>, pub user: Option<Pubkey>,
pub error_punishment: f32, pub error_punishment: f32,
pub replied: bool, pub replied: bool,
pub negentropy_sub: Option<String>, pub negentropy_sub: Option<String>,
@ -482,8 +473,8 @@ impl WebSocketService {
.unwrap() .unwrap()
.get_event_by_offset(new_event_offset)?; .get_event_by_offset(new_event_offset)?;
let event_flags = nostr::event_flags(event, self.authed_as.as_slice()); let event_flags = nostr::event_flags(event, &self.user);
let authorized_user = self.authed_as.iter().any(|u| is_authorized_user(*u)); let authorized_user = self.user.map(is_authorized_user).unwrap_or(false);
'subs: for (subid, filters) in self.subscriptions.iter() { 'subs: for (subid, filters) in self.subscriptions.iter() {
for filter in filters.iter() { for filter in filters.iter() {

View File

@ -124,9 +124,10 @@ impl WebSocketService {
return Err(ChorusError::TooManySubscriptions.into()); return Err(ChorusError::TooManySubscriptions.into());
} }
let authorized_user = self.authed_as.iter().any(|u| crate::is_authorized_user(*u)); let user = self.user;
let authorized_user = self.user.map(crate::is_authorized_user).unwrap_or(false);
if self.authed_as.is_empty() { if user.is_none() {
for filter in filters.iter() { for filter in filters.iter() {
// If any DM kinds were requested, complain. // If any DM kinds were requested, complain.
// But if NO kinds were requested, we will just silently not return DMs (elsewhere) // But if NO kinds were requested, we will just silently not return DMs (elsewhere)
@ -162,7 +163,7 @@ impl WebSocketService {
for filter in filters.iter() { for filter in filters.iter() {
let screen = |event: &Event| -> ScreenResult { let screen = |event: &Event| -> ScreenResult {
let event_flags = event_flags(event, self.authed_as.as_slice()); let event_flags = event_flags(event, &user);
screen_outgoing_event(event, &event_flags, authorized_user) screen_outgoing_event(event, &event_flags, authorized_user)
}; };
let (filter_events, was_redacted) = { let (filter_events, was_redacted) = {
@ -316,12 +317,13 @@ impl WebSocketService {
} }
async fn event_inner(&mut self) -> Result<(), Error> { async fn event_inner(&mut self) -> Result<(), Error> {
let authorized_user = self.authed_as.iter().any(|u| crate::is_authorized_user(*u)); let user = self.user;
let authorized_user = self.user.map(crate::is_authorized_user).unwrap_or(false);
// Delineate the event back out of the session buffer // Delineate the event back out of the session buffer
let event = unsafe { Event::delineate(&self.buffer)? }; let event = unsafe { Event::delineate(&self.buffer)? };
let event_flags = event_flags(event, self.authed_as.as_slice()); let event_flags = event_flags(event, &user);
if GLOBALS.config.read().verify_events { if GLOBALS.config.read().verify_events {
// Verify the event is valid (id is hash, signature is valid) // Verify the event is valid (id is hash, signature is valid)
@ -345,10 +347,10 @@ impl WebSocketService {
// Screen the event to see if we are willing to accept it // Screen the event to see if we are willing to accept it
if !screen_incoming_event(event, event_flags, authorized_user).await? { if !screen_incoming_event(event, event_flags, authorized_user).await? {
if self.authed_as.is_empty() { if self.user.is_some() {
return Err(ChorusError::AuthRequired.into());
} else {
return Err(ChorusError::Restricted.into()); return Err(ChorusError::Restricted.into());
} else {
return Err(ChorusError::AuthRequired.into());
} }
} }
@ -461,7 +463,7 @@ impl WebSocketService {
} }
// They are now authenticated // They are now authenticated
self.authed_as.push(event.pubkey()); self.user = Some(event.pubkey());
Ok(()) Ok(())
} }
@ -540,12 +542,13 @@ impl WebSocketService {
return Ok(()); return Ok(());
} }
let authorized_user = self.authed_as.iter().any(|u| crate::is_authorized_user(*u)); let user = self.user;
let authorized_user = self.user.map(crate::is_authorized_user).unwrap_or(false);
// Find all matching events // Find all matching events
let mut events: Vec<&Event> = Vec::new(); let mut events: Vec<&Event> = Vec::new();
let screen = |event: &Event| -> ScreenResult { let screen = |event: &Event| -> ScreenResult {
let event_flags = event_flags(event, self.authed_as.as_slice()); let event_flags = event_flags(event, &user);
screen_outgoing_event(event, &event_flags, authorized_user) screen_outgoing_event(event, &event_flags, authorized_user)
}; };
let (filter_events, _redacted) = { let (filter_events, _redacted) = {
@ -868,10 +871,13 @@ pub struct EventFlags {
pub tags_current_user: bool, pub tags_current_user: bool,
} }
pub fn event_flags(event: &Event, authed_as: &[Pubkey]) -> EventFlags { pub fn event_flags(event: &Event, user: &Option<Pubkey>) -> EventFlags {
let author_is_an_authorized_user = crate::is_authorized_user(event.pubkey()); let author_is_an_authorized_user = crate::is_authorized_user(event.pubkey());
let author_is_current_user = authed_as.iter().any(|u| *u == event.pubkey()); let author_is_current_user = match user {
None => false,
Some(pk) => event.pubkey() == *pk,
};
let mut tags_an_authorized_user = false; let mut tags_an_authorized_user = false;
let mut tags_current_user = false; let mut tags_current_user = false;
@ -881,9 +887,11 @@ pub fn event_flags(event: &Event, authed_as: &[Pubkey]) -> EventFlags {
if let Some(b"p") = tag.next() { if let Some(b"p") = tag.next() {
if let Some(value) = tag.next() { if let Some(value) = tag.next() {
if let Ok(tagged_pk) = Pubkey::read_hex(value) { if let Ok(tagged_pk) = Pubkey::read_hex(value) {
if authed_as.contains(&tagged_pk) { if let Some(current_user) = user {
if *current_user == tagged_pk {
tags_current_user = true; tags_current_user = true;
} }
}
if crate::is_authorized_user(tagged_pk) { if crate::is_authorized_user(tagged_pk) {
tags_an_authorized_user = true; tags_an_authorized_user = true;

View File

@ -53,36 +53,6 @@ pub async fn serve_http(
let uri = request.uri().to_owned(); let uri = request.uri().to_owned();
if p == "/privacy-policy" {
let config = &*GLOBALS.config.read();
if let Some(pp) = &config.privacy_policy {
let response = Response::builder()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "Authorization, *")
.header("Access-Control-Allow-Methods", "*")
.header("Allow", "OPTIONS, GET, HEAD")
.header("Content-Type", "text/plain")
.status(StatusCode::OK)
.body(Full::new(pp.clone().into()).map_err(|e| e.into()).boxed())?;
return Ok(response);
}
}
if p == "/terms-of-service" {
let config = &*GLOBALS.config.read();
if let Some(tos) = &config.terms_of_service {
let response = Response::builder()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "Authorization, *")
.header("Access-Control-Allow-Methods", "*")
.header("Allow", "OPTIONS, GET, HEAD")
.header("Content-Type", "text/plain")
.status(StatusCode::OK)
.body(Full::new(tos.clone().into()).map_err(|e| e.into()).boxed())?;
return Ok(response);
}
}
// Try blossom if enabled // Try blossom if enabled
if GLOBALS.config.read().blossom_directory.is_some() { if GLOBALS.config.read().blossom_directory.is_some() {
match blossom::handle(request).await { match blossom::handle(request).await {

View File

@ -5,7 +5,6 @@ use crate::ip::HashedPeer;
use http_body_util::combinators::BoxBody; use http_body_util::combinators::BoxBody;
use http_body_util::{BodyExt, Full}; use http_body_util::{BodyExt, Full};
use hyper::body::Bytes; use hyper::body::Bytes;
use hyper::http::uri::Uri;
use hyper::{Response, StatusCode}; use hyper::{Response, StatusCode};
pub async fn serve_nip11(peer: HashedPeer) -> Result<Response<BoxBody<Bytes, Error>>, Error> { pub async fn serve_nip11(peer: HashedPeer) -> Result<Response<BoxBody<Bytes, Error>>, Error> {
@ -81,62 +80,24 @@ fn build_rid(config: &Config) -> String {
rid.push_str(description); rid.push_str(description);
rid.push('\"'); rid.push('\"');
} }
if let Some(banner_url) = &config.banner_url {
rid.push(',');
rid.push_str("\"banner\":\"");
rid.push_str(banner_url);
rid.push('\"');
}
if let Some(icon_url) = &config.icon_url { if let Some(icon_url) = &config.icon_url {
rid.push(','); rid.push(',');
rid.push_str("\"icon\":\""); rid.push_str("\"icon\":\"");
rid.push_str(icon_url); rid.push_str(icon_url);
rid.push('\"'); rid.push('\"');
} }
if let Some(pubkey) = &config.contact_public_key {
let mut pkh: [u8; 64] = [0; 64];
pubkey.write_hex(&mut pkh).unwrap();
rid.push(',');
rid.push_str("\"pubkey\":\"");
rid.push_str(unsafe { std::str::from_utf8_unchecked(pkh.as_slice()) });
rid.push('\"');
}
if let Some(contact) = &config.contact { if let Some(contact) = &config.contact {
rid.push(','); rid.push(',');
rid.push_str("\"contact\":\""); rid.push_str("\"contact\":\"");
rid.push_str(contact); rid.push_str(contact);
rid.push('\"'); rid.push('\"');
} }
if config.privacy_policy.is_some() { if let Some(pubkey) = &config.contact_public_key {
let mut pkh: [u8; 64] = [0; 64];
pubkey.write_hex(&mut pkh).unwrap();
rid.push(','); rid.push(',');
rid.push_str("\"privacy_policy\":\""); rid.push_str("\"pubkey\":\"");
let url = match config.uri_parts( rid.push_str(unsafe { std::str::from_utf8_unchecked(pkh.as_slice()) });
Uri::from_static("https://authority-will-be-replaced/privacy-policy"),
true,
) {
Ok(parts) => match Uri::from_parts(parts) {
Ok(uri) => format!("{}", uri),
Err(_) => "".to_owned(),
},
Err(_) => "".to_owned(),
};
rid.push_str(&url);
rid.push('\"');
}
if config.terms_of_service.is_some() {
rid.push(',');
rid.push_str("\"terms_of_service\":\"");
let url = match config.uri_parts(
Uri::from_static("https://authority-will-be-replaced/terms-of-service"),
true,
) {
Ok(parts) => match Uri::from_parts(parts) {
Ok(uri) => format!("{}", uri),
Err(_) => "".to_owned(),
},
Err(_) => "".to_owned(),
};
rid.push_str(&url);
rid.push('\"'); rid.push('\"');
} }

View File

@ -18,12 +18,6 @@ then
exit 1 exit 1
fi fi
# ADD ADMIN AS A USER ------------
echo "Adding user..."
PUBKEY=12bb541d03bfc3cab0f4a8e4db28947f60faae6fca4e315eb27f809c6eff9a0b
../target/release/chorus_cmd ./config.toml add_user $PUBKEY 0
# UPLOAD TEST ------------ # UPLOAD TEST ------------
FILE="./Example.png" FILE="./Example.png"

View File

@ -1,3 +0,0 @@
data
relay-tester

View File

@ -1,12 +0,0 @@
# Testing Chorus with relay-tester
First, git clone https://github.com/mikedilger/relay-tester and build that project
(cargo build --release).
Then copy that target/release/relay-tester binary into this directory.
Then run from two different shells, in this order:
shell1: ./test_chorus.sh
shell2: ./run_relay_tester.sh

View File

@ -1,12 +0,0 @@
#!/bin/bash
if [ ! -x relay-tester ] ; then
echo "You must build https://github.com/mikedilge/relay-tester and copy the "
echo "resultant target/release/relay-tester binary into this directory."
exit 1
fi
./relay-tester \
ws://localhost:8080/ \
nsec16xfd467kyd3xpu9x5u4933u00v73xrl0jyq9rk5ktd9t2j38k20qtwxuj3 \
nsec1l50yuf6uxm2l5qxm87fkm56z3m7g88jnfy5s6az5wscxpu5l2yqq6qwk88

View File

@ -1,10 +0,0 @@
#!/bin/bash
echo "Use ws://localhost:8080/ as the relay url"
cargo build --release
rm -rf ./data/
../target/release/chorus_init ./test_chorus.toml
../target/release/chorus_cmd ./test_chorus.toml add_user de16d3ed2d5ceb91d33e39dbe30585164e0c19f3f2e2a5b121def086b447a2e5 0
../target/release/chorus_cmd ./test_chorus.toml add_user 35d6bbcf17fc31a9c4f7a2f68aa40ad32c8f9de1ae77505dc5eb3722d8b2987d 0
../target/release/chorus ./test_chorus.toml

View File

@ -1,40 +0,0 @@
# See contrib/chorus.toml for a documented config file
data_directory = "./data"
ip_address = "127.0.0.1"
port = 8080
hostname = "localhost"
chorus_is_behind_a_proxy = false
use_tls = false
certchain_pem_path = "tls/fullchain.pem"
key_pem_path = "tls/privkey.pem"
name = "Chorus Sample"
description = "A sample run of the Chorus relay"
#icon_url =
open_relay = false
admin_hex_keys = [
"ee11a5dff40c19a555f41fe42b48f00e618c91225622ae37b6c2bb67b76c4e49",
# npub1mctd8mfdtn4er5e788d7xpv9ze8qcx0n7t32tvfpmmcgddz85tjsuyxe7z
"de16d3ed2d5ceb91d33e39dbe30585164e0c19f3f2e2a5b121def086b447a2e5",
# npub1xhtthnchlsc6n38h5tmg4fq26vkgl80p4em4qhw9avmj9k9jnp7sm78ql6
"35d6bbcf17fc31a9c4f7a2f68aa40ad32c8f9de1ae77505dc5eb3722d8b2987d"
]
verify_events = true
allow_scraping = false
allow_scrape_if_limited_to = 100
allow_scrape_if_max_seconds = 7200
max_subscriptions = 128
serve_ephemeral = true
serve_relay_lists = true
server_log_level = "Info"
library_log_level = "Info"
client_log_level = "Debug"
enable_ip_blocking = false
minimum_ban_seconds = 1
timeout_seconds = 60
max_connections_per_ip = 5
throttling_bytes_per_second = 131072
throttling_burst = 4194304
enable_negentropy = true