From 7fdf88eaa862c840fec8ce7fe08143d748cdd84d Mon Sep 17 00:00:00 2001 From: glyph Date: Fri, 4 Mar 2022 10:53:49 +0200 Subject: [PATCH 01/14] fix and improve all login and password-related workflows --- peach-lib/Cargo.toml | 3 ++ peach-lib/issues_to_fix | 23 ++++++++ peach-lib/src/config_manager.rs | 7 ++- peach-lib/src/error.rs | 20 +++---- peach-lib/src/password_utils.rs | 38 +++++++++++-- peach-web/src/router.rs | 5 +- peach-web/src/routes/authentication.rs | 29 +++++----- peach-web/src/routes/settings/admin.rs | 39 ++++---------- peach-web/templates/login.html.tera | 23 ++++---- .../settings/admin/add_admin.html.tera | 17 ------ .../settings/admin/change_password.html.tera | 18 ++++--- .../settings/admin/configure_admin.html.tera | 44 ++++++++------- .../settings/admin/forgot_password.html.tera | 13 ++--- .../settings/admin/reset_password.html.tera | 53 ++++++------------- 14 files changed, 169 insertions(+), 163 deletions(-) create mode 100644 peach-lib/issues_to_fix delete mode 100644 peach-web/templates/settings/admin/add_admin.html.tera diff --git a/peach-lib/Cargo.toml b/peach-lib/Cargo.toml index 4d4fe38..fc51192 100644 --- a/peach-lib/Cargo.toml +++ b/peach-lib/Cargo.toml @@ -5,9 +5,12 @@ authors = ["Andrew Reid "] edition = "2018" [dependencies] +async-std = "1.10.0" chrono = "0.4.19" dirs = "4.0" fslock="0.1.6" +#golgi = { git = "https://git.coopcloud.tech/golgi-ssb/golgi" } +golgi = { path = "../../../playground/rust/golgi" } jsonrpc-client-core = "0.5" jsonrpc-client-http = "0.5" jsonrpc-core = "8.0.1" diff --git a/peach-lib/issues_to_fix b/peach-lib/issues_to_fix new file mode 100644 index 0000000..fc8d8d5 --- /dev/null +++ b/peach-lib/issues_to_fix @@ -0,0 +1,23 @@ + + + - permissions for /var/lib/peachcloud + - everything fails if we don't have write permissions + - can't write config + + - configure admin (add new admin) + - isn't persisted to config.yml + - on second try, it is persisted + + - password reset via ssb pm is now working + - it sends a temporary password but says nothing about username + - what is the default username? + - why do we even have a username? + + - login with temporary password fails + - "Invalid password: Password error: hash value in YAML configuration file is empty." + + - things are generally working now :) + + - for all form inputs: + - use a proper label (not just a placeholder) + - login diff --git a/peach-lib/src/config_manager.rs b/peach-lib/src/config_manager.rs index e3894ab..dfffd42 100644 --- a/peach-lib/src/config_manager.rs +++ b/peach-lib/src/config_manager.rs @@ -1,12 +1,14 @@ //! Interfaces for writing and reading PeachCloud configurations, stored in yaml. //! -//! Different PeachCloud microservices import peach-lib, so that they can share this interface. +//! Different PeachCloud microservices import peach-lib, so that they can share +//! this interface. //! //! The configuration file is located at: "/var/lib/peachcloud/config.yml" use std::fs; use fslock::LockFile; +use log::debug; use serde::{Deserialize, Serialize}; use crate::error::PeachError; @@ -72,6 +74,7 @@ pub fn load_peach_config() -> Result { let peach_config_exists = std::path::Path::new(YAML_PATH).exists(); let peach_config: PeachConfig = if !peach_config_exists { + debug!("Loading peach config: {} does not exist", YAML_PATH); PeachConfig { external_domain: "".to_string(), dyn_domain: "".to_string(), @@ -87,6 +90,7 @@ pub fn load_peach_config() -> Result { } // otherwise we load peach config from disk else { + debug!("Loading peach config: {} exists", YAML_PATH); let contents = fs::read_to_string(YAML_PATH).map_err(|source| PeachError::Read { source, path: YAML_PATH.to_string(), @@ -177,6 +181,7 @@ pub fn set_admin_password_hash(password_hash: &str) -> Result Result { let peach_config = load_peach_config()?; + debug!("Admin password hash: {}", peach_config.admin_password_hash); if !peach_config.admin_password_hash.is_empty() { Ok(peach_config.admin_password_hash) } else { diff --git a/peach-lib/src/error.rs b/peach-lib/src/error.rs index ac4ef97..ba396b1 100644 --- a/peach-lib/src/error.rs +++ b/peach-lib/src/error.rs @@ -61,11 +61,8 @@ pub enum PeachError { /// Represents a failure to parse or compile a regular expression. Regex(regex::Error), - /// Represents a failure to successfully execute an sbot command. - SbotCli { - /// The `stderr` output from the sbot command. - msg: String, - }, + /// Represents a failure to successfully execute an sbot command (via golgi). + Sbot(String), /// Represents a failure to serialize or deserialize JSON. SerdeJson(serde_json::error::Error), @@ -117,7 +114,7 @@ impl std::error::Error for PeachError { PeachError::PasswordNotSet => None, PeachError::Read { ref source, .. } => Some(source), PeachError::Regex(_) => None, - PeachError::SbotCli { .. } => None, + PeachError::Sbot(_) => None, PeachError::SerdeJson(_) => None, PeachError::SerdeYaml(_) => None, PeachError::SsbAdminIdNotFound { .. } => None, @@ -153,22 +150,19 @@ impl std::fmt::Display for PeachError { write!(f, "Date/time parse error: {}", path) } PeachError::PasswordIncorrect => { - write!(f, "Password error: user-supplied password is incorrect") + write!(f, "password is incorrect") } PeachError::PasswordMismatch => { - write!(f, "Password error: user-supplied passwords do not match") + write!(f, "passwords do not match") } PeachError::PasswordNotSet => { - write!( - f, - "Password error: hash value in YAML configuration file is empty" - ) + write!(f, "hash value in YAML configuration file is empty") } PeachError::Read { ref path, .. } => { write!(f, "Read error: {}", path) } PeachError::Regex(ref err) => err.fmt(f), - PeachError::SbotCli { ref msg } => { + PeachError::Sbot(ref msg) => { write!(f, "Sbot error: {}", msg) } PeachError::SerdeJson(ref err) => err.fmt(f), diff --git a/peach-lib/src/password_utils.rs b/peach-lib/src/password_utils.rs index fa28904..d0aff4d 100644 --- a/peach-lib/src/password_utils.rs +++ b/peach-lib/src/password_utils.rs @@ -1,7 +1,10 @@ +use async_std::task; +use golgi::Sbot; +use log::debug; use nanorand::{Rng, WyRand}; use sha3::{Digest, Sha3_256}; -use crate::{config_manager, error::PeachError}; +use crate::{config_manager, error::PeachError, sbot::SbotConfig}; /// Returns Ok(()) if the supplied password is correct, /// and returns Err if the supplied password is incorrect. @@ -102,8 +105,37 @@ 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 { - // TODO: replace with golgi - //sbot_client::private_message(&msg, &ssb_admin_id)?; + // use golgi to send a private message on scuttlebutt + match task::block_on(publish_private_msg(&msg, &ssb_admin_id)) { + Ok(_) => (), + Err(e) => return Err(PeachError::Sbot(e)), + } } Ok(()) } + +async fn publish_private_msg(msg: &str, recipient: &str) -> Result<(), String> { + // retrieve latest go-sbot configuration parameters + let sbot_config = SbotConfig::read().ok(); + + let msg = msg.to_string(); + let recipient = vec![recipient.to_string()]; + + // initialise sbot connection with ip:port and shscap from config file + let mut 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 + .map_err(|e| e.to_string())? + } + None => Sbot::init(None, None).await.map_err(|e| e.to_string())?, + }; + + debug!("Publishing a Scuttlebutt private message with temporary password"); + match sbot_client.publish_private(msg, recipient).await { + Ok(_) => Ok(()), + Err(e) => Err(format!("Failed to publish private message: {}", e)), + } +} diff --git a/peach-web/src/router.rs b/peach-web/src/router.rs index ee43d47..38aed11 100644 --- a/peach-web/src/router.rs +++ b/peach-web/src/router.rs @@ -31,9 +31,6 @@ pub fn mount_peachpub_routes(rocket: Rocket) -> Rocket { login, login_post, logout, - reboot_cmd, - shutdown_cmd, - power_menu, settings_menu, set_theme, ], @@ -43,7 +40,6 @@ pub fn mount_peachpub_routes(rocket: Rocket) -> Rocket { routes![ admin_menu, configure_admin, - add_admin, add_admin_post, delete_admin_post, change_password, @@ -101,6 +97,7 @@ pub fn mount_peachpub_routes(rocket: Rocket) -> Rocket { /// required to run a complete PeachCloud build. pub fn mount_peachcloud_routes(rocket: Rocket) -> Rocket { mount_peachpub_routes(rocket) + .mount("/", routes![reboot_cmd, shutdown_cmd, power_menu,]) .mount( "/settings/network", routes![ diff --git a/peach-web/src/routes/authentication.rs b/peach-web/src/routes/authentication.rs index 04df16c..3ff8f2b 100644 --- a/peach-web/src/routes/authentication.rs +++ b/peach-web/src/routes/authentication.rs @@ -6,7 +6,6 @@ use rocket::{ post, request::{self, FlashMessage, FromRequest, Request}, response::{Flash, Redirect}, - serde::Deserialize, }; use rocket_dyn_templates::{tera::Context, Template}; @@ -89,17 +88,14 @@ pub fn login(flash: Option) -> Template { Template::render("login", &context.into_json()) } -#[derive(Debug, Deserialize, FromForm)] +#[derive(Debug, FromForm)] pub struct LoginForm { - pub username: String, pub password: String, } -/// Takes in a LoginForm and returns Ok(()) if username and password -/// are correct to authenticate with peach-web. +/// Takes in a LoginForm and returns Ok(()) if the password is correct. /// -/// Note: currently there is only one user, and the username should always -/// be "admin". +/// Note: there is currently only one user, therefore we don't need a username. pub fn verify_login_form(login_form: LoginForm) -> Result<(), PeachError> { password_utils::verify_password(&login_form.password) } @@ -117,13 +113,14 @@ pub fn login_post(login_form: Form, cookies: &CookieJar<'_>) -> Templ TemplateOrRedirect::Redirect(Redirect::to("/")) } - Err(_) => { + Err(e) => { + let err_msg = format!("Invalid password: {}", e); // if unsuccessful login, render /login page again let mut context = Context::new(); context.insert("back", &Some("/".to_string())); context.insert("title", &Some("Login".to_string())); context.insert("flash_name", &("error".to_string())); - context.insert("flash_msg", &("Invalid password".to_string())); + context.insert("flash_msg", &(err_msg)); TemplateOrRedirect::Template(Template::render("login", &context.into_json())) } @@ -142,7 +139,7 @@ pub fn logout(cookies: &CookieJar<'_>) -> Flash { // HELPERS AND ROUTES FOR /reset_password -#[derive(Debug, Deserialize, FromForm)] +#[derive(Debug, FromForm)] pub struct ResetPasswordForm { pub temporary_password: String, pub new_password1: String, @@ -198,7 +195,7 @@ pub fn reset_password_post(reset_password_form: Form) -> Temp let (flash_name, flash_msg) = match save_reset_password_form(reset_password_form.into_inner()) { Ok(_) => ( "success".to_string(), - "New password is now saved. Return home to login".to_string(), + "New password has been saved. Return home to login".to_string(), ), Err(err) => ( "error".to_string(), @@ -266,9 +263,9 @@ pub fn send_password_reset_post() -> Template { // HELPERS AND ROUTES FOR /settings/change_password -#[derive(Debug, Deserialize, FromForm)] +#[derive(Debug, FromForm)] pub struct PasswordForm { - pub old_password: String, + pub current_password: String, pub new_password1: String, pub new_password2: String, } @@ -277,9 +274,9 @@ pub struct PasswordForm { pub fn save_password_form(password_form: PasswordForm) -> Result<(), PeachWebError> { info!( "change password!: {} {} {}", - password_form.old_password, password_form.new_password1, password_form.new_password2 + password_form.current_password, password_form.new_password1, password_form.new_password2 ); - password_utils::verify_password(&password_form.old_password)?; + password_utils::verify_password(&password_form.current_password)?; // if the previous line did not throw an error, then the old password is correct password_utils::validate_new_passwords( &password_form.new_password1, @@ -321,7 +318,7 @@ pub fn change_password_post(password_form: Form, _auth: Authentica let (flash_name, flash_msg) = match save_password_form(password_form.into_inner()) { Ok(_) => ( "success".to_string(), - "New password is now saved".to_string(), + "New password has been saved".to_string(), ), Err(err) => ( "error".to_string(), diff --git a/peach-web/src/routes/settings/admin.rs b/peach-web/src/routes/settings/admin.rs index c2312a3..89b296f 100644 --- a/peach-web/src/routes/settings/admin.rs +++ b/peach-web/src/routes/settings/admin.rs @@ -3,7 +3,6 @@ use rocket::{ get, post, request::FlashMessage, response::{Flash, Redirect}, - serde::Deserialize, uri, }; use rocket_dyn_templates::{tera::Context, Template}; @@ -77,50 +76,31 @@ pub fn configure_admin(flash: Option, _auth: Authenticated) -> Tem // HELPERS AND ROUTES FOR /settings/admin/add -#[derive(Debug, Deserialize, FromForm)] +#[derive(Debug, FromForm)] pub struct AddAdminForm { pub ssb_id: String, } pub fn save_add_admin_form(admin_form: AddAdminForm) -> Result<(), PeachWebError> { let _result = config_manager::add_ssb_admin_id(&admin_form.ssb_id)?; + // if the previous line didn't throw an error then it was a success Ok(()) } -#[get("/add")] -pub fn add_admin(flash: Option, _auth: Authenticated) -> Template { - // retrieve current ui theme - let theme = utils::get_theme(); - - let mut context = Context::new(); - context.insert("theme", &theme); - context.insert("back", &Some("/settings/admin/configure".to_string())); - context.insert("title", &Some("Add Admin".to_string())); - - // check to see if there is a flash message to display - if let Some(flash) = flash { - context.insert("flash_name", &Some(flash.kind().to_string())); - context.insert("flash_msg", &Some(flash.message().to_string())); - }; - - // template_dir is set in Rocket.toml - Template::render("settings/admin/add_admin", &context.into_json()) -} - #[post("/add", data = "")] pub fn add_admin_post(add_admin_form: Form, _auth: Authenticated) -> Flash { let result = save_add_admin_form(add_admin_form.into_inner()); let url = uri!("/settings/admin/configure"); match result { - Ok(_) => Flash::success(Redirect::to(url), "Successfully added new admin"), - Err(_) => Flash::error(Redirect::to(url), "Failed to add new admin"), + Ok(_) => Flash::success(Redirect::to(url), "Added SSB administrator"), + Err(e) => Flash::error(Redirect::to(url), format!("Failed to add new admin: {}", e)), } } // HELPERS AND ROUTES FOR /settings/admin/delete -#[derive(Debug, Deserialize, FromForm)] +#[derive(Debug, FromForm)] pub struct DeleteAdminForm { pub ssb_id: String, } @@ -131,9 +111,12 @@ pub fn delete_admin_post( _auth: Authenticated, ) -> Flash { let result = config_manager::delete_ssb_admin_id(&delete_admin_form.ssb_id); - let url = uri!(configure_admin); + let url = uri!("/settings/admin", configure_admin); match result { - Ok(_) => Flash::success(Redirect::to(url), "Successfully removed admin id"), - Err(_) => Flash::error(Redirect::to(url), "Failed to remove admin id"), + Ok(_) => Flash::success(Redirect::to(url), "Removed SSB administrator"), + Err(e) => Flash::error( + Redirect::to(url), + format!("Failed to remove admin id: {}", e), + ), } } diff --git a/peach-web/templates/login.html.tera b/peach-web/templates/login.html.tera index 31d7ccf..6235a96 100644 --- a/peach-web/templates/login.html.tera +++ b/peach-web/templates/login.html.tera @@ -2,22 +2,19 @@ {%- block card %}
-
-
- - + +
- -
- + + + + + - +
{% include "snippets/flash_message" %} - -
+
- {%- endblock card -%} diff --git a/peach-web/templates/settings/admin/add_admin.html.tera b/peach-web/templates/settings/admin/add_admin.html.tera deleted file mode 100644 index 158bad9..0000000 --- a/peach-web/templates/settings/admin/add_admin.html.tera +++ /dev/null @@ -1,17 +0,0 @@ -{%- extends "nav" -%} -{%- block card %} - -
-
-
- -
- - Cancel -
-
- - {% include "snippets/flash_message" %} -
-
-{%- endblock card -%} diff --git a/peach-web/templates/settings/admin/change_password.html.tera b/peach-web/templates/settings/admin/change_password.html.tera index b3b0749..3308593 100644 --- a/peach-web/templates/settings/admin/change_password.html.tera +++ b/peach-web/templates/settings/admin/change_password.html.tera @@ -3,13 +3,17 @@
- - - - - - -
+
+ + + + + + + + + + Cancel
diff --git a/peach-web/templates/settings/admin/configure_admin.html.tera b/peach-web/templates/settings/admin/configure_admin.html.tera index 0127e1b..fa1843a 100644 --- a/peach-web/templates/settings/admin/configure_admin.html.tera +++ b/peach-web/templates/settings/admin/configure_admin.html.tera @@ -2,25 +2,31 @@ {%- block card %}
-
-

