Introduce theme support (plus blobstore size) #80

Merged
glyph merged 5 commits from theme_support into main 2022-02-07 07:56:35 +00:00
34 changed files with 2245 additions and 149 deletions

View File

@ -1,11 +1,31 @@
//! Data types and associated methods for monitoring and configuring go-sbot.
use std::{fs, fs::File, io::Write, process::Command, str};
use std::{fs, fs::File, io, io::Write, path::PathBuf, process::Command, str};
use serde::{Deserialize, Serialize};
use crate::error::PeachError;
/* HELPER FUNCTIONS */
// iterate over the given directory path to determine the size of the directory
fn dir_size(path: impl Into<PathBuf>) -> io::Result<u64> {
fn dir_size(mut dir: fs::ReadDir) -> io::Result<u64> {
dir.try_fold(0, |acc, file| {
let file = file?;
let size = match file.metadata()? {
data if data.is_dir() => dir_size(fs::read_dir(file.path())?)?,
data => data.len(),
};
Ok(acc + size)
})
}
dir_size(fs::read_dir(path.into())?)
}
/* SBOT-RELATED TYPES AND METHODS */
/// go-sbot process status.
#[derive(Debug, Serialize, Deserialize)]
pub struct SbotStatus {
@ -19,6 +39,8 @@ pub struct SbotStatus {
pub uptime: Option<String>,
/// Downtime for the process (if state is `inactive`).
pub downtime: Option<String>,
/// Size of the blobs directory in bytes.
pub blobstore: Option<u64>,
}
/// Default builder for `SbotStatus`.
@ -30,6 +52,7 @@ impl Default for SbotStatus {
memory: None,
uptime: None,
downtime: None,
blobstore: None,
}
}
}
@ -101,6 +124,15 @@ impl SbotStatus {
}
}
// determine path of user's home directory
let mut blobstore_path = dirs::home_dir().ok_or(PeachError::HomeDir)?;
// append the blobstore path
blobstore_path.push(".ssb-go/blobs/sha256");
// determine the size of the blobstore directory in bytes
status.blobstore = dir_size(blobstore_path).ok();
Ok(status)
}
}

View File

@ -36,6 +36,7 @@ maintenance = { status = "actively-developed" }
[dependencies]
env_logger = "0.8"
lazy_static = "1.4.0"
log = "0.4"
nest = "1.0.0"
peach-lib = { path = "../peach-lib" }

View File

@ -11,6 +11,7 @@ pub struct ConfigureDNSContext {
pub title: Option<String>,
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub theme: Option<String>,
}
impl ConfigureDNSContext {
@ -31,6 +32,7 @@ impl ConfigureDNSContext {
title: None,
flash_name: None,
flash_msg: None,
theme: None,
}
}
}

View File

@ -32,11 +32,14 @@ pub mod routes;
mod tests;
pub mod utils;
use std::process;
use std::{process, sync::RwLock};
use lazy_static::lazy_static;
use log::{debug, error, info};
use rocket::{fairing::AdHoc, serde::Deserialize, Build, Rocket};
use utils::Theme;
pub type BoxError = Box<dyn std::error::Error>;
/// Application configuration parameters.
@ -51,6 +54,10 @@ pub struct RocketConfig {
standalone_mode: bool,
}
lazy_static! {
static ref THEME: RwLock<Theme> = RwLock::new(Theme::Light);
}
static WLAN_IFACE: &str = "wlan0";
static AP_IFACE: &str = "ap0";

View File

@ -6,7 +6,7 @@ use crate::routes::{
catchers::*,
index::*,
scuttlebutt::*,
settings::{admin::*, dns::*, menu::*, network::*, scuttlebutt::*},
settings::{admin::*, dns::*, menu::*, network::*, scuttlebutt::*, theme::*},
status::{device::*, network::*, scuttlebutt::*},
};
@ -28,6 +28,7 @@ pub fn mount_peachpub_routes(rocket: Rocket<Build>) -> Rocket<Build> {
shutdown_cmd,
power_menu,
settings_menu,
set_theme,
],
)
.mount(

View File

@ -13,8 +13,8 @@ use rocket_dyn_templates::{tera::Context, Template};
use peach_lib::{error::PeachError, password_utils};
use crate::error::PeachWebError;
use crate::utils;
use crate::utils::TemplateOrRedirect;
//use crate::DisableAuth;
use crate::RocketConfig;
// HELPERS AND STRUCTS FOR AUTHENTICATION WITH COOKIES
@ -72,7 +72,11 @@ impl<'r> FromRequest<'r> for Authenticated {
#[get("/login")]
pub fn login(flash: Option<FlashMessage>) -> 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("Login".to_string()));
@ -166,7 +170,11 @@ pub fn save_reset_password_form(password_form: ResetPasswordForm) -> Result<(),
/// and is specifically for users who have forgotten their password.
#[get("/reset_password")]
pub fn reset_password(flash: Option<FlashMessage>) -> 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("Reset Password".to_string()));
@ -211,7 +219,11 @@ pub fn reset_password_post(reset_password_form: Form<ResetPasswordForm>) -> Temp
/// to initiate the sending of a new password reset.
#[get("/forgot_password")]
pub fn forgot_password_page(flash: Option<FlashMessage>) -> 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("Send Password Reset".to_string()));
@ -281,7 +293,11 @@ pub fn save_password_form(password_form: PasswordForm) -> Result<(), PeachWebErr
/// Change password request handler. This is used by a user who is already logged in.
#[get("/change_password")]
pub fn change_password(flash: Option<FlashMessage>, _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".to_string()));
context.insert("title", &Some("Change Password".to_string()));

View File

@ -3,16 +3,21 @@ use rocket::{get, request::FlashMessage, State};
use rocket_dyn_templates::{tera::Context, Template};
use crate::routes::authentication::Authenticated;
use crate::utils;
use crate::RocketConfig;
// HELPERS AND ROUTES FOR / (HOME PAGE)
#[get("/")]
pub fn home(_auth: Authenticated, config: &State<RocketConfig>) -> 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("flash_name", &None::<()>);
context.insert("flash_msg", &None::<()>);
@ -28,7 +33,11 @@ pub fn home(_auth: Authenticated, config: &State<RocketConfig>) -> Template {
#[get("/help")]
pub fn help(flash: Option<FlashMessage>) -> 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()));

