Merge pull request 'Upgrade peach-web to use rocket 0.5' (#15) from rocket0.5 into main

Reviewed-on: #15
This commit is contained in:
notplants 2021-11-05 11:07:02 +00:00
commit 0249a191d1
17 changed files with 1640 additions and 945 deletions

1693
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,150 +0,0 @@
//! # peach-web
//!
//! `peach-web` provides a web interface for monitoring and interacting with the
//! PeachCloud device. This allows administration of the single-board computer
//! (ie. Raspberry Pi) running PeachCloud, as well as the ssb-server and related
//! plugins.
//!
//! ## Design
//!
//! `peach-web` is written primarily in Rust and presents a web interface for
//! interacting with the device. The stack currently consists of Rocket (Rust
//! web framework), Tera (Rust template engine inspired by Jinja2 and the Django
//! template language), HTML, CSS and JavaScript. Additional functionality is
//! provided by JSON-RPC clients for the `peach-network` and `peach-stats`
//! microservices.
//!
//! HTML is rendered server-side. Request handlers call JSON-RPC microservices
//! and serve HTML and assets. A JSON API is exposed for remote calls and
//! dynamic client-side content updates via vanilla JavaScript following
//! unobstructive design principles. A basic Websockets server is included,
//! though is not currently utilised. Each Tera template is passed a context
//! object. In the case of Rust, this object is a `struct` and must implement
//! `Serialize`. The fields of the context object are available in the context
//! of the template to be rendered.
#![feature(proc_macro_hygiene, decl_macro)]
pub mod error;
pub mod routes;
#[cfg(test)]
mod tests;
pub mod utils;
mod ws;
use std::{env, thread};
use log::{debug, error, info};
use rocket::{catchers, routes};
use rocket_contrib::templates::Template;
use crate::routes::authentication::*;
use crate::routes::device::*;
use crate::routes::helpers::*;
use crate::routes::index::*;
use crate::routes::ping::*;
use crate::routes::scuttlebutt::*;
use crate::routes::settings::admin::*;
use crate::routes::settings::dns::*;
use crate::routes::settings::network::*;
use crate::ws::*;
pub type BoxError = Box<dyn std::error::Error>;
// create rocket instance & mount web & json routes (makes testing easier)
fn rocket() -> rocket::Rocket {
rocket::ignite()
.mount(
"/",
routes![
add_credentials, // WEB ROUTE
connect_wifi, // WEB ROUTE
disconnect_wifi, // WEB ROUTE
deploy_ap, // WEB ROUTE
deploy_client, // WEB ROUTE
device_stats, // WEB ROUTE
files, // WEB ROUTE
forget_wifi, // WEB ROUTE
help, // WEB ROUTE
index, // WEB ROUTE
login, // WEB ROUTE
logout, // WEB ROUTE
messages, // WEB ROUTE
network_home, // WEB ROUTE
network_add_ssid, // WEB ROUTE
network_add_wifi, // WEB ROUTE
network_detail, // WEB ROUTE
peers, // WEB ROUTE
profile, // WEB ROUTE
reboot_cmd, // WEB ROUTE
shutdown_cmd, // WEB ROUTE
shutdown_menu, // WEB ROUTE
wifi_list, // WEB ROUTE
wifi_password, // WEB ROUTE
wifi_set_password, // WEB ROUTE
wifi_usage, // WEB ROUTE
wifi_usage_alerts, // WEB ROUTE
wifi_usage_reset, // WEB ROUTE
configure_dns, // WEB ROUTE
configure_dns_post, // WEB ROUTE
change_password, // WEB ROUTE
reset_password, // WEB ROUTE
reset_password_post, // WEB ROUTE
send_password_reset_page, // WEB ROUTE
send_password_reset_post, // WEB ROUTE
configure_admin, // WEB ROUTE
add_admin, // WEB ROUTE
add_admin_post, // WEB ROUTE
delete_admin_post, // WEB ROUTE
activate_ap, // JSON API
activate_client, // JSON API
add_wifi, // JSON API
connect_ap, // JSON API
disconnect_ap, // JSON API
forget_ap, // JSON API
modify_password, // JSON API
ping_pong, // JSON API
test_route, // JSON API
ping_network, // JSON API
ping_oled, // JSON API
ping_stats, // JSON API
reset_data_total, // JSON API
return_ip, // JSON API
return_rssi, // JSON API
return_ssid, // JSON API
return_state, // JSON API
return_status, // JSON API
reboot_device, // JSON API
scan_networks, // JSON API
shutdown_device, // JSON API
update_wifi_alerts, // JSON API
save_dns_configuration_endpoint, // JSON API
save_password_form_endpoint, // JSON API
reset_password_form_endpoint, // JSON API
],
)
.register(catchers![not_found, internal_error])
.attach(Template::fairing())
}
// launch the rocket server
pub fn run() -> Result<(), BoxError> {
info!("Starting up.");
// spawn a separate thread for rocket to prevent blocking websockets
thread::spawn(|| {
info!("Launching Rocket server.");
rocket().launch();
});
// NOTE: websockets are not currently in use (may be in the future)
let ws_addr = env::var("PEACH_WEB_WS").unwrap_or_else(|_| "0.0.0.0:5115".to_string());
match websocket_server(ws_addr) {
Ok(_) => debug!("Websocket server terminated without error."),
Err(e) => error!("Error starting the websocket server: {}", e),
};
Ok(())
}

View File

@ -1,16 +1,145 @@
//! Initialize the logger and run the application, catching any errors. //! # peach-web
//!
//! `peach-web` provides a web interface for monitoring and interacting with the
//! PeachCloud device. This allows administration of the single-board computer
//! (ie. Raspberry Pi) running PeachCloud, as well as the ssb-server and related
//! plugins.
//!
//! ## Design
//!
//! `peach-web` is written primarily in Rust and presents a web interface for
//! interacting with the device. The stack currently consists of Rocket (Rust
//! web framework), Tera (Rust template engine inspired by Jinja2 and the Django
//! template language), HTML, CSS and JavaScript. Additional functionality is
//! provided by JSON-RPC clients for the `peach-network` and `peach-stats`
//! microservices.
//!
//! HTML is rendered server-side. Request handlers call JSON-RPC microservices
//! and serve HTML and assets. A JSON API is exposed for remote calls and
//! dynamic client-side content updates via vanilla JavaScript following
//! unobstructive design principles. Each Tera template is passed a context
//! object. In the case of Rust, this object is a `struct` and must implement
//! `Serialize`. The fields of the context object are available in the context
//! of the template to be rendered.
#![feature(proc_macro_hygiene, decl_macro)]
pub mod error;
pub mod routes;
#[cfg(test)]
mod tests;
pub mod utils;
use log::{info, error};
use std::process; use std::process;
use log::error; use rocket::{catchers, routes, Rocket, Build, fs::FileServer};
use rocket_dyn_templates::Template;
fn main() { use crate::routes::authentication::*;
// initialize the logger use crate::routes::device::*;
use crate::routes::helpers::*;
use crate::routes::index::*;
use crate::routes::ping::*;
use crate::routes::scuttlebutt::*;
use crate::routes::settings::admin::*;
use crate::routes::settings::dns::*;
use crate::routes::settings::network::*;
pub type BoxError = Box<dyn std::error::Error>;
/// Create rocket instance & mount all routes.
fn init_rocket() -> Rocket<Build> {
rocket::build()
.mount(
"/",
routes![
add_credentials, // WEB ROUTE
connect_wifi, // WEB ROUTE
disconnect_wifi, // WEB ROUTE
deploy_ap, // WEB ROUTE
deploy_client, // WEB ROUTE
device_stats, // WEB ROUTE
forget_wifi, // WEB ROUTE
help, // WEB ROUTE
index, // WEB ROUTE
login, // WEB ROUTE
logout, // WEB ROUTE
messages, // WEB ROUTE
network_home, // WEB ROUTE
network_add_ssid, // WEB ROUTE
network_add_wifi, // WEB ROUTE
network_detail, // WEB ROUTE
peers, // WEB ROUTE
profile, // WEB ROUTE
reboot_cmd, // WEB ROUTE
shutdown_cmd, // WEB ROUTE
shutdown_menu, // WEB ROUTE
wifi_list, // WEB ROUTE
wifi_password, // WEB ROUTE
wifi_set_password, // WEB ROUTE
wifi_usage, // WEB ROUTE
wifi_usage_alerts, // WEB ROUTE
wifi_usage_reset, // WEB ROUTE
configure_dns, // WEB ROUTE
configure_dns_post, // WEB ROUTE
change_password, // WEB ROUTE
reset_password, // WEB ROUTE
reset_password_post, // WEB ROUTE
send_password_reset_page, // WEB ROUTE
send_password_reset_post, // WEB ROUTE
configure_admin, // WEB ROUTE
add_admin, // WEB ROUTE
add_admin_post, // WEB ROUTE
delete_admin_post, // WEB ROUTE
activate_ap, // JSON API
activate_client, // JSON API
add_wifi, // JSON API
connect_ap, // JSON API
disconnect_ap, // JSON API
forget_ap, // JSON API
modify_password, // JSON API
ping_pong, // JSON API
ping_network, // JSON API
ping_oled, // JSON API
ping_stats, // JSON API
reset_data_total, // JSON API
return_ip, // JSON API
return_rssi, // JSON API
return_ssid, // JSON API
return_state, // JSON API
return_status, // JSON API
reboot_device, // JSON API
scan_networks, // JSON API
shutdown_device, // JSON API
update_wifi_alerts, // JSON API
save_dns_configuration_endpoint, // JSON API
save_password_form_endpoint, // JSON API
reset_password_form_endpoint, // JSON API
],
)
.mount("/", FileServer::from("static"))
.register("/", catchers![not_found, internal_error])
.attach(Template::fairing())
}
/// Launch the peach-web rocket server.
#[rocket::main]
async fn main() {
// initialize logger
env_logger::init(); env_logger::init();
// handle errors returned from `run` // initialize rocket
if let Err(e) = peach_web::run() { info!("Initializing Rocket");
error!("Application error: {}", e); let rocket = init_rocket();
// launch rocket
info!("Launching Rocket");
if let Err(e) = rocket.launch().await {
error!("Error in Rocket application: {}", e);
process::exit(1); process::exit(1);
} }
} }

View File

@ -1,14 +1,17 @@
use log::{debug, info}; use log::{debug, info};
use rocket::request::{FlashMessage, Form, FromForm}; use rocket::request::{FlashMessage};
use rocket::form::{Form, FromForm};
use rocket::response::{Flash, Redirect}; use rocket::response::{Flash, Redirect};
use rocket::{get, post}; use rocket::{get, post};
use rocket_contrib::{json::Json, templates::Template}; use rocket::serde::json::Json;
use serde::{Deserialize, Serialize}; use rocket_dyn_templates::Template;
use rocket::serde::{Deserialize, Serialize};
use peach_lib::password_utils; use peach_lib::password_utils;
use crate::error::PeachWebError; use crate::error::PeachWebError;
use crate::utils::{build_json_response, JsonResponse}; use crate::utils::build_json_response;
use rocket::serde::json::Value;
// HELPERS AND ROUTES FOR /login // HELPERS AND ROUTES FOR /login
@ -39,8 +42,8 @@ pub fn login(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().to_string()); context.flash_msg = Some(flash.message().to_string());
}; };
Template::render("login", &context) Template::render("login", &context)
} }
@ -138,8 +141,8 @@ pub fn reset_password(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().to_string()); context.flash_msg = Some(flash.message().to_string());
}; };
Template::render("password/reset_password", &context) Template::render("password/reset_password", &context)
} }
@ -178,18 +181,18 @@ pub fn reset_password_post(reset_password_form: Form<ResetPasswordForm>) -> Temp
#[post("/public/api/v1/reset_password", data = "<reset_password_form>")] #[post("/public/api/v1/reset_password", data = "<reset_password_form>")]
pub fn reset_password_form_endpoint( pub fn reset_password_form_endpoint(
reset_password_form: Json<ResetPasswordForm>, reset_password_form: Json<ResetPasswordForm>,
) -> Json<JsonResponse> { ) -> Value {
let result = save_reset_password_form(reset_password_form.into_inner()); let result = save_reset_password_form(reset_password_form.into_inner());
match result { match result {
Ok(_) => { Ok(_) => {
let status = "success".to_string(); let status = "success".to_string();
let msg = "New password is now saved. Return home to login.".to_string(); let msg = "New password is now saved. Return home to login.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(err) => { Err(err) => {
let status = "error".to_string(); let status = "error".to_string();
let msg = format!("{}", err); let msg = format!("{}", err);
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
@ -224,8 +227,8 @@ pub fn send_password_reset_page(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().to_string()); context.flash_msg = Some(flash.message().to_string());
}; };
Template::render("password/send_password_reset", &context) Template::render("password/send_password_reset", &context)
} }
@ -295,8 +298,8 @@ pub fn change_password(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().to_string()); context.flash_msg = Some(flash.message().to_string());
}; };
Template::render("password/change_password", &context) Template::render("password/change_password", &context)
} }
@ -330,18 +333,18 @@ pub fn change_password_post(password_form: Form<PasswordForm>) -> Template {
/// JSON change password form request handler. /// JSON change password form request handler.
#[post("/api/v1/settings/change_password", data = "<password_form>")] #[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()); let result = save_password_form(password_form.into_inner());
match result { match result {
Ok(_) => { Ok(_) => {
let status = "success".to_string(); let status = "success".to_string();
let msg = "Your password was successfully changed".to_string(); let msg = "Your password was successfully changed".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(err) => { Err(err) => {
let status = "error".to_string(); let status = "error".to_string();
let msg = format!("{}", err); let msg = format!("{}", err);
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }

View File

@ -4,7 +4,8 @@ use rocket::{
request::FlashMessage, request::FlashMessage,
response::{Flash, Redirect}, response::{Flash, Redirect},
}; };
use rocket_contrib::{json::Json, templates::Template};
use rocket_dyn_templates::Template;
use serde::Serialize; use serde::Serialize;
use std::{ use std::{
io, io,
@ -15,7 +16,8 @@ use peach_lib::config_manager::load_peach_config;
use peach_lib::stats_client::{CpuStatPercentages, DiskUsage, LoadAverage, MemStat}; use peach_lib::stats_client::{CpuStatPercentages, DiskUsage, LoadAverage, MemStat};
use peach_lib::{dyndns_client, network_client, oled_client, sbot_client, stats_client}; use peach_lib::{dyndns_client, network_client, oled_client, sbot_client, stats_client};
use crate::utils::{build_json_response, JsonResponse}; use crate::utils::build_json_response;
use rocket::serde::json::Value;
// HELPERS AND ROUTES FOR /device // HELPERS AND ROUTES FOR /device
@ -156,8 +158,8 @@ pub fn device_stats(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().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("device", &context) Template::render("device", &context)
@ -188,19 +190,19 @@ pub fn reboot_cmd() -> Flash<Redirect> {
/// JSON request handler for device reboot. /// JSON request handler for device reboot.
#[post("/api/v1/device/reboot")] #[post("/api/v1/device/reboot")]
pub fn reboot_device() -> Json<JsonResponse> { pub fn reboot_device() -> Value {
match reboot() { match reboot() {
Ok(_) => { Ok(_) => {
debug!("Going down for reboot..."); debug!("Going down for reboot...");
let status = "success".to_string(); let status = "success".to_string();
let msg = "Going down for reboot.".to_string(); let msg = "Going down for reboot.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(_) => { Err(_) => {
warn!("Reboot failed"); warn!("Reboot failed");
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to reboot the device.".to_string(); let msg = "Failed to reboot the device.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
@ -226,19 +228,19 @@ pub fn shutdown_cmd() -> Flash<Redirect> {
// shutdown the device // shutdown the device
#[post("/api/v1/device/shutdown")] #[post("/api/v1/device/shutdown")]
pub fn shutdown_device() -> Json<JsonResponse> { pub fn shutdown_device() -> Value {
match shutdown() { match shutdown() {
Ok(_) => { Ok(_) => {
debug!("Going down for shutdown..."); debug!("Going down for shutdown...");
let status = "success".to_string(); let status = "success".to_string();
let msg = "Going down for shutdown.".to_string(); let msg = "Going down for shutdown.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(_) => { Err(_) => {
warn!("Shutdown failed"); warn!("Shutdown failed");
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to shutdown the device.".to_string(); let msg = "Failed to shutdown the device.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
@ -272,8 +274,8 @@ pub fn shutdown_menu(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().to_string()); context.flash_msg = Some(flash.message().to_string());
}; };
Template::render("shutdown", &context) Template::render("shutdown", &context)
} }

View File

@ -1,13 +1,7 @@
use log::debug; use log::debug;
use rocket::{catch, get, response::NamedFile}; use rocket::{catch};
use rocket_contrib::templates::Template; use rocket_dyn_templates::Template;
use serde::Serialize; 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 ERROR // HELPERS AND ROUTES FOR 404 ERROR

View File

@ -1,5 +1,5 @@
use rocket::{get, request::FlashMessage}; use rocket::{get, request::FlashMessage};
use rocket_contrib::templates::Template; use rocket_dyn_templates::Template;
use serde::Serialize; use serde::Serialize;
// HELPERS AND ROUTES FOR / (HOME PAGE) // HELPERS AND ROUTES FOR / (HOME PAGE)
@ -60,8 +60,8 @@ pub fn help(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().to_string()); context.flash_msg = Some(flash.message().to_string());
}; };
Template::render("help", &context) Template::render("help", &context)
} }

View File

@ -1,86 +1,76 @@
//! Helper routes for pinging services to check that they are active //! Helper routes for pinging services to check that they are active
use log::{debug, warn}; use log::{debug, warn};
use rocket::get; use rocket::get;
use rocket_contrib::json::Json; use rocket::serde::json::{Value};
use peach_lib::dyndns_client::is_dns_updater_online;
use peach_lib::network_client; use peach_lib::network_client;
use peach_lib::oled_client; use peach_lib::oled_client;
use peach_lib::stats_client; use peach_lib::stats_client;
use crate::utils::{build_json_response, JsonResponse}; use crate::utils::build_json_response;
/// Status route: useful for checking connectivity from web client. /// Status route: useful for checking connectivity from web client.
#[get("/api/v1/ping")] #[get("/api/v1/ping")]
pub fn ping_pong() -> Json<JsonResponse> { pub fn ping_pong() -> Value {
// ping pong // ping pong
let status = "success".to_string(); let status = "success".to_string();
let msg = "pong!".to_string(); let msg = "pong!".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
}
/// Test route: useful for ad hoc testing.
#[get("/api/v1/test")]
pub fn test_route() -> Json<JsonResponse> {
let val = is_dns_updater_online().unwrap();
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")] #[get("/api/v1/ping/network")]
pub fn ping_network() -> Json<JsonResponse> { pub fn ping_network() -> Value {
match network_client::ping() { match network_client::ping() {
Ok(_) => { Ok(_) => {
debug!("peach-network responded successfully"); debug!("peach-network responded successfully");
let status = "success".to_string(); let status = "success".to_string();
let msg = "peach-network is available.".to_string(); let msg = "peach-network is available.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(_) => { Err(_) => {
warn!("peach-network failed to respond"); warn!("peach-network failed to respond");
let status = "error".to_string(); let status = "error".to_string();
let msg = "peach-network is unavailable.".to_string(); let msg = "peach-network is unavailable.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
/// Status route: check availability of `peach-oled` microservice. /// Status route: check availability of `peach-oled` microservice.
#[get("/api/v1/ping/oled")] #[get("/api/v1/ping/oled")]
pub fn ping_oled() -> Json<JsonResponse> { pub fn ping_oled() -> Value {
match oled_client::ping() { match oled_client::ping() {
Ok(_) => { Ok(_) => {
debug!("peach-oled responded successfully"); debug!("peach-oled responded successfully");
let status = "success".to_string(); let status = "success".to_string();
let msg = "peach-oled is available.".to_string(); let msg = "peach-oled is available.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(_) => { Err(_) => {
warn!("peach-oled failed to respond"); warn!("peach-oled failed to respond");
let status = "error".to_string(); let status = "error".to_string();
let msg = "peach-oled is unavailable.".to_string(); let msg = "peach-oled is unavailable.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
/// Status route: check availability of `peach-stats` microservice. /// Status route: check availability of `peach-stats` microservice.
#[get("/api/v1/ping/stats")] #[get("/api/v1/ping/stats")]
pub fn ping_stats() -> Json<JsonResponse> { pub fn ping_stats() -> Value {
match stats_client::ping() { match stats_client::ping() {
Ok(_) => { Ok(_) => {
debug!("peach-stats responded successfully"); debug!("peach-stats responded successfully");
let status = "success".to_string(); let status = "success".to_string();
let msg = "peach-stats is available.".to_string(); let msg = "peach-stats is available.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(_) => { Err(_) => {
warn!("peach-stats failed to respond"); warn!("peach-stats failed to respond");
let status = "error".to_string(); let status = "error".to_string();
let msg = "peach-stats is unavailable.".to_string(); let msg = "peach-stats is unavailable.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }

View File

@ -1,7 +1,7 @@
//! Routes for ScuttleButt related functionality. //! Routes for ScuttleButt related functionality.
use rocket::{get, request::FlashMessage}; use rocket::{get, request::FlashMessage};
use rocket_contrib::templates::Template; use rocket_dyn_templates::Template;
use serde::Serialize; use serde::Serialize;
// HELPERS AND ROUTES FOR /messages // HELPERS AND ROUTES FOR /messages
@ -33,8 +33,8 @@ pub fn messages(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().to_string()); context.flash_msg = Some(flash.message().to_string());
}; };
Template::render("messages", &context) Template::render("messages", &context)
} }
@ -68,8 +68,8 @@ pub fn peers(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().to_string()); context.flash_msg = Some(flash.message().to_string());
}; };
Template::render("peers", &context) Template::render("peers", &context)
} }
@ -103,8 +103,8 @@ pub fn profile(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().to_string()); context.flash_msg = Some(flash.message().to_string());
}; };
Template::render("profile", &context) Template::render("profile", &context)
} }

View File

@ -1,11 +1,12 @@
use rocket::{ use rocket::{
get, post, get, post,
request::{FlashMessage, Form, FromForm}, request::FlashMessage,
form::{Form, FromForm},
response::{Flash, Redirect}, response::{Flash, Redirect},
uri, uri,
}; };
use rocket_contrib::templates::Template; use rocket_dyn_templates::Template;
use serde::{Deserialize, Serialize}; use rocket::serde::{Deserialize, Serialize};
use peach_lib::config_manager; use peach_lib::config_manager;
use peach_lib::config_manager::load_peach_config; use peach_lib::config_manager::load_peach_config;
@ -47,8 +48,8 @@ pub fn configure_admin(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().to_string()); context.flash_msg = Some(flash.message().to_string());
}; };
Template::render("admin/configure_admin", &context) Template::render("admin/configure_admin", &context)
} }
@ -93,8 +94,8 @@ pub fn add_admin(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().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("admin/add_admin", &context) Template::render("admin/add_admin", &context)

View File

@ -1,10 +1,12 @@
use log::info; use log::info;
use rocket::{ use rocket::{
get, post, get, post,
request::{FlashMessage, Form, FromForm}, request::FlashMessage,
form::{Form, FromForm}
}; };
use rocket_contrib::{json::Json, templates::Template}; use rocket::serde::json::Json;
use serde::{Deserialize, Serialize}; use rocket_dyn_templates::Template;
use rocket::serde::{Deserialize, Serialize};
use peach_lib::config_manager; use peach_lib::config_manager;
use peach_lib::config_manager::load_peach_config; use peach_lib::config_manager::load_peach_config;
@ -18,7 +20,8 @@ use peach_lib::jsonrpc_client_core::{Error, ErrorKind};
use peach_lib::jsonrpc_core::types::error::ErrorCode; use peach_lib::jsonrpc_core::types::error::ErrorCode;
use crate::error::PeachWebError; use crate::error::PeachWebError;
use crate::utils::{build_json_response, JsonResponse}; use crate::utils::build_json_response;
use rocket::serde::json::Value;
#[derive(Debug, Deserialize, FromForm)] #[derive(Debug, Deserialize, FromForm)]
pub struct DnsForm { pub struct DnsForm {
@ -118,8 +121,8 @@ pub fn configure_dns(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().to_string()); context.flash_msg = Some(flash.message().to_string());
}; };
Template::render("configure_dns", &context) Template::render("configure_dns", &context)
} }
@ -150,18 +153,18 @@ pub fn configure_dns_post(dns: Form<DnsForm>) -> Template {
} }
#[post("/api/v1/dns/configure", data = "<dns_form>")] #[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()); let result = save_dns_configuration(dns_form.into_inner());
match result { match result {
Ok(_) => { Ok(_) => {
let status = "success".to_string(); let status = "success".to_string();
let msg = "New dynamic dns configuration is now enabled".to_string(); let msg = "New dynamic dns configuration is now enabled".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(err) => { Err(err) => {
let status = "error".to_string(); let status = "error".to_string();
let msg = format!("{}", err); let msg = format!("{}", err);
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }

View File

@ -1,15 +1,16 @@
use log::{debug, warn}; use log::{debug, warn};
use percent_encoding::percent_decode;
use rocket::{ use rocket::{
get, get,
http::RawStr,
post, post,
request::{FlashMessage, Form, FromForm}, request::FlashMessage,
form::{Form, FromForm},
response::{Flash, Redirect}, response::{Flash, Redirect},
uri, UriDisplayQuery, uri, UriDisplayQuery,
}; };
use rocket_contrib::{json, json::Json, templates::Template}; use rocket::serde::json::{json, Json};
use serde::{Deserialize, Serialize}; use rocket_dyn_templates::Template;
use rocket::serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use peach_lib::network_client; use peach_lib::network_client;
@ -18,7 +19,8 @@ use peach_lib::stats_client::Traffic;
use crate::utils::monitor; use crate::utils::monitor;
use crate::utils::monitor::{Alert, Data, Threshold}; use crate::utils::monitor::{Alert, Data, Threshold};
use crate::utils::{build_json_response, JsonResponse}; use crate::utils::build_json_response;
use rocket::serde::json::Value;
// STRUCTS USED BY NETWORK ROUTES // STRUCTS USED BY NETWORK ROUTES
@ -50,7 +52,7 @@ pub fn wifi_usage_reset() -> Flash<Redirect> {
#[post("/network/wifi/connect", data = "<network>")] #[post("/network/wifi/connect", data = "<network>")]
pub fn connect_wifi(network: Form<Ssid>) -> Flash<Redirect> { pub fn connect_wifi(network: Form<Ssid>) -> Flash<Redirect> {
let ssid = &network.ssid; let ssid = &network.ssid;
let url = uri!(network_detail: ssid); let url = uri!(network_detail(ssid = ssid));
match network_client::id("wlan0", ssid) { match network_client::id("wlan0", ssid) {
Ok(id) => match network_client::connect(&id, "wlan0") { Ok(id) => match network_client::connect(&id, "wlan0") {
Ok(_) => Flash::success(Redirect::to(url), "Connected to chosen network"), Ok(_) => Flash::success(Redirect::to(url), "Connected to chosen network"),
@ -84,21 +86,19 @@ pub fn forget_wifi(network: Form<Ssid>) -> Flash<Redirect> {
} }
#[get("/network/wifi/modify?<ssid>")] #[get("/network/wifi/modify?<ssid>")]
pub fn wifi_password(ssid: &RawStr, flash: Option<FlashMessage>) -> Template { pub fn wifi_password(ssid: &str, flash: Option<FlashMessage>) -> Template {
// decode ssid from url
let decoded_ssid = percent_decode(ssid.as_bytes()).decode_utf8().unwrap();
let mut context = NetworkAddContext { let mut context = NetworkAddContext {
back: Some("/network/wifi".to_string()), back: Some("/network/wifi".to_string()),
flash_name: None, flash_name: None,
flash_msg: None, flash_msg: None,
selected: Some(decoded_ssid.to_string()), selected: Some(ssid.to_string()),
title: Some("Update WiFi Password".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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().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("network_modify", &context) Template::render("network_modify", &context)
@ -108,7 +108,7 @@ pub fn wifi_password(ssid: &RawStr, flash: Option<FlashMessage>) -> Template {
pub fn wifi_set_password(wifi: Form<WiFi>) -> Flash<Redirect> { pub fn wifi_set_password(wifi: Form<WiFi>) -> Flash<Redirect> {
let ssid = &wifi.ssid; let ssid = &wifi.ssid;
let pass = &wifi.pass; let pass = &wifi.pass;
let url = uri!(network_detail: ssid); let url = uri!(network_detail(ssid = ssid));
match network_client::update("wlan0", ssid, pass) { match network_client::update("wlan0", 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(
@ -283,8 +283,8 @@ pub fn network_home(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().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("network_card", &context) Template::render("network_card", &context)
@ -383,8 +383,8 @@ pub fn wifi_list(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().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("network_list", &context) Template::render("network_list", &context)
@ -540,19 +540,17 @@ impl NetworkDetailContext {
} }
#[get("/network/wifi?<ssid>")] #[get("/network/wifi?<ssid>")]
pub fn network_detail(ssid: &RawStr, flash: Option<FlashMessage>) -> Template { pub fn network_detail(ssid: &str, flash: Option<FlashMessage>) -> Template {
// assign context through context_builder call // assign context through context_builder call
let mut context = NetworkDetailContext::build(); let mut context = NetworkDetailContext::build();
context.back = Some("/network/wifi".to_string()); context.back = Some("/network/wifi".to_string());
context.title = Some("WiFi Network".to_string()); context.title = Some("WiFi Network".to_string());
// decode ssid from url context.selected = Some(ssid.to_string());
let decoded_ssid = percent_decode(ssid.as_bytes()).decode_utf8().unwrap();
context.selected = Some(decoded_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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().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("network_detail", &context) Template::render("network_detail", &context)
@ -581,8 +579,8 @@ pub fn network_add_wifi(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().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("network_add", &context) Template::render("network_add", &context)
@ -611,18 +609,16 @@ impl NetworkAddContext {
} }
#[get("/network/wifi/add?<ssid>")] #[get("/network/wifi/add?<ssid>")]
pub fn network_add_ssid(ssid: &RawStr, flash: Option<FlashMessage>) -> Template { pub fn network_add_ssid(ssid: &str, flash: Option<FlashMessage>) -> Template {
// decode ssid from url
let decoded_ssid = percent_decode(ssid.as_bytes()).decode_utf8().unwrap();
let mut context = NetworkAddContext::build(); let mut context = NetworkAddContext::build();
context.back = Some("/network/wifi".to_string()); context.back = Some("/network/wifi".to_string());
context.selected = Some(decoded_ssid.to_string()); context.selected = Some(ssid.to_string());
context.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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().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("network_add", &context) Template::render("network_add", &context)
@ -731,8 +727,8 @@ pub fn wifi_usage(flash: Option<FlashMessage>) -> Template {
// 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.name().to_string()); context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.msg().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("network_usage", &context) Template::render("network_usage", &context)
@ -759,25 +755,25 @@ pub fn wifi_usage_alerts(thresholds: Form<Threshold>) -> Flash<Redirect> {
} }
#[post("/api/v1/network/wifi/usage", data = "<thresholds>")] #[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()) { match monitor::update_store(thresholds.into_inner()) {
Ok(_) => { Ok(_) => {
debug!("WiFi data usage thresholds updated."); debug!("WiFi data usage thresholds updated.");
let status = "success".to_string(); let status = "success".to_string();
let msg = "Updated alert threshold and flags.".to_string(); let msg = "Updated alert threshold and flags.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(_) => { Err(_) => {
warn!("Failed to update WiFi data usage thresholds."); warn!("Failed to update WiFi data usage thresholds.");
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to update WiFi data usage thresholds.".to_string(); let msg = "Failed to update WiFi data usage thresholds.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
#[post("/api/v1/network/wifi/usage/reset")] #[post("/api/v1/network/wifi/usage/reset")]
pub fn reset_data_total() -> Json<JsonResponse> { pub fn reset_data_total() -> Value {
match monitor::reset_data() { match monitor::reset_data() {
Ok(_) => { Ok(_) => {
debug!("Reset network data usage total."); debug!("Reset network data usage total.");
@ -795,13 +791,13 @@ pub fn reset_data_total() -> Json<JsonResponse> {
let data = json!(current_traffic); let data = json!(current_traffic);
let status = "success".to_string(); let status = "success".to_string();
let msg = "Reset network data usage total.".to_string(); let msg = "Reset network data usage total.".to_string();
Json(build_json_response(status, Some(data), Some(msg))) build_json_response(status, Some(data), Some(msg))
} }
Err(_) => { Err(_) => {
warn!("Failed to reset network data usage total."); warn!("Failed to reset network data usage total.");
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to reset network data usage total.".to_string(); let msg = "Failed to reset network data usage total.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
@ -809,18 +805,18 @@ pub fn reset_data_total() -> Json<JsonResponse> {
// HELPERS AND ROUTES FOR ACCESS POINT ACTIVATION // HELPERS AND ROUTES FOR ACCESS POINT ACTIVATION
#[post("/api/v1/network/activate_ap")] #[post("/api/v1/network/activate_ap")]
pub fn activate_ap() -> Json<JsonResponse> { pub fn activate_ap() -> Value {
// 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_client::activate_ap() {
Ok(_) => { Ok(_) => {
let status = "success".to_string(); let status = "success".to_string();
Json(build_json_response(status, None, None)) build_json_response(status, None, None)
} }
Err(_) => { Err(_) => {
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to activate WiFi access point.".to_string(); let msg = "Failed to activate WiFi access point.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
@ -828,24 +824,24 @@ pub fn activate_ap() -> Json<JsonResponse> {
// HELPERS AND ROUTES FOR WIFI CLIENT MANAGEMENT // HELPERS AND ROUTES FOR WIFI CLIENT MANAGEMENT
#[post("/api/v1/network/activate_client")] #[post("/api/v1/network/activate_client")]
pub fn activate_client() -> Json<JsonResponse> { pub fn activate_client() -> Value {
// 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_client::activate_client() {
Ok(_) => { Ok(_) => {
let status = "success".to_string(); let status = "success".to_string();
Json(build_json_response(status, None, None)) build_json_response(status, None, None)
} }
Err(_) => { Err(_) => {
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to activate WiFi client mode.".to_string(); let msg = "Failed to activate WiFi client mode.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
#[post("/api/v1/network/wifi", data = "<wifi>")] #[post("/api/v1/network/wifi", data = "<wifi>")]
pub fn add_wifi(wifi: Json<WiFi>) -> Json<JsonResponse> { pub fn add_wifi(wifi: Json<WiFi>) -> Value {
// generate and write wifi config to wpa_supplicant // generate and write wifi config to wpa_supplicant
match network_client::add(&wifi.ssid, &wifi.pass) { match network_client::add(&wifi.ssid, &wifi.pass) {
Ok(_) => { Ok(_) => {
@ -858,20 +854,20 @@ pub fn add_wifi(wifi: Json<WiFi>) -> Json<JsonResponse> {
// json response for successful update // json response for successful update
let status = "success".to_string(); let status = "success".to_string();
let msg = "WiFi credentials added.".to_string(); let msg = "WiFi credentials added.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(_) => { Err(_) => {
debug!("Failed to add WiFi credentials."); debug!("Failed to add WiFi credentials.");
// json response for failed update // json response for failed update
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to add WiFi credentials.".to_string(); let msg = "Failed to add WiFi credentials.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
#[post("/api/v1/network/wifi/connect", data = "<ssid>")] #[post("/api/v1/network/wifi/connect", data = "<ssid>")]
pub fn connect_ap(ssid: Json<Ssid>) -> Json<JsonResponse> { pub fn connect_ap(ssid: Json<Ssid>) -> Value {
// retrieve the id for the given network ssid // retrieve the id for the given network ssid
match network_client::id("wlan0", &ssid.ssid) { match network_client::id("wlan0", &ssid.ssid) {
// attempt connection with the given network // attempt connection with the given network
@ -879,60 +875,60 @@ pub fn connect_ap(ssid: Json<Ssid>) -> Json<JsonResponse> {
Ok(_) => { Ok(_) => {
let status = "success".to_string(); let status = "success".to_string();
let msg = "Connected to chosen network.".to_string(); let msg = "Connected to chosen network.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(_) => { Err(_) => {
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to connect to chosen network.".to_string(); let msg = "Failed to connect to chosen network.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
}, },
Err(_) => { Err(_) => {
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to retrieve the network ID.".to_string(); let msg = "Failed to retrieve the network ID.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
#[post("/api/v1/network/wifi/disconnect", data = "<ssid>")] #[post("/api/v1/network/wifi/disconnect", data = "<ssid>")]
pub fn disconnect_ap(ssid: Json<Ssid>) -> Json<JsonResponse> { pub fn disconnect_ap(ssid: Json<Ssid>) -> Value {
// attempt to disable the current network for wlan0 interface // attempt to disable the current network for wlan0 interface
match network_client::disable("wlan0", &ssid.ssid) { match network_client::disable("wlan0", &ssid.ssid) {
Ok(_) => { Ok(_) => {
let status = "success".to_string(); let status = "success".to_string();
let msg = "Disconnected from WiFi network.".to_string(); let msg = "Disconnected from WiFi network.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(_) => { Err(_) => {
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to disconnect from WiFi network.".to_string(); let msg = "Failed to disconnect from WiFi network.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
#[post("/api/v1/network/wifi/forget", data = "<network>")] #[post("/api/v1/network/wifi/forget", data = "<network>")]
pub fn forget_ap(network: Json<Ssid>) -> Json<JsonResponse> { pub fn forget_ap(network: Json<Ssid>) -> Value {
let ssid = &network.ssid; let ssid = &network.ssid;
match network_client::forget("wlan0", ssid) { match network_client::forget("wlan0", ssid) {
Ok(_) => { Ok(_) => {
debug!("Removed WiFi credentials for chosen network."); debug!("Removed WiFi credentials for chosen network.");
let status = "success".to_string(); let status = "success".to_string();
let msg = "WiFi network credentials removed.".to_string(); let msg = "WiFi network credentials removed.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(_) => { Err(_) => {
warn!("Failed to remove WiFi credentials."); warn!("Failed to remove WiFi credentials.");
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to remove WiFi network credentials.".to_string(); let msg = "Failed to remove WiFi network credentials.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
#[post("/api/v1/network/wifi/modify", data = "<wifi>")] #[post("/api/v1/network/wifi/modify", data = "<wifi>")]
pub fn modify_password(wifi: Json<WiFi>) -> Json<JsonResponse> { pub fn modify_password(wifi: Json<WiFi>) -> Value {
let ssid = &wifi.ssid; let ssid = &wifi.ssid;
let pass = &wifi.pass; let pass = &wifi.pass;
// we are using a helper function (`update`) to delete the old // we are using a helper function (`update`) to delete the old
@ -943,13 +939,13 @@ pub fn modify_password(wifi: Json<WiFi>) -> Json<JsonResponse> {
debug!("WiFi password updated for chosen network."); debug!("WiFi password updated for chosen network.");
let status = "success".to_string(); let status = "success".to_string();
let msg = "WiFi password updated.".to_string(); let msg = "WiFi password updated.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
Err(_) => { Err(_) => {
warn!("Failed to update WiFi password."); warn!("Failed to update WiFi password.");
let status = "error".to_string(); let status = "error".to_string();
let msg = "Failed to update WiFi password.".to_string(); let msg = "Failed to update WiFi password.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
@ -957,7 +953,7 @@ pub fn modify_password(wifi: Json<WiFi>) -> Json<JsonResponse> {
// HELPERS AND ROUTES FOR NETWORK STATE QUERIES // HELPERS AND ROUTES FOR NETWORK STATE QUERIES
#[get("/api/v1/network/ip")] #[get("/api/v1/network/ip")]
pub fn return_ip() -> Json<JsonResponse> { pub fn return_ip() -> Value {
// retrieve ip for wlan0 or set to x.x.x.x if not found // retrieve ip for wlan0 or set to x.x.x.x if not found
let wlan_ip = match network_client::ip("wlan0") { let wlan_ip = match network_client::ip("wlan0") {
Ok(ip) => ip, Ok(ip) => ip,
@ -973,45 +969,45 @@ pub fn return_ip() -> Json<JsonResponse> {
"ap0": ap_ip "ap0": ap_ip
}); });
let status = "success".to_string(); let status = "success".to_string();
Json(build_json_response(status, Some(data), None)) build_json_response(status, Some(data), None)
} }
#[get("/api/v1/network/rssi")] #[get("/api/v1/network/rssi")]
pub fn return_rssi() -> Json<JsonResponse> { pub fn return_rssi() -> Value {
// retrieve rssi for connected network // retrieve rssi for connected network
match network_client::rssi("wlan0") { match network_client::rssi("wlan0") {
Ok(rssi) => { Ok(rssi) => {
let status = "success".to_string(); let status = "success".to_string();
let data = json!(rssi); let data = json!(rssi);
Json(build_json_response(status, Some(data), None)) build_json_response(status, Some(data), None)
} }
Err(_) => { Err(_) => {
let status = "success".to_string(); let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string(); let msg = "Not currently connected to an access point.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
#[get("/api/v1/network/ssid")] #[get("/api/v1/network/ssid")]
pub fn return_ssid() -> Json<JsonResponse> { pub fn return_ssid() -> Value {
// retrieve ssid for connected network // retrieve ssid for connected network
match network_client::ssid("wlan0") { match network_client::ssid("wlan0") {
Ok(network) => { Ok(network) => {
let status = "success".to_string(); let status = "success".to_string();
let data = json!(network); let data = json!(network);
Json(build_json_response(status, Some(data), None)) build_json_response(status, Some(data), None)
} }
Err(_) => { Err(_) => {
let status = "success".to_string(); let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string(); let msg = "Not currently connected to an access point.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
#[get("/api/v1/network/state")] #[get("/api/v1/network/state")]
pub fn return_state() -> Json<JsonResponse> { pub fn return_state() -> Value {
// retrieve state of wlan0 or set to x.x.x.x if not found // retrieve state of wlan0 or set to x.x.x.x if not found
let wlan_state = match network_client::state("wlan0") { let wlan_state = match network_client::state("wlan0") {
Ok(state) => state, Ok(state) => state,
@ -1027,39 +1023,39 @@ pub fn return_state() -> Json<JsonResponse> {
"ap0": ap_state "ap0": ap_state
}); });
let status = "success".to_string(); let status = "success".to_string();
Json(build_json_response(status, Some(data), None)) build_json_response(status, Some(data), None)
} }
#[get("/api/v1/network/status")] #[get("/api/v1/network/status")]
pub fn return_status() -> Json<JsonResponse> { pub fn return_status() -> Value {
// retrieve status info for wlan0 interface // retrieve status info for wlan0 interface
match network_client::status("wlan0") { match network_client::status("wlan0") {
Ok(network) => { Ok(network) => {
let status = "success".to_string(); let status = "success".to_string();
let data = json!(network); let data = json!(network);
Json(build_json_response(status, Some(data), None)) build_json_response(status, Some(data), None)
} }
Err(_) => { Err(_) => {
let status = "success".to_string(); let status = "success".to_string();
let msg = "Not currently connected to an access point.".to_string(); let msg = "Not currently connected to an access point.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }
#[get("/api/v1/network/wifi")] #[get("/api/v1/network/wifi")]
pub fn scan_networks() -> Json<JsonResponse> { pub fn scan_networks() -> Value {
// retrieve scan results for access-points within range of wlan0 // retrieve scan results for access-points within range of wlan0
match network_client::available_networks("wlan0") { match network_client::available_networks("wlan0") {
Ok(networks) => { Ok(networks) => {
let status = "success".to_string(); let status = "success".to_string();
let data = json!(networks); let data = json!(networks);
Json(build_json_response(status, Some(data), None)) build_json_response(status, Some(data), None)
} }
Err(_) => { Err(_) => {
let status = "success".to_string(); let status = "success".to_string();
let msg = "Unable to scan for networks. Interface may be deactivated.".to_string(); let msg = "Unable to scan for networks. Interface may be deactivated.".to_string();
Json(build_json_response(status, None, Some(msg))) build_json_response(status, None, Some(msg))
} }
} }
} }

View File

@ -1,24 +1,24 @@
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use rocket::serde::json::{Value, json};
use rocket::http::{ContentType, Status}; use rocket::http::{ContentType, Status};
use rocket::local::Client; use rocket::local::blocking::Client;
use rocket_contrib::json;
use crate::utils::build_json_response; use crate::utils::build_json_response;
use super::rocket; use super::init_rocket;
// helper function to test correct retrieval and content of a file // helper function to test correct retrieval and content of a file
fn test_query_file<T>(path: &str, file: T, status: Status) fn test_query_file<T>(path: &str, file: T, status: Status)
where where
T: Into<Option<&'static str>>, T: Into<Option<&'static str>>,
{ {
let client = Client::new(rocket()).unwrap(); let client = Client::tracked(init_rocket()).unwrap();
let mut response = client.get(path).dispatch(); let response = client.get(path).dispatch();
assert_eq!(response.status(), status); assert_eq!(response.status(), status);
let body_data = response.body().and_then(|body| body.into_bytes()); let body_data = response.into_bytes();
if let Some(filename) = file.into() { if let Some(filename) = file.into() {
let expected_data = read_file_content(filename); let expected_data = read_file_content(filename);
assert!(body_data.map_or(false, |s| s == expected_data)); assert!(body_data.map_or(false, |s| s == expected_data));
@ -39,11 +39,11 @@ fn read_file_content(path: &str) -> Vec<u8> {
#[test] #[test]
fn index_html() { fn index_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/").dispatch(); let response = client.get("/").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("/peers")); assert!(body.contains("/peers"));
assert!(body.contains("/profile")); assert!(body.contains("/profile"));
assert!(body.contains("/messages")); assert!(body.contains("/messages"));
@ -54,11 +54,11 @@ fn index_html() {
#[test] #[test]
fn network_card_html() { fn network_card_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/network").dispatch(); let response = client.get("/network").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("MODE")); assert!(body.contains("MODE"));
assert!(body.contains("SSID")); assert!(body.contains("SSID"));
assert!(body.contains("IP")); assert!(body.contains("IP"));
@ -72,11 +72,11 @@ fn network_card_html() {
#[test] #[test]
fn network_list_html() { fn network_list_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/network/wifi").dispatch(); let response = client.get("/network/wifi").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("WiFi Networks")); assert!(body.contains("WiFi Networks"));
assert!(body.contains("No saved or available networks found.")); assert!(body.contains("No saved or available networks found."));
} }
@ -84,21 +84,21 @@ fn network_list_html() {
// TODO: needs further testing once template has been refactored // TODO: needs further testing once template has been refactored
#[test] #[test]
fn network_detail_html() { fn network_detail_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let response = client.get("/network/wifi?ssid=Home").dispatch(); let response = client.get("/network/wifi?ssid=Home").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
//let body = response.body_string().unwrap(); //let body = response.into_string().unwrap();
//assert!(body.contains("Network not found")); //assert!(body.contains("Network not found"));
} }
#[test] #[test]
fn network_add_html() { fn network_add_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/network/wifi/add").dispatch(); let response = client.get("/network/wifi/add").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Add WiFi Network")); assert!(body.contains("Add WiFi Network"));
assert!(body.contains("SSID")); assert!(body.contains("SSID"));
assert!(body.contains("Password")); assert!(body.contains("Password"));
@ -108,11 +108,11 @@ fn network_add_html() {
#[test] #[test]
fn network_add_ssid_html() { fn network_add_ssid_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/network/wifi/add?ssid=Home").dispatch(); let response = client.get("/network/wifi/add?ssid=Home").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Add WiFi Network")); assert!(body.contains("Add WiFi Network"));
assert!(body.contains("Home")); assert!(body.contains("Home"));
assert!(body.contains("Password")); assert!(body.contains("Password"));
@ -122,11 +122,11 @@ fn network_add_ssid_html() {
#[test] #[test]
fn device_html() { fn device_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/device").dispatch(); let response = client.get("/device").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Device Status")); assert!(body.contains("Device Status"));
assert!(body.contains("Networking")); assert!(body.contains("Networking"));
assert!(body.contains("Display")); assert!(body.contains("Display"));
@ -135,71 +135,71 @@ fn device_html() {
#[test] #[test]
fn help_html() { fn help_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/help").dispatch(); let response = client.get("/help").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Help")); assert!(body.contains("Help"));
} }
#[test] #[test]
fn login_html() { fn login_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/login").dispatch(); let response = client.get("/login").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Login")); assert!(body.contains("Login"));
} }
#[test] #[test]
fn messages_html() { fn messages_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/messages").dispatch(); let response = client.get("/messages").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Private Messages")); assert!(body.contains("Private Messages"));
} }
#[test] #[test]
fn peers_html() { fn peers_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/peers").dispatch(); let response = client.get("/peers").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Scuttlebutt Peers")); assert!(body.contains("Scuttlebutt Peers"));
} }
#[test] #[test]
fn profile_html() { fn profile_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/profile").dispatch(); let response = client.get("/profile").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Profile")); assert!(body.contains("Profile"));
} }
#[test] #[test]
fn shutdown_html() { fn shutdown_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/shutdown").dispatch(); let response = client.get("/shutdown").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Shutdown Device")); assert!(body.contains("Shutdown Device"));
} }
#[test] #[test]
fn network_usage_html() { fn network_usage_html() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client.get("/network/wifi/usage").dispatch(); let response = client.get("/network/wifi/usage").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::HTML)); assert_eq!(response.content_type(), Some(ContentType::HTML));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Network Data Usage")); assert!(body.contains("Network Data Usage"));
assert!(body.contains("WARNING THRESHOLD")); assert!(body.contains("WARNING THRESHOLD"));
assert!(body.contains("Update")); assert!(body.contains("Update"));
@ -208,7 +208,7 @@ fn network_usage_html() {
#[test] #[test]
fn add_credentials() { fn add_credentials() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let response = client let response = client
.post("/network/wifi/add") .post("/network/wifi/add")
.header(ContentType::Form) .header(ContentType::Form)
@ -220,7 +220,7 @@ fn add_credentials() {
#[test] #[test]
fn forget_wifi() { fn forget_wifi() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let response = client let response = client
.post("/network/wifi/forget") .post("/network/wifi/forget")
.header(ContentType::Form) .header(ContentType::Form)
@ -232,7 +232,7 @@ fn forget_wifi() {
#[test] #[test]
fn modify_password() { fn modify_password() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let response = client let response = client
.post("/network/wifi/modify") .post("/network/wifi/modify")
.header(ContentType::Form) .header(ContentType::Form)
@ -244,7 +244,7 @@ fn modify_password() {
#[test] #[test]
fn deploy_ap() { fn deploy_ap() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let response = client.get("/network/ap/activate").dispatch(); let response = client.get("/network/ap/activate").dispatch();
// check for 303 status (redirect) // check for 303 status (redirect)
assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.status(), Status::SeeOther);
@ -253,7 +253,7 @@ fn deploy_ap() {
#[test] #[test]
fn deploy_client() { fn deploy_client() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let response = client.get("/network/wifi/activate").dispatch(); let response = client.get("/network/wifi/activate").dispatch();
// check for 303 status (redirect) // check for 303 status (redirect)
assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.status(), Status::SeeOther);
@ -264,7 +264,7 @@ fn deploy_client() {
#[test] #[test]
fn activate_ap() { fn activate_ap() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let response = client let response = client
.post("/api/v1/network/activate_ap") .post("/api/v1/network/activate_ap")
.header(ContentType::JSON) .header(ContentType::JSON)
@ -275,7 +275,7 @@ fn activate_ap() {
#[test] #[test]
fn activate_client() { fn activate_client() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let response = client let response = client
.post("/api/v1/network/activate_client") .post("/api/v1/network/activate_client")
.header(ContentType::JSON) .header(ContentType::JSON)
@ -286,54 +286,54 @@ fn activate_client() {
#[test] #[test]
fn return_ip() { fn return_ip() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client let response = client
.get("/api/v1/network/ip") .get("/api/v1/network/ip")
.header(ContentType::JSON) .header(ContentType::JSON)
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::JSON)); assert_eq!(response.content_type(), Some(ContentType::JSON));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("wlan0")); assert!(body.contains("wlan0"));
assert!(body.contains("ap0")); assert!(body.contains("ap0"));
} }
#[test] #[test]
fn return_rssi() { fn return_rssi() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client let response = client
.get("/api/v1/network/rssi") .get("/api/v1/network/rssi")
.header(ContentType::JSON) .header(ContentType::JSON)
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::JSON)); assert_eq!(response.content_type(), Some(ContentType::JSON));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Not currently connected to an access point.")); assert!(body.contains("Not currently connected to an access point."));
} }
#[test] #[test]
fn return_ssid() { fn return_ssid() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client let response = client
.get("/api/v1/network/ssid") .get("/api/v1/network/ssid")
.header(ContentType::JSON) .header(ContentType::JSON)
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::JSON)); assert_eq!(response.content_type(), Some(ContentType::JSON));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Not currently connected to an access point.")); assert!(body.contains("Not currently connected to an access point."));
} }
#[test] #[test]
fn return_state() { fn return_state() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client let response = client
.get("/api/v1/network/state") .get("/api/v1/network/state")
.header(ContentType::JSON) .header(ContentType::JSON)
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::JSON)); assert_eq!(response.content_type(), Some(ContentType::JSON));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("ap0")); assert!(body.contains("ap0"));
assert!(body.contains("wlan0")); assert!(body.contains("wlan0"));
assert!(body.contains("unavailable")); assert!(body.contains("unavailable"));
@ -341,82 +341,82 @@ fn return_state() {
#[test] #[test]
fn return_status() { fn return_status() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client let response = client
.get("/api/v1/network/status") .get("/api/v1/network/status")
.header(ContentType::JSON) .header(ContentType::JSON)
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::JSON)); assert_eq!(response.content_type(), Some(ContentType::JSON));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Not currently connected to an access point.")); assert!(body.contains("Not currently connected to an access point."));
} }
#[test] #[test]
fn scan_networks() { fn scan_networks() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client let response = client
.get("/api/v1/network/wifi") .get("/api/v1/network/wifi")
.header(ContentType::JSON) .header(ContentType::JSON)
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::JSON)); assert_eq!(response.content_type(), Some(ContentType::JSON));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Unable to scan for networks. Interface may be deactivated.")); assert!(body.contains("Unable to scan for networks. Interface may be deactivated."));
} }
#[test] #[test]
fn add_wifi() { fn add_wifi() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client let response = client
.post("/api/v1/network/wifi") .post("/api/v1/network/wifi")
.header(ContentType::JSON) .header(ContentType::JSON)
.body(r#"{ "ssid": "Home", "pass": "Password" }"#) .body(r#"{ "ssid": "Home", "pass": "Password" }"#)
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::JSON)); assert_eq!(response.content_type(), Some(ContentType::JSON));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Failed to add WiFi credentials.")); assert!(body.contains("Failed to add WiFi credentials."));
} }
#[test] #[test]
fn remove_wifi() { fn remove_wifi() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client let response = client
.post("/api/v1/network/wifi/forget") .post("/api/v1/network/wifi/forget")
.header(ContentType::JSON) .header(ContentType::JSON)
.body(r#"{ "ssid": "Home" }"#) .body(r#"{ "ssid": "Home" }"#)
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::JSON)); assert_eq!(response.content_type(), Some(ContentType::JSON));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Failed to remove WiFi network credentials.")); assert!(body.contains("Failed to remove WiFi network credentials."));
} }
#[test] #[test]
fn new_password() { fn new_password() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client let response = client
.post("/api/v1/network/wifi/modify") .post("/api/v1/network/wifi/modify")
.header(ContentType::JSON) .header(ContentType::JSON)
.body(r#"{ "ssid": "Home", "pass": "Password" }"#) .body(r#"{ "ssid": "Home", "pass": "Password" }"#)
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::JSON)); assert_eq!(response.content_type(), Some(ContentType::JSON));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("Failed to update WiFi password.")); assert!(body.contains("Failed to update WiFi password."));
} }
#[test] #[test]
fn ping_pong() { fn ping_pong() {
let client = Client::new(rocket()).expect("valid rocket instance"); let client = Client::tracked(init_rocket()).expect("valid rocket instance");
let mut response = client let response = client
.get("/api/v1/ping") .get("/api/v1/ping")
.header(ContentType::JSON) .header(ContentType::JSON)
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::JSON)); assert_eq!(response.content_type(), Some(ContentType::JSON));
let body = response.body_string().unwrap(); let body = response.into_string().unwrap();
assert!(body.contains("pong!")); assert!(body.contains("pong!"));
} }
@ -426,12 +426,13 @@ fn ping_pong() {
fn test_build_json_response() { fn test_build_json_response() {
let status = "success".to_string(); let status = "success".to_string();
let data = json!("WiFi credentials added.".to_string()); let data = json!("WiFi credentials added.".to_string());
let json = build_json_response(status, Some(data), None); let j: Value = build_json_response(status, Some(data), None);
assert_eq!(json.status, "success"); assert_eq!(j["status"], "success");
assert_eq!(json.data, Some(json!("WiFi credentials added."))); assert_eq!(j["data"], "WiFi credentials added.");
assert_eq!(json.msg, None); assert_eq!(j["msg"], json!(null));
} }
// FILE TESTS // FILE TESTS
#[test] #[test]
@ -471,16 +472,16 @@ fn invalid_path() {
#[test] #[test]
fn invalid_get_request() { fn invalid_get_request() {
let client = Client::new(rocket()).unwrap(); let client = Client::tracked(init_rocket()).unwrap();
// try to get a path that doesn't exist // try to get a path that doesn't exist
let mut res = client let res = client
.get("/message/99") .get("/message/99")
.header(ContentType::JSON) .header(ContentType::JSON)
.dispatch(); .dispatch();
assert_eq!(res.status(), Status::NotFound); assert_eq!(res.status(), Status::NotFound);
let body = res.body_string().unwrap(); let body = res.into_string().unwrap();
assert!(body.contains("404: Page Not Found")); assert!(body.contains("404: Page Not Found"));
assert!(body.contains("No PeachCloud resource exists for this URL.")); assert!(body.contains("No PeachCloud resource exists for this URL."));
} }

View File

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

View File

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

View File

@ -1,65 +0,0 @@
// NOTE: websockets are not currently in use for PeachCloud but may be in the
// future.
use std::io;
use std::thread;
use log::{debug, info};
use websocket::sync::Server;
use websocket::{Message, OwnedMessage};
pub fn websocket_server(address: String) -> io::Result<()> {
// 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
thread::spawn(move || {
if !connection
.protocols()
.contains(&"rust-websocket".to_string())
{
connection.reject().unwrap();
return;
}
let mut client = connection.use_protocol("rust-websocket").accept().unwrap();
let client_ip = client.peer_addr().unwrap();
debug!("Websocket connection from {}.", client_ip);
let msg_text = "Websocket successfully connected.".to_string();
let message = Message::text(msg_text);
client.send_message(&message).unwrap();
let (mut receiver, mut sender) = client.split().unwrap();
for message in receiver.incoming_messages() {
let message = message.unwrap();
match message {
OwnedMessage::Close(_) => {
debug!("Received close message.");
let message = Message::close();
sender.send_message(&message).unwrap();
debug!("Websocket client {} disconnected.", client_ip);
return;
}
OwnedMessage::Ping(data) => {
debug!("Received ping message.");
let message = Message::pong(data);
sender.send_message(&message).unwrap();
}
_ => {
sender.send_message(&message).unwrap();
debug!("Received unknown message: {:?}", message);
}
}
}
});
}
Ok(())
}