Current Admins

- {% if not ssb_admin_ids %} -
- There are no currently configured admins. -
- {% else %} - {% for admin in ssb_admin_ids %} -
- - - {{ admin }} - +
Administrators are identified and added by their Scuttlebutt public keys. These accounts will be sent private messages on Scuttlebutt when a password reset is requested.
+ {% if not ssb_admin_ids %} +
+ There are no currently configured admins. +
+ {% else %} + {% for admin in ssb_admin_ids %} +
+
+ +

{{ admin }}

+
- {% endfor %} - {% endif %} - Add Admin -
- - {% include "snippets/flash_message" %} + + {% endfor %} + {% endif %} +
+
+ + +
+ + + + {% include "snippets/flash_message" %} +
{%- endblock card -%} diff --git a/peach-web/templates/settings/admin/forgot_password.html.tera b/peach-web/templates/settings/admin/forgot_password.html.tera index f0a254e..f664224 100644 --- a/peach-web/templates/settings/admin/forgot_password.html.tera +++ b/peach-web/templates/settings/admin/forgot_password.html.tera @@ -2,14 +2,15 @@ {%- block card %}
-
-

Click the button below to send a new temporary password which can be used to change your device password. -

- The temporary password will be sent in an SSB private message to the admin of this device.

+
+

