2021-10-28 08:01:31 +00:00
|
|
|
use serde::Serialize;
|
2021-10-28 12:09:56 +00:00
|
|
|
use rocket_contrib::json::{Json};
|
2021-10-28 08:01:31 +00:00
|
|
|
use log::{debug, info, warn};
|
2021-10-28 12:09:56 +00:00
|
|
|
use rocket::request::{FlashMessage};
|
|
|
|
use rocket::response::{Flash, Redirect};
|
|
|
|
use rocket::{get, post};
|
2021-10-28 12:33:27 +00:00
|
|
|
use std::io;
|
|
|
|
use std::process::{Command, Output};
|
2021-10-28 08:01:31 +00:00
|
|
|
use rocket_contrib::templates::Template;
|
|
|
|
|
|
|
|
use peach_lib::config_manager::load_peach_config;
|
|
|
|
use peach_lib::dyndns_client;
|
|
|
|
use peach_lib::network_client;
|
|
|
|
use peach_lib::oled_client;
|
|
|
|
use peach_lib::sbot_client;
|
|
|
|
use peach_lib::stats_client;
|
2021-10-28 12:09:56 +00:00
|
|
|
use peach_lib::stats_client::{CpuStatPercentages, DiskUsage, LoadAverage, MemStat};
|
|
|
|
|
2021-10-28 12:33:27 +00:00
|
|
|
use crate::utils::{build_json_response, JsonResponse};
|
2021-10-28 08:01:31 +00:00
|
|
|
|
|
|
|
/// # helpers and routes for /device
|
|
|
|
/////////////////////////////////
|
|
|
|
|
|
|
|
// used in /device for system statistics
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
pub struct DeviceContext {
|
|
|
|
pub back: Option<String>,
|
|
|
|
pub cpu_stat_percent: Option<CpuStatPercentages>,
|
|
|
|
pub disk_stats: Vec<DiskUsage>,
|
|
|
|
pub flash_name: Option<String>,
|
|
|
|
pub flash_msg: Option<String>,
|
|
|
|
pub load_average: Option<LoadAverage>,
|
|
|
|
pub mem_stats: Option<MemStat>,
|
|
|
|
pub network_ping: String,
|
|
|
|
pub oled_ping: String,
|
|
|
|
pub stats_ping: String,
|
|
|
|
pub dyndns_enabled: bool,
|
|
|
|
pub dyndns_is_online: bool,
|
|
|
|
pub config_is_valid: bool,
|
|
|
|
pub sbot_is_online: bool,
|
|
|
|
pub title: Option<String>,
|
|
|
|
pub uptime: Option<i32>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DeviceContext {
|
|
|
|
pub fn build() -> DeviceContext {
|
|
|
|
// convert result to Option<CpuStatPercentages>, discard any error
|
|
|
|
let cpu_stat_percent = stats_client::cpu_stats_percent().ok();
|
|
|
|
let load_average = stats_client::load_average().ok();
|
|
|
|
let mem_stats = stats_client::mem_stats().ok();
|
|
|
|
let network_ping = match network_client::ping() {
|
|
|
|
Ok(_) => "ONLINE".to_string(),
|
|
|
|
Err(_) => "OFFLINE".to_string(),
|
|
|
|
};
|
|
|
|
let oled_ping = match oled_client::ping() {
|
|
|
|
Ok(_) => "ONLINE".to_string(),
|
|
|
|
Err(_) => "OFFLINE".to_string(),
|
|
|
|
};
|
|
|
|
let stats_ping = match stats_client::ping() {
|
|
|
|
Ok(_) => "ONLINE".to_string(),
|
|
|
|
Err(_) => "OFFLINE".to_string(),
|
|
|
|
};
|
|
|
|
let uptime = match stats_client::uptime() {
|
|
|
|
Ok(mins) => mins,
|
|
|
|
Err(_) => "Unavailable".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
// serialize disk usage data into Vec<DiskUsage>
|
|
|
|
let disk_usage_stats = match stats_client::disk_usage() {
|
|
|
|
Ok(disks) => {
|
|
|
|
let partitions: Vec<DiskUsage> = serde_json::from_str(disks.as_str())
|
|
|
|
.expect("Failed to deserialize disk_usage response");
|
|
|
|
partitions
|
|
|
|
}
|
|
|
|
Err(_) => Vec::new(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut disk_stats = Vec::new();
|
|
|
|
// select only the partition we're interested in: /dev/mmcblk0p2 ("/")
|
|
|
|
for disk in disk_usage_stats {
|
|
|
|
if disk.mountpoint == "/" {
|
|
|
|
disk_stats.push(disk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse the uptime string to a signed integer (for math)
|
|
|
|
let uptime_parsed = uptime.parse::<i32>().ok();
|
|
|
|
|
|
|
|
// dyndns_is_online & config_is_valid
|
|
|
|
let dyndns_enabled: bool;
|
|
|
|
let dyndns_is_online: bool;
|
|
|
|
let config_is_valid: bool;
|
|
|
|
let load_peach_config_result = load_peach_config();
|
|
|
|
match load_peach_config_result {
|
|
|
|
Ok(peach_config) => {
|
|
|
|
dyndns_enabled = peach_config.dyn_enabled;
|
|
|
|
config_is_valid = true;
|
|
|
|
if dyndns_enabled {
|
|
|
|
let is_dyndns_online_result = dyndns_client::is_dns_updater_online();
|
|
|
|
match is_dyndns_online_result {
|
|
|
|
Ok(is_online) => {
|
|
|
|
dyndns_is_online = is_online;
|
|
|
|
}
|
|
|
|
Err(_err) => {
|
|
|
|
dyndns_is_online = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dyndns_is_online = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(_err) => {
|
|
|
|
dyndns_enabled = false;
|
|
|
|
dyndns_is_online = false;
|
|
|
|
config_is_valid = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// test if go-sbot is running
|
|
|
|
let sbot_is_online: bool;
|
|
|
|
let sbot_is_online_result = sbot_client::is_sbot_online();
|
|
|
|
match sbot_is_online_result {
|
|
|
|
Ok(val) => {
|
|
|
|
sbot_is_online = val;
|
|
|
|
}
|
|
|
|
Err(_err) => {
|
|
|
|
sbot_is_online = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceContext {
|
|
|
|
back: None,
|
|
|
|
cpu_stat_percent,
|
|
|
|
disk_stats,
|
|
|
|
flash_name: None,
|
|
|
|
flash_msg: None,
|
|
|
|
load_average,
|
|
|
|
mem_stats,
|
|
|
|
network_ping,
|
|
|
|
oled_ping,
|
|
|
|
stats_ping,
|
|
|
|
dyndns_enabled,
|
|
|
|
dyndns_is_online,
|
|
|
|
config_is_valid,
|
|
|
|
sbot_is_online,
|
|
|
|
title: None,
|
|
|
|
uptime: uptime_parsed,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/device")]
|
|
|
|
pub fn device_stats(flash: Option<FlashMessage>) -> Template {
|
|
|
|
// assign context through context_builder call
|
|
|
|
let mut context = DeviceContext::build();
|
|
|
|
context.back = Some("/".to_string());
|
|
|
|
context.title = Some("Device Status".to_string());
|
|
|
|
// check to see if there is a flash message to display
|
|
|
|
if let Some(flash) = flash {
|
|
|
|
// add flash message contents to the context object
|
|
|
|
context.flash_name = Some(flash.name().to_string());
|
|
|
|
context.flash_msg = Some(flash.msg().to_string());
|
|
|
|
};
|
|
|
|
// template_dir is set in Rocket.toml
|
|
|
|
Template::render("device", &context)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// # helpers and routes for /device/reboot
|
|
|
|
/////////////////////////////////
|
|
|
|
|
|
|
|
/// Executes a system command to reboot the device immediately.
|
|
|
|
pub fn reboot() -> io::Result<Output> {
|
|
|
|
info!("Rebooting the device");
|
|
|
|
// ideally, we'd like to reboot after 5 seconds to allow time for JSON
|
|
|
|
// response but this is not possible with the `shutdown` command alone.
|
|
|
|
// TODO: send "rebooting..." message to `peach-oled` for display
|
|
|
|
Command::new("sudo")
|
|
|
|
.arg("shutdown")
|
|
|
|
.arg("-r")
|
|
|
|
.arg("now")
|
|
|
|
.output()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/device/reboot")]
|
|
|
|
pub fn reboot_cmd() -> Flash<Redirect> {
|
|
|
|
match reboot() {
|
|
|
|
Ok(_) => Flash::success(Redirect::to("/shutdown"), "Rebooting the device"),
|
|
|
|
Err(_) => Flash::error(Redirect::to("/shutdown"), "Failed to reboot the device"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// reboot the device
|
|
|
|
#[post("/api/v1/device/reboot")]
|
|
|
|
pub fn reboot_device() -> Json<JsonResponse> {
|
|
|
|
match reboot() {
|
|
|
|
Ok(_) => {
|
|
|
|
debug!("Going down for reboot...");
|
|
|
|
let status = "success".to_string();
|
|
|
|
let msg = "Going down for reboot.".to_string();
|
|
|
|
Json(build_json_response(status, None, Some(msg)))
|
|
|
|
}
|
|
|
|
Err(_) => {
|
|
|
|
warn!("Reboot failed");
|
|
|
|
let status = "error".to_string();
|
|
|
|
let msg = "Failed to reboot the device.".to_string();
|
|
|
|
Json(build_json_response(status, None, Some(msg)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// # helpers and routes for /device/shutdown
|
|
|
|
/////////////////////////////////
|
|
|
|
|
|
|
|
/// Executes a system command to shutdown the device immediately.
|
|
|
|
pub fn shutdown() -> io::Result<Output> {
|
|
|
|
info ! ("Shutting down the device");
|
|
|
|
// ideally, we'd like to reboot after 5 seconds to allow time for JSON
|
|
|
|
// response but this is not possible with the `shutdown` command alone.
|
|
|
|
// TODO: send "shutting down..." message to `peach-oled` for display
|
|
|
|
Command::new("sudo").arg("shutdown").arg("now").output()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/device/shutdown")]
|
|
|
|
pub fn shutdown_cmd() -> Flash<Redirect> {
|
|
|
|
match shutdown() {
|
|
|
|
Ok(_) => Flash::success(Redirect::to("/shutdown"), "Shutting down the device"),
|
|
|
|
Err(_) => Flash::error(Redirect::to("/shutdown"), "Failed to shutdown the device"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// shutdown the device
|
|
|
|
#[post("/api/v1/device/shutdown")]
|
|
|
|
pub fn shutdown_device() -> Json<JsonResponse> {
|
|
|
|
match shutdown() {
|
|
|
|
Ok(_) => {
|
|
|
|
debug!("Going down for shutdown...");
|
|
|
|
let status = "success".to_string();
|
|
|
|
let msg = "Going down for shutdown.".to_string();
|
|
|
|
Json(build_json_response(status, None, Some(msg)))
|
|
|
|
}
|
|
|
|
Err(_) => {
|
|
|
|
warn!("Shutdown failed");
|
|
|
|
let status = "error".to_string();
|
|
|
|
let msg = "Failed to shutdown the device.".to_string();
|
|
|
|
Json(build_json_response(status, None, Some(msg)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// # helpers and routes for /shutdown
|
|
|
|
/////////////////////////////////
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
pub struct ShutdownContext {
|
|
|
|
pub back: Option<String>,
|
|
|
|
pub flash_name: Option<String>,
|
|
|
|
pub flash_msg: Option<String>,
|
|
|
|
pub title: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ShutdownContext {
|
|
|
|
pub fn build() -> ShutdownContext {
|
|
|
|
ShutdownContext {
|
|
|
|
back: None,
|
|
|
|
flash_name: None,
|
|
|
|
flash_msg: None,
|
|
|
|
title: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/shutdown")]
|
|
|
|
pub fn shutdown_menu(flash: Option<FlashMessage>) -> Template {
|
|
|
|
let mut context = ShutdownContext::build();
|
|
|
|
context.back = Some("/".to_string());
|
|
|
|
context.title = Some("Shutdown Device".to_string());
|
|
|
|
// check to see if there is a flash message to display
|
|
|
|
if let Some(flash) = flash {
|
|
|
|
// add flash message contents to the context object
|
|
|
|
context.flash_name = Some(flash.name().to_string());
|
|
|
|
context.flash_msg = Some(flash.msg().to_string());
|
|
|
|
};
|
|
|
|
Template::render("shutdown", &context)
|
|
|
|
}
|