resolve merge conflicts
This commit is contained in:
commit
8032b83c41
File diff suppressed because it is too large
Load Diff
|
@ -10,6 +10,7 @@ members = [
|
||||||
"peach-menu",
|
"peach-menu",
|
||||||
"peach-monitor",
|
"peach-monitor",
|
||||||
"peach-stats",
|
"peach-stats",
|
||||||
|
"peach-jsonrpc-server",
|
||||||
"peach-probe",
|
"peach-probe",
|
||||||
"peach-dyndns-updater"
|
"peach-dyndns-updater"
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "peach-jsonrpc-server"
|
||||||
|
authors = ["Andrew Reid <glyph@mycelial.technology>"]
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
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"
|
||||||
|
repository = "https://git.coopcloud.tech/PeachCloud/peach-workspace"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "AGPL-3.0-only"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
env_logger = "0.9"
|
||||||
|
jsonrpc-core = "18"
|
||||||
|
jsonrpc-http-server = "18"
|
||||||
|
log = "0.4"
|
||||||
|
miniserde = "0.1.15"
|
||||||
|
peach-stats = { path = "../peach-stats", features = ["miniserde_support"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
jsonrpc-test = "18"
|
|
@ -0,0 +1,72 @@
|
||||||
|
# peach-jsonrpc-server
|
||||||
|
|
||||||
|
A JSON-RPC server for the PeachCloud system which exposes an API over HTTP.
|
||||||
|
|
||||||
|
Currently includes peach-stats capability (system statistics).
|
||||||
|
|
||||||
|
## JSON-RPC API
|
||||||
|
|
||||||
|
| Method | Description | Returns |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `cpu_stats` | CPU statistics | `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` |
|
||||||
|
| `load_average` | Load average statistics | `one`, `five`, `fifteen` |
|
||||||
|
| `mem_stats` | Memory statistics | `total`, `free`, `used` |
|
||||||
|
| `ping` | Microservice status | `success` if running |
|
||||||
|
| `uptime` | System uptime | `secs` |
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
The JSON-RPC HTTP server is currently hardcoded to run on "127.0.0.1:5110". Address and port configuration settings will later be exposed via CLI arguments and possibly an environment variable.
|
||||||
|
|
||||||
|
Logging is made available with `env_logger`:
|
||||||
|
|
||||||
|
`export RUST_LOG=info`
|
||||||
|
|
||||||
|
Other logging levels include `debug`, `warn` and `error`.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Clone the peach-workspace repo:
|
||||||
|
|
||||||
|
`git clone https://git.coopcloud.tech/PeachCloud/peach-workspace`
|
||||||
|
|
||||||
|
Move into the repo peaach-jsonrpc-server directory and compile a release build:
|
||||||
|
|
||||||
|
`cd peach-jsonrpc-server`
|
||||||
|
`cargo build --release`
|
||||||
|
|
||||||
|
Run the binary:
|
||||||
|
|
||||||
|
`./peach-workspace/target/release/peach-jsonrpc-server`
|
||||||
|
|
||||||
|
## Debian Packaging
|
||||||
|
|
||||||
|
TODO.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
**Get CPU Statistics**
|
||||||
|
|
||||||
|
With microservice running, open a second terminal window and use `curl` to call server methods:
|
||||||
|
|
||||||
|
`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "cpu_stats", "id":1 }' 127.0.0.1:5110`
|
||||||
|
|
||||||
|
Server responds with:
|
||||||
|
|
||||||
|
`{"jsonrpc":"2.0","result":"{\"user\":4661083,\"system\":1240371,\"idle\":326838290,\"nice\":0}","id":1}`
|
||||||
|
|
||||||
|
**Get System Uptime**
|
||||||
|
|
||||||
|
With microservice running, open a second terminal window and use `curl` to call server methods:
|
||||||
|
|
||||||
|
`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "uptime", "id":1 }' 127.0.0.1:5110`
|
||||||
|
|
||||||
|
Server responds with:
|
||||||
|
|
||||||
|
`{"jsonrpc":"2.0","result":"{\"secs\":840968}","id":1}`
|
||||||
|
|
||||||
|
### Licensing
|
||||||
|
|
||||||
|
AGPL-3.0
|
|
@ -0,0 +1,46 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use jsonrpc_core::{Error as JsonRpcError, ErrorCode};
|
||||||
|
use peach_stats::StatsError;
|
||||||
|
|
||||||
|
/// Custom error type encapsulating all possible errors for a JSON-RPC server
|
||||||
|
/// and associated methods.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum JsonRpcServerError {
|
||||||
|
/// An error returned from the `peach-stats` library.
|
||||||
|
Stats(StatsError),
|
||||||
|
/// An expected JSON-RPC method parameter was not provided.
|
||||||
|
MissingParameter(JsonRpcError),
|
||||||
|
/// Failed to parse a provided JSON-RPC method parameter.
|
||||||
|
ParseParameter(JsonRpcError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for JsonRpcServerError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
JsonRpcServerError::ParseParameter(ref source) => {
|
||||||
|
write!(f, "Failed to parse parameter: {}", source)
|
||||||
|
}
|
||||||
|
JsonRpcServerError::MissingParameter(ref source) => {
|
||||||
|
write!(f, "Missing expected parameter: {}", source)
|
||||||
|
}
|
||||||
|
JsonRpcServerError::Stats(ref source) => {
|
||||||
|
write!(f, "{}", source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<JsonRpcServerError> for JsonRpcError {
|
||||||
|
fn from(err: JsonRpcServerError) -> Self {
|
||||||
|
match &err {
|
||||||
|
JsonRpcServerError::Stats(source) => JsonRpcError {
|
||||||
|
code: ErrorCode::ServerError(-32001),
|
||||||
|
message: format!("{}", source),
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
JsonRpcServerError::MissingParameter(source) => source.clone(),
|
||||||
|
JsonRpcServerError::ParseParameter(source) => source.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
//! # peach-jsonrpc-server
|
||||||
|
//!
|
||||||
|
//! A JSON-RPC server which exposes an API over HTTP.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
use jsonrpc_core::{IoHandler, Value};
|
||||||
|
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
|
||||||
|
use log::info;
|
||||||
|
use miniserde::json;
|
||||||
|
use peach_stats::stats;
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
use crate::error::JsonRpcServerError;
|
||||||
|
|
||||||
|
/// Create JSON-RPC I/O handler, add RPC methods and launch HTTP server.
|
||||||
|
pub fn run() -> Result<(), JsonRpcServerError> {
|
||||||
|
info!("Starting up.");
|
||||||
|
|
||||||
|
info!("Creating JSON-RPC I/O handler.");
|
||||||
|
let mut io = IoHandler::default();
|
||||||
|
|
||||||
|
io.add_sync_method("ping", |_| Ok(Value::String("success".to_string())));
|
||||||
|
|
||||||
|
// TODO: add blocks of methods according to provided flags
|
||||||
|
|
||||||
|
/* PEACH-STATS RPC METHODS */
|
||||||
|
|
||||||
|
io.add_sync_method("cpu_stats", move |_| {
|
||||||
|
info!("Fetching CPU statistics.");
|
||||||
|
let cpu = stats::cpu_stats().map_err(JsonRpcServerError::Stats)?;
|
||||||
|
let json_cpu = json::to_string(&cpu);
|
||||||
|
|
||||||
|
Ok(Value::String(json_cpu))
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_sync_method("cpu_stats_percent", move |_| {
|
||||||
|
info!("Fetching CPU statistics as percentages.");
|
||||||
|
let cpu = stats::cpu_stats_percent().map_err(JsonRpcServerError::Stats)?;
|
||||||
|
let json_cpu = json::to_string(&cpu);
|
||||||
|
|
||||||
|
Ok(Value::String(json_cpu))
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_sync_method("disk_usage", move |_| {
|
||||||
|
info!("Fetching disk usage statistics.");
|
||||||
|
let disks = stats::disk_usage().map_err(JsonRpcServerError::Stats)?;
|
||||||
|
let json_disks = json::to_string(&disks);
|
||||||
|
|
||||||
|
Ok(Value::String(json_disks))
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_sync_method("load_average", move |_| {
|
||||||
|
info!("Fetching system load average statistics.");
|
||||||
|
let avg = stats::load_average().map_err(JsonRpcServerError::Stats)?;
|
||||||
|
let json_avg = json::to_string(&avg);
|
||||||
|
|
||||||
|
Ok(Value::String(json_avg))
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_sync_method("mem_stats", move |_| {
|
||||||
|
info!("Fetching current memory statistics.");
|
||||||
|
let mem = stats::mem_stats().map_err(JsonRpcServerError::Stats)?;
|
||||||
|
let json_mem = json::to_string(&mem);
|
||||||
|
|
||||||
|
Ok(Value::String(json_mem))
|
||||||
|
});
|
||||||
|
|
||||||
|
io.add_sync_method("uptime", move |_| {
|
||||||
|
info!("Fetching system uptime.");
|
||||||
|
let uptime = stats::uptime().map_err(JsonRpcServerError::Stats)?;
|
||||||
|
let json_uptime = json::to_string(&uptime);
|
||||||
|
|
||||||
|
Ok(Value::String(json_uptime))
|
||||||
|
});
|
||||||
|
|
||||||
|
let http_server =
|
||||||
|
env::var("PEACH_JSONRPC_SERVER").unwrap_or_else(|_| "127.0.0.1:5110".to_string());
|
||||||
|
|
||||||
|
info!("Starting JSON-RPC server on {}.", http_server);
|
||||||
|
let server = ServerBuilder::new(io)
|
||||||
|
.cors(DomainsValidation::AllowOnly(vec![
|
||||||
|
AccessControlAllowOrigin::Null,
|
||||||
|
]))
|
||||||
|
.start_http(
|
||||||
|
&http_server
|
||||||
|
.parse()
|
||||||
|
.expect("Invalid HTTP address and port combination"),
|
||||||
|
)
|
||||||
|
.expect("Unable to start RPC server");
|
||||||
|
|
||||||
|
server.wait();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use jsonrpc_core::{Error as JsonRpcError, ErrorCode};
|
||||||
|
use jsonrpc_test as test_rpc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_success() {
|
||||||
|
let rpc = {
|
||||||
|
let mut io = IoHandler::new();
|
||||||
|
io.add_sync_method("rpc_success_response", |_| {
|
||||||
|
Ok(Value::String("success".into()))
|
||||||
|
});
|
||||||
|
test_rpc::Rpc::from(io)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(rpc.request("rpc_success_response", &()), r#""success""#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_parse_error() {
|
||||||
|
let rpc = {
|
||||||
|
let mut io = IoHandler::new();
|
||||||
|
io.add_sync_method("rpc_parse_error", |_| {
|
||||||
|
let e = JsonRpcError {
|
||||||
|
code: ErrorCode::ParseError,
|
||||||
|
message: String::from("Parse error"),
|
||||||
|
data: None,
|
||||||
|
};
|
||||||
|
Err(JsonRpcError::from(JsonRpcServerError::MissingParameter(e)))
|
||||||
|
});
|
||||||
|
test_rpc::Rpc::from(io)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
rpc.request("rpc_parse_error", &()),
|
||||||
|
r#"{
|
||||||
|
"code": -32700,
|
||||||
|
"message": "Parse error"
|
||||||
|
}"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
//! # peach-jsonrpc-server
|
||||||
|
//!
|
||||||
|
//! A JSON-RPC server which exposes an over HTTP.
|
||||||
|
//!
|
||||||
|
//! Currently includes peach-stats capability (system statistics).
|
||||||
|
//!
|
||||||
|
//! ## API
|
||||||
|
//!
|
||||||
|
//! | Method | Description | Returns |
|
||||||
|
//! | --- | --- | --- |
|
||||||
|
//! | `cpu_stats` | CPU statistics | `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` |
|
||||||
|
//! | `load_average` | Load average statistics | `one`, `five`, `fifteen` |
|
||||||
|
//! | `mem_stats` | Memory statistics | `total`, `free`, `used` |
|
||||||
|
//! | `ping` | Microservice status | `success` if running |
|
||||||
|
//! | `uptime` | System uptime | `secs` |
|
||||||
|
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// initalize the logger
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
// handle errors returned from `run`
|
||||||
|
if let Err(e) = peach_jsonrpc_server::run() {
|
||||||
|
error!("Application error: {}", e);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,6 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
env_logger = "0.6"
|
|
||||||
fslock="0.1.6"
|
fslock="0.1.6"
|
||||||
jsonrpc-client-core = "0.5"
|
jsonrpc-client-core = "0.5"
|
||||||
jsonrpc-client-http = "0.5"
|
jsonrpc-client-http = "0.5"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "peach-oled"
|
name = "peach-oled"
|
||||||
version = "0.1.3"
|
version = "0.1.4"
|
||||||
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
|
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Write and draw to OLED display using JSON-RPC over HTTP."
|
description = "Write and draw to OLED display using JSON-RPC over HTTP."
|
||||||
|
@ -27,18 +27,16 @@ travis-ci = { repository = "peachcloud/peach-oled", branch = "master" }
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
jsonrpc-core = "11.0.0"
|
|
||||||
jsonrpc-http-server = "11.0.0"
|
|
||||||
linux-embedded-hal = "0.2.2"
|
|
||||||
embedded-graphics = "0.4.7"
|
embedded-graphics = "0.4.7"
|
||||||
tinybmp = "0.1.0"
|
env_logger = "0.9"
|
||||||
ssd1306 = "0.2.6"
|
jsonrpc-core = "18"
|
||||||
serde = { version = "1.0.87", features = ["derive"] }
|
jsonrpc-http-server = "18"
|
||||||
serde_json = "1.0.39"
|
linux-embedded-hal = "0.2.2"
|
||||||
log = "0.4.0"
|
log = "0.4"
|
||||||
env_logger = "0.6.1"
|
serde = { version = "1", features = ["derive"] }
|
||||||
snafu = "0.4.1"
|
|
||||||
nix="0.11"
|
nix="0.11"
|
||||||
|
ssd1306 = "0.2.6"
|
||||||
|
tinybmp = "0.1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
jsonrpc-test = "11.0.0"
|
jsonrpc-test = "18"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# peach-oled
|
# peach-oled
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.com/peachcloud/peach-oled.svg?branch=master)](https://travis-ci.com/peachcloud/peach-oled) ![Generic badge](https://img.shields.io/badge/version-0.1.3-<COLOR>.svg)
|
[![Build Status](https://travis-ci.com/peachcloud/peach-oled.svg?branch=master)](https://travis-ci.com/peachcloud/peach-oled) ![Generic badge](https://img.shields.io/badge/version-0.1.4-<COLOR>.svg)
|
||||||
|
|
||||||
OLED microservice module for PeachCloud. Write to a 128x64 OLED display with SDD1306 driver (I2C) using [JSON-RPC](https://www.jsonrpc.org/specification) over http.
|
OLED microservice module for PeachCloud. Write to a 128x64 OLED display with SDD1306 driver (I2C) using [JSON-RPC](https://www.jsonrpc.org/specification) over http.
|
||||||
|
|
||||||
|
|
|
@ -1,44 +1,68 @@
|
||||||
use std::error;
|
use std::{error, fmt};
|
||||||
|
|
||||||
use jsonrpc_core::{types::error::Error, ErrorCode};
|
use jsonrpc_core::types::error::Error as JsonRpcError;
|
||||||
use linux_embedded_hal as hal;
|
use jsonrpc_core::ErrorCode;
|
||||||
use snafu::Snafu;
|
use linux_embedded_hal::i2cdev::linux::LinuxI2CError;
|
||||||
|
|
||||||
pub type BoxError = Box<dyn error::Error>;
|
#[derive(Debug)]
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
|
||||||
#[snafu(visibility(pub(crate)))]
|
|
||||||
pub enum OledError {
|
pub enum OledError {
|
||||||
#[snafu(display("Failed to create interface for I2C device: {}", source))]
|
|
||||||
I2CError {
|
I2CError {
|
||||||
source: hal::i2cdev::linux::LinuxI2CError,
|
source: LinuxI2CError,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[snafu(display("Coordinate {} out of range {}: {}", coord, range, value))]
|
|
||||||
InvalidCoordinate {
|
InvalidCoordinate {
|
||||||
coord: String,
|
coord: String,
|
||||||
range: String,
|
range: String,
|
||||||
value: i32,
|
value: i32,
|
||||||
},
|
},
|
||||||
|
InvalidFontSize {
|
||||||
// TODO: implement for validate() in src/lib.rs
|
font: String,
|
||||||
#[snafu(display("Font size invalid: {}", font))]
|
},
|
||||||
InvalidFontSize { font: String },
|
InvalidString {
|
||||||
|
len: usize,
|
||||||
#[snafu(display("String length out of range 0-21: {}", len))]
|
},
|
||||||
InvalidString { len: usize },
|
MissingParameter {
|
||||||
|
source: JsonRpcError,
|
||||||
#[snafu(display("Missing expected parameter: {}", e))]
|
},
|
||||||
MissingParameter { e: Error },
|
ParseError {
|
||||||
|
source: JsonRpcError,
|
||||||
#[snafu(display("Failed to parse parameter: {}", e))]
|
},
|
||||||
ParseError { e: Error },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OledError> for Error {
|
impl error::Error for OledError {}
|
||||||
|
|
||||||
|
impl fmt::Display for OledError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
OledError::ParseError { ref source } => {
|
||||||
|
write!(f, "Failed to parse parameter: {}", source)
|
||||||
|
}
|
||||||
|
OledError::MissingParameter { ref source } => {
|
||||||
|
write!(f, "Missing expected parameter: {}", source)
|
||||||
|
}
|
||||||
|
OledError::InvalidString { len } => {
|
||||||
|
write!(f, "String length out of range 0-21: {}", len)
|
||||||
|
}
|
||||||
|
OledError::InvalidFontSize { ref font } => {
|
||||||
|
write!(f, "Invalid font size: {}", font)
|
||||||
|
}
|
||||||
|
OledError::InvalidCoordinate {
|
||||||
|
ref coord,
|
||||||
|
ref range,
|
||||||
|
value,
|
||||||
|
} => {
|
||||||
|
write!(f, "Coordinate {} out of range {}: {}", coord, range, value)
|
||||||
|
}
|
||||||
|
OledError::I2CError { ref source } => {
|
||||||
|
write!(f, "Failed to create interface for I2C device: {}", source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OledError> for JsonRpcError {
|
||||||
fn from(err: OledError) -> Self {
|
fn from(err: OledError) -> Self {
|
||||||
match &err {
|
match &err {
|
||||||
OledError::I2CError { source } => Error {
|
OledError::I2CError { source } => JsonRpcError {
|
||||||
code: ErrorCode::ServerError(-32000),
|
code: ErrorCode::ServerError(-32000),
|
||||||
message: format!("Failed to create interface for I2C device: {}", source),
|
message: format!("Failed to create interface for I2C device: {}", source),
|
||||||
data: None,
|
data: None,
|
||||||
|
@ -47,7 +71,7 @@ impl From<OledError> for Error {
|
||||||
coord,
|
coord,
|
||||||
value,
|
value,
|
||||||
range,
|
range,
|
||||||
} => Error {
|
} => JsonRpcError {
|
||||||
code: ErrorCode::ServerError(-32001),
|
code: ErrorCode::ServerError(-32001),
|
||||||
message: format!(
|
message: format!(
|
||||||
"Validation error: coordinate {} out of range {}: {}",
|
"Validation error: coordinate {} out of range {}: {}",
|
||||||
|
@ -55,18 +79,18 @@ impl From<OledError> for Error {
|
||||||
),
|
),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
OledError::InvalidFontSize { font } => Error {
|
OledError::InvalidFontSize { font } => JsonRpcError {
|
||||||
code: ErrorCode::ServerError(-32002),
|
code: ErrorCode::ServerError(-32002),
|
||||||
message: format!("Validation error: {} is not an accepted font size. Use 6x8, 6x12, 8x16 or 12x16 instead", font),
|
message: format!("Validation error: {} is not an accepted font size. Use 6x8, 6x12, 8x16 or 12x16 instead", font),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
OledError::InvalidString { len } => Error {
|
OledError::InvalidString { len } => JsonRpcError {
|
||||||
code: ErrorCode::ServerError(-32003),
|
code: ErrorCode::ServerError(-32003),
|
||||||
message: format!("Validation error: string length {} out of range 0-21", len),
|
message: format!("Validation error: string length {} out of range 0-21", len),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
OledError::MissingParameter { e } => e.clone(),
|
OledError::MissingParameter { source } => source.clone(),
|
||||||
OledError::ParseError { e } => e.clone(),
|
OledError::ParseError { source } => source.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,23 +6,23 @@ use std::{
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use embedded_graphics::coord::Coord;
|
use embedded_graphics::{
|
||||||
use embedded_graphics::fonts::{Font12x16, Font6x12, Font6x8, Font8x16};
|
coord::Coord,
|
||||||
use embedded_graphics::image::Image1BPP;
|
fonts::{Font12x16, Font6x12, Font6x8, Font8x16},
|
||||||
use embedded_graphics::prelude::*;
|
image::Image1BPP,
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
use hal::I2cdev;
|
use hal::I2cdev;
|
||||||
use jsonrpc_core::{types::error::Error, IoHandler, Params, Value};
|
use jsonrpc_core::{types::error::Error, IoHandler, Params, Value};
|
||||||
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
|
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
|
||||||
use linux_embedded_hal as hal;
|
use linux_embedded_hal as hal;
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use snafu::{ensure, ResultExt};
|
use ssd1306::{prelude::*, Builder};
|
||||||
use ssd1306::prelude::*;
|
|
||||||
use ssd1306::Builder;
|
|
||||||
|
|
||||||
use crate::error::{BoxError, I2CError, InvalidCoordinate, InvalidString, OledError};
|
use crate::error::OledError;
|
||||||
|
|
||||||
//define the Graphic struct for receiving draw commands
|
// define the Graphic struct for receiving draw commands
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Graphic {
|
pub struct Graphic {
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
|
@ -32,7 +32,7 @@ pub struct Graphic {
|
||||||
y_coord: i32,
|
y_coord: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
//define the Msg struct for receiving write commands
|
// define the Msg struct for receiving write commands
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Msg {
|
pub struct Msg {
|
||||||
x_coord: i32,
|
x_coord: i32,
|
||||||
|
@ -41,86 +41,61 @@ pub struct Msg {
|
||||||
font_size: String,
|
font_size: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
//definte the On struct for receiving power on/off commands
|
// definte the On struct for receiving power on/off commands
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct On {
|
pub struct On {
|
||||||
on: bool,
|
on: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(m: &Msg) -> Result<(), OledError> {
|
fn validate(msg: &Msg) -> Result<(), OledError> {
|
||||||
ensure!(
|
if msg.string.len() > 21 {
|
||||||
m.string.len() <= 21,
|
Err(OledError::InvalidString {
|
||||||
InvalidString {
|
len: msg.string.len(),
|
||||||
len: m.string.len()
|
})
|
||||||
}
|
} else if msg.x_coord < 0 || msg.x_coord > 128 {
|
||||||
);
|
Err(OledError::InvalidCoordinate {
|
||||||
|
|
||||||
ensure!(
|
|
||||||
m.x_coord >= 0,
|
|
||||||
InvalidCoordinate {
|
|
||||||
coord: "x".to_string(),
|
coord: "x".to_string(),
|
||||||
range: "0-128".to_string(),
|
range: "0-128".to_string(),
|
||||||
value: m.x_coord,
|
value: msg.x_coord,
|
||||||
}
|
})
|
||||||
);
|
} else if msg.y_coord < 0 || msg.y_coord > 147 {
|
||||||
|
Err(OledError::InvalidCoordinate {
|
||||||
ensure!(
|
|
||||||
m.x_coord < 129,
|
|
||||||
InvalidCoordinate {
|
|
||||||
coord: "x".to_string(),
|
|
||||||
range: "0-128".to_string(),
|
|
||||||
value: m.x_coord,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
ensure!(
|
|
||||||
m.y_coord >= 0,
|
|
||||||
InvalidCoordinate {
|
|
||||||
coord: "y".to_string(),
|
coord: "y".to_string(),
|
||||||
range: "0-47".to_string(),
|
range: "0-47".to_string(),
|
||||||
value: m.y_coord,
|
value: msg.y_coord,
|
||||||
}
|
})
|
||||||
);
|
} else {
|
||||||
|
Ok(())
|
||||||
ensure!(
|
}
|
||||||
m.y_coord < 148,
|
|
||||||
InvalidCoordinate {
|
|
||||||
coord: "y".to_string(),
|
|
||||||
range: "0-47".to_string(),
|
|
||||||
value: m.y_coord,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() -> Result<(), BoxError> {
|
pub fn run() -> Result<(), OledError> {
|
||||||
info!("Starting up.");
|
info!("Starting up.");
|
||||||
|
|
||||||
debug!("Creating interface for I2C device.");
|
debug!("Creating interface for I2C device.");
|
||||||
let i2c = I2cdev::new("/dev/i2c-1").context(I2CError)?;
|
let i2c = I2cdev::new("/dev/i2c-1").map_err(|source| OledError::I2CError { source })?;
|
||||||
|
|
||||||
let mut disp: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
|
let mut display: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
|
||||||
|
|
||||||
info!("Initializing the display.");
|
info!("Initializing the display.");
|
||||||
disp.init().unwrap_or_else(|_| {
|
display.init().unwrap_or_else(|_| {
|
||||||
error!("Problem initializing the OLED display.");
|
error!("Problem initializing the OLED display.");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
debug!("Flushing the display.");
|
debug!("Flushing the display.");
|
||||||
disp.flush().unwrap_or_else(|_| {
|
display.flush().unwrap_or_else(|_| {
|
||||||
error!("Problem flushing the OLED display.");
|
error!("Problem flushing the OLED display.");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
let oled = Arc::new(Mutex::new(disp));
|
let oled = Arc::new(Mutex::new(display));
|
||||||
let oled_clone = Arc::clone(&oled);
|
let oled_clone = Arc::clone(&oled);
|
||||||
|
|
||||||
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_method("clear", move |_| {
|
io.add_sync_method("clear", move |_| {
|
||||||
let mut oled = oled_clone.lock().unwrap();
|
let mut oled = oled_clone.lock().unwrap();
|
||||||
info!("Clearing the display.");
|
info!("Clearing the display.");
|
||||||
oled.clear();
|
oled.clear();
|
||||||
|
@ -134,21 +109,20 @@ pub fn run() -> Result<(), BoxError> {
|
||||||
|
|
||||||
let oled_clone = Arc::clone(&oled);
|
let oled_clone = Arc::clone(&oled);
|
||||||
|
|
||||||
io.add_method("draw", move |params: Params| {
|
io.add_sync_method("draw", move |params: Params| {
|
||||||
let g: Result<Graphic, Error> = params.parse();
|
let graphic: Graphic = params.parse()?;
|
||||||
let g: Graphic = g?;
|
|
||||||
// TODO: add simple byte validation function
|
// TODO: add simple byte validation function
|
||||||
let mut oled = oled_clone.lock().unwrap();
|
let mut oled = oled_clone.lock().unwrap();
|
||||||
info!("Drawing image to the display.");
|
info!("Drawing image to the display.");
|
||||||
let im =
|
let image = Image1BPP::new(&graphic.bytes, graphic.width, graphic.height)
|
||||||
Image1BPP::new(&g.bytes, g.width, g.height).translate(Coord::new(g.x_coord, g.y_coord));
|
.translate(Coord::new(graphic.x_coord, graphic.y_coord));
|
||||||
oled.draw(im.into_iter());
|
oled.draw(image.into_iter());
|
||||||
Ok(Value::String("success".into()))
|
Ok(Value::String("success".into()))
|
||||||
});
|
});
|
||||||
|
|
||||||
let oled_clone = Arc::clone(&oled);
|
let oled_clone = Arc::clone(&oled);
|
||||||
|
|
||||||
io.add_method("flush", move |_| {
|
io.add_sync_method("flush", move |_| {
|
||||||
let mut oled = oled_clone.lock().unwrap();
|
let mut oled = oled_clone.lock().unwrap();
|
||||||
info!("Flushing the display.");
|
info!("Flushing the display.");
|
||||||
oled.flush().unwrap_or_else(|_| {
|
oled.flush().unwrap_or_else(|_| {
|
||||||
|
@ -160,9 +134,9 @@ pub fn run() -> Result<(), BoxError> {
|
||||||
|
|
||||||
let oled_clone = Arc::clone(&oled);
|
let oled_clone = Arc::clone(&oled);
|
||||||
|
|
||||||
io.add_method("ping", |_| Ok(Value::String("success".to_string())));
|
io.add_sync_method("ping", |_| Ok(Value::String("success".to_string())));
|
||||||
|
|
||||||
io.add_method("power", move |params: Params| {
|
io.add_sync_method("power", move |params: Params| {
|
||||||
let o: Result<On, Error> = params.parse();
|
let o: Result<On, Error> = params.parse();
|
||||||
let o: On = o?;
|
let o: On = o?;
|
||||||
let mut oled = oled_clone.lock().unwrap();
|
let mut oled = oled_clone.lock().unwrap();
|
||||||
|
@ -180,37 +154,36 @@ pub fn run() -> Result<(), BoxError> {
|
||||||
|
|
||||||
let oled_clone = Arc::clone(&oled);
|
let oled_clone = Arc::clone(&oled);
|
||||||
|
|
||||||
io.add_method("write", move |params: Params| {
|
io.add_sync_method("write", move |params: Params| {
|
||||||
info!("Received a 'write' request.");
|
info!("Received a 'write' request.");
|
||||||
let m: Result<Msg, Error> = params.parse();
|
let msg = params.parse()?;
|
||||||
let m: Msg = m?;
|
validate(&msg)?;
|
||||||
validate(&m)?;
|
|
||||||
|
|
||||||
let mut oled = oled_clone.lock().unwrap();
|
let mut oled = oled_clone.lock().unwrap();
|
||||||
|
|
||||||
info!("Writing to the display.");
|
info!("Writing to the display.");
|
||||||
if m.font_size == "6x8" {
|
if msg.font_size == "6x8" {
|
||||||
oled.draw(
|
oled.draw(
|
||||||
Font6x8::render_str(&m.string)
|
Font6x8::render_str(&msg.string)
|
||||||
.translate(Coord::new(m.x_coord, m.y_coord))
|
.translate(Coord::new(msg.x_coord, msg.y_coord))
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
} else if m.font_size == "6x12" {
|
} else if msg.font_size == "6x12" {
|
||||||
oled.draw(
|
oled.draw(
|
||||||
Font6x12::render_str(&m.string)
|
Font6x12::render_str(&msg.string)
|
||||||
.translate(Coord::new(m.x_coord, m.y_coord))
|
.translate(Coord::new(msg.x_coord, msg.y_coord))
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
} else if m.font_size == "8x16" {
|
} else if msg.font_size == "8x16" {
|
||||||
oled.draw(
|
oled.draw(
|
||||||
Font8x16::render_str(&m.string)
|
Font8x16::render_str(&msg.string)
|
||||||
.translate(Coord::new(m.x_coord, m.y_coord))
|
.translate(Coord::new(msg.x_coord, msg.y_coord))
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
} else if m.font_size == "12x16" {
|
} else if msg.font_size == "12x16" {
|
||||||
oled.draw(
|
oled.draw(
|
||||||
Font12x16::render_str(&m.string)
|
Font12x16::render_str(&msg.string)
|
||||||
.translate(Coord::new(m.x_coord, m.y_coord))
|
.translate(Coord::new(msg.x_coord, msg.y_coord))
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -255,7 +228,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_method("rpc_success_response", |_| {
|
io.add_sync_method("rpc_success_response", |_| {
|
||||||
Ok(Value::String("success".into()))
|
Ok(Value::String("success".into()))
|
||||||
});
|
});
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
|
@ -269,7 +242,7 @@ mod tests {
|
||||||
fn rpc_internal_error() {
|
fn rpc_internal_error() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_method("rpc_internal_error", |_| Err(Error::internal_error()));
|
io.add_sync_method("rpc_internal_error", |_| Err(Error::internal_error()));
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -287,7 +260,7 @@ mod tests {
|
||||||
fn rpc_i2c_io_error() {
|
fn rpc_i2c_io_error() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_method("rpc_i2c_io_error", |_| {
|
io.add_sync_method("rpc_i2c_io_error", |_| {
|
||||||
let io_err = IoError::new(ErrorKind::PermissionDenied, "oh no!");
|
let io_err = IoError::new(ErrorKind::PermissionDenied, "oh no!");
|
||||||
let source = LinuxI2CError::Io(io_err);
|
let source = LinuxI2CError::Io(io_err);
|
||||||
Err(Error::from(OledError::I2CError { source }))
|
Err(Error::from(OledError::I2CError { source }))
|
||||||
|
@ -310,7 +283,7 @@ mod tests {
|
||||||
fn rpc_i2c_nix_error() {
|
fn rpc_i2c_nix_error() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_method("rpc_i2c_nix_error", |_| {
|
io.add_sync_method("rpc_i2c_nix_error", |_| {
|
||||||
let nix_err = NixError::InvalidPath;
|
let nix_err = NixError::InvalidPath;
|
||||||
let source = LinuxI2CError::Nix(nix_err);
|
let source = LinuxI2CError::Nix(nix_err);
|
||||||
Err(Error::from(OledError::I2CError { source }))
|
Err(Error::from(OledError::I2CError { source }))
|
||||||
|
@ -326,14 +299,14 @@ mod tests {
|
||||||
}"#
|
}"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// test to ensure correct InvalidCoordinate error response
|
// test to ensure correct InvalidCoordinate error response
|
||||||
#[test]
|
#[test]
|
||||||
fn rpc_invalid_coord() {
|
fn rpc_invalid_coord() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_method("rpc_invalid_coord", |_| {
|
io.add_sync_method("rpc_invalid_coord", |_| {
|
||||||
Err(Error::from(OledError::InvalidCoordinate {
|
Err(Error::from(OledError::InvalidCoordinate {
|
||||||
coord: "x".to_string(),
|
coord: "x".to_string(),
|
||||||
range: "0-128".to_string(),
|
range: "0-128".to_string(),
|
||||||
|
@ -357,7 +330,7 @@ mod tests {
|
||||||
fn rpc_invalid_fontsize() {
|
fn rpc_invalid_fontsize() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_method("rpc_invalid_fontsize", |_| {
|
io.add_sync_method("rpc_invalid_fontsize", |_| {
|
||||||
Err(Error::from(OledError::InvalidFontSize {
|
Err(Error::from(OledError::InvalidFontSize {
|
||||||
font: "24x32".to_string(),
|
font: "24x32".to_string(),
|
||||||
}))
|
}))
|
||||||
|
@ -379,7 +352,7 @@ mod tests {
|
||||||
fn rpc_invalid_string() {
|
fn rpc_invalid_string() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_method("rpc_invalid_string", |_| {
|
io.add_sync_method("rpc_invalid_string", |_| {
|
||||||
Err(Error::from(OledError::InvalidString { len: 22 }))
|
Err(Error::from(OledError::InvalidString { len: 22 }))
|
||||||
});
|
});
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
|
@ -399,15 +372,15 @@ mod tests {
|
||||||
fn rpc_invalid_params() {
|
fn rpc_invalid_params() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_method("rpc_invalid_params", |_| {
|
io.add_sync_method("rpc_invalid_params", |_| {
|
||||||
let e = Error {
|
let source = Error {
|
||||||
code: ErrorCode::InvalidParams,
|
code: ErrorCode::InvalidParams,
|
||||||
message: String::from("invalid params"),
|
message: String::from("invalid params"),
|
||||||
data: Some(Value::String(
|
data: Some(Value::String(
|
||||||
"Invalid params: invalid type: null, expected struct Msg.".into(),
|
"Invalid params: invalid type: null, expected struct Msg.".into(),
|
||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
Err(Error::from(OledError::MissingParameter { e }))
|
Err(Error::from(OledError::MissingParameter { source }))
|
||||||
});
|
});
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
};
|
};
|
||||||
|
@ -427,13 +400,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_method("rpc_parse_error", |_| {
|
io.add_sync_method("rpc_parse_error", |_| {
|
||||||
let e = Error {
|
let source = Error {
|
||||||
code: ErrorCode::ParseError,
|
code: ErrorCode::ParseError,
|
||||||
message: String::from("Parse error"),
|
message: String::from("Parse error"),
|
||||||
data: None,
|
data: None,
|
||||||
};
|
};
|
||||||
Err(Error::from(OledError::ParseError { e }))
|
Err(Error::from(OledError::ParseError { source }))
|
||||||
});
|
});
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,40 +1,31 @@
|
||||||
[package]
|
[package]
|
||||||
name = "peach-stats"
|
name = "peach-stats"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
|
authors = ["Andrew Reid <glyph@mycelial.technology>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Query system statistics using JSON-RPC over HTTP. Provides a JSON-RPC wrapper around the probes and systemstat crates."
|
description = "Query system statistics. Provides a wrapper around the probes and systemstat crates."
|
||||||
|
keywords = ["peachcloud", "system stats", "system statistics", "disk", "memory"]
|
||||||
homepage = "https://opencollective.com/peachcloud"
|
homepage = "https://opencollective.com/peachcloud"
|
||||||
repository = "https://github.com/peachcloud/peach-stats"
|
repository = "https://git.coopcloud.tech/PeachCloud/peach-workspace/src/branch/main/peach-stats"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "AGPL-3.0-only"
|
license = "LGPL-3.0-only"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[package.metadata.deb]
|
|
||||||
depends = "$auto"
|
|
||||||
extended-description = """\
|
|
||||||
peach-stats is a system statistics microservice module for PeachCloud. \
|
|
||||||
Query system statistics using JSON-RPC over HTTP. Provides a JSON-RPC \
|
|
||||||
wrapper around the probes and systemstat crates."""
|
|
||||||
maintainer-scripts="debian"
|
|
||||||
systemd-units = { unit-name = "peach-stats" }
|
|
||||||
assets = [
|
|
||||||
["target/release/peach-stats", "usr/bin/", "755"],
|
|
||||||
["README.md", "usr/share/doc/peach-stats/README", "644"],
|
|
||||||
]
|
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
travis-ci = { repository = "peachcloud/peach-stats", branch = "master" }
|
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
env_logger = "0.9"
|
|
||||||
jsonrpc-core = "18"
|
|
||||||
jsonrpc-http-server = "18"
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miniserde = "0.1.15"
|
miniserde = { version = "0.1.15", optional = true }
|
||||||
probes = "0.4.1"
|
probes = "0.4.1"
|
||||||
|
serde = { version = "1.0.130", features = ["derive"], optional = true }
|
||||||
systemstat = "0.1.10"
|
systemstat = "0.1.10"
|
||||||
|
|
||||||
[dev-dependencies]
|
[features]
|
||||||
jsonrpc-test = "18"
|
default = []
|
||||||
|
|
||||||
|
# Provide `Serialize` and `Deserialize` traits for library structs using `miniserde`
|
||||||
|
miniserde_support = ["miniserde"]
|
||||||
|
|
||||||
|
# Provide `Serialize` and `Deserialize` traits for library structs using `serde`
|
||||||
|
serde_support = ["serde"]
|
||||||
|
|
|
@ -1,109 +1,47 @@
|
||||||
# peach-stats
|
# peach-stats
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.com/peachcloud/peach-stats.svg?branch=master)](https://travis-ci.com/peachcloud/peach-stats) ![Generic badge](https://img.shields.io/badge/version-0.1.3-<COLOR>.svg)
|
![Generic badge](https://img.shields.io/badge/version-0.2.0-<COLOR>.svg)
|
||||||
|
|
||||||
System statistics microservice module for PeachCloud. Provides a JSON-RPC wrapper around the [probes](https://crates.io/crates/probes) and [systemstat](https://crates.io/crates/systemstat) crates.
|
System statistics library for PeachCloud. Provides a wrapper around the [probes](https://crates.io/crates/probes) and [systemstat](https://crates.io/crates/systemstat) crates.
|
||||||
|
|
||||||
### JSON-RPC API
|
Currently offers the following statistics and associated data structures:
|
||||||
|
|
||||||
| Method | Description | Returns |
|
- CPU: `user`, `system`, `nice`, `idle` (as values or percentages)
|
||||||
| --- | --- | --- |
|
- Disk usage: `filesystem`, `one_k_blocks`, `one_k_blocks_used`,
|
||||||
| `cpu_stats` | CPU statistics | `user`, `system`, `nice`, `idle` |
|
`one_k_blocks_free`, `used_percentage`, `mountpoint`
|
||||||
| `cpu_stats_percent` | CPU statistics as percentages | `user`, `system`, `nice`, `idle` |
|
- Load average: `one`, `five`, `fifteen`
|
||||||
| `disk_usage` | Disk usage statistics (array of disks) | `filesystem`, `one_k_blocks`, `one_k_blocks_used`, `one_k_blocks_free`, `used_percentage`, `mountpoint` |
|
- Memory: `total`, `free`, `used`
|
||||||
| `load_average` | Load average statistics | `one`, `five`, `fifteen` |
|
- Uptime: `seconds`
|
||||||
| `mem_stats` | Memory statistics | `total`, `free`, `used` |
|
|
||||||
| `ping` | Microservice status | `success` if running |
|
|
||||||
| `uptime` | System uptime | `secs` |
|
|
||||||
|
|
||||||
### Environment
|
## Example Usage
|
||||||
|
|
||||||
The JSON-RPC HTTP server address and port can be configured with the `PEACH_STATS_SERVER` environment variable:
|
```rust
|
||||||
|
use peach_stats::{stats, StatsError};
|
||||||
|
|
||||||
`export PEACH_STATS_SERVER=127.0.0.1:5000`
|
fn main() -> Result<(), StatsError> {
|
||||||
|
let cpu = stats::cpu_stats()?;
|
||||||
|
let cpu_percentages = stats::cpu_stats_percent()?;
|
||||||
|
let disks = stats::disk_usage()?;
|
||||||
|
let load = stats::load_average()?;
|
||||||
|
let mem = stats::mem_stats()?;
|
||||||
|
let uptime = stats::uptime()?;
|
||||||
|
|
||||||
When not set, the value defaults to `127.0.0.1:5113`.
|
// do things with the retrieved values...
|
||||||
|
|
||||||
Logging is made available with `env_logger`:
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
`export RUST_LOG=info`
|
## Feature Flags
|
||||||
|
|
||||||
Other logging levels include `debug`, `warn` and `error`.
|
Feature flags are used to offer `Serialize` and `Deserialize` implementations for all `struct` data types provided by this library. These traits are not provided by default. A choice of `miniserde` and `serde` is provided.
|
||||||
|
|
||||||
### Setup
|
Define the desired feature in the `Cargo.toml` manifest of your project:
|
||||||
|
|
||||||
Clone this repo:
|
```toml
|
||||||
|
peach-stats = { version = "0.1.0", features = ["miniserde_support"] }
|
||||||
|
```
|
||||||
|
|
||||||
`git clone https://github.com/peachcloud/peach-stats.git`
|
## License
|
||||||
|
|
||||||
Move into the repo and compile a release build:
|
|
||||||
|
|
||||||
`cd peach-stats`
|
|
||||||
`cargo build --release`
|
|
||||||
|
|
||||||
Run the binary:
|
|
||||||
|
|
||||||
`./target/release/peach-stats`
|
|
||||||
|
|
||||||
### Debian Packaging
|
|
||||||
|
|
||||||
A `systemd` service file and Debian maintainer scripts are included in the `debian` directory, allowing `peach-stats` 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.
|
|
||||||
|
|
||||||
Install `cargo-deb`:
|
|
||||||
|
|
||||||
`cargo install cargo-deb`
|
|
||||||
|
|
||||||
Move into the repo:
|
|
||||||
|
|
||||||
`cd peach-stats`
|
|
||||||
|
|
||||||
Build the package:
|
|
||||||
|
|
||||||
`cargo deb`
|
|
||||||
|
|
||||||
The output will be written to `target/debian/peach-stats_0.1.0_arm64.deb` (or similar).
|
|
||||||
|
|
||||||
Build the package (aarch64):
|
|
||||||
|
|
||||||
`cargo deb --target aarch64-unknown-linux-gnu`
|
|
||||||
|
|
||||||
Install the package as follows:
|
|
||||||
|
|
||||||
`sudo dpkg -i target/debian/peach-stats_0.1.0_arm64.deb`
|
|
||||||
|
|
||||||
The service will be automatically enabled and started.
|
|
||||||
|
|
||||||
Uninstall the service:
|
|
||||||
|
|
||||||
`sudo apt-get remove peach-stats`
|
|
||||||
|
|
||||||
Remove configuration files (not removed with `apt-get remove`):
|
|
||||||
|
|
||||||
`sudo apt-get purge peach-stats`
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
**Get CPU Statistics**
|
|
||||||
|
|
||||||
With microservice running, open a second terminal window and use `curl` to call server methods:
|
|
||||||
|
|
||||||
`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "cpu_stats", "id":1 }' 127.0.0.1:5113`
|
|
||||||
|
|
||||||
Server responds with:
|
|
||||||
|
|
||||||
`{"jsonrpc":"2.0","result":"{\"user\":4661083,\"system\":1240371,\"idle\":326838290,\"nice\":0}","id":1}`
|
|
||||||
|
|
||||||
**Get System Uptime**
|
|
||||||
|
|
||||||
With microservice running, open a second terminal window and use `curl` to call server methods:
|
|
||||||
|
|
||||||
`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "uptime", "id":1 }' 127.0.0.1:5113`
|
|
||||||
|
|
||||||
Server responds with:
|
|
||||||
|
|
||||||
`{"jsonrpc":"2.0","result":"{\"secs\":840968}","id":1}`
|
|
||||||
|
|
||||||
### Licensing
|
|
||||||
|
|
||||||
AGPL-3.0
|
|
||||||
|
|
||||||
|
LGPL-3.0.
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=Query system statistics using JSON-RPC over HTTP.
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=peach-stats
|
|
||||||
Environment="RUST_LOG=error"
|
|
||||||
ExecStart=/usr/bin/peach-stats
|
|
||||||
Restart=always
|
|
||||||
CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYS_BOOT CAP_SYS_TIME CAP_KILL CAP_WAKE_ALARM CAP_LINUX_IMMUTABLE CAP_BLOCK_SUSPEND CAP_LEASE CAP_SYS_NICE CAP_SYS_RESOURCE CAP_RAWIO CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_DAC_* CAP_FOWNER CAP_IPC_OWNER CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_AUDIT_*
|
|
||||||
InaccessibleDirectories=/home
|
|
||||||
LockPersonality=yes
|
|
||||||
NoNewPrivileges=yes
|
|
||||||
PrivateDevices=yes
|
|
||||||
PrivateTmp=yes
|
|
||||||
PrivateUsers=yes
|
|
||||||
ProtectControlGroups=yes
|
|
||||||
ProtectHome=yes
|
|
||||||
ProtectKernelModules=yes
|
|
||||||
ProtectKernelTunables=yes
|
|
||||||
ProtectSystem=yes
|
|
||||||
ReadOnlyDirectories=/var
|
|
||||||
RestrictAddressFamilies=~AF_INET6 AF_UNIX
|
|
||||||
SystemCallFilter=~@reboot @clock @debug @module @mount @swap @resources @privileged
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
|
@ -1,69 +1,44 @@
|
||||||
use std::{error, fmt, io};
|
//! Custom error type for `peach-stats`.
|
||||||
|
|
||||||
use jsonrpc_core::{types::error::Error, ErrorCode};
|
|
||||||
use probes::ProbeError;
|
use probes::ProbeError;
|
||||||
|
use std::{error, fmt, io::Error as IoError};
|
||||||
|
|
||||||
|
/// Custom error type encapsulating all possible errors when retrieving system
|
||||||
|
/// statistics.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum StatError {
|
pub enum StatsError {
|
||||||
CpuStat { source: ProbeError },
|
/// Failed to retrieve CPU statistics.
|
||||||
DiskUsage { source: ProbeError },
|
CpuStat(ProbeError),
|
||||||
LoadAvg { source: ProbeError },
|
/// Failed to retrieve disk usage statistics.
|
||||||
MemStat { source: ProbeError },
|
DiskUsage(ProbeError),
|
||||||
Uptime { source: io::Error },
|
/// Failed to retrieve load average statistics.
|
||||||
|
LoadAvg(ProbeError),
|
||||||
|
/// Failed to retrieve memory usage statistics.
|
||||||
|
MemStat(ProbeError),
|
||||||
|
/// Failed to retrieve system uptime.
|
||||||
|
Uptime(IoError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for StatError {}
|
impl error::Error for StatsError {}
|
||||||
|
|
||||||
impl fmt::Display for StatError {
|
impl fmt::Display for StatsError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
StatError::CpuStat { ref source } => {
|
StatsError::CpuStat(ref source) => {
|
||||||
write!(f, "Failed to retrieve CPU statistics: {}", source)
|
write!(f, "Failed to retrieve CPU statistics: {}", source)
|
||||||
}
|
}
|
||||||
StatError::DiskUsage { ref source } => {
|
StatsError::DiskUsage(ref source) => {
|
||||||
write!(f, "Failed to retrieve disk usage statistics: {}", source)
|
write!(f, "Failed to retrieve disk usage statistics: {}", source)
|
||||||
}
|
}
|
||||||
StatError::LoadAvg { ref source } => {
|
StatsError::LoadAvg(ref source) => {
|
||||||
write!(f, "Failed to retrieve load average statistics: {}", source)
|
write!(f, "Failed to retrieve load average statistics: {}", source)
|
||||||
}
|
}
|
||||||
StatError::MemStat { ref source } => {
|
StatsError::MemStat(ref source) => {
|
||||||
write!(f, "Failed to retrieve memory statistics: {}", source)
|
write!(f, "Failed to retrieve memory statistics: {}", source)
|
||||||
}
|
}
|
||||||
StatError::Uptime { ref source } => {
|
StatsError::Uptime(ref source) => {
|
||||||
write!(f, "Failed to retrieve system uptime: {}", source)
|
write!(f, "Failed to retrieve system uptime: {}", source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<StatError> for Error {
|
|
||||||
fn from(err: StatError) -> Self {
|
|
||||||
match &err {
|
|
||||||
StatError::CpuStat { source } => Error {
|
|
||||||
code: ErrorCode::ServerError(-32001),
|
|
||||||
message: format!("Failed to retrieve CPU statistics: {}", source),
|
|
||||||
data: None,
|
|
||||||
},
|
|
||||||
StatError::DiskUsage { source } => Error {
|
|
||||||
code: ErrorCode::ServerError(-32001),
|
|
||||||
message: format!("Failed to retrieve disk usage statistics: {}", source),
|
|
||||||
data: None,
|
|
||||||
},
|
|
||||||
StatError::LoadAvg { source } => Error {
|
|
||||||
code: ErrorCode::ServerError(-32001),
|
|
||||||
message: format!("Failed to retrieve load average statistics: {}", source),
|
|
||||||
data: None,
|
|
||||||
},
|
|
||||||
StatError::MemStat { source } => Error {
|
|
||||||
code: ErrorCode::ServerError(-32001),
|
|
||||||
message: format!("Failed to retrieve memory statistics: {}", source),
|
|
||||||
data: None,
|
|
||||||
},
|
|
||||||
StatError::Uptime { source } => Error {
|
|
||||||
code: ErrorCode::ServerError(-32001),
|
|
||||||
message: format!("Failed to retrieve system uptime: {}", source),
|
|
||||||
data: None,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,103 +1,48 @@
|
||||||
mod error;
|
#![warn(missing_docs)]
|
||||||
mod stats;
|
|
||||||
mod structs;
|
|
||||||
|
|
||||||
use std::{env, result::Result};
|
//! # peach-stats
|
||||||
|
//!
|
||||||
|
//! System statistics retrieval library; designed for use with the PeachCloud platform.
|
||||||
|
//!
|
||||||
|
//! Currently offers the following statistics and associated data structures:
|
||||||
|
//!
|
||||||
|
//! - CPU: `user`, `system`, `nice`, `idle` (as values or percentages)
|
||||||
|
//! - Disk usage: `filesystem`, `one_k_blocks`, `one_k_blocks_used`,
|
||||||
|
//! `one_k_blocks_free`, `used_percentage`, `mountpoint`
|
||||||
|
//! - Load average: `one`, `five`, `fifteen`
|
||||||
|
//! - Memory: `total`, `free`, `used`
|
||||||
|
//! - Uptime: `seconds`
|
||||||
|
//!
|
||||||
|
//! ## Example Usage
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use peach_stats::{stats, StatsError};
|
||||||
|
//!
|
||||||
|
//! fn main() -> Result<(), StatsError> {
|
||||||
|
//! let cpu = stats::cpu_stats()?;
|
||||||
|
//! let cpu_percentages = stats::cpu_stats_percent()?;
|
||||||
|
//! let disks = stats::disk_usage()?;
|
||||||
|
//! let load = stats::load_average()?;
|
||||||
|
//! let mem = stats::mem_stats()?;
|
||||||
|
//! let uptime = stats::uptime()?;
|
||||||
|
//!
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Feature Flags
|
||||||
|
//!
|
||||||
|
//! Feature flags are used to offer `Serialize` and `Deserialize` implementations
|
||||||
|
//! for all `struct` data types provided by this library. These traits are not
|
||||||
|
//! provided by default. A choice of `miniserde` and `serde` is provided.
|
||||||
|
//!
|
||||||
|
//! Define the desired feature in the `Cargo.toml` manifest of your project:
|
||||||
|
//!
|
||||||
|
//! ```toml
|
||||||
|
//! peach-stats = { version = "0.1.0", features = ["miniserde_support"] }
|
||||||
|
//! ```
|
||||||
|
|
||||||
use jsonrpc_core::{IoHandler, Value};
|
pub mod error;
|
||||||
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
|
pub mod stats;
|
||||||
use log::info;
|
|
||||||
|
|
||||||
use crate::error::StatError;
|
pub use crate::error::StatsError;
|
||||||
|
|
||||||
pub fn run() -> Result<(), StatError> {
|
|
||||||
info!("Starting up.");
|
|
||||||
|
|
||||||
info!("Creating JSON-RPC I/O handler.");
|
|
||||||
let mut io = IoHandler::default();
|
|
||||||
|
|
||||||
io.add_method("cpu_stats", move |_| async {
|
|
||||||
info!("Fetching CPU statistics.");
|
|
||||||
let stats = stats::cpu_stats()?;
|
|
||||||
|
|
||||||
Ok(Value::String(stats))
|
|
||||||
});
|
|
||||||
|
|
||||||
io.add_method("cpu_stats_percent", move |_| async {
|
|
||||||
info!("Fetching CPU statistics as percentages.");
|
|
||||||
let stats = stats::cpu_stats_percent()?;
|
|
||||||
|
|
||||||
Ok(Value::String(stats))
|
|
||||||
});
|
|
||||||
|
|
||||||
io.add_method("disk_usage", move |_| async {
|
|
||||||
info!("Fetching disk usage statistics.");
|
|
||||||
let disks = stats::disk_usage()?;
|
|
||||||
|
|
||||||
Ok(Value::String(disks))
|
|
||||||
});
|
|
||||||
|
|
||||||
io.add_method("load_average", move |_| async {
|
|
||||||
info!("Fetching system load average statistics.");
|
|
||||||
let avg = stats::load_average()?;
|
|
||||||
|
|
||||||
Ok(Value::String(avg))
|
|
||||||
});
|
|
||||||
|
|
||||||
io.add_method("mem_stats", move |_| async {
|
|
||||||
info!("Fetching current memory statistics.");
|
|
||||||
let mem = stats::mem_stats()?;
|
|
||||||
|
|
||||||
Ok(Value::String(mem))
|
|
||||||
});
|
|
||||||
|
|
||||||
io.add_method("ping", |_| async {
|
|
||||||
Ok(Value::String("success".to_string()))
|
|
||||||
});
|
|
||||||
|
|
||||||
io.add_method("uptime", move |_| async {
|
|
||||||
info!("Fetching system uptime.");
|
|
||||||
let uptime = stats::uptime()?;
|
|
||||||
|
|
||||||
Ok(Value::String(uptime))
|
|
||||||
});
|
|
||||||
|
|
||||||
let http_server = env::var("PEACH_OLED_STATS").unwrap_or_else(|_| "127.0.0.1:5113".to_string());
|
|
||||||
|
|
||||||
info!("Starting JSON-RPC server on {}.", http_server);
|
|
||||||
let server = ServerBuilder::new(io)
|
|
||||||
.cors(DomainsValidation::AllowOnly(vec![
|
|
||||||
AccessControlAllowOrigin::Null,
|
|
||||||
]))
|
|
||||||
.start_http(
|
|
||||||
&http_server
|
|
||||||
.parse()
|
|
||||||
.expect("Invalid HTTP address and port combination"),
|
|
||||||
)
|
|
||||||
.expect("Unable to start RPC server");
|
|
||||||
|
|
||||||
info!("Listening for requests.");
|
|
||||||
server.wait();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use jsonrpc_test as test_rpc;
|
|
||||||
|
|
||||||
// test to ensure correct success response
|
|
||||||
#[test]
|
|
||||||
fn rpc_success() {
|
|
||||||
let rpc = {
|
|
||||||
let mut io = IoHandler::new();
|
|
||||||
io.add_method("rpc_success_response", |_| async {
|
|
||||||
Ok(Value::String("success".into()))
|
|
||||||
});
|
|
||||||
test_rpc::Rpc::from(io)
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(rpc.request("rpc_success_response", &()), r#""success""#);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
use std::process;
|
|
||||||
|
|
||||||
use log::error;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// initialize the logger
|
|
||||||
env_logger::init();
|
|
||||||
|
|
||||||
// handle errors returned from `run`
|
|
||||||
if let Err(e) = peach_stats::run() {
|
|
||||||
error!("Application error: {}", e);
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +1,96 @@
|
||||||
|
//! System statistics retrieval functions and associated data types.
|
||||||
|
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
|
||||||
use miniserde::json;
|
#[cfg(feature = "miniserde_support")]
|
||||||
|
use miniserde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[cfg(feature = "serde_support")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use probes::{cpu, disk_usage, load, memory};
|
use probes::{cpu, disk_usage, load, memory};
|
||||||
use systemstat::{Platform, System};
|
use systemstat::{Platform, System};
|
||||||
|
|
||||||
use crate::error::StatError;
|
use crate::error::StatsError;
|
||||||
use crate::structs::{CpuStat, CpuStatPercentages, DiskUsage, LoadAverage, MemStat};
|
|
||||||
|
|
||||||
pub fn cpu_stats() -> Result<String, StatError> {
|
/// CPU statistics.
|
||||||
let cpu_stats = cpu::proc::read().map_err(|source| StatError::CpuStat { source })?;
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "miniserde_support", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
|
||||||
|
pub struct CpuStat {
|
||||||
|
/// Time spent running user space (application) code.
|
||||||
|
pub user: u64,
|
||||||
|
/// Time spent running kernel code.
|
||||||
|
pub system: u64,
|
||||||
|
/// Time spent doing nothing.
|
||||||
|
pub idle: u64,
|
||||||
|
/// Time spent running user space processes which have been niced.
|
||||||
|
pub nice: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CPU statistics as percentages.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "miniserde_support", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
|
||||||
|
pub struct CpuStatPercentages {
|
||||||
|
/// Time spent running user space (application) code.
|
||||||
|
pub user: f32,
|
||||||
|
/// Time spent running kernel code.
|
||||||
|
pub system: f32,
|
||||||
|
/// Time spent doing nothing.
|
||||||
|
pub idle: f32,
|
||||||
|
/// Time spent running user space processes which have been niced.
|
||||||
|
pub nice: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disk usage statistics.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "miniserde_support", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
|
||||||
|
pub struct DiskUsage {
|
||||||
|
/// Filesystem device path.
|
||||||
|
pub filesystem: Option<String>,
|
||||||
|
/// Total amount of disk space as a number of 1,000 kilobyte blocks.
|
||||||
|
pub one_k_blocks: u64,
|
||||||
|
/// Total amount of used disk space as a number of 1,000 kilobyte blocks.
|
||||||
|
pub one_k_blocks_used: u64,
|
||||||
|
/// Total amount of free / available disk space as a number of 1,000 kilobyte blocks.
|
||||||
|
pub one_k_blocks_free: u64,
|
||||||
|
/// Total amount of used disk space as a percentage.
|
||||||
|
pub used_percentage: u32,
|
||||||
|
/// Mountpoint of the disk / partition.
|
||||||
|
pub mountpoint: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load average statistics.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "miniserde_support", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
|
||||||
|
pub struct LoadAverage {
|
||||||
|
/// Average computational work performed over the past minute.
|
||||||
|
pub one: f32,
|
||||||
|
/// Average computational work performed over the past five minutes.
|
||||||
|
pub five: f32,
|
||||||
|
/// Average computational work performed over the past fifteen minutes.
|
||||||
|
pub fifteen: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Memory statistics.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "miniserde_support", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
|
||||||
|
pub struct MemStat {
|
||||||
|
/// Total amount of physical memory in kilobytes.
|
||||||
|
pub total: u64,
|
||||||
|
/// Total amount of free / available physical memory in kilobytes.
|
||||||
|
pub free: u64,
|
||||||
|
/// Total amount of used physical memory in kilobytes.
|
||||||
|
pub used: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve the current CPU statistics.
|
||||||
|
pub fn cpu_stats() -> Result<CpuStat, StatsError> {
|
||||||
|
let cpu_stats = cpu::proc::read().map_err(StatsError::CpuStat)?;
|
||||||
let s = cpu_stats.stat;
|
let s = cpu_stats.stat;
|
||||||
let cpu = CpuStat {
|
let cpu = CpuStat {
|
||||||
user: s.user,
|
user: s.user,
|
||||||
|
@ -16,13 +98,13 @@ pub fn cpu_stats() -> Result<String, StatError> {
|
||||||
nice: s.nice,
|
nice: s.nice,
|
||||||
idle: s.idle,
|
idle: s.idle,
|
||||||
};
|
};
|
||||||
let json_cpu = json::to_string(&cpu);
|
|
||||||
|
|
||||||
Ok(json_cpu)
|
Ok(cpu)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cpu_stats_percent() -> Result<String, StatError> {
|
/// Retrieve the current CPU statistics as percentages.
|
||||||
let cpu_stats = cpu::proc::read().map_err(|source| StatError::CpuStat { source })?;
|
pub fn cpu_stats_percent() -> Result<CpuStatPercentages, StatsError> {
|
||||||
|
let cpu_stats = cpu::proc::read().map_err(StatsError::CpuStat)?;
|
||||||
let s = cpu_stats.stat.in_percentages();
|
let s = cpu_stats.stat.in_percentages();
|
||||||
let cpu = CpuStatPercentages {
|
let cpu = CpuStatPercentages {
|
||||||
user: s.user,
|
user: s.user,
|
||||||
|
@ -30,13 +112,13 @@ pub fn cpu_stats_percent() -> Result<String, StatError> {
|
||||||
nice: s.nice,
|
nice: s.nice,
|
||||||
idle: s.idle,
|
idle: s.idle,
|
||||||
};
|
};
|
||||||
let json_cpu = json::to_string(&cpu);
|
|
||||||
|
|
||||||
Ok(json_cpu)
|
Ok(cpu)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disk_usage() -> Result<String, StatError> {
|
/// Retrieve the current disk usage statistics for each available disk / partition.
|
||||||
let disks = disk_usage::read().map_err(|source| StatError::DiskUsage { source })?;
|
pub fn disk_usage() -> Result<Vec<DiskUsage>, StatsError> {
|
||||||
|
let disks = disk_usage::read().map_err(StatsError::DiskUsage)?;
|
||||||
let mut disk_usages = Vec::new();
|
let mut disk_usages = Vec::new();
|
||||||
for d in disks {
|
for d in disks {
|
||||||
let disk = DiskUsage {
|
let disk = DiskUsage {
|
||||||
|
@ -49,42 +131,39 @@ pub fn disk_usage() -> Result<String, StatError> {
|
||||||
};
|
};
|
||||||
disk_usages.push(disk);
|
disk_usages.push(disk);
|
||||||
}
|
}
|
||||||
let json_disks = json::to_string(&disk_usages);
|
|
||||||
|
|
||||||
Ok(json_disks)
|
Ok(disk_usages)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_average() -> Result<String, StatError> {
|
/// Retrieve the current load average statistics.
|
||||||
let l = load::read().map_err(|source| StatError::LoadAvg { source })?;
|
pub fn load_average() -> Result<LoadAverage, StatsError> {
|
||||||
|
let l = load::read().map_err(StatsError::LoadAvg)?;
|
||||||
let load_avg = LoadAverage {
|
let load_avg = LoadAverage {
|
||||||
one: l.one,
|
one: l.one,
|
||||||
five: l.five,
|
five: l.five,
|
||||||
fifteen: l.fifteen,
|
fifteen: l.fifteen,
|
||||||
};
|
};
|
||||||
let json_load_avg = json::to_string(&load_avg);
|
|
||||||
|
|
||||||
Ok(json_load_avg)
|
Ok(load_avg)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mem_stats() -> Result<String, StatError> {
|
/// Retrieve the current memory usage statistics.
|
||||||
let m = memory::read().map_err(|source| StatError::MemStat { source })?;
|
pub fn mem_stats() -> Result<MemStat, StatsError> {
|
||||||
|
let m = memory::read().map_err(StatsError::MemStat)?;
|
||||||
let mem = MemStat {
|
let mem = MemStat {
|
||||||
total: m.total(),
|
total: m.total(),
|
||||||
free: m.free(),
|
free: m.free(),
|
||||||
used: m.used(),
|
used: m.used(),
|
||||||
};
|
};
|
||||||
let json_mem = json::to_string(&mem);
|
|
||||||
|
|
||||||
Ok(json_mem)
|
Ok(mem)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uptime() -> Result<String, StatError> {
|
/// Retrieve the system uptime in seconds.
|
||||||
|
pub fn uptime() -> Result<u64, StatsError> {
|
||||||
let sys = System::new();
|
let sys = System::new();
|
||||||
let uptime = sys
|
let uptime = sys.uptime().map_err(StatsError::Uptime)?;
|
||||||
.uptime()
|
|
||||||
.map_err(|source| StatError::Uptime { source })?;
|
|
||||||
let uptime_secs = uptime.as_secs();
|
let uptime_secs = uptime.as_secs();
|
||||||
let json_uptime = json::to_string(&uptime_secs);
|
|
||||||
|
|
||||||
Ok(json_uptime)
|
Ok(uptime_secs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
use miniserde::Serialize;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct CpuStat {
|
|
||||||
pub user: u64,
|
|
||||||
pub system: u64,
|
|
||||||
pub idle: u64,
|
|
||||||
pub nice: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct CpuStatPercentages {
|
|
||||||
pub user: f32,
|
|
||||||
pub system: f32,
|
|
||||||
pub idle: f32,
|
|
||||||
pub nice: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct DiskUsage {
|
|
||||||
pub filesystem: Option<String>,
|
|
||||||
pub one_k_blocks: u64,
|
|
||||||
pub one_k_blocks_used: u64,
|
|
||||||
pub one_k_blocks_free: u64,
|
|
||||||
pub used_percentage: u32,
|
|
||||||
pub mountpoint: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct LoadAverage {
|
|
||||||
pub one: f32,
|
|
||||||
pub five: f32,
|
|
||||||
pub fifteen: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct MemStat {
|
|
||||||
pub total: u64,
|
|
||||||
pub free: u64,
|
|
||||||
pub used: u64,
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "peach-web"
|
name = "peach-web"
|
||||||
version = "0.4.11"
|
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."
|
||||||
|
@ -38,17 +38,16 @@ maintenance = { status = "actively-developed" }
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nest = "1.0.0"
|
nest = "1.0.0"
|
||||||
|
openssl = { version = "0.10", features = ["vendored"] }
|
||||||
peach-lib = { path = "../peach-lib" }
|
peach-lib = { path = "../peach-lib" }
|
||||||
percent-encoding = "2.1.0"
|
percent-encoding = "2.1.0"
|
||||||
|
regex = "1"
|
||||||
rocket = { version = "0.5.0-rc.1", features = ["json", "secrets"] }
|
rocket = { version = "0.5.0-rc.1", features = ["json", "secrets"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
snafu = "0.6"
|
snafu = "0.6"
|
||||||
tera = { version = "1.12.1", features = ["builtins"] }
|
tera = { version = "1.12.1", features = ["builtins"] }
|
||||||
websocket = "0.26"
|
|
||||||
regex = "1"
|
|
||||||
xdg = "2.2.0"
|
xdg = "2.2.0"
|
||||||
openssl = { version = "0.10", features = ["vendored"] }
|
|
||||||
|
|
||||||
[dependencies.rocket_dyn_templates]
|
[dependencies.rocket_dyn_templates]
|
||||||
version = "0.1.0-rc.1"
|
version = "0.1.0-rc.1"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# peach-web
|
# peach-web
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.com/peachcloud/peach-web.svg?branch=master)](https://travis-ci.com/peachcloud/peach-web) ![Generic badge](https://img.shields.io/badge/version-0.4.6-<COLOR>.svg)
|
[![Build Status](https://travis-ci.com/peachcloud/peach-web.svg?branch=master)](https://travis-ci.com/peachcloud/peach-web) ![Generic badge](https://img.shields.io/badge/version-0.4.12-<COLOR>.svg)
|
||||||
|
|
||||||
## Web Interface for PeachCloud
|
## Web Interface for PeachCloud
|
||||||
|
|
||||||
|
@ -39,12 +39,22 @@ _Note: Networking functionality requires peach-network microservice to be runnin
|
||||||
|
|
||||||
### Environment
|
### Environment
|
||||||
|
|
||||||
|
**Deployment Mode**
|
||||||
|
|
||||||
The web application deployment mode is configured with the `ROCKET_ENV` environment variable:
|
The web application deployment mode is configured with the `ROCKET_ENV` environment variable:
|
||||||
|
|
||||||
`export ROCKET_ENV=stage`
|
`export ROCKET_ENV=stage`
|
||||||
|
|
||||||
Other deployment modes are `dev` and `prod`. Read the [Rocket Environment Configurations docs](https://rocket.rs/v0.5-rc/guide/configuration/#environment-variables) for further information.
|
Other deployment modes are `dev` and `prod`. Read the [Rocket Environment Configurations docs](https://rocket.rs/v0.5-rc/guide/configuration/#environment-variables) for further information.
|
||||||
|
|
||||||
|
**Authentication**
|
||||||
|
|
||||||
|
Authentication is disabled in `development` mode and enabled by default when running the application in `production` mode. It can be disabled by setting the `ROCKET_DISABLE_AUTH` environment variable to `true`:
|
||||||
|
|
||||||
|
`export ROCKET_DISABLE_AUTH=true`
|
||||||
|
|
||||||
|
**Logging**
|
||||||
|
|
||||||
Logging is made available with `env_logger`:
|
Logging is made available with `env_logger`:
|
||||||
|
|
||||||
`export RUST_LOG=info`
|
`export RUST_LOG=info`
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
[development]
|
[development]
|
||||||
template_dir = "templates/"
|
template_dir = "templates/"
|
||||||
|
disable_auth = true
|
||||||
|
|
||||||
[production]
|
[production]
|
||||||
template_dir = "templates/"
|
template_dir = "templates/"
|
||||||
|
disable_auth = false
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use log::info;
|
use log::info;
|
||||||
use rocket::form::{Form, FromForm};
|
use rocket::form::{Form, FromForm};
|
||||||
use rocket::request::FlashMessage;
|
use rocket::http::{Cookie, CookieJar, Status};
|
||||||
|
use rocket::request::{self, FlashMessage, FromRequest, Request};
|
||||||
use rocket::response::{Flash, Redirect};
|
use rocket::response::{Flash, Redirect};
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::{
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
json::{Json, Value},
|
||||||
use rocket::{get, post};
|
Deserialize, Serialize,
|
||||||
|
};
|
||||||
|
use rocket::{get, post, Config};
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::Template;
|
||||||
|
|
||||||
use peach_lib::error::PeachError;
|
use peach_lib::error::PeachError;
|
||||||
|
@ -12,9 +15,6 @@ use peach_lib::password_utils;
|
||||||
|
|
||||||
use crate::error::PeachWebError;
|
use crate::error::PeachWebError;
|
||||||
use crate::utils::{build_json_response, TemplateOrRedirect};
|
use crate::utils::{build_json_response, TemplateOrRedirect};
|
||||||
use rocket::http::{Cookie, CookieJar, Status};
|
|
||||||
use rocket::request::{self, FromRequest, Request};
|
|
||||||
use rocket::serde::json::Value;
|
|
||||||
|
|
||||||
// HELPERS AND STRUCTS FOR AUTHENTICATION WITH COOKIES
|
// HELPERS AND STRUCTS FOR AUTHENTICATION WITH COOKIES
|
||||||
|
|
||||||
|
@ -42,14 +42,27 @@ impl<'r> FromRequest<'r> for Authenticated {
|
||||||
type Error = LoginError;
|
type Error = LoginError;
|
||||||
|
|
||||||
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
let authenticated = req
|
// check for `disable_auth` config value; set to `false` if unset
|
||||||
.cookies()
|
// can be set via the `ROCKET_DISABLE_AUTH` environment variable
|
||||||
.get_private(AUTH_COOKIE_KEY)
|
// - env var, if set, takes precedence over value defined in `Rocket.toml`
|
||||||
.and_then(|cookie| cookie.value().parse().ok())
|
let authentication_is_disabled: bool = match Config::figment().find_value("disable_auth") {
|
||||||
.map(|_value: String| Authenticated {});
|
// deserialize the boolean value; set to `false` if an error is encountered
|
||||||
match authenticated {
|
Ok(value) => value.deserialize().unwrap_or(false),
|
||||||
Some(auth) => request::Outcome::Success(auth),
|
Err(_) => false,
|
||||||
None => request::Outcome::Failure((Status::Forbidden, LoginError::UserNotLoggedIn)),
|
};
|
||||||
|
if authentication_is_disabled {
|
||||||
|
let auth = Authenticated {};
|
||||||
|
request::Outcome::Success(auth)
|
||||||
|
} else {
|
||||||
|
let authenticated = req
|
||||||
|
.cookies()
|
||||||
|
.get_private(AUTH_COOKIE_KEY)
|
||||||
|
.and_then(|cookie| cookie.value().parse().ok())
|
||||||
|
.map(|_value: String| Authenticated {});
|
||||||
|
match authenticated {
|
||||||
|
Some(auth) => request::Outcome::Success(auth),
|
||||||
|
None => request::Outcome::Failure((Status::Forbidden, LoginError::UserNotLoggedIn)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ pub fn save_dns_configuration(dns_form: DnsForm) -> Result<(), PeachWebError> {
|
||||||
info!("Failed to register dyndns domain: {:?}", err);
|
info!("Failed to register dyndns domain: {:?}", err);
|
||||||
// json response for failed update
|
// json response for failed update
|
||||||
let msg: String = match err {
|
let msg: String = match err {
|
||||||
PeachError::JsonRpcClientCore { source } => {
|
PeachError::JsonRpcClientCore(source) => {
|
||||||
match source {
|
match source {
|
||||||
Error(ErrorKind::JsonRpcError(err), _state) => match err.code {
|
Error(ErrorKind::JsonRpcError(err), _state) => match err.code {
|
||||||
ErrorCode::ServerError(-32030) => {
|
ErrorCode::ServerError(-32030) => {
|
||||||
|
|
|
@ -4,17 +4,29 @@ use std::io::Read;
|
||||||
use rocket::http::{ContentType, Status};
|
use rocket::http::{ContentType, Status};
|
||||||
use rocket::local::blocking::Client;
|
use rocket::local::blocking::Client;
|
||||||
use rocket::serde::json::{json, Value};
|
use rocket::serde::json::{json, Value};
|
||||||
|
use rocket::{Build, Config, Rocket};
|
||||||
|
|
||||||
use crate::utils::build_json_response;
|
use crate::utils::build_json_response;
|
||||||
|
|
||||||
use super::init_rocket;
|
use super::init_rocket;
|
||||||
|
|
||||||
|
// define authentication mode
|
||||||
|
const DISABLE_AUTH: bool = true;
|
||||||
|
|
||||||
|
/// Wrapper around `init_rocket()` to simplify the process of invoking the application with the desired authentication status. This is particularly useful for testing purposes.
|
||||||
|
fn init_test_rocket(disable_auth: bool) -> Rocket<Build> {
|
||||||
|
// set authentication based on provided `disable_auth` value
|
||||||
|
Config::figment().merge(("disable_auth", disable_auth));
|
||||||
|
|
||||||
|
init_rocket()
|
||||||
|
}
|
||||||
|
|
||||||
// helper function to test correct retrieval and content of a file
|
// helper function to test correct retrieval and content of a file
|
||||||
fn test_query_file<T>(path: &str, file: T, status: Status)
|
fn test_query_file<T>(path: &str, file: T, status: Status)
|
||||||
where
|
where
|
||||||
T: Into<Option<&'static str>>,
|
T: Into<Option<&'static str>>,
|
||||||
{
|
{
|
||||||
let client = Client::tracked(init_rocket()).unwrap();
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).unwrap();
|
||||||
let response = client.get(path).dispatch();
|
let response = client.get(path).dispatch();
|
||||||
assert_eq!(response.status(), status);
|
assert_eq!(response.status(), status);
|
||||||
|
|
||||||
|
@ -39,7 +51,7 @@ fn read_file_content(path: &str) -> Vec<u8> {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn index_html() {
|
fn index_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/").dispatch();
|
let response = client.get("/").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -54,7 +66,7 @@ fn index_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn help_html() {
|
fn help_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/help").dispatch();
|
let response = client.get("/help").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -64,7 +76,7 @@ fn help_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn login_html() {
|
fn login_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/login").dispatch();
|
let response = client.get("/login").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -74,7 +86,7 @@ fn login_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn logout_html() {
|
fn logout_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/logout").dispatch();
|
let response = client.get("/logout").dispatch();
|
||||||
// check for 303 status (redirect to "/login")
|
// check for 303 status (redirect to "/login")
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
|
@ -83,7 +95,7 @@ fn logout_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn power_html() {
|
fn power_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/power").dispatch();
|
let response = client.get("/power").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -97,7 +109,7 @@ NOTE: these tests are comment-out for the moment, due to the fact that they invo
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reboot() {
|
fn reboot() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/power/reboot").dispatch();
|
let response = client.get("/power/reboot").dispatch();
|
||||||
// check for redirect
|
// check for redirect
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
|
@ -105,7 +117,7 @@ fn reboot() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shutdown() {
|
fn shutdown() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/power/shutdown").dispatch();
|
let response = client.get("/power/shutdown").dispatch();
|
||||||
// check for redirect
|
// check for redirect
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
|
@ -116,7 +128,7 @@ fn shutdown() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block() {
|
fn block() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/scuttlebutt/block")
|
.post("/scuttlebutt/block")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
|
@ -127,7 +139,7 @@ fn block() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn blocks_html() {
|
fn blocks_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/blocks").dispatch();
|
let response = client.get("/scuttlebutt/blocks").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -137,7 +149,7 @@ fn blocks_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn follow() {
|
fn follow() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/scuttlebutt/follow")
|
.post("/scuttlebutt/follow")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
|
@ -149,7 +161,7 @@ fn follow() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn follows_html() {
|
fn follows_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/follows").dispatch();
|
let response = client.get("/scuttlebutt/follows").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -159,7 +171,7 @@ fn follows_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn followers_html() {
|
fn followers_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/followers").dispatch();
|
let response = client.get("/scuttlebutt/followers").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -169,7 +181,7 @@ fn followers_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn friends_html() {
|
fn friends_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/friends").dispatch();
|
let response = client.get("/scuttlebutt/friends").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -179,7 +191,7 @@ fn friends_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn peers_html() {
|
fn peers_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/peers").dispatch();
|
let response = client.get("/scuttlebutt/peers").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -189,7 +201,7 @@ fn peers_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn private_html() {
|
fn private_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/private").dispatch();
|
let response = client.get("/scuttlebutt/private").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -199,7 +211,7 @@ fn private_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn profile_html() {
|
fn profile_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/profile").dispatch();
|
let response = client.get("/scuttlebutt/profile").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -209,7 +221,7 @@ fn profile_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn publish_post() {
|
fn publish_post() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/scuttlebutt/publish")
|
.post("/scuttlebutt/publish")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
|
@ -220,7 +232,7 @@ fn publish_post() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unfollow() {
|
fn unfollow() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/scuttlebutt/unfollow")
|
.post("/scuttlebutt/unfollow")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
|
@ -233,7 +245,7 @@ fn unfollow() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn admin_settings_menu_html() {
|
fn admin_settings_menu_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/admin").dispatch();
|
let response = client.get("/settings/admin").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -245,7 +257,7 @@ fn admin_settings_menu_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_admin_html() {
|
fn add_admin_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/admin/add").dispatch();
|
let response = client.get("/settings/admin/add").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -258,7 +270,7 @@ fn add_admin_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_admin() {
|
fn add_admin() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/settings/admin/add")
|
.post("/settings/admin/add")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
|
@ -270,21 +282,21 @@ fn add_admin() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn change_password_html() {
|
fn change_password_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/admin/change_password").dispatch();
|
let response = client.get("/settings/admin/change_password").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
let body = response.into_string().unwrap();
|
let body = response.into_string().unwrap();
|
||||||
assert!(body.contains("Change Password"));
|
assert!(body.contains("Change Password"));
|
||||||
assert!(body.contains("Old Password"));
|
assert!(body.contains("Current password"));
|
||||||
assert!(body.contains("Enter New Password"));
|
assert!(body.contains("New password"));
|
||||||
assert!(body.contains("Re-Enter New Password"));
|
assert!(body.contains("New password duplicate"));
|
||||||
assert!(body.contains("Save"));
|
assert!(body.contains("Save"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn configure_admin_html() {
|
fn configure_admin_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/admin/configure").dispatch();
|
let response = client.get("/settings/admin/configure").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -296,7 +308,7 @@ fn configure_admin_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forgot_password_html() {
|
fn forgot_password_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/admin/forgot_password").dispatch();
|
let response = client.get("/settings/admin/forgot_password").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -308,7 +320,7 @@ fn forgot_password_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn network_settings_menu_html() {
|
fn network_settings_menu_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network").dispatch();
|
let response = client.get("/settings/network").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -318,7 +330,7 @@ fn network_settings_menu_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deploy_ap() {
|
fn deploy_ap() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/ap/activate").dispatch();
|
let response = client.get("/settings/network/ap/activate").dispatch();
|
||||||
// check for 303 status (redirect)
|
// check for 303 status (redirect)
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
|
@ -327,7 +339,7 @@ fn deploy_ap() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dns_settings_html() {
|
fn dns_settings_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/dns").dispatch();
|
let response = client.get("/settings/network/dns").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -341,7 +353,7 @@ fn dns_settings_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_aps_html() {
|
fn list_aps_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/wifi").dispatch();
|
let response = client.get("/settings/network/wifi").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -353,7 +365,7 @@ fn list_aps_html() {
|
||||||
// TODO: needs further testing once template has been refactored
|
// TODO: needs further testing once template has been refactored
|
||||||
#[test]
|
#[test]
|
||||||
fn ap_details_html() {
|
fn ap_details_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/wifi?ssid=Home").dispatch();
|
let response = client.get("/settings/network/wifi?ssid=Home").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -363,7 +375,7 @@ fn ap_details_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deploy_client() {
|
fn deploy_client() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/wifi/activate").dispatch();
|
let response = client.get("/settings/network/wifi/activate").dispatch();
|
||||||
// check for 303 status (redirect)
|
// check for 303 status (redirect)
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
|
@ -372,7 +384,7 @@ fn deploy_client() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_ap_html() {
|
fn add_ap_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/wifi/add").dispatch();
|
let response = client.get("/settings/network/wifi/add").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -386,7 +398,7 @@ fn add_ap_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_ap_ssid_html() {
|
fn add_ap_ssid_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/settings/network/wifi/add?ssid=Home")
|
.get("/settings/network/wifi/add?ssid=Home")
|
||||||
.dispatch();
|
.dispatch();
|
||||||
|
@ -402,7 +414,7 @@ fn add_ap_ssid_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_credentials() {
|
fn add_credentials() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/settings/network/wifi/add")
|
.post("/settings/network/wifi/add")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
|
@ -414,7 +426,7 @@ fn add_credentials() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forget_wifi() {
|
fn forget_wifi() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/settings/network/wifi/forget")
|
.post("/settings/network/wifi/forget")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
|
@ -426,7 +438,7 @@ fn forget_wifi() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn modify_password() {
|
fn modify_password() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/settings/network/wifi/modify")
|
.post("/settings/network/wifi/modify")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
|
@ -438,7 +450,7 @@ fn modify_password() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn data_usage_html() {
|
fn data_usage_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/wifi/usage").dispatch();
|
let response = client.get("/settings/network/wifi/usage").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -453,7 +465,7 @@ fn data_usage_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scuttlebutt_settings_menu_html() {
|
fn scuttlebutt_settings_menu_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/scuttlebutt").dispatch();
|
let response = client.get("/settings/scuttlebutt").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -472,7 +484,7 @@ fn scuttlebutt_settings_menu_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn status_html() {
|
fn status_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/status").dispatch();
|
let response = client.get("/status").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -485,7 +497,7 @@ fn status_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn network_status_html() {
|
fn network_status_html() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client.get("/status/network").dispatch();
|
let response = client.get("/status/network").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
|
@ -502,7 +514,7 @@ fn network_status_html() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn activate_ap() {
|
fn activate_ap() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/api/v1/network/activate_ap")
|
.post("/api/v1/network/activate_ap")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -513,7 +525,7 @@ fn activate_ap() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn activate_client() {
|
fn activate_client() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/api/v1/network/activate_client")
|
.post("/api/v1/network/activate_client")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -524,7 +536,7 @@ fn activate_client() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_ip() {
|
fn return_ip() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/ip")
|
.get("/api/v1/network/ip")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -538,7 +550,7 @@ fn return_ip() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_rssi() {
|
fn return_rssi() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/rssi")
|
.get("/api/v1/network/rssi")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -551,7 +563,7 @@ fn return_rssi() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_ssid() {
|
fn return_ssid() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/ssid")
|
.get("/api/v1/network/ssid")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -564,7 +576,7 @@ fn return_ssid() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_state() {
|
fn return_state() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/state")
|
.get("/api/v1/network/state")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -579,7 +591,7 @@ fn return_state() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_status() {
|
fn return_status() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/status")
|
.get("/api/v1/network/status")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -592,7 +604,7 @@ fn return_status() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scan_networks() {
|
fn scan_networks() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/wifi")
|
.get("/api/v1/network/wifi")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -605,7 +617,7 @@ fn scan_networks() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_wifi() {
|
fn add_wifi() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/api/v1/network/wifi")
|
.post("/api/v1/network/wifi")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -619,7 +631,7 @@ fn add_wifi() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_wifi() {
|
fn remove_wifi() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/api/v1/network/wifi/forget")
|
.post("/api/v1/network/wifi/forget")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -633,7 +645,7 @@ fn remove_wifi() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_password() {
|
fn new_password() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/api/v1/network/wifi/modify")
|
.post("/api/v1/network/wifi/modify")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -647,7 +659,7 @@ fn new_password() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ping_pong() {
|
fn ping_pong() {
|
||||||
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/ping")
|
.get("/api/v1/ping")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
|
@ -709,7 +721,7 @@ fn invalid_path() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_get_request() {
|
fn invalid_get_request() {
|
||||||
let client = Client::tracked(init_rocket()).unwrap();
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).unwrap();
|
||||||
|
|
||||||
// try to get a path that doesn't exist
|
// try to get a path that doesn't exist
|
||||||
let res = client
|
let res = client
|
||||||
|
|
|
@ -220,12 +220,18 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.capsule-container {
|
.capsule-container {
|
||||||
margin-left: 2rem;
|
margin-left: 1rem;
|
||||||
margin-right: 2rem;
|
margin-right: 1rem;
|
||||||
padding-top: 1rem;
|
|
||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width: 600px) {
|
||||||
|
.capsule-container {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CARDS
|
* CARDS
|
||||||
*/
|
*/
|
||||||
|
@ -235,6 +241,7 @@ body {
|
||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: 600px) {
|
@media only screen and (min-width: 600px) {
|
||||||
|
@ -248,8 +255,6 @@ body {
|
||||||
.card-container {
|
.card-container {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
/* padding-top: 1rem; */
|
|
||||||
/* padding-bottom: 1rem; */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-container {
|
.form-container {
|
||||||
|
@ -560,6 +565,7 @@ html {
|
||||||
font-size: var(--font-size-6);
|
font-size: var(--font-size-6);
|
||||||
margin-left: 2rem;
|
margin-left: 2rem;
|
||||||
margin-right: 2rem;
|
margin-right: 2rem;
|
||||||
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,16 +1,28 @@
|
||||||
/*
|
/*
|
||||||
* behavioural layer for the `change_password.html.tera` template,
|
|
||||||
*/
|
behavioural layer for the `change_password.html.tera` template
|
||||||
|
|
||||||
|
- intercept button click for save (form submission of passwords)
|
||||||
|
- perform json api call
|
||||||
|
- update the dom
|
||||||
|
|
||||||
|
methods:
|
||||||
|
|
||||||
|
PEACH_AUTH.changePassword();
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
var PEACH_AUTH = {};
|
||||||
|
|
||||||
// catch click of 'Save' button and make POST request
|
// catch click of 'Save' button and make POST request
|
||||||
PEACH.add = function() {
|
PEACH_AUTH.changePassword = function() {
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.body.addEventListener('submit', function(e) {
|
document.body.addEventListener('submit', function(e) {
|
||||||
// prevent redirect on button press (default behavior)
|
// prevent redirect on button press (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// capture form data
|
// capture form data
|
||||||
var formElement = document.querySelector("form");
|
var formElement = document.querySelector("form");
|
||||||
// create form data object from the wifiCreds form element
|
// create form data object from the changePassword form element
|
||||||
var formData = new FormData(formElement);
|
var formData = new FormData(formElement);
|
||||||
var object = {};
|
var object = {};
|
||||||
// assign values from form
|
// assign values from form
|
||||||
|
@ -22,7 +34,7 @@ PEACH.add = function() {
|
||||||
var jsonData = JSON.stringify(object);
|
var jsonData = JSON.stringify(object);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH.flashMsg("info", "Saving new password.");
|
PEACH.flashMsg("info", "Saving new password.");
|
||||||
// send add_wifi POST request
|
// send change_password POST request
|
||||||
fetch("/api/v1/admin/change_password", {
|
fetch("/api/v1/admin/change_password", {
|
||||||
method: "post",
|
method: "post",
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -41,5 +53,5 @@ PEACH.add = function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var addInstance = PEACH;
|
var changePassInstance = PEACH_AUTH;
|
||||||
addInstance.add();
|
changePassInstance.changePassword();
|
||||||
|
|
|
@ -43,5 +43,4 @@ PEACH.flashMsg = function(status, msg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var addInstance = PEACH;
|
var commonInstance = PEACH;
|
||||||
addInstance.add();
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/*
|
/*
|
||||||
|
|
||||||
behavioural layer for the `configure_dns.html.tera` template,
|
behavioural layer for the `configure_dns.html.tera` template,
|
||||||
corresponding to the web route `/network/dns`
|
corresponding to the web route `/settings/network/dns`
|
||||||
|
|
||||||
- intercept button click for add (form submission of credentials)
|
- intercept button click for save (form submission of dns settings)
|
||||||
- perform json api call
|
- perform json api call
|
||||||
- update the dom
|
- update the dom
|
||||||
|
|
||||||
|
@ -12,14 +12,14 @@ corresponding to the web route `/network/dns`
|
||||||
var PEACH_DNS = {};
|
var PEACH_DNS = {};
|
||||||
|
|
||||||
// catch click of 'Add' button and make POST request
|
// catch click of 'Add' button and make POST request
|
||||||
PEACH_DNS.add = function() {
|
PEACH_DNS.configureDns = function() {
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.body.addEventListener('submit', function(e) {
|
document.body.addEventListener('submit', function(e) {
|
||||||
// prevent redirect on button press (default behavior)
|
// prevent redirect on button press (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// capture form data
|
// capture form data
|
||||||
var formElement = document.querySelector("form");
|
var formElement = document.querySelector("form");
|
||||||
// create form data object from the wifiCreds form element
|
// create form data object from the configureDNS form element
|
||||||
var formData = new FormData(formElement);
|
var formData = new FormData(formElement);
|
||||||
var object = {};
|
var object = {};
|
||||||
// set checkbox to false (the value is only passed to formData if it is "on")
|
// set checkbox to false (the value is only passed to formData if it is "on")
|
||||||
|
@ -36,7 +36,7 @@ PEACH_DNS.add = function() {
|
||||||
console.log(object);
|
console.log(object);
|
||||||
var jsonData = JSON.stringify(object);
|
var jsonData = JSON.stringify(object);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH_DNS.flashMsg("info", "Saving new DNS configurations");
|
PEACH.flashMsg("info", "Saving new DNS configurations");
|
||||||
// send add_wifi POST request
|
// send add_wifi POST request
|
||||||
fetch("/api/v1/network/dns/configure", {
|
fetch("/api/v1/network/dns/configure", {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -50,49 +50,14 @@ PEACH_DNS.add = function() {
|
||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_DNS.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
let statusIndicator = document.getElementById("dyndns-status-indicator");
|
let statusIndicator = document.getElementById("dyndns-status-indicator");
|
||||||
statusIndicator.remove();
|
// only remove the "dyndns-status-indicator" element if it exists
|
||||||
|
if (statusIndicator != null ) statusIndicator.remove();
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// display a message by appending a paragraph element
|
var configureDnsInstance = PEACH_DNS;
|
||||||
PEACH_DNS.flashMsg = function(status, msg) {
|
configureDnsInstance.configureDns();
|
||||||
// set the class of the element according to status
|
|
||||||
var elementClass;
|
|
||||||
if (status === "success") {
|
|
||||||
elementClass = "capsule center-text flash-message font-success";
|
|
||||||
} else if (status === "info") {
|
|
||||||
elementClass = "capsule center-text flash-message font-info";
|
|
||||||
} else {
|
|
||||||
elementClass = "capsule center-text flash-message font-failure";
|
|
||||||
};
|
|
||||||
|
|
||||||
var flashElement = document.getElementById("flashMsg");
|
|
||||||
// if flashElement exists, update the class & text
|
|
||||||
if (flashElement) {
|
|
||||||
flashElement.className = elementClass;
|
|
||||||
flashElement.innerText = msg;
|
|
||||||
// if flashElement does not exist, create it, set id, class, text & append
|
|
||||||
} else {
|
|
||||||
// create new div for flash message
|
|
||||||
var flashDiv = document.createElement("DIV");
|
|
||||||
// set div attributes
|
|
||||||
flashDiv.id = "flashMsg";
|
|
||||||
flashDiv.className = elementClass;
|
|
||||||
// add json response message to flash message div
|
|
||||||
var flashMsg = document.createTextNode(msg);
|
|
||||||
flashDiv.appendChild(flashMsg);
|
|
||||||
// insert the flash message div below the button div
|
|
||||||
var buttonDiv = document.getElementById("buttonDiv");
|
|
||||||
// flashDiv will be added to the end since buttonDiv is the last
|
|
||||||
// child within the parent element (card-container div)
|
|
||||||
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var addInstance = PEACH_DNS;
|
|
||||||
addInstance.add();
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ corresponding to the web route `/network/wifi/add`
|
||||||
methods:
|
methods:
|
||||||
|
|
||||||
PEACH_NETWORK.add();
|
PEACH_NETWORK.add();
|
||||||
PEACH_NETWORK.flashMsg(status, msg);
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -34,7 +33,7 @@ PEACH_NETWORK.add = function() {
|
||||||
// perform json serialization
|
// perform json serialization
|
||||||
var jsonData = JSON.stringify(object);
|
var jsonData = JSON.stringify(object);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH_NETWORK.flashMsg("info", "Adding WiFi credentials...");
|
PEACH.flashMsg("info", "Adding WiFi credentials...");
|
||||||
// send add_wifi POST request
|
// send add_wifi POST request
|
||||||
fetch("/api/v1/network/wifi", {
|
fetch("/api/v1/network/wifi", {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -48,46 +47,11 @@ PEACH_NETWORK.add = function() {
|
||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// display a message by appending a paragraph element
|
|
||||||
PEACH_NETWORK.flashMsg = function(status, msg) {
|
|
||||||
// set the class of the element according to status
|
|
||||||
var elementClass;
|
|
||||||
if (status === "success") {
|
|
||||||
elementClass = "capsule center-text flash-message font-success";
|
|
||||||
} else if (status === "info") {
|
|
||||||
elementClass = "capsule center-text flash-message font-info";
|
|
||||||
} else {
|
|
||||||
elementClass = "capsule center-text flash-message font-failure";
|
|
||||||
};
|
|
||||||
|
|
||||||
var flashElement = document.getElementById("flashMsg");
|
|
||||||
// if flashElement exists, update the class & text
|
|
||||||
if (flashElement) {
|
|
||||||
flashElement.className = elementClass;
|
|
||||||
flashElement.innerText = msg;
|
|
||||||
// if flashElement does not exist, create it, set id, class, text & append
|
|
||||||
} else {
|
|
||||||
// create new div for flash message
|
|
||||||
var flashDiv = document.createElement("DIV");
|
|
||||||
// set div attributes
|
|
||||||
flashDiv.id = "flashMsg";
|
|
||||||
flashDiv.className = elementClass;
|
|
||||||
// add json response message to flash message div
|
|
||||||
var flashMsg = document.createTextNode(msg);
|
|
||||||
flashDiv.appendChild(flashMsg);
|
|
||||||
// insert the flash message div below the button div
|
|
||||||
var buttonDiv = document.getElementById("buttonDiv");
|
|
||||||
// flashDiv will be added to the end since buttonDiv is the last
|
|
||||||
// child within the parent element (card-container div)
|
|
||||||
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var addInstance = PEACH_NETWORK;
|
var addInstance = PEACH_NETWORK;
|
||||||
addInstance.add();
|
addInstance.add();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
|
|
||||||
behavioural layer for the `network_card.html.tera` template,
|
behavioural layer for the `network_card.html.tera` template,
|
||||||
corresponding to the web route `/network`
|
corresponding to the web route `/settings/network`
|
||||||
|
|
||||||
- intercept form submissions
|
- intercept form submissions
|
||||||
- perform json api calls
|
- perform json api calls
|
||||||
|
@ -11,10 +11,8 @@ methods:
|
||||||
|
|
||||||
PEACH_NETWORK.activateAp();
|
PEACH_NETWORK.activateAp();
|
||||||
PEACH_NETWORK.activateClient();
|
PEACH_NETWORK.activateClient();
|
||||||
PEACH_NETWORK.apOnline();
|
PEACH_NETWORK.apMode();
|
||||||
PEACH_NETWORK.clientOffline();
|
PEACH_NETWORK.clientMode();
|
||||||
PEACH_NETWORK.clientOnline();
|
|
||||||
PEACH_NETWORK.flashMsg(status, msg);
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -29,7 +27,7 @@ PEACH_NETWORK.activateAp = function() {
|
||||||
// prevent form submission (default behavior)
|
// prevent form submission (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH_NETWORK.flashMsg("info", "Deploying access point...");
|
PEACH.flashMsg("info", "Deploying access point...");
|
||||||
// send activate_ap POST request
|
// send activate_ap POST request
|
||||||
fetch("/api/v1/network/activate_ap", {
|
fetch("/api/v1/network/activate_ap", {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -44,10 +42,10 @@ PEACH_NETWORK.activateAp = function() {
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
console.log(jsonData.msg);
|
console.log(jsonData.msg);
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
// if ap activation is successful, update the ui
|
// if ap activation is successful, update the ui
|
||||||
if (jsonData.status === "success") {
|
if (jsonData.status === "success") {
|
||||||
PEACH_NETWORK.apOnline();
|
PEACH_NETWORK.apMode();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
|
@ -64,7 +62,7 @@ PEACH_NETWORK.activateClient = function() {
|
||||||
// prevent form submission (default behavior)
|
// prevent form submission (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH_NETWORK.flashMsg("info", "Enabling WiFi client...");
|
PEACH.flashMsg("info", "Enabling WiFi client...");
|
||||||
// send activate_ap POST request
|
// send activate_ap POST request
|
||||||
fetch("/api/v1/network/activate_client", {
|
fetch("/api/v1/network/activate_client", {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -79,10 +77,10 @@ PEACH_NETWORK.activateClient = function() {
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
console.log(jsonData.msg);
|
console.log(jsonData.msg);
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
// if client activation is successful, update the ui
|
// if client activation is successful, update the ui
|
||||||
if (jsonData.status === "success") {
|
if (jsonData.status === "success") {
|
||||||
PEACH_NETWORK.clientOnline();
|
PEACH_NETWORK.clientMode();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
|
@ -90,21 +88,12 @@ PEACH_NETWORK.activateClient = function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// update ui for access point mode (status: online)
|
// replace 'Deploy Access Point' button with 'Enable WiFi' button
|
||||||
PEACH_NETWORK.apOnline = function() {
|
PEACH_NETWORK.apMode = function() {
|
||||||
console.log('Activating AP Mode');
|
|
||||||
|
|
||||||
// update network mode and status (icon & label)
|
|
||||||
let i = document.getElementById("netModeIcon");
|
|
||||||
i.className = "center icon icon-active";
|
|
||||||
i.src = "icons/router.svg";
|
|
||||||
let l = document.getElementById("netModeLabel");
|
|
||||||
l.textContent = "ONLINE";
|
|
||||||
|
|
||||||
// create Enable WiFi button and add it to button div
|
// create Enable WiFi button and add it to button div
|
||||||
var wifiButton = document.createElement("A");
|
var wifiButton = document.createElement("A");
|
||||||
wifiButton.className = "button center";
|
wifiButton.className = "button center";
|
||||||
wifiButton.href = "/network/wifi/activate";
|
wifiButton.href = "/settings/network/wifi/activate";
|
||||||
wifiButton.id = "connectWifi";
|
wifiButton.id = "connectWifi";
|
||||||
var label = "Enable WiFi";
|
var label = "Enable WiFi";
|
||||||
var buttonText = document.createTextNode(label);
|
var buttonText = document.createTextNode(label);
|
||||||
|
@ -114,88 +103,31 @@ PEACH_NETWORK.apOnline = function() {
|
||||||
let buttons = document.getElementById("buttons");
|
let buttons = document.getElementById("buttons");
|
||||||
buttons.appendChild(wifiButton);
|
buttons.appendChild(wifiButton);
|
||||||
|
|
||||||
// remove the old 'Activate Access Point' button
|
// remove the old 'Deploy Access Point' button
|
||||||
let apButton = document.getElementById("deployAccessPoint");
|
let apButton = document.getElementById("deployAccessPoint");
|
||||||
apButton.style = "display: none;";
|
apButton.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
// update ui for wifi client mode (status: online)
|
// replace 'Enable WiFi' button with 'Deploy Access Point' button
|
||||||
PEACH_NETWORK.clientOnline = function() {
|
PEACH_NETWORK.clientMode = function() {
|
||||||
console.log('Activating Client Mode');
|
// create Deploy Access Point button and add it to button div
|
||||||
|
var apButton = document.createElement("A");
|
||||||
|
apButton.className = "button center";
|
||||||
|
apButton.href = "/settings/network/ap/activate";
|
||||||
|
apButton.id = "deployAccessPoint";
|
||||||
|
var label = "Deploy Access Point";
|
||||||
|
var buttonText = document.createTextNode(label);
|
||||||
|
apButton.appendChild(buttonText);
|
||||||
|
|
||||||
// update network mode and status (icon & label)
|
// append the new button to the buttons div
|
||||||
let i = document.getElementById("netModeIcon");
|
let buttons = document.getElementById("buttons");
|
||||||
i.className = "center icon icon-active";
|
buttons.appendChild(apButton);
|
||||||
i.src = "icons/wifi.svg";
|
|
||||||
let l = document.getElementById("netModeLabel");
|
|
||||||
l.textContent = "ONLINE";
|
|
||||||
|
|
||||||
// TODO: think about updates for buttons (transition from ap mode)
|
// remove the old 'Enable Wifi' button
|
||||||
}
|
let wifiButton = document.getElementById("connectWifi");
|
||||||
|
wifiButton.remove();
|
||||||
// update ui for wifi client mode (status: offline)
|
|
||||||
PEACH_NETWORK.clientOffline = function() {
|
|
||||||
console.log('Activating Client Mode');
|
|
||||||
|
|
||||||
// update network mode and status (icon & label)
|
|
||||||
let i = document.getElementById("netModeIcon");
|
|
||||||
i.className = "center icon icon-inactive";
|
|
||||||
i.src = "icons/wifi.svg";
|
|
||||||
let l = document.getElementById("netModeLabel");
|
|
||||||
l.textContent = "OFFLINE";
|
|
||||||
|
|
||||||
// TODO: think about updates for buttons (transition from ap mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// display a message by appending a paragraph element
|
|
||||||
PEACH_NETWORK.flashMsg = function(status, msg) {
|
|
||||||
// set the class of the element according to status
|
|
||||||
var elementClass;
|
|
||||||
if (status === "success") {
|
|
||||||
elementClass = "capsule center-text flash-message font-success";
|
|
||||||
} else if (status === "info") {
|
|
||||||
elementClass = "capsule center-text flash-message font-info";
|
|
||||||
} else {
|
|
||||||
elementClass = "capsule center-text flash-message font-failure";
|
|
||||||
};
|
|
||||||
|
|
||||||
var flashElement = document.getElementById("flashMsg");
|
|
||||||
// if flashElement exists, update the class & text
|
|
||||||
if (flashElement) {
|
|
||||||
flashElement.className = elementClass;
|
|
||||||
flashElement.innerText = msg;
|
|
||||||
// if flashElement does not exist, create it, set id, class, text & append
|
|
||||||
} else {
|
|
||||||
// create new div for flash message
|
|
||||||
var flashDiv = document.createElement("DIV");
|
|
||||||
// set div attributes
|
|
||||||
flashDiv.id = "flashMsg";
|
|
||||||
flashDiv.className = elementClass;
|
|
||||||
// add json response message to flash message div
|
|
||||||
var flashMsg = document.createTextNode(msg);
|
|
||||||
flashDiv.appendChild(flashMsg);
|
|
||||||
// insert the flash message div above the three icon grid div
|
|
||||||
var gridDiv = document.getElementById("gridDiv");
|
|
||||||
gridDiv.parentNode.insertBefore(flashDiv, gridDiv);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var networkInstance = PEACH_NETWORK;
|
var networkInstance = PEACH_NETWORK;
|
||||||
networkInstance.activateAp();
|
networkInstance.activateAp();
|
||||||
networkInstance.activateClient();
|
networkInstance.activateClient();
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
async function exampleFetch() {
|
|
||||||
const response = await fetch('/api/v1/network/state');
|
|
||||||
const myJson = await response.json();
|
|
||||||
//const jsonData = JSON.parse(myJson);
|
|
||||||
console.log(myJson.data.wlan0);
|
|
||||||
//var state = document.createElement("P");
|
|
||||||
//state.innerText = ""jsonData.wlan0;
|
|
||||||
//document.body.appendChild(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
exampleFetch()
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
|
|
||||||
behavioural layer for the `network_detail.html.tera` template,
|
behavioural layer for the `network_detail.html.tera` template,
|
||||||
corresponding to the web route `/network/wifi?<ssid>`
|
corresponding to the web route `/settings/network/wifi?<ssid>`
|
||||||
|
|
||||||
- intercept button clicks for connect, disconnect and forget
|
- intercept button clicks for connect, disconnect and forget
|
||||||
- perform json api call
|
- perform json api call
|
||||||
|
@ -12,7 +12,6 @@ methods:
|
||||||
PEACH_NETWORK.connect();
|
PEACH_NETWORK.connect();
|
||||||
PEACH_NETWORK.disconnect();
|
PEACH_NETWORK.disconnect();
|
||||||
PEACH_NETWORK.forget();
|
PEACH_NETWORK.forget();
|
||||||
PEACH_NETWORK.flashMsg(status, msg);
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -33,7 +32,7 @@ PEACH_NETWORK.connect = function() {
|
||||||
// perform json serialization
|
// perform json serialization
|
||||||
var jsonData = JSON.stringify(ssidData);
|
var jsonData = JSON.stringify(ssidData);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH_NETWORK.flashMsg("info", "Connecting to access point...");
|
PEACH.flashMsg("info", "Connecting to access point...");
|
||||||
// send add_wifi POST request
|
// send add_wifi POST request
|
||||||
fetch("/api/v1/network/wifi/connect", {
|
fetch("/api/v1/network/wifi/connect", {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -47,7 +46,7 @@ PEACH_NETWORK.connect = function() {
|
||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
};
|
};
|
||||||
|
@ -69,7 +68,7 @@ PEACH_NETWORK.disconnect = function() {
|
||||||
// perform json serialization
|
// perform json serialization
|
||||||
var jsonData = JSON.stringify(ssidData);
|
var jsonData = JSON.stringify(ssidData);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH_NETWORK.flashMsg("info", "Disconnecting from access point...");
|
PEACH.flashMsg("info", "Disconnecting from access point...");
|
||||||
// send disconnect_wifi POST request
|
// send disconnect_wifi POST request
|
||||||
fetch("/api/v1/network/wifi/disconnect", {
|
fetch("/api/v1/network/wifi/disconnect", {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -83,7 +82,7 @@ PEACH_NETWORK.disconnect = function() {
|
||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
};
|
};
|
||||||
|
@ -105,7 +104,7 @@ PEACH_NETWORK.forget = function() {
|
||||||
// perform json serialization
|
// perform json serialization
|
||||||
var jsonData = JSON.stringify(ssidData);
|
var jsonData = JSON.stringify(ssidData);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH_NETWORK.flashMsg("info", "Removing credentials for access point...");
|
PEACH.flashMsg("info", "Removing credentials for access point...");
|
||||||
// send forget_ap POST request
|
// send forget_ap POST request
|
||||||
fetch("/api/v1/network/wifi/forget", {
|
fetch("/api/v1/network/wifi/forget", {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -119,48 +118,13 @@ PEACH_NETWORK.forget = function() {
|
||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// display a message by appending a paragraph element
|
|
||||||
PEACH_NETWORK.flashMsg = function(status, msg) {
|
|
||||||
// set the class of the element according to status
|
|
||||||
var elementClass;
|
|
||||||
if (status === "success") {
|
|
||||||
elementClass = "capsule center-text flash-message font-success";
|
|
||||||
} else if (status === "info") {
|
|
||||||
elementClass = "capsule center-text flash-message font-info";
|
|
||||||
} else {
|
|
||||||
elementClass = "capsule center-text flash-message font-failure";
|
|
||||||
};
|
|
||||||
|
|
||||||
var flashElement = document.getElementById("flashMsg");
|
|
||||||
// if flashElement exists, update the class & text
|
|
||||||
if (flashElement) {
|
|
||||||
flashElement.className = elementClass;
|
|
||||||
flashElement.innerText = msg;
|
|
||||||
// if flashElement does not exist, create it, set id, class, text & append
|
|
||||||
} else {
|
|
||||||
// create new div for flash message
|
|
||||||
var flashDiv = document.createElement("DIV");
|
|
||||||
// set div attributes
|
|
||||||
flashDiv.id = "flashMsg";
|
|
||||||
flashDiv.className = elementClass;
|
|
||||||
// add json response message to flash message div
|
|
||||||
var flashMsg = document.createTextNode(msg);
|
|
||||||
flashDiv.appendChild(flashMsg);
|
|
||||||
// insert the flash message div below the button div
|
|
||||||
var buttonDiv = document.getElementById("buttonDiv");
|
|
||||||
// flashDiv will be added to the end since buttonDiv is the last
|
|
||||||
// child within the parent element (card-container div)
|
|
||||||
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var detailInstance = PEACH_NETWORK;
|
var detailInstance = PEACH_NETWORK;
|
||||||
detailInstance.connect();
|
detailInstance.connect();
|
||||||
detailInstance.disconnect();
|
detailInstance.disconnect();
|
||||||
|
|
|
@ -9,7 +9,6 @@ behavioural layer for the `network_modify.html.tera` template
|
||||||
methods:
|
methods:
|
||||||
|
|
||||||
PEACH_NETWORK.modify();
|
PEACH_NETWORK.modify();
|
||||||
PEACH_NETWORK.flashMsg(status, msg);
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -33,7 +32,7 @@ PEACH_NETWORK.modify = function() {
|
||||||
// perform json serialization
|
// perform json serialization
|
||||||
var jsonData = JSON.stringify(object);
|
var jsonData = JSON.stringify(object);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH_NETWORK.flashMsg("info", "Updating WiFi password...");
|
PEACH.flashMsg("info", "Updating WiFi password...");
|
||||||
// send new_password POST request
|
// send new_password POST request
|
||||||
fetch("/api/v1/network/wifi/modify", {
|
fetch("/api/v1/network/wifi/modify", {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -47,46 +46,11 @@ PEACH_NETWORK.modify = function() {
|
||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// display a message by appending a paragraph element
|
|
||||||
PEACH_NETWORK.flashMsg = function(status, msg) {
|
|
||||||
// set the class of the element according to status
|
|
||||||
var elementClass;
|
|
||||||
if (status === "success") {
|
|
||||||
elementClass = "capsule center-text flash-message font-success";
|
|
||||||
} else if (status === "info") {
|
|
||||||
elementClass = "capsule center-text flash-message font-info";
|
|
||||||
} else {
|
|
||||||
elementClass = "capsule center-text flash-message font-failure";
|
|
||||||
};
|
|
||||||
|
|
||||||
var flashElement = document.getElementById("flashMsg");
|
|
||||||
// if flashElement exists, update the class & text
|
|
||||||
if (flashElement) {
|
|
||||||
flashElement.className = elementClass;
|
|
||||||
flashElement.innerText = msg;
|
|
||||||
// if flashElement does not exist, create it, set id, class, text & append
|
|
||||||
} else {
|
|
||||||
// create new div for flash message
|
|
||||||
var flashDiv = document.createElement("DIV");
|
|
||||||
// set div attributes
|
|
||||||
flashDiv.id = "flashMsg";
|
|
||||||
flashDiv.className = elementClass;
|
|
||||||
// add json response message to flash message div
|
|
||||||
var flashMsg = document.createTextNode(msg);
|
|
||||||
flashDiv.appendChild(flashMsg);
|
|
||||||
// insert the flash message div below the button div
|
|
||||||
var buttonDiv = document.getElementById("buttonDiv");
|
|
||||||
// flashDiv will be added to the end since buttonDiv is the last
|
|
||||||
// child within the parent element (card-container div)
|
|
||||||
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var modifyInstance = PEACH_NETWORK;
|
var modifyInstance = PEACH_NETWORK;
|
||||||
modifyInstance.modify();
|
modifyInstance.modify();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
|
|
||||||
behavioural layer for the `network_usage.html.tera` template,
|
behavioural layer for the `network_usage.html.tera` template,
|
||||||
corresponding to the web route `/network/wifi/usage`
|
corresponding to the web route `/settings/network/wifi/usage`
|
||||||
|
|
||||||
- intercept form submissions
|
- intercept form submissions
|
||||||
- perform json api calls
|
- perform json api calls
|
||||||
|
@ -13,7 +13,6 @@ methods:
|
||||||
PEACH_NETWORK.resetUsage();
|
PEACH_NETWORK.resetUsage();
|
||||||
PEACH_NETWORK.toggleWarning();
|
PEACH_NETWORK.toggleWarning();
|
||||||
PEACH_NETWORK.toggleCutoff();
|
PEACH_NETWORK.toggleCutoff();
|
||||||
PEACH_NETWORK.flashMsg(status, msg);
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ PEACH_NETWORK.updateAlerts = function() {
|
||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
|
@ -79,7 +78,7 @@ PEACH_NETWORK.resetUsage = function() {
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
console.log(jsonData.msg);
|
console.log(jsonData.msg);
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
// if reset is successful, update the ui
|
// if reset is successful, update the ui
|
||||||
if (jsonData.status === "success") {
|
if (jsonData.status === "success") {
|
||||||
console.log(jsonData.data);
|
console.log(jsonData.data);
|
||||||
|
@ -133,39 +132,6 @@ PEACH_NETWORK.toggleCutoff = function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// display a message by appending a paragraph element
|
|
||||||
PEACH_NETWORK.flashMsg = function(status, msg) {
|
|
||||||
// set the class of the element according to status
|
|
||||||
var elementClass;
|
|
||||||
if (status === "success") {
|
|
||||||
elementClass = "capsule center-text flash-message font-success";
|
|
||||||
} else if (status === "info") {
|
|
||||||
elementClass = "capsule center-text flash-message font-info";
|
|
||||||
} else {
|
|
||||||
elementClass = "capsule center-text flash-message font-failure";
|
|
||||||
};
|
|
||||||
|
|
||||||
var flashElement = document.getElementById("flashMsg");
|
|
||||||
// if flashElement exists, update the class & text
|
|
||||||
if (flashElement) {
|
|
||||||
flashElement.className = elementClass;
|
|
||||||
flashElement.innerText = msg;
|
|
||||||
// if flashElement does not exist, create it, set id, class, text & append
|
|
||||||
} else {
|
|
||||||
// create new div for flash message
|
|
||||||
var flashDiv = document.createElement("DIV");
|
|
||||||
// set div attributes
|
|
||||||
flashDiv.id = "flashMsg";
|
|
||||||
flashDiv.className = elementClass;
|
|
||||||
// add json response message to flash message div
|
|
||||||
var flashMsg = document.createTextNode(msg);
|
|
||||||
flashDiv.appendChild(flashMsg);
|
|
||||||
// insert the flash message div below the button div
|
|
||||||
var buttonDiv = document.getElementById("buttonDiv");
|
|
||||||
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var usageInstance = PEACH_NETWORK;
|
var usageInstance = PEACH_NETWORK;
|
||||||
usageInstance.resetUsage();
|
usageInstance.resetUsage();
|
||||||
usageInstance.toggleWarning();
|
usageInstance.toggleWarning();
|
||||||
|
|
|
@ -11,7 +11,6 @@ methods:
|
||||||
|
|
||||||
PEACH_DEVICE.reboot();
|
PEACH_DEVICE.reboot();
|
||||||
PEACH_DEVICE.shutdown();
|
PEACH_DEVICE.shutdown();
|
||||||
PEACH_DEVICE.flashMsg(status, msg);
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -26,7 +25,7 @@ PEACH_DEVICE.reboot = function() {
|
||||||
// prevent redirect on button press (default behavior)
|
// prevent redirect on button press (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// write reboot flash message
|
// write reboot flash message
|
||||||
PEACH_DEVICE.flashMsg("success", "Rebooting the device...");
|
PEACH.flashMsg("success", "Rebooting the device...");
|
||||||
// send reboot_device POST request
|
// send reboot_device POST request
|
||||||
fetch("/api/v1/admin/reboot", {
|
fetch("/api/v1/admin/reboot", {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -41,7 +40,7 @@ PEACH_DEVICE.reboot = function() {
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
console.log(jsonData.msg);
|
console.log(jsonData.msg);
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_DEVICE.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
|
@ -57,7 +56,7 @@ PEACH_DEVICE.shutdown = function() {
|
||||||
// prevent form submission (default behavior)
|
// prevent form submission (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// write shutdown flash message
|
// write shutdown flash message
|
||||||
PEACH_DEVICE.flashMsg("success", "Shutting down the device...");
|
PEACH.flashMsg("success", "Shutting down the device...");
|
||||||
// send shutdown_device POST request
|
// send shutdown_device POST request
|
||||||
fetch("/api/v1/shutdown", {
|
fetch("/api/v1/shutdown", {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -72,48 +71,13 @@ PEACH_DEVICE.shutdown = function() {
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
console.log(jsonData.msg);
|
console.log(jsonData.msg);
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH_DEVICE.flashMsg(jsonData.status, jsonData.msg);
|
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// display a message by appending a paragraph element
|
|
||||||
PEACH_DEVICE.flashMsg = function(status, msg) {
|
|
||||||
// set the class of the element according to status
|
|
||||||
var elementClass;
|
|
||||||
if (status === "success") {
|
|
||||||
elementClass = "capsule center-text flash-message font-success";
|
|
||||||
} else if (status === "info") {
|
|
||||||
elementClass = "capsule center-text flash-message font-info";
|
|
||||||
} else {
|
|
||||||
elementClass = "capsule center-text flash-message font-failure";
|
|
||||||
};
|
|
||||||
|
|
||||||
var flashElement = document.getElementById("flashMsg");
|
|
||||||
// if flashElement exists, update the class & text
|
|
||||||
if (flashElement) {
|
|
||||||
flashElement.className = elementClass;
|
|
||||||
flashElement.innerText = msg;
|
|
||||||
// if flashElement does not exist, create it, set id, class, text & append
|
|
||||||
} else {
|
|
||||||
// create new div for flash message
|
|
||||||
var flashDiv = document.createElement("DIV");
|
|
||||||
// set div attributes
|
|
||||||
flashDiv.id = "flashMsg";
|
|
||||||
flashDiv.className = elementClass;
|
|
||||||
// add json response message to flash message div
|
|
||||||
var flashMsg = document.createTextNode(msg);
|
|
||||||
flashDiv.appendChild(flashMsg);
|
|
||||||
// insert the flash message div below the button div
|
|
||||||
var buttonDiv = document.getElementById("buttonDiv");
|
|
||||||
// flashDiv will be added to the end since buttonDiv is the last
|
|
||||||
// child within the parent element (card-container div)
|
|
||||||
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var deviceInstance = PEACH_DEVICE;
|
var deviceInstance = PEACH_DEVICE;
|
||||||
deviceInstance.reboot();
|
deviceInstance.reboot();
|
||||||
deviceInstance.shutdown();
|
deviceInstance.shutdown();
|
||||||
|
|
|
@ -2,15 +2,17 @@
|
||||||
* behavioural layer for the `reset_password.html.tera` template,
|
* behavioural layer for the `reset_password.html.tera` template,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var PEACH_AUTH = {};
|
||||||
|
|
||||||
// catch click of 'Save' button and make POST request
|
// catch click of 'Save' button and make POST request
|
||||||
PEACH.add = function() {
|
PEACH_AUTH.resetPassword = function() {
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.body.addEventListener('submit', function(e) {
|
document.body.addEventListener('submit', function(e) {
|
||||||
// prevent redirect on button press (default behavior)
|
// prevent redirect on button press (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// capture form data
|
// capture form data
|
||||||
var formElement = document.querySelector("form");
|
var formElement = document.querySelector("form");
|
||||||
// create form data object from the wifiCreds form element
|
// create form data object from the changePassword form element
|
||||||
var formData = new FormData(formElement);
|
var formData = new FormData(formElement);
|
||||||
var object = {};
|
var object = {};
|
||||||
// assign values from form
|
// assign values from form
|
||||||
|
@ -41,5 +43,5 @@ PEACH.add = function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var addInstance = PEACH;
|
var resetPassInstance = PEACH_AUTH;
|
||||||
addInstance.add();
|
resetPassInstance.resetPassword();
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
{%- extends "nav" -%}
|
{%- extends "nav" -%}
|
||||||
{%- block card %}
|
{%- block card %}
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="card-container capsule info-border">
|
<div class="capsule-container">
|
||||||
<p>No PeachCloud resource exists for this URL. Please ensure that the URL in the address bar is correct.</p>
|
<div class="capsule info-border">
|
||||||
<p>Click the back arrow in the top-left or the PeachCloud logo at the bottom of your screen to return Home.</p>
|
<p>No PeachCloud resource exists for this URL. Please ensure that the URL in the address bar is correct.</p>
|
||||||
</div>
|
<p>Click the back arrow in the top-left or the PeachCloud logo at the bottom of your screen to return Home.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -4,47 +4,22 @@
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<form id="changePassword" action="/settings/change_password" method="post">
|
<form id="changePassword" action="/settings/change_password" method="post">
|
||||||
<div class="input-wrapper">
|
<!-- input for current password -->
|
||||||
<!-- input for old password -->
|
<input id="currentPassword" class="center input" name="current_password" type="password" placeholder="Current password" title="Current password" autofocus>
|
||||||
<label id="old_password" class="label-small input-label font-near-black">
|
<!-- input for new password -->
|
||||||
<label class="label-small input-label font-gray" for="old_password" style="padding-top: 0.25rem;">Old Password</label>
|
<input id="newPassword" class="center input" name="new_password1" type="password" placeholder="New password" title="New password">
|
||||||
<input id="old_password" class="form-input" style="margin-bottom: 0;"
|
<!-- input for duplicate new password -->
|
||||||
name="old_password" type="password" title="old password" value="">
|
<input id="newPasswordDuplicate" class="center input" name="new_password2" type="password" placeholder="Re-enter new password" title="New password duplicate">
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-wrapper">
|
|
||||||
<!-- input for new password1 -->
|
|
||||||
<label id="new_password1" class="label-small input-label font-near-black">
|
|
||||||
<label class="label-small input-label font-gray" for="new_password1" style="padding-top: 0.25rem;">Enter New Password</label>
|
|
||||||
<input id="new_password1" class="form-input" style="margin-bottom: 0;"
|
|
||||||
name="new_password1" title="new_password1" type="password" value="">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-wrapper">
|
|
||||||
<!-- input for new password2 -->
|
|
||||||
<label id="new_password2" class="label-small input-label font-near-black">
|
|
||||||
<label class="label-small input-label font-gray" for="new_password2" style="padding-top: 0.25rem;">Re-Enter New Password</label>
|
|
||||||
<input id="new_password2" class="form-input" style="margin-bottom: 0;"
|
|
||||||
name="new_password2" title="new_password2" type="password" value="">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="buttonDiv">
|
<div id="buttonDiv">
|
||||||
<input id="changePasswordButton" class="button button-primary center" title="Add" type="submit" value="Save">
|
<input id="savePassword" class="button button-primary center" title="Add" type="submit" value="Save">
|
||||||
|
<a class="button button-secondary center" href="/settings/admin" title="Cancel">Cancel</a>
|
||||||
</div>
|
</div>
|
||||||
<a class="button button-secondary center" href="/settings/admin" title="Cancel">Cancel</a>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- FLASH MESSAGE -->
|
<!-- FLASH MESSAGE -->
|
||||||
{% include "snippets/flash_message" %}
|
{% include "snippets/flash_message" %}
|
||||||
|
|
||||||
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
||||||
{% include "snippets/noscript" %}
|
{% include "snippets/noscript" %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript" src="/js/change_password.js"></script>
|
<script type="text/javascript" src="/js/change_password.js"></script>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -1,25 +1,20 @@
|
||||||
{%- extends "nav" -%}
|
{%- extends "nav" -%}
|
||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!--PUBLIC PAGE FOR SENDING A NEW TEMPORARY PASSWORD TO BE USED TO RESET YOUR PASSWORD -->
|
<!-- PASSWORD RESET REQUEST CARD -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<p class="text-notice" style="width: 80%; margin:auto; margin-bottom: 35px; margin-top: 20px;">
|
<div class="capsule capsule-container info-border">
|
||||||
Click the button below to send a new temporary password which can be used to change your device password.
|
<p class="card-text">Click the button below to send a new temporary password which can be used to change your device password.
|
||||||
<br/><br/>
|
</br></br>
|
||||||
The temporary password will be sent in an SSB private message to the admin of this device.
|
The temporary password will be sent in an SSB private message to the admin of this device.</p>
|
||||||
</p>
|
</div>
|
||||||
|
|
||||||
<form id="sendPasswordReset" action="/send_password_reset" method="post">
|
<form id="sendPasswordReset" action="/send_password_reset" method="post">
|
||||||
<div id="buttonDiv">
|
<div id="buttonDiv">
|
||||||
<input type="submit" class="button center button-secondary" value="Send Password Reset" title="Send Password Reset Link"/>
|
<input class="button button-primary center" style="margin-top: 1rem;" type="submit" value="Send Password Reset" title="Send Password Reset Link"/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- FLASH MESSAGE -->
|
<!-- FLASH MESSAGE -->
|
||||||
{% include "snippets/flash_message" %}
|
{% include "snippets/flash_message" %}
|
||||||
|
|
||||||
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
||||||
{% include "snippets/noscript" %}
|
{% include "snippets/noscript" %}
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -2,12 +2,10 @@
|
||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!-- ADMIN SETTINGS MENU -->
|
<!-- ADMIN SETTINGS MENU -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="card-container">
|
<!-- BUTTONS -->
|
||||||
<!-- BUTTONS -->
|
<div id="settingsButtons">
|
||||||
<div id="settingsButtons">
|
<a id="change" class="button button-primary center" href="/settings/admin/change_password" title="Change Password">Change Password</a>
|
||||||
<a id="change" class="button button-primary center" href="/settings/admin/change_password" title="Change Password">Change Password</a>
|
<a id="configure" class="button button-primary center" href="/settings/admin/configure" title="Configure Admin">Configure Admin</a>
|
||||||
<a id="configure" class="button button-primary center" href="/settings/admin/configure" title="Configure Admin">Configure Admin</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -2,13 +2,11 @@
|
||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!-- SETTINGS MENU -->
|
<!-- SETTINGS MENU -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="card-container">
|
<!-- BUTTONS -->
|
||||||
<!-- BUTTONS -->
|
<div id="settingsButtons">
|
||||||
<div id="settingsButtons">
|
<a id="network" class="button button-primary center" href="/settings/network" title="Network Settings">Network</a>
|
||||||
<a id="network" class="button button-primary center" href="/settings/network" title="Network Settings">Network</a>
|
<a id="scuttlebutt" class="button button-primary center" href="/settings/scuttlebutt" title="Scuttlebutt Settings">Scuttlebutt</a>
|
||||||
<a id="scuttlebutt" class="button button-primary center" href="/settings/scuttlebutt" title="Scuttlebutt Settings">Scuttlebutt</a>
|
<a id="admin" class="button button-primary center" href="/settings/admin" title="Administrator Settings">Administration</a>
|
||||||
<a id="admin" class="button button-primary center" href="/settings/admin" title="Administrator Settings">Administration</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -2,19 +2,17 @@
|
||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!-- SCUTTLEBUTT SETTINGS MENU -->
|
<!-- SCUTTLEBUTT SETTINGS MENU -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="card-container">
|
<!-- BUTTONS -->
|
||||||
<!-- BUTTONS -->
|
<div id="settingsButtons">
|
||||||
<div id="settingsButtons">
|
<a id="networkKey" class="button button-primary center" href="/settings/scuttlebutt/network_key" title="Set Network Key">Set Network Key</a>
|
||||||
<a id="networkKey" class="button button-primary center" href="/settings/scuttlebutt/network_key" title="Set Network Key">Set Network Key</a>
|
<a id="replicationHops" class="button button-primary center" href="/settings/scuttlebutt/hops" title="Set Replication Hops">Set Replication Hops</a>
|
||||||
<a id="replicationHops" class="button button-primary center" href="/settings/scuttlebutt/hops" title="Set Replication Hops">Set Replication Hops</a>
|
<a id="removeFeeds" class="button button-primary center" href="/settings/scuttlebutt/remove_feeds" title="Remove Blocked Feeds">Remove Blocked Feeds</a>
|
||||||
<a id="removeFeeds" class="button button-primary center" href="/settings/scuttlebutt/remove_feeds" title="Remove Blocked Feeds">Remove Blocked Feeds</a>
|
<a id="setDirectory" class="button button-primary center" href="/settings/scuttlebutt/set_directory" title="Set Database Directory">Set Database Directory</a>
|
||||||
<a id="setDirectory" class="button button-primary center" href="/settings/scuttlebutt/set_directory" title="Set Database Directory">Set Database Directory</a>
|
<a id="checkFilesystem" class="button button-primary center" href="/settings/scuttlebutt/check_fs" title="Check Filesystem">Check Filesystem</a>
|
||||||
<a id="checkFilesystem" class="button button-primary center" href="/settings/scuttlebutt/check_fs" title="Check Filesystem">Check Filesystem</a>
|
<a id="repairFilesystem" class="button button-primary center" href="/settings/scuttlebutt/repair" title="Repair Filesystem">Repair Filesystem</a>
|
||||||
<a id="repairFilesystem" class="button button-primary center" href="/settings/scuttlebutt/repair" title="Repair Filesystem">Repair Filesystem</a>
|
<a id="disable" class="button button-primary center" href="/settings/scuttlebutt/disable" title="Disable Sbot">Disable Sbot</a>
|
||||||
<a id="disable" class="button button-primary center" href="/settings/scuttlebutt/disable" title="Disable Sbot">Disable Sbot</a>
|
<a id="enable" class="button button-primary center" href="/settings/scuttlebutt/enable" title="Enable Sbot">Enable Sbot</a>
|
||||||
<a id="enable" class="button button-primary center" href="/settings/scuttlebutt/enable" title="Enable Sbot">Enable Sbot</a>
|
<a id="restart" class="button button-primary center" href="/settings/scuttlebutt/restart" title="Restart Sbot">Restart Sbot</a>
|
||||||
<a id="restart" class="button button-primary center" href="/settings/scuttlebutt/restart" title="Restart Sbot">Restart Sbot</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
Loading…
Reference in New Issue