Click the 'Send Password Reset' button to send a new temporary password which can be used to change your device password.

+

The temporary password will be sent in an SSB private message to the admin of this device.

+

Once you have the temporary password, click the 'Set New Password' button to reach the password reset page.

-
+
diff --git a/peach-web/templates/settings/admin/reset_password.html.tera b/peach-web/templates/settings/admin/reset_password.html.tera index 0d4d4b6..7fb4bd9 100644 --- a/peach-web/templates/settings/admin/reset_password.html.tera +++ b/peach-web/templates/settings/admin/reset_password.html.tera @@ -2,41 +2,22 @@ {%- block card %}
-
-
-
- - -
- -
- - -
- -
- - -
- -
- -
-
- - {% include "snippets/flash_message" %} -
+
+
+ + + + + + + + + + + +
+
+ + {% include "snippets/flash_message" %}
{%- endblock card -%} -- 2.49.0 From 3572fd4e7b0b45ec6cb5a86ea84e486a2b18f33c Mon Sep 17 00:00:00 2001 From: glyph Date: Fri, 4 Mar 2022 10:56:07 +0200 Subject: [PATCH 02/14] remove password hash logging --- peach-lib/issues_to_fix | 23 ----------------------- peach-lib/src/config_manager.rs | 1 - 2 files changed, 24 deletions(-) delete mode 100644 peach-lib/issues_to_fix diff --git a/peach-lib/issues_to_fix b/peach-lib/issues_to_fix deleted file mode 100644 index fc8d8d5..0000000 --- a/peach-lib/issues_to_fix +++ /dev/null @@ -1,23 +0,0 @@ - - - - permissions for /var/lib/peachcloud - - everything fails if we don't have write permissions - - can't write config - - - configure admin (add new admin) - - isn't persisted to config.yml - - on second try, it is persisted - - - password reset via ssb pm is now working - - it sends a temporary password but says nothing about username - - what is the default username? - - why do we even have a username? - - - login with temporary password fails - - "Invalid password: Password error: hash value in YAML configuration file is empty." - - - things are generally working now :) - - - for all form inputs: - - use a proper label (not just a placeholder) - - login diff --git a/peach-lib/src/config_manager.rs b/peach-lib/src/config_manager.rs index dfffd42..e871774 100644 --- a/peach-lib/src/config_manager.rs +++ b/peach-lib/src/config_manager.rs @@ -181,7 +181,6 @@ pub fn set_admin_password_hash(password_hash: &str) -> Result Result { let peach_config = load_peach_config()?; - debug!("Admin password hash: {}", peach_config.admin_password_hash); if !peach_config.admin_password_hash.is_empty() { Ok(peach_config.admin_password_hash) } else { -- 2.49.0 From 6cdd6dc41b50a9e445fe934a668b7e0bcd8b4413 Mon Sep 17 00:00:00 2001 From: glyph Date: Fri, 4 Mar 2022 10:58:50 +0200 Subject: [PATCH 03/14] remove unnecessary serde derivations, state-based remplate rendering, styling improvements --- peach-web/src/context/network.rs | 10 +++------ peach-web/src/context/scuttlebutt.rs | 9 ++++---- peach-web/src/routes/scuttlebutt.rs | 22 +++++++++++++------ peach-web/src/routes/settings/dns.rs | 3 +-- peach-web/src/routes/settings/network.rs | 5 ++--- peach-web/src/routes/settings/scuttlebutt.rs | 3 +-- peach-web/static/css/peachcloud.css | 8 +++++-- .../templates/scuttlebutt/peers.html.tera | 5 +++++ .../scuttlebutt/peers_list.html.tera | 18 ++++++++++----- .../templates/scuttlebutt/search.html.tera | 4 ++-- .../settings/scuttlebutt/menu.html.tera | 2 -- .../templates/status/scuttlebutt.html.tera | 16 +++++++++----- 12 files changed, 61 insertions(+), 44 deletions(-) diff --git a/peach-web/src/context/network.rs b/peach-web/src/context/network.rs index 3ce1ee5..9e3b1b4 100644 --- a/peach-web/src/context/network.rs +++ b/peach-web/src/context/network.rs @@ -3,11 +3,7 @@ use std::collections::HashMap; -use rocket::{ - form::FromForm, - serde::{Deserialize, Serialize}, - UriDisplayQuery, -}; +use rocket::{form::FromForm, serde::Serialize, UriDisplayQuery}; use peach_network::{ network, @@ -36,12 +32,12 @@ pub fn ap_state() -> String { } } -#[derive(Debug, Deserialize, FromForm, UriDisplayQuery)] +#[derive(Debug, FromForm, UriDisplayQuery)] pub struct Ssid { pub ssid: String, } -#[derive(Debug, Deserialize, FromForm)] +#[derive(Debug, FromForm)] pub struct WiFi { pub ssid: String, pub pass: String, diff --git a/peach-web/src/context/scuttlebutt.rs b/peach-web/src/context/scuttlebutt.rs index 9d319a6..6a1fe5e 100644 --- a/peach-web/src/context/scuttlebutt.rs +++ b/peach-web/src/context/scuttlebutt.rs @@ -62,11 +62,11 @@ impl StatusContext { // retrieve go-sbot systemd process status let sbot_status = SbotStatus::read()?; + // retrieve latest go-sbot configuration parameters + let sbot_config = SbotConfig::read().ok(); + // we only want to try and interact with the sbot if it's active if sbot_status.state == Some("active".to_string()) { - // retrieve latest go-sbot configuration parameters - let sbot_config = SbotConfig::read().ok(); - let mut sbot_client = init_sbot_with_config(&sbot_config).await?; // retrieve the local id @@ -80,14 +80,13 @@ impl StatusContext { // assign the sequence number of the latest msg context.latest_seq = Some(msgs[0].sequence); - - context.sbot_config = sbot_config; } else { // the sbot is not currently active; return a helpful message context.flash_name = Some("warning".to_string()); context.flash_msg = Some("The Sbot is currently inactive. As a result, status data cannot be retrieved. Visit the Scuttlebutt settings menu to start the Sbot and then try again".to_string()); } + context.sbot_config = sbot_config; context.sbot_status = Some(sbot_status); Ok(context) diff --git a/peach-web/src/routes/scuttlebutt.rs b/peach-web/src/routes/scuttlebutt.rs index 01aa108..1d12076 100644 --- a/peach-web/src/routes/scuttlebutt.rs +++ b/peach-web/src/routes/scuttlebutt.rs @@ -156,7 +156,7 @@ pub async fn create_invite(invite: Form, _auth: Authenticated) -> Flash< Ok(false) => { return Flash::warning( Redirect::to(url), - "The Sbot is currently inactive. As a result, new posts cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", + "The Sbot is currently inactive. As a result, new invite codes cannot be generated. Visit the Scuttlebutt settings menu to start the Sbot and then try again", ) } // failed to retrieve go-sbot systemd process status @@ -335,12 +335,20 @@ pub fn peers(flash: Option, _auth: Authenticated) -> Template { context.insert("title", &Some("Scuttlebutt Peers")); context.insert("back", &Some("/")); - // check to see if there is a flash message to display - if let Some(flash) = flash { - // add flash message contents to the context object - context.insert("flash_name", &Some(flash.kind().to_string())); - context.insert("flash_msg", &Some(flash.message().to_string())); - }; + // display a helpful message if the sbot is inactive + if let Ok(false) = is_sbot_active() { + context.insert("sbot_state", &Some("inactive".to_string())); + context.insert("flash_name", &Some("warning".to_string())); + context.insert("flash_msg", &Some("The Sbot is currently inactive. As a result, social lists and interactions are unavailable. Visit the Scuttlebutt settings menu to start the Sbot and then try again".to_string())); + } else { + context.insert("sbot_state", &Some("active".to_string())); + // check to see if there is a flash message to display + if let Some(flash) = flash { + // add flash message contents to the context object + context.insert("flash_name", &Some(flash.kind().to_string())); + context.insert("flash_msg", &Some(flash.message().to_string())); + }; + } Template::render("scuttlebutt/peers", &context.into_json()) } diff --git a/peach-web/src/routes/settings/dns.rs b/peach-web/src/routes/settings/dns.rs index 4d50672..376a115 100644 --- a/peach-web/src/routes/settings/dns.rs +++ b/peach-web/src/routes/settings/dns.rs @@ -3,7 +3,6 @@ use rocket::{ form::{Form, FromForm}, get, post, request::FlashMessage, - serde::Deserialize, }; use rocket_dyn_templates::Template; @@ -19,7 +18,7 @@ use crate::{ utils, }; -#[derive(Debug, Deserialize, FromForm)] +#[derive(Debug, FromForm)] pub struct DnsForm { pub external_domain: String, pub enable_dyndns: bool, diff --git a/peach-web/src/routes/settings/network.rs b/peach-web/src/routes/settings/network.rs index 14f0530..0e67c15 100644 --- a/peach-web/src/routes/settings/network.rs +++ b/peach-web/src/routes/settings/network.rs @@ -4,7 +4,6 @@ use rocket::{ get, post, request::FlashMessage, response::{Flash, Redirect}, - serde::Deserialize, uri, UriDisplayQuery, }; use rocket_dyn_templates::{tera::Context, Template}; @@ -21,12 +20,12 @@ use crate::{ // STRUCTS USED BY NETWORK ROUTES -#[derive(Debug, Deserialize, FromForm, UriDisplayQuery)] +#[derive(Debug, FromForm, UriDisplayQuery)] pub struct Ssid { pub ssid: String, } -#[derive(Debug, Deserialize, FromForm)] +#[derive(Debug, FromForm)] pub struct WiFi { pub ssid: String, pub pass: String, diff --git a/peach-web/src/routes/settings/scuttlebutt.rs b/peach-web/src/routes/settings/scuttlebutt.rs index 8e364d4..54b66f7 100644 --- a/peach-web/src/routes/settings/scuttlebutt.rs +++ b/peach-web/src/routes/settings/scuttlebutt.rs @@ -10,14 +10,13 @@ use rocket::{ get, post, request::FlashMessage, response::{Flash, Redirect}, - serde::Deserialize, }; use rocket_dyn_templates::{tera::Context, Template}; use crate::routes::authentication::Authenticated; use crate::utils; -#[derive(Debug, Deserialize, FromForm)] +#[derive(Debug, FromForm)] pub struct SbotConfigForm { /// Directory path for the log and indexes. repo: String, diff --git a/peach-web/static/css/peachcloud.css b/peach-web/static/css/peachcloud.css index 676d43a..984ab2a 100644 --- a/peach-web/static/css/peachcloud.css +++ b/peach-web/static/css/peachcloud.css @@ -720,7 +720,7 @@ form { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; - margin-top: 0.5rem; + /* margin-top: 0.5rem; */ margin-bottom: 1rem; padding-left: 5px; line-height: 1.5rem; @@ -738,6 +738,7 @@ form { .message-input { height: 7rem; + margin-top: 0.5rem; overflow: auto; resize: vertical; width: 100%; @@ -770,7 +771,6 @@ form { } .label-medium { - color: var(--text-color); font-size: var(--font-size-3); display: block; } @@ -799,6 +799,10 @@ form { /* color: var(--font-near-black); */ } +.link:hover { + text-decoration: underline; +} + /* * LISTS */ diff --git a/peach-web/templates/scuttlebutt/peers.html.tera b/peach-web/templates/scuttlebutt/peers.html.tera index 23d667f..5409bf7 100644 --- a/peach-web/templates/scuttlebutt/peers.html.tera +++ b/peach-web/templates/scuttlebutt/peers.html.tera @@ -2,6 +2,8 @@ {%- block card %}
+ {# only render the peer menu elements if the sbot is active #} + {%- if sbot_state == "active" %}
@@ -12,5 +14,8 @@ Invites
+ {%- endif %} + + {% include "snippets/flash_message" %}
{%- endblock card -%} diff --git a/peach-web/templates/scuttlebutt/peers_list.html.tera b/peach-web/templates/scuttlebutt/peers_list.html.tera index c2dcfb4..9f7ee27 100644 --- a/peach-web/templates/scuttlebutt/peers_list.html.tera +++ b/peach-web/templates/scuttlebutt/peers_list.html.tera @@ -1,24 +1,30 @@ {%- extends "nav" -%} {%- block card %}
- {%- if peers %} + {%- if peers %} {%- else %}

No follows found

- {%- endif %} + {%- endif %}
{%- endblock card -%} diff --git a/peach-web/templates/scuttlebutt/search.html.tera b/peach-web/templates/scuttlebutt/search.html.tera index d5e508d..f4582dc 100644 --- a/peach-web/templates/scuttlebutt/search.html.tera +++ b/peach-web/templates/scuttlebutt/search.html.tera @@ -9,8 +9,8 @@
+ + {% include "snippets/flash_message" %} - - {% include "snippets/flash_message" %}
{%- endblock card -%} diff --git a/peach-web/templates/settings/scuttlebutt/menu.html.tera b/peach-web/templates/settings/scuttlebutt/menu.html.tera index a2f5513..93fb2d3 100644 --- a/peach-web/templates/settings/scuttlebutt/menu.html.tera +++ b/peach-web/templates/settings/scuttlebutt/menu.html.tera @@ -11,8 +11,6 @@ {% else %} Start Sbot {% endif %} - Check Filesystem - Remove Blocked Feeds
{% include "snippets/flash_message" %} diff --git a/peach-web/templates/status/scuttlebutt.html.tera b/peach-web/templates/status/scuttlebutt.html.tera index 998efad..7e1e4d5 100644 --- a/peach-web/templates/status/scuttlebutt.html.tera +++ b/peach-web/templates/status/scuttlebutt.html.tera @@ -5,12 +5,12 @@ {%- if sbot_status.memory -%} {% set mem = sbot_status.memory / 1024 / 1024 | round | int -%} {%- else -%} - {% set mem = "X" -%} + {% set mem = "0" -%} {%- endif -%} {%- if sbot_status.blobstore -%} {% set blobs = sbot_status.blobstore / 1024 / 1024 | round | int -%} {%- else -%} - {% set blobs = "X" -%} + {% set blobs = "0" -%} {%- endif -%}
@@ -54,8 +54,12 @@
+ {% if sbot_status.state == "active" %} + {% else %} + + {% endif %}
@@ -65,22 +69,22 @@
- +
- - + +
- +
-- 2.49.0 From 84656ff251f1fb02a08d9b897afa90cf0e96e1b9 Mon Sep 17 00:00:00 2001 From: glyph Date: Mon, 7 Mar 2022 11:26:13 +0200 Subject: [PATCH 04/14] set default password --- peach-lib/src/config_manager.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/peach-lib/src/config_manager.rs b/peach-lib/src/config_manager.rs index e871774..e29f8d6 100644 --- a/peach-lib/src/config_manager.rs +++ b/peach-lib/src/config_manager.rs @@ -51,7 +51,7 @@ pub struct PeachConfig { } // helper functions for serializing and deserializing PeachConfig from disc -fn save_peach_config(peach_config: PeachConfig) -> Result { +pub fn save_peach_config(peach_config: PeachConfig) -> Result { // use a file lock to avoid race conditions while saving config let mut lock = LockFile::open(LOCK_FILE_PATH)?; lock.lock()?; @@ -84,7 +84,8 @@ pub fn load_peach_config() -> Result { dyn_tsig_key_path: "".to_string(), dyn_enabled: false, ssb_admin_ids: Vec::new(), - admin_password_hash: "".to_string(), + // default password is `peach` + admin_password_hash: "146".to_string(), temporary_password_hash: "".to_string(), } } -- 2.49.0 From 824cbdbc0cc869e66aeaeacfdd6e897549f869d8 Mon Sep 17 00:00:00 2001 From: glyph Date: Mon, 7 Mar 2022 11:26:52 +0200 Subject: [PATCH 05/14] save default peachcloud config if file does not exist --- peach-web/src/main.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/peach-web/src/main.rs b/peach-web/src/main.rs index 1e7f1b4..8150710 100644 --- a/peach-web/src/main.rs +++ b/peach-web/src/main.rs @@ -34,6 +34,7 @@ use std::{process, sync::RwLock}; use lazy_static::lazy_static; use log::{debug, error, info}; +use peach_lib::{config_manager, config_manager::YAML_PATH as PEACH_CONFIG}; use rocket::{fairing::AdHoc, serde::Deserialize, Build, Rocket}; use utils::Theme; @@ -90,6 +91,18 @@ async fn main() { // initialize logger env_logger::init(); + // check if /var/lib/peachcloud/config.yml exists + if !std::path::Path::new(PEACH_CONFIG).exists() { + info!("PeachCloud configuration file not found; loading default values"); + // since we're in the intialisation phase, panic if the loading fails + let config = + config_manager::load_peach_config().expect("peachcloud configuration loading failed"); + + info!("Saving default PeachCloud configuration values to file"); + // this ensures a config file is created if it does not already exist + config_manager::save_peach_config(config).expect("peachcloud configuration saving failed"); + } + // initialize rocket let rocket = init_rocket(); -- 2.49.0 From af34829cb05bc71234e163b3af927cafc7a42f4b Mon Sep 17 00:00:00 2001 From: glyph Date: Mon, 7 Mar 2022 11:34:10 +0200 Subject: [PATCH 06/14] add reset password option to admin menu --- peach-web/templates/settings/admin/menu.html.tera | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/peach-web/templates/settings/admin/menu.html.tera b/peach-web/templates/settings/admin/menu.html.tera index 111cb73..dbd62b5 100644 --- a/peach-web/templates/settings/admin/menu.html.tera +++ b/peach-web/templates/settings/admin/menu.html.tera @@ -4,8 +4,9 @@ {%- endblock card -%} -- 2.49.0 From 1a8ac3f57fe5ef37b1ea37fc72b07dffdd4302c3 Mon Sep 17 00:00:00 2001 From: glyph Date: Mon, 7 Mar 2022 11:35:19 +0200 Subject: [PATCH 07/14] change help to guide and add text --- peach-web/src/router.rs | 2 +- peach-web/src/routes/index.rs | 10 ++++----- peach-web/templates/guide.html.tera | 33 +++++++++++++++++++++++++++++ peach-web/templates/help.html.tera | 10 --------- peach-web/templates/home.html.tera | 4 ++-- 5 files changed, 41 insertions(+), 18 deletions(-) create mode 100644 peach-web/templates/guide.html.tera delete mode 100644 peach-web/templates/help.html.tera diff --git a/peach-web/src/router.rs b/peach-web/src/router.rs index 38aed11..0872af0 100644 --- a/peach-web/src/router.rs +++ b/peach-web/src/router.rs @@ -26,7 +26,7 @@ pub fn mount_peachpub_routes(rocket: Rocket) -> Rocket { .mount( "/", routes![ - help, + guide, home, login, login_post, diff --git a/peach-web/src/routes/index.rs b/peach-web/src/routes/index.rs index e796c03..45eb21c 100644 --- a/peach-web/src/routes/index.rs +++ b/peach-web/src/routes/index.rs @@ -29,17 +29,17 @@ pub fn home(_auth: Authenticated, config: &State) -> Template { Template::render("home", &context.into_json()) } -// HELPERS AND ROUTES FOR /help +// HELPERS AND ROUTES FOR /guide -#[get("/help")] -pub fn help(flash: Option) -> Template { +#[get("/guide")] +pub fn guide(flash: Option) -> Template { // retrieve current ui theme let theme = utils::get_theme(); let mut context = Context::new(); context.insert("theme", &theme); context.insert("back", &Some("/".to_string())); - context.insert("title", &Some("Help".to_string())); + context.insert("title", &Some("Guide".to_string())); // check to see if there is a flash message to display if let Some(flash) = flash { @@ -47,5 +47,5 @@ pub fn help(flash: Option) -> Template { context.insert("flash_msg", &Some(flash.message().to_string())); }; - Template::render("help", &context.into_json()) + Template::render("guide", &context.into_json()) } diff --git a/peach-web/templates/guide.html.tera b/peach-web/templates/guide.html.tera new file mode 100644 index 0000000..07c850b --- /dev/null +++ b/peach-web/templates/guide.html.tera @@ -0,0 +1,33 @@ +{%- extends "nav" -%} +{%- block card %} + +
+
+ +
+ Getting started +

The Scuttlebutt server (sbot) will be inactive when you first run PeachCloud. This is to allow configuration parameters to be set before it is activated for the first time. Navigate to the Sbot Configuration page to configure your system. The default configuration will be fine for most usecases.

+

Once the configuration is set, navigate to the Scuttlebutt settings menu to start the sbot. If the server starts successfully, you will see a green smiley face on the home page. If the face is orange and sleeping, that means the sbot is still inactive (ie. the process is not running). If the face is red and dead, that means the sbot failed to start - indicated an error. For now, the best way to gain insight into the problem is to check the systemd log. Open a terminal and enter: systemctl --user status go-sbot.service. The log output may give some clues about the source of the error.

+
+ +
+ Submit a bug report +

Bug reports can be submitted by filing an issue on the peach-workspace git repo. Before filing a report, first check to see if an issue already exists for the bug you've encountered. If not, you're invited to submit a new report; the template will guide you through several questions.

+
+ +
+ Share feedback & request support +

You're invited to share your thoughts and experiences of PeachCloud in the #peachcloud channel on Scuttlebutt. The channel is also a good place to ask for help.

+

Alternatively, we have a Matrix channel for discussion about PeachCloud and you can also reach out to @glyph via email.

+
+ +
+ Contribute to PeachCloud +

PeachCloud is free, open-source software and relies on donations and grants to fund develop. Donations can be made on our OpenCollective page.

+

Programmers, designers, artists and writers are also welcome to contribute to the project. Please visit the main PeachCloud git repository to find out more details or contact the team via Scuttlebutt, Matrix or email.

+
+
+ + {% include "snippets/flash_message" %} +
+{%- endblock card -%} diff --git a/peach-web/templates/help.html.tera b/peach-web/templates/help.html.tera deleted file mode 100644 index 0109d1e..0000000 --- a/peach-web/templates/help.html.tera +++ /dev/null @@ -1,10 +0,0 @@ -{%- extends "nav" -%} -{%- block card %} - -
-
- - {% include "snippets/flash_message" %} -
-
-{%- endblock card -%} diff --git a/peach-web/templates/home.html.tera b/peach-web/templates/home.html.tera index 9013c7c..27ff7de 100644 --- a/peach-web/templates/home.html.tera +++ b/peach-web/templates/home.html.tera @@ -52,14 +52,14 @@ - +
- +
-- 2.49.0 From 983aa0689cb099ed12534acba6f3152a4027731f Mon Sep 17 00:00:00 2001 From: glyph Date: Mon, 7 Mar 2022 11:36:03 +0200 Subject: [PATCH 08/14] improve messaging for when sbot is inactive --- peach-web/src/context/scuttlebutt.rs | 171 ++++++------- peach-web/src/routes/scuttlebutt.rs | 229 +++++++++++------- .../templates/scuttlebutt/inactive.html.tera | 11 + .../scuttlebutt/peers_list.html.tera | 2 +- .../templates/scuttlebutt/private.html.tera | 2 +- .../templates/scuttlebutt/profile.html.tera | 14 +- 6 files changed, 242 insertions(+), 187 deletions(-) create mode 100644 peach-web/templates/scuttlebutt/inactive.html.tera diff --git a/peach-web/src/context/scuttlebutt.rs b/peach-web/src/context/scuttlebutt.rs index 6a1fe5e..9f5ac77 100644 --- a/peach-web/src/context/scuttlebutt.rs +++ b/peach-web/src/context/scuttlebutt.rs @@ -424,98 +424,88 @@ impl ProfileContext { // retrieve go-sbot systemd process status let sbot_status = SbotStatus::read()?; - // we only want to try and interact with the sbot if it's active - if sbot_status.state == Some("active".to_string()) { - // retrieve latest go-sbot configuration parameters - let sbot_config = SbotConfig::read().ok(); + // retrieve latest go-sbot configuration parameters + let sbot_config = SbotConfig::read().ok(); - let mut sbot_client = init_sbot_with_config(&sbot_config).await?; + let mut sbot_client = init_sbot_with_config(&sbot_config).await?; - let local_id = sbot_client.whoami().await?; + let local_id = sbot_client.whoami().await?; - // if an ssb_id has been provided to the context builder, we assume that - // the profile info being retrieved is for a peer (ie. not for our local - // profile) - let id = if ssb_id.is_some() { - // we are not dealing with the local profile - context.is_local_profile = false; + // if an ssb_id has been provided to the context builder, we assume that + // the profile info being retrieved is for a peer (ie. not for our local + // profile) + let id = if ssb_id.is_some() { + // we are not dealing with the local profile + context.is_local_profile = false; - // we're safe to unwrap here because we know it's `Some(id)` - let peer_id = ssb_id.unwrap(); + // we're safe to unwrap here because we know it's `Some(id)` + let peer_id = ssb_id.unwrap(); - // determine relationship between peer and local id - let follow_query = RelationshipQuery { - source: local_id.clone(), - dest: peer_id.clone(), - }; - - // query follow state - context.following = match sbot_client.friends_is_following(follow_query).await { - Ok(following) if following == "true" => Some(true), - Ok(following) if following == "false" => Some(false), - _ => None, - }; - - // TODO: i don't like that we have to instantiate the same query object - // twice. see if we can streamline this in golgi - let block_query = RelationshipQuery { - source: local_id.clone(), - dest: peer_id.clone(), - }; - - // query block state - context.blocking = match sbot_client.friends_is_blocking(block_query).await { - Ok(blocking) if blocking == "true" => Some(true), - Ok(blocking) if blocking == "false" => Some(false), - _ => None, - }; - - peer_id - } else { - // if an ssb_id has not been provided, retrieve the local id using whoami - context.is_local_profile = true; - - local_id + // determine relationship between peer and local id + let follow_query = RelationshipQuery { + source: local_id.clone(), + dest: peer_id.clone(), }; - // TODO: add relationship state context if not local profile - // ie. lookup is_following and is_blocking, set context accordingly + // query follow state + context.following = match sbot_client.friends_is_following(follow_query).await { + Ok(following) if following == "true" => Some(true), + Ok(following) if following == "false" => Some(false), + _ => None, + }; - // retrieve the profile info for the given id - let info = sbot_client.get_profile_info(&id).await?; - // set each context field accordingly - for (key, val) in info { - match key.as_str() { - "name" => context.name = Some(val), - "description" => context.description = Some(val), - "image" => context.image = Some(val), - _ => (), - } - } + // TODO: i don't like that we have to instantiate the same query object + // twice. see if we can streamline this in golgi + let block_query = RelationshipQuery { + source: local_id.clone(), + dest: peer_id.clone(), + }; - // assign the ssb public key to the context - // (could be for the local profile or a peer) - context.id = Some(id); + // query block state + context.blocking = match sbot_client.friends_is_blocking(block_query).await { + Ok(blocking) if blocking == "true" => Some(true), + Ok(blocking) if blocking == "false" => Some(false), + _ => None, + }; - // determine the path to the blob defined by the value of `context.image` - if let Some(ref blob_id) = context.image { - context.blob_path = match blobs::get_blob_path(&blob_id) { - Ok(path) => { - // if we get the path, check if the blob is in the blobstore. - // this allows us to default to a placeholder image in the template - if let Ok(exists) = utils::blob_is_stored_locally(&path).await { - context.blob_exists = exists - }; - - Some(path) - } - Err(_) => None, - } - } + peer_id } else { - // the sbot is not currently active; return a helpful message - context.flash_name = Some("warning".to_string()); - context.flash_msg = Some("The Sbot is currently inactive. As a result, profile data cannot be retrieved. Visit the Scuttlebutt settings menu to start the Sbot and then try again".to_string()); + // if an ssb_id has not been provided, retrieve the local id using whoami + context.is_local_profile = true; + + local_id + }; + + // retrieve the profile info for the given id + let info = sbot_client.get_profile_info(&id).await?; + // set each context field accordingly + for (key, val) in info { + match key.as_str() { + "name" => context.name = Some(val), + "description" => context.description = Some(val), + "image" => context.image = Some(val), + _ => (), + } + } + + // assign the ssb public key to the context + // (could be for the local profile or a peer) + context.id = Some(id); + + // determine the path to the blob defined by the value of `context.image` + if let Some(ref blob_id) = context.image { + context.blob_path = match blobs::get_blob_path(&blob_id) { + Ok(path) => { + // if we get the path, check if the blob is in the blobstore. + // this allows us to default to a placeholder image in the template + if let Ok(exists) = utils::blob_is_stored_locally(&path).await { + context.blob_exists = exists + }; + + Some(path) + } + Err(_) => None, + } } context.sbot_status = Some(sbot_status); @@ -563,22 +553,15 @@ impl PrivateContext { // retrieve go-sbot systemd process status let sbot_status = SbotStatus::read()?; - // we only want to try and interact with the sbot if it's active - if sbot_status.state == Some("active".to_string()) { - // retrieve latest go-sbot configuration parameters - let sbot_config = SbotConfig::read().ok(); + // retrieve latest go-sbot configuration parameters + let sbot_config = SbotConfig::read().ok(); - let mut sbot_client = init_sbot_with_config(&sbot_config).await?; + let mut sbot_client = init_sbot_with_config(&sbot_config).await?; - context.recipient_id = recipient_id; + context.recipient_id = recipient_id; - let local_id = sbot_client.whoami().await?; - context.id = Some(local_id); - } else { - // the sbot is not currently active; return a helpful message - context.flash_name = Some("warning".to_string()); - context.flash_msg = Some("The Sbot is currently inactive. As a result, private messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again".to_string()); - } + let local_id = sbot_client.whoami().await?; + context.id = Some(local_id); context.sbot_status = Some(sbot_status); diff --git a/peach-web/src/routes/scuttlebutt.rs b/peach-web/src/routes/scuttlebutt.rs index 1d12076..e9c1d4b 100644 --- a/peach-web/src/routes/scuttlebutt.rs +++ b/peach-web/src/routes/scuttlebutt.rs @@ -156,7 +156,7 @@ pub async fn create_invite(invite: Form, _auth: Authenticated) -> Flash< Ok(false) => { return Flash::warning( Redirect::to(url), - "The Sbot is currently inactive. As a result, new invite codes cannot be generated. Visit the Scuttlebutt settings menu to start the Sbot and then try again", + "The Sbot is inactive. New invite codes cannot be generated. Visit the Scuttlebutt settings menu to start the Sbot and then try again", ) } // failed to retrieve go-sbot systemd process status @@ -173,34 +173,53 @@ pub async fn private( flash: Option>, _auth: Authenticated, ) -> Template { - if let Some(ref key) = public_key { - // `url_decode` replaces '+' with ' ', so we need to revert that - public_key = Some(key.replace(' ', "+")); - } + // display a helpful message if the sbot is inactive + if let Ok(false) = is_sbot_active() { + // retrieve current ui theme + let theme = utils::get_theme(); - // build the private context object - let context = PrivateContext::build(public_key).await; + let mut context = Context::new(); + context.insert("theme", &theme); + context.insert("back", &Some("/".to_string())); + context.insert("title", &Some("Private Messages".to_string())); + context.insert( + "unavailable_msg", + &Some("Private messages cannot be published.".to_string()), + ); - match context { - // we were able to build the context without errors - Ok(mut context) => { - // check to see if there is a flash message to display - if let Some(flash) = flash { - // add flash message contents to the context object - context.flash_name = Some(flash.kind().to_string()); - context.flash_msg = Some(flash.message().to_string()); - }; - - Template::render("scuttlebutt/private", &context) + // render the "sbot is inactive" template + return Template::render("scuttlebutt/inactive", &context.into_json()); + // otherwise, build the full context and render the private message template + } else { + if let Some(ref key) = public_key { + // `url_decode` replaces '+' with ' ', so we need to revert that + public_key = Some(key.replace(' ', "+")); } - // an error occurred while building the context - Err(e) => { - // build the default context and pass along the error message - let mut context = PrivateContext::default(); - context.flash_name = Some("error".to_string()); - context.flash_msg = Some(e.to_string()); - Template::render("scuttlebutt/private", &context) + // build the private context object + let context = PrivateContext::build(public_key).await; + + match context { + // we were able to build the context without errors + Ok(mut context) => { + // check to see if there is a flash message to display + if let Some(flash) = flash { + // add flash message contents to the context object + context.flash_name = Some(flash.kind().to_string()); + context.flash_msg = Some(flash.message().to_string()); + }; + + Template::render("scuttlebutt/private", &context) + } + // an error occurred while building the context + Err(e) => { + // build the default context and pass along the error message + let mut context = PrivateContext::default(); + context.flash_name = Some("error".to_string()); + context.flash_msg = Some(e.to_string()); + + Template::render("scuttlebutt/private", &context) + } } } } @@ -256,7 +275,7 @@ pub async fn private_post(private: Form, _auth: Authenticated) -> Flash Ok(false) => { return Flash::warning( Redirect::to(url), - "The Sbot is currently inactive. As a result, new private message cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", + "The Sbot is inactive. New private message cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", ); } // failed to retrieve go-sbot systemd process status @@ -337,9 +356,13 @@ pub fn peers(flash: Option, _auth: Authenticated) -> Template { // display a helpful message if the sbot is inactive if let Ok(false) = is_sbot_active() { - context.insert("sbot_state", &Some("inactive".to_string())); - context.insert("flash_name", &Some("warning".to_string())); - context.insert("flash_msg", &Some("The Sbot is currently inactive. As a result, social lists and interactions are unavailable. Visit the Scuttlebutt settings menu to start the Sbot and then try again".to_string())); + context.insert( + "unavailable_msg", + &Some("Social lists and interactions are unavailable.".to_string()), + ); + + // render the "sbot is inactive" template + return Template::render("scuttlebutt/inactive", &context.into_json()); } else { context.insert("sbot_state", &Some("active".to_string())); // check to see if there is a flash message to display @@ -396,7 +419,7 @@ pub async fn publish(post: Form, _auth: Authenticated) -> Flash Ok(false) => { return Flash::warning( Redirect::to(url), - "The Sbot is currently inactive. As a result, new posts cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", + "The Sbot is inactive. New posts cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", ); } Err(e) => return Flash::error(Redirect::to(url), e), @@ -439,7 +462,7 @@ pub async fn follow(peer: Form, _auth: Authenticated) -> Flash { Ok(false) => { return Flash::warning( Redirect::to(url), - "The Sbot is currently inactive. As a result, follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", + "The Sbot is inactive. Follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", ); } Err(e) => return Flash::error(Redirect::to(url), e), @@ -484,7 +507,7 @@ pub async fn unfollow(peer: Form, _auth: Authenticated) -> Flash Ok(false) => { return Flash::warning( Redirect::to(url), - "The Sbot is currently inactive. As a result, follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", + "The Sbot is inactive. Follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", ); } Err(e) => return Flash::error(Redirect::to(url), e), @@ -527,7 +550,7 @@ pub async fn block(peer: Form, _auth: Authenticated) -> Flash { Ok(false) => { return Flash::warning( Redirect::to(url), - "The Sbot is currently inactive. As a result, follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", + "The Sbot is inactive. Follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", ); } Err(e) => return Flash::error(Redirect::to(url), e), @@ -571,7 +594,7 @@ pub async fn unblock(peer: Form, _auth: Authenticated) -> Flash Ok(false) => { return Flash::warning( Redirect::to(url), - "The Sbot is currently inactive. As a result, follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", + "The Sbot is inactive. Follow messages cannot be published. Visit the Scuttlebutt settings menu to start the Sbot and then try again", ); } Err(e) => return Flash::error(Redirect::to(url), e), @@ -590,41 +613,59 @@ pub async fn profile( flash: Option>, _auth: Authenticated, ) -> Template { - if let Some(ref key) = public_key { - // `url_decode` replaces '+' with ' ', so we need to revert that - public_key = Some(key.replace(' ', "+")); - } + // display a helpful message if the sbot is inactive + if let Ok(false) = is_sbot_active() { + // retrieve current ui theme + let theme = utils::get_theme(); - // build the profile context object - let context = ProfileContext::build(public_key).await; + let mut context = Context::new(); + context.insert("theme", &theme); + context.insert("back", &Some("/".to_string())); + context.insert("title", &Some("Profile".to_string())); + context.insert( + "unavailable_msg", + &Some("Profile data cannot be retrieved.".to_string()), + ); - match context { - // we were able to build the context without errors - Ok(mut context) => { - // check to see if there is a flash message to display - if let Some(flash) = flash { - // add flash message contents to the context object - context.flash_name = Some(flash.kind().to_string()); - context.flash_msg = Some(flash.message().to_string()); - }; - - Template::render("scuttlebutt/profile", &context) + // render the "sbot is inactive" template + return Template::render("scuttlebutt/inactive", &context.into_json()); + } else { + if let Some(ref key) = public_key { + // `url_decode` replaces '+' with ' ', so we need to revert that + public_key = Some(key.replace(' ', "+")); } - // an error occurred while building the context - Err(e) => { - // build the default context and pass along the error message - let mut context = ProfileContext::default(); - // flash name and msg will be `Some` if the sbot is inactive (in - // that case, they are set by the context builder). - // otherwise, we need to assign the name and returned error msg - // to the flash. - if context.flash_name.is_none() || context.flash_msg.is_none() { - context.flash_name = Some("error".to_string()); - context.flash_msg = Some(e.to_string()); + // build the profile context object + let context = ProfileContext::build(public_key).await; + + match context { + // we were able to build the context without errors + Ok(mut context) => { + // check to see if there is a flash message to display + if let Some(flash) = flash { + // add flash message contents to the context object + context.flash_name = Some(flash.kind().to_string()); + context.flash_msg = Some(flash.message().to_string()); + }; + + Template::render("scuttlebutt/profile", &context) } + // an error occurred while building the context + Err(e) => { + // build the default context and pass along the error message + let mut context = ProfileContext::default(); - Template::render("scuttlebutt/profile", &context) + // flash name and msg will be `Some` if the sbot is inactive (in + // that case, they are set by the context builder). + // otherwise, we need to assign the name and returned error msg + // to the flash. + if context.flash_name.is_none() || context.flash_msg.is_none() { + context.flash_name = Some("error".to_string()); + context.flash_msg = Some(e.to_string()); + } + + Template::render("scuttlebutt/profile", &context) + } } } } @@ -635,31 +676,49 @@ pub async fn profile( /// for the local Scuttlebutt profile. #[get("/profile/update")] pub async fn update_profile(flash: Option>, _auth: Authenticated) -> Template { - // build the profile context object - let context = ProfileContext::build(None).await; + // display a helpful message if the sbot is inactive + if let Ok(false) = is_sbot_active() { + // retrieve current ui theme + let theme = utils::get_theme(); - match context { - // we were able to build the context without errors - Ok(mut context) => { - context.back = Some("/scuttlebutt/profile".to_string()); + let mut context = Context::new(); + context.insert("theme", &theme); + context.insert("back", &Some("/".to_string())); + context.insert("title", &Some("Profile".to_string())); + context.insert( + "unavailable_msg", + &Some("Profile data cannot be retrieved.".to_string()), + ); - // check to see if there is a flash message to display - if let Some(flash) = flash { - // add flash message contents to the context object - context.flash_name = Some(flash.kind().to_string()); - context.flash_msg = Some(flash.message().to_string()); - }; + // render the "sbot is inactive" template + return Template::render("scuttlebutt/inactive", &context.into_json()); + } else { + // build the profile context object + let context = ProfileContext::build(None).await; - Template::render("scuttlebutt/update_profile", &context) - } - // an error occurred while building the context - Err(e) => { - // build the default context and pass along the error message - let mut context = ProfileContext::default(); - context.flash_name = Some("error".to_string()); - context.flash_msg = Some(e.to_string()); + match context { + // we were able to build the context without errors + Ok(mut context) => { + context.back = Some("/scuttlebutt/profile".to_string()); - Template::render("scuttlebutt/update_profile", &context) + // check to see if there is a flash message to display + if let Some(flash) = flash { + // add flash message contents to the context object + context.flash_name = Some(flash.kind().to_string()); + context.flash_msg = Some(flash.message().to_string()); + }; + + Template::render("scuttlebutt/update_profile", &context) + } + // an error occurred while building the context + Err(e) => { + // build the default context and pass along the error message + let mut context = ProfileContext::default(); + context.flash_name = Some("error".to_string()); + context.flash_msg = Some(e.to_string()); + + Template::render("scuttlebutt/update_profile", &context) + } } } } @@ -780,7 +839,7 @@ pub async fn update_profile_post( Ok(false) => { return Flash::warning( Redirect::to(url), - "The Sbot is currently inactive. As a result, profile data cannot be updated. Visit the Scuttlebutt settings menu to start the Sbot and then try again", + "The Sbot is inactive. Profile data cannot be updated. Visit the Scuttlebutt settings menu to start the Sbot and then try again", ); } Err(e) => return Flash::error(Redirect::to(url), e), diff --git a/peach-web/templates/scuttlebutt/inactive.html.tera b/peach-web/templates/scuttlebutt/inactive.html.tera new file mode 100644 index 0000000..a4ce398 --- /dev/null +++ b/peach-web/templates/scuttlebutt/inactive.html.tera @@ -0,0 +1,11 @@ +{%- extends "nav" -%} +{%- block card %} + +
+{%- endblock card -%} diff --git a/peach-web/templates/scuttlebutt/peers_list.html.tera b/peach-web/templates/scuttlebutt/peers_list.html.tera index 9f7ee27..32fcf60 100644 --- a/peach-web/templates/scuttlebutt/peers_list.html.tera +++ b/peach-web/templates/scuttlebutt/peers_list.html.tera @@ -2,7 +2,7 @@ {%- block card %}
{%- if peers %} -