View File

@ -11,6 +11,7 @@ use rocket::{
use rocket_dyn_templates::Template;
use crate::routes::authentication::Authenticated;
use crate::utils;
// HELPERS AND ROUTES FOR /private
@ -20,6 +21,7 @@ pub struct PrivateContext {
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub title: Option<String>,
pub theme: Option<String>,
}
impl PrivateContext {
@ -29,6 +31,7 @@ impl PrivateContext {
flash_name: None,
flash_msg: None,
title: None,
theme: None,
}
}
}
@ -36,15 +39,21 @@ impl PrivateContext {
/// A private message composition and publication page.
#[get("/private")]
pub fn private(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
// retrieve current ui theme
let theme = utils::get_theme();
let mut context = PrivateContext::build();
context.back = Some("/".to_string());
context.title = Some("Private Messages".to_string());
context.theme = Some(theme);
// 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/messages", &context)
}
@ -56,6 +65,7 @@ pub struct PeerContext {
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub title: Option<String>,
pub theme: Option<String>,
}
impl PeerContext {
@ -65,6 +75,7 @@ impl PeerContext {
flash_name: None,
flash_msg: None,
title: None,
theme: None,
}
}
}
@ -72,15 +83,21 @@ impl PeerContext {
/// A peer menu which allows navigating to lists of friends, follows, followers and blocks.
#[get("/peers")]
pub fn peers(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
// retrieve current ui theme
let theme = utils::get_theme();
let mut context = PeerContext::build();
context.back = Some("/".to_string());
context.title = Some("Scuttlebutt Peers".to_string());
context.theme = Some(theme);
// 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/peers", &context)
}
@ -91,7 +108,10 @@ pub struct Post {
pub text: String,
}
/// 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).
/// 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 = "<post>")]
pub fn publish(post: Form<Post>, _auth: Authenticated) -> Flash<Redirect> {
let post_text = &post.text;
@ -101,6 +121,7 @@ pub fn publish(post: Form<Post>, _auth: Authenticated) -> Flash<Redirect> {
// redirect to the profile template without public key ("home" / local profile)
let pub_key: std::option::Option<&str> = None;
let profile_url = uri!(profile(pub_key));
// consider adding the message reference to the flash message (or render it in the template for
// `profile`
Flash::success(Redirect::to(profile_url), "Published public post")
@ -113,7 +134,9 @@ pub struct PublicKey {
pub key: String,
}
/// 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).
/// 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_key>")]
pub fn follow(pub_key: Form<PublicKey>, _auth: Authenticated) -> Flash<Redirect> {
let public_key = &pub_key.key;
@ -123,12 +146,15 @@ pub fn follow(pub_key: Form<PublicKey>, _auth: Authenticated) -> Flash<Redirect>
// redirect to the profile template with provided public key
let profile_url = uri!(profile(Some(public_key)));
let success_msg = format!("Followed {}", public_key);
Flash::success(Redirect::to(profile_url), success_msg)
}
// HELPERS AND ROUTES FOR /unfollow
/// 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).
/// 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_key>")]
pub fn unfollow(pub_key: Form<PublicKey>, _auth: Authenticated) -> Flash<Redirect> {
let public_key = &pub_key.key;
@ -138,12 +164,15 @@ pub fn unfollow(pub_key: Form<PublicKey>, _auth: Authenticated) -> Flash<Redirec
// redirect to the profile template with provided public key
let profile_url = uri!(profile(Some(public_key)));
let success_msg = format!("Unfollowed {}", public_key);
Flash::success(Redirect::to(profile_url), success_msg)
}
// HELPERS AND ROUTES FOR /block
/// 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).
/// 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_key>")]
pub fn block(pub_key: Form<PublicKey>, _auth: Authenticated) -> Flash<Redirect> {
let public_key = &pub_key.key;
@ -153,6 +182,7 @@ pub fn block(pub_key: Form<PublicKey>, _auth: Authenticated) -> Flash<Redirect>
// redirect to the profile template with provided public key
let profile_url = uri!(profile(Some(public_key)));
let success_msg = format!("Blocked {}", public_key);
Flash::success(Redirect::to(profile_url), success_msg)
}
@ -164,6 +194,7 @@ pub struct ProfileContext {
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub title: Option<String>,
pub theme: Option<String>,
}
impl ProfileContext {
@ -173,6 +204,7 @@ impl ProfileContext {
flash_name: None,
flash_msg: None,
title: None,
theme: None,
}
}
}
@ -184,15 +216,21 @@ pub fn profile(
flash: Option<FlashMessage>,
_auth: Authenticated,
) -> Template {
// retrieve current ui theme
let theme = utils::get_theme();
let mut context = ProfileContext::build();
context.back = Some("/".to_string());
context.title = Some("Profile".to_string());
context.theme = Some(theme);
// 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)
}
@ -204,6 +242,7 @@ pub struct FriendsContext {
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub title: Option<String>,
pub theme: Option<String>,
}
impl FriendsContext {
@ -213,6 +252,7 @@ impl FriendsContext {
flash_name: None,
flash_msg: None,
title: None,
theme: None,
}
}
}
@ -221,9 +261,13 @@ impl FriendsContext {
/// key of the peer.
#[get("/friends")]
pub fn friends(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
// retrieve current ui theme
let theme = utils::get_theme();
let mut context = FriendsContext::build();
context.back = Some("/scuttlebutt/peers".to_string());
context.title = Some("Friends".to_string());
context.theme = Some(theme);
// check to see if there is a flash message to display
if let Some(flash) = flash {
@ -231,6 +275,7 @@ pub fn friends(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
Template::render("scuttlebutt/peers_list", &context)
}
@ -242,6 +287,7 @@ pub struct FollowsContext {
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub title: Option<String>,
pub theme: Option<String>,
}
impl FollowsContext {
@ -251,6 +297,7 @@ impl FollowsContext {
flash_name: None,
flash_msg: None,
title: None,
theme: None,
}
}
}
@ -259,9 +306,13 @@ impl FollowsContext {
/// key of the peer.
#[get("/follows")]
pub fn follows(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
// retrieve current ui theme
let theme = utils::get_theme();
let mut context = FollowsContext::build();
context.back = Some("/scuttlebutt/peers".to_string());
context.title = Some("Follows".to_string());
context.theme = Some(theme);
// check to see if there is a flash message to display
if let Some(flash) = flash {
@ -269,6 +320,7 @@ pub fn follows(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
Template::render("scuttlebutt/peers_list", &context)
}
@ -280,6 +332,7 @@ pub struct FollowersContext {
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub title: Option<String>,
pub theme: Option<String>,
}
impl FollowersContext {
@ -289,6 +342,7 @@ impl FollowersContext {
flash_name: None,
flash_msg: None,
title: None,
theme: None,
}
}
}
@ -297,9 +351,13 @@ impl FollowersContext {
/// key of the peer.
#[get("/followers")]
pub fn followers(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
// retrieve current ui theme
let theme = utils::get_theme();
let mut context = FollowersContext::build();
context.back = Some("/scuttlebutt/peers".to_string());
context.title = Some("Followers".to_string());
context.theme = Some(theme);
// check to see if there is a flash message to display
if let Some(flash) = flash {
@ -307,6 +365,7 @@ pub fn followers(flash: Option<FlashMessage>, _auth: Authenticated) -> Template
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
Template::render("scuttlebutt/peers_list", &context)
}
@ -318,6 +377,7 @@ pub struct BlocksContext {
pub flash_name: Option<String>,
pub flash_msg: Option<String>,
pub title: Option<String>,
pub theme: Option<String>,
}
impl BlocksContext {
@ -327,6 +387,7 @@ impl BlocksContext {
flash_name: None,
flash_msg: None,
title: None,
theme: None,
}
}
}
@ -335,9 +396,13 @@ impl BlocksContext {
/// key of the peer.
#[get("/blocks")]
pub fn blocks(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
// retrieve current ui theme
let theme = utils::get_theme();
let mut context = BlocksContext::build();
context.back = Some("/scuttlebutt/peers".to_string());
context.title = Some("Blocks".to_string());
context.theme = Some(theme);
// check to see if there is a flash message to display
if let Some(flash) = flash {
@ -345,5 +410,6 @@ pub fn blocks(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
Template::render("scuttlebutt/peers_list", &context)
}

View File

@ -12,13 +12,18 @@ use peach_lib::config_manager;
use crate::error::PeachWebError;
use crate::routes::authentication::Authenticated;
use crate::utils;
// HELPERS AND ROUTES FOR /settings/admin
/// Administrator settings menu.
#[get("/")]
pub fn admin_menu(flash: Option<FlashMessage>, _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".to_string()));
context.insert("title", &Some("Administrator Settings".to_string()));
@ -36,7 +41,11 @@ pub fn admin_menu(flash: Option<FlashMessage>, _auth: Authenticated) -> Template
/// View and delete currently configured admin.
#[get("/configure")]
pub fn configure_admin(flash: Option<FlashMessage>, _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".to_string()));
context.insert("title", &Some("Configure Admin".to_string()));
@ -81,7 +90,11 @@ pub fn save_add_admin_form(admin_form: AddAdminForm) -> Result<(), PeachWebError
#[get("/add")]
pub fn add_admin(flash: Option<FlashMessage>, _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()));

View File

@ -16,6 +16,7 @@ use peach_lib::{
use crate::{
context::dns::ConfigureDNSContext, error::PeachWebError, routes::authentication::Authenticated,
utils,
};
#[derive(Debug, Deserialize, FromForm)]
@ -76,11 +77,14 @@ pub fn save_dns_configuration(dns_form: DnsForm) -> Result<(), PeachWebError> {
#[get("/dns")]
pub fn configure_dns(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
let mut context = ConfigureDNSContext::build();
// retrieve current ui theme
let theme = utils::get_theme();
let mut context = ConfigureDNSContext::build();
// set back icon link to network route
context.back = Some("/settings/network".to_string());
context.title = Some("Configure DNS".to_string());
context.theme = Some(theme);
// check to see if there is a flash message to display
if let Some(flash) = flash {

View File

@ -2,6 +2,7 @@ use rocket::{get, request::FlashMessage, State};
use rocket_dyn_templates::{tera::Context, Template};
use crate::routes::authentication::Authenticated;
use crate::utils;
use crate::RocketConfig;
// HELPERS AND ROUTES FOR /settings
@ -13,7 +14,11 @@ pub fn settings_menu(
flash: Option<FlashMessage>,
config: &State<RocketConfig>,
) -> 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("Settings".to_string()));

View File

@ -3,3 +3,4 @@ pub mod dns;
pub mod menu;
pub mod network;
pub mod scuttlebutt;
pub mod theme;

View File

@ -15,6 +15,7 @@ use rocket::{
use rocket_dyn_templates::{tera::Context, Template};
use crate::routes::authentication::Authenticated;
use crate::utils;
#[derive(Debug, Deserialize, FromForm)]
pub struct SbotConfigForm {
@ -58,10 +59,14 @@ pub struct SbotConfigForm {
/// Scuttlebutt settings menu.
#[get("/")]
pub fn ssb_settings_menu(flash: Option<FlashMessage>, _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()));
@ -77,6 +82,9 @@ pub fn ssb_settings_menu(flash: Option<FlashMessage>, _auth: Authenticated) -> T
/// Sbot configuration page (includes form for updating configuration parameters).
#[get("/configure")]
pub fn configure_sbot(flash: Option<FlashMessage>, _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);
@ -85,6 +93,7 @@ pub fn configure_sbot(flash: Option<FlashMessage>, _auth: Authenticated) -> Temp
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);
@ -135,16 +144,14 @@ pub fn configure_sbot_post(
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),
_ => (),
if let Err(e) = systemctl_sbot_cmd("enable") {
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),
_ => (),
if let Err(e) = systemctl_sbot_cmd("disable") {
warn!("Failed to disable go-sbot.service: {}", e)
}
}
};

View File

@ -0,0 +1,16 @@
use rocket::{get, response::Redirect};
use crate::routes::authentication::Authenticated;
use crate::{utils, utils::Theme};
/// Set the user-interface theme according to the query parameter value.
#[get("/theme?<theme>")]
pub fn set_theme(_auth: Authenticated, theme: &str) -> Redirect {
match theme {
"light" => utils::set_theme(Theme::Light),
"dark" => utils::set_theme(Theme::Dark),
_ => (),
}
Redirect::to("/")
}

View File

@ -3,18 +3,24 @@ use rocket::{get, State};
use rocket_dyn_templates::{tera::Context, Template};
use crate::routes::authentication::Authenticated;
use crate::utils;
use crate::RocketConfig;
// HELPERS AND ROUTES FOR /status/scuttlebutt
#[get("/scuttlebutt")]
pub fn scuttlebutt_status(_auth: Authenticated, config: &State<RocketConfig>) -> Template {
// retrieve current ui theme
let theme = utils::get_theme();
// 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();
context.insert("theme", &theme);
context.insert("sbot_status", &sbot_status);
context.insert("sbot_config", &sbot_config);
context.insert("flash_name", &None::<()>);

View File

@ -1,9 +1,33 @@
pub mod monitor;
use rocket_dyn_templates::Template;
use log::info;
use rocket::response::{Redirect, Responder};
use rocket::serde::Serialize;
use rocket_dyn_templates::Template;
use crate::THEME;
// THEME FUNCTIONS
#[derive(Debug, Copy, Clone)]
pub enum Theme {
Light,
Dark,
}
pub fn get_theme() -> String {
let current_theme = THEME.read().unwrap();
match *current_theme {
Theme::Dark => "dark".to_string(),
_ => "light".to_string(),
}
}
pub fn set_theme(theme: Theme) {
info!("set ui theme to: {:?}", theme);
let mut writable_theme = THEME.write().unwrap();
*writable_theme = theme;
}
// HELPER FUNCTIONS

View File

@ -166,6 +166,40 @@
--light: var(--light-gray);
--dark: var(--near-black);
/* LIGHT (default) THEME VARIABLES */
--background: var(--moon-gray);
--button-background: var(--light-gray);
--circle-background: var(--light-gray);
--circle-small-hover-background: var(--white);
--color-ssb: var(--hot-pink);
--color-settings: var(--purple);
--color-status: var(--green);
--color-info: var(--info);
--capsule-background: var(--light-gray);
--text-color-normal: var(--near-black);
--text-color-gray: var(--mid-gray);
--text-color-light-gray: var(--moon-gray);
--text-color-light: var(--white);
--icon-normal: invert(0%) sepia(1%) saturate(100%) hue-rotate(79deg) brightness(86%) contrast(87%);
--icon-gray: invert(72%) sepia(8%) saturate(14%) hue-rotate(316deg) brightness(93%) contrast(92%);
--icon-light: invert();
/* DARK THEME VARIABLES */
/*--background-dark: var(--dark-gray);*/
--background-dark: #222;
--button-background-dark: var(--mid-gray);
/*--capsule-background-dark: var(--light-gray);*/
--capsule-background-dark: #333;
--circle-background-dark: var(--silver);
--circle-small-hover-background-dark: var(--moon-gray);
}
/* we need to add shades for each accent colour
*
* --info-100
@ -174,4 +208,3 @@
* --info-400
* --info-500
*/
}

View File

@ -26,6 +26,8 @@
* - NAVIGATION
* - PARAGRAPHS
* - SWITCHES / SLIDERS
* - TEXT
* - TITLES
*
\* ------------------------------ */
@ -128,13 +130,57 @@
*/
body {
background-color: var(--moon-gray);
background-color: var(--background);
height: 100%;
display: flex;
flex-direction: column;
margin: 0;
}
/*
* BORDERS
*/
.border-circle-small {
border: 4px solid;
}
.border-settings {
border-color: var(--color-settings);
}
.border-ssb {
border-color: var(--color-ssb);
}
.border-status {
border-color: var(--color-status);
}
.border-primary {
border-color: var(--primary);
}
.border-success {
border-color: var(--success);
}
.border-info {
border-color: var(--info);
}
.border-warning {
border-color: var(--warning);
}
.border-danger {
border-color: var(--danger);
}
.border-dark-gray {
border-color: var(--dark-gray);
}
/*
* BUTTONS
*/
@ -146,7 +192,7 @@ body {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: var(--near-black);
color: var(--button-text-color);
cursor: pointer;
padding: 10px;
text-align: center;
@ -169,37 +215,42 @@ body {
}
.button-primary {
background-color: var(--light-gray);
background-color: var(--button-background);
}
.button-primary:hover {
background-color: var(--primary);
color: var(--button-text-hover-color);
}
.button-primary:focus {
background-color: var(--primary);
color: var(--button-text-hover-color);
outline: none;
}
.button-secondary {
background-color: var(--light-gray);
background-color: var(--button-background);
}
.button-secondary:hover {
background-color: var(--light-silver);
color: var(--button-text-hover-color);
}
.button-secondary:focus {
background-color: var(--light-silver);
color: var(--button-text-hover-color);
outline: none;
}
.button-warning {
background-color: var(--light-gray);
background-color: var(--button-background);
}
.button-warning:hover {
background-color: var(--light-red);
color: var(--button-text-hover-color);
}
.button-warning:focus {
@ -213,9 +264,9 @@ body {
.capsule {
padding: 1rem;
border: var(--border-width-1) solid;
border-style: solid;
border-radius: var(--border-radius-3);
background-color: var(--light-gray);
background-color: var(--capsule-background);
/* margin-top: 1rem; */
/* margin-bottom: 1rem; */
}
@ -272,6 +323,7 @@ body {
}
.card-text {
color: var(--text-color);
margin: 0;
font-size: var(--font-size-5);
padding-bottom: 0.3rem;
@ -294,7 +346,7 @@ body {
.circle {
align-items: center;
background: var(--light-gray);
background: var(--circle-background);
border-radius: 50%;
box-shadow: var(--box-shadow-3);
display: flex;
@ -307,6 +359,15 @@ body {
width: 5rem;
}
.circle-small:hover {
background: var(--circle-small-hover-background);
}
.circle-small:focus {
background: var(--circle-small-hover-background);
outline: none;
}
.circle-medium {
height: 8rem;
width: 8rem;
@ -391,30 +452,6 @@ body {
background-color: var(--light);
}
.primary-border {
border-color: var(--primary);
}
.success-border {
border-color: var(--success);
}
.info-border {
border-color: var(--info);
}
.warning-border {
border-color: var(--warning);
}
.danger-border {
border-color: var(--danger);
}
.dark-gray-border {
border-color: var(--dark-gray);
}
/*
* GRIDS
*/
@ -555,6 +592,30 @@ body {
html {
height: 100%;
--background: var(--background);
--button-background: var(--button-background);
--button-text-color: var(--text-color-normal);
--button-text-hover-color: var(--text-color-normal);
--circle-background: var(--circle-background);
--circle-small-hover-background: var(--circle-small-hover-background);
--icon-color: var(--icon-normal);
--nav-icon-color: var(--nav-icon-color);
--text-color: var(--text-color-normal);
}
html[data-theme='dark'] {
--background: var(--background-dark);
--button-background: var(--button-background-dark);
--button-text-color: var(--text-color-light);
--button-text-hover-color: var(--text-color-normal);
--capsule-background: var(--capsule-background-dark);
--circle-background: var(--circle-background-dark);
--circle-small-hover-background: var(--circle-small-hover-background-dark);
--icon-color: var(--icon-light);
--nav-icon-color: var(--nav-icon-color-light);
--text-color: var(--text-color-light);
--text-color-gray: var(--text-color-light-gray);
}
/*
@ -573,28 +634,21 @@ html {
* FONTS
*/
.font-normal {
color: var(--text-color);
}
.font-near-black {
color: var(--near-black);
}
.font-gray {
color: var(--mid-gray);
color: var(--text-color-gray);
}
.font-light-gray {
color: var(--silver);
}
.font-success {
color: var(--success);
}
.font-warning {
color: var(--warning);
}
.font-failure {
color: var(--danger);
/* color: var(--silver); */
color: var(--text-color-light-gray);
}
/*
@ -637,12 +691,12 @@ form {
/* icon-active: sets color of icon svg to near-black */
.icon-active {
filter: invert(0%) sepia(1%) saturate(4171%) hue-rotate(79deg) brightness(86%) contrast(87%);
filter: var(--icon-color);
}
/* icon-inactive: sets color of icon svg to gray */
.icon-inactive {
filter: invert(72%) sepia(8%) saturate(14%) hue-rotate(316deg) brightness(93%) contrast(92%);
filter: var(--icon-gray);
}
/*
@ -699,10 +753,11 @@ form {
font-family: var(--sans-serif);
font-size: var(--font-size-7);
display: block;
margin-bottom: 2px;
/* margin-bottom: 2px; */
}
.label-medium {
color: var(--text-color);
font-size: var(--font-size-3);
display: block;
}
@ -728,7 +783,7 @@ form {
.link {
text-decoration: none;
color: var(--font-near-black);
/* color: var(--font-near-black); */
}
/*
@ -861,6 +916,7 @@ meter::-moz-meter-bar {
}
.nav-title {
color: var(--text-color);
font-family: var(--sans-serif);
font-size: var(--font-size-4);
font-weight: normal;
@ -976,6 +1032,26 @@ input:checked + .slider:before {
border-radius: 50%;
}
/*
* TEXT
*/
.text-info {
color: var(--info);
}
.text-danger {
color: var(--danger);
}
.text-success {
color: var(--success);
}
.text-warning {
color: var(--warning);
}
/*
* TITLES
*/

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 57 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,6 +1,6 @@
<!doctype html>
<html lang="en">
<html lang="en"{% if theme %} data-theme="{{ theme }}"{% endif %}>
<head>
<meta charset="utf-8">
<title>PeachCloud</title>

View File

@ -2,16 +2,9 @@
{%- block card %}
<!-- HELP MENU -->
<div class="card center">
<div class="card-container">
<div class="capsule capsule-container border-info">
<!-- FLASH MESSAGE -->
<!-- check for flash message and display accordingly -->
{%- if flash_msg and flash_name == "success" %}
<!-- display success message -->
<div class="center-text flash-message font-success" style="padding-left: 5px;">{{ flash_msg }}.</div>
{%- elif flash_msg and flash_name == "error" %}
<!-- display error message -->
<div class="center-text flash-message font-failure" style="padding-left: 5px;">{{ flash_msg }}.</div>
{%- endif %}
{% include "snippets/flash_message" %}
</div>
</div>
{%- endblock card -%}

View File

@ -5,27 +5,39 @@
<!-- top-left -->
<!-- PEERS LINK AND ICON -->
<a class="top-left" href="/scuttlebutt/peers" title="Scuttlebutt Peers">
<div class="circle circle-small">
<div class="circle circle-small border-circle-small border-ssb">
<img class="icon-medium" src="/icons/users.svg">
</div>
</a>
<!-- top-middle -->
<!-- CURRENT USER LINK AND ICON -->
<a class="top-middle" href="/scuttlebutt/profile" title="Profile">
<div class="circle circle-small">
<div class="circle circle-small border-circle-small border-ssb">
<img class="icon-medium" src="/icons/user.svg">
</div>
</a>
<!-- top-right -->
<!-- MESSAGES LINK AND ICON -->
<a class="top-right" href="/scuttlebutt/private" title="Private Messages">
<div class="circle circle-small">
<div class="circle circle-small border-circle-small border-ssb">
<img class="icon-medium" src="/icons/envelope.svg">
</div>
</a>
<!-- middle -->
<a class="middle">
<div class="circle circle-large {% if sbot_status.state == "active" %}circle-success{% else %}circle-error{% endif %}"></div>
{% if sbot_status.state == "active" %}
<div class="circle circle-large circle-success">
<p style="font-size: 4rem; color: var(--near-black);">^_^</p>
</div>
{% elif sbot_status.state == "inactive" %}
<div class="circle circle-large circle-warning">
<p style="font-size: 4rem; color: var(--near-black);">z_z</p>
</div>
{% else %}
<div class="circle circle-large circle-error">
<p style="font-size: 4rem; color: var(--near-black);">x_x</p>
</div>
{% endif %}
</a>
<!-- bottom-left -->
<!-- SYSTEM STATUS LINK AND ICON -->
@ -34,21 +46,21 @@
{% else -%}
<a class="bottom-left" href="/status" title="Status">
{%- endif -%}
<div class="circle circle-small">
<div class="circle circle-small border-circle-small {% if sbot_status.state == "active" %}border-success{% elif sbot_status.state == "inactive" %}border-warning{% else %}border-danger{% endif %}">
<img class="icon-medium" src="/icons/heart-pulse.svg">
</div>
</a>
<!-- bottom-middle -->
<!-- PEACHCLOUD GUIDEBOOK LINK AND ICON -->
<a class="bottom-middle" href="/help" title="Help Menu">
<div class="circle circle-small">
<div class="circle circle-small border-circle-small border-info">
<img class="icon-medium" src="/icons/book.svg">
</div>
</a>
<!-- bottom-right -->
<!-- SYSTEM SETTINGS LINK AND ICON -->
<a class="bottom-right" href="/settings" title="Settings Menu">
<div class="circle circle-small">
<div class="circle circle-small border-circle-small border-settings">
<img class="icon-medium" src="/icons/cog.svg">
</div>
</a>

View File

@ -3,7 +3,7 @@
<!-- LOGIN FORM -->
<div class="card center">
<div class="card-container">
<form id="login_form" action="/login" method="post">
<form id="login_form" class="center" action="/login" method="post">
<!-- input for username -->
<input id="username" name="username" class="center input" type="text" placeholder="Username" title="Username for authentication" autofocus/>
<!-- input for password -->
@ -12,12 +12,10 @@
<input id="loginUser" class="button button-primary center" title="Login" type="submit" value="Login">
</div>
</form>
<!-- FLASH MESSAGE -->
{% include "snippets/flash_message" %}
<div class="center-text" style="margin-top: 25px;">
<a href="/settings/admin/forgot_password" class="label-small link">Forgot Password?</a>
<div class="center-text" style="margin-top: 25px;">
<a href="/settings/admin/forgot_password" class="label-small link font-gray">Forgot Password?</a>
</div>
</div>
</div>

View File

@ -17,13 +17,25 @@
<!-- Bottom nav bar -->
<nav class="nav-bar">
<a class="nav-item" href="https://scuttlebutt.nz/">
<img class="icon-medium nav-icon-left" title="Scuttlebutt Website" src="/icons/hermies.png" alt="Secure Scuttlebutt">
<img class="icon-medium nav-icon-left" title="Scuttlebutt Website" src="/icons/hermies_hex{% if theme and theme == "dark" %}_light{% endif %}.svg" alt="Secure Scuttlebutt">
</a>
<a class="nav-item" href="/">
<img class="icon nav-icon-left" src="/icons/peach-icon.png" alt="PeachCloud" title="Home">
</a>
<a class="nav-item" href="/power">
<img class="icon-medium nav-icon-right icon-active" title="Shutdown" src="/icons/power.svg" alt="Power switch">
{# only render a theme-switcher icon if the `theme` variable has been set #}
{% if theme and theme == "light" %}
<a class="nav-item" href="/theme?theme=dark">
<img class="icon-medium nav-icon-right icon-active" title="Toggle theme" src="/icons/moon.png" alt="Moon">
</a>
{% elif theme and theme == "dark" %}
<a class="nav-item" href="/theme?theme=light">
<img class="icon-medium nav-icon-right icon-active" title="Toggle theme" src="/icons/sun.png" alt="Sun">
</a>
{% else %}
{# render hidden element to maintain correctly alignment of other bottom nav icons #}
<a class="nav-item" style="visibility: hidden;" href="/theme?theme=light">
<img class="icon-medium nav-icon-right icon-active" title="Toggle theme" src="/icons/sun.png" alt="Sun">
</a>
{% endif %}
</nav>
{%- endblock nav -%}

View File

@ -2,7 +2,7 @@
{%- block card %}
<!-- SCUTTLEBUTT MESSAGES -->
<div class="card center">
<div class="card-container">
<div class="capsule capsule-container border-ssb">
<!-- FLASH MESSAGE -->
{% include "snippets/flash_message" %}
</div>

View File

@ -5,7 +5,7 @@
<!-- PROFILE INFO BOX -->
<div class="capsule capsule-profile" title="Scuttlebutt account profile information">
<!-- edit profile button -->
<img id="editProfile" class="icon-small nav-icon-right" src="/icons/pencil.svg" alt="Profile picture">
<img id="editProfile" class="icon-small icon-active nav-icon-right" src="/icons/pencil.svg" alt="Profile picture">
<!-- PROFILE BIO -->
<!-- profile picture -->
<img id="profilePicture" class="icon-large" src="{ image_path }" alt="Profile picture">
@ -15,14 +15,14 @@
<p id="profileDescription" style="margin-top: 1rem" class="card-text" title="Description">{ description }</p>
</div>
<!-- PUBLIC POST FORM -->
<form id="postForm" action="/scuttlebutt/post" method="post">
<form id="postForm" class="center" action="/scuttlebutt/post" method="post">
<!-- input for message contents -->
<textarea id="publicPost" class="center input message-input" title="Compose Public Post"></textarea>
<input id="publishPost" class="button button-primary center" title="Publish" type="submit" value="Publish">
</form>
<!-- BUTTONS -->
<!-- TODO: each of these buttons needs to be a form with a public key -->
<div id="buttons">
<div id="buttons" style="margin-top: 2rem;">
<a id="followPeer" class="button button-primary center" href="/scuttlebutt/follow" title="Follow Peer">Follow</a>
<a id="blockPeer" class="button button-warning center" href="/scuttlebutt/block" title="Block Peer">Block</a>
<a id="privateMessage" class="button button-primary center" href="/scuttlebutt/private_message" title="Private Message">Private Message</a>

View File

@ -2,7 +2,7 @@
{%- block card %}
<!-- CHANGE PASSWORD FORM -->
<div class="card center">
<form id="changePassword" action="/settings/admin/change_password" method="post">
<form id="changePassword" class="center" action="/settings/admin/change_password" method="post">
<!-- input for current password -->
<input id="currentPassword" class="center input" name="current_password" type="password" placeholder="Current password" title="Current password" autofocus>
<!-- input for new password -->

View File

@ -3,9 +3,9 @@
<!-- CONFIGURE ADMIN PAGE -->
<div class="card center">
<div class="text-container">
<h4> Current Admins </h4>
<h4 class="font-normal">Current Admins</h4>
{% if not ssb_admin_ids %}
<div>
<div class="card-text">
There are no currently configured admins.
</div>
{% else %}

View File

@ -15,57 +15,59 @@
<!-- SBOT CONFIGURATION FORM -->
<div class="card center">
<form id="sbotConfig" class="center" action="/settings/scuttlebutt/configure" method="post">
<div class="center" style="display: flex; flex-direction: column;" title="Number of hops to replicate">
<label for="hops" class="label-small">HOPS</label>
<div class="center" style="display: flex; flex-direction: column; margin-bottom: 2rem;" title="Number of hops to replicate">
<label for="hops" class="label-small font-gray">HOPS</label>
<div id="hops" style="display: flex; justify-content: space-evenly;">
<input type="radio" id="hops_0" name="hops" value="0"{% if hops == 0 %} checked{% endif %}>
<label for="hops_0">0</label>
<input type="radio" id="hops_1" name="hops" value="1"{% if hops == 1 %} checked{% endif %}>
<label for="hops_1">1</label>
<input type="radio" id="hops_2" name="hops" value="2"{% if hops == 2 %} checked{% endif %}>
<label for="hops_2">2</label>
<input type="radio" id="hops_3" name="hops" value="3"{% if hops == 3 %} checked{% endif %}>
<label for="hops_3">3</label>
<input type="radio" id="hops_4" name="hops" value="4"{% if hops == 4 %} checked{% endif %}>
<label for="hops_4">4</label><br>
<div>
<input type="radio" id="hops_0" name="hops" value="0"{% if hops == 0 %} checked{% endif %}>
<label class="font-normal" for="hops_0">0</label>
</div>
<div>
<input type="radio" id="hops_1" name="hops" value="1"{% if hops == 1 %} checked{% endif %}>
<label class="font-normal" for="hops_1">1</label>
</div>
<div>
<input type="radio" id="hops_2" name="hops" value="2"{% if hops == 2 %} checked{% endif %}>
<label class="font-normal" for="hops_2">2</label>
</div>
<div>
<input type="radio" id="hops_3" name="hops" value="3"{% if hops == 3 %} checked{% endif %}>
<label class="font-normal" for="hops_3">3</label>
</div>
<div>
<input type="radio" id="hops_4" name="hops" value="4"{% if hops == 4 %} checked{% endif %}>
<label class="font-normal" for="hops_4">4</label>
</div>
</div>
</div>
<br>
<div class="center" style="display: flex; justify-content: space-between;">
<div style="display: flex; flex-direction: column; width: 60%;" title="IP address on which the sbot runs">
<label for="ip" class="label-small">IP ADDRESS</label>
<div style="display: flex; flex-direction: column; width: 60%; margin-bottom: 2rem;" title="IP address on which the sbot runs">
<label for="ip" class="label-small font-gray">IP ADDRESS</label>
<input type="text" id="ip" name="lis_ip" value="{{ listen_addr.0 }}">
</div>
<div style="display: flex; flex-direction: column; width: 20%;" title="Port on which the sbot runs">
<label for="port" class="label-small">PORT</label>
<div style="display: flex; flex-direction: column; width: 20%; margin-bottom: 2rem;" title="Port on which the sbot runs">
<label for="port" class="label-small font-gray">PORT</label>
<input type="text" id="port" name="lis_port" value="{{ listen_addr.1 }}">
</div>
</div>
<br>
<div class="center" style="display: flex; flex-direction: column;" title="Network key (aka 'caps key') to define the Scuttleverse in which the sbot operates in">
<label for="network_key" class="label-small">NETWORK KEY</label>
<div class="center" style="display: flex; flex-direction: column; margin-bottom: 2rem;" title="Network key (aka 'caps key') to define the Scuttleverse in which the sbot operates in">
<label for="network_key" class="label-small font-gray">NETWORK KEY</label>
<input type="text" id="network_key" name="shscap" value="{{ sbot_config.shscap }}">
</div>
<br>
<div class="center" style="display: flex; flex-direction: column;" title="Directory in which the sbot database is saved">
<label for="database_dir" class="label-small">DATABASE DIRECTORY</label>
<div class="center" style="display: flex; flex-direction: column; margin-bottom: 2rem;" title="Directory in which the sbot database is saved">
<label for="database_dir" class="label-small font-gray">DATABASE DIRECTORY</label>
<input type="text" id="database_dir" name="repo" value="{{ sbot_config.repo }}">
</div>
<br>
<div class="center">
<input type="checkbox" id="lanBroadcast" name="localadv"{% if sbot_config.localadv == true %} checked{% endif %}>
<label for="lanBroadcast" title="Broadcast the IP and port of this sbot instance so that local peers can discovery it and attempt to connect">Enable LAN Broadcasting</label><br>
<br>
<input type="checkbox" id="lanDiscovery" name="localdiscov"{% if sbot_config.localdiscov == true %} checked{% endif %}>
<label for="lanDiscovery" title="Listen for the presence of local peers and attempt to connect if found">Enable LAN Discovery</label><br>
<br>
<input type="checkbox" id="startup" name="startup"{% if run_on_startup == "enabled" %} checked{% endif %}>
<label for="startup" title="Run the pub automatically on system startup">Run pub on system startup</label><br>
<br>
<input type="checkbox" id="lanBroadcast" style="margin-bottom: 1rem;" name="localadv"{% if sbot_config.localadv == true %} checked{% endif %}>
<label class="font-normal" for="lanBroadcast" title="Broadcast the IP and port of this sbot instance so that local peers can discovery it and attempt to connect">Enable LAN Broadcasting</label><br>
<input type="checkbox" id="lanDiscovery" style="margin-bottom: 1rem;" name="localdiscov"{% if sbot_config.localdiscov == true %} checked{% endif %}>
<label class="font-normal" for="lanDiscovery" title="Listen for the presence of local peers and attempt to connect if found">Enable LAN Discovery</label><br>
<input type="checkbox" id="startup" style="margin-bottom: 1rem;" name="startup"{% if run_on_startup == "enabled" %} checked{% endif %}>
<label class="font-normal" for="startup" title="Run the pub automatically on system startup">Run pub when computer starts</label><br>
<input type="checkbox" id="repair" name="repair"{% if sbot_config.repair == true %} checked{% endif %}>
<label for="repair" title="Attempt to repair the filesystem before starting the pub">Attempt filesystem repair when pub starts</label><br>
<label class="font-normal" for="repair" title="Attempt to repair the filesystem when starting the pub">Attempt filesystem repair when pub starts</label>
</div>
<br>
<!-- hidden input elements for all other config variables -->
<input type="hidden" id="debugdir" name="debugdir" value="{{ sbot_config.debugdir }}">
<input type="hidden" id="hmac" name="hmac" value="{{ sbot_config.hmac }}">
@ -75,7 +77,7 @@
<input type="hidden" id="promisc" name="promisc" value="{{ sbot_config.promisc }}">
<input type="hidden" id="nounixsock" name="nounixsock" value="{{ sbot_config.nounixsock }}">
<!-- BUTTONS -->
<input id="saveConfig" class="button button-primary center" type="submit" title="Save configuration parameters to file" value="Save">
<input id="saveConfig" class="button button-primary center" style="margin-top: 2rem;" type="submit" title="Save configuration parameters to file" value="Save">
<input id="saveRestartConfig" class="button button-primary center" type="submit" title="Save configuration parameters to file and then (re)start the pub" value="Save & Restart" formaction="/settings/scuttlebutt/configure?restart=true">
<a id="restoreDefaults" class="button button-warning center" href="/settings/scuttlebutt/configure/default" title="Restore default configuration parameters and save them to file">Restore Defaults</a>
</form>

View File

@ -1,11 +1,11 @@
<!-- check for flash message and display accordingly -->
{% if flash_msg and flash_name == "success" %}
<!-- display success message -->
<div class="capsule center-text flash-message font-success">{{ flash_msg }}.</div>
<div class="capsule center-text flash-message font-normal border-success">{{ flash_msg }}.</div>
{%- elif flash_msg and flash_name == "info" %}
<!-- display info message -->
<div class="capsule center-text flash-message font-info">{{ flash_msg }}.</div>
<div class="capsule center-text flash-message font-normal border-info">{{ flash_msg }}.</div>
{%- elif flash_msg and flash_name == "error" %}
<!-- display error message -->
<div class="capsule center-text flash-message font-failure">{{ flash_msg }}.</div>
{%- endif -%}
<div class="capsule center-text flash-message font-normal border-danger">{{ flash_msg }}.</div>
{%- endif -%}

View File

@ -6,21 +6,26 @@
{% set mem = sbot_status.memory / 1024 / 1024 | round -%}
{%- else -%}
{% set mem = "X" -%}
{%- endif -%}
{%- if sbot_status.blobstore -%}
{% set blobs = sbot_status.blobstore / 1024 / 1024 | round -%}
{%- else -%}
{% set blobs = "X" -%}
{%- endif -%}
<!-- SCUTTLEBUTT STATUS -->
<div class="card center">
<!-- SBOT INFO BOX -->
<div class="capsule capsule-container {% if sbot_status.state == "active" %}success-border{% else %}warning-border{% endif %}">
<div class="capsule capsule-container {% if sbot_status.state == "active" %}border-success{% elif sbot_status.state == "inactive" %}border-warning{% else %}border-danger{% endif %}">
<!-- SBOT STATUS GRID -->
<div class="two-grid" title="go-sbot process state">
<!-- top-right config icon -->
<a class="link two-grid-top-right" href="/settings/scuttlebutt" title="Configure Scuttlebutt settings">
<img id="configureNetworking" class="icon-small" src="/icons/cog.svg" alt="Configure">
<img id="configureNetworking" class="icon-small icon-active" src="/icons/cog.svg" alt="Configure">
</a>
<!-- left column -->
<!-- go-sbot state icon with label -->
<div class="grid-column-1">
<img id="sbotStateIcon" class="center icon {% if sbot_status.state == "inactive" %}icon-inactive{% endif %}" src="/icons/hermies.svg" alt="Hermies">
<img id="sbotStateIcon" class="center icon {% if sbot_status.state == "active" %}icon-active{% else %}icon-inactive{% endif %}" src="/icons/hermies.svg" alt="Hermies">
<label id="sbotStateLabel" for="sbotStateIcon" class="center label-small font-gray" style="margin-top: 0.5rem;" title="Sbot state">{{ sbot_status.state | upper }}</label>
</div>
<!-- right column -->
@ -78,25 +83,25 @@
<!-- THREE-ACROSS STACK -->
<div class="three-grid card-container" style="margin-top: 1rem;">
<div class="stack">
<img class="icon" title="Hops" src="/icons/orbits.png">
<img class="icon icon-active" title="Hops" src="/icons/orbits.png">
<div class="flex-grid" style="padding-top: 0.5rem;">
<label class="label-medium" style="padding-right: 3px;" title="Replication hops">{{ sbot_config.hops }}</label>
</div>
<label class="label-small font-gray">HOPS</label>
</div>
<div class="stack">
<img class="icon" title="Blobs" src="/icons/image-file.png">
<img class="icon icon-active" title="Blobs" src="/icons/image-file.png">
<div class="flex-grid" style="padding-top: 0.5rem;">
<label class="label-medium" style="padding-right: 3px;" title="Blobstore size in MB">163</label>
<label class="label-small font-near-black">MB</label>
<label class="label-medium{% if sbot_status.state == "inactive" %} font-gray{% endif %}" style="padding-right: 3px;" title="Blobstore size in MB">{{ blobs }}</label>
<label class="label-small{% if sbot_status.state == "inactive" %} font-gray{% else %} font-normal{% endif %}">MB</label>
</div>
<label class="label-small font-gray">BLOBSTORE</label>
</div>
<div class="stack">
<img class="icon{% if not sbot_status.memory %} icon-inactive{% endif %}" title="Memory" src="/icons/ram.png">
<img class="icon{% if sbot_status.memory %} icon-active{% else %} icon-inactive{% endif %}" title="Memory" src="/icons/ram.png">
<div class="flex-grid" style="padding-top: 0.5rem;">
<label class="label-medium{% if sbot_status.state == "inactive" %} font-gray{% endif %}" style="padding-right: 3px;" title="Memory usage of the go-sbot process in MB">{{ mem }}</label>
<label class="label-small {% if sbot_status.state == "inactive" %}font-gray{% else %}font-near-black{% endif %}">MB</label>
<label class="label-small{% if sbot_status.state == "inactive" %} font-gray{% else %} font-normal{% endif %}">MB</label>
</div>
<label class="label-small font-gray">MEMORY</label>
</div>