diff --git a/Cargo.lock b/Cargo.lock index 062f632..22ab5c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "ahash" version = "0.8.11" @@ -169,6 +180,37 @@ dependencies = [ "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.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d50cb541e6d09e119e717c64c46ed33f49be7fa592fa805d56c11d6a7ff093c" +dependencies = [ + "async-utility", + "futures", + "futures-util", + "js-sys", + "tokio", + "tokio-rustls 0.26.0", + "tokio-socks", + "tokio-tungstenite", + "url", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "atoi" version = "2.0.0" @@ -193,6 +235,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "atomic-destructor" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d919cb60ba95c87ba42777e9e246c4e8d658057299b437b7512531ce0a09a23" +dependencies = [ + "tracing", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -324,9 +375,9 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.4" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788902099d47c8682efe6a7afb01c8d58b9794ba66c06affd81c3d6b560743eb" +checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" dependencies = [ "base58ck", "bech32", @@ -772,6 +823,12 @@ dependencies = [ "syn", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "der" version = "0.7.9" @@ -1055,6 +1112,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1215,6 +1278,18 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "group" version = "0.13.0" @@ -1285,6 +1360,11 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "hashlink" @@ -1981,6 +2061,7 @@ dependencies = [ "lnvps_db", "log", "nostr", + "nostr-sdk", "pretty_env_logger", "rand", "reqwest", @@ -2037,6 +2118,15 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.2", +] + [[package]] name = "matchers" version = "0.1.0" @@ -2171,6 +2261,7 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aad4b767bbed24ac5eb4465bfb83bc1210522eb99d67cf4e547ec2ec7e47786" dependencies = [ + "aes", "async-trait", "base64 0.22.1", "bech32", @@ -2191,6 +2282,55 @@ dependencies = [ "url", ] +[[package]] +name = "nostr-database" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23696338d51e45cd44e061823847f4b0d1d362eca80d5033facf9c184149f72f" +dependencies = [ + "async-trait", + "lru", + "nostr", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "nostr-relay-pool" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15fcc6e3f0ca54d0fc779009bc5f2684cea9147be3b6aa68a7d301ea590f95f5" +dependencies = [ + "async-utility", + "async-wsocket", + "atomic-destructor", + "negentropy 0.3.1", + "negentropy 0.4.3", + "nostr", + "nostr-database", + "thiserror", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "nostr-sdk" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "491221fc89b1aa189a0de640127127d68b4e7c5c1d44371b04d9a6d10694b5af" +dependencies = [ + "async-utility", + "atomic-destructor", + "nostr", + "nostr-database", + "nostr-relay-pool", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3119,6 +3259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "once_cell", + "ring", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle", @@ -4044,6 +4185,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-socks" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" +dependencies = [ + "either", + "futures-util", + "thiserror", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.16" @@ -4055,6 +4208,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "rustls 0.23.19", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tungstenite", + "webpki-roots", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -4245,6 +4414,26 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand", + "rustls 0.23.19", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + [[package]] name = "typenum" version = "1.17.0" @@ -4355,6 +4544,12 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf16_iter" version = "1.0.5" @@ -4489,6 +4684,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "whoami" version = "1.5.2" diff --git a/Cargo.toml b/Cargo.toml index 0b567f9..c9e032c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,9 @@ edition = "2021" name = "api" [features] -default = ["mikrotik"] +default = ["mikrotik", "nostr-dm"] mikrotik = [] +nostr-dm = ["dep:nostr-sdk", "nostr-sdk/nip04", "nostr-sdk/nip44", "nostr-sdk/nip59"] [dependencies] lnvps_db = { path = "lnvps_db" } @@ -32,3 +33,5 @@ clap = { version = "4.5.21", features = ["derive"] } ssh2 = "0.9.4" ssh-key = "0.6.7" lettre = { version = "0.11.10", features = ["tokio1-native-tls"] } + +nostr-sdk = { version = "0.37.0", optional = true, default-features = false } \ No newline at end of file diff --git a/lnvps_db/src/mysql.rs b/lnvps_db/src/mysql.rs index 26d1712..e1affd4 100644 --- a/lnvps_db/src/mysql.rs +++ b/lnvps_db/src/mysql.rs @@ -31,7 +31,7 @@ impl LNVpsDb for LNVpsDbMysql { } async fn upsert_user(&self, pubkey: &[u8; 32]) -> Result { - let res = sqlx::query("insert ignore into users(pubkey) values(?) returning id") + let res = sqlx::query("insert ignore into users(pubkey,contact_nip17) values(?,1) returning id") .bind(pubkey.as_slice()) .fetch_optional(&self.db) .await?; diff --git a/src/bin/api.rs b/src/bin/api.rs index 274cdd6..e380b45 100644 --- a/src/bin/api.rs +++ b/src/bin/api.rs @@ -12,6 +12,8 @@ use lnvps::status::VmStateCache; use lnvps::worker::{WorkJob, Worker}; use lnvps_db::{LNVpsDb, LNVpsDbMysql}; use log::error; +use nostr::Keys; +use nostr_sdk::Client; use std::net::{IpAddr, SocketAddr}; use std::time::Duration; @@ -50,6 +52,16 @@ async fn main() -> Result<(), Error> { db.execute(setup_script).await?; } + let nostr_client = if let Some(ref c) = settings.nostr { + let cx = Client::builder().signer(Keys::parse(&c.nsec)?).build(); + for r in &c.relays { + cx.add_relay(r.clone()).await?; + } + cx.connect().await; + Some(cx) + } else { + None + }; let router = settings.router.as_ref().map(|r| r.get_router()); let status = VmStateCache::new(); let worker_provisioner = @@ -58,7 +70,13 @@ async fn main() -> Result<(), Error> { .get_provisioner(db.clone(), router, lnd.clone(), exchange.clone()); worker_provisioner.init().await?; - let mut worker = Worker::new(db.clone(), worker_provisioner, &settings, status.clone()); + let mut worker = Worker::new( + db.clone(), + worker_provisioner, + &settings, + status.clone(), + nostr_client.clone(), + ); let sender = worker.sender(); // send a startup notification diff --git a/src/settings.rs b/src/settings.rs index 9799afb..f832a25 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -24,6 +24,9 @@ pub struct Settings { /// Network router config pub router: Option, + + /// Nostr config for sending DM's + pub nostr: Option, } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -33,6 +36,12 @@ pub struct LndConfig { pub macaroon: PathBuf, } +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct NostrConfig { + pub relays: Vec, + pub nsec: String, +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub enum RouterConfig { Mikrotik { diff --git a/src/worker.rs b/src/worker.rs index d05eb84..0dee6cf 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -8,10 +8,11 @@ use chrono::{Days, Utc}; use lettre::message::{MessageBuilder, MultiPart}; use lettre::transport::smtp::authentication::Credentials; use lettre::AsyncTransport; -use lettre::{AsyncSmtpTransport, Tokio1Executor, Transport}; +use lettre::{AsyncSmtpTransport, Tokio1Executor}; use lnvps_db::LNVpsDb; use log::{debug, error, info}; -use rocket::futures::SinkExt; +use nostr::{EventBuilder, PublicKey}; +use nostr_sdk::Client; use std::ops::Add; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; @@ -38,6 +39,7 @@ pub struct Worker { vm_state_cache: VmStateCache, tx: UnboundedSender, rx: UnboundedReceiver, + client: Option, last_check_vms: u64, } @@ -61,6 +63,7 @@ impl Worker { provisioner: P, settings: impl Into, vm_state_cache: VmStateCache, + client: Option, ) -> Self { let (tx, rx) = unbounded_channel(); Self { @@ -70,6 +73,7 @@ impl Worker { settings: settings.into(), tx, rx, + client, last_check_vms: Utc::now().timestamp() as u64, } } @@ -243,10 +247,20 @@ impl Worker { } } if user.contact_nip4 { - // send DM + // send dm } if user.contact_nip17 { - // send dm + if let Some(c) = self.client.as_ref() { + let sig = c.signer().await?; + let ev = EventBuilder::private_msg( + &sig, + PublicKey::from_slice(&user.pubkey)?, + message, + None, + ) + .await?; + c.send_event(ev).await?; + } } Ok(()) }