Compare commits

..

10 Commits

Author SHA1 Message Date
458b5bb58a
Skip empty batch
All checks were successful
continuous-integration/drone Build is passing
2024-09-05 10:46:47 +01:00
52ec0d7b5a
Print filters json 2024-09-05 10:40:18 +01:00
ae107978f7
Batch requests 2024-09-05 10:29:30 +01:00
d828a4b0f0
Demand replaceable events 2024-08-19 19:51:18 +01:00
be3d2ed478
Config listen address 2024-08-19 15:42:03 +01:00
add69fecfa
Config setup 2024-08-16 23:06:56 +01:00
ac91e30db7
Basic fetch 2024-08-16 22:55:46 +01:00
70e0d70d8b
accept NIP19 2024-08-08 22:28:46 +01:00
45a3004128
clean using 2024-08-08 22:14:05 +01:00
64375c23c5
Get replaceable events 2024-08-08 22:13:28 +01:00
9 changed files with 718 additions and 43 deletions

435
Cargo.lock generated
View File

@ -104,6 +104,38 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "async-utility"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a349201d80b4aa18d17a34a182bdd7f8ddf845e9e57d2ea130a12e10ef1e3a47"
dependencies = [
"futures-util",
"gloo-timers",
"tokio",
"wasm-bindgen-futures",
]
[[package]]
name = "async-wsocket"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5725a0615e4eb98e82e9cb963529398114e3fccfbf0e8b9111d605e2ac443e46"
dependencies = [
"async-utility",
"futures",
"futures-util",
"js-sys",
"thiserror",
"tokio",
"tokio-rustls",
"tokio-socks",
"tokio-tungstenite",
"url",
"wasm-bindgen",
"web-sys",
]
[[package]] [[package]]
name = "atomic" name = "atomic"
version = "0.5.3" version = "0.5.3"
@ -119,6 +151,15 @@ dependencies = [
"bytemuck", "bytemuck",
] ]
[[package]]
name = "atomic-destructor"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d919cb60ba95c87ba42777e9e246c4e8d658057299b437b7512531ce0a09a23"
dependencies = [
"tracing",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.3.0" version = "1.3.0"
@ -233,6 +274,9 @@ name = "bitflags"
version = "2.6.0" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
@ -332,6 +376,55 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "config"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
dependencies = [
"async-trait",
"convert_case",
"json5",
"lazy_static",
"nom",
"pathdiff",
"ron",
"rust-ini",
"serde",
"serde_json",
"toml",
"yaml-rust",
]
[[package]]
name = "const-random"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
dependencies = [
"const-random-macro",
]
[[package]]
name = "const-random-macro"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
dependencies = [
"getrandom",
"once_cell",
"tiny-keccak",
]
[[package]]
name = "convert_case"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
dependencies = [
"unicode-segmentation",
]
[[package]] [[package]]
name = "cookie" name = "cookie"
version = "0.18.1" version = "0.18.1"
@ -376,6 +469,12 @@ version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -387,6 +486,12 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "data-encoding"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.3.11" version = "0.3.11"
@ -440,6 +545,15 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "dlv-list"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
dependencies = [
"const-random",
]
[[package]] [[package]]
name = "either" name = "either"
version = "1.13.0" version = "1.13.0"
@ -661,6 +775,18 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "gloo-timers"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.26" version = "0.3.26"
@ -680,6 +806,12 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.5" version = "0.14.5"
@ -889,7 +1021,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown 0.14.5",
"serde", "serde",
] ]
@ -953,6 +1085,17 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "json5"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
dependencies = [
"pest",
"pest_derive",
"serde",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.5.0" version = "1.5.0"
@ -965,12 +1108,30 @@ version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.14" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lnurl-pay"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02c042191c2e3f27147decfad8182eea2c7dd1c6c1733562e25d3d401369669d"
dependencies = [
"bech32",
"reqwest",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.12" version = "0.4.12"
@ -1008,7 +1169,7 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
dependencies = [ dependencies = [
"hashbrown", "hashbrown 0.14.5",
] ]
[[package]] [[package]]
@ -1032,6 +1193,12 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.7.4" version = "0.7.4"
@ -1078,10 +1245,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e664971378a3987224f7a0e10059782035e89899ae403718ee07de85bec42afe" checksum = "e664971378a3987224f7a0e10059782035e89899ae403718ee07de85bec42afe"
[[package]] [[package]]
name = "nostr" name = "nom"
version = "0.33.0" version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f08db214560a34bf7c4c1fea09a8461b9412bae58ba06e99ce3177d89fa1e0a6" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "nostr"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5897e4142fcc33c4f1d58ad17f665e87dcba70de7e370c0bda1aa0fb73212c2a"
dependencies = [ dependencies = [
"aes", "aes",
"base64 0.21.7", "base64 0.21.7",
@ -1109,9 +1286,9 @@ dependencies = [
[[package]] [[package]]
name = "nostr-database" name = "nostr-database"
version = "0.33.0" version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9f6c72d0d0842de637f7fba6e70764f719257d29dad8fc5f7352810b0f117ad" checksum = "1926ef55392f3eea1bbe4a1358b64bbf12dd6eb554f40f483941a102c6263fc6"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"flatbuffers", "flatbuffers",
@ -1122,14 +1299,78 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "nostr-relay-pool"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6480cf60564957a2a64bd050d047ee0717e08dced7a389e22ef4e9fc104edd2"
dependencies = [
"async-utility",
"async-wsocket",
"atomic-destructor",
"nostr",
"nostr-database",
"thiserror",
"tokio",
"tokio-stream",
"tracing",
]
[[package]]
name = "nostr-sdk"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca0c0c5f8ddbdfc064ea71883191ec53de6ed52b5dca10ab07f0810b99e91acc"
dependencies = [
"async-utility",
"atomic-destructor",
"lnurl-pay",
"nostr",
"nostr-database",
"nostr-relay-pool",
"nostr-signer",
"nostr-zapper",
"nwc",
"thiserror",
"tokio",
"tracing",
]
[[package]]
name = "nostr-signer"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c30294a7be7d9d5ac777954812f5c7b4ae2a1e583a62e33537f87d98ab23729"
dependencies = [
"async-utility",
"nostr",
"nostr-relay-pool",
"thiserror",
"tokio",
"tracing",
]
[[package]]
name = "nostr-zapper"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf3ba30e807145e9cb924faf8fb0719e460f613088e99c753b67c2a9929c5b7"
dependencies = [
"async-trait",
"nostr",
"thiserror",
]
[[package]] [[package]]
name = "nostr_services_rs" name = "nostr_services_rs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"config",
"log", "log",
"nostr", "nostr",
"nostr-database", "nostr-database",
"nostr-sdk",
"pretty_env_logger", "pretty_env_logger",
"rocket", "rocket",
"serde", "serde",
@ -1163,6 +1404,20 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "nwc"
version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a16ac06bc273fcd4ead47c0c5a58b6cc7db2247fc7a64dd9bc11cf18e3efeb4"
dependencies = [
"async-utility",
"nostr",
"nostr-relay-pool",
"nostr-zapper",
"thiserror",
"tracing",
]
[[package]] [[package]]
name = "object" name = "object"
version = "0.36.1" version = "0.36.1"
@ -1184,6 +1439,16 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "ordered-multimap"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
dependencies = [
"dlv-list",
"hashbrown 0.13.2",
]
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -1249,6 +1514,12 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "pathdiff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]] [[package]]
name = "pbkdf2" name = "pbkdf2"
version = "0.12.2" version = "0.12.2"
@ -1288,6 +1559,51 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pest"
version = "2.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95"
dependencies = [
"memchr",
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f"
dependencies = [
"once_cell",
"pest",
"sha2",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.5" version = "1.1.5"
@ -1683,6 +1999,28 @@ dependencies = [
"uncased", "uncased",
] ]
[[package]]
name = "ron"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
dependencies = [
"base64 0.21.7",
"bitflags 2.6.0",
"serde",
"serde_derive",
]
[[package]]
name = "rust-ini"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
dependencies = [
"cfg-if",
"ordered-multimap",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.24" version = "0.1.24"
@ -1883,6 +2221,17 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.8" version = "0.10.8"
@ -2082,6 +2431,15 @@ dependencies = [
"time-core", "time-core",
] ]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.8.0" version = "1.8.0"
@ -2160,6 +2518,22 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tokio-tungstenite"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd"
dependencies = [
"futures-util",
"log",
"rustls",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tungstenite",
"webpki-roots",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.11" version = "0.7.11"
@ -2301,6 +2675,26 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "tungstenite"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8"
dependencies = [
"byteorder",
"bytes",
"data-encoding",
"http 1.1.0",
"httparse",
"log",
"rand",
"rustls",
"rustls-pki-types",
"sha1",
"thiserror",
"utf-8",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"
@ -2316,6 +2710,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "ucd-trie"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]] [[package]]
name = "uncased" name = "uncased"
version = "0.9.10" version = "0.9.10"
@ -2347,6 +2747,12 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.4" version = "0.2.4"
@ -2381,6 +2787,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]] [[package]]
name = "valuable" name = "valuable"
version = "0.1.0" version = "0.1.0"
@ -2691,6 +3103,15 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]] [[package]]
name = "yansi" name = "yansi"
version = "1.0.1" version = "1.0.1"

View File

@ -5,11 +5,13 @@ edition = "2021"
[dependencies] [dependencies]
tokio = { version = "1.38.1", features = ["rt", "rt-multi-thread", "macros"] } tokio = { version = "1.38.1", features = ["rt", "rt-multi-thread", "macros"] }
nostr = "0.33.0" nostr = { version = "0.34.0" }
nostr-sdk = { version = "0.34.0" }
rocket = { version = "0.5.1", features = ["json"] } rocket = { version = "0.5.1", features = ["json"] }
serde = { version = "1.0.204", features = ["derive"] } serde = { version = "1.0.204", features = ["derive"] }
log = "0.4.22" log = "0.4.22"
pretty_env_logger = "0.5.0" pretty_env_logger = "0.5.0"
anyhow = "1.0.86" anyhow = "1.0.86"
nostr-database = { version = "0.33.0", features = ["flatbuf"] } nostr-database = { version = "0.34.0", features = ["flatbuf"] }
sled = "0.34.7" sled = "0.34.7"
config = { version = "0.14.0", features = ["toml"] }

14
README.md Normal file
View File

@ -0,0 +1,14 @@
# nostr-services-rs
Simple API for serving nostr applications
## API
### `POST /event`
Save event in database
### `GET /event/<event-id>`
Get event by ID as `hex/nevent/naddr`
### `GET /event/<kind>/<pubkey>`
Get regular replaceable event for pubkey (non-parameterized)

7
config.toml Normal file
View File

@ -0,0 +1,7 @@
# List of relays to connect to for fetching data
relays = [
"wss://relay.snort.social",
"wss://relay.damus.io",
"wss://nos.lol"
]

View File

@ -1,27 +1,25 @@
use anyhow::Error; use nostr::{Event, EventId, FromBech32, Kind, PublicKey};
use nostr::{Event, EventId, JsonUtil, Kind}; use nostr::prelude::{Nip19, Nip19Event};
use nostr_database::{DatabaseError, DynNostrDatabase, NostrDatabase}; use rocket::{Route, State};
use rocket::{Data, Route, State};
use rocket::http::Status; use rocket::http::Status;
use rocket::serde::json::Json; use rocket::serde::json::Json;
use crate::fetch::FetchQueue;
use crate::store::SledDatabase; use crate::store::SledDatabase;
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![import_event, get_event] routes![import_event, get_event, get_event_by_kind]
} }
#[rocket::post("/event", data = "<data>")] #[rocket::post("/event", data = "<data>")]
async fn import_event( async fn import_event(db: &State<SledDatabase>, data: Json<Event>) -> Status {
db: &State<SledDatabase>,
data: Json<Event>,
) -> Status {
if data.verify().is_err() { if data.verify().is_err() {
return Status::InternalServerError; return Status::InternalServerError;
} }
if let Ok(v) = db.save_event(&data).await { if let Ok(v) = db.save_event(&data).await {
match v { match v {
true => Status::Ok, true => Status::Ok,
false => Status::Conflict false => Status::Conflict,
} }
} else { } else {
Status::InternalServerError Status::InternalServerError
@ -31,14 +29,62 @@ async fn import_event(
#[rocket::get("/event/<id>")] #[rocket::get("/event/<id>")]
async fn get_event( async fn get_event(
db: &State<SledDatabase>, db: &State<SledDatabase>,
fetch: &State<FetchQueue>,
id: &str, id: &str,
) -> Option<Json<Event>> { ) -> Option<Json<Event>> {
let id = match EventId::parse(id) { let id = match Nip19::from_bech32(id) {
Ok(i) => i, Ok(i) => i,
_ => return None _ => return None,
}; };
match db.event_by_id(id).await { match db.event_by_id(&id) {
Ok(ev) => Some(Json::from(ev)), Ok(ev) => Some(Json::from(ev)),
_ => None _ => {
let mut fetch = fetch.inner().clone();
match fetch.demand(&id).await.await {
Ok(Some(ev)) => {
if let Err(e) = db.save_event(&ev).await {
warn!("Failed to save event {}", e);
}
Some(Json::from(ev))
}
_ => None,
}
}
}
}
#[rocket::get("/event/<kind>/<pubkey>")]
async fn get_event_by_kind(
db: &State<SledDatabase>,
fetch: &State<FetchQueue>,
kind: u32, pubkey: &str,
) -> Option<Json<Event>> {
let pk = match PublicKey::parse(pubkey) {
Ok(i) => i,
_ => return None,
};
let kind = Kind::from(kind as u16);
if !kind.is_replaceable() {
return None;
}
match db.event_by_kind_pubkey(kind.as_u32(), &pk) {
Ok(ev) => Some(Json::from(ev)),
_ => {
let mut fetch = fetch.inner().clone();
match fetch.demand(&Nip19::Event(Nip19Event {
event_id: EventId::all_zeros(),
kind: Some(kind),
author: Some(pk),
relays: vec![],
})).await.await {
Ok(Some(ev)) => {
if let Err(e) = db.save_event(&ev).await {
warn!("Failed to save event {}", e);
}
Some(Json::from(ev))
}
_ => None
}
}
} }
} }

98
src/fetch.rs Normal file
View File

@ -0,0 +1,98 @@
use std::collections::VecDeque;
use std::sync::Arc;
use std::time::Duration;
use nostr::prelude::Nip19;
use nostr::{serde_json, Event, EventId, Filter, Kind, Url};
use nostr_sdk::{FilterOptions, RelayOptions, RelayPool};
use tokio::sync::{oneshot, Mutex};
struct QueueItem {
pub handler: oneshot::Sender<Option<Event>>,
pub request: Nip19,
}
#[derive(Clone)]
pub struct FetchQueue {
queue: Arc<Mutex<VecDeque<QueueItem>>>,
pool: Arc<Mutex<RelayPool>>,
}
impl FetchQueue {
pub fn new() -> Self {
Self {
queue: Arc::new(Mutex::new(VecDeque::new())),
pool: Default::default(),
}
}
pub async fn add_relay(&mut self, relay: Url) -> Result<bool, anyhow::Error> {
let pool_lock = self.pool.lock().await;
pool_lock
.add_relay(relay.clone(), RelayOptions::default())
.await?;
if let Err(e) = pool_lock.connect_relay(relay, None).await {
Err(anyhow::Error::new(e))
} else {
Ok(true)
}
}
pub async fn demand(&mut self, ent: &Nip19) -> oneshot::Receiver<Option<Event>> {
let (tx, rx) = oneshot::channel();
let mut q_lock = self.queue.lock().await;
q_lock.push_back(QueueItem {
handler: tx,
request: ent.clone(),
});
rx
}
pub async fn process_queue(&self) {
let mut q_lock = self.queue.lock().await;
let mut batch = Vec::new();
while let Some(q) = q_lock.pop_front() {
batch.push(q);
}
if batch.len() > 0 {
let filters: Vec<Filter> =
batch.iter().map(move |x| Self::nip19_to_filter(&x.request).unwrap()).collect();
let pool_lock = self.pool.lock().await;
info!("Sending filters: {}", serde_json::to_string(&filters).unwrap());
if let Ok(evs) = pool_lock
.get_events_of(filters, Duration::from_secs(2), FilterOptions::ExitOnEOSE)
.await
{
for b in batch {
let f = Self::nip19_to_filter(&b.request).unwrap();
let ev = evs.iter().find(|e| f.match_event(e));
b.handler.send(ev.cloned()).unwrap()
}
}
}
}
fn nip19_to_filter(filter: &Nip19) -> Option<Filter> {
match filter {
Nip19::Coordinate(c) => Some(Filter::from(c)),
Nip19::Event(e) => {
let mut f = Filter::new();
if e.event_id.ne(&EventId::all_zeros()) {
f = f.id(e.event_id);
}
if let Some(a) = e.author {
f = f.author(a);
}
if let Some(k) = e.kind {
f = f.kind(k);
}
Some(f)
}
Nip19::EventId(e) => Some(Filter::new().id(*e)),
Nip19::Pubkey(pk) => Some(Filter::new().author(*pk).kind(Kind::Metadata)),
_ => None,
}
}
}

View File

@ -1,24 +1,61 @@
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
use std::net::{IpAddr, SocketAddr};
use std::time::Duration;
use anyhow::Error; use anyhow::Error;
use nostr::Event; use config::Config;
use nostr_database::{DynNostrDatabase, NostrDatabase};
use rocket::shield::Shield; use rocket::shield::Shield;
use crate::fetch::FetchQueue;
use crate::settings::Settings;
use crate::store::SledDatabase; use crate::store::SledDatabase;
mod events; mod events;
mod fetch;
mod store; mod store;
mod settings;
#[rocket::main] #[rocket::main]
async fn main() -> Result<(), Error> { async fn main() -> Result<(), Error> {
pretty_env_logger::init(); pretty_env_logger::init();
let builder = Config::builder()
.add_source(config::File::with_name("config.toml"))
.add_source(config::Environment::with_prefix("APP"))
.build()?;
let settings: Settings = builder.try_deserialize()?;
let db = SledDatabase::new("nostr.db"); let db = SledDatabase::new("nostr.db");
let rocket = rocket::Rocket::build() let mut fetch = FetchQueue::new();
for x in settings.relays
{
fetch.add_relay(x).await.unwrap();
}
let fetch2 = fetch.clone();
tokio::spawn(async move {
loop {
fetch2.process_queue().await;
tokio::time::sleep(Duration::from_millis(100)).await;
}
});
let mut config = rocket::Config::default();
let ip: SocketAddr = match &settings.listen {
Some(i) => i.parse().unwrap(),
None => SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 8000)
};
config.address = ip.ip();
config.port = ip.port();
let rocket = rocket::Rocket::custom(config)
.manage(db) .manage(db)
.manage(fetch)
.attach(Shield::new()) // disable .attach(Shield::new()) // disable
.mount("/", events::routes()) .mount("/", events::routes())
.launch() .launch()
@ -30,4 +67,4 @@ async fn main() -> Result<(), Error> {
} else { } else {
Ok(()) Ok(())
} }
} }

11
src/settings.rs Normal file
View File

@ -0,0 +1,11 @@
use nostr_sdk::Url;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Settings {
/// Listen address for web API
pub listen: Option<String>,
/// List of relays to connect to for fetching data
pub relays: Vec<Url>
}

View File

@ -1,7 +1,9 @@
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
use nostr::{Event, EventId}; use nostr::prelude::Nip19;
use nostr::util::hex;
use nostr::{Event, EventId, PublicKey};
use nostr_database::{FlatBufferBuilder, FlatBufferDecode, FlatBufferEncode}; use nostr_database::{FlatBufferBuilder, FlatBufferDecode, FlatBufferEncode};
use sled::{Db, IVec}; use sled::{Db, IVec};
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -34,31 +36,48 @@ impl SledDatabase {
} }
fn write_replaceable_index(&self, event: &Event) -> Result<bool, anyhow::Error> { fn write_replaceable_index(&self, event: &Event) -> Result<bool, anyhow::Error> {
let rpk = Self::replaceable_index_key(event); let rpk = Self::replaceable_index_key_of_event(event);
if let Err(e) = self.db.update_and_fetch(rpk, |prev| { match self.db.update_and_fetch(rpk, |prev| {
if let Some(prev) = prev { if let Some(prev) = prev {
let timestamp: u64 = u64::from_be_bytes(prev[..8].try_into().unwrap()); let timestamp: u64 = u64::from_be_bytes(prev[..8].try_into().unwrap());
if timestamp < event.created_at.as_u64() { if timestamp < event.created_at.as_u64() {
let new_val = Self::replaceable_index_value(event); let new_val = Self::replaceable_index_value(event);
Some(IVec::from(new_val.as_slice())) Some(IVec::from(new_val.as_slice()))
} else { } else {
None Some(IVec::from(prev))
} }
} else { } else {
let new_val = Self::replaceable_index_value(event); let new_val = Self::replaceable_index_value(event);
Some(IVec::from(new_val.as_slice())) Some(IVec::from(new_val.as_slice()))
} }
}) { }) {
return Err(anyhow::Error::new(e)); Err(e) => Err(anyhow::Error::new(e)),
Ok(v) => match v {
Some(v) => {
info!(
"Wrote index {} = {}",
hex::encode(rpk),
hex::encode(v.as_ref())
);
Ok(true)
}
None => {
info!("Duplicate or older index {}", hex::encode(rpk));
Ok(false)
}
},
} }
Ok(false)
} }
fn replaceable_index_key(event: &Event) -> [u8; 36] { fn replaceable_index_key_of_event(event: &Event) -> [u8; 36] {
Self::replaceable_index_key(event.kind.as_u32(), &event.pubkey)
}
fn replaceable_index_key(kind: u32, pubkey: &PublicKey) -> [u8; 36] {
let mut rpk = [0; 4 + 32]; // kind:pubkey let mut rpk = [0; 4 + 32]; // kind:pubkey
rpk[..4].copy_from_slice(&event.kind.as_u32().to_be_bytes()); rpk[..4].copy_from_slice(&kind.to_be_bytes());
rpk[4..].copy_from_slice(&event.pubkey.to_bytes()); rpk[4..].copy_from_slice(&pubkey.to_bytes());
rpk rpk
} }
@ -69,16 +88,36 @@ impl SledDatabase {
new_val new_val
} }
pub async fn event_by_id(&self, event_id: EventId) -> Result<Event, anyhow::Error> { pub fn event_by_id(&self, event_id: &Nip19) -> Result<Event, anyhow::Error> {
match self.db.get(event_id.as_bytes()) { let id_key = match event_id {
Nip19::EventId(e) => e.as_bytes(),
Nip19::Event(e) => e.event_id.as_bytes(),
_ => return Err(anyhow::Error::msg("Not supported ID format")),
};
match self.db.get(id_key) {
Ok(v) => match v { Ok(v) => match v {
Some(v) => match Event::decode(&v) { Some(v) => match Event::decode(&v) {
Ok(v) => Ok(v), Ok(v) => Ok(v),
Err(e) => Err(anyhow::Error::new(e)) Err(e) => Err(anyhow::Error::new(e)),
}, },
None => Err(anyhow::Error::msg("Not Found")) None => Err(anyhow::Error::msg("Not Found")),
} },
Err(e) => Err(anyhow::Error::new(e)) Err(e) => Err(anyhow::Error::new(e)),
} }
} }
}
pub fn event_by_kind_pubkey(
&self,
kind: u32,
pubkey: &PublicKey,
) -> Result<Event, anyhow::Error> {
let rpk = Self::replaceable_index_key(kind, pubkey);
match self.db.get(rpk) {
Ok(v) => match v {
Some(v) => self.event_by_id(&Nip19::EventId(EventId::from_slice(v[8..].as_ref())?)),
None => Err(anyhow::Error::msg("Not Found")),
},
Err(e) => Err(anyhow::Error::new(e)),
}
}
}