Upgrade peach-web to use rocket 0.5 #15

Merged
notplants merged 9 commits from rocket0.5 into main 2021-11-05 11:07:03 +00:00
25 changed files with 1038 additions and 3903 deletions
Showing only changes of commit 2507747f94 - Show all commits

865
Cargo.lock generated

File diff suppressed because it is too large Load Diff

1460
peach-buttons/Cargo.lock generated

File diff suppressed because it is too large Load Diff

1603
peach-oled/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -28,13 +28,13 @@ travis-ci = { repository = "peachcloud/peach-stats", branch = "master" }
maintenance = { status = "actively-developed" }
[dependencies]
env_logger = "0.6"
jsonrpc-core = "11"
jsonrpc-http-server = "11"
jsonrpc-test = "11"
env_logger = "0.9"
jsonrpc-core = "18"
jsonrpc-http-server = "18"
log = "0.4"
probes = "0.3"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
snafu = "0.4"
systemstat = "0.1"
miniserde = "0.1.15"
probes = "0.4.1"
systemstat = "0.1.10"
[dev-dependencies]
jsonrpc-test = "18"

View File

@ -14,7 +14,7 @@ System statistics microservice module for PeachCloud. Provides a JSON-RPC wrappe
| `load_average` | Load average statistics | `one`, `five`, `fifteen` |
| `mem_stats` | Memory statistics | `total`, `free`, `used` |
| `ping` | Microservice status | `success` if running |
| `uptime` | System uptime | `secs`, `nanos` |
| `uptime` | System uptime | `secs` |
### Environment
@ -101,7 +101,7 @@ With microservice running, open a second terminal window and use `curl` to call
Server responds with:
`{"jsonrpc":"2.0","result":"{\"secs\":840968,\"nanos\":0}","id":1}`
`{"jsonrpc":"2.0","result":"{\"secs\":840968}","id":1}`
### Licensing

View File

