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"
peach-lib = { path = "../peach-lib" }
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_json = "1.0"
snafu = "0.6"
@ -50,7 +50,6 @@ regex = "1"
xdg = "2.2.0"
openssl = { version = "0.10", features = ["vendored"] }
[dependencies.rocket_contrib]
version = "0.4.10"
default-features = false
features = ["json", "tera_templates"]
[dependencies.rocket_dyn_templates]
version = "0.1.0-rc.1"
features = ["tera"]

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 log::error;
use rocket::{catchers, routes, Rocket, Build, fs::FileServer};
use rocket_dyn_templates::Template;
fn main() {
// initialize the logger
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::*;
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();
// handle errors returned from `run`
if let Err(e) = peach_web::run() {
error!("Application error: {}", e);
// initialize rocket
info!("Initializing Rocket");
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);
}
}

View File

@ -1,14 +1,17 @@
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::{get, post};
use rocket_contrib::{json::Json, templates::Template};
use serde::{Deserialize, Serialize};
use rocket::serde::json::Json;
use rocket_dyn_templates::Template;
use rocket::serde::{Deserialize, Serialize};
use peach_lib::password_utils;
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
@ -39,8 +42,8 @@ pub fn login(flash: Option<FlashMessage>) -> Template {
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
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>")]
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(_) => {
let status = "success".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) => {
let status = "error".to_string();
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
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.
#[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(_) => {
let status = "success".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) => {
let status = "error".to_string();
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,
response::{Flash, Redirect},
};
use rocket_contrib::{json::Json, templates::Template};
use rocket_dyn_templates::Template;
use serde::Serialize;
use std::{
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::{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
@ -156,8 +158,8 @@ pub fn device_stats(flash: Option<FlashMessage>) -> Template {
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
Template::render("device", &context)
@ -188,19 +190,19 @@ pub fn reboot_cmd() -> Flash<Redirect> {
/// 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...");
let status = "success".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(_) => {
warn!("Reboot failed");
let status = "error".to_string();
let msg = "Failed to reboot the device.".to_string();
Json(build_json_response(status, None, Some(msg)))
build_json_response(status, None, Some(msg))
}
}
}
@ -226,19 +228,19 @@ 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...");
let status = "success".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(_) => {
warn!("Shutdown failed");
let status = "error".to_string();
let msg = "Failed to shutdown the device.".to_string();
Json(build_json_response(status, None, Some(msg)))
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
Template::render("shutdown", &context)
}

View File

@ -1,13 +1,7 @@
use log::debug;
use rocket::{catch, get, response::NamedFile};
use rocket_contrib::templates::Template;
use rocket::{catch};
use rocket_dyn_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 ERROR

View File

@ -1,5 +1,5 @@
use rocket::{get, request::FlashMessage};
use rocket_contrib::templates::Template;
use rocket_dyn_templates::Template;
use serde::Serialize;
// 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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
Template::render("help", &context)
}

View File

@ -1,86 +1,76 @@
//! Helper routes for pinging services to check that they are active
use log::{debug, warn};
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::oled_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.
#[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.
#[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)))
build_json_response(status, None, Some(msg))
}
/// 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");
let status = "success".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(_) => {
warn!("peach-network failed to respond");
let status = "error".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.
#[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");
let status = "success".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(_) => {
warn!("peach-oled failed to respond");
let status = "error".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.
#[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");
let status = "success".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(_) => {
warn!("peach-stats failed to respond");
let status = "error".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.
use rocket::{get, request::FlashMessage};
use rocket_contrib::templates::Template;
use rocket_dyn_templates::Template;
use serde::Serialize;
// 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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
Template::render("profile", &context)
}

View File

@ -1,11 +1,12 @@
use rocket::{
get, post,
request::{FlashMessage, Form, FromForm},
request::FlashMessage,
form::{Form, FromForm},
response::{Flash, Redirect},
uri,
};
use rocket_contrib::templates::Template;
use serde::{Deserialize, Serialize};
use rocket_dyn_templates::Template;
use rocket::serde::{Deserialize, Serialize};
use peach_lib::config_manager;
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
Template::render("admin/add_admin", &context)

View File

@ -1,10 +1,12 @@
use log::info;
use rocket::{
get, post,
request::{FlashMessage, Form, FromForm},
request::FlashMessage,
form::{Form, FromForm}
};
use rocket_contrib::{json::Json, templates::Template};
use serde::{Deserialize, Serialize};
use rocket::serde::json::Json;
use rocket_dyn_templates::Template;
use rocket::serde::{Deserialize, Serialize};
use peach_lib::config_manager;
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 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)]
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
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>")]
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(_) => {
let status = "success".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) => {
let status = "error".to_string();
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 percent_encoding::percent_decode;
use rocket::{
get,
http::RawStr,
post,
request::{FlashMessage, Form, FromForm},
request::FlashMessage,
form::{Form, FromForm},
response::{Flash, Redirect},
uri, UriDisplayQuery,
};
use rocket_contrib::{json, json::Json, templates::Template};
use serde::{Deserialize, Serialize};
use rocket::serde::json::{json, Json};
use rocket_dyn_templates::Template;
use rocket::serde::{Deserialize, Serialize};
use std::collections::HashMap;
use peach_lib::network_client;
@ -18,7 +19,8 @@ use peach_lib::stats_client::Traffic;
use crate::utils::monitor;
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
@ -50,7 +52,7 @@ pub fn wifi_usage_reset() -> Flash<Redirect> {
#[post("/network/wifi/connect", data = "<network>")]
pub fn connect_wifi(network: Form<Ssid>) -> Flash<Redirect> {
let ssid = &network.ssid;
let url = uri!(network_detail: ssid);
let url = uri!(network_detail(ssid = ssid));
match network_client::id("wlan0", ssid) {
Ok(id) => match network_client::connect(&id, "wlan0") {
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>")]
pub fn wifi_password(ssid: &RawStr, flash: Option<FlashMessage>) -> Template {
// decode ssid from url
let decoded_ssid = percent_decode(ssid.as_bytes()).decode_utf8().unwrap();
pub fn wifi_password(ssid: &str, flash: Option<FlashMessage>) -> Template {
let mut context = NetworkAddContext {
back: Some("/network/wifi".to_string()),
flash_name: None,
flash_msg: None,
selected: Some(decoded_ssid.to_string()),
selected: Some(ssid.to_string()),
title: Some("Update WiFi Password".to_string()),
};
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
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> {
let ssid = &wifi.ssid;
let pass = &wifi.pass;
let url = uri!(network_detail: ssid);
let url = uri!(network_detail(ssid = ssid));
match network_client::update("wlan0", ssid, pass) {
Ok(_) => Flash::success(Redirect::to(url), "WiFi password updated".to_string()),
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
Template::render("network_list", &context)
@ -540,19 +540,17 @@ impl NetworkDetailContext {
}
#[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
let mut context = NetworkDetailContext::build();
context.back = Some("/network/wifi".to_string());
context.title = Some("WiFi Network".to_string());
// decode ssid from url
let decoded_ssid = percent_decode(ssid.as_bytes()).decode_utf8().unwrap();
context.selected = Some(decoded_ssid.to_string());
context.selected = Some(ssid.to_string());
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
Template::render("network_add", &context)
@ -611,18 +609,16 @@ impl NetworkAddContext {
}
#[get("/network/wifi/add?<ssid>")]
pub fn network_add_ssid(ssid: &RawStr, flash: Option<FlashMessage>) -> Template {
// decode ssid from url
let decoded_ssid = percent_decode(ssid.as_bytes()).decode_utf8().unwrap();
pub fn network_add_ssid(ssid: &str, flash: Option<FlashMessage>) -> Template {
let mut context = NetworkAddContext::build();
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());
// check to see if there is a flash message to display
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
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
if let Some(flash) = flash {
// add flash message contents to the context object
context.flash_name = Some(flash.name().to_string());
context.flash_msg = Some(flash.msg().to_string());
context.flash_name = Some(flash.kind().to_string());
context.flash_msg = Some(flash.message().to_string());
};
// template_dir is set in Rocket.toml
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>")]
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.");
let status = "success".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(_) => {
warn!("Failed to update WiFi data usage thresholds.");
let status = "error".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")]
pub fn reset_data_total() -> Json<JsonResponse> {
pub fn reset_data_total() -> Value {
match monitor::reset_data() {
Ok(_) => {
debug!("Reset network data usage total.");
@ -795,13 +791,13 @@ pub fn reset_data_total() -> Json<JsonResponse> {
let data = json!(current_traffic);
let status = "success".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(_) => {
warn!("Failed to reset network data usage total.");
let status = "error".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
#[post("/api/v1/network/activate_ap")]
pub fn activate_ap() -> Json<JsonResponse> {
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))
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)))
build_json_response(status, None, Some(msg))
}
}
}
@ -828,24 +824,24 @@ pub fn activate_ap() -> Json<JsonResponse> {
// HELPERS AND ROUTES FOR WIFI CLIENT MANAGEMENT
#[post("/api/v1/network/activate_client")]
pub fn activate_client() -> Json<JsonResponse> {
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))
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)))
build_json_response(status, None, Some(msg))
}
}
}
#[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
match network_client::add(&wifi.ssid, &wifi.pass) {
Ok(_) => {
@ -858,20 +854,20 @@ pub fn add_wifi(wifi: Json<WiFi>) -> Json<JsonResponse> {
// 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)))
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)))
build_json_response(status, None, Some(msg))
}
}
}
#[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
match network_client::id("wlan0", &ssid.ssid) {
// attempt connection with the given network
@ -879,60 +875,60 @@ pub fn connect_ap(ssid: Json<Ssid>) -> Json<JsonResponse> {
Ok(_) => {
let status = "success".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(_) => {
let status = "error".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(_) => {
let status = "error".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>")]
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
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)))
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)))
build_json_response(status, None, Some(msg))
}
}
}
#[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;
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)))
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)))
build_json_response(status, None, Some(msg))
}
}
}
#[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 pass = &wifi.pass;
// 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.");
let status = "success".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(_) => {
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)))
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
#[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
let wlan_ip = match network_client::ip("wlan0") {
Ok(ip) => ip,
@ -973,45 +969,45 @@ pub fn return_ip() -> Json<JsonResponse> {
"ap0": ap_ip
});
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")]
pub fn return_rssi() -> Json<JsonResponse> {
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))
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)))
build_json_response(status, None, Some(msg))
}
}
}
#[get("/api/v1/network/ssid")]
pub fn return_ssid() -> Json<JsonResponse> {
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))
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)))
build_json_response(status, None, Some(msg))
}
}
}
#[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
let wlan_state = match network_client::state("wlan0") {
Ok(state) => state,
@ -1027,39 +1023,39 @@ pub fn return_state() -> Json<JsonResponse> {
"ap0": ap_state
});
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")]
pub fn return_status() -> Json<JsonResponse> {
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))
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)))
build_json_response(status, None, Some(msg))
}
}
}
#[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
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))
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)))
build_json_response(status, None, Some(msg))
}
}
}

View File

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

View File

@ -1,25 +1,16 @@
pub mod monitor;
use rocket_contrib::json::JsonValue;
use serde::Serialize;
use rocket::serde::json::{Value, json};
use rocket::serde::{Serialize};
// 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(
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)]

View File

@ -3,8 +3,8 @@
use std::convert::TryInto;
use nest::{Error, Store, Value};
use rocket::request::FromForm;
use serde::{Deserialize, Serialize};
use rocket::form::{FromForm};
use rocket::serde::{Deserialize, Serialize};
use serde_json::json;
/// 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(())
}