part 3: follow and unfollow, but don't save to db yet

This commit is contained in:
Daan Wynen 2022-09-27 19:19:24 +02:00
parent e72b652780
commit 99ba9adb80
6 changed files with 295 additions and 29 deletions

135
Cargo.lock generated
View File

@ -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"

View File

@ -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"

54
src/db.rs Normal file
View File

@ -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<Option<IVec>> {
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
}
}
}

View File

@ -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])
}

View File

@ -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<FlashMessage<'_>>) -> Template {
}
#[post("/subscribe", data = "<peer>")]
pub async fn subscribe_form(peer: Form<PeerForm>) -> Result<Redirect, Flash<Redirect>> {
pub async fn subscribe_form(db: &State<Database>, peer: Form<PeerForm>) -> Result<Redirect, Flash<Redirect>> {
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<PeerForm>) -> Result<Redirect, Flash<Redi
return Err(Flash::error(Redirect::to(uri!(home)), validation_err_msg));
} else {
info!("Public key {} is valid.", &peer.public_key);
if let Ok(whoami) = sbot::whoami().await {
match sbot::is_following(&whoami, &peer.public_key).await {
Ok(status) if status.as_str() == "false" => {
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 = "<peer>")]
pub async fn unsubscribe_form(peer: Form<PeerForm>) -> Result<Redirect, Flash<Redirect>> {
pub async fn unsubscribe_form(db: &State<Database>, peer: Form<PeerForm>) -> Result<Redirect, Flash<Redirect>> {
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<PeerForm>) -> Result<Redirect, Flash<R
return Err(Flash::error(Redirect::to(uri!(home)), validation_err_msg));
} else {
info!("Public key {} is valid.", &peer.public_key);
if let Ok(whoami) = sbot::whoami().await {
match sbot::is_following(&whoami, &peer.public_key).await {
Ok(status) if status.as_str() == "false" => {
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)))

View File

@ -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<Stri
.await
.map_err(|e| e.to_string())
}
pub async fn follow_peer(public_key: &str) -> Result<String, String> {
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<String, String> {
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<String, String> {
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)
}
}