86 Commits

Author SHA1 Message Date
ec93c9830b Formatting 2022-07-28 14:58:24 +02:00
f6c30a327a Remove debug statements 2022-07-28 14:56:27 +02:00
dbf1c648ae Working in docker 2022-07-28 14:53:45 +02:00
8d9aeb6662 Change default name of go-sbot.service to go-sbot 2022-07-28 14:01:19 +02:00
b24fb387b9 Fix supervisorctl 2022-07-27 19:03:36 +02:00
ceb7e502ce modifications to system commands 2022-07-20 14:26:37 +02:00
6407495292 Merge pull request 'Update go-sbot systemctl commands (remove --user)' (#132) from fix_systemctl_calls into main
Reviewed-on: #132
2022-07-15 09:55:06 +00:00
05c1577f2a upgrade probes version to avoid precise_time error
All checks were successful
continuous-integration/drone/pr Build is passing
2022-07-15 09:00:31 +01:00
add169db07 bump patch version
Some checks failed
continuous-integration/drone/pr Build is failing
2022-07-14 08:57:34 +01:00
fcb17d6802 remove --user and add sudo for systemctl calls 2022-07-14 08:57:22 +01:00
01138eef35 Merge branch 'main' of https://git.coopcloud.tech/PeachCloud/peach-workspace into main7 2022-07-11 13:17:34 +02:00
2637b28380 Bump peach-config to v0.1.26 2022-07-11 13:17:24 +02:00
03a0a51f4d Merge pull request 'Add publish-address function to peach-config' (#130) from publish-address into main
Reviewed-on: #130
2022-07-11 10:25:09 +00:00
eddb167c4c Remove KVT to Value map and retrieve sequence number directly from KVT
All checks were successful
continuous-integration/drone/pr Build is passing
2022-07-07 15:54:19 +01:00
9704269c8a Remove Cargo.lock from .gitignore 2022-07-07 15:44:31 +01:00
466db8ceea Update sbot.rs to new history_stream api
All checks were successful
continuous-integration/drone/pr Build is passing
2022-07-07 13:42:32 +02:00
90badbfe30 Add cargo.lock to .gitignore
Some checks failed
continuous-integration/drone/pr Build is failing
2022-07-07 10:57:27 +02:00
7489916d5f Remove cargo.lock 2022-07-07 10:57:03 +02:00
7daab74b37 Fix golgi import
Some checks failed
continuous-integration/drone/pr Build is failing
2022-07-07 10:51:44 +02:00
58bf306d3b Fix golgi import
Some checks failed
continuous-integration/drone/pr Build is failing
2022-07-07 10:49:49 +02:00
bdac23092a Change changepassword to change-password
Some checks failed
continuous-integration/drone/pr Build is failing
2022-07-07 10:41:36 +02:00
f1ab2caa08 Fix golgi imports
Some checks failed
continuous-integration/drone/pr Build is failing
2022-07-06 12:39:55 +02:00
1fab4f3c43 Merge branch 'main' of https://git.coopcloud.tech/PeachCloud/peach-workspace into main7
Some checks failed
continuous-integration/drone/pr Build is failing
2022-07-06 12:34:45 +02:00
8dcd594dd7 Publish address 2022-07-06 12:34:24 +02:00
fcaa9e29c4 Merge pull request 'Add whoami command to peach-config' (#128) from who-am-i into main
Reviewed-on: #128
2022-07-05 13:10:26 +00:00
c6fc5c2992 Merge branch 'main' into who-am-i
All checks were successful
continuous-integration/drone/pr Build is passing
2022-07-05 12:52:21 +00:00
1258a3697d Cargo fmt
All checks were successful
continuous-integration/drone/pr Build is passing
2022-07-04 14:54:00 +02:00
7e94135839 Whoami
Some checks failed
continuous-integration/drone/pr Build is failing
2022-07-04 14:35:17 +02:00
f002f5cf3e Working on peach-config 2022-07-04 13:23:55 +02:00
fba1e91d8b Merge pull request 'Insert domain into invite' (#125) from insert-invite into main
Reviewed-on: #125
2022-06-30 13:00:46 +00:00
6621a09ec9 Insert domain into invite
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-29 16:23:41 -04:00
170b037248 Merge pull request 'Update configuration sections of README' (#122) from update_readme into main
Reviewed-on: #122
2022-06-28 10:37:12 +00:00
251aaf9237 merge main
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-28 09:16:09 +01:00
123ebc06cc Merge pull request 'Introduce JS for improved back button behaviour' (#121) from js_back_button into main
Reviewed-on: #121
2022-06-28 08:14:42 +00:00
9ce27d17c5 merge main
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-28 09:13:54 +01:00
2a8cf4ecfb update configuration sections
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-27 09:01:00 +01:00
d1a55e29d7 bump patch version and update lockfile
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-23 12:02:09 +01:00
4568577f81 add js to overlay back button functionality 2022-06-23 12:01:18 +01:00
ab0e27c14d Merge pull request 'Improve back button behaviour' (#120) from improved_back_button into main
Reviewed-on: #120
2022-06-23 11:00:21 +00:00
65b5f95a90 update lockfile
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-21 12:14:30 +01:00
a60d892e95 bump the patch version 2022-06-21 12:14:14 +01:00
5bd8a68ddf set, retrieve and reset back_url cookies 2022-06-21 12:13:36 +01:00
a6f52ce384 add cookie utils module for requests and responses 2022-06-21 12:13:04 +01:00
c71cc3992d Merge pull request 'Configure EBT replication setting' (#118) from enable_ebt_replication into main
Reviewed-on: #118
2022-06-16 11:04:39 +00:00
56fafc8d67 bump manifest version and add max as author
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-16 11:41:50 +01:00
414508f8ff merge changes from main 2022-06-16 11:40:35 +01:00
6ad5c620c1 Merge pull request 'URL encode SSB public keys on profile page' (#116) from fix_encoding_decoding into main
Reviewed-on: #116
2022-06-16 10:38:11 +00:00
76d5e6a355 fix probes version
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-16 11:19:04 +01:00
11e94fa421 merge changes from main
Some checks failed
continuous-integration/drone/pr Build is failing
2022-06-16 11:10:32 +01:00
216b60b86a bump version in manifest 2022-06-16 11:07:44 +01:00
a70f5e227d Merge pull request 'Fix link to Set New Password route' (#117) from fix_set_new_passwork_link into main
Reviewed-on: #117
2022-06-16 10:02:12 +00:00
cddcb8f9bd fix spacing in manifest
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-16 09:19:59 +01:00
8b33f8c174 Merge branch 'fix_set_new_passwork_link' of ssh://git.coopcloud.tech:2222/PeachCloud/peach-workspace into fix_set_new_passwork_link 2022-06-16 09:19:04 +01:00
1b43dc8b18 bump patch version 2022-06-16 09:18:38 +01:00
2d7b74d377 Merge branch 'main' into fix_set_new_passwork_link
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-16 08:14:45 +00:00
6b34864289 Merge pull request 'Change go-sbot.service name to be configurable' (#113) from go-sbot-service into main
Reviewed-on: #113
2022-06-15 12:03:29 +00:00
87ad2439b9 Fix cargo fmt
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-15 13:46:51 +02:00
5838faf128 Cargo fmt
Some checks failed
continuous-integration/drone/pr Build is failing
2022-06-15 13:37:36 +02:00
9c6fa00ec7 Fix cargo.lock
Some checks failed
continuous-integration/drone/pr Build is failing
2022-06-15 13:33:56 +02:00
a81b8b42cf Use main golgi branch 2022-06-15 12:48:51 +02:00
cdcff3475c Fix cargo.lock
Some checks failed
continuous-integration/drone/pr Build is failing
2022-06-15 12:47:11 +02:00
077c2a9178 Fix clippy errors 2022-06-15 12:44:34 +02:00
8b0b872d21 Reponse to CR 2022-06-15 12:36:44 +02:00
218a70b8f8 add lockfile
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-15 08:58:45 +01:00
50dcb2cf9e add checkbox to enable / disable ebt 2022-06-15 08:58:21 +01:00
e1877b5024 fix link to set new password
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-15 08:46:16 +01:00
0923c24693 update lockfile
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-14 09:03:24 +01:00
f3d4ba9fe5 url-encode the ssb id for profile buttons 2022-06-14 09:03:08 +01:00
16e6d42f87 Cargo clippy
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-26 23:19:31 +02:00
3493e5adb9 Change sbot.rs to use configurable go-sbot service name 2022-05-26 23:18:22 +02:00
5147eed497 Change sbot.rs to use configurable go-sbot service name 2022-05-26 23:17:20 +02:00
9f6ba14123 Cargo fmt
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-25 14:50:41 +02:00
21fb29c322 Working sbot
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-25 14:49:05 +02:00
6c9e5fd3fd Merge branch 'go-sbot-service' of https://git.coopcloud.tech/PeachCloud/peach-workspace into change-paths
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-25 12:26:11 +02:00
3adb226969 Bump version number 2022-05-25 12:25:54 +02:00
92f516b161 Merge pull request 'Fix lockfile' (#114) from fix-lock into go-sbot-service
Some checks reported errors
continuous-integration/drone/pr Build was killed
Reviewed-on: #114
2022-05-20 14:33:18 +00:00
543470b949 Fix lockfile
Some checks reported errors
continuous-integration/drone/pr Build was killed
2022-05-20 16:28:55 +02:00
6434471599 Bump version numbers
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-20 13:55:21 +02:00
56c142a387 Make go-sbot.service name configurable
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-20 13:35:37 +02:00
7deaa00d6e Fix hardcoded path
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-20 13:31:06 +02:00
bf7f2c8e31 Merge pull request 'Update golgi init functions with keystore selector' (#107) from keystore_selector into main
Reviewed-on: #107
2022-05-16 13:12:00 +00:00
dc79833e2b merge wpactrl and golgi api updates
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-13 13:15:12 +02:00
b0b79fef24 fix probes:0.3.0 lockfile error
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-13 12:44:51 +02:00
98497fa5ae merge latest changes from main
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-13 12:35:37 +02:00
827ccbd4dc update lockfile
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-11 16:42:32 +02:00
c21e2d090c introduce keystore selector for golgi 2022-05-11 16:42:11 +02:00
30 changed files with 756 additions and 537 deletions

687
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,5 @@
[workspace]
members = [
"peach-buttons",
"peach-oled",
"peach-lib",
"peach-config",
@ -13,3 +11,4 @@ members = [
"peach-jsonrpc-server",
"peach-dyndns-updater"
]

View File

@ -1,6 +1,6 @@
[package]
name = "peach-config"
version = "0.1.25"
version = "0.1.26"
authors = ["Andrew Reid <gnomad@cryptolab.net>", "Max Fowler <max@mfowler.info>"]
edition = "2018"
description = "Command line tool for installing, updating and configuring PeachCloud"
@ -37,3 +37,5 @@ log = "0.4"
lazy_static = "1.4.0"
peach-lib = { path = "../peach-lib" }
rpassword = "5.0"
golgi = { git = "https://git.coopcloud.tech/golgi-ssb/golgi.git" }
async-std = "1.10.0"

View File

@ -1,4 +1,5 @@
#![allow(clippy::nonstandard_macro_braces)]
use golgi::error::GolgiError;
use peach_lib::error::PeachError;
pub use snafu::ResultExt;
use snafu::Snafu;
@ -35,6 +36,12 @@ pub enum PeachConfigError {
ChangePasswordError { source: PeachError },
#[snafu(display("Entered passwords did not match. Please try again."))]
InvalidPassword,
#[snafu(display("Error in peach lib: {}", source))]
PeachLibError { source: PeachError },
#[snafu(display("Error in golgi: {}", source))]
Golgi { source: GolgiError },
#[snafu(display("{}", message))]
CmdInputError { message: String },
}
impl From<std::io::Error> for PeachConfigError {
@ -51,3 +58,15 @@ impl From<serde_json::Error> for PeachConfigError {
PeachConfigError::SerdeError { source: err }
}
}
impl From<PeachError> for PeachConfigError {
fn from(err: PeachError) -> PeachConfigError {
PeachConfigError::PeachLibError { source: err }
}
}
impl From<GolgiError> for PeachConfigError {
fn from(err: GolgiError) -> PeachConfigError {
PeachConfigError::Golgi { source: err }
}
}

View File

@ -2,10 +2,12 @@ mod change_password;
mod constants;
mod error;
mod generate_manifest;
mod publish_address;
mod set_permissions;
mod setup_networking;
mod setup_peach;
mod setup_peach_deb;
mod status;
mod update;
mod utils;
@ -44,12 +46,21 @@ enum PeachConfig {
Update(UpdateOpts),
/// Changes the password for the peach-web interface
#[structopt(name = "changepassword")]
#[structopt(name = "change-password")]
ChangePassword(ChangePasswordOpts),
/// Updates file permissions on PeachCloud device
#[structopt(name = "permissions")]
SetPermissions,
/// Returns sbot id if sbot is running
#[structopt(name = "whoami")]
WhoAmI,
/// Publish domain and port.
/// It takes an address argument of the form host:port
#[structopt(name = "publish-address")]
PublishAddress(PublishAddressOpts),
}
#[derive(StructOpt, Debug)]
@ -90,6 +101,13 @@ pub struct ChangePasswordOpts {
password: Option<String>,
}
#[derive(StructOpt, Debug)]
pub struct PublishAddressOpts {
/// Specify address in the form domain:port
#[structopt(short, long)]
address: String,
}
arg_enum! {
/// enum options for real-time clock choices
#[derive(Debug)]
@ -102,7 +120,7 @@ arg_enum! {
}
}
fn main() {
async fn run() {
// initialize the logger
env_logger::init();
@ -155,6 +173,34 @@ fn main() {
)
}
},
PeachConfig::WhoAmI => match status::whoami().await {
Ok(sbot_id) => {
println!("{:?}", sbot_id);
{}
}
Err(err) => {
error!("sbot whoami encountered an error: {}", err)
}
},
PeachConfig::PublishAddress(opts) => {
match publish_address::publish_address(opts.address).await {
Ok(_) => {}
Err(err) => {
error!(
"peach-config encountered an error during publish address: {}",
err
)
}
}
}
}
}
}
// Enable an async main function and execute the `run()` function,
// catching any errors and printing them to `stderr` before exiting the
// process.
#[async_std::main]
async fn main() {
run().await;
}

View File

@ -0,0 +1,37 @@
use crate::error::PeachConfigError;
use golgi::kuska_ssb::api::dto::content::PubAddress;
use golgi::messages::SsbMessageContent;
use peach_lib::sbot::init_sbot;
/// Utility function to publish the address (domain:port) of the pub
/// publishing the address causes the domain and port to be used for invite generation,
/// and also gossips this pub address to their peers
pub async fn publish_address(address: String) -> Result<(), PeachConfigError> {
// split address into domain:port
let split: Vec<&str> = address.split(':').collect();
let (domain, port): (&str, &str) = (split[0], split[1]);
// convert port to u16
let port_as_u16: u16 = port
.parse()
.map_err(|_err| PeachConfigError::CmdInputError {
message: "Failure to parse domain and port. Address must be of the format host:port."
.to_string(),
})?;
// publish address
let mut sbot = init_sbot().await?;
let pub_id = sbot.whoami().await?;
// Compose a `pub` address type message.
let pub_address_msg = SsbMessageContent::Pub {
address: Some(PubAddress {
// Host name (can be an IP address if onboarding over WiFi).
host: Some(domain.to_string()),
// Port.
port: port_as_u16,
// Public key.
key: pub_id,
}),
};
// Publish the `pub` address message.
let _pub_msg_ref = sbot.publish(pub_address_msg).await?;
Ok(())
}

View File

@ -1,17 +1,17 @@
use lazy_static::lazy_static;
use peach_lib::config_manager::get_config_value;
use peach_lib::config_manager;
use crate::error::PeachConfigError;
use crate::utils::cmd;
lazy_static! {
pub static ref PEACH_CONFIGDIR: String = get_config_value("PEACH_CONFIGDIR")
pub static ref PEACH_CONFIGDIR: String = config_manager::get_config_value("PEACH_CONFIGDIR")
.expect("Failed to load config value for PEACH_CONFIGDIR");
pub static ref PEACH_WEBDIR: String =
get_config_value("PEACH_WEBDIR").expect("Failed to load config value for PEACH_WEBDIR");
pub static ref PEACH_HOMEDIR: String =
get_config_value("PEACH_HOMEDIR").expect("Failed to load config value for PEACH_HOMEDIR");
pub static ref PEACH_WEBDIR: String = config_manager::get_config_value("PEACH_WEBDIR")
.expect("Failed to load config value for PEACH_WEBDIR");
pub static ref PEACH_HOMEDIR: String = config_manager::get_config_value("PEACH_HOMEDIR")
.expect("Failed to load config value for PEACH_HOMEDIR");
}
/// Utility function to set correct file permissions on the PeachCloud device.

View File

@ -0,0 +1,9 @@
use crate::error::PeachConfigError;
use peach_lib::sbot::init_sbot;
/// Utility function to check if sbot is running via the whoami method
pub async fn whoami() -> Result<String, PeachConfigError> {
let mut sbot = init_sbot().await?;
let sbot_id = sbot.whoami().await?;
Ok(sbot_id)
}

View File

@ -1,6 +1,6 @@
[package]
name = "peach-lib"
version = "1.3.3"
version = "1.3.5"
authors = ["Andrew Reid <glyph@mycelial.technology>"]
edition = "2018"
@ -9,7 +9,7 @@ async-std = "1.10"
chrono = "0.4"
dirs = "4.0"
fslock="0.1"
golgi = { git = "https://git.coopcloud.tech/golgi-ssb/golgi" }
golgi = { git = "https://git.coopcloud.tech/golgi-ssb/golgi.git" }
jsonrpc-client-core = "0.5"
jsonrpc-client-http = "0.5"
jsonrpc-core = "8.0"

View File

@ -57,9 +57,11 @@ pub fn get_peach_config_defaults() -> HashMap<String, String> {
("DYN_NAMESERVER", "ns.peachcloud.org"),
("DYN_ENABLED", "false"),
("SSB_ADMIN_IDS", ""),
("SYSTEM_MANAGER", "systemd"),
("ADMIN_PASSWORD_HASH", "47"),
("TEMPORARY_PASSWORD_HASH", ""),
("GO_SBOT_DATADIR", "/home/peach/.ssb-go"),
("GO_SBOT_SERVICE", "go-sbot"),
("PEACH_CONFIGDIR", "/var/lib/peachcloud"),
("PEACH_HOMEDIR", "/home/peach"),
("PEACH_WEBDIR", "/usr/share/peach-web"),
@ -131,7 +133,10 @@ pub fn save_peach_config_to_disc(
peach_config: HashMap<String, String>,
) -> Result<HashMap<String, String>, PeachError> {
// use a file lock to avoid race conditions while saving config
let mut lock = LockFile::open(&*LOCK_FILE_PATH)?;
let mut lock = LockFile::open(&*LOCK_FILE_PATH).map_err(|source| PeachError::Read {
source,
path: LOCK_FILE_PATH.to_string(),
})?;
lock.lock()?;
// first convert Hashmap to BTreeMap (so that keys are saved in deterministic alphabetical order)

View File

@ -2,6 +2,7 @@
//! Error handling for various aspects of the PeachCloud system, including the network, OLED, stats and dyndns JSON-RPC clients, as well as the configuration manager, sbot client and password utilities.
use golgi::GolgiError;
use std::{io, str, string};
/// This type represents all possible errors that can occur when interacting with the PeachCloud library.
@ -102,6 +103,13 @@ pub enum PeachError {
/// The file path for the write attempt.
path: String,
},
/// Represents a Golgi error
Golgi(GolgiError),
/// Represents a generic system error, whose details are specified in the string message
System(String),
}
impl std::error::Error for PeachError {
@ -130,6 +138,8 @@ impl std::error::Error for PeachError {
PeachError::Utf8ToStr(_) => None,
PeachError::Utf8ToString(_) => None,
PeachError::Write { ref source, .. } => Some(source),
PeachError::Golgi(_) => None,
PeachError::System(_) => None,
}
}
}
@ -187,6 +197,10 @@ impl std::fmt::Display for PeachError {
PeachError::Write { ref path, .. } => {
write!(f, "Write error: {}", path)
}
PeachError::Golgi(ref err) => err.fmt(f),
PeachError::System(ref msg) => {
write!(f, "system error: {}", msg)
}
}
}
}
@ -256,3 +270,10 @@ impl From<string::FromUtf8Error> for PeachError {
PeachError::Utf8ToString(err)
}
}
impl From<GolgiError> for PeachError {
fn from(err: GolgiError) -> PeachError {
PeachError::Golgi(err)
}
}

View File

@ -2,7 +2,10 @@
use std::{fs, fs::File, io, io::Write, path::PathBuf, process::Command, str};
use crate::config_manager::get_config_value;
use golgi::{sbot::Keystore, Sbot};
use log::{debug};
use crate::config_manager;
use serde::{Deserialize, Serialize};
use crate::error::PeachError;
@ -61,13 +64,48 @@ impl Default for SbotStatus {
impl SbotStatus {
/// Retrieve statistics for the go-sbot systemd process by querying `systemctl`.
pub fn read() -> Result<Self, PeachError> {
let system_manager = config_manager::get_config_value("SYSTEM_MANAGER")?;
match system_manager.as_str() {
"systemd" => {
SbotStatus::read_from_systemctl()
},
"supervisord" => {
SbotStatus::read_from_supervisorctl()
},
_ => Err(PeachError::System(format!(
"Invalid configuration for SYSTEM_MANAGER: {:?}",
system_manager)))
}
}
pub fn read_from_supervisorctl() -> Result<Self, PeachError> {
let mut status = SbotStatus::default();
let info_output = Command::new("supervisorctl")
.arg("status")
.arg(config_manager::get_config_value("GO_SBOT_SERVICE")?)
.output()?;
let service_info = std::str::from_utf8(&info_output.stdout)?;
for line in service_info.lines() {
// example line
// go-sbot RUNNING pid 11, uptime 0:04:23
if line.contains("RUNNING") {
// TODO: this should be an enum
status.state = Some("active".to_string());
}
}
Ok(status)
}
pub fn read_from_systemctl() -> Result<Self, PeachError> {
let mut status = SbotStatus::default();
// note this command does not need to be run as sudo
// because non-privileged users are able to run systemctl show
let info_output = Command::new("systemctl")
.arg("show")
.arg("go-sbot.service")
.arg(config_manager::get_config_value("GO_SBOT_SERVICE")?)
.arg("--no-page")
.output()?;
@ -89,7 +127,7 @@ impl SbotStatus {
// because non-privileged users are able to run systemctl status
let status_output = Command::new("systemctl")
.arg("status")
.arg("go-sbot.service")
.arg(config_manager::get_config_value("GO_SBOT_SERVICE")?)
.output()?;
let service_status = str::from_utf8(&status_output.stdout)?;
@ -107,8 +145,8 @@ impl SbotStatus {
// using the index of the first ';' + 2 and the last ';'
status.boot_state = Some(line[start + 2..end].to_string());
}
// example of the output line we're looking for here:
// `Active: active (running) since Mon 2022-01-24 16:22:51 SAST; 4min 14s ago`
// example of the output line we're looking for here:
// `Active: active (running) since Mon 2022-01-24 16:22:51 SAST; 4min 14s ago`
} else if line.contains("Active:") {
let before_time = line.find(';');
let after_time = line.find(" ago");
@ -119,7 +157,7 @@ impl SbotStatus {
// if service is active then the `time` reading is uptime
if status.state == Some("active".to_string()) {
status.uptime = time.map(|t| t.to_string())
// if service is inactive then the `time` reading is downtime
// if service is inactive then the `time` reading is downtime
} else if status.state == Some("inactive".to_string()) {
status.downtime = time.map(|t| t.to_string())
}
@ -128,7 +166,10 @@ impl SbotStatus {
}
// get path to blobstore
let blobstore_path = format!("{}/blobs/sha256", get_config_value("GO_SBOT_DATADIR")?);
let blobstore_path = format!(
"{}/blobs/sha256",
config_manager::get_config_value("GO_SBOT_DATADIR")?
);
// determine the size of the blobstore directory in bytes
status.blobstore = dir_size(blobstore_path).ok();
@ -199,9 +240,11 @@ impl Default for SbotConfig {
impl SbotConfig {
/// Read the go-sbot `config.toml` file from file and deserialize into `SbotConfig`.
pub fn read() -> Result<Self, PeachError> {
// determine path of user's home directory
let mut config_path = dirs::home_dir().ok_or(PeachError::HomeDir)?;
config_path.push(".ssb-go/config.toml");
// determine path of user's go-sbot config.toml
let config_path = format!(
"{}/config.toml",
config_manager::get_config_value("GO_SBOT_DATADIR")?
);
let config_contents = fs::read_to_string(config_path)?;
@ -217,8 +260,11 @@ impl SbotConfig {
// convert the provided `SbotConfig` instance to a string
let config_string = toml::to_string(&config)?;
// determine path of user's home directory
let config_path = format!("{}/config.toml", get_config_value("GO_SBOT_DATADIR")?);
// determine path of user's go-sbot config.toml
let config_path = format!(
"{}/config.toml",
config_manager::get_config_value("GO_SBOT_DATADIR")?
);
// open config file for writing
let mut file = File::create(config_path)?;
@ -232,3 +278,25 @@ impl SbotConfig {
Ok(())
}
}
/// Initialise an sbot client
pub async fn init_sbot() -> Result<Sbot, PeachError> {
// read sbot config from config.toml
let sbot_config = SbotConfig::read().ok();
debug!("Initialising an sbot client with configuration parameters");
// initialise sbot connection with ip:port and shscap from config file
let key_path = format!(
"{}/secret",
config_manager::get_config_value("GO_SBOT_DATADIR")?
);
let 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(Keystore::CustomGoSbot(key_path), Some(ip_port), None).await?
}
None => Sbot::init(Keystore::CustomGoSbot(key_path), None, None).await?,
};
Ok(sbot_client)
}

View File

@ -1,6 +1,6 @@
[package]
name = "peach-stats"
version = "0.3.0"
version = "0.3.1"
authors = ["Andrew Reid <glyph@mycelial.technology>"]
edition = "2018"
description = "Query system statistics. Provides a wrapper around the probes and systemstat crates."

View File

@ -44,8 +44,8 @@ impl SbotStat {
pub fn sbot_stats() -> Result<SbotStat, StatsError> {
let mut status = SbotStat::default();
let info_output = Command::new("/usr/bin/systemctl")
.arg("--user")
let info_output = Command::new("sudo")
.arg("systemctl")
.arg("show")
.arg("go-sbot.service")
.arg("--no-page")
@ -66,8 +66,8 @@ pub fn sbot_stats() -> Result<SbotStat, StatsError> {
}
}
let status_output = Command::new("/usr/bin/systemctl")
.arg("--user")
let status_output = Command::new("sudo")
.arg("systemctl")
.arg("status")
.arg("go-sbot.service")
.output()

View File

@ -1,7 +1,7 @@
[package]
name = "peach-web"
version = "0.6.13"
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
version = "0.6.19"
authors = ["Andrew Reid <gnomad@cryptolab.net>", "Max Fowler <max@mfowler.info>"]
edition = "2018"
description = "peach-web is a web application which provides a web interface for monitoring and interacting with the PeachCloud device. This allows administration of the single-board computer (ie. Raspberry Pi) running PeachCloud, as well as the ssb-server and related plugins."
homepage = "https://opencollective.com/peachcloud"

View File

@ -1,6 +1,6 @@
# peach-web
![Generic badge](https://img.shields.io/badge/version-0.6.0-<COLOR>.svg)
![Generic badge](https://img.shields.io/badge/version-0.6.18-<COLOR>.svg)
## Web Interface for PeachCloud
@ -17,7 +17,7 @@ The web interface is primarily designed as a means of managing a Scuttlebutt pub
Additional features are focused on administration of the device itself. This includes networking functionality and device statistics.
The peach-web stack currently consists of [Rouille](https://crates.io/crates/rouille) (Rust web framework), [Maud](https://maud.lambda.xyz/) (Rust template engine), HTML and CSS. Scuttlebutt functionality is provided by [golgi](http://golgi.mycelial.technology).
The peach-web stack currently consists of [Rouille](https://crates.io/crates/rouille) (Rust web framework), [Maud](https://maud.lambda.xyz/) (Rust template engine), HTML, CSS and a tiny bit of JS. Scuttlebutt functionality is provided by [golgi](http://golgi.mycelial.technology).
_Note: This is a work-in-progress._
@ -36,11 +36,46 @@ Run the binary:
`../target/release/peach-web`
## Environment
## Development Setup
In order to test `peach-web` on a development machine you will need to have a running instance of `go-sbot` (please see the [go-sbot README](https://github.com/cryptoscope/ssb) for installation details). The `GO_SBOT_DATADIR` environment variable or corresponding config variable must be set to `/home/<user>/.ssb-go` and the `PEACH_HOMEDIR` variable must be set to `/home/<user>`. See the Configuration section below for more details.
The `go-sbot` process must be managed by `systemd` in order for it to be controlled via the `peach-web` web interface. Here is a basic `go-sbot.service` file:
```
[Unit]
Description=GoSSB server.
[Service]
ExecStart=/usr/bin/go-sbot
Environment="LIBRARIAN_WRITEALL=0"
Restart=always
[Install]
WantedBy=multi-user.target
```
And a `sudoers` rule must be created to allow the `go-sbot.service` state to be modified without requiring a password. Here is an example `/etc/sudoers.d/peach-web` file:
```
# Control go-sbot service without sudo passworkd
<user> ALL=(ALL) NOPASSWD: /bin/systemctl start go-sbot.service, /bin/systemctl restart go-sbot.service, /bin/systemctl stop go-sbot.service, /bin/systemctl enable go-sbot.service, /bin/systemctl disable go-sbot.service
```
## Configuration
By default, configuration variables are stored in `/var/lib/peachcloud/config.yml`. The variables in the file are updated by `peach-web` when changes are made to configurations via the web interface. Since `peach-web` has no database, all configurations are stored in this file.
A non-default configuration directory can be defined via the `PEACH_CONFIGDIR` environment variable or corresponding key in the `config.yml` file.
### Configuration Mode
The web application can be run with a minimal set of routes and functionality (PeachPub - a simple sbot manager) or with the full-suite of capabilities, including network management and access to device statistics (PeachCloud). The mode is enabled by default (as defined in `Rocket.toml`) but can be overwritten using the `STANDALONE_MODE` environment variable: `true` or `false`. If the variable is unset or the value is incorrectly set, the application defaults to standalone mode.
The web application can be run with a minimal set of routes and functionality (PeachPub - a simple sbot manager) or with the full-suite of capabilities, including network management and access to device statistics (PeachCloud).
The application runs in PeachPub mode by default. The complete PeachCloud mode will be available once a large refactor is complete; it is not currently in working order so it's best to stick with PeachPub for now.
The running mode can be defined by setting the `STANDALONE_MODE` environment variable (`true` for PeachPub or `false` for PeachCloud). Alternatively, the desired mode can be set by modifying the PeachCloud configuration file.
### Authentication
@ -56,6 +91,14 @@ Logging is made available with `env_logger`:
Other logging levels include `debug`, `warn` and `error`.
### Dynamic DNS Configuration
Most users will want to use the default PeachCloud dynamic dns server.
If the config dyn_use_custom_server=false, then default values will be used.
If the config dyn_use_custom_server=true, then a value must also be set for dyn_dns_server_address (e.g. "http://peachdynserver.commoninternet.net").
This value is the URL of the instance of peach-dyndns-server that requests will be sent to for domain registration.
Using a custom value can here can be useful for testing.
## Debian Packaging
A `systemd` service file and Debian maintainer scripts are included in the `debian` directory, allowing `peach-web` to be easily bundled as a Debian package (`.deb`). The `cargo-deb` [crate](https://crates.io/crates/cargo-deb) can be used to achieve this.
@ -88,20 +131,6 @@ Remove configuration files (not removed with `apt-get remove`):
`sudo apt-get purge peach-web`
## Configuration
Configuration variables are stored in /var/lib/peachcloud/config.yml.
Peach-web also updates this file when changes are made to configurations via
the web interface. peach-web has no database, so all configurations are stored in this file.
### Dynamic DNS Configuration
Most users will want to use the default PeachCloud dynamic dns server.
If the config dyn_use_custom_server=false, then default values will be used.
If the config dyn_use_custom_server=true, then a value must also be set for dyn_dns_server_address (e.g. "http://peachdynserver.commoninternet.net").
This value is the URL of the instance of peach-dyndns-server that requests will be sent to for domain registration.
Using a custom value can here can be useful for testing.
## Design
`peach-web` has been designed with simplicity and resource minimalism in mind. Both the dependencies used by the project, as well as the code itself, reflect these design priorities. The Rouille micro-web-framework and Maud templating engine have been used to present a web interface for interacting with the device. HTML is rendered server-side and request handlers call `peach-` libraries and serve HTML and assets. The optimised binary for `peach-web` can be compiled on a RPi 3 B+ in approximately 30 minutes.

View File

@ -18,6 +18,7 @@ pub enum PeachWebError {
Json(JsonError),
OsString,
PeachLib { source: PeachError, msg: String },
System(String),
Yaml(YamlError),
}
@ -31,6 +32,7 @@ impl std::error::Error for PeachWebError {
PeachWebError::Json(ref source) => Some(source),
PeachWebError::OsString => None,
PeachWebError::PeachLib { ref source, .. } => Some(source),
PeachWebError::System(_) => None,
PeachWebError::Yaml(ref source) => Some(source),
}
}
@ -54,6 +56,9 @@ impl std::fmt::Display for PeachWebError {
"Filesystem error: failed to convert OsString to String for go-ssb directory path"
),
PeachWebError::PeachLib { ref source, .. } => write!(f, "{}", source),
PeachWebError::System(ref msg) => {
write!(f, "system error: {}", msg)
}
PeachWebError::Yaml(ref source) => write!(f, "Serde YAML error: {}", source),
}
}

View File

@ -1,6 +1,10 @@
use rouille::{router, Request, Response};
use crate::{routes, templates, utils::flash::FlashResponse, SessionData};
use crate::{
routes, templates,
utils::{cookie::CookieResponse, flash::FlashResponse},
SessionData,
};
// TODO: add mount_peachcloud_routes()
// https://github.com/tomaka/rouille/issues/232#issuecomment-919225104
@ -22,6 +26,8 @@ pub fn mount_peachpub_routes(
router!(request,
(GET) (/) => {
Response::html(routes::home::build_template())
// reset the back_url cookie each time we visit the homepage
.reset_cookie("back_url")
},
(GET) (/auth/change) => {
@ -49,6 +55,9 @@ pub fn mount_peachpub_routes(
(GET) (/scuttlebutt/blocks) => {
Response::html(routes::scuttlebutt::blocks::build_template())
// add a back_url cookie to allow the path of the back button
// to be set correctly on the /scuttlebutt/profile page
.add_cookie("back_url=/scuttlebutt/blocks")
},
(POST) (/scuttlebutt/follow) => {
@ -57,10 +66,16 @@ pub fn mount_peachpub_routes(
(GET) (/scuttlebutt/follows) => {
Response::html(routes::scuttlebutt::follows::build_template())
// add a back_url cookie to allow the path of the back button
// to be set correctly on the /scuttlebutt/profile page
.add_cookie("back_url=/scuttlebutt/follows")
},
(GET) (/scuttlebutt/friends) => {
Response::html(routes::scuttlebutt::friends::build_template())
// add a back_url cookie to allow the path of the back button
// to be set correctly on the /scuttlebutt/profile page
.add_cookie("back_url=/scuttlebutt/friends")
},
(GET) (/scuttlebutt/invites) => {
@ -117,6 +132,9 @@ pub fn mount_peachpub_routes(
(POST) (/scuttlebutt/search) => {
routes::scuttlebutt::search::handle_form(request)
// add a back_url cookie to allow the path of the back button
// to be set correctly on the /scuttlebutt/profile page
.add_cookie("back_url=/scuttlebutt/search")
},
(POST) (/scuttlebutt/unblock) => {
@ -187,7 +205,7 @@ pub fn mount_peachpub_routes(
},
(GET) (/status/scuttlebutt) => {
Response::html(routes::status::scuttlebutt::build_template())
Response::html(routes::status::scuttlebutt::build_template()).add_cookie("back_url=/status/scuttlebutt")
},
// render the not_found template and set a 404 status code if none of

View File

@ -30,7 +30,7 @@ pub fn build_template(request: &Request) -> PreEscaped<String> {
form id="sendPasswordReset" action="/auth/temporary" method="post" {
div id="buttonDiv" {
input class="button button-primary center" style="margin-top: 1rem;" type="submit" value="Send Temporary Password" title="Send temporary password to Scuttlebutt admin(s)";
a href="/auth/reset_password" class="button button-primary center" title="Set a new password using the temporary password" {
a href="/auth/reset" class="button button-primary center" title="Set a new password using the temporary password" {
"Set New Password"
}
}

View File

@ -4,7 +4,7 @@ use rouille::Request;
use crate::{
templates,
utils::{flash::FlashRequest, sbot, sbot::Profile, theme},
utils::{cookie::CookieRequest, flash::FlashRequest, sbot, sbot::Profile, theme},
};
// ROUTE: /scuttlebutt/profile
@ -83,13 +83,15 @@ fn social_interaction_buttons_template(profile: &Profile) -> Markup {
@match (profile.following, &profile.id) {
(Some(false), Some(ssb_id)) => {
form id="followForm" class="center" action="/scuttlebutt/follow" method="post" {
input type="hidden" id="publicKey" name="public_key" value=(ssb_id);
// url encode the ssb_id value
input type="hidden" id="publicKey" name="public_key" value=(ssb_id.replace('/', "%2F"));
input id="followPeer" class="button button-primary center" type="submit" title="Follow Peer" value="Follow";
}
},
(Some(true), Some(ssb_id)) => {
form id="unfollowForm" class="center" action="/scuttlebutt/unfollow" method="post" {
input type="hidden" id="publicKey" name="public_key" value=(ssb_id);
// url encode the ssb_id value
input type="hidden" id="publicKey" name="public_key" value=(ssb_id.replace('/', "%2F"));
input id="unfollowPeer" class="button button-primary center" type="submit" title="Unfollow Peer" value="Unfollow";
}
},
@ -98,13 +100,15 @@ fn social_interaction_buttons_template(profile: &Profile) -> Markup {
@match (profile.blocking, &profile.id) {
(Some(false), Some(ssb_id)) => {
form id="blockForm" class="center" action="/scuttlebutt/block" method="post" {
input type="hidden" id="publicKey" name="public_key" value=(ssb_id);
// url encode the ssb_id value
input type="hidden" id="publicKey" name="public_key" value=(ssb_id.replace('/', "%2F"));
input id="blockPeer" class="button button-primary center" type="submit" title="Block Peer" value="Block";
}
},
(Some(true), Some(ssb_id)) => {
form id="unblockForm" class="center" action="/scuttlebutt/unblock" method="post" {
input type="hidden" id="publicKey" name="public_key" value=(ssb_id);
// url encode the ssb_id value
input type="hidden" id="publicKey" name="public_key" value=(ssb_id.replace('/', "%2F"));
input id="unblockPeer" class="button button-primary center" type="submit" title="Unblock Peer" value="Unblock";
}
},
@ -112,7 +116,8 @@ fn social_interaction_buttons_template(profile: &Profile) -> Markup {
}
@if let Some(ssb_id) = &profile.id {
form class="center" {
a id="privateMessage" class="button button-primary center" href={ "/scuttlebutt/private/" (ssb_id) } title="Private Message" {
// url encode the ssb_id
a id="privateMessage" class="button button-primary center" href={ "/scuttlebutt/private/" (ssb_id.replace('/', "%2F")) } title="Private Message" {
"Send Private Message"
}
}
@ -169,7 +174,15 @@ pub fn build_template(request: &Request, ssb_id: Option<String>) -> PreEscaped<S
_ => templates::inactive::build_template("Profile is unavailable."),
};
let body = templates::nav::build_template(profile_template, "Profile", Some("/"));
// a request to /scuttlebutt/profile can originate via the Friends,
// Follows or Blocks menu - as well as the Search page and Homepage.
// therefore, we check to see if the `back_url` cookie has been set
// and assign the path of the back button accordingly.
// for example, if the request has come via the Friends menu then the
// `back_url` cookie will be set with a value of "/scuttlebutt/friends".
let back_url = request.retrieve_cookie("back_url").or(Some("/"));
let body = templates::nav::build_template(profile_template, "Profile", back_url);
// query the current theme so we can pass it into the base template builder
let theme = theme::get_theme();

View File

@ -122,6 +122,15 @@ pub fn build_template(request: &Request) -> PreEscaped<String> {
input type="text" id="database_dir" name="repo" value=(sbot_config.repo);
}
div class="center" {
@if sbot_config.enable_ebt {
input type="checkbox" id="ebtReplication" style="margin-bottom: 1rem;" name="enable_ebt" checked;
} @else {
input type="checkbox" id="ebtReplication" style="margin-bottom: 1rem;" name="enable_ebt";
}
label class="font-normal" for="ebtReplication" title="Enable Epidemic Broadcast Tree (EBT) replication instead of legacy replication" {
"Enable EBT Replication"
}
br;
@if sbot_config.localadv {
input type="checkbox" id="lanBroadcast" style="margin-bottom: 1rem;" name="localadv" checked;
} @else {
@ -157,7 +166,6 @@ pub fn build_template(request: &Request) -> PreEscaped<String> {
input type="hidden" id="hmac" name="hmac" value=(sbot_config.hmac);
input type="hidden" id="wslis" name="wslis" value=(sbot_config.wslis);
input type="hidden" id="debuglis" name="debuglis" value=(sbot_config.debuglis);
input type="hidden" id="enable_ebt" name="enable_ebt" value=(sbot_config.enable_ebt);
input type="hidden" id="promisc" name="promisc" value=(sbot_config.promisc);
input type="hidden" id="nounixsock" name="nounixsock" value=(sbot_config.nounixsock);
(PreEscaped("<!-- BUTTONS -->"))
@ -175,8 +183,11 @@ pub fn build_template(request: &Request) -> PreEscaped<String> {
// wrap the nav bars around the settings menu template content
// parameters are template, title and back url
let body =
templates::nav::build_template(menu_template, "Scuttlebutt Settings", Some("/settings"));
let body = templates::nav::build_template(
menu_template,
"Scuttlebutt Settings",
Some("/settings/scuttlebutt"),
);
// query the current theme so we can pass it into the base template builder
let theme = theme::get_theme();
@ -232,13 +243,13 @@ pub fn handle_form(request: &Request, restart: bool) -> Response {
match data.startup {
true => {
debug!("Enabling go-sbot.service");
if let Err(e) = sbot::systemctl_sbot_cmd("enable") {
if let Err(e) = sbot::system_sbot_cmd("enable") {
warn!("Failed to enable go-sbot.service: {}", e)
}
}
false => {
debug!("Disabling go-sbot.service");
if let Err(e) = sbot::systemctl_sbot_cmd("disable") {
if let Err(e) = sbot::system_sbot_cmd("disable") {
warn!("Failed to disable go-sbot.service: {}", e)
}
}

View File

@ -4,7 +4,7 @@ use rouille::Request;
use crate::{
templates,
utils::{flash::FlashRequest, theme},
utils::{cookie::CookieRequest, flash::FlashRequest, theme},
};
/// Read the status of the go-sbot service and render buttons accordingly.
@ -53,10 +53,13 @@ pub fn build_template(request: &Request) -> PreEscaped<String> {
}
};
// retrieve the value of the "back_url" cookie
// if the cookie value is not found then set a hardcoded fallback value
let back_url = request.retrieve_cookie("back_url").or(Some("/settings"));
// wrap the nav bars around the settings menu template content
// parameters are template, title and back url
let body =
templates::nav::build_template(menu_template, "Scuttlebutt Settings", Some("/settings"));
let body = templates::nav::build_template(menu_template, "Scuttlebutt Settings", back_url);
// query the current theme so we can pass it into the base template builder
let theme = theme::get_theme();

View File

@ -1,7 +1,7 @@
use log::info;
use rouille::Response;
use crate::utils::{flash::FlashResponse, sbot::systemctl_sbot_cmd};
use crate::utils::{flash::FlashResponse, sbot};
// ROUTE: /settings/scuttlebutt/restart
@ -10,9 +10,9 @@ use crate::utils::{flash::FlashResponse, sbot::systemctl_sbot_cmd};
/// the attempt via a flash message.
pub fn restart_sbot() -> Response {
info!("Restarting go-sbot.service");
let (flash_name, flash_msg) = match systemctl_sbot_cmd("stop") {
let (flash_name, flash_msg) = match sbot::system_sbot_cmd("stop") {
// if stop was successful, try to start the process
Ok(_) => match systemctl_sbot_cmd("start") {
Ok(_) => match sbot::system_sbot_cmd("start") {
Ok(_) => (
"flash_name=success".to_string(),
"flash_msg=Sbot process has been restarted".to_string(),

View File

@ -1,7 +1,7 @@
use log::info;
use rouille::Response;
use crate::utils::{flash::FlashResponse, sbot::systemctl_sbot_cmd};
use crate::utils::{flash::FlashResponse, sbot};
// ROUTE: /settings/scuttlebutt/start
@ -10,7 +10,7 @@ use crate::utils::{flash::FlashResponse, sbot::systemctl_sbot_cmd};
/// the attempt via a flash message.
pub fn start_sbot() -> Response {
info!("Starting go-sbot.service");
let (flash_name, flash_msg) = match systemctl_sbot_cmd("start") {
let (flash_name, flash_msg) = match sbot::system_sbot_cmd("start") {
Ok(_) => (
"flash_name=success".to_string(),
"flash_msg=Sbot process has been started".to_string(),

View File

@ -1,7 +1,7 @@
use log::info;
use rouille::Response;
use crate::utils::{flash::FlashResponse, sbot::systemctl_sbot_cmd};
use crate::utils::{flash::FlashResponse, sbot};
// ROUTE: /settings/scuttlebutt/stop
@ -10,7 +10,7 @@ use crate::utils::{flash::FlashResponse, sbot::systemctl_sbot_cmd};
/// the attempt via a flash message.
pub fn stop_sbot() -> Response {
info!("Stopping go-sbot.service");
let (flash_name, flash_msg) = match systemctl_sbot_cmd("stop") {
let (flash_name, flash_msg) = match sbot::system_sbot_cmd("stop") {
Ok(_) => (
"flash_name=success".to_string(),
"flash_msg=Sbot process has been stopped".to_string(),

View File

@ -1,5 +1,25 @@
use maud::{html, PreEscaped, DOCTYPE};
/// JavaScript event listener for the back button on the top navigation bar of
/// the UI.
///
/// When the button is clicked, prevent the default behaviour and invoke
/// the history API to load the previous URL (page) in the history list.
fn js_back_button_script() -> PreEscaped<String> {
html! {
(PreEscaped("
<script>
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('backButton').onclick = function(e) {
e.preventDefault();
history.back();
}
});
</script>
"))
}
}
/// Base template builder.
///
/// Takes an HTML body as input and splices it into the base template.
@ -14,6 +34,7 @@ pub fn build_template(body: PreEscaped<String>, theme: String) -> PreEscaped<Str
meta name="viewport" content="width=devide-width, initial-scale=1.0";
link rel="stylesheet" href="/css/peachcloud.css";
link rel="stylesheet" href="/css/_variables.css";
(js_back_button_script())
title { "PeachCloud" }
}
body {

View File

@ -38,7 +38,7 @@ pub fn build_template(
html! {
(PreEscaped("<!-- Top navigation bar -->"))
nav class="nav-bar" {
a class="nav-item" href=[back] title="Back" {
a id="backButton" class="nav-item" href=[back] title="Back" {
img class="icon-medium nav-icon-left icon-active" src="/icons/back.svg" alt="Back";
}
h1 class="nav-title" { (title) }

View File

@ -0,0 +1,64 @@
use rouille::{input, Request, Response};
// The CookieRequest and CookieResponse traits are currently only used
// to add, retrieve and reset the `back_url` cookie. That cookie is
// used to set the URL of the in-UI back button when visiting a page
// which can be arrived at via several paths.
//
// An example of this is the Scuttlebutt Settings menu (/settings/scuttlebutt),
// which can be accessed via the Settings menu (/settings) or the Scuttlebutt
// Status page (/status/scuttlebutt). We need to be able to set the path of
// the back button to point to the correct page (ie. the one from which we've
// come).
//
// The `back_url` cookie is also used on the Profile page
// (/scuttlebutt/profile).
/// Cookie trait for `Request`.
pub trait CookieRequest {
/// Retrieve a cookie value from a `Request`.
fn retrieve_cookie(&self, cookie_name: &str) -> Option<&str>;
}
impl CookieRequest for Request {
fn retrieve_cookie(&self, cookie_name: &str) -> Option<&str> {
// check for cookie using given name
let cookie_val = input::cookies(self)
.find(|&(n, _)| n == cookie_name)
// return the value of the cookie (key is already known)
.map(|key_val| key_val.1);
cookie_val
}
}
/// Cookie trait for `Response`.
pub trait CookieResponse {
/// Add a cookie containing the given data to a `Response`. Data should be
/// in the form of `cookie_name=cookie_val`.
fn add_cookie(self, cookie_name_val: &str) -> Response;
/// Reset a cookie value for a `Response`.
fn reset_cookie(self, cookie_name: &str) -> Response;
}
impl CookieResponse for Response {
fn add_cookie(self, cookie_name_val: &str) -> Response {
// set the cookie header
// max-age is currently set to 3600 seconds (1 hour)
self.with_additional_header(
"Set-Cookie",
format!("{}; Max-Age=3600; SameSite=Lax; Path=/", cookie_name_val),
)
}
fn reset_cookie(self, cookie_name: &str) -> Response {
// set a blank cookie to clear the cookie from the previous request
self.with_additional_header(
"Set-Cookie",
format!(
"{}=; Max-Age=0; SameSite=Lax; Path=/; Expires=Fri, 21 Aug 1987 12:00:00 UTC",
cookie_name
),
)
}
}

View File

@ -1,3 +1,4 @@
pub mod cookie;
pub mod flash;
pub mod sbot;
pub mod theme;

View File

@ -3,7 +3,6 @@ use std::{
error::Error,
fs,
fs::File,
io,
io::prelude::*,
path::Path,
process::{Command, Output},
@ -13,9 +12,10 @@ use async_std::task;
use dirs;
use futures::stream::TryStreamExt;
use golgi::{
api::friends::RelationshipQuery, blobs, messages::SsbMessageValue, sbot::Keystore, Sbot,
api::friends::RelationshipQuery, blobs, messages::SsbMessageKVT, sbot::Keystore, Sbot,
};
use log::debug;
use peach_lib::config_manager;
use peach_lib::sbot::SbotConfig;
use rouille::input::post::BufferedFile;
use temporary::Directory;
@ -24,22 +24,58 @@ use crate::{error::PeachWebError, utils::sbot};
// SBOT HELPER FUNCTIONS
/// Executes a systemctl command for the go-sbot.service process.
pub fn systemctl_sbot_cmd(cmd: &str) -> io::Result<Output> {
Command::new("sudo")
.arg("systemctl")
.arg(cmd)
.arg("go-sbot.service")
.output()
/// On non-docker based deployments (peachcloud, yunohost), we use systemctl
/// On docker-based deployments, we use supervisord
/// This utility function calls the correct system calls based on these parameters.
pub fn system_sbot_cmd(cmd: &str) -> Result<Output, PeachWebError> {
let system_manager = config_manager::get_config_value("SYSTEM_MANAGER")?;
match system_manager.as_str() {
"systemd" => {
let output = Command::new("sudo")
.arg("systemctl")
.arg(cmd)
.arg(config_manager::get_config_value("GO_SBOT_SERVICE")?)
.output()?;
Ok(output)
}
"supervisord" => {
match cmd {
"enable" => {
// TODO: implement this
let output = Command::new("echo")
.arg("implement this (enable)")
.output()?;
Ok(output)
}
"disable" => {
let output = Command::new("echo")
.arg("implement this (disable)")
.output()?;
Ok(output)
}
_ => {
let output = Command::new("supervisorctl")
.arg(cmd)
.arg(config_manager::get_config_value("GO_SBOT_SERVICE")?)
.output()?;
Ok(output)
}
}
}
_ => Err(PeachWebError::System(format!(
"Invalid configuration for SYSTEM_MANAGER: {:?}",
system_manager
))),
}
}
/// Executes a systemctl stop command followed by start command.
/// Executes a system stop command followed by start command.
/// Returns a redirect with a flash message stating the output of the restart attempt.
pub fn restart_sbot_process() -> (String, String) {
debug!("Restarting go-sbot.service");
match systemctl_sbot_cmd("stop") {
match system_sbot_cmd("stop") {
// if stop was successful, try to start the process
Ok(_) => match systemctl_sbot_cmd("start") {
Ok(_) => match system_sbot_cmd("start") {
Ok(_) => (
"success".to_string(),
"Updated configuration and restarted the sbot process".to_string(),
@ -68,15 +104,18 @@ pub async fn init_sbot_with_config(
) -> Result<Sbot, PeachWebError> {
debug!("Initialising an sbot client with configuration parameters");
// initialise sbot connection with ip:port and shscap from config file
let key_path = format!(
"{}/secret",
config_manager::get_config_value("GO_SBOT_DATADIR")?
);
let 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(Keystore::GoSbot, Some(ip_port), None).await?
Sbot::init(Keystore::CustomGoSbot(key_path), Some(ip_port), None).await?
}
None => Sbot::init(Keystore::GoSbot, None, None).await?,
None => Sbot::init(Keystore::CustomGoSbot(key_path), None, None).await?,
};
Ok(sbot_client)
}
@ -130,7 +169,7 @@ pub fn latest_sequence_number() -> Result<u64, Box<dyn Error>> {
let id = sbot_client.whoami().await?;
let history_stream = sbot_client.create_history_stream(id).await?;
let mut msgs: Vec<SsbMessageValue> = history_stream.try_collect().await?;
let mut msgs: Vec<SsbMessageKVT> = history_stream.try_collect().await?;
// there will be zero messages when the sbot is run for the first time
if msgs.is_empty() {
@ -140,7 +179,7 @@ pub fn latest_sequence_number() -> Result<u64, Box<dyn Error>> {
msgs.reverse();
// return the sequence number of the latest msg
Ok(msgs[0].sequence)
Ok(msgs[0].value.sequence)
}
})
}
@ -153,7 +192,13 @@ pub fn create_invite(uses: u16) -> Result<String, Box<dyn Error>> {
let mut sbot_client = init_sbot_with_config(&sbot_config).await?;
debug!("Generating Scuttlebutt invite code");
let invite_code = sbot_client.invite_create(uses).await?;
let mut invite_code = sbot_client.invite_create(uses).await?;
// insert domain into invite if one is configured
let domain = config_manager::get_config_value("EXTERNAL_DOMAIN")?;
if !domain.is_empty() {
invite_code = domain + &invite_code[4..];
}
Ok(invite_code)
})