diff --git a/peach-web/src/main.rs b/peach-web/src/main.rs index e86c2f5..8bd8977 100644 --- a/peach-web/src/main.rs +++ b/peach-web/src/main.rs @@ -132,6 +132,10 @@ fn main() { Response::html(routes::settings::menu::build()) }, + (GET) (/settings/scuttlebutt) => { + Response::html(routes::settings::scuttlebutt::menu::build()) + }, + // The code block is called if none of the other blocks matches the request. // We return an empty response with a 404 status code. _ => Response::empty_404() diff --git a/peach-web/src/routes/settings/menu.rs b/peach-web/src/routes/settings/menu.rs index d7f520c..121aefd 100644 --- a/peach-web/src/routes/settings/menu.rs +++ b/peach-web/src/routes/settings/menu.rs @@ -4,7 +4,7 @@ use crate::{templates, CONFIG}; // TODO: flash message implementation for rouille // -/// Main settings menu template builder. +/// Settings menu template builder. pub fn build() -> PreEscaped { let menu_template = html! { (PreEscaped("")) diff --git a/peach-web/src/routes/settings/mod.rs b/peach-web/src/routes/settings/mod.rs index b9a9b54..0b2a088 100644 --- a/peach-web/src/routes/settings/mod.rs +++ b/peach-web/src/routes/settings/mod.rs @@ -2,5 +2,5 @@ //pub mod dns; pub mod menu; //pub mod network; -//pub mod scuttlebutt; +pub mod scuttlebutt; //pub mod theme; diff --git a/peach-web/src/routes/settings/scuttlebutt.rs b/peach-web/src/routes/settings/scuttlebutt.rs deleted file mode 100644 index 54b66f7..0000000 --- a/peach-web/src/routes/settings/scuttlebutt.rs +++ /dev/null @@ -1,285 +0,0 @@ -use std::{ - io, - process::{Command, Output}, -}; - -use log::{info, warn}; -use peach_lib::sbot::{SbotConfig, SbotStatus}; -use rocket::{ - form::{Form, FromForm}, - get, post, - request::FlashMessage, - response::{Flash, Redirect}, -}; -use rocket_dyn_templates::{tera::Context, Template}; - -use crate::routes::authentication::Authenticated; -use crate::utils; - -#[derive(Debug, 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, - /// Attempt to repair the filesystem before starting. - repair: bool, -} - -// HELPERS AND ROUTES FOR /settings/scuttlebutt - -/// Scuttlebutt settings menu. -#[get("/")] -pub fn ssb_settings_menu(flash: Option, _auth: Authenticated) -> Template { - // retrieve current ui theme - let theme = utils::get_theme(); - - // retrieve go-sbot systemd process status - let sbot_status = SbotStatus::read().ok(); - - let mut context = Context::new(); - context.insert("theme", &theme); - context.insert("sbot_status", &sbot_status); - context.insert("back", &Some("/settings".to_string())); - context.insert("title", &Some("Scuttlebutt Settings".to_string())); - - if let Some(flash) = flash { - context.insert("flash_name", &Some(flash.kind().to_string())); - context.insert("flash_msg", &Some(flash.message().to_string())); - }; - - Template::render("settings/scuttlebutt/menu", &context.into_json()) -} - -/// Sbot configuration page (includes form for updating configuration parameters). -#[get("/configure")] -pub fn configure_sbot(flash: Option, _auth: Authenticated) -> Template { - // retrieve current ui theme - let theme = utils::get_theme(); - - // 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("theme", &theme); - 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())); - context.insert("flash_msg", &Some(flash.message().to_string())); - }; - - 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( - restart: bool, - 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, - repair: owned_config.repair, - }; - - match owned_config.startup { - true => { - info!("Enabling go-sbot.service"); - if let Err(e) = systemctl_sbot_cmd("enable") { - warn!("Failed to enable go-sbot.service: {}", e) - } - } - false => { - info!("Disabling go-sbot.service"); - if let Err(e) = systemctl_sbot_cmd("disable") { - warn!("Failed to disable go-sbot.service: {}", e) - } - } - }; - - // write config to file - match SbotConfig::write(config) { - 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), - ), - } -} - -/// 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 { - info!("Starting go-sbot.service"); - match systemctl_sbot_cmd("start") { - Ok(_) => Flash::success( - Redirect::to("/settings/scuttlebutt"), - "Sbot process has been started", - ), - Err(_) => Flash::error( - Redirect::to("/settings/scuttlebutt"), - "Failed to start the sbot process", - ), - } -} - -/// 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. -#[get("/stop")] -pub fn stop_sbot(_auth: Authenticated) -> Flash { - info!("Stopping go-sbot.service"); - match systemctl_sbot_cmd("stop") { - Ok(_) => Flash::success( - Redirect::to("/settings/scuttlebutt"), - "Sbot process has been stopped", - ), - Err(_) => Flash::error( - Redirect::to("/settings/scuttlebutt"), - "Failed to stop the sbot process", - ), - } -} - -/// 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. -#[get("/restart")] -pub fn restart_sbot(_auth: Authenticated) -> Flash { - restart_sbot_process( - "/settings/scuttlebutt", - "Sbot process has been restarted", - "Failed to start the sbot process", - "Failed to stop the sbot process", - ) -} - -// 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() -} - -/// 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/src/routes/settings/scuttlebutt/menu.rs b/peach-web/src/routes/settings/scuttlebutt/menu.rs new file mode 100644 index 0000000..c8b35df --- /dev/null +++ b/peach-web/src/routes/settings/scuttlebutt/menu.rs @@ -0,0 +1,51 @@ +use maud::{html, PreEscaped}; +use peach_lib::sbot::SbotStatus; + +use crate::templates; + +// TODO: flash message implementation for rouille +// + +/// Read the status of the go-sbot service and render buttons accordingly. +fn render_process_buttons() -> PreEscaped { + // retrieve go-sbot systemd process status + let sbot_status = SbotStatus::read(); + + html! { + // render the stop and restart buttons if sbot process is currently active + @if let Ok(status) = sbot_status { + @if status.state == Some("active".to_string()) { + a id="stop" class="button button-primary center" href="/settings/scuttlebutt/stop" title="Stop Sbot" { "Stop Sbot" } + a id="restart" class="button button-primary center" href="/settings/scuttlebutt/restart" title="Restart Sbot" { "Restart Sbot" } + // render the start button if sbot process is currently inactive + } @else { + a id="start" class="button button-primary center" href="/settings/scuttlebutt/start" title="Start Sbot" { "Start Sbot" } + } + // render the start button if an error was returned by the status query + } @else { + a id="start" class="button button-primary center" href="/settings/scuttlebutt/start" title="Start Sbot" { "Start Sbot" } + } + } +} + +/// Scuttlebutt settings menu template builder. +pub fn build() -> PreEscaped { + let menu_template = html! { + (PreEscaped("")) + div class="card center" { + (PreEscaped("")) + div id="settingsButtons" { + a id="configureSbot" class="button button-primary center" href="/settings/scuttlebutt/configure" title="Configure Sbot" { "Configure Sbot" } + // conditionally render the start / stop / restart buttons + (render_process_buttons()) + } + } + }; + + // wrap the nav bars around the settings menu template content + // parameters are template, title and back url + let body = templates::nav::build(menu_template, "Scuttlebutt Settings", Some("/settings")); + + // render the base template with the provided body + templates::base::build(body) +} diff --git a/peach-web/src/routes/settings/scuttlebutt/mod.rs b/peach-web/src/routes/settings/scuttlebutt/mod.rs new file mode 100644 index 0000000..b9a0e3e --- /dev/null +++ b/peach-web/src/routes/settings/scuttlebutt/mod.rs @@ -0,0 +1 @@ +pub mod menu;