271 lines
11 KiB
Rust
271 lines
11 KiB
Rust
use std::{
|
|
collections::HashMap,
|
|
error::Error,
|
|
fs,
|
|
fs::File,
|
|
io,
|
|
path::Path,
|
|
process::{Command, Output},
|
|
};
|
|
|
|
use async_std::task;
|
|
use dirs;
|
|
use futures::stream::TryStreamExt;
|
|
use golgi::{api::friends::RelationshipQuery, blobs, messages::SsbMessageValue, Sbot};
|
|
use log::info;
|
|
use peach_lib::sbot::SbotConfig;
|
|
use temporary::Directory;
|
|
|
|
use crate::{error::PeachWebError, utils::sbot};
|
|
|
|
// SBOT HELPER FUNCTIONS
|
|
|
|
/// Executes a systemctl command for the go-sbot.service process.
|
|
pub fn systemctl_sbot_cmd(cmd: &str) -> io::Result<Output> {
|
|
Command::new("systemctl")
|
|
.arg("--user")
|
|
.arg(cmd)
|
|
.arg("go-sbot.service")
|
|
.output()
|
|
}
|
|
|
|
/// Initialise an sbot client with the given configuration parameters.
|
|
pub async fn init_sbot_with_config(
|
|
sbot_config: &Option<SbotConfig>,
|
|
) -> Result<Sbot, PeachWebError> {
|
|
// initialise sbot connection with ip:port and shscap from config file
|
|
let sbot_client = match sbot_config {
|
|
// TODO: panics if we pass `Some(conf.shscap)` as second arg
|
|
Some(conf) => {
|
|
let ip_port = conf.lis.clone();
|
|
Sbot::init(Some(ip_port), None).await?
|
|
}
|
|
None => Sbot::init(None, None).await?,
|
|
};
|
|
|
|
Ok(sbot_client)
|
|
}
|
|
|
|
// FILEPATH FUNCTIONS
|
|
// return the path of the ssb-go directory
|
|
pub fn get_go_ssb_path() -> Result<String, PeachWebError> {
|
|
let go_ssb_path = match SbotConfig::read() {
|
|
Ok(conf) => conf.repo,
|
|
// return the default path if unable to read `config.toml`
|
|
Err(_) => {
|
|
// determine the home directory
|
|
let mut home_path = dirs::home_dir().ok_or_else(|| PeachWebError::HomeDir)?;
|
|
// add the go-ssb subdirectory
|
|
home_path.push(".ssb-go");
|
|
// convert the PathBuf to a String
|
|
home_path
|
|
.into_os_string()
|
|
.into_string()
|
|
.map_err(|_| PeachWebError::OsString)?
|
|
}
|
|
};
|
|
Ok(go_ssb_path)
|
|
}
|
|
|
|
// check whether a blob is in the blobstore
|
|
pub async fn blob_is_stored_locally(blob_path: &str) -> Result<bool, PeachWebError> {
|
|
let go_ssb_path = get_go_ssb_path()?;
|
|
let complete_path = format!("{}/blobs/sha256/{}", go_ssb_path, blob_path);
|
|
let blob_exists_locally = Path::new(&complete_path).exists();
|
|
Ok(blob_exists_locally)
|
|
}
|
|
|
|
/*
|
|
// take the path to a file, add it to the blobstore and return the blob id
|
|
pub async fn write_blob_to_store(file: &mut TempFile<'_>) -> Result<String, PeachWebError> {
|
|
// create temporary directory and path
|
|
let temp_dir = Directory::new("blob")?;
|
|
// we performed a `file.name().is_some()` check before calling `write_blob_to_store`
|
|
// so it should be safe to do a simple unwrap here
|
|
let filename = file.name().expect("retrieving filename from uploaded file");
|
|
let temp_path = temp_dir.join(filename);
|
|
// write file to temporary path
|
|
file.persist_to(&temp_path).await?;
|
|
// open the file and read it into a buffer
|
|
let mut file = File::open(&temp_path)?;
|
|
let mut buffer = Vec::new();
|
|
file.read_to_end(&mut buffer)?;
|
|
// hash the bytes representing the file
|
|
let (hex_hash, blob_id) = blobs::hash_blob(&buffer)?;
|
|
// define the blobstore path and blob filename
|
|
let (blob_dir, blob_filename) = hex_hash.split_at(2);
|
|
let go_ssb_path = get_go_ssb_path()?;
|
|
let blobstore_sub_dir = format!("{}/blobs/sha256/{}", go_ssb_path, blob_dir);
|
|
// create the blobstore sub-directory
|
|
fs::create_dir_all(&blobstore_sub_dir)?;
|
|
// copy the file to the blobstore
|
|
let blob_path = format!("{}/{}", blobstore_sub_dir, blob_filename);
|
|
fs::copy(temp_path, blob_path)?;
|
|
Ok(blob_id)
|
|
}
|
|
*/
|
|
|
|
pub fn latest_sequence_number() -> Result<u64, Box<dyn Error>> {
|
|
// retrieve latest go-sbot configuration parameters
|
|
let sbot_config = SbotConfig::read().ok();
|
|
|
|
task::block_on(async {
|
|
let mut sbot_client = init_sbot_with_config(&sbot_config).await?;
|
|
|
|
// retrieve the local id
|
|
let id = sbot_client.whoami().await?;
|
|
|
|
let history_stream = sbot_client.create_history_stream(id).await?;
|
|
let mut msgs: Vec<SsbMessageValue> = history_stream.try_collect().await?;
|
|
|
|
// reverse the list of messages so we can easily reference the latest one
|
|
msgs.reverse();
|
|
|
|
// return the sequence number of the latest msg
|
|
Ok(msgs[0].sequence)
|
|
})
|
|
}
|
|
|
|
pub fn get_blocks_list() -> Result<Vec<HashMap<String, String>>, Box<dyn Error>> {
|
|
// retrieve latest go-sbot configuration parameters
|
|
let sbot_config = SbotConfig::read().ok();
|
|
|
|
task::block_on(async {
|
|
let mut sbot_client = init_sbot_with_config(&sbot_config).await?;
|
|
|
|
let blocks = sbot_client.get_blocks().await?;
|
|
|
|
// we'll use this to store the profile info for each peer whom we block
|
|
let mut peer_list = Vec::new();
|
|
|
|
if !blocks.is_empty() {
|
|
for peer in blocks.iter() {
|
|
// trim whitespace (including newline characters) and
|
|
// remove the inverted-commas around the id
|
|
let key = peer.trim().replace('"', "");
|
|
// retrieve the profile info for the given peer
|
|
let mut peer_info = sbot_client.get_profile_info(&key).await?;
|
|
// insert the public key of the peer into the info hashmap
|
|
peer_info.insert("id".to_string(), key.to_string());
|
|
// we do not even attempt to find the blob for a blocked peer,
|
|
// since it may be vulgar to cause distress to the local peer.
|
|
peer_info.insert("blob_exists".to_string(), "false".to_string());
|
|
// push profile info to peer_list vec
|
|
peer_list.push(peer_info)
|
|
}
|
|
}
|
|
|
|
// return the list of blocked peers
|
|
Ok(peer_list)
|
|
})
|
|
}
|
|
|
|
pub fn get_follows_list() -> Result<Vec<HashMap<String, String>>, Box<dyn Error>> {
|
|
// retrieve latest go-sbot configuration parameters
|
|
let sbot_config = SbotConfig::read().ok();
|
|
|
|
task::block_on(async {
|
|
let mut sbot_client = init_sbot_with_config(&sbot_config).await?;
|
|
|
|
let follows = sbot_client.get_follows().await?;
|
|
|
|
// we'll use this to store the profile info for each peer who follows us
|
|
let mut peer_list = Vec::new();
|
|
|
|
if !follows.is_empty() {
|
|
for peer in follows.iter() {
|
|
// trim whitespace (including newline characters) and
|
|
// remove the inverted-commas around the id
|
|
let key = peer.trim().replace('"', "");
|
|
// retrieve the profile info for the given peer
|
|
let mut peer_info = sbot_client.get_profile_info(&key).await?;
|
|
// insert the public key of the peer into the info hashmap
|
|
peer_info.insert("id".to_string(), key.to_string());
|
|
// retrieve the profile image blob id for the given peer
|
|
if let Some(blob_id) = peer_info.get("image") {
|
|
// look-up the path for the image blob
|
|
if let Ok(blob_path) = blobs::get_blob_path(&blob_id) {
|
|
// insert the image blob path of the peer into the info hashmap
|
|
peer_info.insert("blob_path".to_string(), blob_path.to_string());
|
|
// check if the blob is in the blobstore
|
|
// set a flag in the info hashmap
|
|
match blob_is_stored_locally(&blob_path).await {
|
|
Ok(exists) if exists == true => {
|
|
peer_info.insert("blob_exists".to_string(), "true".to_string())
|
|
}
|
|
_ => peer_info.insert("blob_exists".to_string(), "false".to_string()),
|
|
};
|
|
}
|
|
}
|
|
// push profile info to peer_list vec
|
|
peer_list.push(peer_info)
|
|
}
|
|
}
|
|
|
|
// return the list of peers
|
|
Ok(peer_list)
|
|
})
|
|
}
|
|
|
|
pub fn get_friends_list() -> Result<Vec<HashMap<String, String>>, Box<dyn Error>> {
|
|
// retrieve latest go-sbot configuration parameters
|
|
let sbot_config = SbotConfig::read().ok();
|
|
|
|
task::block_on(async {
|
|
let mut sbot_client = init_sbot_with_config(&sbot_config).await?;
|
|
|
|
let local_id = sbot_client.whoami().await?;
|
|
|
|
let follows = sbot_client.get_follows().await?;
|
|
|
|
// we'll use this to store the profile info for each friend
|
|
let mut peer_list = Vec::new();
|
|
|
|
if !follows.is_empty() {
|
|
for peer in follows.iter() {
|
|
// trim whitespace (including newline characters) and
|
|
// remove the inverted-commas around the id
|
|
let peer_id = peer.trim().replace('"', "");
|
|
// retrieve the profile info for the given peer
|
|
let mut peer_info = sbot_client.get_profile_info(&peer_id).await?;
|
|
// insert the public key of the peer into the info hashmap
|
|
peer_info.insert("id".to_string(), peer_id.to_string());
|
|
// retrieve the profile image blob id for the given peer
|
|
if let Some(blob_id) = peer_info.get("image") {
|
|
// look-up the path for the image blob
|
|
if let Ok(blob_path) = blobs::get_blob_path(&blob_id) {
|
|
// insert the image blob path of the peer into the info hashmap
|
|
peer_info.insert("blob_path".to_string(), blob_path.to_string());
|
|
// check if the blob is in the blobstore
|
|
// set a flag in the info hashmap
|
|
match sbot::blob_is_stored_locally(&blob_path).await {
|
|
Ok(exists) if exists == true => {
|
|
peer_info.insert("blob_exists".to_string(), "true".to_string())
|
|
}
|
|
_ => peer_info.insert("blob_exists".to_string(), "false".to_string()),
|
|
};
|
|
}
|
|
}
|
|
|
|
// check if the peer follows us (making us friends)
|
|
let follow_query = RelationshipQuery {
|
|
source: peer_id.to_string(),
|
|
dest: local_id.clone(),
|
|
};
|
|
|
|
// query follow state
|
|
match sbot_client.friends_is_following(follow_query).await {
|
|
Ok(following) if following == "true" => {
|
|
// only push profile info to peer_list vec if they follow us
|
|
peer_list.push(peer_info)
|
|
}
|
|
_ => (),
|
|
};
|
|
}
|
|
}
|
|
|
|
// return the list of peers
|
|
Ok(peer_list)
|
|
})
|
|
}
|