From 3397e5eb752ee2305f18846ad8c843bb47c6f20a Mon Sep 17 00:00:00 2001 From: glyph Date: Tue, 1 Feb 2022 10:03:46 +0200 Subject: [PATCH 01/10] add config file reader for go-sbot --- peach-lib/Cargo.toml | 2 + peach-lib/src/error.rs | 23 ++++- peach-lib/src/lib.rs | 2 +- peach-lib/src/password_utils.rs | 5 +- peach-lib/src/sbot.rs | 152 ++++++++++++++++++++++++++++++++ peach-lib/src/sbot_client.rs | 111 ----------------------- 6 files changed, 180 insertions(+), 115 deletions(-) create mode 100644 peach-lib/src/sbot.rs delete mode 100644 peach-lib/src/sbot_client.rs diff --git a/peach-lib/Cargo.toml b/peach-lib/Cargo.toml index 53c8e92..4d4fe38 100644 --- a/peach-lib/Cargo.toml +++ b/peach-lib/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] chrono = "0.4.19" +dirs = "4.0" fslock="0.1.6" jsonrpc-client-core = "0.5" jsonrpc-client-http = "0.5" @@ -16,4 +17,5 @@ regex = "1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_yaml = "0.8" +toml = "0.5.8" sha3 = "0.10.0" diff --git a/peach-lib/src/error.rs b/peach-lib/src/error.rs index e309195..3e7dff4 100644 --- a/peach-lib/src/error.rs +++ b/peach-lib/src/error.rs @@ -7,6 +7,9 @@ use std::{io, str, string}; /// This type represents all possible errors that can occur when interacting with the PeachCloud library. #[derive(Debug)] pub enum PeachError { + /// Represents a failure to determine the path of the user's home directory. + HomeDir, + /// Represents all other cases of `std::io::Error`. Io(io::Error), @@ -67,6 +70,9 @@ pub enum PeachError { /// Represents a failure to serialize or deserialize JSON. SerdeJson(serde_json::error::Error), + /// Represents a failure to deserialize TOML. + Toml(toml::de::Error), + /// Represents a failure to serialize or deserialize YAML. SerdeYaml(serde_yaml::Error), @@ -87,7 +93,7 @@ pub enum PeachError { Write { /// The underlying source of the error. source: io::Error, - /// The file path for the write attemp. + /// The file path for the write attempt. path: String, }, } @@ -95,6 +101,7 @@ pub enum PeachError { impl std::error::Error for PeachError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match *self { + PeachError::HomeDir => None, PeachError::Io(_) => None, PeachError::JsonRpcClientCore(_) => None, PeachError::JsonRpcCore(_) => None, @@ -111,6 +118,7 @@ impl std::error::Error for PeachError { PeachError::SerdeJson(_) => None, PeachError::SerdeYaml(_) => None, PeachError::SsbAdminIdNotFound { .. } => None, + PeachError::Toml(_) => None, PeachError::Utf8ToStr(_) => None, PeachError::Utf8ToString(_) => None, PeachError::Write { ref source, .. } => Some(source), @@ -121,6 +129,12 @@ impl std::error::Error for PeachError { impl std::fmt::Display for PeachError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match *self { + PeachError::HomeDir => { + write!( + f, + "Unable to determine the path of the user's home directory" + ) + } PeachError::Io(ref err) => err.fmt(f), PeachError::JsonRpcClientCore(ref err) => err.fmt(f), PeachError::JsonRpcCore(ref err) => { @@ -158,6 +172,7 @@ impl std::fmt::Display for PeachError { PeachError::SsbAdminIdNotFound { ref id } => { write!(f, "Config error: SSB admin ID `{}` not found", id) } + PeachError::Toml(ref err) => err.fmt(f), PeachError::Utf8ToStr(ref err) => err.fmt(f), PeachError::Utf8ToString(ref err) => err.fmt(f), PeachError::Write { ref path, .. } => { @@ -209,6 +224,12 @@ impl From for PeachError { } } +impl From for PeachError { + fn from(err: toml::de::Error) -> PeachError { + PeachError::Toml(err) + } +} + impl From for PeachError { fn from(err: str::Utf8Error) -> PeachError { PeachError::Utf8ToStr(err) diff --git a/peach-lib/src/lib.rs b/peach-lib/src/lib.rs index 1144fc7..cdbe8fa 100644 --- a/peach-lib/src/lib.rs +++ b/peach-lib/src/lib.rs @@ -4,7 +4,7 @@ pub mod error; pub mod network_client; pub mod oled_client; pub mod password_utils; -pub mod sbot_client; +pub mod sbot; pub mod stats_client; // re-export error types diff --git a/peach-lib/src/password_utils.rs b/peach-lib/src/password_utils.rs index 9b427a2..fa28904 100644 --- a/peach-lib/src/password_utils.rs +++ b/peach-lib/src/password_utils.rs @@ -1,7 +1,7 @@ use nanorand::{Rng, WyRand}; use sha3::{Digest, Sha3_256}; -use crate::{config_manager, error::PeachError, sbot_client}; +use crate::{config_manager, error::PeachError}; /// Returns Ok(()) if the supplied password is correct, /// and returns Err if the supplied password is incorrect. @@ -102,7 +102,8 @@ using this link: http://peach.local/reset_password", // finally send the message to the admins let peach_config = config_manager::load_peach_config()?; for ssb_admin_id in peach_config.ssb_admin_ids { - sbot_client::private_message(&msg, &ssb_admin_id)?; + // TODO: replace with golgi + //sbot_client::private_message(&msg, &ssb_admin_id)?; } Ok(()) } diff --git a/peach-lib/src/sbot.rs b/peach-lib/src/sbot.rs new file mode 100644 index 0000000..2a2e5d5 --- /dev/null +++ b/peach-lib/src/sbot.rs @@ -0,0 +1,152 @@ +//! Data types and associated methods for monitoring and configuring go-sbot. + +use std::{fs, process::Command, str}; + +use serde::{Deserialize, Serialize}; +use toml; + +use crate::error::PeachError; + +/// go-sbot process status. +#[derive(Debug, Serialize, Deserialize)] +pub struct SbotStatus { + /// Current process state. + pub state: Option, + /// Current process boot state. + pub boot_state: Option, + /// Current process memory usage in bytes. + pub memory: Option, + /// Uptime for the process (if state is `active`). + pub uptime: Option, + /// Downtime for the process (if state is `inactive`). + pub downtime: Option, +} + +impl SbotStatus { + /// Default builder for `SbotStatus`. + fn default() -> Self { + Self { + state: None, + boot_state: None, + memory: None, + uptime: None, + downtime: None, + } + } + + /// Retrieve statistics for the go-sbot systemd process by querying `systemctl`. + pub fn read() -> Result { + let mut status = SbotStatus::default(); + + let info_output = Command::new("/usr/bin/systemctl") + .arg("--user") + .arg("show") + .arg("go-sbot.service") + .arg("--no-page") + .output()?; + + let service_info = std::str::from_utf8(&info_output.stdout)?; + + for line in service_info.lines() { + if line.starts_with("ActiveState=") { + if let Some(state) = line.strip_prefix("ActiveState=") { + status.state = Some(state.to_string()) + } + } else if line.starts_with("MemoryCurrent=") { + if let Some(memory) = line.strip_prefix("MemoryCurrent=") { + status.memory = memory.parse().ok() + } + } + } + + let status_output = Command::new("/usr/bin/systemctl") + .arg("--user") + .arg("status") + .arg("go-sbot.service") + .output()?; + + let service_status = str::from_utf8(&status_output.stdout)?; + //.map_err(PeachError::Utf8ToStr)?; + + for line in service_status.lines() { + // example of the output line we're looking for: + // `Loaded: loaded (/home/glyph/.config/systemd/user/go-sbot.service; enabled; vendor + // preset: enabled)` + if line.contains("Loaded:") { + let before_boot_state = line.find(';'); + let after_boot_state = line.rfind(';'); + if let (Some(start), Some(end)) = (before_boot_state, after_boot_state) { + // extract the enabled / disabled from the `Loaded: ...` line + // using the index of the first ';' + 2 and the last ';' + status.boot_state = Some(line[start + 2..end].to_string()); + } + // example of the output line we're looking for here: + // `Active: active (running) since Mon 2022-01-24 16:22:51 SAST; 4min 14s ago` + } else if line.contains("Active:") { + let before_time = line.find(';'); + let after_time = line.find(" ago"); + if let (Some(start), Some(end)) = (before_time, after_time) { + // extract the uptime / downtime from the `Active: ...` line + // using the index of ';' + 2 and the index of " ago" + let time = Some(&line[start + 2..end]); + // if service is active then the `time` reading is uptime + if status.state == Some("active".to_string()) { + status.uptime = time.map(|t| t.to_string()) + // if service is inactive then the `time` reading is downtime + } else if status.state == Some("inactive".to_string()) { + status.downtime = time.map(|t| t.to_string()) + } + } + } + } + + Ok(status) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SbotConfig { + // TODO: maybe define as a Path type? + /// Directory path for the log and indexes. + repo: Option, + /// Directory path for writing debug output. + debugdir: Option, + /// Secret-handshake app-key (aka. network key). + shscap: Option, + /// HMAC hash used to sign messages. + hmac: Option, + /// Replication hops (1: friends, 2: friends of friends). + hops: Option, + /// Address to listen on. + lis: Option, + /// Address to listen on for WebSocket connections. + wslis: Option, + /// Address to for metrics and pprof HTTP server. + debuglis: Option, + /// Enable sending local UDP broadcasts. + localadv: Option, + /// Enable listening for UDP broadcasts and connecting. + localdiscov: Option, + /// Enable syncing by using epidemic-broadcast-trees (EBT). + #[serde(rename = "enable-ebt")] + enable_ebt: Option, + /// Bypass graph auth and fetch remote's feed (useful for pubs that are restoring their data + /// from peer; user beware - caveats about). + promisc: Option, + /// Disable the UNIX socket RPC interface. + nounixsock: Option, +} + +impl SbotConfig { + pub fn read() -> Result { + // determine path of user's home directory + let mut config_path = dirs::home_dir().ok_or(PeachError::HomeDir)?; + config_path.push(".ssb-go/config.toml"); + + let config_contents = fs::read_to_string(config_path)?; + + let config: SbotConfig = toml::from_str(&config_contents)?; + + Ok(config) + } +} diff --git a/peach-lib/src/sbot_client.rs b/peach-lib/src/sbot_client.rs deleted file mode 100644 index bb6cf36..0000000 --- a/peach-lib/src/sbot_client.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Interfaces for monitoring and configuring go-sbot using sbotcli. - -use std::process::Command; - -use serde::{Deserialize, Serialize}; - -use crate::error::PeachError; - -pub fn is_sbot_online() -> Result { - let output = Command::new("/usr/bin/systemctl") - .arg("status") - .arg("peach-go-sbot") - .output()?; - let status = output.status; - // returns true if the service had an exist status of 0 (is running) - let is_running = status.success(); - Ok(is_running) -} - -/// currently go-sbotcli determines where the working directory is -/// using the home directory of th user that invokes it -/// this could be changed to be supplied as CLI arg -/// but for now all sbotcli commands must first become peach-go-sbot before running -/// the sudoers file is configured to allow this to happen without a password -pub fn sbotcli_command() -> Command { - let mut command = Command::new("sudo"); - command - .arg("-u") - .arg("peach-go-sbot") - .arg("/usr/bin/sbotcli"); - command -} - -pub fn post(msg: &str) -> Result<(), PeachError> { - let mut command = sbotcli_command(); - let output = command.arg("publish").arg("post").arg(msg).output()?; - if output.status.success() { - Ok(()) - } else { - let stderr = std::str::from_utf8(&output.stderr)?; - Err(PeachError::SbotCli { - msg: format!("Error making ssb post: {}", stderr), - }) - } -} - -#[derive(Serialize, Deserialize)] -struct WhoAmIValue { - id: String, -} - -pub fn whoami() -> Result { - let mut command = sbotcli_command(); - let output = command.arg("call").arg("whoami").output()?; - let text_output = std::str::from_utf8(&output.stdout)?; - let value: WhoAmIValue = serde_json::from_str(text_output)?; - let id = value.id; - Ok(id) -} - -pub fn create_invite(uses: i32) -> Result { - let mut command = sbotcli_command(); - let output = command - .arg("invite") - .arg("create") - .arg("--uses") - .arg(uses.to_string()) - .output()?; - let text_output = std::str::from_utf8(&output.stdout)?; - let output = text_output.replace('\n', ""); - Ok(output) -} - -pub fn update_pub_name(new_name: &str) -> Result<(), PeachError> { - let pub_ssb_id = whoami()?; - let mut command = sbotcli_command(); - let output = command - .arg("publish") - .arg("about") - .arg("--name") - .arg(new_name) - .arg(pub_ssb_id) - .output()?; - if output.status.success() { - Ok(()) - } else { - let stderr = std::str::from_utf8(&output.stderr)?; - Err(PeachError::SbotCli { - msg: format!("Error updating pub name: {}", stderr), - }) - } -} - -pub fn private_message(msg: &str, recipient: &str) -> Result<(), PeachError> { - let mut command = sbotcli_command(); - let output = command - .arg("publish") - .arg("post") - .arg("--recps") - .arg(recipient) - .arg(msg) - .output()?; - if output.status.success() { - Ok(()) - } else { - let stderr = std::str::from_utf8(&output.stderr)?; - Err(PeachError::SbotCli { - msg: format!("Error sending ssb private message: {}", stderr), - }) - } -} -- 2.40.1 From d801c957bd519b32af9a75a9b5da040e9bfd1265 Mon Sep 17 00:00:00 2001 From: glyph Date: Tue, 1 Feb 2022 10:06:45 +0200 Subject: [PATCH 02/10] introduce sbot status data --- peach-web/src/routes/index.rs | 8 ++--- peach-web/src/routes/status/device.rs | 11 +++--- peach-web/src/routes/status/scuttlebutt.rs | 12 ++++--- peach-web/templates/home.html.tera | 2 +- .../scuttlebutt/configure_sbot.html.tera | 36 ++++++++++++------- .../settings/scuttlebutt/menu.html.tera | 2 +- .../templates/status/scuttlebutt.html.tera | 28 +++++++-------- 7 files changed, 58 insertions(+), 41 deletions(-) diff --git a/peach-web/src/routes/index.rs b/peach-web/src/routes/index.rs index 8b40d53..54011d5 100644 --- a/peach-web/src/routes/index.rs +++ b/peach-web/src/routes/index.rs @@ -1,4 +1,4 @@ -use peach_stats::sbot; +use peach_lib::sbot::SbotStatus; use rocket::{get, request::FlashMessage, State}; use rocket_dyn_templates::{tera::Context, Template}; @@ -9,11 +9,11 @@ use crate::RocketConfig; #[get("/")] pub fn home(_auth: Authenticated, config: &State) -> Template { - // retrieve go-sbot systemd process stats - let sbot_stats = sbot::sbot_stats().ok(); + // retrieve go-sbot systemd process status + let sbot_status = SbotStatus::read().ok(); let mut context = Context::new(); - context.insert("sbot_stats", &sbot_stats); + context.insert("sbot_status", &sbot_status); context.insert("flash_name", &None::<()>); context.insert("flash_msg", &None::<()>); context.insert("title", &None::<()>); diff --git a/peach-web/src/routes/status/device.rs b/peach-web/src/routes/status/device.rs index dd3845b..ec66837 100644 --- a/peach-web/src/routes/status/device.rs +++ b/peach-web/src/routes/status/device.rs @@ -12,7 +12,7 @@ use std::{ }; use peach_lib::{ - config_manager::load_peach_config, dyndns_client, network_client, oled_client, sbot_client, + config_manager::load_peach_config, dyndns_client, network_client, oled_client, sbot::SbotStatus, }; use peach_stats::{ stats, @@ -117,10 +117,11 @@ impl StatusContext { } // test if go-sbot is running - let sbot_is_online_result = sbot_client::is_sbot_online(); - let sbot_is_online: bool = match sbot_is_online_result { - Ok(val) => val, - Err(_err) => false, + let sbot_status = SbotStatus::read(); + let sbot_is_online: bool = match sbot_status { + // return true if state is active + Ok(status) => matches!(status.state == Some("active".to_string()), true), + _ => false, }; StatusContext { diff --git a/peach-web/src/routes/status/scuttlebutt.rs b/peach-web/src/routes/status/scuttlebutt.rs index aab41fb..d4e5b51 100644 --- a/peach-web/src/routes/status/scuttlebutt.rs +++ b/peach-web/src/routes/status/scuttlebutt.rs @@ -1,4 +1,4 @@ -use peach_stats::sbot; +use peach_lib::sbot::{SbotConfig, SbotStatus}; use rocket::{get, State}; use rocket_dyn_templates::{tera::Context, Template}; @@ -9,10 +9,14 @@ use crate::RocketConfig; #[get("/scuttlebutt")] pub fn scuttlebutt_status(_auth: Authenticated, config: &State) -> Template { + // retrieve go-sbot systemd process status + let sbot_status = SbotStatus::read().ok(); + // retrieve go-sbot configuration parameters + let sbot_config = SbotConfig::read().ok(); + let mut context = Context::new(); - // retrieve go-sbot systemd process stats - let sbot_stats = sbot::sbot_stats().ok(); - context.insert("sbot_stats", &sbot_stats); + context.insert("sbot_status", &sbot_status); + context.insert("sbot_config", &sbot_config); context.insert("flash_name", &None::<()>); context.insert("flash_msg", &None::<()>); context.insert("title", &Some("Scuttlebutt Status")); diff --git a/peach-web/templates/home.html.tera b/peach-web/templates/home.html.tera index fe0e298..ecce8c2 100644 --- a/peach-web/templates/home.html.tera +++ b/peach-web/templates/home.html.tera @@ -25,7 +25,7 @@ -
+
diff --git a/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera b/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera index f72c5a6..40a597e 100644 --- a/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera +++ b/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera @@ -1,20 +1,32 @@ {%- extends "nav" -%} {%- block card %} + {# ASSIGN VARIABLES #} + {# ---------------- #} + {%- if sbot_config.hops -%} + {% set hops = sbot_config.hops -%} + {%- else -%} + {% set hops = "X" -%} + {%- endif -%} + {%- if sbot_config.lis -%} + {%- set listen_addr = sbot_config.lis | split(pat=":") -%} + {%- else -%} + {%- set listen_addr = ["", ""] -%} + {%- endif -%}
- + - + - + - + - +
@@ -22,32 +34,32 @@
- +
- +

- +

- +

- +

- +

- +

diff --git a/peach-web/templates/settings/scuttlebutt/menu.html.tera b/peach-web/templates/settings/scuttlebutt/menu.html.tera index ae72f49..346b89a 100644 --- a/peach-web/templates/settings/scuttlebutt/menu.html.tera +++ b/peach-web/templates/settings/scuttlebutt/menu.html.tera @@ -5,7 +5,7 @@
Configure Sbot - {% if sbot_stats.state == "active" %} + {% if sbot_status.state == "active" %} Stop Sbot Restart Sbot {% else %} diff --git a/peach-web/templates/status/scuttlebutt.html.tera b/peach-web/templates/status/scuttlebutt.html.tera index d99eb5e..d147bbf 100644 --- a/peach-web/templates/status/scuttlebutt.html.tera +++ b/peach-web/templates/status/scuttlebutt.html.tera @@ -2,15 +2,15 @@ {%- block card %} {# ASSIGN VARIABLES #} {# ---------------- #} - {%- if sbot_stats.memory -%} - {% set mem = sbot_stats.memory / 1024 / 1024 | round -%} + {%- if sbot_status.memory -%} + {% set mem = sbot_status.memory / 1024 / 1024 | round -%} {%- else -%} {% set mem = "X" -%} {%- endif -%}
-
+
@@ -20,25 +20,25 @@
- Hermies - + Hermies +

1.1.0-alpha

- {% if sbot_stats.state == "active" %} + {% if sbot_status.state == "active" %} -

{{ sbot_stats.uptime }}

+

{{ sbot_status.uptime }}

{# render downtime element if downtime is `Some(time)` #} {# downtime will be `None` if service is stopped and disabled #} - {%- elif sbot_stats.downtime -%} + {%- elif sbot_status.downtime -%} -

{{ sbot_stats.downtime }}

+

{{ sbot_status.downtime }}

{%- endif -%} - {% if sbot_stats.boot_state == "enabled" %} + {% if sbot_status.boot_state == "enabled" %}

Enabled

{% else %}

Disabled

@@ -80,7 +80,7 @@
- +
@@ -93,10 +93,10 @@
- +
- - + +
-- 2.40.1 From 46926bf468b5eebe5de8450685df504b18d1f70b Mon Sep 17 00:00:00 2001 From: glyph Date: Tue, 1 Feb 2022 16:07:19 +0200 Subject: [PATCH 03/10] add config writer method and required error variants --- peach-lib/src/error.rs | 19 +++++++++++++++---- peach-lib/src/sbot.rs | 20 ++++++++++++++++++-- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/peach-lib/src/error.rs b/peach-lib/src/error.rs index 3e7dff4..ac4ef97 100644 --- a/peach-lib/src/error.rs +++ b/peach-lib/src/error.rs @@ -71,7 +71,10 @@ pub enum PeachError { SerdeJson(serde_json::error::Error), /// Represents a failure to deserialize TOML. - Toml(toml::de::Error), + TomlDeser(toml::de::Error), + + /// Represents a failure to serialize TOML. + TomlSer(toml::ser::Error), /// Represents a failure to serialize or deserialize YAML. SerdeYaml(serde_yaml::Error), @@ -118,7 +121,8 @@ impl std::error::Error for PeachError { PeachError::SerdeJson(_) => None, PeachError::SerdeYaml(_) => None, PeachError::SsbAdminIdNotFound { .. } => None, - PeachError::Toml(_) => None, + PeachError::TomlDeser(_) => None, + PeachError::TomlSer(_) => None, PeachError::Utf8ToStr(_) => None, PeachError::Utf8ToString(_) => None, PeachError::Write { ref source, .. } => Some(source), @@ -172,7 +176,8 @@ impl std::fmt::Display for PeachError { PeachError::SsbAdminIdNotFound { ref id } => { write!(f, "Config error: SSB admin ID `{}` not found", id) } - PeachError::Toml(ref err) => err.fmt(f), + PeachError::TomlDeser(ref err) => err.fmt(f), + PeachError::TomlSer(ref err) => err.fmt(f), PeachError::Utf8ToStr(ref err) => err.fmt(f), PeachError::Utf8ToString(ref err) => err.fmt(f), PeachError::Write { ref path, .. } => { @@ -226,7 +231,13 @@ impl From for PeachError { impl From for PeachError { fn from(err: toml::de::Error) -> PeachError { - PeachError::Toml(err) + PeachError::TomlDeser(err) + } +} + +impl From for PeachError { + fn from(err: toml::ser::Error) -> PeachError { + PeachError::TomlSer(err) } } diff --git a/peach-lib/src/sbot.rs b/peach-lib/src/sbot.rs index 2a2e5d5..e9b9081 100644 --- a/peach-lib/src/sbot.rs +++ b/peach-lib/src/sbot.rs @@ -1,9 +1,8 @@ //! Data types and associated methods for monitoring and configuring go-sbot. -use std::{fs, process::Command, str}; +use std::{fs, fs::File, io::Write, process::Command, str}; use serde::{Deserialize, Serialize}; -use toml; use crate::error::PeachError; @@ -149,4 +148,21 @@ impl SbotConfig { Ok(config) } + + pub fn write(config: SbotConfig) -> Result<(), PeachError> { + // convert the provided `SbotConfig` instance to a string + let config_string = toml::to_string(&config)?; + + // determine path of user's home directory + let mut config_path = dirs::home_dir().ok_or(PeachError::HomeDir)?; + config_path.push(".ssb-go/config.toml"); + + // open config file for writing + let mut file = File::create(config_path)?; + + // write the config string to file + write!(file, "{}", config_string)?; + + Ok(()) + } } -- 2.40.1 From 90a90096f4a04df5b46022fbaeed9850f1404999 Mon Sep 17 00:00:00 2001 From: glyph Date: Wed, 2 Feb 2022 14:13:02 +0200 Subject: [PATCH 04/10] remove option type wrappers and implement defaults for SbotConfig --- peach-lib/src/sbot.rs | 64 ++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/peach-lib/src/sbot.rs b/peach-lib/src/sbot.rs index e9b9081..b784017 100644 --- a/peach-lib/src/sbot.rs +++ b/peach-lib/src/sbot.rs @@ -21,8 +21,8 @@ pub struct SbotStatus { pub downtime: Option, } -impl SbotStatus { - /// Default builder for `SbotStatus`. +/// Default builder for `SbotStatus`. +impl Default for SbotStatus { fn default() -> Self { Self { state: None, @@ -32,7 +32,9 @@ impl SbotStatus { downtime: None, } } +} +impl SbotStatus { /// Retrieve statistics for the go-sbot systemd process by querying `systemctl`. pub fn read() -> Result { let mut status = SbotStatus::default(); @@ -103,40 +105,64 @@ impl SbotStatus { } } +/// go-sbot configuration parameters. #[derive(Debug, Serialize, Deserialize)] +#[serde(default)] pub struct SbotConfig { // TODO: maybe define as a Path type? /// Directory path for the log and indexes. - repo: Option, + pub repo: String, /// Directory path for writing debug output. - debugdir: Option, + pub debugdir: String, /// Secret-handshake app-key (aka. network key). - shscap: Option, + pub shscap: String, /// HMAC hash used to sign messages. - hmac: Option, + pub hmac: String, /// Replication hops (1: friends, 2: friends of friends). - hops: Option, + pub hops: u8, /// Address to listen on. - lis: Option, + pub lis: String, /// Address to listen on for WebSocket connections. - wslis: Option, + pub wslis: String, /// Address to for metrics and pprof HTTP server. - debuglis: Option, + pub debuglis: String, /// Enable sending local UDP broadcasts. - localadv: Option, + pub localadv: bool, /// Enable listening for UDP broadcasts and connecting. - localdiscov: Option, + pub localdiscov: bool, /// Enable syncing by using epidemic-broadcast-trees (EBT). - #[serde(rename = "enable-ebt")] - enable_ebt: Option, + #[serde(rename(serialize = "enable_ebt", deserialize = "enable-ebt"))] + pub enable_ebt: bool, /// Bypass graph auth and fetch remote's feed (useful for pubs that are restoring their data /// from peer; user beware - caveats about). - promisc: Option, + pub promisc: bool, /// Disable the UNIX socket RPC interface. - nounixsock: Option, + pub nounixsock: bool, +} + +/// Default configuration values for go-sbot. +impl Default for SbotConfig { + fn default() -> Self { + Self { + repo: ".ssb-go".to_string(), + debugdir: "".to_string(), + shscap: "1KHLiKZvAvjbY1ziZEHMXawbCEIM6qwjCDm3VYRan/s=".to_string(), + hmac: "".to_string(), + hops: 1, + lis: ":8008".to_string(), + wslis: ":8989".to_string(), + debuglis: "localhost:6078".to_string(), + localadv: false, + localdiscov: false, + enable_ebt: false, + promisc: false, + nounixsock: false, + } + } } impl SbotConfig { + /// Read the go-sbot `config.toml` file from file and deserialize into `SbotConfig`. pub fn read() -> Result { // determine path of user's home directory let mut config_path = dirs::home_dir().ok_or(PeachError::HomeDir)?; @@ -149,7 +175,10 @@ impl SbotConfig { Ok(config) } + /// Write the given `SbotConfig` to the go-sbot `config.toml` file. pub fn write(config: SbotConfig) -> Result<(), PeachError> { + let repo_comment = "# For details about go-sbot configuration, please visit the repo: https://github.com/cryptoscope/ssb\n".to_string(); + // convert the provided `SbotConfig` instance to a string let config_string = toml::to_string(&config)?; @@ -160,6 +189,9 @@ impl SbotConfig { // open config file for writing let mut file = File::create(config_path)?; + // write the repo comment to file + write!(file, "{}", repo_comment)?; + // write the config string to file write!(file, "{}", config_string)?; -- 2.40.1 From 89b502be2548362a85192a1a61378028f11c0215 Mon Sep 17 00:00:00 2001 From: glyph Date: Wed, 2 Feb 2022 14:14:12 +0200 Subject: [PATCH 05/10] add configuration routes for the sbot --- peach-web/src/router.rs | 2 + peach-web/src/routes/scuttlebutt.rs | 24 +-- peach-web/src/routes/settings/scuttlebutt.rs | 158 ++++++++++++++++-- .../scuttlebutt/configure_sbot.html.tera | 31 ++-- 4 files changed, 174 insertions(+), 41 deletions(-) diff --git a/peach-web/src/router.rs b/peach-web/src/router.rs index c4a017e..d8cf8cb 100644 --- a/peach-web/src/router.rs +++ b/peach-web/src/router.rs @@ -51,6 +51,8 @@ pub fn mount_peachpub_routes(rocket: Rocket) -> Rocket { routes![ ssb_settings_menu, configure_sbot, + configure_sbot_default, + configure_sbot_post, restart_sbot, start_sbot, stop_sbot diff --git a/peach-web/src/routes/scuttlebutt.rs b/peach-web/src/routes/scuttlebutt.rs index ede8699..178a0ec 100644 --- a/peach-web/src/routes/scuttlebutt.rs +++ b/peach-web/src/routes/scuttlebutt.rs @@ -93,11 +93,7 @@ pub struct Post { /// Publish a public Scuttlebutt post. Redirects to profile page of the PeachCloud local identity with a flash message describing the outcome of the action (may be successful or unsuccessful). #[post("/publish", data = "")] -pub fn publish( - post: Form, - flash: Option, - _auth: Authenticated, -) -> Flash { +pub fn publish(post: Form, _auth: Authenticated) -> Flash { let post_text = &post.text; // perform the sbotcli publish action using post_text // if successful, redirect to home profile page and flash "success" @@ -119,11 +115,7 @@ pub struct PublicKey { /// Follow a Scuttlebutt profile specified by the given public key. Redirects to the appropriate profile page with a flash message describing the outcome of the action (may be successful or unsuccessful). #[post("/follow", data = "")] -pub fn follow( - pub_key: Form, - flash: Option, - _auth: Authenticated, -) -> Flash { +pub fn follow(pub_key: Form, _auth: Authenticated) -> Flash { let public_key = &pub_key.key; // perform the sbotcli follow action using &pub_key.0 // if successful, redirect to profile page with provided public key and flash "success" @@ -138,11 +130,7 @@ pub fn follow( /// Unfollow a Scuttlebutt profile specified by the given public key. Redirects to the appropriate profile page with a flash message describing the outcome of the action (may be successful or unsuccessful). #[post("/unfollow", data = "")] -pub fn unfollow( - pub_key: Form, - flash: Option, - _auth: Authenticated, -) -> Flash { +pub fn unfollow(pub_key: Form, _auth: Authenticated) -> Flash { let public_key = &pub_key.key; // perform the sbotcli unfollow action using &pub_key.0 // if successful, redirect to profile page with provided public key and flash "success" @@ -157,11 +145,7 @@ pub fn unfollow( /// Block a Scuttlebutt profile specified by the given public key. Redirects to the appropriate profile page with a flash message describing the outcome of the action (may be successful or unsuccessful). #[post("/block", data = "")] -pub fn block( - pub_key: Form, - flash: Option, - _auth: Authenticated, -) -> Flash { +pub fn block(pub_key: Form, _auth: Authenticated) -> Flash { let public_key = &pub_key.key; // perform the sbotcli block action using &pub_key.0 // if successful, redirect to profile page with provided public key and flash "success" diff --git a/peach-web/src/routes/settings/scuttlebutt.rs b/peach-web/src/routes/settings/scuttlebutt.rs index b663106..ca76714 100644 --- a/peach-web/src/routes/settings/scuttlebutt.rs +++ b/peach-web/src/routes/settings/scuttlebutt.rs @@ -3,26 +3,64 @@ use std::{ process::{Command, Output}, }; -use log::info; -use peach_stats::sbot; +use log::{info, warn}; +use peach_lib::sbot::{SbotConfig, SbotStatus}; use rocket::{ - get, + form::{Form, FromForm}, + get, post, request::FlashMessage, response::{Flash, Redirect}, + serde::Deserialize, }; use rocket_dyn_templates::{tera::Context, Template}; use crate::routes::authentication::Authenticated; +#[derive(Debug, Deserialize, FromForm)] +pub struct SbotConfigForm { + /// Directory path for the log and indexes. + repo: String, + /// Directory path for writing debug output. + debugdir: String, + /// Secret-handshake app-key (aka. network key). + shscap: String, + /// HMAC hash used to sign messages. + hmac: String, + /// Replication hops (1: friends, 2: friends of friends). + hops: u8, + /// IP address to listen on. + lis_ip: String, + /// Port to listen on. + lis_port: String, + /// Address to listen on for WebSocket connections. + wslis: String, + /// Address to for metrics and pprof HTTP server. + debuglis: String, + /// Enable sending local UDP broadcasts. + localadv: bool, + /// Enable listening for UDP broadcasts and connecting. + localdiscov: bool, + /// Enable syncing by using epidemic-broadcast-trees (EBT). + enable_ebt: bool, + /// Bypass graph auth and fetch remote's feed (useful for pubs that are restoring their data + /// from peer; user beware - caveats about). + promisc: bool, + /// Disable the UNIX socket RPC interface. + nounixsock: bool, + /// Run the go-sbot on system start-up (systemd service enabled). + startup: bool, +} + // HELPERS AND ROUTES FOR /settings/scuttlebutt /// Scuttlebutt settings menu. #[get("/")] pub fn ssb_settings_menu(flash: Option, _auth: Authenticated) -> Template { + // retrieve go-sbot systemd process status + let sbot_status = SbotStatus::read().ok(); + let mut context = Context::new(); - // retrieve go-sbot systemd process stats - let sbot_stats = sbot::sbot_stats().ok(); - context.insert("sbot_stats", &sbot_stats); + context.insert("sbot_status", &sbot_status); context.insert("back", &Some("/settings".to_string())); context.insert("title", &Some("Scuttlebutt Settings".to_string())); @@ -37,9 +75,18 @@ pub fn ssb_settings_menu(flash: Option, _auth: Authenticated) -> T /// Sbot configuration page (includes form for updating configuration parameters). #[get("/configure")] pub fn configure_sbot(flash: Option, _auth: Authenticated) -> Template { + // retrieve go-sbot systemd process status + let sbot_status = SbotStatus::read().ok(); + let run_on_startup = sbot_status.map(|status| status.boot_state); + + // retrieve sbot config parameters + let sbot_config = SbotConfig::read().ok(); + let mut context = Context::new(); context.insert("back", &Some("/settings/scuttlebutt".to_string())); context.insert("title", &Some("Sbot Configuration".to_string())); + context.insert("sbot_config", &sbot_config); + context.insert("run_on_startup", &Some(run_on_startup)); if let Some(flash) = flash { context.insert("flash_name", &Some(flash.kind().to_string())); @@ -49,12 +96,90 @@ pub fn configure_sbot(flash: Option, _auth: Authenticated) -> Temp Template::render("settings/scuttlebutt/configure_sbot", &context.into_json()) } +// TODO: consider using `Contextual` here to collect all submitted form +// fields to re-render forms with submitted values on error + +/// Receive the sbot configuration form data and save it to file. +#[post("/configure", data = "")] +pub fn configure_sbot_post(config: Form, _auth: Authenticated) -> Flash { + // call `into_inner()` to take ownership of the `config` data + let owned_config = config.into_inner(); + + // concat the ip and port for listen address + let lis = format!("{}:{}", owned_config.lis_ip, owned_config.lis_port); + + // instantiate `SbotConfig` from form data + let config = SbotConfig { + lis, + hops: owned_config.hops, + repo: owned_config.repo, + debugdir: owned_config.debugdir, + shscap: owned_config.shscap, + localadv: owned_config.localadv, + localdiscov: owned_config.localdiscov, + hmac: owned_config.hmac, + wslis: owned_config.wslis, + debuglis: owned_config.debuglis, + enable_ebt: owned_config.enable_ebt, + promisc: owned_config.promisc, + nounixsock: owned_config.nounixsock, + }; + + match owned_config.startup { + true => { + info!("Enabling go-sbot.service"); + match systemctl_sbot_cmd("enable") { + Err(e) => warn!("Failed to enable go-sbot.service: {}", e), + _ => (), + } + } + false => { + info!("Disabling go-sbot.service"); + match systemctl_sbot_cmd("disable") { + Err(e) => warn!("Failed to disable go-sbot.service: {}", e), + _ => (), + } + } + }; + + // write config to file + match SbotConfig::write(config) { + Ok(_) => Flash::success( + Redirect::to("/settings/scuttlebutt/configure"), + "Updated configuration", + ), + Err(e) => Flash::error( + Redirect::to("/settings/scuttlebutt/configure"), + format!("Failed to update configuration: {}", e), + ), + } +} + +/// Set default configuration parameters for the go-sbot and save them to file. +#[get("/configure/default")] +pub fn configure_sbot_default(_auth: Authenticated) -> Flash { + let default_config = SbotConfig::default(); + + // write default config to file + match SbotConfig::write(default_config) { + Ok(_) => Flash::success( + Redirect::to("/settings/scuttlebutt/configure"), + "Restored default configuration", + ), + Err(e) => Flash::error( + Redirect::to("/settings/scuttlebutt/configure"), + format!("Failed to restore default configuration: {}", e), + ), + } +} + /// 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. #[get("/start")] pub fn start_sbot(_auth: Authenticated) -> Flash { - match start_sbot_cmd() { + info!("Starting go-sbot.service"); + match systemctl_sbot_cmd("start") { Ok(_) => Flash::success( Redirect::to("/settings/scuttlebutt"), "Sbot process has been started", @@ -71,7 +196,8 @@ pub fn start_sbot(_auth: Authenticated) -> Flash { /// the attempt via a flash message. #[get("/stop")] pub fn stop_sbot(_auth: Authenticated) -> Flash { - match stop_sbot_cmd() { + info!("Stopping go-sbot.service"); + match systemctl_sbot_cmd("stop") { Ok(_) => Flash::success( Redirect::to("/settings/scuttlebutt"), "Sbot process has been stopped", @@ -88,10 +214,10 @@ pub fn stop_sbot(_auth: Authenticated) -> Flash { /// the attempt via a flash message. #[get("/restart")] pub fn restart_sbot(_auth: Authenticated) -> Flash { - // try to stop the process - match stop_sbot_cmd() { + info!("Restarting go-sbot.service"); + match systemctl_sbot_cmd("stop") { // if stop was successful, try to start the process - Ok(_) => match start_sbot_cmd() { + Ok(_) => match systemctl_sbot_cmd("start") { Ok(_) => Flash::success( Redirect::to("/settings/scuttlebutt"), "Sbot process has been restarted", @@ -111,6 +237,16 @@ pub fn restart_sbot(_auth: Authenticated) -> Flash { // HELPER FUNCTIONS +/// Executes a systemctl command for the go-sbot.service process. +pub fn systemctl_sbot_cmd(cmd: &str) -> io::Result { + //info!("Disabling go-sbot.service"); + Command::new("systemctl") + .arg("--user") + .arg(cmd) + .arg("go-sbot.service") + .output() +} + /// Executes a systemctl disable command for the go-sbot.service process. pub fn disable_sbot_cmd() -> io::Result { info!("Disabling go-sbot.service"); diff --git a/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera b/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera index 40a597e..617bb32 100644 --- a/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera +++ b/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera @@ -14,7 +14,7 @@ {%- endif -%}
- +
@@ -34,37 +34,48 @@
- +
- +

- +

- +

- +

- +

- +

- - + + + + + + + + + + + Restore Defaults + + {% include "snippets/flash_message" %}
{%- endblock card -%} -- 2.40.1 From f0d972f46be5371d5d8ac77627ab8e7b2fc3a5f7 Mon Sep 17 00:00:00 2001 From: glyph Date: Wed, 2 Feb 2022 14:48:39 +0200 Subject: [PATCH 06/10] add repair parameter to SbotConfig --- peach-lib/src/sbot.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/peach-lib/src/sbot.rs b/peach-lib/src/sbot.rs index b784017..d08e6db 100644 --- a/peach-lib/src/sbot.rs +++ b/peach-lib/src/sbot.rs @@ -138,6 +138,8 @@ pub struct SbotConfig { pub promisc: bool, /// Disable the UNIX socket RPC interface. pub nounixsock: bool, + /// Attempt to repair the filesystem before starting. + pub repair: bool, } /// Default configuration values for go-sbot. @@ -157,6 +159,7 @@ impl Default for SbotConfig { enable_ebt: false, promisc: false, nounixsock: false, + repair: false, } } } -- 2.40.1 From 33604ac0dc5ff3f0a8b826d294d32f552844f9ef Mon Sep 17 00:00:00 2001 From: glyph Date: Wed, 2 Feb 2022 14:49:14 +0200 Subject: [PATCH 07/10] change form width for better mobile styling --- peach-web/static/css/peachcloud.css | 15 ++++++++++ .../settings/admin/change_password.html.tera | 30 +++++++++---------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/peach-web/static/css/peachcloud.css b/peach-web/static/css/peachcloud.css index 93936d4..99ec9d2 100644 --- a/peach-web/static/css/peachcloud.css +++ b/peach-web/static/css/peachcloud.css @@ -15,6 +15,7 @@ * - HTML * - FLASH MESSAGE * - FONTS + * - FORMS * - ICONS * - INPUTS * - LABELS @@ -596,6 +597,20 @@ html { color: var(--danger); } +/* + * FORMS + */ + +form { + width: 90%; +} + +@media only screen and (min-width: 600px) { + form { + width: 100%; + } +} + /* * ICONS */ diff --git a/peach-web/templates/settings/admin/change_password.html.tera b/peach-web/templates/settings/admin/change_password.html.tera index fe30e82..604fe46 100644 --- a/peach-web/templates/settings/admin/change_password.html.tera +++ b/peach-web/templates/settings/admin/change_password.html.tera @@ -2,21 +2,19 @@ {%- block card %}
-
-
- - - - - - -
- - Cancel -
-
- - {% include "snippets/flash_message" %} -
+
+ + + + + + +
+ + Cancel +
+
+ + {% include "snippets/flash_message" %}
{%- endblock card -%} -- 2.40.1 From a46b58b206279fe9e54df6c7ad96835e4d979575 Mon Sep 17 00:00:00 2001 From: glyph Date: Wed, 2 Feb 2022 14:49:36 +0200 Subject: [PATCH 08/10] add repair fs config option --- peach-web/src/routes/settings/scuttlebutt.rs | 44 ++----------------- .../scuttlebutt/configure_sbot.html.tera | 25 ++++++----- .../settings/scuttlebutt/menu.html.tera | 1 - 3 files changed, 17 insertions(+), 53 deletions(-) diff --git a/peach-web/src/routes/settings/scuttlebutt.rs b/peach-web/src/routes/settings/scuttlebutt.rs index ca76714..6ce5758 100644 --- a/peach-web/src/routes/settings/scuttlebutt.rs +++ b/peach-web/src/routes/settings/scuttlebutt.rs @@ -49,6 +49,8 @@ pub struct SbotConfigForm { nounixsock: bool, /// Run the go-sbot on system start-up (systemd service enabled). startup: bool, + /// Attempt to repair the filesystem before starting. + repair: bool, } // HELPERS AND ROUTES FOR /settings/scuttlebutt @@ -123,6 +125,7 @@ pub fn configure_sbot_post(config: Form, _auth: Authenticated) - enable_ebt: owned_config.enable_ebt, promisc: owned_config.promisc, nounixsock: owned_config.nounixsock, + repair: owned_config.repair, }; match owned_config.startup { @@ -239,50 +242,9 @@ pub fn restart_sbot(_auth: Authenticated) -> Flash { /// Executes a systemctl command for the go-sbot.service process. pub fn systemctl_sbot_cmd(cmd: &str) -> io::Result { - //info!("Disabling go-sbot.service"); Command::new("systemctl") .arg("--user") .arg(cmd) .arg("go-sbot.service") .output() } - -/// Executes a systemctl disable command for the go-sbot.service process. -pub fn disable_sbot_cmd() -> io::Result { - info!("Disabling go-sbot.service"); - Command::new("systemctl") - .arg("--user") - .arg("disable") - .arg("go-sbot.service") - .output() -} - -/// Executes a systemctl enable command for the go-sbot.service process. -pub fn enable_sbot_cmd() -> io::Result { - info!("Enabling go-sbot.service"); - Command::new("systemctl") - .arg("--user") - .arg("enable") - .arg("go-sbot.service") - .output() -} - -/// Executes a systemctl start command for the go-sbot.service process. -pub fn start_sbot_cmd() -> io::Result { - info!("Starting go-sbot.service"); - Command::new("systemctl") - .arg("--user") - .arg("start") - .arg("go-sbot.service") - .output() -} - -/// Executes a systemctl stop command for the go-sbot.service process. -pub fn stop_sbot_cmd() -> io::Result { - info!("Stopping go-sbot.service"); - Command::new("systemctl") - .arg("--user") - .arg("stop") - .arg("go-sbot.service") - .output() -} diff --git a/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera b/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera index 617bb32..94f846f 100644 --- a/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera +++ b/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera @@ -14,8 +14,8 @@ {%- endif -%}
-
-
+ +
@@ -31,7 +31,7 @@

-
+
@@ -42,25 +42,28 @@

-
+

-
+

-
+
-
+

- -
+ +

- -
+ +
+
+ +

diff --git a/peach-web/templates/settings/scuttlebutt/menu.html.tera b/peach-web/templates/settings/scuttlebutt/menu.html.tera index 346b89a..a2f5513 100644 --- a/peach-web/templates/settings/scuttlebutt/menu.html.tera +++ b/peach-web/templates/settings/scuttlebutt/menu.html.tera @@ -12,7 +12,6 @@ Start Sbot {% endif %} Check Filesystem - Repair Filesystem Remove Blocked Feeds
-- 2.40.1 From dfc173d9417bcee9e961ec85190624b7646dfd71 Mon Sep 17 00:00:00 2001 From: glyph Date: Wed, 2 Feb 2022 16:20:30 +0200 Subject: [PATCH 09/10] add clarity about need for restart after config update --- peach-web/src/routes/settings/scuttlebutt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peach-web/src/routes/settings/scuttlebutt.rs b/peach-web/src/routes/settings/scuttlebutt.rs index 6ce5758..e8c53a7 100644 --- a/peach-web/src/routes/settings/scuttlebutt.rs +++ b/peach-web/src/routes/settings/scuttlebutt.rs @@ -149,7 +149,7 @@ pub fn configure_sbot_post(config: Form, _auth: Authenticated) - match SbotConfig::write(config) { Ok(_) => Flash::success( Redirect::to("/settings/scuttlebutt/configure"), - "Updated configuration", + "Configuration updated. The pub must be (re)started for the changes to take effect", ), Err(e) => Flash::error( Redirect::to("/settings/scuttlebutt/configure"), -- 2.40.1 From f6292407d036f8e5109f946c44619cd72f4befcb Mon Sep 17 00:00:00 2001 From: glyph Date: Wed, 2 Feb 2022 16:32:10 +0200 Subject: [PATCH 10/10] add save and restart option for config form --- peach-web/src/routes/settings/scuttlebutt.rs | 79 +++++++++++++------ .../scuttlebutt/configure_sbot.html.tera | 1 + 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/peach-web/src/routes/settings/scuttlebutt.rs b/peach-web/src/routes/settings/scuttlebutt.rs index e8c53a7..bf100ad 100644 --- a/peach-web/src/routes/settings/scuttlebutt.rs +++ b/peach-web/src/routes/settings/scuttlebutt.rs @@ -102,8 +102,12 @@ pub fn configure_sbot(flash: Option, _auth: Authenticated) -> Temp // fields to re-render forms with submitted values on error /// Receive the sbot configuration form data and save it to file. -#[post("/configure", data = "")] -pub fn configure_sbot_post(config: Form, _auth: Authenticated) -> Flash { +#[post("/configure?", data = "")] +pub fn configure_sbot_post( + restart: bool, + config: Form, + _auth: Authenticated, +) -> Flash { // call `into_inner()` to take ownership of the `config` data let owned_config = config.into_inner(); @@ -147,10 +151,26 @@ pub fn configure_sbot_post(config: Form, _auth: Authenticated) - // write config to file match SbotConfig::write(config) { - Ok(_) => Flash::success( - Redirect::to("/settings/scuttlebutt/configure"), - "Configuration updated. The pub must be (re)started for the changes to take effect", - ), + Ok(_) => { + // if `restart` query parameter is `true`, attempt sbot process (re)start + if restart { + restart_sbot_process( + // redirect url + "/settings/scuttlebutt/configure", + // success flash msg + "Updated configuration and restarted the sbot process", + // first failed flash msg + "Updated configuration but failed to start the sbot process", + // second failed flash msg + "Updated configuration but failed to stop the sbot process", + ) + } else { + Flash::success( + Redirect::to("/settings/scuttlebutt/configure"), + "Updated configuration", + ) + } + } Err(e) => Flash::error( Redirect::to("/settings/scuttlebutt/configure"), format!("Failed to update configuration: {}", e), @@ -217,25 +237,12 @@ pub fn stop_sbot(_auth: Authenticated) -> Flash { /// the attempt via a flash message. #[get("/restart")] pub fn restart_sbot(_auth: Authenticated) -> Flash { - info!("Restarting go-sbot.service"); - match systemctl_sbot_cmd("stop") { - // if stop was successful, try to start the process - Ok(_) => match systemctl_sbot_cmd("start") { - Ok(_) => Flash::success( - Redirect::to("/settings/scuttlebutt"), - "Sbot process has been restarted", - ), - - Err(_) => Flash::error( - Redirect::to("/settings/scuttlebutt"), - "Failed to start the sbot process", - ), - }, - Err(_) => Flash::error( - Redirect::to("/settings/scuttlebutt"), - "Failed to stop the sbot process", - ), - } + restart_sbot_process( + "/settings/scuttlebutt", + "Sbot process has been restarted", + "Failed to start the sbot process", + "Failed to stop the sbot process", + ) } // HELPER FUNCTIONS @@ -248,3 +255,25 @@ pub fn systemctl_sbot_cmd(cmd: &str) -> io::Result { .arg("go-sbot.service") .output() } + +/// Executes a systemctl stop command followed by start command. +/// Returns a redirect with a flash message stating the output of the restart attempt. +fn restart_sbot_process( + redirect_url: &str, + success_msg: &str, + start_failed_msg: &str, + stop_failed_msg: &str, +) -> Flash { + let url = redirect_url.to_string(); + + info!("Restarting go-sbot.service"); + match systemctl_sbot_cmd("stop") { + // if stop was successful, try to start the process + Ok(_) => match systemctl_sbot_cmd("start") { + Ok(_) => Flash::success(Redirect::to(url), success_msg), + + Err(e) => Flash::error(Redirect::to(url), format!("{}: {}", start_failed_msg, e)), + }, + Err(e) => Flash::error(Redirect::to(url), format!("{}: {}", stop_failed_msg, e)), + } +} diff --git a/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera b/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera index 94f846f..a791519 100644 --- a/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera +++ b/peach-web/templates/settings/scuttlebutt/configure_sbot.html.tera @@ -76,6 +76,7 @@ + Restore Defaults -- 2.40.1