diff --git a/peach-web/src/main.rs b/peach-web/src/main.rs index 023775c..351ccff 100644 --- a/peach-web/src/main.rs +++ b/peach-web/src/main.rs @@ -170,7 +170,20 @@ fn main() { }, (GET) (/settings/scuttlebutt) => { - Response::html(routes::settings::scuttlebutt::menu::build_template()) + Response::html(routes::settings::scuttlebutt::menu::build_template(request)) + .reset_flash() + }, + + (GET) (/settings/scuttlebutt/restart) => { + routes::settings::scuttlebutt::restart::restart_sbot() + }, + + (GET) (/settings/scuttlebutt/start) => { + routes::settings::scuttlebutt::start::start_sbot() + }, + + (GET) (/settings/scuttlebutt/stop) => { + routes::settings::scuttlebutt::stop::stop_sbot() }, (GET) (/settings/scuttlebutt/configure) => { diff --git a/peach-web/src/routes/settings/menu.rs b/peach-web/src/routes/settings/menu.rs index 9a3c16c..dddfd94 100644 --- a/peach-web/src/routes/settings/menu.rs +++ b/peach-web/src/routes/settings/menu.rs @@ -2,8 +2,8 @@ use maud::{html, PreEscaped}; use crate::{templates, CONFIG}; -// TODO: flash message implementation for rouille -// +// ROUTE: /settings + /// Settings menu template builder. pub fn build_template() -> PreEscaped { let menu_template = html! { diff --git a/peach-web/src/routes/settings/scuttlebutt/menu.rs b/peach-web/src/routes/settings/scuttlebutt/menu.rs index 58c9822..8acd000 100644 --- a/peach-web/src/routes/settings/scuttlebutt/menu.rs +++ b/peach-web/src/routes/settings/scuttlebutt/menu.rs @@ -1,10 +1,8 @@ use maud::{html, PreEscaped}; use peach_lib::sbot::SbotStatus; +use rouille::Request; -use crate::templates; - -// TODO: flash message implementation for rouille -// +use crate::{templates, utils::flash::FlashRequest}; /// Read the status of the go-sbot service and render buttons accordingly. fn render_process_buttons() -> PreEscaped { @@ -28,8 +26,13 @@ fn render_process_buttons() -> PreEscaped { } } +// ROUTE: /settings/scuttlebutt + /// Scuttlebutt settings menu template builder. -pub fn build_template() -> PreEscaped { +pub fn build_template(request: &Request) -> PreEscaped { + // check for flash cookies; will be (None, None) if no flash cookies are found + let (flash_name, flash_msg) = request.retrieve_flash(); + let menu_template = html! { (PreEscaped("")) div class="card center" { @@ -39,6 +42,11 @@ pub fn build_template() -> PreEscaped { // conditionally render the start / stop / restart buttons (render_process_buttons()) } + // render flash message if cookies were found in the request + @if let (Some(name), Some(msg)) = (flash_name, flash_msg) { + (PreEscaped("")) + (templates::flash::build_template(name, &msg)) + } } }; diff --git a/peach-web/src/routes/settings/scuttlebutt/mod.rs b/peach-web/src/routes/settings/scuttlebutt/mod.rs index 485da58..e969135 100644 --- a/peach-web/src/routes/settings/scuttlebutt/mod.rs +++ b/peach-web/src/routes/settings/scuttlebutt/mod.rs @@ -1,2 +1,5 @@ pub mod configure; pub mod menu; +pub mod restart; +pub mod start; +pub mod stop; diff --git a/peach-web/src/routes/settings/scuttlebutt/restart.rs b/peach-web/src/routes/settings/scuttlebutt/restart.rs new file mode 100644 index 0000000..3e25b73 --- /dev/null +++ b/peach-web/src/routes/settings/scuttlebutt/restart.rs @@ -0,0 +1,33 @@ +use log::info; +use rouille::Response; + +use crate::utils::{flash::FlashResponse, sbot::systemctl_sbot_cmd}; + +// ROUTE: /settings/scuttlebutt/restart + +/// Attempt to restart the go-sbot.service process. +/// Redirect to the Scuttlebutt settings menu and communicate the outcome of +/// the attempt via a flash message. +pub fn restart_sbot() -> Response { + info!("Restarting go-sbot.service"); + let (flash_name, flash_msg) = match systemctl_sbot_cmd("stop") { + // if stop was successful, try to start the process + Ok(_) => match systemctl_sbot_cmd("start") { + Ok(_) => ( + "flash_name=success".to_string(), + "flash_msg=Sbot process has been restarted".to_string(), + ), + Err(e) => ( + "flash_name=error".to_string(), + format!("flash_msg=Failed to start the sbot process: {}", e), + ), + }, + Err(e) => ( + "flash_name=error".to_string(), + format!("flash_msg=Failed to stop the sbot process: {}", e), + ), + }; + + // redirect to the scuttlebutt settings menu + Response::redirect_303("/settings/scuttlebutt").add_flash(flash_name, flash_msg) +} diff --git a/peach-web/src/routes/settings/scuttlebutt/start.rs b/peach-web/src/routes/settings/scuttlebutt/start.rs new file mode 100644 index 0000000..2ee7f4e --- /dev/null +++ b/peach-web/src/routes/settings/scuttlebutt/start.rs @@ -0,0 +1,26 @@ +use log::info; +use rouille::Response; + +use crate::utils::{flash::FlashResponse, sbot::systemctl_sbot_cmd}; + +// ROUTE: /settings/scuttlebutt/start + +/// Attempt to start the go-sbot.service process. +/// Redirect to the Scuttlebutt settings menu and communicate the outcome of +/// the attempt via a flash message. +pub fn start_sbot() -> Response { + info!("Starting go-sbot.service"); + let (flash_name, flash_msg) = match systemctl_sbot_cmd("start") { + Ok(_) => ( + "flash_name=success".to_string(), + "flash_msg=Sbot process has been started".to_string(), + ), + Err(_) => ( + "flash_name=error".to_string(), + "flash_msg=Failed to start the sbot process".to_string(), + ), + }; + + // redirect to the scuttlebutt settings menu + Response::redirect_303("/settings/scuttlebutt").add_flash(flash_name, flash_msg) +} diff --git a/peach-web/src/routes/settings/scuttlebutt/stop.rs b/peach-web/src/routes/settings/scuttlebutt/stop.rs new file mode 100644 index 0000000..d548698 --- /dev/null +++ b/peach-web/src/routes/settings/scuttlebutt/stop.rs @@ -0,0 +1,26 @@ +use log::info; +use rouille::Response; + +use crate::utils::{flash::FlashResponse, sbot::systemctl_sbot_cmd}; + +// ROUTE: /settings/scuttlebutt/stop + +/// Attempt to stop the go-sbot.service process. +/// Redirect to the Scuttlebutt settings menu and communicate the outcome of +/// the attempt via a flash message. +pub fn stop_sbot() -> Response { + info!("Stopping go-sbot.service"); + let (flash_name, flash_msg) = match systemctl_sbot_cmd("stop") { + Ok(_) => ( + "flash_name=success".to_string(), + "flash_msg=Sbot process has been stopped".to_string(), + ), + Err(_) => ( + "flash_name=error".to_string(), + "flash_msg=Failed to stop the sbot process".to_string(), + ), + }; + + // redirect to the scuttlebutt settings menu + Response::redirect_303("/settings/scuttlebutt").add_flash(flash_name, flash_msg) +} diff --git a/peach-web/src/utils/sbot.rs b/peach-web/src/utils/sbot.rs index d916476..5dcf05e 100644 --- a/peach-web/src/utils/sbot.rs +++ b/peach-web/src/utils/sbot.rs @@ -1,19 +1,35 @@ -use std::io::prelude::*; -use std::{collections::HashMap, error::Error}; -use std::{fs, fs::File, path::Path}; +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::{blobs, messages::SsbMessageValue, Sbot}; +use golgi::{api::friends::RelationshipQuery, blobs, messages::SsbMessageValue, Sbot}; use log::info; use peach_lib::sbot::SbotConfig; use temporary::Directory; -use crate::error::PeachWebError; +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 { + 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, ) -> Result { @@ -191,16 +207,64 @@ pub fn get_follows_list() -> Result>, Box }) } -pub fn get_friends_list() -> Result, Box> { +pub fn get_friends_list() -> Result>, Box> { // 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?; - // return the sequence number of the latest msg - //Ok(msgs[0].sequence) + let local_id = sbot_client.whoami().await?; - Ok(vec!["temp".to_string()]) + 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) }) }