@ -1,67 +1,69 @@
use std::{error, io};
use std::{error, fmt, io};
use jsonrpc_core::{types::error::Error, ErrorCode};
use probes::ProbeError;
use serde_json::Error as SerdeError;
use snafu::Snafu;
pub type BoxError = Box<dyn error::Error>;
#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
#[derive(Debug)]
pub enum StatError {
#[snafu(display("Failed to retrieve CPU statistics: {}", source))]
ReadCpuStat { source: ProbeError },
CpuStat { source: ProbeError },
DiskUsage { source: ProbeError },
LoadAvg { source: ProbeError },
MemStat { source: ProbeError },
Uptime { source: io::Error },
}
#[snafu(display("Failed to retrieve disk usage statistics: {}", source))]
ReadDiskUsage { source: ProbeError },
impl error::Error for StatError {}
#[snafu(display("Failed to retrieve load average statistics: {}", source))]
ReadLoadAvg { source: ProbeError },
#[snafu(display("Failed to retrieve memory statistics: {}", source))]
ReadMemStat { source: ProbeError },
#[snafu(display("Failed to retrieve system uptime: {}", source))]
ReadUptime { source: io::Error },
#[snafu(display("JSON serialization failed: {}", source))]
SerdeSerialize { source: SerdeError },
impl fmt::Display for StatError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
StatError::CpuStat { ref source } => {
write!(f, "Failed to retrieve CPU statistics: {}", source)
}
StatError::DiskUsage { ref source } => {
write!(f, "Failed to retrieve disk usage statistics: {}", source)
}
StatError::LoadAvg { ref source } => {
write!(f, "Failed to retrieve load average statistics: {}", source)
}
StatError::MemStat { ref source } => {
write!(f, "Failed to retrieve memory statistics: {}", source)
}
StatError::Uptime { ref source } => {
write!(f, "Failed to retrieve system uptime: {}", source)
}
}
}
}
impl From<StatError> for Error {
fn from(err: StatError) -> Self {
match &err {
StatError::ReadCpuStat { source } => Error {
StatError::CpuStat { source } => Error {
code: ErrorCode::ServerError(-32001),
message: format!("Failed to retrieve CPU statistics: {}", source),
data: None,
},
StatError::ReadDiskUsage { source } => Error {
StatError::DiskUsage { source } => Error {
code: ErrorCode::ServerError(-32001),
message: format!("Failed to retrieve disk usage statistics: {}", source),
data: None,
},
StatError::ReadLoadAvg { source } => Error {
StatError::LoadAvg { source } => Error {
code: ErrorCode::ServerError(-32001),
message: format!("Failed to retrieve load average statistics: {}", source),
data: None,
},
StatError::ReadMemStat { source } => Error {
StatError::MemStat { source } => Error {
code: ErrorCode::ServerError(-32001),
message: format!("Failed to retrieve memory statistics: {}", source),
data: None,
},
StatError::ReadUptime { source } => Error {
StatError::Uptime { source } => Error {
code: ErrorCode::ServerError(-32001),
message: format!("Failed to retrieve system uptime: {}", source),
data: None,
},
StatError::SerdeSerialize { source } => Error {
code: ErrorCode::ServerError(-32002),
message: format!("JSON serialization failed: {}", source),
data: None,
},
}
}
}

View File

@ -6,56 +6,56 @@ use std::{env, result::Result};
use jsonrpc_core::{IoHandler, Value};
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
#[allow(unused_imports)]
use jsonrpc_test as test;
use log::info;
use crate::error::BoxError;
use crate::error::StatError;
pub fn run() -> Result<(), BoxError> {
pub fn run() -> Result<(), StatError> {
info!("Starting up.");
info!("Creating JSON-RPC I/O handler.");
let mut io = IoHandler::default();
io.add_method("cpu_stats", move |_| {
io.add_method("cpu_stats", move |_| async {
info!("Fetching CPU statistics.");
let stats = stats::cpu_stats()?;
Ok(Value::String(stats))
});
io.add_method("cpu_stats_percent", move |_| {
io.add_method("cpu_stats_percent", move |_| async {
info!("Fetching CPU statistics as percentages.");
let stats = stats::cpu_stats_percent()?;
Ok(Value::String(stats))
});
io.add_method("disk_usage", move |_| {
io.add_method("disk_usage", move |_| async {
info!("Fetching disk usage statistics.");
let disks = stats::disk_usage()?;
Ok(Value::String(disks))
});
io.add_method("load_average", move |_| {
io.add_method("load_average", move |_| async {
info!("Fetching system load average statistics.");
let avg = stats::load_average()?;
Ok(Value::String(avg))
});
io.add_method("mem_stats", move |_| {
io.add_method("mem_stats", move |_| async {
info!("Fetching current memory statistics.");
let mem = stats::mem_stats()?;
Ok(Value::String(mem))
});
io.add_method("ping", |_| Ok(Value::String("success".to_string())));
io.add_method("ping", |_| async {
Ok(Value::String("success".to_string()))
});
io.add_method("uptime", move |_| {
io.add_method("uptime", move |_| async {
info!("Fetching system uptime.");
let uptime = stats::uptime()?;
@ -85,16 +85,17 @@ pub fn run() -> Result<(), BoxError> {
#[cfg(test)]
mod tests {
use super::*;
use jsonrpc_test as test_rpc;
// test to ensure correct success response
#[test]
fn rpc_success() {
let rpc = {
let mut io = IoHandler::new();
io.add_method("rpc_success_response", |_| {
io.add_method("rpc_success_response", |_| async {
Ok(Value::String("success".into()))
});
test::Rpc::from(io)
test_rpc::Rpc::from(io)
};
assert_eq!(rpc.request("rpc_success_response", &()), r#""success""#);

View File

@ -1,14 +1,14 @@
use std::result::Result;
use miniserde::json;
use probes::{cpu, disk_usage, load, memory};
use snafu::ResultExt;
use systemstat::{Platform, System};
use crate::error::*;
use crate::error::StatError;
use crate::structs::{CpuStat, CpuStatPercentages, DiskUsage, LoadAverage, MemStat};
pub fn cpu_stats() -> Result<String, StatError> {
let cpu_stats = cpu::proc::read().context(ReadCpuStat)?;
let cpu_stats = cpu::proc::read().map_err(|source| StatError::CpuStat { source })?;
let s = cpu_stats.stat;
let cpu = CpuStat {
user: s.user,
@ -16,13 +16,13 @@ pub fn cpu_stats() -> Result<String, StatError> {
nice: s.nice,
idle: s.idle,
};
let json_cpu = serde_json::to_string(&cpu).context(SerdeSerialize)?;
let json_cpu = json::to_string(&cpu);
Ok(json_cpu)
}
pub fn cpu_stats_percent() -> Result<String, StatError> {
let cpu_stats = cpu::proc::read().context(ReadCpuStat)?;
let cpu_stats = cpu::proc::read().map_err(|source| StatError::CpuStat { source })?;
let s = cpu_stats.stat.in_percentages();
let cpu = CpuStatPercentages {
user: s.user,
@ -30,13 +30,13 @@ pub fn cpu_stats_percent() -> Result<String, StatError> {
nice: s.nice,
idle: s.idle,
};
let json_cpu = serde_json::to_string(&cpu).context(SerdeSerialize)?;
let json_cpu = json::to_string(&cpu);
Ok(json_cpu)
}
pub fn disk_usage() -> Result<String, StatError> {
let disks = disk_usage::read().context(ReadDiskUsage)?;
let disks = disk_usage::read().map_err(|source| StatError::DiskUsage { source })?;
let mut disk_usages = Vec::new();
for d in disks {
let disk = DiskUsage {
@ -49,39 +49,42 @@ pub fn disk_usage() -> Result<String, StatError> {
};
disk_usages.push(disk);
}
let json_disks = serde_json::to_string(&disk_usages).context(SerdeSerialize)?;
let json_disks = json::to_string(&disk_usages);
Ok(json_disks)
}
pub fn load_average() -> Result<String, StatError> {
let l = load::read().context(ReadLoadAvg)?;
let l = load::read().map_err(|source| StatError::LoadAvg { source })?;
let load_avg = LoadAverage {
one: l.one,
five: l.five,
fifteen: l.fifteen,
};
let json_load_avg = serde_json::to_string(&load_avg).context(SerdeSerialize)?;
let json_load_avg = json::to_string(&load_avg);
Ok(json_load_avg)
}
pub fn mem_stats() -> Result<String, StatError> {
let m = memory::read().context(ReadMemStat)?;
let m = memory::read().map_err(|source| StatError::MemStat { source })?;
let mem = MemStat {
total: m.total(),
free: m.free(),
used: m.used(),
};
let json_mem = serde_json::to_string(&mem).context(SerdeSerialize)?;
let json_mem = json::to_string(&mem);
Ok(json_mem)
}
pub fn uptime() -> Result<String, StatError> {
let sys = System::new();
let uptime = sys.uptime().context(ReadUptime)?;
let json_uptime = serde_json::to_string(&uptime).context(SerdeSerialize)?;
let uptime = sys
.uptime()
.map_err(|source| StatError::Uptime { source })?;
let uptime_secs = uptime.as_secs();
let json_uptime = json::to_string(&uptime_secs);
Ok(json_uptime)
}

View File

@ -1,4 +1,4 @@
use serde::Serialize;
use miniserde::Serialize;
#[derive(Debug, Serialize)]
pub struct CpuStat {

View File

@ -40,7 +40,7 @@ log = "0.4"
nest = "1.0.0"
peach-lib = { path = "../peach-lib" }
percent-encoding = "2.1.0"
rocket = "0.5.0-rc.1"
rocket = { version = "0.5.0-rc.1", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
snafu = "0.6"
@ -53,4 +53,4 @@ openssl = { version = "0.10", features = ["vendored"] }
[dependencies.rocket_contrib]
version = "0.4.10"
default-features = false
features = ["json", "tera_templates"]
features = ["tera_templates"]

View File

@ -1,4 +1,4 @@
//!! different types of PeachWebError
//! Custom error type representing all possible error variants for peach-web.
use peach_lib::error::PeachError;
use peach_lib::{serde_json, serde_yaml};

View File

@ -24,15 +24,12 @@
//! of the template to be rendered.
#![feature(proc_macro_hygiene, decl_macro)]
// this is to ignore a clippy warning that suggests
// to replace code with the same code that is already there (possibly a bug)
#![allow(clippy::nonstandard_macro_braces)]
pub mod error;
pub mod routes;
pub mod utils;
#[cfg(test)]
mod tests;
pub mod utils;
mod ws;
use std::{env, thread};

View File

@ -1,20 +1,17 @@
use serde::{Serialize, Deserialize};
use rocket_contrib::json::{Json};
use log::{debug, info};
use rocket::request::{FlashMessage, Form};
use rocket::request::{FlashMessage, Form, FromForm};
use rocket::response::{Flash, Redirect};
use rocket::{get, post};
use rocket::request::FromForm;
use rocket::serde::json::Json;
use rocket_contrib::templates::Template;
use rocket::serde::{Deserialize, Serialize};
use peach_lib::password_utils;
use crate::utils::{build_json_response, JsonResponse};
use crate::error::PeachWebError;
use crate::utils::{build_json_response, JsonResponse};
/// # helpers and routes for /login
/////////////////////////////////
// HELPERS AND ROUTES FOR /login
#[derive(Debug, Serialize)]
pub struct LoginContext {
@ -49,8 +46,7 @@ pub fn login(flash: Option<FlashMessage>) -> Template {
Template::render("login", &context)
}
/// # helpers and routes for /logout
/////////////////////////////////
// HELPERS AND ROUTES FOR /logout
#[post("/logout")]
pub fn logout() -> Flash<Redirect> {
@ -68,8 +64,7 @@ pub fn logout() -> Flash<Redirect> {
Flash::success(Redirect::to("/"), "Logged out")
}
/// # helpers and routes for /reset_password
//////////////////////////////////////////
// HELPERS AND ROUTES FOR /reset_password
#[derive(Debug, Deserialize, FromForm)]
pub struct ResetPasswordForm {
@ -116,7 +111,7 @@ impl ChangePasswordContext {
}
}
/// 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> {
info!(
"reset password!: {} {} {}",
@ -133,9 +128,9 @@ pub fn save_reset_password_form(password_form: ResetPasswordForm) -> Result<(),
Ok(())
}
/// this reset password route is used by a user who is not logged in
/// and is specifically for users who have forgotten their password
/// all routes under /public/* are excluded from nginx basic auth via the nginx config
/// Password reset request handler. This route is used by a user who is not logged in
/// and is specifically for users who have forgotten their password.
/// All routes under /public/* are excluded from nginx basic auth via the nginx config.
#[get("/reset_password")]
pub fn reset_password(flash: Option<FlashMessage>) -> Template {
let mut context = ResetPasswordContext::build();
@ -150,9 +145,9 @@ pub fn reset_password(flash: Option<FlashMessage>) -> Template {
Template::render("password/reset_password", &context)
}
/// this reset password route is used by a user who is not logged in
/// and is specifically for users who have forgotten their password
/// and is excluded from nginx basic auth via the nginx config
/// 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.
/// This route is excluded from nginx basic auth via the nginx config.
#[post("/reset_password", data = "<reset_password_form>")]
pub fn reset_password_post(reset_password_form: Form<ResetPasswordForm>) -> Template {
let result = save_reset_password_form(reset_password_form.into_inner());
@ -178,13 +173,13 @@ pub fn reset_password_post(reset_password_form: Form<ResetPasswordForm>) -> Temp
}
}
/// this reset password route is used by a user who is not logged in
/// and is specifically for users who have forgotten their password
/// all routes under /public/* are excluded from nginx basic auth via the nginx config
/// JSON 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.
/// All routes under /public/* are excluded from nginx basic auth via the nginx config.
#[post("/public/api/v1/reset_password", data = "<reset_password_form>")]
pub fn reset_password_form_endpoint(
reset_password_form: Json<ResetPasswordForm>,
) -> Json<JsonResponse> {
) -> Value {
let result = save_reset_password_form(reset_password_form.into_inner());
match result {
Ok(_) => {
@ -200,10 +195,7 @@ pub fn reset_password_form_endpoint(
}
}
/// # helpers and routes for /send_password_reset
////////////////////////////////////////////////
// HELPERS AND ROUTES FOR /send_password_reset
#[derive(Debug, Serialize)]
pub struct SendPasswordResetContext {
@ -224,7 +216,7 @@ impl SendPasswordResetContext {
}
}
/// this route is used by a user who is not logged in to send a new password reset link
/// Password reset request handler. This route is used by a user who is not logged in to send a new password reset link.
#[get("/send_password_reset")]
pub fn send_password_reset_page(flash: Option<FlashMessage>) -> Template {
let mut context = SendPasswordResetContext::build();
@ -239,8 +231,9 @@ pub fn send_password_reset_page(flash: Option<FlashMessage>) -> Template {
Template::render("password/send_password_reset", &context)
}
/// this send_password_reset route is used by a user who is not logged in
/// and is specifically for users who have forgotten their password
/// Send password reset request handler. This route is used by a user who is not logged in
/// and is specifically for users who have forgotten their password. A successful request results
/// in a Scuttlebutt private message being sent to the account of the device admin.
#[post("/send_password_reset")]
pub fn send_password_reset_post() -> Template {
info!("++ send password reset post");
@ -267,8 +260,7 @@ pub fn send_password_reset_post() -> Template {
}
}
/// # helpers and routes for /settings/change_password
//////////////////////////////////////////
// HELPERS AND ROUTES FOR /settings/change_password
#[derive(Debug, Deserialize, FromForm)]
pub struct PasswordForm {
@ -277,7 +269,7 @@ pub struct PasswordForm {
pub new_password2: String,
}
/// this function is for use by a user who is already logged in to change their password
/// Password save form request handler. This function is for use by a user who is already logged in to change their password.
pub fn save_password_form(password_form: PasswordForm) -> Result<(), PeachWebError> {
info!(
"change password!: {} {} {}",
@ -294,7 +286,7 @@ pub fn save_password_form(password_form: PasswordForm) -> Result<(), PeachWebErr
Ok(())
}
/// this change password route 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("/settings/change_password")]
pub fn change_password(flash: Option<FlashMessage>) -> Template {
let mut context = ChangePasswordContext::build();
@ -310,7 +302,7 @@ pub fn change_password(flash: Option<FlashMessage>) -> Template {
Template::render("password/change_password", &context)
}
/// this change password 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("/settings/change_password", data = "<password_form>")]
pub fn change_password_post(password_form: Form<PasswordForm>) -> Template {
let result = save_password_form(password_form.into_inner());
@ -337,8 +329,9 @@ pub fn change_password_post(password_form: Form<PasswordForm>) -> Template {
}
}
/// JSON change password form request handler.
#[post("/api/v1/settings/change_password", data = "<password_form>")]
pub fn save_password_form_endpoint(password_form: Json<PasswordForm>) -> Json<JsonResponse> {
pub fn save_password_form_endpoint(password_form: Json<PasswordForm>) -> Value {
let result = save_password_form(password_form.into_inner());
match result {
Ok(_) => {
@ -352,4 +345,4 @@ pub fn save_password_form_endpoint(password_form: Json<PasswordForm>) -> Json<Js
Json(build_json_response(status, None, Some(msg)))
}
}
}
}

View File

@ -1,27 +1,26 @@
use serde::Serialize;
use rocket_contrib::json::{Json};
use log::{debug, info, warn};
use rocket::request::{FlashMessage};
use rocket::response::{Flash, Redirect};
use rocket::{get, post};
use std::io;
use std::process::{Command, Output};
use rocket::{
get, post,
request::FlashMessage,
response::{Flash, Redirect},
};
use rocket::serde::json::Json;
use rocket_contrib::templates::Template;
use serde::Serialize;
use std::{
io,
process::{Command, Output},
};
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;
use peach_lib::stats_client::{CpuStatPercentages, DiskUsage, LoadAverage, MemStat};
use peach_lib::{dyndns_client, network_client, oled_client, sbot_client, stats_client};
use crate::utils::{build_json_response, JsonResponse};
/// # helpers and routes for /device
/////////////////////////////////
// HELPERS AND ROUTES FOR /device
// used in /device for system statistics
/// System statistics data.
#[derive(Debug, Serialize)]
pub struct DeviceContext {
pub back: Option<String>,
@ -165,8 +164,7 @@ pub fn device_stats(flash: Option<FlashMessage>) -> Template {
Template::render("device", &context)
}
/// # helpers and routes for /device/reboot
/////////////////////////////////
// HELPERS AND ROUTES FOR /device/reboot
/// Executes a system command to reboot the device immediately.
pub fn reboot() -> io::Result<Output> {
@ -189,9 +187,9 @@ pub fn reboot_cmd() -> Flash<Redirect> {
}
}
// reboot the device
/// JSON request handler for device reboot.
#[post("/api/v1/device/reboot")]
pub fn reboot_device() -> Json<JsonResponse> {
pub fn reboot_device() -> Value {
match reboot() {
Ok(_) => {
debug!("Going down for reboot...");
@ -208,17 +206,15 @@ pub fn reboot_device() -> Json<JsonResponse> {
}
}
/// # helpers and routes for /device/shutdown
/////////////////////////////////
// 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()
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")]
@ -231,7 +227,7 @@ pub fn shutdown_cmd() -> Flash<Redirect> {
// shutdown the device
#[post("/api/v1/device/shutdown")]
pub fn shutdown_device() -> Json<JsonResponse> {
pub fn shutdown_device() -> Value {
match shutdown() {
Ok(_) => {
debug!("Going down for shutdown...");
@ -248,9 +244,7 @@ pub fn shutdown_device() -> Json<JsonResponse> {
}
}
/// # helpers and routes for /shutdown
/////////////////////////////////
// HELPERS AND ROUTES FOR /shutdown
#[derive(Debug, Serialize)]
pub struct ShutdownContext {
@ -283,4 +277,4 @@ pub fn shutdown_menu(flash: Option<FlashMessage>) -> Template {
context.flash_msg = Some(flash.msg().to_string());
};
Template::render("shutdown", &context)
}
}

View File

@ -1,18 +1,16 @@
use std::path::{Path, PathBuf};
use log::{debug};
use rocket::response::{NamedFile};
use rocket::{catch, get};
use log::debug;
use rocket::{catch, get, response::NamedFile};
use rocket_contrib::templates::Template;
use serde::Serialize;
use std::path::{Path, PathBuf};
#[get("/<file..>", rank = 2)]
pub fn files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("static/").join(file)).ok()
}
/// # helpers and routes for /404
/////////////////////////////////
// HELPERS AND ROUTES FOR 404 ERROR
#[derive(Debug, Serialize)]
pub struct ErrorContext {
pub back: Option<String>,
@ -44,8 +42,7 @@ pub fn not_found() -> Template {
Template::render("not_found", context)
}
/// # helpers and routes for 500
/////////////////////////////////
// HELPERS AND ROUTES FOR 500 ERROR
#[catch(500)]
pub fn internal_error() -> Template {
@ -58,4 +55,3 @@ pub fn internal_error() -> Template {
Template::render("internal_error", context)
}

View File

@ -1,11 +1,8 @@
use rocket::request::{FlashMessage};
use rocket::{get};
use rocket::{get, request::FlashMessage};
use rocket_contrib::templates::Template;
use serde::Serialize;
/// # helpers and routes for / (home page)
/////////////////////////////////////
// HELPERS AND ROUTES FOR / (HOME PAGE)
#[derive(Debug, Serialize)]
pub struct HomeContext {
@ -34,8 +31,7 @@ pub fn index() -> Template {
Template::render("index", &context)
}
/// # helpers and routes for /help
/////////////////////////////////
// HELPERS AND ROUTES FOR /help
#[derive(Debug, Serialize)]
pub struct HelpContext {
@ -69,5 +65,3 @@ pub fn help(flash: Option<FlashMessage>) -> Template {
};
Template::render("help", &context)
}

View File

@ -1,36 +1,36 @@
//! Helper routes for pinging services to check that they are active
use rocket_contrib::json::{Json};
use log::{debug, warn};
use rocket::{get};
use rocket::get;
use rocket_contrib::json::Json;
use peach_lib::dyndns_client::{is_dns_updater_online};
use peach_lib::dyndns_client::is_dns_updater_online;
use peach_lib::network_client;
use peach_lib::oled_client;
use peach_lib::stats_client;
use crate::utils::{build_json_response, JsonResponse};
// status route: useful for checking connectivity from web client
/// Status route: useful for checking connectivity from web client.
#[get("/api/v1/ping")]
pub fn ping_pong() -> Json<JsonResponse> {
pub fn ping_pong() -> Value {
// ping pong
let status = "success".to_string();
let msg = "pong!".to_string();
Json(build_json_response(status, None, Some(msg)))
}
// test route: useful for ad hoc testing
/// Test route: useful for ad hoc testing.
#[get("/api/v1/test")]
glyph marked this conversation as resolved Outdated
Outdated
Review

Can we make this route name more specific? One option is to stick with the established ping route convention and use: /api/v1/ping/dyndns.

Can we make this route name more specific? One option is to stick with the established `ping` route convention and use: `/api/v1/ping/dyndns`.

I removed the test route,
-- fwiw the way I was using it was not as a test that needs to continue to exist,
but while building dyn_dns_updater,
a route that I could use to trigger the code I wanted to test.

Basically just a place where I can put arbitary code, and then call it by visiting that route.

But I will just temporarily re-add my lil test route when I want to use it for that, and it makes sense to not live in the codebase where it could cause confusion.

I removed the test route, -- fwiw the way I was using it was not as a test that needs to continue to exist, but while building dyn_dns_updater, a route that I could use to trigger the code I wanted to test. Basically just a place where I can put arbitary code, and then call it by visiting that route. But I will just temporarily re-add my lil test route when I want to use it for that, and it makes sense to not live in the codebase where it could cause confusion.
Outdated
Review

That makes sense to me. I can see the usefulness of a set of JSON /api/v1/diagnosis/... routes for these sorts of checks. It's really handy to have a programmatic and console-based way to query state.

That makes sense to me. I can see the usefulness of a set of JSON `/api/v1/diagnosis/...` routes for these sorts of checks. It's really handy to have a programmatic and console-based way to query state.
pub fn test_route() -> Json<JsonResponse> {
pub fn test_route() -> Value {
let val = is_dns_updater_online().unwrap();
glyph marked this conversation as resolved
Review

This is going to panic and crash the server if is_dns_updater_online returns an error. We should rather match on the Result and return an appropriate JSON Value if there's an error.

This is going to panic and crash the server if `is_dns_updater_online` returns an error. We should rather match on the `Result` and return an appropriate JSON `Value` if there's an error.
let status = "success".to_string();
let msg = val.to_string();
Json(build_json_response(status, None, Some(msg)))
}
// status route: check availability of `peach-network` microservice
/// Status route: check availability of `peach-network` microservice.
#[get("/api/v1/ping/network")]
pub fn ping_network() -> Json<JsonResponse> {
pub fn ping_network() -> Value {
match network_client::ping() {
Ok(_) => {
debug!("peach-network responded successfully");
@ -47,9 +47,9 @@ pub fn ping_network() -> Json<JsonResponse> {
}
}
// status route: check availability of `peach-oled` microservice
/// Status route: check availability of `peach-oled` microservice.
#[get("/api/v1/ping/oled")]
pub fn ping_oled() -> Json<JsonResponse> {
pub fn ping_oled() -> Value {
match oled_client::ping() {
Ok(_) => {
debug!("peach-oled responded successfully");
@ -66,9 +66,9 @@ pub fn ping_oled() -> Json<JsonResponse> {
}
}
// status route: check availability of `peach-stats` microservice
/// Status route: check availability of `peach-stats` microservice.
#[get("/api/v1/ping/stats")]
pub fn ping_stats() -> Json<JsonResponse> {
pub fn ping_stats() -> Value {
match stats_client::ping() {
Ok(_) => {
debug!("peach-stats responded successfully");
@ -83,4 +83,4 @@ pub fn ping_stats() -> Json<JsonResponse> {
Json(build_json_response(status, None, Some(msg)))
}
}
}
}

View File

@ -1,11 +1,10 @@
//! Routes for scuttlebutt related functionality.
use rocket::request::{FlashMessage};
use rocket::{get};
//! Routes for ScuttleButt related functionality.
use rocket::{get, request::FlashMessage};
use rocket_contrib::templates::Template;
use serde::Serialize;
/// # helpers and routes for /messages
/////////////////////////////////
// HELPERS AND ROUTES FOR /messages
#[derive(Debug, Serialize)]
pub struct MessageContext {
@ -40,8 +39,7 @@ pub fn messages(flash: Option<FlashMessage>) -> Template {
Template::render("messages", &context)
}
/// # helpers and routes for /peers
/////////////////////////////////
// HELPERS AND ROUTES FOR /peers
#[derive(Debug, Serialize)]
pub struct PeerContext {
@ -76,9 +74,7 @@ pub fn peers(flash: Option<FlashMessage>) -> Template {
Template::render("peers", &context)
}
/// # helpers and routes for /profile
/////////////////////////////////
// HELPERS AND ROUTES FOR /profile
#[derive(Debug, Serialize)]
pub struct ProfileContext {
@ -111,4 +107,4 @@ pub fn profile(flash: Option<FlashMessage>) -> Template {
context.flash_msg = Some(flash.msg().to_string());
};
Template::render("profile", &context)
}
}

View File

@ -1,19 +1,18 @@
use rocket::request::{FlashMessage, Form};
use rocket::response::{Flash, Redirect};
use rocket::{get, post, uri};
use rocket::request::FromForm;
use rocket::{
get, post,
request::{FlashMessage, Form, FromForm},
response::{Flash, Redirect},
uri,
};
use rocket_contrib::templates::Template;
use serde::{Serialize, Deserialize};
use rocket::serde::{Deserialize, Serialize};
use peach_lib::config_manager;
use peach_lib::config_manager::{load_peach_config};
use peach_lib::config_manager::load_peach_config;
use crate::error::PeachWebError;
/// # helpers and routes for /settings/configure_admin
/////////////////////////////////
// HELPERS AND ROUTES FOR /settings/configure_admin
#[derive(Debug, Serialize)]
pub struct ConfigureAdminContext {
@ -38,7 +37,7 @@ impl ConfigureAdminContext {
}
}
/// this is a route for viewing and deleting currently configured admin
/// View and delete currently configured admin.
#[get("/settings/configure_admin")]
pub fn configure_admin(flash: Option<FlashMessage>) -> Template {
let mut context = ConfigureAdminContext::build();
@ -54,8 +53,7 @@ pub fn configure_admin(flash: Option<FlashMessage>) -> Template {
Template::render("admin/configure_admin", &context)
}
/// # helpers and routes for /settings/admin/add
/////////////////////////////////
// HELPERS AND ROUTES FOR /settings/admin/add
#[derive(Debug, Deserialize, FromForm)]
pub struct AddAdminForm {
@ -112,8 +110,7 @@ pub fn add_admin_post(add_admin_form: Form<AddAdminForm>) -> Flash<Redirect> {
}
}
/// # helpers and routes for /settings/admin/delete
/////////////////////////////////
// HELPERS AND ROUTES FOR /settings/admin/delete
#[derive(Debug, Deserialize, FromForm)]
pub struct DeleteAdminForm {

View File

@ -1,16 +1,19 @@
use rocket_contrib::json::{Json};
use log::{info};
use rocket::request::{FlashMessage, Form};
use rocket::{get, post};
use rocket::request::FromForm;
use log::info;
use rocket::{
get, post,
request::{FlashMessage, Form, FromForm},
};
use rocket::serde::json::Json;
use rocket_contrib::templates::Template;
use serde::{Serialize, Deserialize};
use rocket::serde::{Deserialize, Serialize};
use peach_lib::config_manager;
use peach_lib::config_manager::{load_peach_config};
use peach_lib::config_manager::load_peach_config;
use peach_lib::dyndns_client;
use peach_lib::dyndns_client::{check_is_new_dyndns_domain, get_full_dynamic_domain};
use peach_lib::dyndns_client::{get_dyndns_subdomain, is_dns_updater_online};
use peach_lib::dyndns_client::{
check_is_new_dyndns_domain, get_dyndns_subdomain, get_full_dynamic_domain,
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;
@ -18,7 +21,6 @@ use peach_lib::jsonrpc_core::types::error::ErrorCode;
use crate::error::PeachWebError;
use crate::utils::{build_json_response, JsonResponse};
#[derive(Debug, Deserialize, FromForm)]
pub struct DnsForm {
pub external_domain: String,
@ -149,7 +151,7 @@ pub fn configure_dns_post(dns: Form<DnsForm>) -> Template {
}
#[post("/api/v1/dns/configure", data = "<dns_form>")]
pub fn save_dns_configuration_endpoint(dns_form: Json<DnsForm>) -> Json<JsonResponse> {
pub fn save_dns_configuration_endpoint(dns_form: Json<DnsForm>) -> Value {
let result = save_dns_configuration(dns_form.into_inner());
match result {
Ok(_) => {
@ -164,5 +166,3 @@ pub fn save_dns_configuration_endpoint(dns_form: Json<DnsForm>) -> Json<JsonResp
}
}
}

View File

@ -1,28 +1,27 @@
use std::collections::HashMap;
use rocket_contrib::json;
use rocket_contrib::json::{Json};
use log::{debug, warn};
use percent_encoding::percent_decode;
use rocket::http::RawStr;
use rocket::request::{FlashMessage, Form};
use rocket::response::{Flash, Redirect};
use rocket::{get, post, uri};
use rocket::request::FromForm;
use rocket::UriDisplayQuery;
use rocket_contrib::templates::Template;
use serde::{Serialize, Deserialize};
use rocket::{
get,
http::RawStr,
post,
request::{FlashMessage, Form, FromForm},
response::{Flash, Redirect},
uri, UriDisplayQuery,
};
use rocket::serde::json::{json, Json};
use rocket_contrib::{templates::Template};
use rocket::serde::{Deserialize, Serialize};
use std::collections::HashMap;
use peach_lib::network_client;
use peach_lib::network_client::{AccessPoint, Networks, Scan};
use peach_lib::stats_client::{Traffic};
use peach_lib::stats_client::Traffic;
use crate::utils::{build_json_response, JsonResponse};
use crate::utils::monitor;
use crate::utils::monitor::{Threshold, Data, Alert};
use crate::utils::monitor::{Alert, Data, Threshold};
use crate::utils::{build_json_response, JsonResponse};
/// # structs used by network routes
////////////////////////////////////
// STRUCTS USED BY NETWORK ROUTES
#[derive(Debug, Deserialize, FromForm, UriDisplayQuery)]
pub struct Ssid {
@ -35,9 +34,7 @@ pub struct WiFi {
pub pass: String,
}
/// # helpers and routes for /network/wifi/usage/reset
/////////////////////////////////
// HELPERS AND ROUTES FOR /network/wifi/usage/reset
#[get("/network/wifi/usage/reset")]
pub fn wifi_usage_reset() -> Flash<Redirect> {
@ -122,8 +119,7 @@ pub fn wifi_set_password(wifi: Form<WiFi>) -> Flash<Redirect> {
}
}
/// # helpers and routes for /network
/////////////////////////////////
// HELPERS AND ROUTES FOR /network
#[derive(Debug, Serialize)]
pub struct NetworkContext {
@ -295,8 +291,7 @@ pub fn network_home(flash: Option<FlashMessage>) -> Template {
Template::render("network_card", &context)
}
/// # helpers and routes for /network/ap/activate
/////////////////////////////////
// HELPERS AND ROUTES FOR /network/ap/activate
#[get("/network/ap/activate")]
pub fn deploy_ap() -> Flash<Redirect> {
@ -311,8 +306,7 @@ pub fn deploy_ap() -> Flash<Redirect> {
}
}
/// # helpers and routes for /network/wifi
/////////////////////////////////
// HELPERS AND ROUTES FOR /network/wifi
#[derive(Debug, Serialize)]
pub struct NetworkListContext {
@ -397,9 +391,7 @@ pub fn wifi_list(flash: Option<FlashMessage>) -> Template {
Template::render("network_list", &context)
}
/// # helpers and routes for /network/wifi<ssid>
/////////////////////////////////
// HELPERS AND ROUTES FOR /network/wifi<ssid>
#[derive(Debug, Serialize)]
pub struct NetworkDetailContext {
@ -567,9 +559,7 @@ pub fn network_detail(ssid: &RawStr, flash: Option<FlashMessage>) -> Template {
Template::render("network_detail", &context)
}
/// # helpers and routes for /network/wifi/activate
/////////////////////////////////
// HELPERS AND ROUTES FOR /network/wifi/activate
#[get("/network/wifi/activate")]
pub fn deploy_client() -> Flash<Redirect> {
@ -581,8 +571,7 @@ pub fn deploy_client() -> Flash<Redirect> {
}
}
/// # helpers and routes for /network/wifi/add
/////////////////////////////////
// HELPERS AND ROUTES FOR /network/wifi/add
#[get("/network/wifi/add")]
pub fn network_add_wifi(flash: Option<FlashMessage>) -> Template {
@ -686,9 +675,7 @@ pub fn add_credentials(wifi: Form<WiFi>) -> Template {
}
}
/// # helpers and routes for /network/wifi/usage
/////////////////////////////////
// HELPERS AND ROUTES FOR WIFI USAGE
#[derive(Debug, Serialize)]
pub struct NetworkAlertContext {
@ -772,264 +759,8 @@ pub fn wifi_usage_alerts(thresholds: Form<Threshold>) -> Flash<Redirect> {
}
}
/// # helpers and routes for /network/activate_ap
//////////////////////////////////
#[post("/api/v1/network/activate_ap")]
pub fn activate_ap() -> Json<JsonResponse> {
// activate the wireless access point
debug!("Activating WiFi access point.");
match network_client::activate_ap() {
Ok(_) => {
let status = "success".to_string();
Json(build_json_response(status, None, None))
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to activate WiFi access point.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/activate_client")]
pub fn activate_client() -> Json<JsonResponse> {
// activate the wireless client
debug!("Activating WiFi client mode.");
match network_client::activate_client() {
Ok(_) => {
let status = "success".to_string();
Json(build_json_response(status, None, None))
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to activate WiFi client mode.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[get("/api/v1/network/ip")]
pub fn return_ip() -> Json<JsonResponse> {
// retrieve ip for wlan0 or set to x.x.x.x if not found
let wlan_ip = match network_client::ip("wlan0") {
Ok(ip) => ip,
Err(_) => "x.x.x.x".to_string(),
};
// retrieve ip for ap0 or set to x.x.x.x if not found
let ap_ip = match network_client::ip("ap0") {
Ok(ip) => ip,
Err(_) => "x.x.x.x".to_string(),
};
let data = json!({
"wlan0": wlan_ip,
"ap0": ap_ip
});
let status = "success".to_string();
Json(build_json_response(status, Some(data), None))
}
#[get("/api/v1/network/rssi")]
pub fn return_rssi() -> Json<JsonResponse> {
// retrieve rssi for connected network
match network_client::rssi("wlan0") {
Ok(rssi) => {
let status = "success".to_string();
let data = json!(rssi);
Json(build_json_response(status, Some(data), None))
}
Err(_) => {
let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[get("/api/v1/network/ssid")]
pub fn return_ssid() -> Json<JsonResponse> {
// retrieve ssid for connected network
match network_client::ssid("wlan0") {
Ok(network) => {
let status = "success".to_string();
let data = json!(network);
Json(build_json_response(status, Some(data), None))
}
Err(_) => {
let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[get("/api/v1/network/state")]
pub fn return_state() -> Json<JsonResponse> {
// retrieve state of wlan0 or set to x.x.x.x if not found
let wlan_state = match network_client::state("wlan0") {
Ok(state) => state,
Err(_) => "unavailable".to_string(),
};
// retrieve state for ap0 or set to x.x.x.x if not found
let ap_state = match network_client::state("ap0") {
Ok(state) => state,
Err(_) => "unavailable".to_string(),
};
let data = json!({
"wlan0": wlan_state,
"ap0": ap_state
});
let status = "success".to_string();
Json(build_json_response(status, Some(data), None))
}
#[get("/api/v1/network/status")]
pub fn return_status() -> Json<JsonResponse> {
// retrieve status info for wlan0 interface
match network_client::status("wlan0") {
Ok(network) => {
let status = "success".to_string();
let data = json!(network);
Json(build_json_response(status, Some(data), None))
}
Err(_) => {
let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[get("/api/v1/network/wifi")]
pub fn scan_networks() -> Json<JsonResponse> {
// retrieve scan results for access-points within range of wlan0
match network_client::available_networks("wlan0") {
Ok(networks) => {
let status = "success".to_string();
let data = json!(networks);
Json(build_json_response(status, Some(data), None))
}
Err(_) => {
let status = "success".to_string();
let msg = "Unable to scan for networks. Interface may be deactivated.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/wifi", data = "<wifi>")]
pub fn add_wifi(wifi: Json<WiFi>) -> Json<JsonResponse> {
// generate and write wifi config to wpa_supplicant
match network_client::add(&wifi.ssid, &wifi.pass) {
Ok(_) => {
debug!("Added WiFi credentials.");
// force reread of wpa_supplicant.conf file with new credentials
match network_client::reconfigure() {
Ok(_) => debug!("Successfully reconfigured wpa_supplicant."),
Err(_) => warn!("Failed to reconfigure wpa_supplicant."),
}
// json response for successful update
let status = "success".to_string();
let msg = "WiFi credentials added.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
Err(_) => {
debug!("Failed to add WiFi credentials.");
// json response for failed update
let status = "error".to_string();
let msg = "Failed to add WiFi credentials.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/wifi/connect", data = "<ssid>")]
pub fn connect_ap(ssid: Json<Ssid>) -> Json<JsonResponse> {
// retrieve the id for the given network ssid
match network_client::id("wlan0", &ssid.ssid) {
// attempt connection with the given network
Ok(id) => match network_client::connect(&id, "wlan0") {
Ok(_) => {
let status = "success".to_string();
let msg = "Connected to chosen network.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to connect to chosen network.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
},
Err(_) => {
let status = "error".to_string();
let msg = "Failed to retrieve the network ID.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/wifi/disconnect", data = "<ssid>")]
pub fn disconnect_ap(ssid: Json<Ssid>) -> Json<JsonResponse> {
// attempt to disable the current network for wlan0 interface
match network_client::disable("wlan0", &ssid.ssid) {
Ok(_) => {
let status = "success".to_string();
let msg = "Disconnected from WiFi network.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to disconnect from WiFi network.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/wifi/forget", data = "<network>")]
pub fn forget_ap(network: Json<Ssid>) -> Json<JsonResponse> {
let ssid = &network.ssid;
match network_client::forget("wlan0", ssid) {
Ok(_) => {
debug!("Removed WiFi credentials for chosen network.");
let status = "success".to_string();
let msg = "WiFi network credentials removed.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
Err(_) => {
warn!("Failed to remove WiFi credentials.");
let status = "error".to_string();
let msg = "Failed to remove WiFi network credentials.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/wifi/modify", data = "<wifi>")]
pub fn modify_password(wifi: Json<WiFi>) -> Json<JsonResponse> {
let ssid = &wifi.ssid;
let pass = &wifi.pass;
// we are using a helper function (`update`) to delete the old
// credentials and add the new ones. this is because the wpa_cli method
// for updating the password does not work.
match network_client::update("wlan0", ssid, pass) {
Ok(_) => {
debug!("WiFi password updated for chosen network.");
let status = "success".to_string();
let msg = "WiFi password updated.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
Err(_) => {
warn!("Failed to update WiFi password.");
let status = "error".to_string();
let msg = "Failed to update WiFi password.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/wifi/usage", data = "<thresholds>")]
pub fn update_wifi_alerts(thresholds: Json<Threshold>) -> Json<JsonResponse> {
pub fn update_wifi_alerts(thresholds: Json<Threshold>) -> Value {
match monitor::update_store(thresholds.into_inner()) {
Ok(_) => {
debug!("WiFi data usage thresholds updated.");
@ -1047,7 +778,7 @@ pub fn update_wifi_alerts(thresholds: Json<Threshold>) -> Json<JsonResponse> {
}
#[post("/api/v1/network/wifi/usage/reset")]
pub fn reset_data_total() -> Json<JsonResponse> {
pub fn reset_data_total() -> Value {
match monitor::reset_data() {
Ok(_) => {
debug!("Reset network data usage total.");
@ -1076,3 +807,260 @@ pub fn reset_data_total() -> Json<JsonResponse> {
}
}
// HELPERS AND ROUTES FOR ACCESS POINT ACTIVATION
#[post("/api/v1/network/activate_ap")]
pub fn activate_ap() -> Value {
// activate the wireless access point
debug!("Activating WiFi access point.");
match network_client::activate_ap() {
Ok(_) => {
let status = "success".to_string();
Json(build_json_response(status, None, None))
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to activate WiFi access point.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
// HELPERS AND ROUTES FOR WIFI CLIENT MANAGEMENT
#[post("/api/v1/network/activate_client")]
pub fn activate_client() -> Value {
// activate the wireless client
debug!("Activating WiFi client mode.");
match network_client::activate_client() {
Ok(_) => {
let status = "success".to_string();
Json(build_json_response(status, None, None))
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to activate WiFi client mode.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/wifi", data = "<wifi>")]
pub fn add_wifi(wifi: Json<WiFi>) -> Value {
// generate and write wifi config to wpa_supplicant
match network_client::add(&wifi.ssid, &wifi.pass) {
Ok(_) => {
debug!("Added WiFi credentials.");
// force reread of wpa_supplicant.conf file with new credentials
match network_client::reconfigure() {
Ok(_) => debug!("Successfully reconfigured wpa_supplicant."),
Err(_) => warn!("Failed to reconfigure wpa_supplicant."),
}
// json response for successful update
let status = "success".to_string();
let msg = "WiFi credentials added.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
Err(_) => {
debug!("Failed to add WiFi credentials.");
// json response for failed update
let status = "error".to_string();
let msg = "Failed to add WiFi credentials.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/wifi/connect", data = "<ssid>")]
pub fn connect_ap(ssid: Json<Ssid>) -> Value {
// retrieve the id for the given network ssid
match network_client::id("wlan0", &ssid.ssid) {
// attempt connection with the given network
Ok(id) => match network_client::connect(&id, "wlan0") {
Ok(_) => {
let status = "success".to_string();
let msg = "Connected to chosen network.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to connect to chosen network.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
},
Err(_) => {
let status = "error".to_string();
let msg = "Failed to retrieve the network ID.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/wifi/disconnect", data = "<ssid>")]
pub fn disconnect_ap(ssid: Json<Ssid>) -> Value {
// attempt to disable the current network for wlan0 interface
match network_client::disable("wlan0", &ssid.ssid) {
Ok(_) => {
let status = "success".to_string();
let msg = "Disconnected from WiFi network.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
Err(_) => {
let status = "error".to_string();
let msg = "Failed to disconnect from WiFi network.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/wifi/forget", data = "<network>")]
pub fn forget_ap(network: Json<Ssid>) -> Value {
let ssid = &network.ssid;
match network_client::forget("wlan0", ssid) {
Ok(_) => {
debug!("Removed WiFi credentials for chosen network.");
let status = "success".to_string();
let msg = "WiFi network credentials removed.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
Err(_) => {
warn!("Failed to remove WiFi credentials.");
let status = "error".to_string();
let msg = "Failed to remove WiFi network credentials.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[post("/api/v1/network/wifi/modify", data = "<wifi>")]
pub fn modify_password(wifi: Json<WiFi>) -> Value {
let ssid = &wifi.ssid;
let pass = &wifi.pass;
// we are using a helper function (`update`) to delete the old
// credentials and add the new ones. this is because the wpa_cli method
// for updating the password does not work.
match network_client::update("wlan0", ssid, pass) {
Ok(_) => {
debug!("WiFi password updated for chosen network.");
let status = "success".to_string();
let msg = "WiFi password updated.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
Err(_) => {
warn!("Failed to update WiFi password.");
let status = "error".to_string();
let msg = "Failed to update WiFi password.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
// HELPERS AND ROUTES FOR NETWORK STATE QUERIES
#[get("/api/v1/network/ip")]
pub fn return_ip() -> Value {
// retrieve ip for wlan0 or set to x.x.x.x if not found
let wlan_ip = match network_client::ip("wlan0") {
Ok(ip) => ip,
Err(_) => "x.x.x.x".to_string(),
};
// retrieve ip for ap0 or set to x.x.x.x if not found
let ap_ip = match network_client::ip("ap0") {
Ok(ip) => ip,
Err(_) => "x.x.x.x".to_string(),
};
let data = json!({
"wlan0": wlan_ip,
"ap0": ap_ip
});
let status = "success".to_string();
Json(build_json_response(status, Some(data), None))
}
#[get("/api/v1/network/rssi")]
pub fn return_rssi() -> Value {
// retrieve rssi for connected network
match network_client::rssi("wlan0") {
Ok(rssi) => {
let status = "success".to_string();
let data = json!(rssi);
Json(build_json_response(status, Some(data), None))
}
Err(_) => {
let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[get("/api/v1/network/ssid")]
pub fn return_ssid() -> Value {
// retrieve ssid for connected network
match network_client::ssid("wlan0") {
Ok(network) => {
let status = "success".to_string();
let data = json!(network);
Json(build_json_response(status, Some(data), None))
}
Err(_) => {
let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[get("/api/v1/network/state")]
pub fn return_state() -> Value {
// retrieve state of wlan0 or set to x.x.x.x if not found
let wlan_state = match network_client::state("wlan0") {
Ok(state) => state,
Err(_) => "unavailable".to_string(),
};
// retrieve state for ap0 or set to x.x.x.x if not found
let ap_state = match network_client::state("ap0") {
Ok(state) => state,
Err(_) => "unavailable".to_string(),
};
let data = json!({
"wlan0": wlan_state,
"ap0": ap_state
});
let status = "success".to_string();
Json(build_json_response(status, Some(data), None))
}
#[get("/api/v1/network/status")]
pub fn return_status() -> Value {
// retrieve status info for wlan0 interface
match network_client::status("wlan0") {
Ok(network) => {
let status = "success".to_string();
let data = json!(network);
Json(build_json_response(status, Some(data), None))
}
Err(_) => {
let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string();
Json(build_json_response(status, None, Some(msg)))
}
}
}
#[get("/api/v1/network/wifi")]
pub fn scan_networks() -> JsonResponse {
// retrieve scan results for access-points within range of wlan0
match network_client::available_networks("wlan0") {
Ok(networks) => {
let status = "success".to_string();
let data = json!(networks);
build_json_response(status, Some(data), None)
}
Err(_) => {
let status = "success".to_string();
let msg = "Unable to scan for networks. Interface may be deactivated.".to_string();
build_json_response(status, None, Some(msg))
}
}
}

View File

@ -5,7 +5,7 @@ use rocket::http::{ContentType, Status};
use rocket::local::Client;
use rocket_contrib::json;
use crate::utils::{build_json_response};
use crate::utils::build_json_response;
use super::rocket;

View File

@ -1,27 +1,27 @@
pub mod monitor;
use rocket_contrib::json::{JsonValue};
use serde::Serialize;
use rocket::serde::json::{Json, Value, json};
use rocket::serde::{Serialize, Deserialize};
// HELPER FUNCTIONS
#[derive(Serialize)]
#[derive(Serialize, Deserialize)]
pub struct JsonResponse {
pub status: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<JsonValue>,
pub data: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg: Option<String>,
}
pub fn build_json_response(
status: String,
data: Option<JsonValue>,
data: Option<Value>,
msg: Option<String>,
) -> JsonResponse {
JsonResponse { status, data, msg }
) -> Value {
json!({ "status": status, "data": data, "msg": msg })
}
#[derive(Debug, Serialize)]
pub struct FlashContext {
pub flash_name: Option<String>,

View File

@ -4,7 +4,7 @@ use std::convert::TryInto;
use nest::{Error, Store, Value};
use rocket::request::FromForm;
use serde::{Deserialize, Serialize};
use rocket::serde::{Deserialize, Serialize};
use serde_json::json;
/// Network traffic data total

View File

@ -9,12 +9,12 @@ use websocket::sync::Server;
use websocket::{Message, OwnedMessage};
pub fn websocket_server(address: String) -> io::Result<()> {
// Start listening for WebSocket connections
// start listening for WebSocket connections
let ws_server = Server::bind(address)?;
info!("Listening for WebSocket connections.");
for connection in ws_server.filter_map(Result::ok) {
// Spawn a new thread for each connection.
// spawn a new thread for each connection
thread::spawn(move || {
if !connection
.protocols()