mirror of
https://github.com/scsibug/nostr-rs-relay.git
synced 2025-05-05 09:09:57 -04:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d72af96d5f | ||
|
b4234eae25 | ||
|
d73cde2844 | ||
|
afbd7559e8 | ||
|
a6b48620fd | ||
|
d71f5cb029 | ||
|
1ed8cc08cc |
1302
Cargo.lock
generated
1302
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@ mirrored on [GitHub](https://github.com/scsibug/nostr-rs-relay).
|
||||
|
||||
[](https://builds.sr.ht/~gheartsfield/nostr-rs-relay/commits/master?)
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## Features
|
||||
@ -158,10 +158,6 @@ Proxy](docs/reverse-proxy.md).
|
||||
|
||||
For development discussions, please feel free to use the [sourcehut
|
||||
mailing list](https://lists.sr.ht/~gheartsfield/nostr-rs-relay-devel).
|
||||
Or, drop by the [Nostr Telegram Channel](https://t.me/nostr_protocol).
|
||||
|
||||
To chat about `nostr-rs-relay` on `nostr` itself; visit our channel on [anigma](https://anigma.io/) or another client that supports [NIP-28](https://github.com/nostr-protocol/nips/blob/master/28.md) chats:
|
||||
* `2ad246a094fee48c6e455dd13d759d5f41b5a233120f5719d81ebc1935075194`
|
||||
|
||||
License
|
||||
---
|
||||
|
@ -15,7 +15,6 @@ use tracing::info;
|
||||
/// Bulk load JSONL data from STDIN to the database specified in config.toml (or ./nostr.db as a default).
|
||||
/// The database must already exist, this will not create a new one.
|
||||
/// Tested against schema v13.
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
let _trace_sub = tracing_subscriber::fmt::try_init();
|
||||
println!("Nostr-rs-relay Bulk Loader");
|
||||
|
18
src/db.rs
18
src/db.rs
@ -281,15 +281,6 @@ pub async fn db_writer(
|
||||
}
|
||||
}
|
||||
|
||||
// send any metadata events to the NIP-05 verifier
|
||||
if nip05_active && event.is_kind_metadata() {
|
||||
// we are sending this prior to even deciding if we
|
||||
// persist it. this allows the nip05 module to
|
||||
// inspect it, update if necessary, or persist a new
|
||||
// event and broadcast it itself.
|
||||
metadata_tx.send(event.clone()).ok();
|
||||
}
|
||||
|
||||
// get a validation result for use in verification and GPRC
|
||||
let validation = if nip05_active {
|
||||
Some(repo.get_latest_user_verification(&event.pubkey).await)
|
||||
@ -390,6 +381,15 @@ pub async fn db_writer(
|
||||
}
|
||||
}
|
||||
|
||||
// send any metadata events to the NIP-05 verifier
|
||||
if nip05_active && event.is_kind_metadata() {
|
||||
// we are sending this prior to even deciding if we
|
||||
// persist it. this allows the nip05 module to
|
||||
// inspect it, update if necessary, or persist a new
|
||||
// event and broadcast it itself.
|
||||
metadata_tx.send(event.clone()).ok();
|
||||
}
|
||||
|
||||
// TODO: cache recent list of authors to remove a DB call.
|
||||
let start = Instant::now();
|
||||
if event.is_ephemeral() {
|
||||
|
@ -472,12 +472,8 @@ mod tests {
|
||||
let mut event = Event::simple_event();
|
||||
event.tags = vec![vec!["e".to_owned(), "foo".to_owned()]];
|
||||
event.build_index();
|
||||
assert!(
|
||||
event.generic_tag_val_intersect(
|
||||
'e',
|
||||
&HashSet::from(["foo".to_owned(), "bar".to_owned()])
|
||||
)
|
||||
);
|
||||
assert!(event
|
||||
.generic_tag_val_intersect('e', &HashSet::from(["foo".to_owned(), "bar".to_owned()])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -121,7 +121,7 @@ fn body_contains_user(username: &str, address: &str, bytes: &hyper::body::Bytes)
|
||||
// get the pubkey for the requested user
|
||||
let check_name = names_map.get(username).and_then(serde_json::Value::as_str);
|
||||
// ensure the address is a match
|
||||
Ok(check_name.map_or(false, |x| x == address))
|
||||
Ok(check_name == Some(address))
|
||||
}
|
||||
|
||||
impl Verifier {
|
||||
@ -243,7 +243,11 @@ impl Verifier {
|
||||
let response_content_length = match response.body().size_hint().upper() {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
info!("missing content length header for account {:?} at URL: {}", nip.to_string(), url);
|
||||
info!(
|
||||
"missing content length header for account {:?} at URL: {}",
|
||||
nip.to_string(),
|
||||
url
|
||||
);
|
||||
return Ok(UserWebVerificationStatus::Unknown);
|
||||
}
|
||||
};
|
||||
|
@ -25,7 +25,9 @@ impl EventResultStatus {
|
||||
pub fn to_bool(&self) -> bool {
|
||||
match self {
|
||||
Self::Duplicate | Self::Saved => true,
|
||||
Self::Invalid | Self::Blocked | Self::RateLimited | Self::Error | Self::Restricted => false,
|
||||
Self::Invalid | Self::Blocked | Self::RateLimited | Self::Error | Self::Restricted => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ impl ClnRestPaymentProcessor {
|
||||
.ok_or(ConfigError::NotFound("rune_path".to_string()))?;
|
||||
let rune = String::from_utf8(fs::read(rune_path)?)
|
||||
.map_err(|_| ConfigError::Message("Rune should be UTF8".to_string()))?;
|
||||
let mut rune_header = HeaderValue::from_str(&rune.trim())
|
||||
let mut rune_header = HeaderValue::from_str(rune.trim())
|
||||
.map_err(|_| ConfigError::Message("Invalid Rune header".to_string()))?;
|
||||
rune_header.set_sensitive(true);
|
||||
|
||||
|
@ -55,12 +55,12 @@ pub enum InvoiceStatus {
|
||||
Expired,
|
||||
}
|
||||
|
||||
impl ToString for InvoiceStatus {
|
||||
fn to_string(&self) -> String {
|
||||
impl std::fmt::Display for InvoiceStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
InvoiceStatus::Paid => "Paid".to_string(),
|
||||
InvoiceStatus::Unpaid => "Unpaid".to_string(),
|
||||
InvoiceStatus::Expired => "Expired".to_string(),
|
||||
InvoiceStatus::Paid => write!(f, "Paid"),
|
||||
InvoiceStatus::Unpaid => write!(f, "Unpaid"),
|
||||
InvoiceStatus::Expired => write!(f, "Expired"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,28 +177,25 @@ ON CONFLICT (id) DO NOTHING"#,
|
||||
let tag_val = &tag[1];
|
||||
// only single-char tags are searchable
|
||||
let tag_char_opt = single_char_tagname(tag_name);
|
||||
match &tag_char_opt {
|
||||
Some(_) => {
|
||||
// if tag value is lowercase hex;
|
||||
if is_lower_hex(tag_val) && (tag_val.len() % 2 == 0) {
|
||||
sqlx::query("INSERT INTO tag (event_id, \"name\", value, value_hex) VALUES($1, $2, NULL, $3) \
|
||||
ON CONFLICT (event_id, \"name\", value, value_hex) DO NOTHING")
|
||||
if tag_char_opt.is_some() {
|
||||
// if tag value is lowercase hex;
|
||||
if is_lower_hex(tag_val) && (tag_val.len() % 2 == 0) {
|
||||
sqlx::query("INSERT INTO tag (event_id, \"name\", value, value_hex) VALUES($1, $2, NULL, $3) \
|
||||
ON CONFLICT (event_id, \"name\", value, value_hex) DO NOTHING")
|
||||
.bind(&id_blob)
|
||||
.bind(tag_name)
|
||||
.bind(hex::decode(tag_val).ok())
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
} else {
|
||||
sqlx::query("INSERT INTO tag (event_id, \"name\", value, value_hex) VALUES($1, $2, $3, NULL) \
|
||||
ON CONFLICT (event_id, \"name\", value, value_hex) DO NOTHING")
|
||||
.bind(tag_name)
|
||||
.bind(hex::decode(tag_val).ok())
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
} else {
|
||||
sqlx::query("INSERT INTO tag (event_id, \"name\", value, value_hex) VALUES($1, $2, $3, NULL) \
|
||||
ON CONFLICT (event_id, \"name\", value, value_hex) DO NOTHING")
|
||||
.bind(&id_blob)
|
||||
.bind(tag_name)
|
||||
.bind(tag_val.as_bytes())
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
}
|
||||
.bind(tag_name)
|
||||
.bind(tag_val.as_bytes())
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,14 +156,11 @@ impl SqliteRepo {
|
||||
let tagval = &tag[1];
|
||||
// only single-char tags are searchable
|
||||
let tagchar_opt = single_char_tagname(tagname);
|
||||
match &tagchar_opt {
|
||||
Some(_) => {
|
||||
tx.execute(
|
||||
"INSERT OR IGNORE INTO tag (event_id, name, value, kind, created_at) VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||
params![ev_id, &tagname, &tagval, e.kind, e.created_at],
|
||||
)?;
|
||||
}
|
||||
None => {}
|
||||
if tagchar_opt.is_some() {
|
||||
tx.execute(
|
||||
"INSERT OR IGNORE INTO tag (event_id, name, value, kind, created_at) VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||
params![ev_id, &tagname, &tagval, e.kind, e.created_at],
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ PRAGMA user_version = {};
|
||||
-- Event Table
|
||||
CREATE TABLE IF NOT EXISTS event (
|
||||
id INTEGER PRIMARY KEY,
|
||||
event_hash BLOB NOT NULL, -- 4-byte hash
|
||||
event_hash BLOB NOT NULL, -- 32-byte SHA256 hash
|
||||
first_seen INTEGER NOT NULL, -- when the event was first seen (not authored!) (seconds since 1970)
|
||||
created_at INTEGER NOT NULL, -- when the event was authored
|
||||
expires_at INTEGER, -- when the event expires and may be deleted
|
||||
|
@ -202,7 +202,7 @@ async fn handle_web_request(
|
||||
.header("Content-Type", "text/html; charset=UTF-8")
|
||||
.body(Body::from(file_content))
|
||||
.expect("request builder"));
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to read relay_page file: {}. Will use default", err);
|
||||
}
|
||||
@ -920,7 +920,7 @@ pub fn start_server(settings: &Settings, shutdown_rx: MpscReceiver<()>) -> Resul
|
||||
info!("starting payment process ...");
|
||||
p.run().await;
|
||||
});
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to start payment process {e}");
|
||||
std::process::exit(1);
|
||||
|
@ -45,8 +45,8 @@ pub struct ReqFilter {
|
||||
|
||||
impl Serialize for ReqFilter {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
if let Some(ids) = &self.ids {
|
||||
@ -80,8 +80,8 @@ impl Serialize for ReqFilter {
|
||||
|
||||
impl<'de> Deserialize<'de> for ReqFilter {
|
||||
fn deserialize<D>(deserializer: D) -> Result<ReqFilter, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let received: Value = Deserialize::deserialize(deserializer)?;
|
||||
let filter = received.as_object().ok_or_else(|| {
|
||||
@ -184,8 +184,8 @@ impl<'de> Deserialize<'de> for Subscription {
|
||||
/// Custom deserializer for subscriptions, which have a more
|
||||
/// complex structure than the other message types.
|
||||
fn deserialize<D>(deserializer: D) -> Result<Subscription, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let mut v: Value = Deserialize::deserialize(deserializer)?;
|
||||
// this should be a 3-or-more element array.
|
||||
@ -673,11 +673,26 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn is_scraper() -> Result<()> {
|
||||
assert!(serde_json::from_str::<Subscription>(r#"["REQ","some-id",{"kinds": [1984],"since": 123,"limit":1}]"#)?.is_scraper());
|
||||
assert!(serde_json::from_str::<Subscription>(r#"["REQ","some-id",{"kinds": [1984]},{"kinds": [1984],"authors":["aaaa"]}]"#)?.is_scraper());
|
||||
assert!(!serde_json::from_str::<Subscription>(r#"["REQ","some-id",{"kinds": [1984],"authors":["aaaa"]}]"#)?.is_scraper());
|
||||
assert!(!serde_json::from_str::<Subscription>(r#"["REQ","some-id",{"ids": ["aaaa"]}]"#)?.is_scraper());
|
||||
assert!(!serde_json::from_str::<Subscription>(r##"["REQ","some-id",{"#p": ["aaaa"],"kinds":[1,4]}]"##)?.is_scraper());
|
||||
assert!(serde_json::from_str::<Subscription>(
|
||||
r#"["REQ","some-id",{"kinds": [1984],"since": 123,"limit":1}]"#
|
||||
)?
|
||||
.is_scraper());
|
||||
assert!(serde_json::from_str::<Subscription>(
|
||||
r#"["REQ","some-id",{"kinds": [1984]},{"kinds": [1984],"authors":["aaaa"]}]"#
|
||||
)?
|
||||
.is_scraper());
|
||||
assert!(!serde_json::from_str::<Subscription>(
|
||||
r#"["REQ","some-id",{"kinds": [1984],"authors":["aaaa"]}]"#
|
||||
)?
|
||||
.is_scraper());
|
||||
assert!(
|
||||
!serde_json::from_str::<Subscription>(r#"["REQ","some-id",{"ids": ["aaaa"]}]"#)?
|
||||
.is_scraper()
|
||||
);
|
||||
assert!(!serde_json::from_str::<Subscription>(
|
||||
r##"["REQ","some-id",{"#p": ["aaaa"],"kinds":[1,4]}]"##
|
||||
)?
|
||||
.is_scraper());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user