Merge pull request 'Wide range of web improvements' (#70) from web_improvements into main
Reviewed-on: #70
This commit is contained in:
commit
5d75aebf0d
|
@ -27,7 +27,6 @@ assets = [
|
||||||
["static/css/*", "/usr/share/peach-web/static/css/", "644"],
|
["static/css/*", "/usr/share/peach-web/static/css/", "644"],
|
||||||
["static/icons/*", "/usr/share/peach-web/static/icons/", "644"],
|
["static/icons/*", "/usr/share/peach-web/static/icons/", "644"],
|
||||||
["static/images/*", "/usr/share/peach-web/static/images/", "644"],
|
["static/images/*", "/usr/share/peach-web/static/images/", "644"],
|
||||||
["static/js/*", "/usr/share/peach-web/static/js/", "644"],
|
|
||||||
["README.md", "/usr/share/doc/peach-web/README", "644"],
|
["README.md", "/usr/share/doc/peach-web/README", "644"],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -37,18 +36,15 @@ maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
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" }
|
||||||
peach-network = { path = "../peach-network", features = ["serde_support"] }
|
peach-network = { path = "../peach-network", features = ["serde_support"] }
|
||||||
peach-stats = { path = "../peach-stats", features = ["serde_support"] }
|
peach-stats = { path = "../peach-stats", features = ["serde_support"] }
|
||||||
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"
|
|
||||||
tera = { version = "1.12.1", features = ["builtins"] }
|
tera = { version = "1.12.1", features = ["builtins"] }
|
||||||
xdg = "2.2.0"
|
xdg = "2.2.0"
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ Move into the repo and compile:
|
||||||
|
|
||||||
Run the tests:
|
Run the tests:
|
||||||
|
|
||||||
`cargo test`
|
`ROCKET_DISABLE_AUTH=true PEACH_STANDALONE_MODE=false cargo test`
|
||||||
|
|
||||||
Move back to the `peach-workspace` directory:
|
Move back to the `peach-workspace` directory:
|
||||||
|
|
||||||
|
@ -35,8 +35,6 @@ Run the binary:
|
||||||
|
|
||||||
`./target/release/peach-web`
|
`./target/release/peach-web`
|
||||||
|
|
||||||
_Note: Networking functionality requires peach-network microservice to be running._
|
|
||||||
|
|
||||||
### Environment
|
### Environment
|
||||||
|
|
||||||
**Deployment Mode**
|
**Deployment Mode**
|
||||||
|
@ -47,6 +45,10 @@ The web application deployment mode is configured with the `ROCKET_ENV` environm
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
**Configuration Mode**
|
||||||
|
|
||||||
|
The web application can be run with a minimal set of routes and functionality (PeachPub - a simple sbot manager) or with the full-suite of capabilities, including network management and access to device statistics (PeachCloud). The mode is configured with the `PEACH_STANDALONE_MODE` environment variable: `true` or `false`. If the variable is unset or the value is incorrectly set, the application defaults to standalone mode.
|
||||||
|
|
||||||
**Authentication**
|
**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`:
|
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`:
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
use peach_lib::{config_manager, dyndns_client};
|
||||||
|
use rocket::serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct ConfigureDNSContext {
|
||||||
|
pub external_domain: String,
|
||||||
|
pub dyndns_subdomain: String,
|
||||||
|
pub enable_dyndns: bool,
|
||||||
|
pub is_dyndns_online: bool,
|
||||||
|
pub back: Option<String>,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub flash_name: Option<String>,
|
||||||
|
pub flash_msg: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigureDNSContext {
|
||||||
|
pub fn build() -> ConfigureDNSContext {
|
||||||
|
// TODO: replace `unwrap` with resilient error handling
|
||||||
|
let peach_config = config_manager::load_peach_config().unwrap();
|
||||||
|
let dyndns_fulldomain = peach_config.dyn_domain;
|
||||||
|
let is_dyndns_online = dyndns_client::is_dns_updater_online().unwrap();
|
||||||
|
let dyndns_subdomain =
|
||||||
|
dyndns_client::get_dyndns_subdomain(&dyndns_fulldomain).unwrap_or(dyndns_fulldomain);
|
||||||
|
|
||||||
|
ConfigureDNSContext {
|
||||||
|
external_domain: peach_config.external_domain,
|
||||||
|
dyndns_subdomain,
|
||||||
|
enable_dyndns: peach_config.dyn_enabled,
|
||||||
|
is_dyndns_online,
|
||||||
|
back: None,
|
||||||
|
title: None,
|
||||||
|
flash_name: None,
|
||||||
|
flash_msg: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod dns;
|
||||||
|
pub mod network;
|
|
@ -0,0 +1,398 @@
|
||||||
|
//! Data retrieval for the purpose of serving routes and hydrating
|
||||||
|
//! network-related HTML templates.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use rocket::{
|
||||||
|
form::FromForm,
|
||||||
|
serde::{Deserialize, Serialize},
|
||||||
|
UriDisplayQuery,
|
||||||
|
};
|
||||||
|
|
||||||
|
use peach_network::{
|
||||||
|
network,
|
||||||
|
network::{Scan, Status, Traffic},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
utils::{
|
||||||
|
monitor,
|
||||||
|
monitor::{Alert, Data, Threshold},
|
||||||
|
},
|
||||||
|
AP_IFACE, WLAN_IFACE,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct AccessPoint {
|
||||||
|
pub detail: Option<Scan>,
|
||||||
|
pub signal: Option<i32>,
|
||||||
|
pub state: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ap_state() -> String {
|
||||||
|
match network::state(&*AP_IFACE) {
|
||||||
|
Ok(Some(state)) => state,
|
||||||
|
_ => "Interface unavailable".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, FromForm, UriDisplayQuery)]
|
||||||
|
pub struct Ssid {
|
||||||
|
pub ssid: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, FromForm)]
|
||||||
|
pub struct WiFi {
|
||||||
|
pub ssid: String,
|
||||||
|
pub pass: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_traffic(traffic: Traffic) -> Option<IfaceTraffic> {
|
||||||
|
// modify traffic values & assign measurement unit
|
||||||
|
// based on received and transmitted values
|
||||||
|
let (rx, rx_unit) = if traffic.received > 1_047_527_424 {
|
||||||
|
// convert to GB
|
||||||
|
(traffic.received / 1_073_741_824, "GB".to_string())
|
||||||
|
} else if traffic.received > 0 {
|
||||||
|
// otherwise, convert it to MB
|
||||||
|
((traffic.received / 1024) / 1024, "MB".to_string())
|
||||||
|
} else {
|
||||||
|
(0, "MB".to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
let (tx, tx_unit) = if traffic.transmitted > 1_047_527_424 {
|
||||||
|
// convert to GB
|
||||||
|
(traffic.transmitted / 1_073_741_824, "GB".to_string())
|
||||||
|
} else if traffic.transmitted > 0 {
|
||||||
|
((traffic.transmitted / 1024) / 1024, "MB".to_string())
|
||||||
|
} else {
|
||||||
|
(0, "MB".to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(IfaceTraffic {
|
||||||
|
rx,
|
||||||
|
rx_unit,
|
||||||
|
tx,
|
||||||
|
tx_unit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct IfaceTraffic {
|
||||||
|
pub rx: u64,
|
||||||
|
pub rx_unit: String,
|
||||||
|
pub tx: u64,
|
||||||
|
pub tx_unit: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct NetworkAlertContext {
|
||||||
|
pub alert: Alert,
|
||||||
|
pub back: Option<String>,
|
||||||
|
pub data_total: Option<Data>, // combined stored and current wifi traffic in bytes
|
||||||
|
pub flash_name: Option<String>,
|
||||||
|
pub flash_msg: Option<String>,
|
||||||
|
pub threshold: Threshold,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub traffic: Option<IfaceTraffic>, // current wifi traffic in bytes (since boot)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkAlertContext {
|
||||||
|
pub fn build() -> NetworkAlertContext {
|
||||||
|
let alert = monitor::get_alerts().unwrap();
|
||||||
|
// stored wifi data values as bytes
|
||||||
|
let stored_traffic = monitor::get_data().unwrap();
|
||||||
|
let threshold = monitor::get_thresholds().unwrap();
|
||||||
|
|
||||||
|
let (traffic, data_total) = match network::traffic(&*WLAN_IFACE) {
|
||||||
|
// convert bytes to mb or gb and add appropriate units
|
||||||
|
Ok(Some(t)) => {
|
||||||
|
let current_traffic = t.received + t.transmitted;
|
||||||
|
let traffic = convert_traffic(t);
|
||||||
|
let total = stored_traffic.total + current_traffic;
|
||||||
|
let data_total = Data { total };
|
||||||
|
(traffic, Some(data_total))
|
||||||
|
}
|
||||||
|
_ => (None, None),
|
||||||
|
};
|
||||||
|
|
||||||
|
NetworkAlertContext {
|
||||||
|
alert,
|
||||||
|
back: None,
|
||||||
|
data_total,
|
||||||
|
flash_name: None,
|
||||||
|
flash_msg: None,
|
||||||
|
threshold,
|
||||||
|
title: None,
|
||||||
|
traffic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct NetworkDetailContext {
|
||||||
|
pub back: Option<String>,
|
||||||
|
pub flash_name: Option<String>,
|
||||||
|
pub flash_msg: Option<String>,
|
||||||
|
pub selected: Option<String>,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub saved_aps: Vec<String>,
|
||||||
|
pub wlan_ip: String,
|
||||||
|
pub wlan_networks: HashMap<String, AccessPoint>,
|
||||||
|
pub wlan_rssi: Option<String>,
|
||||||
|
pub wlan_ssid: String,
|
||||||
|
pub wlan_state: String,
|
||||||
|
pub wlan_status: Option<Status>,
|
||||||
|
pub wlan_traffic: Option<IfaceTraffic>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkDetailContext {
|
||||||
|
pub fn build() -> NetworkDetailContext {
|
||||||
|
let wlan_ip = match network::ip(&*WLAN_IFACE) {
|
||||||
|
Ok(Some(ip)) => ip,
|
||||||
|
_ => "x.x.x.x".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// list of networks saved in wpa_supplicant.conf
|
||||||
|
let wlan_list = match network::saved_networks() {
|
||||||
|
Ok(Some(ssids)) => ssids,
|
||||||
|
_ => Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// list of networks saved in wpa_supplicant.conf
|
||||||
|
let saved_aps = wlan_list.clone();
|
||||||
|
|
||||||
|
let wlan_rssi = match network::rssi_percent(&*WLAN_IFACE) {
|
||||||
|
Ok(rssi) => rssi,
|
||||||
|
Err(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// list of networks currently in range (online & accessible)
|
||||||
|
let wlan_scan = match network::available_networks(&*WLAN_IFACE) {
|
||||||
|
Ok(Some(networks)) => networks,
|
||||||
|
_ => Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let wlan_ssid = match network::ssid(&*WLAN_IFACE) {
|
||||||
|
Ok(Some(ssid)) => ssid,
|
||||||
|
_ => "Not connected".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let wlan_state = match network::state(&*WLAN_IFACE) {
|
||||||
|
Ok(Some(state)) => state,
|
||||||
|
_ => "Interface unavailable".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let wlan_status = match network::status(&*WLAN_IFACE) {
|
||||||
|
Ok(status) => status,
|
||||||
|
// interface unavailable
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let wlan_traffic = match network::traffic(&*WLAN_IFACE) {
|
||||||
|
// convert bytes to mb or gb and add appropriate units
|
||||||
|
Ok(Some(traffic)) => convert_traffic(traffic),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// create a hashmap to combine wlan_list & wlan_scan without repetition
|
||||||
|
let mut wlan_networks = HashMap::new();
|
||||||
|
|
||||||
|
for ap in wlan_scan {
|
||||||
|
let ssid = ap.ssid.clone();
|
||||||
|
let rssi = ap.signal_level.clone();
|
||||||
|
// parse the string to a signed integer (for math)
|
||||||
|
let rssi_parsed = rssi.parse::<i32>().unwrap();
|
||||||
|
// perform rssi (dBm) to quality (%) conversion
|
||||||
|
let quality_percent = 2 * (rssi_parsed + 100);
|
||||||
|
let ap_detail = AccessPoint {
|
||||||
|
detail: Some(ap),
|
||||||
|
state: "Available".to_string(),
|
||||||
|
signal: Some(quality_percent),
|
||||||
|
};
|
||||||
|
wlan_networks.insert(ssid, ap_detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
for network in wlan_list {
|
||||||
|
// avoid repetition by checking that ssid is not already in list
|
||||||
|
if !wlan_networks.contains_key(&network) {
|
||||||
|
let ssid = network.clone();
|
||||||
|
let net_detail = AccessPoint {
|
||||||
|
detail: None,
|
||||||
|
state: "Not in range".to_string(),
|
||||||
|
signal: None,
|
||||||
|
};
|
||||||
|
wlan_networks.insert(ssid, net_detail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkDetailContext {
|
||||||
|
back: None,
|
||||||
|
flash_name: None,
|
||||||
|
flash_msg: None,
|
||||||
|
selected: None,
|
||||||
|
title: None,
|
||||||
|
saved_aps,
|
||||||
|
wlan_ip,
|
||||||
|
wlan_networks,
|
||||||
|
wlan_rssi,
|
||||||
|
wlan_ssid,
|
||||||
|
wlan_state,
|
||||||
|
wlan_status,
|
||||||
|
wlan_traffic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct NetworkListContext {
|
||||||
|
pub ap_state: String,
|
||||||
|
pub back: Option<String>,
|
||||||
|
pub flash_name: Option<String>,
|
||||||
|
pub flash_msg: Option<String>,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub wlan_networks: HashMap<String, String>,
|
||||||
|
pub wlan_ssid: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkListContext {
|
||||||
|
pub fn build() -> NetworkListContext {
|
||||||
|
// list of networks saved in wpa_supplicant.conf
|
||||||
|
let wlan_list = match network::saved_networks() {
|
||||||
|
Ok(Some(ssids)) => ssids,
|
||||||
|
_ => Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// list of networks currently in range (online & accessible)
|
||||||
|
let wlan_scan = match network::available_networks(&*WLAN_IFACE) {
|
||||||
|
Ok(Some(networks)) => networks,
|
||||||
|
_ => Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let wlan_ssid = match network::ssid(&*WLAN_IFACE) {
|
||||||
|
Ok(Some(ssid)) => ssid,
|
||||||
|
_ => "Not connected".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// create a hashmap to combine wlan_list & wlan_scan without repetition
|
||||||
|
let mut wlan_networks = HashMap::new();
|
||||||
|
for ap in wlan_scan {
|
||||||
|
wlan_networks.insert(ap.ssid, "Available".to_string());
|
||||||
|
}
|
||||||
|
for network in wlan_list {
|
||||||
|
// insert ssid (with state) only if it doesn't already exist
|
||||||
|
wlan_networks
|
||||||
|
.entry(network)
|
||||||
|
.or_insert_with(|| "Not in range".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let ap_state = match network::state(&*AP_IFACE) {
|
||||||
|
Ok(Some(state)) => state,
|
||||||
|
_ => "Interface unavailable".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
NetworkListContext {
|
||||||
|
ap_state,
|
||||||
|
back: None,
|
||||||
|
flash_msg: None,
|
||||||
|
flash_name: None,
|
||||||
|
title: None,
|
||||||
|
wlan_networks,
|
||||||
|
wlan_ssid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct NetworkStatusContext {
|
||||||
|
pub ap_ip: String,
|
||||||
|
pub ap_ssid: String,
|
||||||
|
pub ap_state: String,
|
||||||
|
pub ap_traffic: Option<IfaceTraffic>,
|
||||||
|
pub wlan_ip: String,
|
||||||
|
pub wlan_rssi: Option<String>,
|
||||||
|
pub wlan_ssid: String,
|
||||||
|
pub wlan_state: String,
|
||||||
|
pub wlan_status: Option<Status>,
|
||||||
|
pub wlan_traffic: Option<IfaceTraffic>,
|
||||||
|
pub flash_name: Option<String>,
|
||||||
|
pub flash_msg: Option<String>,
|
||||||
|
// passing in the ssid of a chosen access point
|
||||||
|
pub selected: Option<String>,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub back: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkStatusContext {
|
||||||
|
pub fn build() -> Self {
|
||||||
|
let ap_ip = match network::ip(&*AP_IFACE) {
|
||||||
|
Ok(Some(ip)) => ip,
|
||||||
|
_ => "x.x.x.x".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ap_ssid = match network::ssid(&*AP_IFACE) {
|
||||||
|
Ok(Some(ssid)) => ssid,
|
||||||
|
_ => "Not currently activated".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ap_state = match network::state(&*AP_IFACE) {
|
||||||
|
Ok(Some(state)) => state,
|
||||||
|
_ => "Interface unavailable".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ap_traffic = match network::traffic(&*AP_IFACE) {
|
||||||
|
// convert bytes to mb or gb and add appropriate units
|
||||||
|
Ok(Some(traffic)) => convert_traffic(traffic),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let wlan_ip = match network::ip(&*WLAN_IFACE) {
|
||||||
|
Ok(Some(ip)) => ip,
|
||||||
|
_ => "x.x.x.x".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let wlan_rssi = match network::rssi_percent(&*WLAN_IFACE) {
|
||||||
|
Ok(rssi) => rssi,
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let wlan_ssid = match network::ssid(&*WLAN_IFACE) {
|
||||||
|
Ok(Some(ssid)) => ssid,
|
||||||
|
_ => "Not connected".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let wlan_state = match network::state(&*WLAN_IFACE) {
|
||||||
|
Ok(Some(state)) => state,
|
||||||
|
_ => "Interface unavailable".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let wlan_status = match network::status(&*WLAN_IFACE) {
|
||||||
|
Ok(status) => status,
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let wlan_traffic = match network::traffic(&*WLAN_IFACE) {
|
||||||
|
// convert bytes to mb or gb and add appropriate units
|
||||||
|
Ok(Some(traffic)) => convert_traffic(traffic),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
NetworkStatusContext {
|
||||||
|
ap_ip,
|
||||||
|
ap_ssid,
|
||||||
|
ap_state,
|
||||||
|
ap_traffic,
|
||||||
|
wlan_ip,
|
||||||
|
wlan_rssi,
|
||||||
|
wlan_ssid,
|
||||||
|
wlan_state,
|
||||||
|
wlan_status,
|
||||||
|
wlan_traffic,
|
||||||
|
flash_name: None,
|
||||||
|
flash_msg: None,
|
||||||
|
selected: None,
|
||||||
|
title: None,
|
||||||
|
back: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,35 +2,57 @@
|
||||||
|
|
||||||
use peach_lib::error::PeachError;
|
use peach_lib::error::PeachError;
|
||||||
use peach_lib::{serde_json, serde_yaml};
|
use peach_lib::{serde_json, serde_yaml};
|
||||||
use snafu::Snafu;
|
use serde_json::error::Error as JsonError;
|
||||||
|
use serde_yaml::Error as YamlError;
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
/// Custom error type encapsulating all possible errors for the web application.
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum PeachWebError {
|
pub enum PeachWebError {
|
||||||
#[snafu(display("Error loading serde json"))]
|
Json(JsonError),
|
||||||
Serde { source: serde_json::error::Error },
|
Yaml(YamlError),
|
||||||
#[snafu(display("Error loading peach-config yaml"))]
|
FailedToRegisterDynDomain(String),
|
||||||
YamlError { source: serde_yaml::Error },
|
PeachLib { source: PeachError, msg: String },
|
||||||
#[snafu(display("{}", msg))]
|
|
||||||
FailedToRegisterDynDomain { msg: String },
|
|
||||||
#[snafu(display("{}: {}", source, msg))]
|
|
||||||
PeachLibError { source: PeachError, msg: String },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<serde_json::error::Error> for PeachWebError {
|
impl std::error::Error for PeachWebError {
|
||||||
fn from(err: serde_json::error::Error) -> PeachWebError {
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
PeachWebError::Serde { source: err }
|
match *self {
|
||||||
|
PeachWebError::Json(ref source) => Some(source),
|
||||||
|
PeachWebError::Yaml(ref source) => Some(source),
|
||||||
|
PeachWebError::FailedToRegisterDynDomain(_) => None,
|
||||||
|
PeachWebError::PeachLib { ref source, .. } => Some(source),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<serde_yaml::Error> for PeachWebError {
|
impl std::fmt::Display for PeachWebError {
|
||||||
fn from(err: serde_yaml::Error) -> PeachWebError {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
PeachWebError::YamlError { source: err }
|
match *self {
|
||||||
|
PeachWebError::Json(ref source) => write!(f, "Serde JSON error: {}", source),
|
||||||
|
PeachWebError::Yaml(ref source) => write!(f, "Serde YAML error: {}", source),
|
||||||
|
PeachWebError::FailedToRegisterDynDomain(ref msg) => {
|
||||||
|
write!(f, "DYN DNS error: {}", msg)
|
||||||
|
}
|
||||||
|
PeachWebError::PeachLib { ref source, .. } => write!(f, "{}", source),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<JsonError> for PeachWebError {
|
||||||
|
fn from(err: JsonError) -> PeachWebError {
|
||||||
|
PeachWebError::Json(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<YamlError> for PeachWebError {
|
||||||
|
fn from(err: YamlError) -> PeachWebError {
|
||||||
|
PeachWebError::Yaml(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PeachError> for PeachWebError {
|
impl From<PeachError> for PeachWebError {
|
||||||
fn from(err: PeachError) -> PeachWebError {
|
fn from(err: PeachError) -> PeachWebError {
|
||||||
PeachWebError::PeachLibError {
|
PeachWebError::PeachLib {
|
||||||
source: err,
|
source: err,
|
||||||
msg: "".to_string(),
|
msg: "".to_string(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,107 +24,41 @@
|
||||||
|
|
||||||
#![feature(proc_macro_hygiene, decl_macro)]
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
|
||||||
|
mod context;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
mod router;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
use std::{env, process};
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use std::process;
|
use rocket::{Build, Rocket};
|
||||||
|
|
||||||
use rocket::{catchers, fs::FileServer, routes, Build, Rocket};
|
|
||||||
use rocket_dyn_templates::Template;
|
|
||||||
|
|
||||||
use crate::routes::authentication::*;
|
|
||||||
use crate::routes::catchers::*;
|
|
||||||
use crate::routes::index::*;
|
|
||||||
use crate::routes::scuttlebutt::*;
|
|
||||||
use crate::routes::status::device::*;
|
|
||||||
use crate::routes::status::network::*;
|
|
||||||
|
|
||||||
use crate::routes::settings::admin::*;
|
|
||||||
use crate::routes::settings::dns::*;
|
|
||||||
use crate::routes::settings::menu::*;
|
|
||||||
use crate::routes::settings::network::*;
|
|
||||||
use crate::routes::settings::scuttlebutt::*;
|
|
||||||
|
|
||||||
pub type BoxError = Box<dyn std::error::Error>;
|
pub type BoxError = Box<dyn std::error::Error>;
|
||||||
|
|
||||||
/// Create rocket instance & mount all routes.
|
lazy_static! {
|
||||||
fn init_rocket() -> Rocket<Build> {
|
// determine run-mode from env var; default to standalone mode (aka peachpub)
|
||||||
rocket::build()
|
static ref STANDALONE_MODE: bool = match env::var("PEACH_STANDALONE_MODE") {
|
||||||
// GENERAL HTML ROUTES
|
// parse the value to a boolean; default to true for any error
|
||||||
.mount(
|
Ok(val) => val.parse().unwrap_or(true),
|
||||||
"/",
|
Err(_) => true
|
||||||
routes![
|
};
|
||||||
help,
|
}
|
||||||
home,
|
|
||||||
login,
|
static WLAN_IFACE: &str = "wlan0";
|
||||||
login_post,
|
static AP_IFACE: &str = "ap0";
|
||||||
logout,
|
|
||||||
reboot_cmd,
|
pub fn init_rocket() -> Rocket<Build> {
|
||||||
shutdown_cmd,
|
info!("Initializing Rocket");
|
||||||
power_menu,
|
if *STANDALONE_MODE {
|
||||||
settings_menu,
|
router::build_minimal_rocket()
|
||||||
],
|
} else {
|
||||||
)
|
router::build_complete_rocket()
|
||||||
// STATUS HTML ROUTES
|
}
|
||||||
.mount("/status", routes![device_status, network_status])
|
|
||||||
// ADMIN SETTINGS HTML ROUTES
|
|
||||||
.mount(
|
|
||||||
"/settings/admin",
|
|
||||||
routes![
|
|
||||||
admin_menu,
|
|
||||||
configure_admin,
|
|
||||||
add_admin,
|
|
||||||
add_admin_post,
|
|
||||||
delete_admin_post,
|
|
||||||
change_password,
|
|
||||||
change_password_post,
|
|
||||||
reset_password,
|
|
||||||
reset_password_post,
|
|
||||||
forgot_password_page,
|
|
||||||
send_password_reset_post,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
// NETWORK SETTINGS HTML ROUTES
|
|
||||||
.mount(
|
|
||||||
"/settings/network",
|
|
||||||
routes![
|
|
||||||
add_credentials,
|
|
||||||
connect_wifi,
|
|
||||||
configure_dns,
|
|
||||||
configure_dns_post,
|
|
||||||
disconnect_wifi,
|
|
||||||
deploy_ap,
|
|
||||||
deploy_client,
|
|
||||||
forget_wifi,
|
|
||||||
network_home,
|
|
||||||
add_ssid,
|
|
||||||
add_wifi,
|
|
||||||
network_detail,
|
|
||||||
wifi_list,
|
|
||||||
wifi_password,
|
|
||||||
wifi_set_password,
|
|
||||||
wifi_usage,
|
|
||||||
wifi_usage_alerts,
|
|
||||||
wifi_usage_reset,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
// SCUTTLEBUTT SETTINGS HTML ROUTES
|
|
||||||
.mount("/settings/scuttlebutt", routes![ssb_settings_menu])
|
|
||||||
// SCUTTLEBUTT SOCIAL HTML ROUTES
|
|
||||||
.mount(
|
|
||||||
"/scuttlebutt",
|
|
||||||
routes![
|
|
||||||
peers, friends, follows, followers, blocks, profile, private, follow, unfollow,
|
|
||||||
block, publish,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.mount("/", FileServer::from("static"))
|
|
||||||
.register("/", catchers![not_found, internal_error, forbidden])
|
|
||||||
.attach(Template::fairing())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Launch the peach-web rocket server.
|
/// Launch the peach-web rocket server.
|
||||||
|
@ -134,7 +68,6 @@ async fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
// initialize rocket
|
// initialize rocket
|
||||||
info!("Initializing Rocket");
|
|
||||||
let rocket = init_rocket();
|
let rocket = init_rocket();
|
||||||
|
|
||||||
// launch rocket
|
// launch rocket
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
use rocket::{catchers, fs::FileServer, routes, Build, Rocket};
|
||||||
|
use rocket_dyn_templates::Template;
|
||||||
|
|
||||||
|
use crate::routes::{
|
||||||
|
authentication::*,
|
||||||
|
catchers::*,
|
||||||
|
index::*,
|
||||||
|
scuttlebutt::*,
|
||||||
|
settings::{admin::*, dns::*, menu::*, network::*, scuttlebutt::*},
|
||||||
|
status::{device::*, network::*},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Create minimal rocket instance and mount routes. This excludes settings
|
||||||
|
/// and status routes related to networking and the device (memory,
|
||||||
|
/// hard disk, CPU etc.).
|
||||||
|
pub fn build_minimal_rocket() -> Rocket<Build> {
|
||||||
|
rocket::build()
|
||||||
|
// GENERAL HTML ROUTES
|
||||||
|
.mount(
|
||||||
|
"/",
|
||||||
|
routes![
|
||||||
|
help,
|
||||||
|
home,
|
||||||
|
login,
|
||||||
|
login_post,
|
||||||
|
logout,
|
||||||
|
reboot_cmd,
|
||||||
|
shutdown_cmd,
|
||||||
|
power_menu,
|
||||||
|
settings_menu,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
// ADMIN SETTINGS HTML ROUTES
|
||||||
|
.mount(
|
||||||
|
"/settings/admin",
|
||||||
|
routes![
|
||||||
|
admin_menu,
|
||||||
|
configure_admin,
|
||||||
|
add_admin,
|
||||||
|
add_admin_post,
|
||||||
|
delete_admin_post,
|
||||||
|
change_password,
|
||||||
|
change_password_post,
|
||||||
|
reset_password,
|
||||||
|
reset_password_post,
|
||||||
|
forgot_password_page,
|
||||||
|
send_password_reset_post,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
// SCUTTLEBUTT SETTINGS HTML ROUTES
|
||||||
|
.mount("/settings/scuttlebutt", routes![ssb_settings_menu])
|
||||||
|
// SCUTTLEBUTT SOCIAL HTML ROUTES
|
||||||
|
.mount(
|
||||||
|
"/scuttlebutt",
|
||||||
|
routes![
|
||||||
|
peers, friends, follows, followers, blocks, profile, private, follow, unfollow,
|
||||||
|
block, publish,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
// STATUS HTML ROUTES
|
||||||
|
// TODO: replace this with a route for `scuttlebutt_status`
|
||||||
|
.mount("/status", routes![device_status, network_status])
|
||||||
|
.mount("/", FileServer::from("static"))
|
||||||
|
.register("/", catchers![not_found, internal_error, forbidden])
|
||||||
|
.attach(Template::fairing())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create complete rocket instance and mount all routes.
|
||||||
|
pub fn build_complete_rocket() -> Rocket<Build> {
|
||||||
|
rocket::build()
|
||||||
|
// GENERAL HTML ROUTES
|
||||||
|
.mount(
|
||||||
|
"/",
|
||||||
|
routes![
|
||||||
|
help,
|
||||||
|
home,
|
||||||
|
login,
|
||||||
|
login_post,
|
||||||
|
logout,
|
||||||
|
reboot_cmd,
|
||||||
|
shutdown_cmd,
|
||||||
|
power_menu,
|
||||||
|
settings_menu,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
// ADMIN SETTINGS HTML ROUTES
|
||||||
|
.mount(
|
||||||
|
"/settings/admin",
|
||||||
|
routes![
|
||||||
|
admin_menu,
|
||||||
|
configure_admin,
|
||||||
|
add_admin,
|
||||||
|
add_admin_post,
|
||||||
|
delete_admin_post,
|
||||||
|
change_password,
|
||||||
|
change_password_post,
|
||||||
|
reset_password,
|
||||||
|
reset_password_post,
|
||||||
|
forgot_password_page,
|
||||||
|
send_password_reset_post,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
// NETWORK SETTINGS HTML ROUTES
|
||||||
|
.mount(
|
||||||
|
"/settings/network",
|
||||||
|
routes![
|
||||||
|
add_credentials,
|
||||||
|
connect_wifi,
|
||||||
|
configure_dns,
|
||||||
|
configure_dns_post,
|
||||||
|
disconnect_wifi,
|
||||||
|
deploy_ap,
|
||||||
|
deploy_client,
|
||||||
|
forget_wifi,
|
||||||
|
network_home,
|
||||||
|
add_ssid,
|
||||||
|
add_wifi,
|
||||||
|
network_detail,
|
||||||
|
wifi_list,
|
||||||
|
wifi_password,
|
||||||
|
wifi_set_password,
|
||||||
|
wifi_usage,
|
||||||
|
wifi_usage_alerts,
|
||||||
|
wifi_usage_reset,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
// SCUTTLEBUTT SETTINGS HTML ROUTES
|
||||||
|
.mount("/settings/scuttlebutt", routes![ssb_settings_menu])
|
||||||
|
// SCUTTLEBUTT SOCIAL HTML ROUTES
|
||||||
|
.mount(
|
||||||
|
"/scuttlebutt",
|
||||||
|
routes![
|
||||||
|
peers, friends, follows, followers, blocks, profile, private, follow, unfollow,
|
||||||
|
block, publish,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
// STATUS HTML ROUTES
|
||||||
|
.mount("/status", routes![device_status, network_status])
|
||||||
|
.mount("/", FileServer::from("static"))
|
||||||
|
.register("/", catchers![not_found, internal_error, forbidden])
|
||||||
|
.attach(Template::fairing())
|
||||||
|
}
|
|
@ -1,14 +1,17 @@
|
||||||
use log::info;
|
use log::info;
|
||||||
use rocket::form::{Form, FromForm};
|
use rocket::{
|
||||||
use rocket::http::{Cookie, CookieJar, Status};
|
form::{Form, FromForm},
|
||||||
use rocket::request::{self, FlashMessage, FromRequest, Request};
|
get,
|
||||||
use rocket::response::{Flash, Redirect};
|
http::{Cookie, CookieJar, Status},
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
post,
|
||||||
use rocket::{get, post, Config};
|
request::{self, FlashMessage, FromRequest, Request},
|
||||||
use rocket_dyn_templates::Template;
|
response::{Flash, Redirect},
|
||||||
|
serde::Deserialize,
|
||||||
|
Config,
|
||||||
|
};
|
||||||
|
use rocket_dyn_templates::{tera::Context, Template};
|
||||||
|
|
||||||
use peach_lib::error::PeachError;
|
use peach_lib::{error::PeachError, password_utils};
|
||||||
use peach_lib::password_utils;
|
|
||||||
|
|
||||||
use crate::error::PeachWebError;
|
use crate::error::PeachWebError;
|
||||||
use crate::utils::TemplateOrRedirect;
|
use crate::utils::TemplateOrRedirect;
|
||||||
|
@ -66,37 +69,19 @@ impl<'r> FromRequest<'r> for Authenticated {
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /login
|
// HELPERS AND ROUTES FOR /login
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct LoginContext {
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LoginContext {
|
|
||||||
pub fn build() -> LoginContext {
|
|
||||||
LoginContext {
|
|
||||||
back: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
title: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/login")]
|
#[get("/login")]
|
||||||
pub fn login(flash: Option<FlashMessage>) -> Template {
|
pub fn login(flash: Option<FlashMessage>) -> Template {
|
||||||
let mut context = LoginContext::build();
|
let mut context = Context::new();
|
||||||
context.back = Some("/".to_string());
|
context.insert("back", &Some("/".to_string()));
|
||||||
context.title = Some("Login".to_string());
|
context.insert("title", &Some("Login".to_string()));
|
||||||
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
|
||||||
};
|
};
|
||||||
Template::render("login", &context)
|
|
||||||
|
Template::render("login", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, FromForm)]
|
#[derive(Debug, Deserialize, FromForm)]
|
||||||
|
@ -116,8 +101,7 @@ pub fn verify_login_form(login_form: LoginForm) -> Result<(), PeachError> {
|
||||||
|
|
||||||
#[post("/login", data = "<login_form>")]
|
#[post("/login", data = "<login_form>")]
|
||||||
pub fn login_post(login_form: Form<LoginForm>, cookies: &CookieJar<'_>) -> TemplateOrRedirect {
|
pub fn login_post(login_form: Form<LoginForm>, cookies: &CookieJar<'_>) -> TemplateOrRedirect {
|
||||||
let result = verify_login_form(login_form.into_inner());
|
match verify_login_form(login_form.into_inner()) {
|
||||||
match result {
|
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// if successful login, add a cookie indicating the user is authenticated
|
// if successful login, add a cookie indicating the user is authenticated
|
||||||
// and redirect to home page
|
// and redirect to home page
|
||||||
|
@ -125,17 +109,18 @@ pub fn login_post(login_form: Form<LoginForm>, cookies: &CookieJar<'_>) -> Templ
|
||||||
// is just admin (this is arbitrary).
|
// is just admin (this is arbitrary).
|
||||||
// If we had multiple users, we could put the user_id here.
|
// If we had multiple users, we could put the user_id here.
|
||||||
cookies.add_private(Cookie::new(AUTH_COOKIE_KEY, ADMIN_USERNAME));
|
cookies.add_private(Cookie::new(AUTH_COOKIE_KEY, ADMIN_USERNAME));
|
||||||
|
|
||||||
TemplateOrRedirect::Redirect(Redirect::to("/"))
|
TemplateOrRedirect::Redirect(Redirect::to("/"))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// if unsuccessful login, render /login page again
|
// if unsuccessful login, render /login page again
|
||||||
let mut context = LoginContext::build();
|
let mut context = Context::new();
|
||||||
context.back = Some("/".to_string());
|
context.insert("back", &Some("/".to_string()));
|
||||||
context.title = Some("Login".to_string());
|
context.insert("title", &Some("Login".to_string()));
|
||||||
context.flash_name = Some("error".to_string());
|
context.insert("flash_name", &("error".to_string()));
|
||||||
let flash_msg = "Invalid password".to_string();
|
context.insert("flash_msg", &("Invalid password".to_string()));
|
||||||
context.flash_msg = Some(flash_msg);
|
|
||||||
TemplateOrRedirect::Template(Template::render("login", &context))
|
TemplateOrRedirect::Template(Template::render("login", &context.into_json()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,44 +144,6 @@ pub struct ResetPasswordForm {
|
||||||
pub new_password2: String,
|
pub new_password2: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct ResetPasswordContext {
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResetPasswordContext {
|
|
||||||
pub fn build() -> ResetPasswordContext {
|
|
||||||
ResetPasswordContext {
|
|
||||||
back: None,
|
|
||||||
title: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct ChangePasswordContext {
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChangePasswordContext {
|
|
||||||
pub fn build() -> ChangePasswordContext {
|
|
||||||
ChangePasswordContext {
|
|
||||||
back: None,
|
|
||||||
title: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify, validate and save the submitted password. This function is publicly exposed for users who have forgotten their password.
|
/// Verify, validate and save the submitted password. This function is publicly exposed for users who have forgotten their password.
|
||||||
pub fn save_reset_password_form(password_form: ResetPasswordForm) -> Result<(), PeachWebError> {
|
pub fn save_reset_password_form(password_form: ResetPasswordForm) -> Result<(), PeachWebError> {
|
||||||
info!(
|
info!(
|
||||||
|
@ -218,81 +165,63 @@ pub fn save_reset_password_form(password_form: ResetPasswordForm) -> Result<(),
|
||||||
/// and is specifically for users who have forgotten their password.
|
/// and is specifically for users who have forgotten their password.
|
||||||
#[get("/reset_password")]
|
#[get("/reset_password")]
|
||||||
pub fn reset_password(flash: Option<FlashMessage>) -> Template {
|
pub fn reset_password(flash: Option<FlashMessage>) -> Template {
|
||||||
let mut context = ResetPasswordContext::build();
|
let mut context = Context::new();
|
||||||
context.back = Some("/".to_string());
|
context.insert("back", &Some("/".to_string()));
|
||||||
context.title = Some("Reset Password".to_string());
|
context.insert("title", &Some("Reset Password".to_string()));
|
||||||
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
|
||||||
};
|
};
|
||||||
Template::render("settings/admin/reset_password", &context)
|
|
||||||
|
Template::render("settings/admin/reset_password", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Password reset form request handler. This route is used by a user who is not logged in
|
/// Password reset form request handler. This route is used by a user who is not logged in
|
||||||
/// and is specifically for users who have forgotten their password.
|
/// and is specifically for users who have forgotten their password.
|
||||||
#[post("/reset_password", data = "<reset_password_form>")]
|
#[post("/reset_password", data = "<reset_password_form>")]
|
||||||
pub fn reset_password_post(reset_password_form: Form<ResetPasswordForm>) -> Template {
|
pub fn reset_password_post(reset_password_form: Form<ResetPasswordForm>) -> Template {
|
||||||
let result = save_reset_password_form(reset_password_form.into_inner());
|
let mut context = Context::new();
|
||||||
match result {
|
context.insert("back", &Some("/".to_string()));
|
||||||
Ok(_) => {
|
context.insert("title", &Some("Reset Password".to_string()));
|
||||||
let mut context = ChangePasswordContext::build();
|
|
||||||
context.back = Some("/".to_string());
|
let (flash_name, flash_msg) = match save_reset_password_form(reset_password_form.into_inner()) {
|
||||||
context.title = Some("Reset Password".to_string());
|
Ok(_) => (
|
||||||
context.flash_name = Some("success".to_string());
|
"success".to_string(),
|
||||||
let flash_msg = "New password is now saved. Return home to login".to_string();
|
"New password is now saved. Return home to login".to_string(),
|
||||||
context.flash_msg = Some(flash_msg);
|
),
|
||||||
Template::render("settings/admin/reset_password", &context)
|
Err(err) => (
|
||||||
}
|
"error".to_string(),
|
||||||
Err(err) => {
|
format!("Failed to reset password: {}", err),
|
||||||
let mut context = ChangePasswordContext::build();
|
),
|
||||||
// set back icon link to network route
|
};
|
||||||
context.back = Some("/".to_string());
|
|
||||||
context.title = Some("Reset Password".to_string());
|
context.insert("flash_name", &Some(flash_name));
|
||||||
context.flash_name = Some("error".to_string());
|
context.insert("flash_msg", &Some(flash_msg));
|
||||||
context.flash_msg = Some(format!("Failed to reset password: {}", err));
|
|
||||||
Template::render("settings/admin/reset_password", &context)
|
Template::render("settings/admin/reset_password", &context.into_json())
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /send_password_reset
|
// HELPERS AND ROUTES FOR /send_password_reset
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct SendPasswordResetContext {
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SendPasswordResetContext {
|
|
||||||
pub fn build() -> SendPasswordResetContext {
|
|
||||||
SendPasswordResetContext {
|
|
||||||
back: None,
|
|
||||||
title: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Page for users who have forgotten their password.
|
/// Page for users who have forgotten their password.
|
||||||
/// This route is used by a user who is not logged in
|
/// This route is used by a user who is not logged in
|
||||||
/// to initiate the sending of a new password reset.
|
/// to initiate the sending of a new password reset.
|
||||||
#[get("/forgot_password")]
|
#[get("/forgot_password")]
|
||||||
pub fn forgot_password_page(flash: Option<FlashMessage>) -> Template {
|
pub fn forgot_password_page(flash: Option<FlashMessage>) -> Template {
|
||||||
let mut context = SendPasswordResetContext::build();
|
let mut context = Context::new();
|
||||||
context.back = Some("/".to_string());
|
context.insert("back", &Some("/".to_string()));
|
||||||
context.title = Some("Send Password Reset".to_string());
|
context.insert("title", &Some("Send Password Reset".to_string()));
|
||||||
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
// add flash message contents to the context object
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
};
|
};
|
||||||
Template::render("settings/admin/forgot_password", &context)
|
|
||||||
|
Template::render("settings/admin/forgot_password", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send password reset request handler. This route is used by a user who is not logged in
|
/// Send password reset request handler. This route is used by a user who is not logged in
|
||||||
|
@ -301,27 +230,25 @@ pub fn forgot_password_page(flash: Option<FlashMessage>) -> Template {
|
||||||
#[post("/send_password_reset")]
|
#[post("/send_password_reset")]
|
||||||
pub fn send_password_reset_post() -> Template {
|
pub fn send_password_reset_post() -> Template {
|
||||||
info!("++ send password reset post");
|
info!("++ send password reset post");
|
||||||
let result = password_utils::send_password_reset();
|
let mut context = Context::new();
|
||||||
match result {
|
context.insert("back", &Some("/".to_string()));
|
||||||
Ok(_) => {
|
context.insert("title", &Some("Send Password Reset".to_string()));
|
||||||
let mut context = ChangePasswordContext::build();
|
|
||||||
context.back = Some("/".to_string());
|
let (flash_name, flash_msg) = match password_utils::send_password_reset() {
|
||||||
context.title = Some("Send Password Reset".to_string());
|
Ok(_) => (
|
||||||
context.flash_name = Some("success".to_string());
|
"success".to_string(),
|
||||||
let flash_msg =
|
"A password reset link has been sent to the admin of this device".to_string(),
|
||||||
"A password reset link has been sent to the admin of this device".to_string();
|
),
|
||||||
context.flash_msg = Some(flash_msg);
|
Err(err) => (
|
||||||
Template::render("settings/admin/forgot_password", &context)
|
"error".to_string(),
|
||||||
}
|
format!("Failed to send password reset link: {}", err),
|
||||||
Err(err) => {
|
),
|
||||||
let mut context = ChangePasswordContext::build();
|
};
|
||||||
context.back = Some("/".to_string());
|
|
||||||
context.title = Some("Send Password Reset".to_string());
|
context.insert("flash_name", &Some(flash_name));
|
||||||
context.flash_name = Some("error".to_string());
|
context.insert("flash_msg", &Some(flash_msg));
|
||||||
context.flash_msg = Some(format!("Failed to send password reset link: {}", err));
|
|
||||||
Template::render("settings/admin/forgot_password", &context)
|
Template::render("settings/admin/forgot_password", &context.into_json())
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings/change_password
|
// HELPERS AND ROUTES FOR /settings/change_password
|
||||||
|
@ -353,42 +280,40 @@ pub fn save_password_form(password_form: PasswordForm) -> Result<(), PeachWebErr
|
||||||
/// Change password request handler. This is used by a user who is already logged in.
|
/// Change password request handler. This is used by a user who is already logged in.
|
||||||
#[get("/change_password")]
|
#[get("/change_password")]
|
||||||
pub fn change_password(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn change_password(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
let mut context = ChangePasswordContext::build();
|
let mut context = Context::new();
|
||||||
// set back icon link to network route
|
context.insert("back", &Some("/settings/admin".to_string()));
|
||||||
context.back = Some("/settings/admin".to_string());
|
context.insert("title", &Some("Change Password".to_string()));
|
||||||
context.title = Some("Change Password".to_string());
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
// add flash message contents to the context object
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
};
|
};
|
||||||
Template::render("settings/admin/change_password", &context)
|
|
||||||
|
Template::render("settings/admin/change_password", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change password form request handler. This route is used by a user who is already logged in.
|
/// Change password form request handler. This route is used by a user who is already logged in.
|
||||||
#[post("/change_password", data = "<password_form>")]
|
#[post("/change_password", data = "<password_form>")]
|
||||||
pub fn change_password_post(password_form: Form<PasswordForm>, _auth: Authenticated) -> Template {
|
pub fn change_password_post(password_form: Form<PasswordForm>, _auth: Authenticated) -> Template {
|
||||||
let result = save_password_form(password_form.into_inner());
|
let mut context = Context::new();
|
||||||
match result {
|
context.insert("back", &Some("/settings/admin".to_string()));
|
||||||
Ok(_) => {
|
context.insert("title", &Some("Change Password".to_string()));
|
||||||
let mut context = ChangePasswordContext::build();
|
|
||||||
// set back icon link to network route
|
let (flash_name, flash_msg) = match save_password_form(password_form.into_inner()) {
|
||||||
context.back = Some("/settings/admin".to_string());
|
Ok(_) => (
|
||||||
context.title = Some("Change Password".to_string());
|
"success".to_string(),
|
||||||
context.flash_name = Some("success".to_string());
|
"New password is now saved".to_string(),
|
||||||
context.flash_msg = Some("New password is now saved".to_string());
|
),
|
||||||
// template_dir is set in Rocket.toml
|
Err(err) => (
|
||||||
Template::render("settings/admin/change_password", &context)
|
"error".to_string(),
|
||||||
}
|
format!("Failed to save new password: {}", err),
|
||||||
Err(err) => {
|
),
|
||||||
let mut context = ChangePasswordContext::build();
|
};
|
||||||
// set back icon link to network route
|
|
||||||
context.back = Some("/settings/admin".to_string());
|
context.insert("flash_name", &Some(flash_name));
|
||||||
context.title = Some("Change Password".to_string());
|
context.insert("flash_msg", &Some(flash_msg));
|
||||||
context.flash_name = Some("error".to_string());
|
|
||||||
context.flash_msg = Some(format!("Failed to save new password: {}", err));
|
Template::render("settings/admin/change_password", &context.into_json())
|
||||||
Template::render("settings/admin/change_password", &context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,69 +1,33 @@
|
||||||
use rocket::{get, request::FlashMessage};
|
use rocket::{get, request::FlashMessage};
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::{tera::Context, Template};
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
use crate::routes::authentication::Authenticated;
|
use crate::routes::authentication::Authenticated;
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR / (HOME PAGE)
|
// HELPERS AND ROUTES FOR / (HOME PAGE)
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct HomeContext {
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HomeContext {
|
|
||||||
pub fn build() -> HomeContext {
|
|
||||||
HomeContext {
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
title: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub fn home(_auth: Authenticated) -> Template {
|
pub fn home(_auth: Authenticated) -> Template {
|
||||||
let context = HomeContext {
|
let mut context = Context::new();
|
||||||
flash_name: None,
|
context.insert("flash_name", &None::<()>);
|
||||||
flash_msg: None,
|
context.insert("flash_msg", &None::<()>);
|
||||||
title: None,
|
context.insert("title", &None::<()>);
|
||||||
};
|
|
||||||
Template::render("home", &context)
|
Template::render("home", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /help
|
// HELPERS AND ROUTES FOR /help
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct HelpContext {
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HelpContext {
|
|
||||||
pub fn build() -> HelpContext {
|
|
||||||
HelpContext {
|
|
||||||
back: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
title: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/help")]
|
#[get("/help")]
|
||||||
pub fn help(flash: Option<FlashMessage>) -> Template {
|
pub fn help(flash: Option<FlashMessage>) -> Template {
|
||||||
let mut context = HelpContext::build();
|
let mut context = Context::new();
|
||||||
context.back = Some("/".to_string());
|
context.insert("back", &Some("/".to_string()));
|
||||||
context.title = Some("Help".to_string());
|
context.insert("title", &Some("Help".to_string()));
|
||||||
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
|
||||||
};
|
};
|
||||||
Template::render("help", &context)
|
|
||||||
|
Template::render("help", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,95 +1,69 @@
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
|
||||||
use rocket::{
|
use rocket::{
|
||||||
form::{Form, FromForm},
|
form::{Form, FromForm},
|
||||||
get, post,
|
get, post,
|
||||||
request::FlashMessage,
|
request::FlashMessage,
|
||||||
response::{Flash, Redirect},
|
response::{Flash, Redirect},
|
||||||
|
serde::Deserialize,
|
||||||
uri,
|
uri,
|
||||||
};
|
};
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::{tera::Context, Template};
|
||||||
|
|
||||||
use peach_lib::config_manager;
|
use peach_lib::config_manager;
|
||||||
use peach_lib::config_manager::load_peach_config;
|
|
||||||
|
|
||||||
use crate::error::PeachWebError;
|
use crate::error::PeachWebError;
|
||||||
use crate::routes::authentication::Authenticated;
|
use crate::routes::authentication::Authenticated;
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings/admin
|
// HELPERS AND ROUTES FOR /settings/admin
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct AdminMenuContext {
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AdminMenuContext {
|
|
||||||
pub fn build() -> AdminMenuContext {
|
|
||||||
AdminMenuContext {
|
|
||||||
back: None,
|
|
||||||
title: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Administrator settings menu.
|
/// Administrator settings menu.
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub fn admin_menu(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn admin_menu(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
let mut context = AdminMenuContext::build();
|
let mut context = Context::new();
|
||||||
// set back icon link to settings route
|
context.insert("back", &Some("/settings".to_string()));
|
||||||
context.back = Some("/settings".to_string());
|
context.insert("title", &Some("Administrator Settings".to_string()));
|
||||||
context.title = Some("Administrator Settings".to_string());
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
|
||||||
};
|
};
|
||||||
Template::render("settings/admin/menu", &context)
|
|
||||||
|
Template::render("settings/admin/menu", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings/admin/configure
|
// HELPERS AND ROUTES FOR /settings/admin/configure
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct ConfigureAdminContext {
|
|
||||||
pub ssb_admin_ids: Vec<String>,
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigureAdminContext {
|
|
||||||
pub fn build() -> ConfigureAdminContext {
|
|
||||||
let peach_config = load_peach_config().unwrap();
|
|
||||||
let ssb_admin_ids = peach_config.ssb_admin_ids;
|
|
||||||
ConfigureAdminContext {
|
|
||||||
ssb_admin_ids,
|
|
||||||
back: None,
|
|
||||||
title: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// View and delete currently configured admin.
|
/// View and delete currently configured admin.
|
||||||
#[get("/configure")]
|
#[get("/configure")]
|
||||||
pub fn configure_admin(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn configure_admin(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
let mut context = ConfigureAdminContext::build();
|
let mut context = Context::new();
|
||||||
// set back icon link to settings route
|
context.insert("back", &Some("/settings/admin".to_string()));
|
||||||
context.back = Some("/settings/admin".to_string());
|
context.insert("title", &Some("Configure Admin".to_string()));
|
||||||
context.title = Some("Configure Admin".to_string());
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
|
||||||
};
|
};
|
||||||
Template::render("settings/admin/configure_admin", &context)
|
|
||||||
|
// load the peach configuration vector
|
||||||
|
match config_manager::load_peach_config() {
|
||||||
|
Ok(config) => {
|
||||||
|
// retrieve the vector of ssb admin ids
|
||||||
|
let ssb_admin_ids = config.ssb_admin_ids;
|
||||||
|
context.insert("ssb_admin_ids", &ssb_admin_ids);
|
||||||
|
}
|
||||||
|
// if load fails, overwrite the flash_name and flash_msg
|
||||||
|
Err(e) => {
|
||||||
|
context.insert("flash_name", &Some("error".to_string()));
|
||||||
|
context.insert(
|
||||||
|
"flash_msg",
|
||||||
|
&Some(format!("Failed to load Peach config: {}", e)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Template::render("settings/admin/configure_admin", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings/admin/add
|
// HELPERS AND ROUTES FOR /settings/admin/add
|
||||||
|
@ -99,25 +73,6 @@ pub struct AddAdminForm {
|
||||||
pub ssb_id: String,
|
pub ssb_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct AddAdminContext {
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAdminContext {
|
|
||||||
pub fn build() -> AddAdminContext {
|
|
||||||
AddAdminContext {
|
|
||||||
back: None,
|
|
||||||
title: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn save_add_admin_form(admin_form: AddAdminForm) -> Result<(), PeachWebError> {
|
pub fn save_add_admin_form(admin_form: AddAdminForm) -> Result<(), PeachWebError> {
|
||||||
let _result = config_manager::add_ssb_admin_id(&admin_form.ssb_id)?;
|
let _result = config_manager::add_ssb_admin_id(&admin_form.ssb_id)?;
|
||||||
// if the previous line didn't throw an error then it was a success
|
// if the previous line didn't throw an error then it was a success
|
||||||
|
@ -126,23 +81,24 @@ pub fn save_add_admin_form(admin_form: AddAdminForm) -> Result<(), PeachWebError
|
||||||
|
|
||||||
#[get("/add")]
|
#[get("/add")]
|
||||||
pub fn add_admin(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn add_admin(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
let mut context = AddAdminContext::build();
|
let mut context = Context::new();
|
||||||
context.back = Some("/settings/admin/configure".to_string());
|
context.insert("back", &Some("/settings/admin/configure".to_string()));
|
||||||
context.title = Some("Add Admin".to_string());
|
context.insert("title", &Some("Add Admin".to_string()));
|
||||||
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// template_dir is set in Rocket.toml
|
// template_dir is set in Rocket.toml
|
||||||
Template::render("settings/admin/add_admin", &context)
|
Template::render("settings/admin/add_admin", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/add", data = "<add_admin_form>")]
|
#[post("/add", data = "<add_admin_form>")]
|
||||||
pub fn add_admin_post(add_admin_form: Form<AddAdminForm>, _auth: Authenticated) -> Flash<Redirect> {
|
pub fn add_admin_post(add_admin_form: Form<AddAdminForm>, _auth: Authenticated) -> Flash<Redirect> {
|
||||||
let result = save_add_admin_form(add_admin_form.into_inner());
|
let result = save_add_admin_form(add_admin_form.into_inner());
|
||||||
let url = uri!(configure_admin);
|
let url = uri!("/settings/admin/configure");
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Flash::success(Redirect::to(url), "Successfully added new admin"),
|
Ok(_) => Flash::success(Redirect::to(url), "Successfully added new admin"),
|
||||||
Err(_) => Flash::error(Redirect::to(url), "Failed to add new admin"),
|
Err(_) => Flash::error(Redirect::to(url), "Failed to add new admin"),
|
||||||
|
|
|
@ -3,23 +3,20 @@ use rocket::{
|
||||||
form::{Form, FromForm},
|
form::{Form, FromForm},
|
||||||
get, post,
|
get, post,
|
||||||
request::FlashMessage,
|
request::FlashMessage,
|
||||||
serde::{Deserialize, Serialize},
|
serde::Deserialize,
|
||||||
};
|
};
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::Template;
|
||||||
|
|
||||||
use peach_lib::config_manager;
|
use peach_lib::{
|
||||||
use peach_lib::config_manager::load_peach_config;
|
config_manager, dyndns_client,
|
||||||
use peach_lib::dyndns_client;
|
error::PeachError,
|
||||||
use peach_lib::dyndns_client::{
|
jsonrpc_client_core::{Error, ErrorKind},
|
||||||
check_is_new_dyndns_domain, get_dyndns_subdomain, get_full_dynamic_domain,
|
jsonrpc_core::types::error::ErrorCode,
|
||||||
is_dns_updater_online,
|
|
||||||
};
|
};
|
||||||
use peach_lib::error::PeachError;
|
|
||||||
use peach_lib::jsonrpc_client_core::{Error, ErrorKind};
|
|
||||||
use peach_lib::jsonrpc_core::types::error::ErrorCode;
|
|
||||||
|
|
||||||
use crate::error::PeachWebError;
|
use crate::{
|
||||||
use crate::routes::authentication::Authenticated;
|
context::dns::ConfigureDNSContext, error::PeachWebError, routes::authentication::Authenticated,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, FromForm)]
|
#[derive(Debug, Deserialize, FromForm)]
|
||||||
pub struct DnsForm {
|
pub struct DnsForm {
|
||||||
|
@ -32,11 +29,12 @@ pub fn save_dns_configuration(dns_form: DnsForm) -> Result<(), PeachWebError> {
|
||||||
// first save local configurations
|
// first save local configurations
|
||||||
config_manager::set_external_domain(&dns_form.external_domain)?;
|
config_manager::set_external_domain(&dns_form.external_domain)?;
|
||||||
config_manager::set_dyndns_enabled_value(dns_form.enable_dyndns)?;
|
config_manager::set_dyndns_enabled_value(dns_form.enable_dyndns)?;
|
||||||
|
|
||||||
// if dynamic dns is enabled and this is a new domain name, then register it
|
// if dynamic dns is enabled and this is a new domain name, then register it
|
||||||
if dns_form.enable_dyndns {
|
if dns_form.enable_dyndns {
|
||||||
let full_dynamic_domain = get_full_dynamic_domain(&dns_form.dynamic_domain);
|
let full_dynamic_domain = dyndns_client::get_full_dynamic_domain(&dns_form.dynamic_domain);
|
||||||
// check if this is a new domain or if its already registered
|
// check if this is a new domain or if its already registered
|
||||||
let is_new_domain = check_is_new_dyndns_domain(&full_dynamic_domain)?;
|
let is_new_domain = dyndns_client::check_is_new_dyndns_domain(&full_dynamic_domain)?;
|
||||||
if is_new_domain {
|
if is_new_domain {
|
||||||
match dyndns_client::register_domain(&full_dynamic_domain) {
|
match dyndns_client::register_domain(&full_dynamic_domain) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
@ -48,24 +46,22 @@ 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(Error(
|
||||||
match source {
|
ErrorKind::JsonRpcError(err),
|
||||||
Error(ErrorKind::JsonRpcError(err), _state) => match err.code {
|
_state,
|
||||||
ErrorCode::ServerError(-32030) => {
|
)) => {
|
||||||
format!("Error registering domain: {} was previously registered", full_dynamic_domain)
|
if let ErrorCode::ServerError(-32030) = err.code {
|
||||||
}
|
format!(
|
||||||
_ => {
|
"Error registering domain: {} was previously registered",
|
||||||
format!("Failed to register dyndns domain {:?}", err)
|
full_dynamic_domain
|
||||||
}
|
)
|
||||||
},
|
} else {
|
||||||
_ => {
|
"Failed to register dyndns domain".to_string()
|
||||||
format!("Failed to register dyndns domain: {:?}", source)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => "Failed to register dyndns domain".to_string(),
|
_ => "Failed to register dyndns domain".to_string(),
|
||||||
};
|
};
|
||||||
Err(PeachWebError::FailedToRegisterDynDomain { msg })
|
Err(PeachWebError::FailedToRegisterDynDomain(msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,74 +74,44 @@ pub fn save_dns_configuration(dns_form: DnsForm) -> Result<(), PeachWebError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct ConfigureDNSContext {
|
|
||||||
pub external_domain: String,
|
|
||||||
pub dyndns_subdomain: String,
|
|
||||||
pub enable_dyndns: bool,
|
|
||||||
pub is_dyndns_online: bool,
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigureDNSContext {
|
|
||||||
pub fn build() -> ConfigureDNSContext {
|
|
||||||
let peach_config = load_peach_config().unwrap();
|
|
||||||
let dyndns_fulldomain = peach_config.dyn_domain;
|
|
||||||
let is_dyndns_online = is_dns_updater_online().unwrap();
|
|
||||||
let dyndns_subdomain =
|
|
||||||
get_dyndns_subdomain(&dyndns_fulldomain).unwrap_or(dyndns_fulldomain);
|
|
||||||
ConfigureDNSContext {
|
|
||||||
external_domain: peach_config.external_domain,
|
|
||||||
dyndns_subdomain,
|
|
||||||
enable_dyndns: peach_config.dyn_enabled,
|
|
||||||
is_dyndns_online,
|
|
||||||
back: None,
|
|
||||||
title: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/dns")]
|
#[get("/dns")]
|
||||||
pub fn configure_dns(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn configure_dns(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
let mut context = ConfigureDNSContext::build();
|
let mut context = ConfigureDNSContext::build();
|
||||||
|
|
||||||
// set back icon link to network route
|
// set back icon link to network route
|
||||||
context.back = Some("/settings/network".to_string());
|
context.back = Some("/settings/network".to_string());
|
||||||
context.title = Some("Configure DNS".to_string());
|
context.title = Some("Configure DNS".to_string());
|
||||||
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
// add flash message contents to the context object
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.flash_name = Some(flash.kind().to_string());
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
context.flash_msg = Some(flash.message().to_string());
|
||||||
};
|
};
|
||||||
|
|
||||||
Template::render("settings/network/configure_dns", &context)
|
Template::render("settings/network/configure_dns", &context)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/dns", data = "<dns>")]
|
#[post("/dns", data = "<dns>")]
|
||||||
pub fn configure_dns_post(dns: Form<DnsForm>, _auth: Authenticated) -> Template {
|
pub fn configure_dns_post(dns: Form<DnsForm>, _auth: Authenticated) -> Template {
|
||||||
let result = save_dns_configuration(dns.into_inner());
|
let result = save_dns_configuration(dns.into_inner());
|
||||||
|
|
||||||
|
let mut context = ConfigureDNSContext::build();
|
||||||
|
|
||||||
|
// set back icon link to network route
|
||||||
|
context.back = Some("/settings/network".to_string());
|
||||||
|
context.title = Some("Configure DNS".to_string());
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let mut context = ConfigureDNSContext::build();
|
|
||||||
// set back icon link to network route
|
|
||||||
context.back = Some("/settings/network".to_string());
|
|
||||||
context.title = Some("Configure DNS".to_string());
|
|
||||||
context.flash_name = Some("success".to_string());
|
context.flash_name = Some("success".to_string());
|
||||||
context.flash_msg = Some("New dynamic dns configuration is now enabled".to_string());
|
context.flash_msg = Some("New dynamic dns configuration is now enabled".to_string());
|
||||||
Template::render("settings/network/configure_dns", &context)
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let mut context = ConfigureDNSContext::build();
|
|
||||||
// set back icon link to network route
|
|
||||||
context.back = Some("/settings/network".to_string());
|
|
||||||
context.title = Some("Configure DNS".to_string());
|
|
||||||
context.flash_name = Some("error".to_string());
|
context.flash_name = Some("error".to_string());
|
||||||
context.flash_msg = Some(format!("Failed to save dns configurations: {}", err));
|
context.flash_msg = Some(format!("Failed to save dns configurations: {}", err));
|
||||||
Template::render("settings/network/configure_dns", &context)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Template::render("settings/network/configure_dns", &context)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,22 @@
|
||||||
use rocket::{get, request::FlashMessage, serde::Serialize};
|
use rocket::{get, request::FlashMessage};
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::{tera::Context, Template};
|
||||||
|
|
||||||
use crate::routes::authentication::Authenticated;
|
use crate::routes::authentication::Authenticated;
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings
|
// HELPERS AND ROUTES FOR /settings
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct SettingsMenuContext {
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SettingsMenuContext {
|
|
||||||
pub fn build() -> SettingsMenuContext {
|
|
||||||
SettingsMenuContext {
|
|
||||||
back: None,
|
|
||||||
title: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// View and delete currently configured admin.
|
/// View and delete currently configured admin.
|
||||||
#[get("/settings")]
|
#[get("/settings")]
|
||||||
pub fn settings_menu(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn settings_menu(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
let mut context = SettingsMenuContext::build();
|
let mut context = Context::new();
|
||||||
// set back icon link to network route
|
context.insert("back", &Some("/".to_string()));
|
||||||
context.back = Some("/".to_string());
|
context.insert("title", &Some("Settings".to_string()));
|
||||||
context.title = Some("Settings".to_string());
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
|
||||||
};
|
};
|
||||||
Template::render("settings/menu", &context)
|
|
||||||
|
Template::render("settings/menu", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
|
|
||||||
use rocket::{
|
use rocket::{
|
||||||
form::{Form, FromForm},
|
form::{Form, FromForm},
|
||||||
get, post,
|
get, post,
|
||||||
request::FlashMessage,
|
request::FlashMessage,
|
||||||
response::{Flash, Redirect},
|
response::{Flash, Redirect},
|
||||||
serde::{Deserialize, Serialize},
|
serde::Deserialize,
|
||||||
uri, UriDisplayQuery,
|
uri, UriDisplayQuery,
|
||||||
};
|
};
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::{tera::Context, Template};
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use peach_lib::network_client;
|
use peach_network::network;
|
||||||
use peach_lib::network_client::{AccessPoint, Networks, Scan};
|
|
||||||
use peach_lib::stats_client::Traffic;
|
|
||||||
|
|
||||||
use crate::routes::authentication::Authenticated;
|
use crate::{
|
||||||
use crate::utils::monitor;
|
context,
|
||||||
use crate::utils::monitor::{Alert, Data, Threshold};
|
context::network::{NetworkAlertContext, NetworkDetailContext, NetworkListContext},
|
||||||
|
routes::authentication::Authenticated,
|
||||||
|
utils::{monitor, monitor::Threshold},
|
||||||
|
AP_IFACE, WLAN_IFACE,
|
||||||
|
};
|
||||||
|
|
||||||
// STRUCTS USED BY NETWORK ROUTES
|
// STRUCTS USED BY NETWORK ROUTES
|
||||||
|
|
||||||
|
@ -50,12 +50,12 @@ pub fn wifi_usage_reset(_auth: Authenticated) -> Flash<Redirect> {
|
||||||
pub fn connect_wifi(network: Form<Ssid>, _auth: Authenticated) -> Flash<Redirect> {
|
pub fn connect_wifi(network: Form<Ssid>, _auth: Authenticated) -> Flash<Redirect> {
|
||||||
let ssid = &network.ssid;
|
let ssid = &network.ssid;
|
||||||
let url = uri!(network_detail(ssid = ssid));
|
let url = uri!(network_detail(ssid = ssid));
|
||||||
match network_client::id("wlan0", ssid) {
|
match network::id(&*WLAN_IFACE, ssid) {
|
||||||
Ok(id) => match network_client::connect(&id, "wlan0") {
|
Ok(Some(id)) => match network::connect(&id, &*WLAN_IFACE) {
|
||||||
Ok(_) => Flash::success(Redirect::to(url), "Connected to chosen network"),
|
Ok(_) => Flash::success(Redirect::to(url), "Connected to chosen network"),
|
||||||
Err(_) => Flash::error(Redirect::to(url), "Failed to connect to chosen network"),
|
Err(_) => Flash::error(Redirect::to(url), "Failed to connect to chosen network"),
|
||||||
},
|
},
|
||||||
Err(_) => Flash::error(Redirect::to(url), "Failed to retrieve the network ID"),
|
_ => Flash::error(Redirect::to(url), "Failed to retrieve the network ID"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ pub fn connect_wifi(network: Form<Ssid>, _auth: Authenticated) -> Flash<Redirect
|
||||||
pub fn disconnect_wifi(network: Form<Ssid>, _auth: Authenticated) -> Flash<Redirect> {
|
pub fn disconnect_wifi(network: Form<Ssid>, _auth: Authenticated) -> Flash<Redirect> {
|
||||||
let ssid = &network.ssid;
|
let ssid = &network.ssid;
|
||||||
let url = uri!(network_home);
|
let url = uri!(network_home);
|
||||||
match network_client::disable("wlan0", ssid) {
|
match network::disable(&*WLAN_IFACE, ssid) {
|
||||||
Ok(_) => Flash::success(Redirect::to(url), "Disconnected from WiFi network"),
|
Ok(_) => Flash::success(Redirect::to(url), "Disconnected from WiFi network"),
|
||||||
Err(_) => Flash::error(Redirect::to(url), "Failed to disconnect from WiFi network"),
|
Err(_) => Flash::error(Redirect::to(url), "Failed to disconnect from WiFi network"),
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ pub fn disconnect_wifi(network: Form<Ssid>, _auth: Authenticated) -> Flash<Redir
|
||||||
pub fn forget_wifi(network: Form<Ssid>, _auth: Authenticated) -> Flash<Redirect> {
|
pub fn forget_wifi(network: Form<Ssid>, _auth: Authenticated) -> Flash<Redirect> {
|
||||||
let ssid = &network.ssid;
|
let ssid = &network.ssid;
|
||||||
let url = uri!(network_home);
|
let url = uri!(network_home);
|
||||||
match network_client::forget("wlan0", ssid) {
|
match network::forget(&*WLAN_IFACE, ssid) {
|
||||||
Ok(_) => Flash::success(Redirect::to(url), "WiFi credentials removed"),
|
Ok(_) => Flash::success(Redirect::to(url), "WiFi credentials removed"),
|
||||||
Err(_) => Flash::error(
|
Err(_) => Flash::error(
|
||||||
Redirect::to(url),
|
Redirect::to(url),
|
||||||
|
@ -84,21 +84,19 @@ pub fn forget_wifi(network: Form<Ssid>, _auth: Authenticated) -> Flash<Redirect>
|
||||||
|
|
||||||
#[get("/wifi/modify?<ssid>")]
|
#[get("/wifi/modify?<ssid>")]
|
||||||
pub fn wifi_password(ssid: &str, flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn wifi_password(ssid: &str, flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
let mut context = NetworkAddContext {
|
let mut context = Context::new();
|
||||||
back: Some("/settings/network/wifi".to_string()),
|
context.insert("back", &Some("/settings/network/wifi".to_string()));
|
||||||
flash_name: None,
|
context.insert("title", &Some("Update WiFi Password".to_string()));
|
||||||
flash_msg: None,
|
context.insert("selected", &Some(ssid.to_string()));
|
||||||
selected: Some(ssid.to_string()),
|
|
||||||
title: Some("Update WiFi Password".to_string()),
|
|
||||||
};
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
// add flash message contents to the context object
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
};
|
};
|
||||||
// template_dir is set in Rocket.toml
|
|
||||||
Template::render("settings/network/modify_ap", &context)
|
Template::render("settings/network/modify_ap", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/wifi/modify", data = "<wifi>")]
|
#[post("/wifi/modify", data = "<wifi>")]
|
||||||
|
@ -106,7 +104,7 @@ pub fn wifi_set_password(wifi: Form<WiFi>, _auth: Authenticated) -> Flash<Redire
|
||||||
let ssid = &wifi.ssid;
|
let ssid = &wifi.ssid;
|
||||||
let pass = &wifi.pass;
|
let pass = &wifi.pass;
|
||||||
let url = uri!(network_detail(ssid = ssid));
|
let url = uri!(network_detail(ssid = ssid));
|
||||||
match network_client::update("wlan0", ssid, pass) {
|
match network::update(&*WLAN_IFACE, ssid, pass) {
|
||||||
Ok(_) => Flash::success(Redirect::to(url), "WiFi password updated".to_string()),
|
Ok(_) => Flash::success(Redirect::to(url), "WiFi password updated".to_string()),
|
||||||
Err(_) => Flash::error(
|
Err(_) => Flash::error(
|
||||||
Redirect::to(url),
|
Redirect::to(url),
|
||||||
|
@ -117,174 +115,23 @@ pub fn wifi_set_password(wifi: Form<WiFi>, _auth: Authenticated) -> Flash<Redire
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings/network
|
// HELPERS AND ROUTES FOR /settings/network
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct NetworkContext {
|
|
||||||
pub ap_ip: String,
|
|
||||||
pub ap_ssid: String,
|
|
||||||
pub ap_state: String,
|
|
||||||
pub ap_traffic: Option<Traffic>,
|
|
||||||
pub wlan_ip: String,
|
|
||||||
pub wlan_rssi: Option<String>,
|
|
||||||
pub wlan_scan: Option<Vec<Scan>>,
|
|
||||||
pub wlan_ssid: String,
|
|
||||||
pub wlan_state: String,
|
|
||||||
pub wlan_status: String,
|
|
||||||
pub wlan_traffic: Option<Traffic>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
// allows for passing in the ssid of a chosen access point
|
|
||||||
// this is used in the network_detail template
|
|
||||||
pub selected: Option<String>,
|
|
||||||
// page title for header in navbar
|
|
||||||
pub title: Option<String>,
|
|
||||||
// url for back-arrow link
|
|
||||||
pub back: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetworkContext {
|
|
||||||
pub fn build() -> NetworkContext {
|
|
||||||
let ap_ip = match network_client::ip("ap0") {
|
|
||||||
Ok(ip) => ip,
|
|
||||||
Err(_) => "x.x.x.x".to_string(),
|
|
||||||
};
|
|
||||||
let ap_ssid = match network_client::ssid("ap0") {
|
|
||||||
Ok(ssid) => ssid,
|
|
||||||
Err(_) => "Not currently activated".to_string(),
|
|
||||||
};
|
|
||||||
let ap_state = match network_client::state("ap0") {
|
|
||||||
Ok(state) => state,
|
|
||||||
Err(_) => "Interface unavailable".to_string(),
|
|
||||||
};
|
|
||||||
let ap_traffic = match network_client::traffic("ap0") {
|
|
||||||
Ok(traffic) => {
|
|
||||||
let mut t = traffic;
|
|
||||||
// modify traffic values & assign measurement unit
|
|
||||||
// based on received and transmitted values
|
|
||||||
// if received > 999 MB, convert it to GB
|
|
||||||
if t.received > 1_047_527_424 {
|
|
||||||
t.received /= 1_073_741_824;
|
|
||||||
t.rx_unit = Some("GB".to_string());
|
|
||||||
} else if t.received > 0 {
|
|
||||||
// otherwise, convert it to MB
|
|
||||||
t.received = (t.received / 1024) / 1024;
|
|
||||||
t.rx_unit = Some("MB".to_string());
|
|
||||||
} else {
|
|
||||||
t.received = 0;
|
|
||||||
t.rx_unit = Some("MB".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.transmitted > 1_047_527_424 {
|
|
||||||
t.transmitted /= 1_073_741_824;
|
|
||||||
t.tx_unit = Some("GB".to_string());
|
|
||||||
} else if t.transmitted > 0 {
|
|
||||||
t.transmitted = (t.transmitted / 1024) / 1024;
|
|
||||||
t.tx_unit = Some("MB".to_string());
|
|
||||||
} else {
|
|
||||||
t.transmitted = 0;
|
|
||||||
t.tx_unit = Some("MB".to_string());
|
|
||||||
}
|
|
||||||
Some(t)
|
|
||||||
}
|
|
||||||
Err(_) => None,
|
|
||||||
};
|
|
||||||
let wlan_ip = match network_client::ip("wlan0") {
|
|
||||||
Ok(ip) => ip,
|
|
||||||
Err(_) => "x.x.x.x".to_string(),
|
|
||||||
};
|
|
||||||
let wlan_rssi = match network_client::rssi_percent("wlan0") {
|
|
||||||
Ok(rssi) => Some(rssi),
|
|
||||||
Err(_) => None,
|
|
||||||
};
|
|
||||||
let wlan_scan = match network_client::available_networks("wlan0") {
|
|
||||||
Ok(networks) => {
|
|
||||||
let scan: Vec<Scan> = serde_json::from_str(networks.as_str())
|
|
||||||
.expect("Failed to deserialize scan_networks response");
|
|
||||||
Some(scan)
|
|
||||||
}
|
|
||||||
Err(_) => None,
|
|
||||||
};
|
|
||||||
let wlan_ssid = match network_client::ssid("wlan0") {
|
|
||||||
Ok(ssid) => ssid,
|
|
||||||
Err(_) => "Not connected".to_string(),
|
|
||||||
};
|
|
||||||
let wlan_state = match network_client::state("wlan0") {
|
|
||||||
Ok(state) => state,
|
|
||||||
Err(_) => "Interface unavailable".to_string(),
|
|
||||||
};
|
|
||||||
let wlan_status = match network_client::status("wlan0") {
|
|
||||||
Ok(status) => status,
|
|
||||||
Err(_) => "Interface unavailable".to_string(),
|
|
||||||
};
|
|
||||||
let wlan_traffic = match network_client::traffic("wlan0") {
|
|
||||||
Ok(traffic) => {
|
|
||||||
let mut t = traffic;
|
|
||||||
// modify traffic values & assign measurement unit
|
|
||||||
// based on received and transmitted values
|
|
||||||
// if received > 999 MB, convert it to GB
|
|
||||||
if t.received > 1_047_527_424 {
|
|
||||||
t.received /= 1_073_741_824;
|
|
||||||
t.rx_unit = Some("GB".to_string());
|
|
||||||
} else if t.received > 0 {
|
|
||||||
// otherwise, convert it to MB
|
|
||||||
t.received = (t.received / 1024) / 1024;
|
|
||||||
t.rx_unit = Some("MB".to_string());
|
|
||||||
} else {
|
|
||||||
t.received = 0;
|
|
||||||
t.rx_unit = Some("MB".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.transmitted > 1_047_527_424 {
|
|
||||||
t.transmitted /= 1_073_741_824;
|
|
||||||
t.tx_unit = Some("GB".to_string());
|
|
||||||
} else if t.transmitted > 0 {
|
|
||||||
t.transmitted = (t.transmitted / 1024) / 1024;
|
|
||||||
t.tx_unit = Some("MB".to_string());
|
|
||||||
} else {
|
|
||||||
t.transmitted = 0;
|
|
||||||
t.tx_unit = Some("MB".to_string());
|
|
||||||
}
|
|
||||||
Some(t)
|
|
||||||
}
|
|
||||||
Err(_) => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
NetworkContext {
|
|
||||||
ap_ip,
|
|
||||||
ap_ssid,
|
|
||||||
ap_state,
|
|
||||||
ap_traffic,
|
|
||||||
wlan_ip,
|
|
||||||
wlan_rssi,
|
|
||||||
wlan_scan,
|
|
||||||
wlan_ssid,
|
|
||||||
wlan_state,
|
|
||||||
wlan_status,
|
|
||||||
wlan_traffic,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
selected: None,
|
|
||||||
title: None,
|
|
||||||
back: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub fn network_home(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn network_home(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
// assign context through context_builder call
|
// assign context
|
||||||
let mut context = NetworkContext::build();
|
let mut context = Context::new();
|
||||||
// set back button (nav) url
|
context.insert("back", &Some("/settings"));
|
||||||
context.back = Some("/settings".to_string());
|
context.insert("title", &Some("Network Configuration"));
|
||||||
// set page title
|
context.insert("ap_state", &context::network::ap_state());
|
||||||
context.title = Some("Network Configuration".to_string());
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
// add flash message contents to the context object
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
};
|
};
|
||||||
|
|
||||||
// template_dir is set in Rocket.toml
|
// template_dir is set in Rocket.toml
|
||||||
Template::render("settings/network/menu", &context)
|
Template::render("settings/network/menu", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings/network/ap/activate
|
// HELPERS AND ROUTES FOR /settings/network/ap/activate
|
||||||
|
@ -293,7 +140,7 @@ pub fn network_home(flash: Option<FlashMessage>, _auth: Authenticated) -> Templa
|
||||||
pub fn deploy_ap(_auth: Authenticated) -> Flash<Redirect> {
|
pub fn deploy_ap(_auth: Authenticated) -> Flash<Redirect> {
|
||||||
// activate the wireless access point
|
// activate the wireless access point
|
||||||
debug!("Activating WiFi access point.");
|
debug!("Activating WiFi access point.");
|
||||||
match network_client::activate_ap() {
|
match network::start_iface_service(&*AP_IFACE) {
|
||||||
Ok(_) => Flash::success(
|
Ok(_) => Flash::success(
|
||||||
Redirect::to("/settings/network"),
|
Redirect::to("/settings/network"),
|
||||||
"Activated WiFi access point",
|
"Activated WiFi access point",
|
||||||
|
@ -307,252 +154,37 @@ pub fn deploy_ap(_auth: Authenticated) -> Flash<Redirect> {
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings/network/wifi
|
// HELPERS AND ROUTES FOR /settings/network/wifi
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct NetworkListContext {
|
|
||||||
pub ap_state: String,
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub wlan_networks: HashMap<String, String>,
|
|
||||||
pub wlan_ssid: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetworkListContext {
|
|
||||||
pub fn build() -> NetworkListContext {
|
|
||||||
// list of networks saved in the wpa_supplicant.conf
|
|
||||||
let wlan_list = match network_client::saved_networks() {
|
|
||||||
Ok(ssids) => {
|
|
||||||
let networks: Vec<Networks> = serde_json::from_str(ssids.as_str())
|
|
||||||
.expect("Failed to deserialize scan_list response");
|
|
||||||
networks
|
|
||||||
}
|
|
||||||
Err(_) => Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// list of networks currently in range (online & accessible)
|
|
||||||
let wlan_scan = match network_client::available_networks("wlan0") {
|
|
||||||
Ok(networks) => {
|
|
||||||
let scan: Vec<Networks> = serde_json::from_str(networks.as_str())
|
|
||||||
.expect("Failed to deserialize scan_networks response");
|
|
||||||
scan
|
|
||||||
}
|
|
||||||
Err(_) => Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let wlan_ssid = match network_client::ssid("wlan0") {
|
|
||||||
Ok(ssid) => ssid,
|
|
||||||
Err(_) => "Not connected".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// create a hashmap to combine wlan_list & wlan_scan without repetition
|
|
||||||
let mut wlan_networks = HashMap::new();
|
|
||||||
for ap in wlan_scan {
|
|
||||||
wlan_networks.insert(ap.ssid, "Available".to_string());
|
|
||||||
}
|
|
||||||
for network in wlan_list {
|
|
||||||
// insert ssid (with state) only if it doesn't already exist
|
|
||||||
wlan_networks
|
|
||||||
.entry(network.ssid)
|
|
||||||
.or_insert_with(|| "Not in range".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
let ap_state = match network_client::state("ap0") {
|
|
||||||
Ok(state) => state,
|
|
||||||
Err(_) => "Interface unavailable".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
NetworkListContext {
|
|
||||||
ap_state,
|
|
||||||
back: None,
|
|
||||||
flash_msg: None,
|
|
||||||
flash_name: None,
|
|
||||||
title: None,
|
|
||||||
wlan_networks,
|
|
||||||
wlan_ssid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/wifi")]
|
#[get("/wifi")]
|
||||||
pub fn wifi_list(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn wifi_list(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
// assign context through context_builder call
|
// assign context through context_builder call
|
||||||
let mut context = NetworkListContext::build();
|
let mut context = NetworkListContext::build();
|
||||||
context.back = Some("/settings/network".to_string());
|
context.back = Some("/settings/network".to_string());
|
||||||
context.title = Some("WiFi Networks".to_string());
|
context.title = Some("WiFi Networks".to_string());
|
||||||
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
// add flash message contents to the context object
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.flash_name = Some(flash.kind().to_string());
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
context.flash_msg = Some(flash.message().to_string());
|
||||||
};
|
};
|
||||||
// template_dir is set in Rocket.toml
|
|
||||||
Template::render("settings/network/list_aps", &context)
|
Template::render("settings/network/list_aps", &context)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings/network/wifi<ssid>
|
// HELPERS AND ROUTES FOR /settings/network/wifi<ssid>
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct NetworkDetailContext {
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
pub saved_aps: Vec<Networks>,
|
|
||||||
pub selected: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub wlan_ip: String,
|
|
||||||
pub wlan_networks: HashMap<String, AccessPoint>,
|
|
||||||
pub wlan_rssi: Option<String>,
|
|
||||||
pub wlan_ssid: String,
|
|
||||||
pub wlan_state: String,
|
|
||||||
pub wlan_status: String,
|
|
||||||
pub wlan_traffic: Option<Traffic>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetworkDetailContext {
|
|
||||||
pub fn build() -> NetworkDetailContext {
|
|
||||||
let wlan_ip = match network_client::ip("wlan0") {
|
|
||||||
Ok(ip) => ip,
|
|
||||||
Err(_) => "x.x.x.x".to_string(),
|
|
||||||
};
|
|
||||||
// list of networks saved in wpa_supplicant.conf
|
|
||||||
let wlan_list = match network_client::saved_networks() {
|
|
||||||
Ok(ssids) => {
|
|
||||||
let networks: Vec<Networks> = serde_json::from_str(ssids.as_str())
|
|
||||||
.expect("Failed to deserialize scan_list response");
|
|
||||||
networks
|
|
||||||
}
|
|
||||||
Err(_) => Vec::new(),
|
|
||||||
};
|
|
||||||
// list of networks saved in wpa_supplicant.conf
|
|
||||||
// HACK: we're running the same function twice (wlan_list)
|
|
||||||
// see if we can implement clone for Vec<Networks> instead
|
|
||||||
let saved_aps = match network_client::saved_networks() {
|
|
||||||
Ok(ssids) => {
|
|
||||||
let networks: Vec<Networks> = serde_json::from_str(ssids.as_str())
|
|
||||||
.expect("Failed to deserialize scan_list response");
|
|
||||||
networks
|
|
||||||
}
|
|
||||||
Err(_) => Vec::new(),
|
|
||||||
};
|
|
||||||
let wlan_rssi = match network_client::rssi_percent("wlan0") {
|
|
||||||
Ok(rssi) => Some(rssi),
|
|
||||||
Err(_) => None,
|
|
||||||
};
|
|
||||||
// list of networks currently in range (online & accessible)
|
|
||||||
let wlan_scan = match network_client::available_networks("wlan0") {
|
|
||||||
Ok(networks) => {
|
|
||||||
let scan: Vec<Scan> = serde_json::from_str(networks.as_str())
|
|
||||||
.expect("Failed to deserialize scan_networks response");
|
|
||||||
scan
|
|
||||||
}
|
|
||||||
Err(_) => Vec::new(),
|
|
||||||
};
|
|
||||||
let wlan_ssid = match network_client::ssid("wlan0") {
|
|
||||||
Ok(ssid) => ssid,
|
|
||||||
Err(_) => "Not connected".to_string(),
|
|
||||||
};
|
|
||||||
let wlan_state = match network_client::state("wlan0") {
|
|
||||||
Ok(state) => state,
|
|
||||||
Err(_) => "Interface unavailable".to_string(),
|
|
||||||
};
|
|
||||||
let wlan_status = match network_client::status("wlan0") {
|
|
||||||
Ok(status) => status,
|
|
||||||
Err(_) => "Interface unavailable".to_string(),
|
|
||||||
};
|
|
||||||
let wlan_traffic = match network_client::traffic("wlan0") {
|
|
||||||
Ok(traffic) => {
|
|
||||||
let mut t = traffic;
|
|
||||||
// modify traffic values & assign measurement unit
|
|
||||||
// based on received and transmitted values
|
|
||||||
// if received > 999 MB, convert it to GB
|
|
||||||
if t.received > 1_047_527_424 {
|
|
||||||
t.received /= 1_073_741_824;
|
|
||||||
t.rx_unit = Some("GB".to_string());
|
|
||||||
} else if t.received > 0 {
|
|
||||||
// otherwise, convert it to MB
|
|
||||||
t.received = (t.received / 1024) / 1024;
|
|
||||||
t.rx_unit = Some("MB".to_string());
|
|
||||||
} else {
|
|
||||||
t.received = 0;
|
|
||||||
t.rx_unit = Some("MB".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.transmitted > 1_047_527_424 {
|
|
||||||
t.transmitted /= 1_073_741_824;
|
|
||||||
t.tx_unit = Some("GB".to_string());
|
|
||||||
} else if t.transmitted > 0 {
|
|
||||||
t.transmitted = (t.transmitted / 1024) / 1024;
|
|
||||||
t.tx_unit = Some("MB".to_string());
|
|
||||||
} else {
|
|
||||||
t.transmitted = 0;
|
|
||||||
t.tx_unit = Some("MB".to_string());
|
|
||||||
}
|
|
||||||
Some(t)
|
|
||||||
}
|
|
||||||
Err(_) => None,
|
|
||||||
};
|
|
||||||
// create a hashmap to combine wlan_list & wlan_scan without repetition
|
|
||||||
let mut wlan_networks = HashMap::new();
|
|
||||||
for ap in wlan_scan {
|
|
||||||
let ssid = ap.ssid.clone();
|
|
||||||
let rssi = ap.signal_level.clone();
|
|
||||||
// parse the string to a signed integer (for math)
|
|
||||||
let rssi_parsed = rssi.parse::<i32>().unwrap();
|
|
||||||
// perform rssi (dBm) to quality (%) conversion
|
|
||||||
let quality_percent = 2 * (rssi_parsed + 100);
|
|
||||||
let ap_detail = AccessPoint {
|
|
||||||
detail: Some(ap),
|
|
||||||
state: "Available".to_string(),
|
|
||||||
signal: Some(quality_percent),
|
|
||||||
};
|
|
||||||
wlan_networks.insert(ssid, ap_detail);
|
|
||||||
}
|
|
||||||
for network in wlan_list {
|
|
||||||
// avoid repetition by checking that ssid is not already in list
|
|
||||||
if !wlan_networks.contains_key(&network.ssid) {
|
|
||||||
let ssid = network.ssid.clone();
|
|
||||||
let net_detail = AccessPoint {
|
|
||||||
detail: None,
|
|
||||||
state: "Not in range".to_string(),
|
|
||||||
signal: None,
|
|
||||||
};
|
|
||||||
wlan_networks.insert(ssid, net_detail);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkDetailContext {
|
|
||||||
back: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
saved_aps,
|
|
||||||
selected: None,
|
|
||||||
title: None,
|
|
||||||
wlan_ip,
|
|
||||||
wlan_networks,
|
|
||||||
wlan_rssi,
|
|
||||||
wlan_ssid,
|
|
||||||
wlan_state,
|
|
||||||
wlan_status,
|
|
||||||
wlan_traffic,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/wifi?<ssid>")]
|
#[get("/wifi?<ssid>")]
|
||||||
pub fn network_detail(ssid: &str, flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn network_detail(ssid: &str, flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
// assign context through context_builder call
|
|
||||||
let mut context = NetworkDetailContext::build();
|
let mut context = NetworkDetailContext::build();
|
||||||
context.back = Some("/settings/network/wifi".to_string());
|
context.back = Some("/settings/network/wifi".to_string());
|
||||||
context.title = Some("WiFi Network".to_string());
|
context.title = Some("WiFi Network".to_string());
|
||||||
context.selected = Some(ssid.to_string());
|
context.selected = Some(ssid.to_string());
|
||||||
// check to see if there is a flash message to display
|
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.flash_name = Some(flash.kind().to_string());
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
context.flash_msg = Some(flash.message().to_string());
|
||||||
};
|
};
|
||||||
// template_dir is set in Rocket.toml
|
|
||||||
Template::render("settings/network/ap_details", &context)
|
Template::render("settings/network/ap_details", &context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,7 +194,7 @@ pub fn network_detail(ssid: &str, flash: Option<FlashMessage>, _auth: Authentica
|
||||||
pub fn deploy_client(_auth: Authenticated) -> Flash<Redirect> {
|
pub fn deploy_client(_auth: Authenticated) -> Flash<Redirect> {
|
||||||
// activate the wireless client
|
// activate the wireless client
|
||||||
debug!("Activating WiFi client mode.");
|
debug!("Activating WiFi client mode.");
|
||||||
match network_client::activate_client() {
|
match network::start_iface_service(&*WLAN_IFACE) {
|
||||||
Ok(_) => Flash::success(Redirect::to("/settings/network"), "Activated WiFi client"),
|
Ok(_) => Flash::success(Redirect::to("/settings/network"), "Activated WiFi client"),
|
||||||
Err(_) => Flash::error(
|
Err(_) => Flash::error(
|
||||||
Redirect::to("/settings/network"),
|
Redirect::to("/settings/network"),
|
||||||
|
@ -575,152 +207,85 @@ pub fn deploy_client(_auth: Authenticated) -> Flash<Redirect> {
|
||||||
|
|
||||||
#[get("/wifi/add")]
|
#[get("/wifi/add")]
|
||||||
pub fn add_wifi(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn add_wifi(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
let mut context = NetworkContext::build();
|
let mut context = Context::new();
|
||||||
// set back icon link to network route
|
context.insert("back", &Some("/settings/network".to_string()));
|
||||||
context.back = Some("/settings/network".to_string());
|
context.insert("title", &Some("Add WiFi Network".to_string()));
|
||||||
context.title = Some("Add WiFi Network".to_string());
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
// add flash message contents to the context object
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
};
|
};
|
||||||
// template_dir is set in Rocket.toml
|
|
||||||
Template::render("settings/network/add_ap", &context)
|
|
||||||
}
|
|
||||||
|
|
||||||
// used in /settings/network/wifi/add?<ssid>
|
Template::render("settings/network/add_ap", &context.into_json())
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct NetworkAddContext {
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
pub selected: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetworkAddContext {
|
|
||||||
pub fn build() -> NetworkAddContext {
|
|
||||||
NetworkAddContext {
|
|
||||||
back: None,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
selected: None,
|
|
||||||
title: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/wifi/add?<ssid>")]
|
#[get("/wifi/add?<ssid>")]
|
||||||
pub fn add_ssid(ssid: &str, flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn add_ssid(ssid: &str, flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
let mut context = NetworkAddContext::build();
|
let mut context = Context::new();
|
||||||
context.back = Some("/settings/network/wifi".to_string());
|
context.insert("back", &Some("/settings/network".to_string()));
|
||||||
context.selected = Some(ssid.to_string());
|
context.insert("title", &Some("Add WiFi Network".to_string()));
|
||||||
context.title = Some("Add WiFi Network".to_string());
|
context.insert("selected", &Some(ssid.to_string()));
|
||||||
|
|
||||||
// check to see if there is a flash message to display
|
// check to see if there is a flash message to display
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
// add flash message contents to the context object
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||||
};
|
};
|
||||||
// template_dir is set in Rocket.toml
|
|
||||||
Template::render("settings/network/add_ap", &context)
|
Template::render("settings/network/add_ap", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/wifi/add", data = "<wifi>")]
|
#[post("/wifi/add", data = "<wifi>")]
|
||||||
pub fn add_credentials(wifi: Form<WiFi>, _auth: Authenticated) -> Template {
|
pub fn add_credentials(wifi: Form<WiFi>, _auth: Authenticated) -> Template {
|
||||||
|
let mut context = Context::new();
|
||||||
|
context.insert("back", &Some("/settings/network".to_string()));
|
||||||
|
context.insert("title", &Some("Add WiFi Network".to_string()));
|
||||||
|
|
||||||
// check if the credentials already exist for this access point
|
// check if the credentials already exist for this access point
|
||||||
// note: this is nicer but it's an unstable feature:
|
// note: this is nicer but it's an unstable feature:
|
||||||
// if check_saved_aps(&wifi.ssid).contains(true)
|
// if check_saved_aps(&wifi.ssid).contains(true)
|
||||||
// use unwrap_or instead, set value to false if err is returned
|
// use unwrap_or instead, set value to false if err is returned
|
||||||
let creds_exist = network_client::saved_ap(&wifi.ssid).unwrap_or(false);
|
//let creds_exist = network::saved_networks(&wifi.ssid).unwrap_or(false);
|
||||||
if creds_exist {
|
let creds_exist = match network::saved_networks() {
|
||||||
let mut context = NetworkAddContext::build();
|
Ok(Some(networks)) => networks.contains(&wifi.ssid),
|
||||||
context.back = Some("/settings/network".to_string());
|
_ => false,
|
||||||
context.flash_name = Some("error".to_string());
|
|
||||||
context.flash_msg =
|
|
||||||
Some("Network credentials already exist for this access point".to_string());
|
|
||||||
context.title = Some("Add WiFi Network".to_string());
|
|
||||||
// return early from handler with "creds already exist" message
|
|
||||||
return Template::render("settings/network/add_ap", &context);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// if credentials not found, generate and write wifi config to wpa_supplicant
|
// if credentials not found, generate and write wifi config to wpa_supplicant
|
||||||
match network_client::add(&wifi.ssid, &wifi.pass) {
|
let (flash_name, flash_msg) = if creds_exist {
|
||||||
Ok(_) => {
|
(
|
||||||
debug!("Added WiFi credentials.");
|
"error".to_string(),
|
||||||
// force reread of wpa_supplicant.conf file with new credentials
|
"Network credentials already exist for this access point".to_string(),
|
||||||
match network_client::reconfigure() {
|
)
|
||||||
Ok(_) => debug!("Successfully reconfigured wpa_supplicant"),
|
} else {
|
||||||
Err(_) => warn!("Failed to reconfigure wpa_supplicant"),
|
match network::add(&*WLAN_IFACE, &wifi.ssid, &wifi.pass) {
|
||||||
|
Ok(_) => {
|
||||||
|
debug!("Added WiFi credentials.");
|
||||||
|
// force reread of wpa_supplicant.conf file with new credentials
|
||||||
|
match network::reconfigure() {
|
||||||
|
Ok(_) => debug!("Successfully reconfigured wpa_supplicant"),
|
||||||
|
Err(_) => warn!("Failed to reconfigure wpa_supplicant"),
|
||||||
|
}
|
||||||
|
("success".to_string(), "Added WiFi credentials".to_string())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Failed to add WiFi credentials.");
|
||||||
|
("error".to_string(), format!("{}", e))
|
||||||
}
|
}
|
||||||
let mut context = NetworkAddContext::build();
|
|
||||||
context.back = Some("/settings/network".to_string());
|
|
||||||
context.flash_name = Some("success".to_string());
|
|
||||||
context.flash_msg = Some("Added WiFi credentials".to_string());
|
|
||||||
context.title = Some("Add WiFi Network".to_string());
|
|
||||||
Template::render("settings/network/add_ap", &context)
|
|
||||||
}
|
}
|
||||||
Err(_) => {
|
};
|
||||||
debug!("Failed to add WiFi credentials.");
|
|
||||||
let mut context = NetworkAddContext::build();
|
context.insert("flash_name", &Some(flash_name));
|
||||||
context.back = Some("/settings/network".to_string());
|
context.insert("flash_msg", &Some(flash_msg));
|
||||||
context.flash_name = Some("error".to_string());
|
|
||||||
context.flash_msg = Some("Failed to add WiFi credentials".to_string());
|
Template::render("settings/network/add_ap", &context.into_json())
|
||||||
context.title = Some("Add WiFi Network".to_string());
|
|
||||||
Template::render("settings/network/add_ap", &context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR WIFI USAGE
|
// HELPERS AND ROUTES FOR WIFI USAGE
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct NetworkAlertContext {
|
|
||||||
pub alert: Alert,
|
|
||||||
pub back: Option<String>,
|
|
||||||
pub data_total: Data, // combined stored and current wifi traffic in bytes
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
pub threshold: Threshold,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub traffic: Traffic, // current wifi traffic in bytes (since boot)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetworkAlertContext {
|
|
||||||
pub fn build() -> NetworkAlertContext {
|
|
||||||
let alert = monitor::get_alerts().unwrap();
|
|
||||||
// stored wifi data values as bytes
|
|
||||||
let stored_traffic = monitor::get_data().unwrap();
|
|
||||||
let threshold = monitor::get_thresholds().unwrap();
|
|
||||||
// current wifi traffic values as bytes
|
|
||||||
let traffic = match network_client::traffic("wlan0") {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(_) => Traffic {
|
|
||||||
received: 0,
|
|
||||||
transmitted: 0,
|
|
||||||
rx_unit: None,
|
|
||||||
tx_unit: None,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let current_traffic = traffic.received + traffic.transmitted;
|
|
||||||
let total = stored_traffic.total + current_traffic;
|
|
||||||
let data_total = Data { total };
|
|
||||||
|
|
||||||
NetworkAlertContext {
|
|
||||||
alert,
|
|
||||||
back: None,
|
|
||||||
data_total,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
threshold,
|
|
||||||
title: None,
|
|
||||||
traffic,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/wifi/usage")]
|
#[get("/wifi/usage")]
|
||||||
pub fn wifi_usage(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn wifi_usage(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
let mut context = NetworkAlertContext::build();
|
let mut context = NetworkAlertContext::build();
|
||||||
|
|
|
@ -1,162 +1,21 @@
|
||||||
use rocket::{get, request::FlashMessage};
|
use rocket::{get, request::FlashMessage};
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::Template;
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
use peach_network::{
|
|
||||||
network,
|
|
||||||
network::{Status, Traffic},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use crate::context::network::NetworkStatusContext;
|
||||||
use crate::routes::authentication::Authenticated;
|
use crate::routes::authentication::Authenticated;
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /status/network
|
// HELPERS AND ROUTES FOR /status/network
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct IfaceTraffic {
|
|
||||||
pub rx: u64,
|
|
||||||
pub rx_unit: Option<String>,
|
|
||||||
pub tx: u64,
|
|
||||||
pub tx_unit: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IfaceTraffic {
|
|
||||||
fn default() -> Self {
|
|
||||||
IfaceTraffic {
|
|
||||||
rx: 0,
|
|
||||||
rx_unit: None,
|
|
||||||
tx: 0,
|
|
||||||
tx_unit: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_traffic(traffic: Traffic) -> Option<IfaceTraffic> {
|
|
||||||
let mut t = IfaceTraffic::default();
|
|
||||||
// modify traffic values & assign measurement units
|
|
||||||
// based on received and transmitted values.
|
|
||||||
// if received > 999 MB, convert it to GB
|
|
||||||
if traffic.received > 1_047_527_424 {
|
|
||||||
t.rx = traffic.received / 1_073_741_824;
|
|
||||||
t.rx_unit = Some("GB".to_string());
|
|
||||||
} else if traffic.received > 0 {
|
|
||||||
// otherwise, convert it to MB
|
|
||||||
t.rx = (traffic.received / 1024) / 1024;
|
|
||||||
t.rx_unit = Some("MB".to_string());
|
|
||||||
} else {
|
|
||||||
t.rx = 0;
|
|
||||||
t.rx_unit = Some("MB".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if traffic.transmitted > 1_047_527_424 {
|
|
||||||
t.tx = traffic.transmitted / 1_073_741_824;
|
|
||||||
t.tx_unit = Some("GB".to_string());
|
|
||||||
} else if traffic.transmitted > 0 {
|
|
||||||
t.tx = (traffic.transmitted / 1024) / 1024;
|
|
||||||
t.tx_unit = Some("MB".to_string());
|
|
||||||
} else {
|
|
||||||
t.tx = 0;
|
|
||||||
t.tx_unit = Some("MB".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct NetworkContext {
|
|
||||||
pub ap_ip: String,
|
|
||||||
pub ap_ssid: String,
|
|
||||||
pub ap_state: String,
|
|
||||||
pub ap_traffic: Option<IfaceTraffic>,
|
|
||||||
pub wlan_ip: String,
|
|
||||||
pub wlan_rssi: Option<String>,
|
|
||||||
pub wlan_ssid: String,
|
|
||||||
pub wlan_state: String,
|
|
||||||
pub wlan_status: Option<Status>,
|
|
||||||
pub wlan_traffic: Option<IfaceTraffic>,
|
|
||||||
pub flash_name: Option<String>,
|
|
||||||
pub flash_msg: Option<String>,
|
|
||||||
// page title for header in navbar
|
|
||||||
pub title: Option<String>,
|
|
||||||
// url for back-arrow link
|
|
||||||
pub back: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetworkContext {
|
|
||||||
pub fn build() -> NetworkContext {
|
|
||||||
let ap_ip = match network::ip("ap0") {
|
|
||||||
Ok(Some(ip)) => ip,
|
|
||||||
_ => "x.x.x.x".to_string(),
|
|
||||||
};
|
|
||||||
let ap_ssid = match network::ssid("ap0") {
|
|
||||||
Ok(Some(ssid)) => ssid,
|
|
||||||
_ => "Not currently activated".to_string(),
|
|
||||||
};
|
|
||||||
let ap_state = match network::state("ap0") {
|
|
||||||
Ok(Some(state)) => state,
|
|
||||||
_ => "Interface unavailable".to_string(),
|
|
||||||
};
|
|
||||||
let ap_traffic = match network::traffic("ap0") {
|
|
||||||
// convert bytes to mb or gb and add appropriate units
|
|
||||||
Ok(Some(traffic)) => convert_traffic(traffic),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
let wlan_ip = match network::ip("wlan0") {
|
|
||||||
Ok(Some(ip)) => ip,
|
|
||||||
_ => "x.x.x.x".to_string(),
|
|
||||||
};
|
|
||||||
let wlan_rssi = match network::rssi_percent("wlan0") {
|
|
||||||
Ok(rssi) => rssi,
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
let wlan_ssid = match network::ssid("wlan0") {
|
|
||||||
Ok(Some(ssid)) => ssid,
|
|
||||||
_ => "Not connected".to_string(),
|
|
||||||
};
|
|
||||||
let wlan_state = match network::state("wlan0") {
|
|
||||||
Ok(Some(state)) => state,
|
|
||||||
_ => "Interface unavailable".to_string(),
|
|
||||||
};
|
|
||||||
let wlan_status = match network::status("wlan0") {
|
|
||||||
Ok(status) => status,
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
let wlan_traffic = match network::traffic("wlan0") {
|
|
||||||
// convert bytes to mb or gb and add appropriate units
|
|
||||||
Ok(Some(traffic)) => convert_traffic(traffic),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
NetworkContext {
|
|
||||||
ap_ip,
|
|
||||||
ap_ssid,
|
|
||||||
ap_state,
|
|
||||||
ap_traffic,
|
|
||||||
wlan_ip,
|
|
||||||
wlan_rssi,
|
|
||||||
wlan_ssid,
|
|
||||||
wlan_state,
|
|
||||||
wlan_status,
|
|
||||||
wlan_traffic,
|
|
||||||
flash_name: None,
|
|
||||||
flash_msg: None,
|
|
||||||
title: None,
|
|
||||||
back: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/network")]
|
#[get("/network")]
|
||||||
pub fn network_status(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
pub fn network_status(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||||
// assign context through context_builder call
|
let mut context = NetworkStatusContext::build();
|
||||||
let mut context = NetworkContext::build();
|
|
||||||
context.back = Some("/status".to_string());
|
context.back = Some("/status".to_string());
|
||||||
context.title = Some("Network Status".to_string());
|
context.title = Some("Network Status".to_string());
|
||||||
// check to see if there is a flash message to display
|
|
||||||
if let Some(flash) = flash {
|
if let Some(flash) = flash {
|
||||||
// add flash message contents to the context object
|
|
||||||
context.flash_name = Some(flash.kind().to_string());
|
context.flash_name = Some(flash.kind().to_string());
|
||||||
context.flash_msg = Some(flash.message().to_string());
|
context.flash_msg = Some(flash.message().to_string());
|
||||||
};
|
};
|
||||||
// template_dir is set in Rocket.toml
|
|
||||||
Template::render("status/network", &context)
|
Template::render("status/network", &context)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,8 @@ 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::{Build, Config, Rocket};
|
use rocket::{Build, Config, Rocket};
|
||||||
|
|
||||||
use crate::utils::build_json_response;
|
|
||||||
|
|
||||||
use super::init_rocket;
|
use super::init_rocket;
|
||||||
|
|
||||||
// define authentication mode
|
// define authentication mode
|
||||||
|
@ -328,6 +325,13 @@ fn network_settings_menu_html() {
|
||||||
assert!(body.contains("Network Configuration"));
|
assert!(body.contains("Network Configuration"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
NOTE: these tests are commented-out for the moment, due to the fact that they
|
||||||
|
invoke system commands (resulting in a `sudo` password request during
|
||||||
|
test execution). see if we can find a way to test the results without
|
||||||
|
triggering the `systemctl` call.
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deploy_ap() {
|
fn deploy_ap() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
|
@ -337,6 +341,16 @@ fn deploy_ap() {
|
||||||
assert_eq!(response.content_type(), None);
|
assert_eq!(response.content_type(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deploy_client() {
|
||||||
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
|
let response = client.get("/settings/network/wifi/activate").dispatch();
|
||||||
|
// check for 303 status (redirect)
|
||||||
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
|
assert_eq!(response.content_type(), None);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dns_settings_html() {
|
fn dns_settings_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
|
@ -373,15 +387,6 @@ fn ap_details_html() {
|
||||||
//assert!(body.contains("Network not found"));
|
//assert!(body.contains("Network not found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deploy_client() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client.get("/settings/network/wifi/activate").dispatch();
|
|
||||||
// check for 303 status (redirect)
|
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
|
||||||
assert_eq!(response.content_type(), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_ap_html() {
|
fn add_ap_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
||||||
|
@ -509,179 +514,6 @@ fn network_status_html() {
|
||||||
assert!(body.contains("DOWNLOAD"));
|
assert!(body.contains("DOWNLOAD"));
|
||||||
assert!(body.contains("UPLOAD"));
|
assert!(body.contains("UPLOAD"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON API ROUTES
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn activate_ap() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.post("/api/v1/network/activate_ap")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn activate_client() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.post("/api/v1/network/activate_client")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_ip() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.get("/api/v1/network/ip")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
let body = response.into_string().unwrap();
|
|
||||||
assert!(body.contains("wlan0"));
|
|
||||||
assert!(body.contains("ap0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_rssi() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.get("/api/v1/network/rssi")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
let body = response.into_string().unwrap();
|
|
||||||
assert!(body.contains("Not currently connected to an access point."));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_ssid() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.get("/api/v1/network/ssid")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
let body = response.into_string().unwrap();
|
|
||||||
assert!(body.contains("Not currently connected to an access point."));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_state() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.get("/api/v1/network/state")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
let body = response.into_string().unwrap();
|
|
||||||
assert!(body.contains("ap0"));
|
|
||||||
assert!(body.contains("wlan0"));
|
|
||||||
assert!(body.contains("unavailable"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_status() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.get("/api/v1/network/status")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
let body = response.into_string().unwrap();
|
|
||||||
assert!(body.contains("Not currently connected to an access point."));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn scan_networks() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.get("/api/v1/network/wifi")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
let body = response.into_string().unwrap();
|
|
||||||
assert!(body.contains("Unable to scan for networks. Interface may be deactivated."));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add_wifi() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.post("/api/v1/network/wifi")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.body(r#"{ "ssid": "Home", "pass": "Password" }"#)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
let body = response.into_string().unwrap();
|
|
||||||
assert!(body.contains("Failed to add WiFi credentials."));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_wifi() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.post("/api/v1/network/wifi/forget")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.body(r#"{ "ssid": "Home" }"#)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
let body = response.into_string().unwrap();
|
|
||||||
assert!(body.contains("Failed to remove WiFi network credentials."));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn new_password() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.post("/api/v1/network/wifi/modify")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.body(r#"{ "ssid": "Home", "pass": "Password" }"#)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
let body = response.into_string().unwrap();
|
|
||||||
assert!(body.contains("Failed to update WiFi password."));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ping_pong() {
|
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
|
||||||
let response = client
|
|
||||||
.get("/api/v1/ping")
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.content_type(), Some(ContentType::JSON));
|
|
||||||
let body = response.into_string().unwrap();
|
|
||||||
assert!(body.contains("pong!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// HELPER FUNCTION TESTS
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_build_json_response() {
|
|
||||||
let status = "success".to_string();
|
|
||||||
let data = json!("WiFi credentials added.".to_string());
|
|
||||||
let j: Value = build_json_response(status, Some(data), None);
|
|
||||||
assert_eq!(j["status"], "success");
|
|
||||||
assert_eq!(j["data"], "WiFi credentials added.");
|
|
||||||
assert_eq!(j["msg"], json!(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
// FILE TESTS
|
// FILE TESTS
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use nest::{Error, Store, Value};
|
use nest::{Error, Store, Value};
|
||||||
use rocket::form::{FromForm};
|
use rocket::form::FromForm;
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,6 @@
|
||||||
<!-- display error message -->
|
<!-- display error message -->
|
||||||
<div class="center-text flash-message font-failure" style="padding-left: 5px;">{{ flash_msg }}.</div>
|
<div class="center-text flash-message font-failure" style="padding-left: 5px;">{{ flash_msg }}.</div>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
<!-- share ux information with the user if JS is disabled -->
|
|
||||||
<noscript>
|
|
||||||
<p class="center-text flash-message">This website will be unresponsive while the device shuts down or reboots.</p>
|
|
||||||
</noscript>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -24,9 +24,8 @@
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<!-- middle -->
|
<!-- middle -->
|
||||||
<a class="middle" href="/hello">
|
<a class="middle">
|
||||||
<div class="circle circle-large">
|
<div class="circle circle-large"></div>
|
||||||
</div>
|
|
||||||
</a>
|
</a>
|
||||||
<!-- bottom-left -->
|
<!-- bottom-left -->
|
||||||
<!-- SYSTEM STATUS LINK AND ICON -->
|
<!-- SYSTEM STATUS LINK AND ICON -->
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
<div class="card-container">
|
<div class="card-container">
|
||||||
<!-- FLASH MESSAGE -->
|
<!-- FLASH MESSAGE -->
|
||||||
{% include "snippets/flash_message" %}
|
{% include "snippets/flash_message" %}
|
||||||
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
|
||||||
{% include "snippets/noscript" %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -10,13 +10,8 @@
|
||||||
<a class="button button-secondary center" href="/settings/admin/configure" title="Cancel">Cancel</a>
|
<a class="button button-secondary center" href="/settings/admin/configure" title="Cancel">Cancel</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<!-- FLASH MESSAGE -->
|
||||||
<!-- FLASH MESSAGE -->
|
{% include "snippets/flash_message" %}
|
||||||
{% include "snippets/flash_message" %}
|
|
||||||
|
|
||||||
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
|
||||||
{% include "snippets/noscript" %}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<!-- CHANGE PASSWORD FORM -->
|
<!-- CHANGE PASSWORD FORM -->
|
||||||
<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/admin/change_password" method="post">
|
||||||
<!-- input for current password -->
|
<!-- input for current password -->
|
||||||
<input id="currentPassword" class="center input" name="current_password" type="password" placeholder="Current password" title="Current password" autofocus>
|
<input id="currentPassword" class="center input" name="current_password" type="password" placeholder="Current password" title="Current password" autofocus>
|
||||||
<!-- input for new password -->
|
<!-- input for new password -->
|
||||||
|
|
|
@ -20,11 +20,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="button button-primary center full-width" style="margin-top: 25px;" href="/settings/admin/add" title="Add Admin">Add Admin</a>
|
<a class="button button-primary center full-width" style="margin-top: 25px;" href="/settings/admin/add" title="Add Admin">Add Admin</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- FLASH MESSAGE -->
|
<!-- FLASH MESSAGE -->
|
||||||
{% include "snippets/flash_message" %}
|
{% include "snippets/flash_message" %}
|
||||||
|
|
||||||
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
|
||||||
{% include "snippets/noscript" %}
|
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -14,7 +14,5 @@
|
||||||
</form>
|
</form>
|
||||||
<!-- FLASH MESSAGE -->
|
<!-- FLASH MESSAGE -->
|
||||||
{% include "snippets/flash_message" %}
|
{% include "snippets/flash_message" %}
|
||||||
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
|
||||||
{% include "snippets/noscript" %}
|
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<!-- NETWORK ADD CREDENTIALS FORM -->
|
<!-- NETWORK ADD CREDENTIALS FORM -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="card-container">
|
<div class="card-container">
|
||||||
<form id="wifiCreds" action="/network/wifi/add" method="post">
|
<form id="wifiCreds" action="/settings/network/wifi/add" method="post">
|
||||||
<!-- input for network ssid -->
|
<!-- input for network ssid -->
|
||||||
<input id="ssid" name="ssid" class="center input" type="text" placeholder="SSID" title="Network name (SSID) for WiFi access point" value="{%- if selected -%}{{ selected }}{%- endif -%}" autofocus>
|
<input id="ssid" name="ssid" class="center input" type="text" placeholder="SSID" title="Network name (SSID) for WiFi access point" value="{%- if selected -%}{{ selected }}{%- endif -%}" autofocus>
|
||||||
<!-- input for network password -->
|
<!-- input for network password -->
|
||||||
|
|
|
@ -1,10 +1,19 @@
|
||||||
{%- extends "nav" -%}
|
{%- extends "nav" -%}
|
||||||
{%- block card -%}
|
{%- block card -%}
|
||||||
|
{# ASSIGN VARIABLES #}
|
||||||
|
{# ---------------- #}
|
||||||
|
{%- if data_total -%}
|
||||||
|
{% set data_usage_total = data_total.total / 1024 / 1024 | round -%}
|
||||||
|
{%- else -%}
|
||||||
|
{% set data_usage_total = "x" -%}
|
||||||
|
{% endif -%}
|
||||||
<!-- NETWORK DATA ALERTS VIEW -->
|
<!-- NETWORK DATA ALERTS VIEW -->
|
||||||
<form id="wifiAlerts" action="/settings/network/wifi/usage" class="card center" method="post">
|
<form id="wifiAlerts" action="/settings/network/wifi/usage" class="card center" method="post">
|
||||||
<div class="stack capsule" style="margin-left: 2rem; margin-right: 2rem;">
|
<div class="stack capsule" style="margin-left: 2rem; margin-right: 2rem;">
|
||||||
<div class="flex-grid">
|
<div class="flex-grid">
|
||||||
<label id="dataTotal" class="label-large" title="Data download total in MB">{{ data_total.total / 1024 / 1024 | round }}</label>
|
<label id="dataTotal" class="label-large" title="Data download total in MB">
|
||||||
|
{{ data_usage_total }}
|
||||||
|
</label>
|
||||||
<label class="label-small font-near-black">MB</label>
|
<label class="label-small font-near-black">MB</label>
|
||||||
</div>
|
</div>
|
||||||
<label class="center-text label-small font-gray">USAGE TOTAL</label>
|
<label class="center-text label-small font-gray">USAGE TOTAL</label>
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
<!-- share ux information with the user if JS is disabled -->
|
|
||||||
<noscript>
|
|
||||||
<div class="capsule flash-message info-border">
|
|
||||||
<p class="center-text">This website may be temporarily unresponsive while settings are being saved.</p>
|
|
||||||
</div>
|
|
||||||
</noscript>
|
|
Loading…
Reference in New Issue