Compare commits
7 Commits
fix-regres
...
network_rp
Author | SHA1 | Date | |
---|---|---|---|
4d08323d77 | |||
df91968762 | |||
9255abb078 | |||
1ad956c0c7 | |||
39c15d0fe5 | |||
fd12e97bc4 | |||
318fa9768a |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,2 @@
|
|||||||
.idea
|
.idea
|
||||||
target
|
target
|
||||||
*peachdeploy.sh
|
|
||||||
*vpsdeploy.sh
|
|
||||||
*bindeploy.sh
|
|
||||||
|
707
Cargo.lock
generated
707
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -11,5 +11,6 @@ members = [
|
|||||||
"peach-monitor",
|
"peach-monitor",
|
||||||
"peach-stats",
|
"peach-stats",
|
||||||
"peach-jsonrpc-server",
|
"peach-jsonrpc-server",
|
||||||
|
"peach-probe",
|
||||||
"peach-dyndns-updater"
|
"peach-dyndns-updater"
|
||||||
]
|
]
|
||||||
|
4
peach-config/.cargo/config
Normal file
4
peach-config/.cargo/config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "aarch64-linux-gnu-gcc"
|
||||||
|
objcopy = { path ="aarch64-linux-gnu-objcopy" }
|
||||||
|
strip = { path ="aarch64-linux-gnu-strip" }
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peach-config"
|
name = "peach-config"
|
||||||
version = "0.1.15"
|
version = "0.1.10"
|
||||||
authors = ["Andrew Reid <gnomad@cryptolab.net>", "Max Fowler <max@mfowler.info>"]
|
authors = ["Andrew Reid <gnomad@cryptolab.net>", "Max Fowler <max@mfowler.info>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Command line tool for installing, updating and configuring PeachCloud"
|
description = "Command line tool for installing, updating and configuring PeachCloud"
|
||||||
@ -35,5 +35,3 @@ structopt = "0.3.13"
|
|||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
peach-lib = { path = "../peach-lib" }
|
|
||||||
rpassword = "5.0"
|
|
||||||
|
@ -8,7 +8,7 @@ dtparam=i2c_arm=on
|
|||||||
# Apply device tree overlay to enable pull-up resistors for buttons
|
# Apply device tree overlay to enable pull-up resistors for buttons
|
||||||
device_tree_overlay=overlays/mygpio.dtbo
|
device_tree_overlay=overlays/mygpio.dtbo
|
||||||
|
|
||||||
kernel=vmlinuz-4.19.0-18-arm64
|
kernel=vmlinuz-4.19.0-17-arm64
|
||||||
# For details on the initramfs directive, see
|
# For details on the initramfs directive, see
|
||||||
# https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=10532
|
# https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=10532
|
||||||
initramfs initrd.img-4.19.0-18-arm64
|
initramfs initrd.img-4.19.0-17-arm64
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
use crate::error::PeachConfigError;
|
|
||||||
use crate::ChangePasswordOpts;
|
|
||||||
use peach_lib::password_utils::set_new_password;
|
|
||||||
|
|
||||||
/// Utility function to set the admin password for peach-web from the command-line.
|
|
||||||
pub fn set_peach_web_password(opts: ChangePasswordOpts) -> Result<(), PeachConfigError> {
|
|
||||||
match opts.password {
|
|
||||||
// read password from CLI arg
|
|
||||||
Some(password) => {
|
|
||||||
set_new_password(&password)
|
|
||||||
.map_err(|err| PeachConfigError::ChangePasswordError { source: err })?;
|
|
||||||
println!(
|
|
||||||
"Your new password has been set for peach-web. You can login through the \
|
|
||||||
web interface with username admin."
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
// read password from tty
|
|
||||||
None => {
|
|
||||||
let pass1 = rpassword::read_password_from_tty(Some("New password: "))?;
|
|
||||||
let pass2 = rpassword::read_password_from_tty(Some("Confirm password: "))?;
|
|
||||||
if pass1 != pass2 {
|
|
||||||
Err(PeachConfigError::InvalidPassword)
|
|
||||||
} else {
|
|
||||||
set_new_password(&pass1)
|
|
||||||
.map_err(|err| PeachConfigError::ChangePasswordError { source: err })?;
|
|
||||||
println!(
|
|
||||||
"Your new password has been set for peach-web. You can login through the \
|
|
||||||
web interface with username admin."
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
#![allow(clippy::nonstandard_macro_braces)]
|
#![allow(clippy::nonstandard_macro_braces)]
|
||||||
use peach_lib::error::PeachError;
|
|
||||||
pub use snafu::ResultExt;
|
pub use snafu::ResultExt;
|
||||||
use snafu::Snafu;
|
use snafu::Snafu;
|
||||||
|
|
||||||
@ -31,10 +30,6 @@ pub enum PeachConfigError {
|
|||||||
},
|
},
|
||||||
#[snafu(display("Error serializing json: {}", source))]
|
#[snafu(display("Error serializing json: {}", source))]
|
||||||
SerdeError { source: serde_json::Error },
|
SerdeError { source: serde_json::Error },
|
||||||
#[snafu(display("Error changing password: {}", source))]
|
|
||||||
ChangePasswordError { source: PeachError },
|
|
||||||
#[snafu(display("Entered passwords did not match. Please try again."))]
|
|
||||||
InvalidPassword,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for PeachConfigError {
|
impl From<std::io::Error> for PeachConfigError {
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
mod change_password;
|
|
||||||
mod constants;
|
mod constants;
|
||||||
mod error;
|
mod error;
|
||||||
mod generate_manifest;
|
mod generate_manifest;
|
||||||
mod set_permissions;
|
|
||||||
mod setup_networking;
|
mod setup_networking;
|
||||||
mod setup_peach;
|
mod setup_peach;
|
||||||
mod setup_peach_deb;
|
mod setup_peach_deb;
|
||||||
@ -14,6 +12,10 @@ use log::error;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
use crate::generate_manifest::generate_manifest;
|
||||||
|
use crate::setup_peach::setup_peach;
|
||||||
|
use crate::update::update;
|
||||||
|
|
||||||
#[derive(StructOpt, Debug)]
|
#[derive(StructOpt, Debug)]
|
||||||
#[structopt(
|
#[structopt(
|
||||||
name = "peach-config",
|
name = "peach-config",
|
||||||
@ -42,14 +44,6 @@ enum PeachConfig {
|
|||||||
/// Updates all PeachCloud microservices
|
/// Updates all PeachCloud microservices
|
||||||
#[structopt(name = "update")]
|
#[structopt(name = "update")]
|
||||||
Update(UpdateOpts),
|
Update(UpdateOpts),
|
||||||
|
|
||||||
/// Changes the password for the peach-web interface
|
|
||||||
#[structopt(name = "changepassword")]
|
|
||||||
ChangePassword(ChangePasswordOpts),
|
|
||||||
|
|
||||||
/// Updates file permissions on PeachCloud device
|
|
||||||
#[structopt(name = "permissions")]
|
|
||||||
SetPermissions,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(StructOpt, Debug)]
|
#[derive(StructOpt, Debug)]
|
||||||
@ -82,14 +76,6 @@ pub struct UpdateOpts {
|
|||||||
list: bool,
|
list: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(StructOpt, Debug)]
|
|
||||||
pub struct ChangePasswordOpts {
|
|
||||||
/// Optional argument to specify password as CLI argument
|
|
||||||
/// if not specified, this command asks for user input for the passwords
|
|
||||||
#[structopt(short, long)]
|
|
||||||
password: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_enum! {
|
arg_enum! {
|
||||||
/// enum options for real-time clock choices
|
/// enum options for real-time clock choices
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -113,48 +99,28 @@ fn main() {
|
|||||||
if let Some(subcommand) = opt.commands {
|
if let Some(subcommand) = opt.commands {
|
||||||
match subcommand {
|
match subcommand {
|
||||||
PeachConfig::Setup(cfg) => {
|
PeachConfig::Setup(cfg) => {
|
||||||
match setup_peach::setup_peach(cfg.no_input, cfg.default_locale, cfg.i2c, cfg.rtc) {
|
match setup_peach(cfg.no_input, cfg.default_locale, cfg.i2c, cfg.rtc) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("peach-config encountered an error: {}", err)
|
error!("peach-config encountered an error: {}", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PeachConfig::Manifest => match generate_manifest::generate_manifest() {
|
PeachConfig::Manifest => match generate_manifest() {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(
|
error!(
|
||||||
"peach-config encountered an error generating manifest: {}",
|
"peach-config countered an error generating manifest: {}",
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PeachConfig::Update(opts) => match update::update(opts) {
|
PeachConfig::Update(opts) => match update(opts) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("peach-config encountered an error during update: {}", err)
|
error!("peach-config encountered an error during update: {}", err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PeachConfig::ChangePassword(opts) => {
|
|
||||||
match change_password::set_peach_web_password(opts) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) => {
|
|
||||||
error!(
|
|
||||||
"peach-config encountered an error during password update: {}",
|
|
||||||
err
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PeachConfig::SetPermissions => match set_permissions::set_permissions() {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) => {
|
|
||||||
error!(
|
|
||||||
"peach-config ecountered an error updating file permissions: {}",
|
|
||||||
err
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
use crate::error::PeachConfigError;
|
|
||||||
use crate::utils::cmd;
|
|
||||||
|
|
||||||
/// All configs are stored in this folder, and should be read/writeable by peach group
|
|
||||||
/// so they can be read and written by all PeachCloud services.
|
|
||||||
pub const CONFIGS_DIR: &str = "/var/lib/peachcloud";
|
|
||||||
pub const PEACH_WEB_DIR: &str = "/usr/share/peach-web";
|
|
||||||
|
|
||||||
/// Utility function to set correct file permissions on the PeachCloud device.
|
|
||||||
/// Accidentally changing file permissions is a fairly common thing to happen,
|
|
||||||
/// so this is a useful CLI function for quickly correcting anything that may be out of order.
|
|
||||||
pub fn set_permissions() -> Result<(), PeachConfigError> {
|
|
||||||
println!("[ UPDATING FILE PERMISSIONS ON PEACHCLOUD DEVICE ]");
|
|
||||||
cmd(&["chmod", "-R", "u+rwX,g+rwX", CONFIGS_DIR])?;
|
|
||||||
cmd(&["chown", "-R", "peach", CONFIGS_DIR])?;
|
|
||||||
cmd(&["chgrp", "-R", "peach", CONFIGS_DIR])?;
|
|
||||||
cmd(&["chmod", "-R", "u+rwX,g+rwX", PEACH_WEB_DIR])?;
|
|
||||||
cmd(&["chown", "-R", "peach-web:peach", PEACH_WEB_DIR])?;
|
|
||||||
println!("[ PERMISSIONS SUCCESSFULLY UPDATED ]");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -68,7 +68,6 @@ pub fn setup_peach(
|
|||||||
"libssl-dev",
|
"libssl-dev",
|
||||||
"nginx",
|
"nginx",
|
||||||
"wget",
|
"wget",
|
||||||
"dnsutils",
|
|
||||||
"-y",
|
"-y",
|
||||||
])?;
|
])?;
|
||||||
|
|
||||||
|
4
peach-dyndns-updater/.cargo/config
Normal file
4
peach-dyndns-updater/.cargo/config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "aarch64-linux-gnu-gcc"
|
||||||
|
objcopy = { path ="aarch64-linux-gnu-objcopy" }
|
||||||
|
strip = { path ="aarch64-linux-gnu-strip" }
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peach-dyndns-updater"
|
name = "peach-dyndns-updater"
|
||||||
version = "0.1.8"
|
version = "0.1.6"
|
||||||
authors = ["Max Fowler <mfowler@commoninternet.net>"]
|
authors = ["Max Fowler <mfowler@commoninternet.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Sytemd timer which keeps a dynamic dns subdomain up to date with the latest device IP using nsupdate."
|
description = "Sytemd timer which keeps a dynamic dns subdomain up to date with the latest device IP using nsupdate."
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# exit when any command fails
|
|
||||||
set -e
|
|
||||||
|
|
||||||
KEYFILE=/Users/notplants/.ssh/id_rsa
|
|
||||||
SERVICE=peach-dyndns-updater
|
|
||||||
|
|
||||||
# deploy
|
|
||||||
rsync -avzh --exclude target --exclude .idea --exclude .git -e "ssh -i $KEYFILE" . rust@167.99.136.83:/srv/peachcloud/automation/peach-workspace/$SERVICE/
|
|
||||||
rsync -avzh --exclude target --exclude .idea --exclude .git -e "ssh -i $KEYFILE" ~/computer/projects/peachcloud/peach-workspace/peach-lib/ rust@167.99.136.83:/srv/peachcloud/automation/peach-workspace/peach-lib/
|
|
||||||
|
|
||||||
echo "++ cross compiling on vps"
|
|
||||||
BIN_PATH=$(ssh -i $KEYFILE rust@167.99.136.83 'cd /srv/peachcloud/automation/peach-workspace/peach-dyndns-updater; /home/rust/.cargo/bin/cargo clean -p peach-lib; /home/rust/.cargo/bin/cargo build --release --target=aarch64-unknown-linux-gnu')
|
|
||||||
|
|
||||||
echo "++ copying ${BIN_PATH} to local"
|
|
||||||
rm -f target/$SERVICE
|
|
||||||
scp -i $KEYFILE rust@167.99.136.83:/srv/peachcloud/automation/peach-workspace/target/aarch64-unknown-linux-gnu/release/peach-dyndns-updater ../target/vps-bin-$SERVICE
|
|
||||||
|
|
||||||
#echo "++ cross compiling"
|
|
||||||
BINFILE="../target/vps-bin-$SERVICE"
|
|
||||||
echo $BINFILE
|
|
||||||
|
|
||||||
|
|
||||||
echo "++ build successful"
|
|
||||||
|
|
||||||
echo "++ copying to pi"
|
|
||||||
ssh -t -i $KEYFILE peach@peach.link 'mkdir -p /srv/dev/bins'
|
|
||||||
scp -i $KEYFILE $BINFILE peach@peach.link:/srv/dev/bins/$SERVICE
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
|||||||
use log::info;
|
|
||||||
use peach_lib::dyndns_client::dyndns_update_ip;
|
use peach_lib::dyndns_client::dyndns_update_ip;
|
||||||
|
use log::{info};
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// initalize the logger
|
// initalize the logger
|
||||||
@ -8,4 +9,4 @@ fn main() {
|
|||||||
info!("Running peach-dyndns-updater");
|
info!("Running peach-dyndns-updater");
|
||||||
let result = dyndns_update_ip();
|
let result = dyndns_update_ip();
|
||||||
info!("result: {:?}", result);
|
info!("result: {:?}", result);
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peach-jsonrpc-server"
|
name = "peach-jsonrpc-server"
|
||||||
authors = ["Andrew Reid <glyph@mycelial.technology>"]
|
authors = ["Andrew Reid <glyph@mycelial.technology>"]
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "JSON-RPC over HTTP for the PeachCloud system. Provides a JSON-RPC wrapper around the stats, network and oled libraries."
|
description = "JSON-RPC over HTTP for the PeachCloud system. Provides a JSON-RPC wrapper around the stats, network and oled libraries."
|
||||||
homepage = "https://opencollective.com/peachcloud"
|
homepage = "https://opencollective.com/peachcloud"
|
||||||
@ -18,8 +18,10 @@ env_logger = "0.9"
|
|||||||
jsonrpc-core = "18"
|
jsonrpc-core = "18"
|
||||||
jsonrpc-http-server = "18"
|
jsonrpc-http-server = "18"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miniserde = "0.1.15"
|
peach-network = { path = "../peach-network", features = ["serde_support"] }
|
||||||
peach-stats = { path = "../peach-stats", features = ["miniserde_support"] }
|
peach-stats = { path = "../peach-stats", features = ["serde_support"] }
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
jsonrpc-test = "18"
|
jsonrpc-test = "18"
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use jsonrpc_core::{Error as JsonRpcError, ErrorCode};
|
use jsonrpc_core::{Error as JsonRpcError, ErrorCode};
|
||||||
|
use peach_network::NetworkError;
|
||||||
use peach_stats::StatsError;
|
use peach_stats::StatsError;
|
||||||
|
use serde_json::Error as SerdeError;
|
||||||
|
|
||||||
/// Custom error type encapsulating all possible errors for a JSON-RPC server
|
/// Custom error type encapsulating all possible errors for a JSON-RPC server
|
||||||
/// and associated methods.
|
/// and associated methods.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum JsonRpcServerError {
|
pub enum ServerError {
|
||||||
|
/// An error returned from the `peach-network` library.
|
||||||
|
Network(NetworkError),
|
||||||
|
/// Failed to serialize a data structure.
|
||||||
|
Serialize(SerdeError),
|
||||||
/// An error returned from the `peach-stats` library.
|
/// An error returned from the `peach-stats` library.
|
||||||
Stats(StatsError),
|
Stats(StatsError),
|
||||||
/// An expected JSON-RPC method parameter was not provided.
|
/// An expected JSON-RPC method parameter was not provided.
|
||||||
@ -15,32 +21,48 @@ pub enum JsonRpcServerError {
|
|||||||
ParseParameter(JsonRpcError),
|
ParseParameter(JsonRpcError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for JsonRpcServerError {
|
impl fmt::Display for ServerError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
JsonRpcServerError::ParseParameter(ref source) => {
|
ServerError::ParseParameter(ref source) => {
|
||||||
write!(f, "Failed to parse parameter: {}", source)
|
write!(f, "Failed to parse parameter: {}", source)
|
||||||
}
|
}
|
||||||
JsonRpcServerError::MissingParameter(ref source) => {
|
ServerError::MissingParameter(ref source) => {
|
||||||
write!(f, "Missing expected parameter: {}", source)
|
write!(f, "Missing expected parameter: {}", source)
|
||||||
}
|
}
|
||||||
JsonRpcServerError::Stats(ref source) => {
|
ServerError::Network(ref source) => {
|
||||||
|
write!(f, "{}", source)
|
||||||
|
}
|
||||||
|
ServerError::Serialize(ref source) => {
|
||||||
|
write!(f, "Serde serialization failure: {}", source)
|
||||||
|
}
|
||||||
|
ServerError::Stats(ref source) => {
|
||||||
write!(f, "{}", source)
|
write!(f, "{}", source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<JsonRpcServerError> for JsonRpcError {
|
impl From<ServerError> for JsonRpcError {
|
||||||
fn from(err: JsonRpcServerError) -> Self {
|
fn from(err: ServerError) -> Self {
|
||||||
match &err {
|
match &err {
|
||||||
JsonRpcServerError::Stats(source) => JsonRpcError {
|
ServerError::Network(source) => JsonRpcError {
|
||||||
code: ErrorCode::ServerError(-32001),
|
code: ErrorCode::ServerError(-32001),
|
||||||
message: format!("{}", source),
|
message: format!("{}", source),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
JsonRpcServerError::MissingParameter(source) => source.clone(),
|
ServerError::Stats(source) => JsonRpcError {
|
||||||
JsonRpcServerError::ParseParameter(source) => source.clone(),
|
code: ErrorCode::ServerError(-32003),
|
||||||
|
message: format!("{}", source),
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
ServerError::Serialize(source) => JsonRpcError {
|
||||||
|
code: ErrorCode::ServerError(-32000),
|
||||||
|
message: format!("{}", source),
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
ServerError::MissingParameter(source) => source.clone(),
|
||||||
|
ServerError::ParseParameter(source) => source.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,72 +5,332 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
|
||||||
use jsonrpc_core::{IoHandler, Value};
|
use jsonrpc_core::{Error as RpcCoreError, IoHandler, Params, Value};
|
||||||
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
|
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
|
||||||
use log::info;
|
use log::info;
|
||||||
use miniserde::json;
|
use peach_network::network;
|
||||||
use peach_stats::stats;
|
use peach_stats::stats;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
use crate::error::JsonRpcServerError;
|
mod params;
|
||||||
|
|
||||||
|
use crate::error::ServerError;
|
||||||
|
use crate::params::{Iface, IfaceId, IfaceIdPass, IfaceSsid, WiFi, WlanAndAp};
|
||||||
|
|
||||||
/// Create JSON-RPC I/O handler, add RPC methods and launch HTTP server.
|
/// Create JSON-RPC I/O handler, add RPC methods and launch HTTP server.
|
||||||
pub fn run() -> Result<(), JsonRpcServerError> {
|
pub fn run() -> Result<(), ServerError> {
|
||||||
info!("Starting up.");
|
info!("Starting up.");
|
||||||
|
|
||||||
info!("Creating JSON-RPC I/O handler.");
|
info!("Creating JSON-RPC I/O handler.");
|
||||||
let mut io = IoHandler::default();
|
let mut io = IoHandler::default();
|
||||||
|
|
||||||
io.add_sync_method("ping", |_| Ok(Value::String("success".to_string())));
|
io.add_method("ping", |_| async {
|
||||||
|
Ok(Value::String("success".to_string()))
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: add blocks of methods according to provided flags
|
// TODO: add blocks of methods according to provided flags
|
||||||
|
|
||||||
|
/* PEACH-NETWORK RPC METHODS */
|
||||||
|
|
||||||
|
// get - all network rpc methods for querying state
|
||||||
|
|
||||||
|
io.add_method("available_networks", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => {
|
||||||
|
match network::available_networks(&i.iface).map_err(ServerError::Network)? {
|
||||||
|
Some(list) => {
|
||||||
|
let json_list =
|
||||||
|
serde_json::to_string(&list).map_err(ServerError::Serialize)?;
|
||||||
|
Ok(Value::String(json_list))
|
||||||
|
}
|
||||||
|
// return `Null` if no networks were found
|
||||||
|
None => Ok(Value::Null),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("id", |params: Params| async move {
|
||||||
|
let parsed: Result<IfaceSsid, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::id(&i.iface, &i.ssid).map_err(ServerError::Network)? {
|
||||||
|
Some(id) => Ok(Value::String(id)),
|
||||||
|
None => Ok(Value::Null),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("ip", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::ip(&i.iface).map_err(ServerError::Network)? {
|
||||||
|
Some(ip) => Ok(Value::String(ip)),
|
||||||
|
None => Ok(Value::Null),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("rssi", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::rssi(&i.iface).map_err(ServerError::Network)? {
|
||||||
|
Some(rssi) => Ok(Value::String(rssi)),
|
||||||
|
None => Ok(Value::Null),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("rssi_percent", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::rssi_percent(&i.iface).map_err(ServerError::Network)? {
|
||||||
|
Some(rssi) => Ok(Value::String(rssi)),
|
||||||
|
None => Ok(Value::Null),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("saved_networks", |_| async {
|
||||||
|
let list = network::saved_networks().map_err(ServerError::Network)?;
|
||||||
|
match list {
|
||||||
|
Some(list) => {
|
||||||
|
let json_list = serde_json::to_string(&list).map_err(ServerError::Serialize)?;
|
||||||
|
Ok(Value::String(json_list))
|
||||||
|
}
|
||||||
|
None => Ok(Value::Null),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("ssid", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::ssid(&i.iface).map_err(ServerError::Network)? {
|
||||||
|
Some(ip) => Ok(Value::String(ip)),
|
||||||
|
None => Ok(Value::Null),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("state", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::state(&i.iface).map_err(ServerError::Network)? {
|
||||||
|
Some(state) => Ok(Value::String(state)),
|
||||||
|
None => Ok(Value::Null),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("status", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::status(&i.iface).map_err(ServerError::Network)? {
|
||||||
|
Some(status) => {
|
||||||
|
let json_status =
|
||||||
|
serde_json::to_string(&status).map_err(ServerError::Serialize)?;
|
||||||
|
Ok(Value::String(json_status))
|
||||||
|
}
|
||||||
|
None => Ok(Value::Null),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("traffic", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::traffic(&i.iface).map_err(ServerError::Network)? {
|
||||||
|
Some(traffic) => {
|
||||||
|
let json_traffic =
|
||||||
|
serde_json::to_string(&traffic).map_err(ServerError::Serialize)?;
|
||||||
|
Ok(Value::String(json_traffic))
|
||||||
|
}
|
||||||
|
None => Ok(Value::Null),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// set - all network rpc methods for modifying state
|
||||||
|
|
||||||
|
io.add_method("add", |params: Params| async move {
|
||||||
|
let parsed: Result<WiFi, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(w) => match network::add(&w.iface, &w.ssid, &w.pass) {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("check_iface", |params: Params| async move {
|
||||||
|
let parsed: Result<WlanAndAp, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(w) => match network::check_iface(&w.wlan_iface, &w.ap_iface) {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("connect", |params: Params| async move {
|
||||||
|
let parsed: Result<IfaceId, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::connect(&i.id, &i.iface) {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("delete", |params: Params| async move {
|
||||||
|
let parsed: Result<IfaceId, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::delete(&i.id, &i.iface) {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("disable", |params: Params| async move {
|
||||||
|
let parsed: Result<IfaceId, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::disable(&i.id, &i.iface) {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("disconnect", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::disconnect(&i.iface) {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("modify", |params: Params| async move {
|
||||||
|
let parsed: Result<IfaceIdPass, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::modify(&i.iface, &i.id, &i.pass) {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("reassociate", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::reassociate(&i.iface) {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("reconfigure", |_| async {
|
||||||
|
match network::reconfigure() {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("reconnect", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::reconnect(&i.iface) {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("save", |_| async {
|
||||||
|
match network::save() {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_method("start_iface_service", |params: Params| async move {
|
||||||
|
let parsed: Result<Iface, RpcCoreError> = params.parse();
|
||||||
|
match parsed {
|
||||||
|
Ok(i) => match network::start_iface_service(&i.iface) {
|
||||||
|
Ok(_) => Ok(Value::String("success".to_string())),
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::Network(e))),
|
||||||
|
},
|
||||||
|
Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/* PEACH-STATS RPC METHODS */
|
/* PEACH-STATS RPC METHODS */
|
||||||
|
|
||||||
io.add_sync_method("cpu_stats", move |_| {
|
io.add_method("cpu_stats", |_| async {
|
||||||
info!("Fetching CPU statistics.");
|
info!("Fetching CPU statistics.");
|
||||||
let cpu = stats::cpu_stats().map_err(JsonRpcServerError::Stats)?;
|
let cpu = stats::cpu_stats().map_err(ServerError::Stats)?;
|
||||||
let json_cpu = json::to_string(&cpu);
|
let json_cpu = serde_json::to_string(&cpu).map_err(ServerError::Serialize)?;
|
||||||
|
|
||||||
Ok(Value::String(json_cpu))
|
Ok(Value::String(json_cpu))
|
||||||
});
|
});
|
||||||
|
|
||||||
io.add_sync_method("cpu_stats_percent", move |_| {
|
io.add_method("cpu_stats_percent", |_| async {
|
||||||
info!("Fetching CPU statistics as percentages.");
|
info!("Fetching CPU statistics as percentages.");
|
||||||
let cpu = stats::cpu_stats_percent().map_err(JsonRpcServerError::Stats)?;
|
let cpu = stats::cpu_stats_percent().map_err(ServerError::Stats)?;
|
||||||
let json_cpu = json::to_string(&cpu);
|
let json_cpu = serde_json::to_string(&cpu).map_err(ServerError::Serialize)?;
|
||||||
|
|
||||||
Ok(Value::String(json_cpu))
|
Ok(Value::String(json_cpu))
|
||||||
});
|
});
|
||||||
|
|
||||||
io.add_sync_method("disk_usage", move |_| {
|
io.add_method("disk_usage", |_| async {
|
||||||
info!("Fetching disk usage statistics.");
|
info!("Fetching disk usage statistics.");
|
||||||
let disks = stats::disk_usage().map_err(JsonRpcServerError::Stats)?;
|
let disks = stats::disk_usage().map_err(ServerError::Stats)?;
|
||||||
let json_disks = json::to_string(&disks);
|
let json_disks = serde_json::to_string(&disks).map_err(ServerError::Serialize)?;
|
||||||
|
|
||||||
Ok(Value::String(json_disks))
|
Ok(Value::String(json_disks))
|
||||||
});
|
});
|
||||||
|
|
||||||
io.add_sync_method("load_average", move |_| {
|
io.add_method("load_average", |_| async {
|
||||||
info!("Fetching system load average statistics.");
|
info!("Fetching system load average statistics.");
|
||||||
let avg = stats::load_average().map_err(JsonRpcServerError::Stats)?;
|
let avg = stats::load_average().map_err(ServerError::Stats)?;
|
||||||
let json_avg = json::to_string(&avg);
|
let json_avg = serde_json::to_string(&avg).map_err(ServerError::Serialize)?;
|
||||||
|
|
||||||
Ok(Value::String(json_avg))
|
Ok(Value::String(json_avg))
|
||||||
});
|
});
|
||||||
|
|
||||||
io.add_sync_method("mem_stats", move |_| {
|
io.add_method("mem_stats", |_| async {
|
||||||
info!("Fetching current memory statistics.");
|
info!("Fetching current memory statistics.");
|
||||||
let mem = stats::mem_stats().map_err(JsonRpcServerError::Stats)?;
|
let mem = stats::mem_stats().map_err(ServerError::Stats)?;
|
||||||
let json_mem = json::to_string(&mem);
|
let json_mem = serde_json::to_string(&mem).map_err(ServerError::Serialize)?;
|
||||||
|
|
||||||
Ok(Value::String(json_mem))
|
Ok(Value::String(json_mem))
|
||||||
});
|
});
|
||||||
|
|
||||||
io.add_sync_method("uptime", move |_| {
|
io.add_method("uptime", |_| async {
|
||||||
info!("Fetching system uptime.");
|
info!("Fetching system uptime.");
|
||||||
let uptime = stats::uptime().map_err(JsonRpcServerError::Stats)?;
|
let uptime = stats::uptime().map_err(ServerError::Stats)?;
|
||||||
let json_uptime = json::to_string(&uptime);
|
let json_uptime = serde_json::to_string(&uptime).map_err(ServerError::Serialize)?;
|
||||||
|
|
||||||
Ok(Value::String(json_uptime))
|
Ok(Value::String(json_uptime))
|
||||||
});
|
});
|
||||||
@ -106,7 +366,7 @@ mod tests {
|
|||||||
fn rpc_success() {
|
fn rpc_success() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_sync_method("rpc_success_response", |_| {
|
io.add_method("rpc_success_response", |_| async {
|
||||||
Ok(Value::String("success".into()))
|
Ok(Value::String("success".into()))
|
||||||
});
|
});
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
@ -119,13 +379,13 @@ mod tests {
|
|||||||
fn rpc_parse_error() {
|
fn rpc_parse_error() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_sync_method("rpc_parse_error", |_| {
|
io.add_method("rpc_parse_error", |_| async {
|
||||||
let e = JsonRpcError {
|
let e = JsonRpcError {
|
||||||
code: ErrorCode::ParseError,
|
code: ErrorCode::ParseError,
|
||||||
message: String::from("Parse error"),
|
message: String::from("Parse error"),
|
||||||
data: None,
|
data: None,
|
||||||
};
|
};
|
||||||
Err(JsonRpcError::from(JsonRpcServerError::MissingParameter(e)))
|
Err(JsonRpcError::from(ServerError::MissingParameter(e)))
|
||||||
});
|
});
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
};
|
};
|
||||||
|
@ -10,12 +10,51 @@
|
|||||||
//!
|
//!
|
||||||
//! | Method | Description | Returns |
|
//! | Method | Description | Returns |
|
||||||
//! | --- | --- | --- |
|
//! | --- | --- | --- |
|
||||||
|
//! | `ping` | Microservice status | `success` if running |
|
||||||
|
//!
|
||||||
|
//! ### Network
|
||||||
|
//!
|
||||||
|
//! Methods for **retrieving data**:
|
||||||
|
//!
|
||||||
|
//! | Method | Description | Returns |
|
||||||
|
//! | --- | --- | --- |
|
||||||
|
//! | `available_networks` | `iface` | List SSID, flags (security), frequency and signal level for all networks in range of given interface |
|
||||||
|
//! | `id` | `iface`, `ssid` | Return ID of given SSID |
|
||||||
|
//! | `ip` | `iface` | Return IP of given network interface |
|
||||||
|
//! | `rssi` | `iface` | Return average signal strength (dBm) for given interface |
|
||||||
|
//! | `rssi_percent` | `iface` | Return average signal strength (%) for given interface |
|
||||||
|
//! | `saved_networks` | | List all networks saved in wpasupplicant config |
|
||||||
|
//! | `ssid` | `iface` | Return SSID of currently-connected network for given interface |
|
||||||
|
//! | `state` | `iface` | Return state of given interface |
|
||||||
|
//! | `status` | `iface` | Return status parameters for given interface |
|
||||||
|
//! | `traffic` | `iface` | Return network traffic for given interface |
|
||||||
|
//!
|
||||||
|
//! Methods for **modifying state**:
|
||||||
|
//!
|
||||||
|
//! | Method | Parameters | Description |
|
||||||
|
//! | --- | --- | --- |
|
||||||
|
//! | `add` | `iface`, `ssid`, `pass` | Add WiFi credentials to `wpa_supplicant-<iface>.conf` |
|
||||||
|
//! | `check_iface` | `wlan_iface`, `ap_iface` | Activate WiFi access point on <ap_iface> if <wlan_iface> is active without a connection |
|
||||||
|
//! | `connect` | `id`, `iface` | Disable other networks and attempt connection with AP represented by given id |
|
||||||
|
//! | `delete` | `id`, `iface` | Remove WiFi credentials for given network id and interface |
|
||||||
|
//! | `disable` | `id`, `iface` | Disable connection with AP represented by given id |
|
||||||
|
//! | `disconnect` | `iface` | Disconnect given interface |
|
||||||
|
//! | `modify` | `id`, `iface`, `pass` | Set a new password for given network id and interface |
|
||||||
|
//! | `reassociate` | `iface` | Reassociate with current AP for given interface |
|
||||||
|
//! | `reconfigure` | | Force wpa_supplicant to re-read its configuration file |
|
||||||
|
//! | `reconnect` | `iface` | Disconnect and reconnect given interface |
|
||||||
|
//! | `save` | | Save configuration changes to `wpa_supplicant-wlan0.conf` |
|
||||||
|
//! | `start_iface_server` | `iface` | Start the `systemd` service for the given interface |
|
||||||
|
//!
|
||||||
|
//! ### System Statistics
|
||||||
|
//!
|
||||||
|
//! | Method | Description | Returns |
|
||||||
|
//! | --- | --- | --- |
|
||||||
//! | `cpu_stats` | CPU statistics | `user`, `system`, `nice`, `idle` |
|
//! | `cpu_stats` | CPU statistics | `user`, `system`, `nice`, `idle` |
|
||||||
//! | `cpu_stats_percent` | CPU statistics as percentages | `user`, `system`, `nice`, `idle` |
|
//! | `cpu_stats_percent` | CPU statistics as percentages | `user`, `system`, `nice`, `idle` |
|
||||||
//! | `disk_usage` | Disk usage statistics (array of disks) | `filesystem`, `one_k_blocks`, `one_k_blocks_used`, `one_k_blocks_free`, `used_percentage`, `mountpoint` |
|
//! | `disk_usage` | Disk usage statistics (array of disks) | `filesystem`, `one_k_blocks`, `one_k_blocks_used`, `one_k_blocks_free`, `used_percentage`, `mountpoint` |
|
||||||
//! | `load_average` | Load average statistics | `one`, `five`, `fifteen` |
|
//! | `load_average` | Load average statistics | `one`, `five`, `fifteen` |
|
||||||
//! | `mem_stats` | Memory statistics | `total`, `free`, `used` |
|
//! | `mem_stats` | Memory statistics | `total`, `free`, `used` |
|
||||||
//! | `ping` | Microservice status | `success` if running |
|
|
||||||
//! | `uptime` | System uptime | `secs` |
|
//! | `uptime` | System uptime | `secs` |
|
||||||
|
|
||||||
use std::process;
|
use std::process;
|
||||||
|
54
peach-jsonrpc-server/src/params.rs
Normal file
54
peach-jsonrpc-server/src/params.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
//! Data structures for parsing JSON-RPC method parameters.
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
// why do we have multiple structs when we could rather combine them by using
|
||||||
|
// `Option<String>` for some fields? simply because it's easier to handle the
|
||||||
|
// parsed parameters in our json-rpc server methods. we don't have to check
|
||||||
|
// if a given field is `Some` or `None` before passing it into the relevant
|
||||||
|
// function.
|
||||||
|
|
||||||
|
/// Network interface name.
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Iface {
|
||||||
|
pub iface: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Network interface name and network identifier.
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct IfaceId {
|
||||||
|
pub iface: String,
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Network interface name, network identifier and password.
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct IfaceIdPass {
|
||||||
|
pub iface: String,
|
||||||
|
pub id: String,
|
||||||
|
pub pass: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Network interface name and network SSID.
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct IfaceSsid {
|
||||||
|
pub iface: String,
|
||||||
|
pub ssid: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wireless interface (for which the WiFi credentials will be added), SSID
|
||||||
|
/// and password for a wireless access point.
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct WiFi {
|
||||||
|
pub iface: String,
|
||||||
|
pub ssid: String,
|
||||||
|
pub pass: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wireles network interface and local Access Point network interface (the
|
||||||
|
/// interface on which we deloy or own AP).
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct WlanAndAp {
|
||||||
|
pub wlan_iface: String,
|
||||||
|
pub ap_iface: String,
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peach-lib"
|
name = "peach-lib"
|
||||||
version = "1.3.2"
|
version = "1.3.1"
|
||||||
authors = ["Andrew Reid <glyph@mycelial.technology>"]
|
authors = ["Andrew Reid <glyph@mycelial.technology>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ jsonrpc-core = "8.0.1"
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
nanorand = "0.6.1"
|
nanorand = "0.6.1"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
|
rust-crypto = "0.2.36"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
sha3 = "0.10.0"
|
|
||||||
|
@ -17,10 +17,6 @@ pub const YAML_PATH: &str = "/var/lib/peachcloud/config.yml";
|
|||||||
// lock file (used to avoid race conditions during config reading & writing)
|
// lock file (used to avoid race conditions during config reading & writing)
|
||||||
pub const LOCK_FILE_PATH: &str = "/var/lib/peachcloud/config.lock";
|
pub const LOCK_FILE_PATH: &str = "/var/lib/peachcloud/config.lock";
|
||||||
|
|
||||||
// default values
|
|
||||||
pub const DEFAULT_DYN_SERVER_ADDRESS: &str = "http://dynserver.dyn.peachcloud.org";
|
|
||||||
pub const DEFAULT_DYN_NAMESERVER: &str = "ns.peachcloud.org";
|
|
||||||
|
|
||||||
// we make use of Serde default values in order to make PeachCloud
|
// we make use of Serde default values in order to make PeachCloud
|
||||||
// robust and keep running even with a not fully complete config.yml
|
// robust and keep running even with a not fully complete config.yml
|
||||||
// main type which represents all peachcloud configurations
|
// main type which represents all peachcloud configurations
|
||||||
@ -33,10 +29,6 @@ pub struct PeachConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub dyn_dns_server_address: String,
|
pub dyn_dns_server_address: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub dyn_use_custom_server: bool,
|
|
||||||
#[serde(default)]
|
|
||||||
pub dyn_nameserver: String,
|
|
||||||
#[serde(default)]
|
|
||||||
pub dyn_tsig_key_path: String,
|
pub dyn_tsig_key_path: String,
|
||||||
#[serde(default)] // default is false
|
#[serde(default)] // default is false
|
||||||
pub dyn_enabled: bool,
|
pub dyn_enabled: bool,
|
||||||
@ -71,19 +63,20 @@ fn save_peach_config(peach_config: PeachConfig) -> Result<PeachConfig, PeachErro
|
|||||||
pub fn load_peach_config() -> Result<PeachConfig, PeachError> {
|
pub fn load_peach_config() -> Result<PeachConfig, PeachError> {
|
||||||
let peach_config_exists = std::path::Path::new(YAML_PATH).exists();
|
let peach_config_exists = std::path::Path::new(YAML_PATH).exists();
|
||||||
|
|
||||||
let peach_config: PeachConfig = if !peach_config_exists {
|
let peach_config: PeachConfig;
|
||||||
PeachConfig {
|
|
||||||
|
// if this is the first time loading peach_config, we can create a default here
|
||||||
|
if !peach_config_exists {
|
||||||
|
peach_config = PeachConfig {
|
||||||
external_domain: "".to_string(),
|
external_domain: "".to_string(),
|
||||||
dyn_domain: "".to_string(),
|
dyn_domain: "".to_string(),
|
||||||
dyn_dns_server_address: DEFAULT_DYN_SERVER_ADDRESS.to_string(),
|
dyn_dns_server_address: "".to_string(),
|
||||||
dyn_use_custom_server: false,
|
|
||||||
dyn_nameserver: DEFAULT_DYN_NAMESERVER.to_string(),
|
|
||||||
dyn_tsig_key_path: "".to_string(),
|
dyn_tsig_key_path: "".to_string(),
|
||||||
dyn_enabled: false,
|
dyn_enabled: false,
|
||||||
ssb_admin_ids: Vec::new(),
|
ssb_admin_ids: Vec::new(),
|
||||||
admin_password_hash: "".to_string(),
|
admin_password_hash: "".to_string(),
|
||||||
temporary_password_hash: "".to_string(),
|
temporary_password_hash: "".to_string(),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
// otherwise we load peach config from disk
|
// otherwise we load peach config from disk
|
||||||
else {
|
else {
|
||||||
@ -91,8 +84,8 @@ pub fn load_peach_config() -> Result<PeachConfig, PeachError> {
|
|||||||
source,
|
source,
|
||||||
path: YAML_PATH.to_string(),
|
path: YAML_PATH.to_string(),
|
||||||
})?;
|
})?;
|
||||||
serde_yaml::from_str(&contents)?
|
peach_config = serde_yaml::from_str(&contents)?;
|
||||||
};
|
}
|
||||||
|
|
||||||
Ok(peach_config)
|
Ok(peach_config)
|
||||||
}
|
}
|
||||||
@ -129,18 +122,6 @@ pub fn get_peachcloud_domain() -> Result<Option<String>, PeachError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dyndns_server_address() -> Result<String, PeachError> {
|
|
||||||
let peach_config = load_peach_config()?;
|
|
||||||
// if the user is using a custom dyn server then load the address from the config
|
|
||||||
if peach_config.dyn_use_custom_server {
|
|
||||||
Ok(peach_config.dyn_dns_server_address)
|
|
||||||
}
|
|
||||||
// otherwise hardcode the address
|
|
||||||
else {
|
|
||||||
Ok(DEFAULT_DYN_SERVER_ADDRESS.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_dyndns_enabled_value(enabled_value: bool) -> Result<PeachConfig, PeachError> {
|
pub fn set_dyndns_enabled_value(enabled_value: bool) -> Result<PeachConfig, PeachError> {
|
||||||
let mut peach_config = load_peach_config()?;
|
let mut peach_config = load_peach_config()?;
|
||||||
peach_config.dyn_enabled = enabled_value;
|
peach_config.dyn_enabled = enabled_value;
|
||||||
|
@ -9,8 +9,13 @@
|
|||||||
//!
|
//!
|
||||||
//! The domain for dyndns updates is stored in /var/lib/peachcloud/config.yml
|
//! The domain for dyndns updates is stored in /var/lib/peachcloud/config.yml
|
||||||
//! The tsig key for authenticating the updates is stored in /var/lib/peachcloud/peach-dyndns/tsig.key
|
//! The tsig key for authenticating the updates is stored in /var/lib/peachcloud/peach-dyndns/tsig.key
|
||||||
use std::ffi::OsStr;
|
use std::{
|
||||||
use std::{fs, fs::OpenOptions, io::Write, process::Command, str::FromStr};
|
fs,
|
||||||
|
fs::OpenOptions,
|
||||||
|
io::Write,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use jsonrpc_client_core::{expand_params, jsonrpc_client};
|
use jsonrpc_client_core::{expand_params, jsonrpc_client};
|
||||||
@ -18,10 +23,13 @@ use jsonrpc_client_http::HttpTransport;
|
|||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::config_manager::get_dyndns_server_address;
|
use crate::{
|
||||||
use crate::{config_manager, error::PeachError};
|
config_manager::{load_peach_config, set_peach_dyndns_config},
|
||||||
|
error::PeachError,
|
||||||
|
};
|
||||||
|
|
||||||
/// constants for dyndns configuration
|
/// constants for dyndns configuration
|
||||||
|
pub const PEACH_DYNDNS_URL: &str = "http://dynserver.dyn.peachcloud.org";
|
||||||
pub const TSIG_KEY_PATH: &str = "/var/lib/peachcloud/peach-dyndns/tsig.key";
|
pub const TSIG_KEY_PATH: &str = "/var/lib/peachcloud/peach-dyndns/tsig.key";
|
||||||
pub const PEACH_DYNDNS_CONFIG_PATH: &str = "/var/lib/peachcloud/peach-dyndns";
|
pub const PEACH_DYNDNS_CONFIG_PATH: &str = "/var/lib/peachcloud/peach-dyndns";
|
||||||
pub const DYNDNS_LOG_PATH: &str = "/var/lib/peachcloud/peach-dyndns/latest_result.log";
|
pub const DYNDNS_LOG_PATH: &str = "/var/lib/peachcloud/peach-dyndns/latest_result.log";
|
||||||
@ -54,10 +62,9 @@ pub fn save_dyndns_key(key: &str) -> Result<(), PeachError> {
|
|||||||
pub fn register_domain(domain: &str) -> std::result::Result<String, PeachError> {
|
pub fn register_domain(domain: &str) -> std::result::Result<String, PeachError> {
|
||||||
debug!("Creating HTTP transport for dyndns client.");
|
debug!("Creating HTTP transport for dyndns client.");
|
||||||
let transport = HttpTransport::new().standalone()?;
|
let transport = HttpTransport::new().standalone()?;
|
||||||
let http_server = get_dyndns_server_address()?;
|
let http_server = PEACH_DYNDNS_URL;
|
||||||
info!("Using dyndns http server address: {:?}", http_server);
|
debug!("Creating HTTP transport handle on {}.", http_server);
|
||||||
debug!("Creating HTTP transport handle on {}.", &http_server);
|
let transport_handle = transport.handle(http_server)?;
|
||||||
let transport_handle = transport.handle(&http_server)?;
|
|
||||||
info!("Creating client for peach-dyndns service.");
|
info!("Creating client for peach-dyndns service.");
|
||||||
let mut client = PeachDynDnsClient::new(transport_handle);
|
let mut client = PeachDynDnsClient::new(transport_handle);
|
||||||
|
|
||||||
@ -66,8 +73,7 @@ pub fn register_domain(domain: &str) -> std::result::Result<String, PeachError>
|
|||||||
// save new TSIG key
|
// save new TSIG key
|
||||||
save_dyndns_key(&key)?;
|
save_dyndns_key(&key)?;
|
||||||
// save new configuration values
|
// save new configuration values
|
||||||
let set_config_result =
|
let set_config_result = set_peach_dyndns_config(domain, PEACH_DYNDNS_URL, TSIG_KEY_PATH, true);
|
||||||
config_manager::set_peach_dyndns_config(domain, &http_server, TSIG_KEY_PATH, true);
|
|
||||||
match set_config_result {
|
match set_config_result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let response = "success".to_string();
|
let response = "success".to_string();
|
||||||
@ -81,9 +87,9 @@ pub fn register_domain(domain: &str) -> std::result::Result<String, PeachError>
|
|||||||
pub fn is_domain_available(domain: &str) -> std::result::Result<bool, PeachError> {
|
pub fn is_domain_available(domain: &str) -> std::result::Result<bool, PeachError> {
|
||||||
debug!("Creating HTTP transport for dyndns client.");
|
debug!("Creating HTTP transport for dyndns client.");
|
||||||
let transport = HttpTransport::new().standalone()?;
|
let transport = HttpTransport::new().standalone()?;
|
||||||
let http_server = get_dyndns_server_address()?;
|
let http_server = PEACH_DYNDNS_URL;
|
||||||
debug!("Creating HTTP transport handle on {}.", &http_server);
|
debug!("Creating HTTP transport handle on {}.", http_server);
|
||||||
let transport_handle = transport.handle(&http_server)?;
|
let transport_handle = transport.handle(http_server)?;
|
||||||
info!("Creating client for peach_network service.");
|
info!("Creating client for peach_network service.");
|
||||||
let mut client = PeachDynDnsClient::new(transport_handle);
|
let mut client = PeachDynDnsClient::new(transport_handle);
|
||||||
|
|
||||||
@ -107,31 +113,31 @@ fn get_public_ip_address() -> Result<String, PeachError> {
|
|||||||
/// Reads dyndns configurations from config.yml
|
/// Reads dyndns configurations from config.yml
|
||||||
/// and then uses nsupdate to update the IP address for the configured domain
|
/// and then uses nsupdate to update the IP address for the configured domain
|
||||||
pub fn dyndns_update_ip() -> Result<bool, PeachError> {
|
pub fn dyndns_update_ip() -> Result<bool, PeachError> {
|
||||||
let peach_config = config_manager::load_peach_config()?;
|
info!("Running dyndns_update_ip");
|
||||||
|
let peach_config = load_peach_config()?;
|
||||||
info!(
|
info!(
|
||||||
"Using config:
|
"Using config:
|
||||||
dyn_tsig_key_path: {:?}
|
dyn_tsig_key_path: {:?}
|
||||||
dyn_domain: {:?}
|
dyn_domain: {:?}
|
||||||
dyn_dns_server_address: {:?}
|
dyn_dns_server_address: {:?}
|
||||||
dyn_enabled: {:?}
|
dyn_enabled: {:?}
|
||||||
dyn_nameserver: {:?}
|
|
||||||
",
|
",
|
||||||
peach_config.dyn_tsig_key_path,
|
peach_config.dyn_tsig_key_path,
|
||||||
peach_config.dyn_domain,
|
peach_config.dyn_domain,
|
||||||
peach_config.dyn_dns_server_address,
|
peach_config.dyn_dns_server_address,
|
||||||
peach_config.dyn_enabled,
|
peach_config.dyn_enabled,
|
||||||
peach_config.dyn_nameserver,
|
|
||||||
);
|
);
|
||||||
if !peach_config.dyn_enabled {
|
if !peach_config.dyn_enabled {
|
||||||
info!("dyndns is not enabled, not updating");
|
info!("dyndns is not enabled, not updating");
|
||||||
Ok(false)
|
Ok(false)
|
||||||
} else {
|
} else {
|
||||||
// call nsupdate passing appropriate configs
|
// call nsupdate passing appropriate configs
|
||||||
let mut nsupdate_command = Command::new("/usr/bin/nsupdate");
|
let mut nsupdate_command = Command::new("/usr/bin/nsupdate")
|
||||||
nsupdate_command
|
|
||||||
.arg("-k")
|
.arg("-k")
|
||||||
.arg(&peach_config.dyn_tsig_key_path)
|
.arg(&peach_config.dyn_tsig_key_path)
|
||||||
.arg("-v");
|
.arg("-v")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
// pass nsupdate commands via stdin
|
// pass nsupdate commands via stdin
|
||||||
let public_ip_address = get_public_ip_address()?;
|
let public_ip_address = get_public_ip_address()?;
|
||||||
info!("found public ip address: {}", public_ip_address);
|
info!("found public ip address: {}", public_ip_address);
|
||||||
@ -142,20 +148,20 @@ pub fn dyndns_update_ip() -> Result<bool, PeachError> {
|
|||||||
update delete {DOMAIN} A
|
update delete {DOMAIN} A
|
||||||
update add {DOMAIN} 30 A {PUBLIC_IP_ADDRESS}
|
update add {DOMAIN} 30 A {PUBLIC_IP_ADDRESS}
|
||||||
send",
|
send",
|
||||||
NAMESERVER = peach_config.dyn_nameserver,
|
NAMESERVER = "ns.peachcloud.org",
|
||||||
ZONE = peach_config.dyn_domain,
|
ZONE = peach_config.dyn_domain,
|
||||||
DOMAIN = peach_config.dyn_domain,
|
DOMAIN = peach_config.dyn_domain,
|
||||||
PUBLIC_IP_ADDRESS = public_ip_address,
|
PUBLIC_IP_ADDRESS = public_ip_address,
|
||||||
);
|
);
|
||||||
info!("ns_commands: {:?}", ns_commands);
|
let mut nsupdate_stdin = nsupdate_command.stdin.take().ok_or(PeachError::NsUpdate {
|
||||||
info!("creating nsupdate temp file");
|
msg: "unable to capture stdin handle for `nsupdate` command".to_string(),
|
||||||
let temp_file_path = "/var/lib/peachcloud/nsupdate.sh";
|
})?;
|
||||||
// write ns_commands to temp_file
|
write!(nsupdate_stdin, "{}", ns_commands).map_err(|source| PeachError::Write {
|
||||||
fs::write(temp_file_path, ns_commands)?;
|
source,
|
||||||
nsupdate_command.arg(temp_file_path);
|
path: peach_config.dyn_tsig_key_path.to_string(),
|
||||||
let nsupdate_output = nsupdate_command.output()?;
|
})?;
|
||||||
let args: Vec<&OsStr> = nsupdate_command.get_args().collect();
|
let nsupdate_output = nsupdate_command.wait_with_output()?;
|
||||||
info!("nsupdate command: {:?}", args);
|
info!("nsupdate output: {:?}", nsupdate_output);
|
||||||
// We only return a successful result if nsupdate was successful
|
// We only return a successful result if nsupdate was successful
|
||||||
if nsupdate_output.status.success() {
|
if nsupdate_output.status.success() {
|
||||||
info!("nsupdate succeeded, returning ok");
|
info!("nsupdate succeeded, returning ok");
|
||||||
@ -198,7 +204,7 @@ pub fn get_num_seconds_since_successful_dns_update() -> Result<Option<i64>, Peac
|
|||||||
})?;
|
})?;
|
||||||
// replace newline if found
|
// replace newline if found
|
||||||
// TODO: maybe we can use `.trim()` instead
|
// TODO: maybe we can use `.trim()` instead
|
||||||
let contents = contents.replace('\n', "");
|
let contents = contents.replace("\n", "");
|
||||||
// TODO: consider adding additional context?
|
// TODO: consider adding additional context?
|
||||||
let time_ran_dt = DateTime::parse_from_rfc3339(&contents).map_err(|source| {
|
let time_ran_dt = DateTime::parse_from_rfc3339(&contents).map_err(|source| {
|
||||||
PeachError::ParseDateTime {
|
PeachError::ParseDateTime {
|
||||||
@ -217,15 +223,20 @@ pub fn get_num_seconds_since_successful_dns_update() -> Result<Option<i64>, Peac
|
|||||||
/// and has successfully run recently (in the last six minutes)
|
/// and has successfully run recently (in the last six minutes)
|
||||||
pub fn is_dns_updater_online() -> Result<bool, PeachError> {
|
pub fn is_dns_updater_online() -> Result<bool, PeachError> {
|
||||||
// first check if it is enabled in peach-config
|
// first check if it is enabled in peach-config
|
||||||
let peach_config = config_manager::load_peach_config()?;
|
let peach_config = load_peach_config()?;
|
||||||
let is_enabled = peach_config.dyn_enabled;
|
let is_enabled = peach_config.dyn_enabled;
|
||||||
// then check if it has successfully run within the last 6 minutes (60*6 seconds)
|
// then check if it has successfully run within the last 6 minutes (60*6 seconds)
|
||||||
let num_seconds_since_successful_update = get_num_seconds_since_successful_dns_update()?;
|
let num_seconds_since_successful_update = get_num_seconds_since_successful_dns_update()?;
|
||||||
let ran_recently: bool = match num_seconds_since_successful_update {
|
let ran_recently: bool;
|
||||||
Some(seconds) => seconds < (60 * 6),
|
match num_seconds_since_successful_update {
|
||||||
|
Some(seconds) => {
|
||||||
|
ran_recently = seconds < (60 * 6);
|
||||||
|
}
|
||||||
// if the value is None, then the last time it ran successfully is unknown
|
// if the value is None, then the last time it ran successfully is unknown
|
||||||
None => false,
|
None => {
|
||||||
};
|
ran_recently = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
// debug log
|
// debug log
|
||||||
info!("is_dyndns_enabled: {:?}", is_enabled);
|
info!("is_dyndns_enabled: {:?}", is_enabled);
|
||||||
info!("dyndns_ran_recently: {:?}", ran_recently);
|
info!("dyndns_ran_recently: {:?}", ran_recently);
|
||||||
@ -247,10 +258,11 @@ pub fn get_dyndns_subdomain(dyndns_full_domain: &str) -> Option<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// helper function which checks if a dyndns domain is new
|
// helper function which checks if a dyndns domain is new
|
||||||
pub fn check_is_new_dyndns_domain(dyndns_full_domain: &str) -> Result<bool, PeachError> {
|
pub fn check_is_new_dyndns_domain(dyndns_full_domain: &str) -> bool {
|
||||||
let peach_config = config_manager::load_peach_config()?;
|
// TODO: return `Result<bool, PeachError>` and replace `unwrap` with `?` operator
|
||||||
|
let peach_config = load_peach_config().unwrap();
|
||||||
let previous_dyndns_domain = peach_config.dyn_domain;
|
let previous_dyndns_domain = peach_config.dyn_domain;
|
||||||
Ok(dyndns_full_domain != previous_dyndns_domain)
|
dyndns_full_domain != previous_dyndns_domain
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonrpc_client!(pub struct PeachDynDnsClient {
|
jsonrpc_client!(pub struct PeachDynDnsClient {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
use crypto::{digest::Digest, sha3::Sha3};
|
||||||
use nanorand::{Rng, WyRand};
|
use nanorand::{Rng, WyRand};
|
||||||
use sha3::{Digest, Sha3_256};
|
|
||||||
|
|
||||||
use crate::{config_manager, error::PeachError, sbot_client};
|
use crate::{config_manager, error::PeachError, sbot_client};
|
||||||
|
|
||||||
@ -37,13 +37,9 @@ pub fn set_new_password(new_password: &str) -> Result<(), PeachError> {
|
|||||||
|
|
||||||
/// Creates a hash from a password string
|
/// Creates a hash from a password string
|
||||||
pub fn hash_password(password: &str) -> String {
|
pub fn hash_password(password: &str) -> String {
|
||||||
let mut hasher = Sha3_256::new();
|
let mut hasher = Sha3::sha3_256();
|
||||||
// write input message
|
hasher.input_str(password);
|
||||||
hasher.update(password);
|
hasher.result_str()
|
||||||
// read hash digest
|
|
||||||
let result = hasher.finalize();
|
|
||||||
// convert `u8` to `String`
|
|
||||||
result[0].to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a new temporary password for the admin user
|
/// Sets a new temporary password for the admin user
|
||||||
|
4
peach-menu/.cargo/config
Normal file
4
peach-menu/.cargo/config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "aarch64-linux-gnu-gcc"
|
||||||
|
objcopy = { path ="aarch64-linux-gnu-objcopy" }
|
||||||
|
strip = { path ="aarch64-linux-gnu-strip" }
|
4
peach-monitor/.cargo/config
Normal file
4
peach-monitor/.cargo/config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "aarch64-linux-gnu-gcc"
|
||||||
|
objcopy = { path ="aarch64-linux-gnu-objcopy" }
|
||||||
|
strip = { path ="aarch64-linux-gnu-strip" }
|
4
peach-network/.cargo/config
Normal file
4
peach-network/.cargo/config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "aarch64-linux-gnu-gcc"
|
||||||
|
objcopy = { path ="aarch64-linux-gnu-objcopy" }
|
||||||
|
strip = { path ="aarch64-linux-gnu-strip" }
|
4
peach-oled/.cargo/config
Normal file
4
peach-oled/.cargo/config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "aarch64-linux-gnu-gcc"
|
||||||
|
objcopy = { path ="aarch64-linux-gnu-objcopy" }
|
||||||
|
strip = { path ="aarch64-linux-gnu-strip" }
|
4
peach-probe/.cargo/config
Normal file
4
peach-probe/.cargo/config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "aarch64-linux-gnu-gcc"
|
||||||
|
objcopy = { path ="aarch64-linux-gnu-objcopy" }
|
||||||
|
strip = { path ="aarch64-linux-gnu-strip" }
|
4
peach-stats/.cargo/config
Normal file
4
peach-stats/.cargo/config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "aarch64-linux-gnu-gcc"
|
||||||
|
objcopy = { path ="aarch64-linux-gnu-objcopy" }
|
||||||
|
strip = { path ="aarch64-linux-gnu-strip" }
|
4
peach-web/.cargo/config
Normal file
4
peach-web/.cargo/config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "aarch64-linux-gnu-gcc"
|
||||||
|
objcopy = { path ="aarch64-linux-gnu-objcopy" }
|
||||||
|
strip = { path ="aarch64-linux-gnu-strip" }
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peach-web"
|
name = "peach-web"
|
||||||
version = "0.4.17"
|
version = "0.4.12"
|
||||||
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
|
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
|
||||||
edition = "2018"
|
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."
|
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."
|
||||||
@ -21,7 +21,6 @@ maintainer-scripts="debian"
|
|||||||
systemd-units = { unit-name = "peach-web" }
|
systemd-units = { unit-name = "peach-web" }
|
||||||
assets = [
|
assets = [
|
||||||
["target/release/peach-web", "/usr/bin/", "755"],
|
["target/release/peach-web", "/usr/bin/", "755"],
|
||||||
["Rocket.toml", "/usr/share/peach-web/Rocket.toml", "644"],
|
|
||||||
["templates/**/*", "/usr/share/peach-web/templates/", "644"],
|
["templates/**/*", "/usr/share/peach-web/templates/", "644"],
|
||||||
["static/*", "/usr/share/peach-web/static/", "644"],
|
["static/*", "/usr/share/peach-web/static/", "644"],
|
||||||
["static/css/*", "/usr/share/peach-web/static/css/", "644"],
|
["static/css/*", "/usr/share/peach-web/static/css/", "644"],
|
||||||
@ -41,8 +40,6 @@ log = "0.4"
|
|||||||
nest = "1.0.0"
|
nest = "1.0.0"
|
||||||
openssl = { version = "0.10", features = ["vendored"] }
|
openssl = { version = "0.10", features = ["vendored"] }
|
||||||
peach-lib = { path = "../peach-lib" }
|
peach-lib = { path = "../peach-lib" }
|
||||||
peach-network = { path = "../peach-network", features = ["serde_support"] }
|
|
||||||
peach-stats = { path = "../peach-stats", features = ["serde_support"] }
|
|
||||||
percent-encoding = "2.1.0"
|
percent-encoding = "2.1.0"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
rocket = { version = "0.5.0-rc.1", features = ["json", "secrets"] }
|
rocket = { version = "0.5.0-rc.1", features = ["json", "secrets"] }
|
||||||
|
@ -97,20 +97,6 @@ Remove configuration files (not removed with `apt-get remove`):
|
|||||||
|
|
||||||
`peach-web` is built on the Rocket webserver and Tera templating engine. It presents a web interface for interacting with the device. HTML is rendered server-side. Request handlers call JSON-RPC microservices and serve HTML and assets. A JSON API is exposed for remote calls and dynamic client-side content updates (via plain JavaScript following unobstructive design principles). Each Tera template is passed a context object. In the case of Rust, this object is a `struct` and must implement `Serialize`. The fields of the context object are available in the context of the template to be rendered.
|
`peach-web` is built on the Rocket webserver and Tera templating engine. It presents a web interface for interacting with the device. HTML is rendered server-side. Request handlers call JSON-RPC microservices and serve HTML and assets. A JSON API is exposed for remote calls and dynamic client-side content updates (via plain JavaScript following unobstructive design principles). Each Tera template is passed a context object. In the case of Rust, this object is a `struct` and must implement `Serialize`. The fields of the context object are available in the context of the template to be rendered.
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
### Licensing
|
### Licensing
|
||||||
|
|
||||||
AGPL-3.0
|
AGPL-3.0
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
[default]
|
|
||||||
secret_key = "VYVUDivXvu8g6llxeJd9F92pMfocml5xl/Jjv5Sk4yw="
|
|
||||||
|
|
||||||
[development]
|
[development]
|
||||||
template_dir = "templates/"
|
template_dir = "templates/"
|
||||||
disable_auth = true
|
disable_auth = true
|
||||||
|
@ -5,18 +5,54 @@ set -e
|
|||||||
adduser --quiet --system peach-web
|
adduser --quiet --system peach-web
|
||||||
usermod -g peach peach-web
|
usermod -g peach peach-web
|
||||||
|
|
||||||
|
# create secret passwords folder if it doesn't already exist
|
||||||
|
mkdir -p /var/lib/peachcloud/passwords
|
||||||
|
chown -R peach-web:peach /var/lib/peachcloud/passwords
|
||||||
|
chmod -R u+rwX,go+rX,go-w /var/lib/peachcloud/passwords
|
||||||
|
|
||||||
# create nginx config
|
# create nginx config
|
||||||
cat <<EOF > /etc/nginx/sites-enabled/default
|
cat <<EOF > /etc/nginx/sites-enabled/default
|
||||||
server {
|
server {
|
||||||
listen 80 default_server;
|
listen 80 default_server;
|
||||||
server_name peach.local www.peach.local;
|
server_name peach.local www.peach.local;
|
||||||
|
|
||||||
|
# nginx authentication
|
||||||
|
auth_basic "If you have forgotten your password visit: http://peach.local/send_password_reset/";
|
||||||
|
auth_basic_user_file /var/lib/peachcloud/passwords/htpasswd;
|
||||||
|
|
||||||
# remove trailing slash if found
|
# remove trailing slash if found
|
||||||
rewrite ^/(.*)/$ /$1 permanent;
|
rewrite ^/(.*)/$ /$1 permanent;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://127.0.0.1:3000;
|
proxy_pass http://127.0.0.1:3000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# public routes
|
||||||
|
location /send_password_reset {
|
||||||
|
auth_basic off;
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
}
|
||||||
|
location /reset_password {
|
||||||
|
auth_basic off;
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
}
|
||||||
|
location /public/ {
|
||||||
|
auth_basic off;
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
}
|
||||||
|
location /js/ {
|
||||||
|
auth_basic off;
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
}
|
||||||
|
location /css/ {
|
||||||
|
auth_basic off;
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
}
|
||||||
|
location /icons/ {
|
||||||
|
auth_basic off;
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ pub fn save_dns_configuration(dns_form: DnsForm) -> Result<(), PeachWebError> {
|
|||||||
if dns_form.enable_dyndns {
|
if dns_form.enable_dyndns {
|
||||||
let full_dynamic_domain = get_full_dynamic_domain(&dns_form.dynamic_domain);
|
let full_dynamic_domain = get_full_dynamic_domain(&dns_form.dynamic_domain);
|
||||||
// check if this is a new domain or if its already registered
|
// check if this is a new domain or if its already registered
|
||||||
let is_new_domain = check_is_new_dyndns_domain(&full_dynamic_domain)?;
|
let is_new_domain = check_is_new_dyndns_domain(&full_dynamic_domain);
|
||||||
if is_new_domain {
|
if is_new_domain {
|
||||||
match dyndns_client::register_domain(&full_dynamic_domain) {
|
match dyndns_client::register_domain(&full_dynamic_domain) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
@ -3,8 +3,8 @@ use rocket::{
|
|||||||
get, post,
|
get, post,
|
||||||
request::FlashMessage,
|
request::FlashMessage,
|
||||||
response::{Flash, Redirect},
|
response::{Flash, Redirect},
|
||||||
serde::json::Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::Template;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::{
|
use std::{
|
||||||
@ -12,16 +12,13 @@ use std::{
|
|||||||
process::{Command, Output},
|
process::{Command, Output},
|
||||||
};
|
};
|
||||||
|
|
||||||
use peach_lib::{
|
use peach_lib::config_manager::load_peach_config;
|
||||||
config_manager::load_peach_config, dyndns_client, network_client, oled_client, sbot_client,
|
use peach_lib::stats_client::{CpuStatPercentages, DiskUsage, LoadAverage, MemStat};
|
||||||
};
|
use peach_lib::{dyndns_client, network_client, oled_client, sbot_client, stats_client};
|
||||||
use peach_stats::{
|
|
||||||
stats,
|
|
||||||
stats::{CpuStatPercentages, DiskUsage, LoadAverage, MemStat},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::routes::authentication::Authenticated;
|
use crate::routes::authentication::Authenticated;
|
||||||
use crate::utils::build_json_response;
|
use crate::utils::build_json_response;
|
||||||
|
use rocket::serde::json::Value;
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /status
|
// HELPERS AND ROUTES FOR /status
|
||||||
|
|
||||||
@ -37,6 +34,7 @@ pub struct StatusContext {
|
|||||||
pub mem_stats: Option<MemStat>,
|
pub mem_stats: Option<MemStat>,
|
||||||
pub network_ping: String,
|
pub network_ping: String,
|
||||||
pub oled_ping: String,
|
pub oled_ping: String,
|
||||||
|
pub stats_ping: String,
|
||||||
pub dyndns_enabled: bool,
|
pub dyndns_enabled: bool,
|
||||||
pub dyndns_is_online: bool,
|
pub dyndns_is_online: bool,
|
||||||
pub config_is_valid: bool,
|
pub config_is_valid: bool,
|
||||||
@ -48,12 +46,9 @@ pub struct StatusContext {
|
|||||||
impl StatusContext {
|
impl StatusContext {
|
||||||
pub fn build() -> StatusContext {
|
pub fn build() -> StatusContext {
|
||||||
// convert result to Option<CpuStatPercentages>, discard any error
|
// convert result to Option<CpuStatPercentages>, discard any error
|
||||||
let cpu_stat_percent = stats::cpu_stats_percent().ok();
|
let cpu_stat_percent = stats_client::cpu_stats_percent().ok();
|
||||||
let load_average = stats::load_average().ok();
|
let load_average = stats_client::load_average().ok();
|
||||||
let mem_stats = stats::mem_stats().ok();
|
let mem_stats = stats_client::mem_stats().ok();
|
||||||
// TODO: add `wpa_supplicant_status` to peach_network to replace this ping call
|
|
||||||
// instead of: "is the network json-rpc server running?", we want to ask:
|
|
||||||
// "is the wpa_supplicant systemd service functioning correctly?"
|
|
||||||
let network_ping = match network_client::ping() {
|
let network_ping = match network_client::ping() {
|
||||||
Ok(_) => "ONLINE".to_string(),
|
Ok(_) => "ONLINE".to_string(),
|
||||||
Err(_) => "OFFLINE".to_string(),
|
Err(_) => "OFFLINE".to_string(),
|
||||||
@ -62,21 +57,22 @@ impl StatusContext {
|
|||||||
Ok(_) => "ONLINE".to_string(),
|
Ok(_) => "ONLINE".to_string(),
|
||||||
Err(_) => "OFFLINE".to_string(),
|
Err(_) => "OFFLINE".to_string(),
|
||||||
};
|
};
|
||||||
|
let stats_ping = match stats_client::ping() {
|
||||||
let uptime = match stats::uptime() {
|
Ok(_) => "ONLINE".to_string(),
|
||||||
Ok(secs) => {
|
Err(_) => "OFFLINE".to_string(),
|
||||||
let uptime_mins = secs / 60;
|
};
|
||||||
uptime_mins.to_string()
|
let uptime = match stats_client::uptime() {
|
||||||
}
|
Ok(mins) => mins,
|
||||||
Err(_) => "Unavailable".to_string(),
|
Err(_) => "Unavailable".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// parse the uptime string to a signed integer (for math)
|
|
||||||
let uptime_parsed = uptime.parse::<i32>().ok();
|
|
||||||
|
|
||||||
// serialize disk usage data into Vec<DiskUsage>
|
// serialize disk usage data into Vec<DiskUsage>
|
||||||
let disk_usage_stats = match stats::disk_usage() {
|
let disk_usage_stats = match stats_client::disk_usage() {
|
||||||
Ok(disks) => disks,
|
Ok(disks) => {
|
||||||
|
let partitions: Vec<DiskUsage> = serde_json::from_str(disks.as_str())
|
||||||
|
.expect("Failed to deserialize disk_usage response");
|
||||||
|
partitions
|
||||||
|
}
|
||||||
Err(_) => Vec::new(),
|
Err(_) => Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -88,6 +84,9 @@ impl StatusContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse the uptime string to a signed integer (for math)
|
||||||
|
let uptime_parsed = uptime.parse::<i32>().ok();
|
||||||
|
|
||||||
// dyndns_is_online & config_is_valid
|
// dyndns_is_online & config_is_valid
|
||||||
let dyndns_enabled: bool;
|
let dyndns_enabled: bool;
|
||||||
let dyndns_is_online: bool;
|
let dyndns_is_online: bool;
|
||||||
@ -140,6 +139,7 @@ impl StatusContext {
|
|||||||
mem_stats,
|
mem_stats,
|
||||||
network_ping,
|
network_ping,
|
||||||
oled_ping,
|
oled_ping,
|
||||||
|
stats_ping,
|
||||||
dyndns_enabled,
|
dyndns_enabled,
|
||||||
dyndns_is_online,
|
dyndns_is_online,
|
||||||
config_is_valid,
|
config_is_valid,
|
||||||
|
@ -2,77 +2,25 @@ use rocket::{get, request::FlashMessage};
|
|||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::Template;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use peach_network::{
|
use peach_lib::network_client;
|
||||||
network,
|
use peach_lib::stats_client::Traffic;
|
||||||
network::{Status, Traffic},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::routes::authentication::Authenticated;
|
use crate::routes::authentication::Authenticated;
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /status/network
|
// HELPERS AND ROUTES FOR /status/network
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct IfaceTraffic {
|
|
||||||
pub rx: u64,
|
|
||||||
pub rx_unit: Option<String>,
|
|
||||||
pub tx: u64,
|
|
||||||
pub tx_unit: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IfaceTraffic {
|
|
||||||
fn default() -> Self {
|
|
||||||
IfaceTraffic {
|
|
||||||
rx: 0,
|
|
||||||
rx_unit: None,
|
|
||||||
tx: 0,
|
|
||||||
tx_unit: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_traffic(traffic: Traffic) -> Option<IfaceTraffic> {
|
|
||||||
let mut t = IfaceTraffic::default();
|
|
||||||
// modify traffic values & assign measurement units
|
|
||||||
// based on received and transmitted values.
|
|
||||||
// if received > 999 MB, convert it to GB
|
|
||||||
if traffic.received > 1_047_527_424 {
|
|
||||||
t.rx = traffic.received / 1_073_741_824;
|
|
||||||
t.rx_unit = Some("GB".to_string());
|
|
||||||
} else if traffic.received > 0 {
|
|
||||||
// otherwise, convert it to MB
|
|
||||||
t.rx = (traffic.received / 1024) / 1024;
|
|
||||||
t.rx_unit = Some("MB".to_string());
|
|
||||||
} else {
|
|
||||||
t.rx = 0;
|
|
||||||
t.rx_unit = Some("MB".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if traffic.transmitted > 1_047_527_424 {
|
|
||||||
t.tx = traffic.transmitted / 1_073_741_824;
|
|
||||||
t.tx_unit = Some("GB".to_string());
|
|
||||||
} else if traffic.transmitted > 0 {
|
|
||||||
t.tx = (traffic.transmitted / 1024) / 1024;
|
|
||||||
t.tx_unit = Some("MB".to_string());
|
|
||||||
} else {
|
|
||||||
t.tx = 0;
|
|
||||||
t.tx_unit = Some("MB".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct NetworkContext {
|
pub struct NetworkContext {
|
||||||
pub ap_ip: String,
|
pub ap_ip: String,
|
||||||
pub ap_ssid: String,
|
pub ap_ssid: String,
|
||||||
pub ap_state: String,
|
pub ap_state: String,
|
||||||
pub ap_traffic: Option<IfaceTraffic>,
|
pub ap_traffic: Option<Traffic>,
|
||||||
pub wlan_ip: String,
|
pub wlan_ip: String,
|
||||||
pub wlan_rssi: Option<String>,
|
pub wlan_rssi: Option<String>,
|
||||||
pub wlan_ssid: String,
|
pub wlan_ssid: String,
|
||||||
pub wlan_state: String,
|
pub wlan_state: String,
|
||||||
pub wlan_status: Option<Status>,
|
pub wlan_status: String,
|
||||||
pub wlan_traffic: Option<IfaceTraffic>,
|
pub wlan_traffic: Option<Traffic>,
|
||||||
pub flash_name: Option<String>,
|
pub flash_name: Option<String>,
|
||||||
pub flash_msg: Option<String>,
|
pub flash_msg: Option<String>,
|
||||||
// page title for header in navbar
|
// page title for header in navbar
|
||||||
@ -83,47 +31,101 @@ pub struct NetworkContext {
|
|||||||
|
|
||||||
impl NetworkContext {
|
impl NetworkContext {
|
||||||
pub fn build() -> NetworkContext {
|
pub fn build() -> NetworkContext {
|
||||||
let ap_ip = match network::ip("ap0") {
|
let ap_ip = match network_client::ip("ap0") {
|
||||||
Ok(Some(ip)) => ip,
|
Ok(ip) => ip,
|
||||||
_ => "x.x.x.x".to_string(),
|
Err(_) => "x.x.x.x".to_string(),
|
||||||
};
|
};
|
||||||
let ap_ssid = match network::ssid("ap0") {
|
let ap_ssid = match network_client::ssid("ap0") {
|
||||||
Ok(Some(ssid)) => ssid,
|
Ok(ssid) => ssid,
|
||||||
_ => "Not currently activated".to_string(),
|
Err(_) => "Not currently activated".to_string(),
|
||||||
};
|
};
|
||||||
let ap_state = match network::state("ap0") {
|
let ap_state = match network_client::state("ap0") {
|
||||||
Ok(Some(state)) => state,
|
Ok(state) => state,
|
||||||
_ => "Interface unavailable".to_string(),
|
Err(_) => "Interface unavailable".to_string(),
|
||||||
};
|
};
|
||||||
let ap_traffic = match network::traffic("ap0") {
|
let ap_traffic = match network_client::traffic("ap0") {
|
||||||
// convert bytes to mb or gb and add appropriate units
|
Ok(traffic) => {
|
||||||
Ok(Some(traffic)) => convert_traffic(traffic),
|
let mut t = traffic;
|
||||||
_ => None,
|
// modify traffic values & assign measurement unit
|
||||||
|
// based on received and transmitted values
|
||||||
|
// if received > 999 MB, convert it to GB
|
||||||
|
if t.received > 1_047_527_424 {
|
||||||
|
t.received /= 1_073_741_824;
|
||||||
|
t.rx_unit = Some("GB".to_string());
|
||||||
|
} else if t.received > 0 {
|
||||||
|
// otherwise, convert it to MB
|
||||||
|
t.received = (t.received / 1024) / 1024;
|
||||||
|
t.rx_unit = Some("MB".to_string());
|
||||||
|
} else {
|
||||||
|
t.received = 0;
|
||||||
|
t.rx_unit = Some("MB".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.transmitted > 1_047_527_424 {
|
||||||
|
t.transmitted /= 1_073_741_824;
|
||||||
|
t.tx_unit = Some("GB".to_string());
|
||||||
|
} else if t.transmitted > 0 {
|
||||||
|
t.transmitted = (t.transmitted / 1024) / 1024;
|
||||||
|
t.tx_unit = Some("MB".to_string());
|
||||||
|
} else {
|
||||||
|
t.transmitted = 0;
|
||||||
|
t.tx_unit = Some("MB".to_string());
|
||||||
|
}
|
||||||
|
Some(t)
|
||||||
|
}
|
||||||
|
Err(_) => None,
|
||||||
};
|
};
|
||||||
let wlan_ip = match network::ip("wlan0") {
|
let wlan_ip = match network_client::ip("wlan0") {
|
||||||
Ok(Some(ip)) => ip,
|
Ok(ip) => ip,
|
||||||
_ => "x.x.x.x".to_string(),
|
Err(_) => "x.x.x.x".to_string(),
|
||||||
};
|
};
|
||||||
let wlan_rssi = match network::rssi_percent("wlan0") {
|
let wlan_rssi = match network_client::rssi_percent("wlan0") {
|
||||||
Ok(rssi) => rssi,
|
Ok(rssi) => Some(rssi),
|
||||||
_ => None,
|
Err(_) => None,
|
||||||
};
|
};
|
||||||
let wlan_ssid = match network::ssid("wlan0") {
|
let wlan_ssid = match network_client::ssid("wlan0") {
|
||||||
Ok(Some(ssid)) => ssid,
|
Ok(ssid) => ssid,
|
||||||
_ => "Not connected".to_string(),
|
Err(_) => "Not connected".to_string(),
|
||||||
};
|
};
|
||||||
let wlan_state = match network::state("wlan0") {
|
let wlan_state = match network_client::state("wlan0") {
|
||||||
Ok(Some(state)) => state,
|
Ok(state) => state,
|
||||||
_ => "Interface unavailable".to_string(),
|
Err(_) => "Interface unavailable".to_string(),
|
||||||
};
|
};
|
||||||
let wlan_status = match network::status("wlan0") {
|
let wlan_status = match network_client::status("wlan0") {
|
||||||
Ok(status) => status,
|
Ok(status) => status,
|
||||||
_ => None,
|
Err(_) => "Interface unavailable".to_string(),
|
||||||
};
|
};
|
||||||
let wlan_traffic = match network::traffic("wlan0") {
|
let wlan_traffic = match network_client::traffic("wlan0") {
|
||||||
// convert bytes to mb or gb and add appropriate units
|
Ok(traffic) => {
|
||||||
Ok(Some(traffic)) => convert_traffic(traffic),
|
let mut t = traffic;
|
||||||
_ => None,
|
// modify traffic values & assign measurement unit
|
||||||
|
// based on received and transmitted values
|
||||||
|
// if received > 999 MB, convert it to GB
|
||||||
|
if t.received > 1_047_527_424 {
|
||||||
|
t.received /= 1_073_741_824;
|
||||||
|
t.rx_unit = Some("GB".to_string());
|
||||||
|
} else if t.received > 0 {
|
||||||
|
// otherwise, convert it to MB
|
||||||
|
t.received = (t.received / 1024) / 1024;
|
||||||
|
t.rx_unit = Some("MB".to_string());
|
||||||
|
} else {
|
||||||
|
t.received = 0;
|
||||||
|
t.rx_unit = Some("MB".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.transmitted > 1_047_527_424 {
|
||||||
|
t.transmitted /= 1_073_741_824;
|
||||||
|
t.tx_unit = Some("GB".to_string());
|
||||||
|
} else if t.transmitted > 0 {
|
||||||
|
t.transmitted = (t.transmitted / 1024) / 1024;
|
||||||
|
t.tx_unit = Some("MB".to_string());
|
||||||
|
} else {
|
||||||
|
t.transmitted = 0;
|
||||||
|
t.tx_unit = Some("MB".to_string());
|
||||||
|
}
|
||||||
|
Some(t)
|
||||||
|
}
|
||||||
|
Err(_) => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
NetworkContext {
|
NetworkContext {
|
||||||
|
@ -42,11 +42,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- PEACH-STATS STATUS STACK -->
|
<!-- PEACH-STATS STATUS STACK -->
|
||||||
<div class="stack capsule success-border">
|
<div class="stack capsule{% if stats_ping == "ONLINE" %} success-border{% else %} warning-border{% endif %}">
|
||||||
<img id="statsIcon" class="icon icon-medium" alt="Stats" title="System statistics microservice status" src="/icons/chart.svg">
|
<img id="statsIcon" class="icon{% if stats_ping == "OFFLINE" %} icon-inactive{% endif %} icon-medium" alt="Stats" title="System statistics microservice status" src="/icons/chart.svg">
|
||||||
<div class="stack" style="padding-top: 0.5rem;">
|
<div class="stack" style="padding-top: 0.5rem;">
|
||||||
<label class="label-small font-near-black">Statistics</label>
|
<label class="label-small font-near-black">Statistics</label>
|
||||||
<label class="label-small font-near-black">AVAILABLE</label>
|
<label class="label-small font-near-black">{{ stats_ping }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{# Display status for dynsdns, config & sbot #}
|
{# Display status for dynsdns, config & sbot #}
|
||||||
|
Reference in New Issue
Block a user