From 99ba9adb80041b581756d3ddd0e267c59f3f696f Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Tue, 27 Sep 2022 19:19:24 +0200 Subject: [PATCH] part 3: follow and unfollow, but don't save to db yet --- Cargo.lock | 135 ++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 4 ++ src/db.rs | 54 ++++++++++++++++++++ src/main.rs | 16 +++++- src/routes.rs | 39 ++++++--------- src/sbot.rs | 76 ++++++++++++++++++++++++++++ 6 files changed, 295 insertions(+), 29 deletions(-) create mode 100644 src/db.rs diff --git a/Cargo.lock b/Cargo.lock index d622f86..58eb30e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -298,6 +298,15 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -342,6 +351,12 @@ version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "1.2.1" @@ -463,6 +478,29 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "memoffset", + "once_cell", + "scopeguard", +] + [[package]] name = "crossbeam-utils" version = "0.8.11" @@ -562,6 +600,15 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-sys" version = "0.3.7" @@ -635,6 +682,16 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "fsevent" version = "0.4.0" @@ -774,6 +831,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "gcc" version = "0.3.55" @@ -1172,7 +1238,7 @@ dependencies = [ "async-std", "async-stream 0.2.1", "base64 0.11.0", - "dirs", + "dirs 2.0.2", "futures", "get_if_addrs", "hex", @@ -1264,10 +1330,14 @@ dependencies = [ name = "lykin" version = "0.1.0" dependencies = [ + "bincode", "golgi", "log", "rocket", "rocket_dyn_templates", + "serde", + "sled", + "xdg", ] [[package]] @@ -1285,6 +1355,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.16" @@ -1460,6 +1539,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.5", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -1467,7 +1557,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.3", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi 0.3.9", ] [[package]] @@ -1810,7 +1914,7 @@ dependencies = [ "memchr", "multer", "num_cpus", - "parking_lot", + "parking_lot 0.12.1", "pin-project-lite", "rand", "ref-cast", @@ -2014,6 +2118,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "sled" +version = "0.34.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" +dependencies = [ + "crc32fast", + "crossbeam-epoch", + "crossbeam-utils", + "fs2", + "fxhash", + "libc", + "log", + "parking_lot 0.11.2", +] + [[package]] name = "slug" version = "0.1.4" @@ -2684,6 +2804,15 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "xdg" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6" +dependencies = [ + "dirs 4.0.0", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index d28791b..4d58130 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,7 @@ rocket = "0.5.0-rc.1" rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["tera"] } golgi = { git = "https://git.coopcloud.tech/golgi-ssb/golgi.git" } log = "0.4" +sled = "0.34" +xdg = "2.4.1" +serde = "1" +bincode = "1.3" diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..6fb301d --- /dev/null +++ b/src/db.rs @@ -0,0 +1,54 @@ +use sled::{Db, Tree, IVec, Result}; + +use std::path::Path; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone)] +pub struct Database { + db: Db, + peer_tree: Tree, +} + +impl Database { + pub fn init(path: &Path) -> Self { + let db = sled::open(path).expect("Failed to open database"); + + let peer_tree = db + .open_tree("peers") + .expect("Failed to open 'peers' database tree"); + + Database { db, peer_tree, } + } + + pub fn add_peer(&self, peer: Peer) -> Result> { + let peer_bytes = bincode::serialize(&peer).unwrap(); + self.peer_tree.insert(&peer.public_key, peer_bytes) + } + + pub fn remove_peer(&self, public_key: &str) -> Result<()> { + self.peer_tree.remove(&public_key).map(|_| ()) + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct Peer { + pub public_key: String, + pub name: String, +} + +impl Peer { + pub fn new(public_key: &str) -> Peer { + Peer { + public_key: public_key.to_string(), + name: "".to_string(), + } + } + + pub fn set_name(self, name: &str) -> Peer { + Self { + name: name.to_string(), + ..self + } + } +} diff --git a/src/main.rs b/src/main.rs index e7f4bc2..2ee4a15 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,29 @@ mod routes; mod sbot; mod utils; +mod db; + +use xdg::BaseDirectories; + use rocket::{launch, routes}; use rocket_dyn_templates::Template; -use crate::routes::*; +use crate::{db::Database, routes::*}; #[launch] async fn rocket() -> _ { + + let xdg_dirs = BaseDirectories::with_prefix("lykin").unwrap(); + + let db_path = xdg_dirs + .place_config_file("database") + .expect("Cannot create database directory"); + + let db = Database::init(&db_path); + rocket::build() + .manage(db) .attach(Template::fairing()) .mount("/", routes![home, subscribe_form, unsubscribe_form]) } diff --git a/src/routes.rs b/src/routes.rs index 88afc23..039bc54 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,10 +1,11 @@ use log::{info, warn}; -use rocket::{form::Form, get, post, response::{Redirect, Flash}, uri, FromForm, request::FlashMessage}; +use rocket::{form::Form, get, post, response::{Redirect, Flash}, uri, FromForm, request::FlashMessage, State}; use rocket_dyn_templates::{context, Template}; use crate::sbot; use crate::utils; +use crate::db::Database; #[derive(FromForm)] pub struct PeerForm { @@ -21,7 +22,7 @@ pub async fn home(flash: Option>) -> Template { } #[post("/subscribe", data = "")] -pub async fn subscribe_form(peer: Form) -> Result> { +pub async fn subscribe_form(db: &State, peer: Form) -> Result> { info!("Subscribing to peer {}", &peer.public_key); if let Err(e) = utils::validate_public_key(&peer.public_key) { let validation_err_msg = format!("Public key {} is invalid: {}", &peer.public_key, e); @@ -29,25 +30,19 @@ pub async fn subscribe_form(peer: Form) -> Result { - info!("Not currently following peer {}", &peer.public_key); - }, - Ok(status) if status.as_str() == "true" => { - info!("Already following peer {}. No further action needed here.", &peer.public_key); - }, - _ => (), + match sbot::follow_if_not_following(&peer.public_key).await { + Ok(_) => (), + Err(e) => { + warn!("{}", e); + return Err(Flash::error(Redirect::to(uri!(home)), e)); } - } else { - warn!("Received an error during `whoami` RPC call. Please ensure the go-sbot is running and try again.") } } Ok(Redirect::to(uri!(home))) } #[post("/unsubscribe", data = "")] -pub async fn unsubscribe_form(peer: Form) -> Result> { +pub async fn unsubscribe_form(db: &State, peer: Form) -> Result> { info!("Unsubscribing to peer {}", &peer.public_key); if let Err(e) = utils::validate_public_key(&peer.public_key) { let validation_err_msg = format!("Public key {} is invalid: {}", &peer.public_key, e); @@ -55,18 +50,12 @@ pub async fn unsubscribe_form(peer: Form) -> Result { - info!("Currently following peer {}", &peer.public_key); - }, - Ok(status) if status.as_str() == "true" => { - info!("Already not following peer {}. No further action needed here.", &peer.public_key); - }, - _ => (), + match sbot::unfollow_if_not_following(&peer.public_key).await { + Ok(_) => (), + Err(e) => { + warn!("{}", e); + return Err(Flash::error(Redirect::to(uri!(home)), e)); } - } else { - warn!("Received an error during `whoami` RPC call. Please ensure the go-sbot is running and try again.") } } Ok(Redirect::to(uri!(home))) diff --git a/src/sbot.rs b/src/sbot.rs index 6683afa..110f55c 100644 --- a/src/sbot.rs +++ b/src/sbot.rs @@ -1,5 +1,7 @@ use std::env; +use log::{info, warn}; + use golgi::{sbot::Keystore, Sbot, api::friends::RelationshipQuery}; @@ -32,3 +34,77 @@ pub async fn is_following(public_key_a: &str, public_key_b: &str) -> Result Result { + let mut sbot = init_sbot().await?; + sbot.follow(public_key).await.map_err(|e| e.to_string()) +} + +pub async fn unfollow_peer(public_key: &str) -> Result { + let mut sbot = init_sbot().await?; + sbot.unfollow(public_key).await.map_err(|e| e.to_string()) +} + +pub async fn get_name(public_key: &str) -> Result { + let mut sbot = init_sbot().await?; + sbot.get_name(public_key).await.map_err(|e| e.to_string()) +} + + +pub async fn follow_if_not_following(remote_peer: &str) -> Result<(), String> { + if let Ok(whoami) = whoami().await { + match is_following(&whoami, remote_peer).await { + Ok(status) if status.as_str() == "false" => { + match follow_peer(remote_peer).await { + Ok(_) => { + info!("Followed peer {}", &remote_peer); + Ok(()) + }, + Err(e) => { + let err_msg = format!("Failed to follow peer {}: {}", remote_peer, e); + warn!("{}", err_msg); + Err(err_msg) + }, + } + }, + Ok(status) if status.as_str() == "true" => { + info!("Already following peer {}. No further action taken", &remote_peer); + Ok(()) + }, + _ => Err("Failed to determine follow status: received unrecognised response from local sbot".to_string()), + } + } else { + let err_msg = String::from("Received an error during `whoami` RPC. Please ensure the go-sbot is running and try again."); + warn!("{}", err_msg); + Err(err_msg) + } +} + +pub async fn unfollow_if_not_following(remote_peer: &str) -> Result<(), String> { + if let Ok(whoami) = whoami().await { + match is_following(&whoami, remote_peer).await { + Ok(status) if status.as_str() == "false" => { + info!("Already not following peer {}. No further action taken", &remote_peer); + Ok(()) + }, + Ok(status) if status.as_str() == "true" => { + match unfollow_peer(remote_peer).await { + Ok(_) => { + info!("Unfollowed peer {}", &remote_peer); + Ok(()) + }, + Err(e) => { + let err_msg = format!("Failed to unfollow peer {}: {}", remote_peer, e); + warn!("{}", err_msg); + Err(err_msg) + }, + } + }, + _ => Err("Failed to determine follow status: received unrecognised response from local sbot".to_string()), + } + } else { + let err_msg = String::from("Received an error during `whoami` RPC. Please ensure the go-sbot is running and try again."); + warn!("{}", err_msg); + Err(err_msg) + } +}