mirror of
https://github.com/mikedilger/chorus.git
synced 2026-05-03 06:51:42 +00:00
Compare commits
No commits in common. "97c040b01c5e38afe9b2eccd17280744a389b1c0" and "db8b29dfc47d682a191e0bacc977c490bdceeea7" have entirely different histories.
97c040b01c
...
db8b29dfc4
@ -64,15 +64,6 @@ announce upgrade instructions until release.
|
|||||||
|
|
||||||
## Change Log
|
## Change Log
|
||||||
|
|
||||||
### Version 2.0
|
|
||||||
|
|
||||||
- IMPORTANT: You need to manually [Migrate](docs/MIGRATION.md) your users and moderators.
|
|
||||||
- Management commands have been added: listeventsneedingmoderation, listadmins, listmoderators,
|
|
||||||
grantmoderator, revokemoderator, listusers, grantuser, revokeuser, stats
|
|
||||||
- fix: subscriptions will be CLOSED: auth-required if any matching event requires auth. Chorus
|
|
||||||
will serve the redacted results first, but will not EOSE.
|
|
||||||
- fix: subscriptions will be CLOSED when completed, without EOSE, if the filter has any ids set.
|
|
||||||
|
|
||||||
### Version 1.7.2
|
### Version 1.7.2
|
||||||
|
|
||||||
- Support for NIP-62 (PR #1256) Right to Vanish
|
- Support for NIP-62 (PR #1256) Right to Vanish
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
# Migration
|
|
||||||
|
|
||||||
## From 1.0 to 2.0
|
|
||||||
|
|
||||||
1) Add to your config file `admin_hex_keys` to include the nostr hex keys of administrators.
|
|
||||||
These will (eventually) be allowed to manage users and moderators remotely via the management
|
|
||||||
interface. Note that being an admin does NOT automatically grant user or moderator rights,
|
|
||||||
it ONLY grants the right to administer users.
|
|
||||||
|
|
||||||
2) Users and moderators are now dynamically configured in the database. Use `chorus_cmd` to
|
|
||||||
manage them from the command line:
|
|
||||||
|
|
||||||
* Adding a user: `chorus_cmd add_user <pubkey> 0`
|
|
||||||
* Adding a moderator: `chorus_cmd add_user <pubkey> 1`
|
|
||||||
* Removing a user or moderator: `chorus_cmd rm_user <pubkey>`
|
|
||||||
* Listing users and moderators: `chorus_cmd dump_users`
|
|
||||||
|
|
||||||
3) Remove the following from your config file as these are no longer used:
|
|
||||||
|
|
||||||
* `user_hex_keys` - users are now dynamically configured and configuration is in the database.
|
|
||||||
* `moderator_hex_keys` - moderators are now dynamically configured and configuration is in the database.
|
|
||||||
|
|
||||||
4) Configuration setting `public_key_kex` has been renamed `contact_public_key_hex` and is
|
|
||||||
used only for the NIP-11 data.
|
|
||||||
|
|
||||||
16
src/lib.rs
16
src/lib.rs
@ -374,7 +374,7 @@ impl WebSocketService {
|
|||||||
if m.len() > self.burst_tokens {
|
if m.len() > self.burst_tokens {
|
||||||
log::info!(target: "Client", "{}: Rate limited exceeded", self.peer);
|
log::info!(target: "Client", "{}: Rate limited exceeded", self.peer);
|
||||||
let reply = NostrReply::Notice("Rate limit exceeded.".into());
|
let reply = NostrReply::Notice("Rate limit exceeded.".into());
|
||||||
self.websocket.send(Message::text(reply.as_json()?)).await?;
|
self.websocket.send(Message::text(reply.as_json())).await?;
|
||||||
let error = ChorusError::RateLimitExceeded;
|
let error = ChorusError::RateLimitExceeded;
|
||||||
self.error_punishment += error.punishment();
|
self.error_punishment += error.punishment();
|
||||||
return Err(error.into());
|
return Err(error.into());
|
||||||
@ -419,7 +419,7 @@ impl WebSocketService {
|
|||||||
|
|
||||||
// Offer AUTH to clients right off the bat
|
// Offer AUTH to clients right off the bat
|
||||||
let reply = NostrReply::Auth(self.challenge.clone());
|
let reply = NostrReply::Auth(self.challenge.clone());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
|
|
||||||
let mut last_message_at = Instant::now();
|
let mut last_message_at = Instant::now();
|
||||||
|
|
||||||
@ -493,7 +493,7 @@ impl WebSocketService {
|
|||||||
let message = NostrReply::Event(subid, event);
|
let message = NostrReply::Event(subid, event);
|
||||||
// note, this is not currently counted in throttling
|
// note, this is not currently counted in throttling
|
||||||
self.websocket
|
self.websocket
|
||||||
.send(Message::text(message.as_json()?))
|
.send(Message::text(message.as_json()))
|
||||||
.await?;
|
.await?;
|
||||||
continue 'subs;
|
continue 'subs;
|
||||||
}
|
}
|
||||||
@ -529,7 +529,7 @@ impl WebSocketService {
|
|||||||
if message.len() > self.burst_tokens {
|
if message.len() > self.burst_tokens {
|
||||||
log::info!(target: "Client", "{}: Rate limited exceeded", self.peer);
|
log::info!(target: "Client", "{}: Rate limited exceeded", self.peer);
|
||||||
let reply = NostrReply::Notice("Rate limit exceeded.".into());
|
let reply = NostrReply::Notice("Rate limit exceeded.".into());
|
||||||
self.websocket.send(Message::text(reply.as_json()?)).await?;
|
self.websocket.send(Message::text(reply.as_json())).await?;
|
||||||
let error = ChorusError::RateLimitExceeded;
|
let error = ChorusError::RateLimitExceeded;
|
||||||
self.error_punishment += error.punishment();
|
self.error_punishment += error.punishment();
|
||||||
return Err(error.into());
|
return Err(error.into());
|
||||||
@ -556,15 +556,15 @@ impl WebSocketService {
|
|||||||
if !self.replied {
|
if !self.replied {
|
||||||
if let Some(subid) = &self.negentropy_sub {
|
if let Some(subid) = &self.negentropy_sub {
|
||||||
let reply = NostrReply::NegErr(subid, format!("error: {e}"));
|
let reply = NostrReply::NegErr(subid, format!("error: {e}"));
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
} else {
|
} else {
|
||||||
let reply = NostrReply::Notice(format!("error: {}", e.inner));
|
let reply = NostrReply::Notice(format!("error: {}", e.inner));
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.error_punishment >= 1.0 {
|
if self.error_punishment >= 1.0 {
|
||||||
let reply = NostrReply::Notice("Closing due to error(s)".into());
|
let reply = NostrReply::Notice("Closing due to error(s)".into());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
return Err(ChorusError::ErrorClose.into());
|
return Err(ChorusError::ErrorClose.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -573,7 +573,7 @@ impl WebSocketService {
|
|||||||
let reply = NostrReply::Notice(
|
let reply = NostrReply::Notice(
|
||||||
"binary messages are not processed by this relay".to_owned(),
|
"binary messages are not processed by this relay".to_owned(),
|
||||||
);
|
);
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
log::info!(target: "Client",
|
log::info!(target: "Client",
|
||||||
"{}: Received unhandled binary message: {:02X?}",
|
"{}: Received unhandled binary message: {:02X?}",
|
||||||
self.peer,
|
self.peer,
|
||||||
|
|||||||
70
src/nostr.rs
70
src/nostr.rs
@ -46,7 +46,7 @@ impl WebSocketService {
|
|||||||
} else {
|
} else {
|
||||||
log::warn!(target: "Client", "{}: Received unhandled text message: {}", self.peer, msg);
|
log::warn!(target: "Client", "{}: Received unhandled text message: {}", self.peer, msg);
|
||||||
let reply = NostrReply::Notice("Command unrecognized".to_owned());
|
let reply = NostrReply::Notice("Command unrecognized".to_owned());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -106,7 +106,7 @@ impl WebSocketService {
|
|||||||
}
|
}
|
||||||
_ => NostrReply::Closed(&subid, NostrReplyPrefix::Error, format!("{}", e.inner)),
|
_ => NostrReply::Closed(&subid, NostrReplyPrefix::Error, format!("{}", e.inner)),
|
||||||
};
|
};
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
Err(e)
|
Err(e)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -141,7 +141,7 @@ impl WebSocketService {
|
|||||||
NostrReplyPrefix::AuthRequired,
|
NostrReplyPrefix::AuthRequired,
|
||||||
"DM kinds were included in the filters".to_owned(),
|
"DM kinds were included in the filters".to_owned(),
|
||||||
);
|
);
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,35 +199,29 @@ impl WebSocketService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let reply = NostrReply::Count(subid, events.len(), opthll);
|
let reply = NostrReply::Count(subid, events.len(), opthll);
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
} else {
|
} else {
|
||||||
for event in events.drain(..) {
|
for event in events.drain(..) {
|
||||||
let reply = NostrReply::Event(subid, event);
|
let reply = NostrReply::Event(subid, event);
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
}
|
|
||||||
|
|
||||||
// New policy Feb 2025: Redactions trigger a "CLOSED: auth-required" because
|
|
||||||
// some clients will not AUTH otherwise.
|
|
||||||
// (But we also already sent partial results, which I think is good)
|
|
||||||
if redacted {
|
|
||||||
// They need to AUTH first
|
|
||||||
let reply = NostrReply::Closed(
|
|
||||||
subid,
|
|
||||||
NostrReplyPrefix::AuthRequired,
|
|
||||||
"At least one matching event requires AUTH".to_owned(),
|
|
||||||
);
|
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if completes {
|
if completes {
|
||||||
// Closed
|
// Closed
|
||||||
let reply = NostrReply::Closed(subid, NostrReplyPrefix::None, "".to_owned());
|
let reply = if redacted {
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
NostrReply::Closed(subid, NostrReplyPrefix::None, "".to_owned())
|
||||||
|
} else {
|
||||||
|
NostrReply::Closed(
|
||||||
|
subid,
|
||||||
|
NostrReplyPrefix::Redacted,
|
||||||
|
"Some matching events could not be served to you.".to_owned(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
} else {
|
} else {
|
||||||
// EOSE
|
// EOSE
|
||||||
let reply = NostrReply::Eose(subid);
|
let reply = NostrReply::Eose(subid);
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,11 +301,11 @@ impl WebSocketService {
|
|||||||
},
|
},
|
||||||
_ => NostrReply::Ok(id, false, NostrReplyPrefix::Error, format!("{}", e.inner)),
|
_ => NostrReply::Ok(id, false, NostrReplyPrefix::Error, format!("{}", e.inner)),
|
||||||
};
|
};
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
Err(e)
|
Err(e)
|
||||||
} else {
|
} else {
|
||||||
let reply = NostrReply::Ok(id, true, NostrReplyPrefix::None, "".to_string());
|
let reply = NostrReply::Ok(id, true, NostrReplyPrefix::None, "".to_string());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,7 +382,7 @@ impl WebSocketService {
|
|||||||
// message, and clients just presume it was closed.
|
// message, and clients just presume it was closed.
|
||||||
/*
|
/*
|
||||||
let reply = NostrReply::Closed(subid, NostrReplyPrefix::None, "".to_owned());
|
let reply = NostrReply::Closed(subid, NostrReplyPrefix::None, "".to_owned());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -416,11 +410,11 @@ impl WebSocketService {
|
|||||||
}
|
}
|
||||||
_ => NostrReply::Ok(id, false, NostrReplyPrefix::Error, format!("{}", e.inner)),
|
_ => NostrReply::Ok(id, false, NostrReplyPrefix::Error, format!("{}", e.inner)),
|
||||||
};
|
};
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
Err(e)
|
Err(e)
|
||||||
} else {
|
} else {
|
||||||
let reply = NostrReply::Ok(id, true, NostrReplyPrefix::None, "".to_string());
|
let reply = NostrReply::Ok(id, true, NostrReplyPrefix::None, "".to_string());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -499,7 +493,7 @@ impl WebSocketService {
|
|||||||
if !GLOBALS.config.read().enable_negentropy {
|
if !GLOBALS.config.read().enable_negentropy {
|
||||||
let reply =
|
let reply =
|
||||||
NostrReply::NegErr(&subid, "blocked: Negentropy sync is disabled".to_owned());
|
NostrReply::NegErr(&subid, "blocked: Negentropy sync is disabled".to_owned());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,14 +527,14 @@ impl WebSocketService {
|
|||||||
// NEG-ERR if the message was empty
|
// NEG-ERR if the message was empty
|
||||||
if incoming_msg.is_empty() {
|
if incoming_msg.is_empty() {
|
||||||
let reply = NostrReply::NegErr(&subid, "error: Empty negentropy message".to_owned());
|
let reply = NostrReply::NegErr(&subid, "error: Empty negentropy message".to_owned());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the version is too high, respond with our version number
|
// If the version is too high, respond with our version number
|
||||||
if incoming_msg[0] != 0x61 {
|
if incoming_msg[0] != 0x61 {
|
||||||
let reply = NostrReply::NegMsg(&subid, vec![0x61]);
|
let reply = NostrReply::NegMsg(&subid, vec![0x61]);
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,11 +588,11 @@ impl WebSocketService {
|
|||||||
match neg.reconcile(&Bytes::from(incoming_msg)) {
|
match neg.reconcile(&Bytes::from(incoming_msg)) {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
let reply = NostrReply::NegMsg(&subid, response.as_bytes().to_owned());
|
let reply = NostrReply::NegMsg(&subid, response.as_bytes().to_owned());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let reply = NostrReply::NegErr(&subid, format!("{e}"));
|
let reply = NostrReply::NegErr(&subid, format!("{e}"));
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,7 +628,7 @@ impl WebSocketService {
|
|||||||
if !GLOBALS.config.read().enable_negentropy {
|
if !GLOBALS.config.read().enable_negentropy {
|
||||||
let reply =
|
let reply =
|
||||||
NostrReply::NegErr(&subid, "blocked: Negentropy sync is disabled".to_owned());
|
NostrReply::NegErr(&subid, "blocked: Negentropy sync is disabled".to_owned());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,7 +650,7 @@ impl WebSocketService {
|
|||||||
// NEG-ERR if the message was empty
|
// NEG-ERR if the message was empty
|
||||||
if incoming_msg.is_empty() {
|
if incoming_msg.is_empty() {
|
||||||
let reply = NostrReply::NegErr(&subid, "error: Empty negentropy message".to_owned());
|
let reply = NostrReply::NegErr(&subid, "error: Empty negentropy message".to_owned());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,14 +658,14 @@ impl WebSocketService {
|
|||||||
// have already happened in NEG-OPEN)
|
// have already happened in NEG-OPEN)
|
||||||
if incoming_msg[0] != 0x61 {
|
if incoming_msg[0] != 0x61 {
|
||||||
let reply = NostrReply::NegErr(&subid, "Version mismatch".to_owned());
|
let reply = NostrReply::NegErr(&subid, "Version mismatch".to_owned());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up the events we have
|
// Look up the events we have
|
||||||
let Some(nsv) = self.neg_subscriptions.get(&subid) else {
|
let Some(nsv) = self.neg_subscriptions.get(&subid) else {
|
||||||
let reply = NostrReply::NegErr(&subid, "Subscription not found".to_owned());
|
let reply = NostrReply::NegErr(&subid, "Subscription not found".to_owned());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -679,11 +673,11 @@ impl WebSocketService {
|
|||||||
match neg.reconcile(&Bytes::from(incoming_msg)) {
|
match neg.reconcile(&Bytes::from(incoming_msg)) {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
let reply = NostrReply::NegMsg(&subid, response.as_bytes().to_owned());
|
let reply = NostrReply::NegMsg(&subid, response.as_bytes().to_owned());
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let reply = NostrReply::NegErr(&subid, format!("{e}"));
|
let reply = NostrReply::NegErr(&subid, format!("{e}"));
|
||||||
self.send(Message::text(reply.as_json()?)).await?;
|
self.send(Message::text(reply.as_json())).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
54
src/reply.rs
54
src/reply.rs
@ -1,4 +1,3 @@
|
|||||||
use crate::Error;
|
|
||||||
use pocket_types::{write_hex, Event, Hll8, Id};
|
use pocket_types::{write_hex, Event, Hll8, Id};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
@ -10,6 +9,7 @@ pub enum NostrReplyPrefix {
|
|||||||
Duplicate,
|
Duplicate,
|
||||||
Blocked,
|
Blocked,
|
||||||
RateLimited,
|
RateLimited,
|
||||||
|
Redacted,
|
||||||
Restricted,
|
Restricted,
|
||||||
Invalid,
|
Invalid,
|
||||||
Error,
|
Error,
|
||||||
@ -24,6 +24,7 @@ impl fmt::Display for NostrReplyPrefix {
|
|||||||
NostrReplyPrefix::Duplicate => write!(f, "duplicate: "),
|
NostrReplyPrefix::Duplicate => write!(f, "duplicate: "),
|
||||||
NostrReplyPrefix::Blocked => write!(f, "blocked: "),
|
NostrReplyPrefix::Blocked => write!(f, "blocked: "),
|
||||||
NostrReplyPrefix::RateLimited => write!(f, "rate-limited: "),
|
NostrReplyPrefix::RateLimited => write!(f, "rate-limited: "),
|
||||||
|
NostrReplyPrefix::Redacted => write!(f, "redacted: "),
|
||||||
NostrReplyPrefix::Restricted => write!(f, "restricted: "),
|
NostrReplyPrefix::Restricted => write!(f, "restricted: "),
|
||||||
NostrReplyPrefix::Invalid => write!(f, "invalid: "),
|
NostrReplyPrefix::Invalid => write!(f, "invalid: "),
|
||||||
NostrReplyPrefix::Error => write!(f, "error: "),
|
NostrReplyPrefix::Error => write!(f, "error: "),
|
||||||
@ -46,59 +47,40 @@ pub enum NostrReply<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NostrReply<'_> {
|
impl NostrReply<'_> {
|
||||||
pub fn as_json(&self) -> Result<String, Error> {
|
pub fn as_json(&self) -> String {
|
||||||
Ok(match self {
|
match self {
|
||||||
NostrReply::Auth(challenge) => {
|
NostrReply::Auth(challenge) => format!(r#"["AUTH","{challenge}"]"#),
|
||||||
let esc_challenge = escape(challenge)?;
|
NostrReply::Event(subid, event) => format!(r#"["EVENT","{subid}",{}]"#, event),
|
||||||
format!(r#"["AUTH","{esc_challenge}"]"#)
|
NostrReply::Ok(id, ok, prefix, msg) => format!(r#"["OK","{id}",{ok},"{prefix}{msg}"]"#),
|
||||||
}
|
NostrReply::Eose(subid) => format!(r#"["EOSE","{subid}"]"#),
|
||||||
NostrReply::Event(subid, event) => {
|
|
||||||
let esc_subid = escape(subid)?;
|
|
||||||
format!(r#"["EVENT","{esc_subid}",{event}]"#)
|
|
||||||
}
|
|
||||||
NostrReply::Ok(id, ok, prefix, msg) => {
|
|
||||||
let esc_msg = escape(msg)?;
|
|
||||||
format!(r#"["OK","{id}",{ok},"{prefix}{esc_msg}"]"#)
|
|
||||||
}
|
|
||||||
NostrReply::Eose(subid) => {
|
|
||||||
let esc_subid = escape(subid)?;
|
|
||||||
format!(r#"["EOSE","{esc_subid}"]"#)
|
|
||||||
}
|
|
||||||
NostrReply::Closed(subid, prefix, msg) => {
|
NostrReply::Closed(subid, prefix, msg) => {
|
||||||
format!(r#"["CLOSED","{subid}","{prefix}{msg}"]"#)
|
format!(r#"["CLOSED","{subid}","{prefix}{msg}"]"#)
|
||||||
}
|
}
|
||||||
NostrReply::Notice(msg) => {
|
NostrReply::Notice(msg) => format!(r#"["NOTICE","{msg}"]"#),
|
||||||
let esc_msg = escape(msg)?;
|
|
||||||
format!(r#"["NOTICE","{esc_msg}"]"#)
|
|
||||||
}
|
|
||||||
NostrReply::Count(subid, c, opthll) => {
|
NostrReply::Count(subid, c, opthll) => {
|
||||||
let esc_subid = escape(subid)?;
|
|
||||||
if let Some(hll) = opthll {
|
if let Some(hll) = opthll {
|
||||||
let hll = hll.to_hex_string();
|
let hll = hll.to_hex_string();
|
||||||
format!(r#"["COUNT","{esc_subid}",{{"count":{c}, "hll":"{hll}"}}]"#)
|
format!(r#"["COUNT","{subid}",{{"count":{c}, "hll":"{hll}"}}]"#)
|
||||||
} else {
|
} else {
|
||||||
format!(r#"["COUNT","{esc_subid}",{{"count":{c}}}]"#)
|
format!(r#"["COUNT","{subid}",{{"count":{c}}}]"#)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NostrReply::NegErr(subid, reason) => {
|
NostrReply::NegErr(subid, reason) => {
|
||||||
let esc_subid = escape(subid)?;
|
format!(r#"["NEG-ERR","{subid}","{reason}"]"#)
|
||||||
let esc_reason = escape(reason)?;
|
|
||||||
format!(r#"["NEG-ERR","{esc_subid}","{esc_reason}"]"#)
|
|
||||||
}
|
}
|
||||||
NostrReply::NegMsg(subid, msg) => {
|
NostrReply::NegMsg(subid, msg) => {
|
||||||
let esc_subid = escape(subid)?;
|
|
||||||
// write msg as hex
|
// write msg as hex
|
||||||
let mut buf: Vec<u8> = vec![0; msg.len() * 2];
|
let mut buf: Vec<u8> = vec![0; msg.len() * 2];
|
||||||
write_hex!(msg, &mut buf, msg.len()).unwrap();
|
write_hex!(msg, &mut buf, msg.len()).unwrap();
|
||||||
let msg_hex = unsafe { std::str::from_utf8_unchecked(&buf) };
|
let msg_hex = unsafe { std::str::from_utf8_unchecked(&buf) };
|
||||||
format!(r#"["NEG-MSG","{esc_subid}","{}"]"#, msg_hex)
|
format!(r#"["NEG-MSG","{subid}","{}"]"#, msg_hex)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn escape(s: &str) -> Result<String, Error> {
|
impl fmt::Display for NostrReply<'_> {
|
||||||
let v: Vec<u8> = Vec::with_capacity(256);
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let e = pocket_types::json::json_escape(s.as_bytes(), v)?;
|
write!(f, "{}", self.as_json())
|
||||||
Ok(unsafe { String::from_utf8_unchecked(e) })
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ fn respond(
|
|||||||
let s: String = serde_json::to_string(&json)?;
|
let s: String = serde_json::to_string(&json)?;
|
||||||
let response = Response::builder()
|
let response = Response::builder()
|
||||||
.header("Access-Control-Allow-Origin", "*")
|
.header("Access-Control-Allow-Origin", "*")
|
||||||
.header("Access-Control-Allow-Headers", "Authorization, *")
|
.header("Access-Control-Allow-Headers", "*")
|
||||||
.header("Access-Control-Allow-Methods", "*")
|
.header("Access-Control-Allow-Methods", "*")
|
||||||
.header("Content-Type", "application/nostr+json+rpc")
|
.header("Content-Type", "application/nostr+json+rpc")
|
||||||
.status(status)
|
.status(status)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user