diff --git a/peach-web/Rocket.toml b/peach-web/Rocket.toml deleted file mode 100644 index c1f285e..0000000 --- a/peach-web/Rocket.toml +++ /dev/null @@ -1,11 +0,0 @@ -[default] -secret_key = "VYVUDivXvu8g6llxeJd9F92pMfocml5xl/Jjv5Sk4yw=" -disable_auth = false -standalone_mode = true - -[debug] -template_dir = "templates/" -disable_auth = true - -[release] -template_dir = "templates/" diff --git a/peach-web/config b/peach-web/config deleted file mode 100644 index aa7ad74..0000000 --- a/peach-web/config +++ /dev/null @@ -1,2 +0,0 @@ -disable_auth=false -standalone_mode=true diff --git a/peach-web/rouille_refactor b/peach-web/rouille_refactor deleted file mode 100644 index f8ad8d7..0000000 --- a/peach-web/rouille_refactor +++ /dev/null @@ -1,76 +0,0 @@ - - -go slow and steady. - -optimise for few dependencies and short compilation times. - -we do not need to be super fast or feature-rich. - -[ architecture ] - - - use the one-file-per-route patten - -[ rouille-specific ] - - - logging - - https://docs.rs/rouille/latest/rouille/fn.log_custom.html - x flash message - - https://docs.rs/rouille/latest/rouille/input/fn.cookies.html - - https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#creating_cookies - - https://docs.rs/rouille/latest/rouille/struct.Response.html#method.with_additional_header - - https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_the_lifetime_of_a_cookie - - file upload - - https://docs.rs/rouille/latest/rouille/input/post/index.html#handling-file-uploads - - auth - - https://github.com/tomaka/rouille/blob/master/examples/login-session.rs - - https://docs.rs/rouille/latest/rouille/struct.Response.html#method.basic_http_auth_login_required - - -[ tasks ] - - - write the settings route(s) - - scuttlebutt - - peers - x menu - - peers list - - invites - - profile - - private - x menu - x guide - x status - x scuttlebutt - x scuttlebutt menu - - configure_sbot - x template - x sbot_config data - - might need some thought...render elements or input data - - admin - x menu - x configure - x add - x delete - - auth - x change password - x form - x post - x reset password - x form - x post - x login - x form - x post - x logout - x get - -[ flash messages ] - - - for now, use simple redirects in the handlers - - then add flash messages later - - - write getter, setter and unsetter for flash messages - - from rocket docs - - A “removal” cookie is a cookie that has the same name as the original cookie but has an empty value, a max-age of 0, and an expiration date far in the past. - - use Response::with_additional_header() method to set cookie - - https://docs.rs/rouille/latest/rouille/struct.Response.html#method.with_additional_header - - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie diff --git a/peach-web/src/context/dns.rs b/peach-web/src/context/dns.rs deleted file mode 100644 index 7a86646..0000000 --- a/peach-web/src/context/dns.rs +++ /dev/null @@ -1,38 +0,0 @@ -use peach_lib::{config_manager, dyndns_client}; -use rocket::serde::Serialize; - -#[derive(Debug, Serialize)] -pub struct ConfigureDNSContext { - pub external_domain: String, - pub dyndns_subdomain: String, - pub enable_dyndns: bool, - pub is_dyndns_online: bool, - pub back: Option, - pub title: Option, - pub flash_name: Option, - pub flash_msg: Option, - pub theme: Option, -} - -impl ConfigureDNSContext { - pub fn build() -> ConfigureDNSContext { - // TODO: replace `unwrap` with resilient error handling - let peach_config = config_manager::load_peach_config().unwrap(); - let dyndns_fulldomain = peach_config.dyn_domain; - let is_dyndns_online = dyndns_client::is_dns_updater_online().unwrap(); - let dyndns_subdomain = - dyndns_client::get_dyndns_subdomain(&dyndns_fulldomain).unwrap_or(dyndns_fulldomain); - - ConfigureDNSContext { - external_domain: peach_config.external_domain, - dyndns_subdomain, - enable_dyndns: peach_config.dyn_enabled, - is_dyndns_online, - back: None, - title: None, - flash_name: None, - flash_msg: None, - theme: None, - } - } -} diff --git a/peach-web/src/context/mod.rs b/peach-web/src/context/mod.rs deleted file mode 100644 index 65581c1..0000000 --- a/peach-web/src/context/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -//pub mod dns; -//pub mod network; -//pub mod scuttlebutt; diff --git a/peach-web/src/context/network.rs b/peach-web/src/context/network.rs deleted file mode 100644 index 9e3b1b4..0000000 --- a/peach-web/src/context/network.rs +++ /dev/null @@ -1,394 +0,0 @@ -//! Data retrieval for the purpose of serving routes and hydrating -//! network-related HTML templates. - -use std::collections::HashMap; - -use rocket::{form::FromForm, serde::Serialize, UriDisplayQuery}; - -use peach_network::{ - network, - network::{Scan, Status, Traffic}, -}; - -use crate::{ - utils::{ - monitor, - monitor::{Alert, Data, Threshold}, - }, - AP_IFACE, WLAN_IFACE, -}; - -#[derive(Debug, Serialize)] -pub struct AccessPoint { - pub detail: Option, - pub signal: Option, - pub state: String, -} - -pub fn ap_state() -> String { - match network::state(&*AP_IFACE) { - Ok(Some(state)) => state, - _ => "Interface unavailable".to_string(), - } -} - -#[derive(Debug, FromForm, UriDisplayQuery)] -pub struct Ssid { - pub ssid: String, -} - -#[derive(Debug, FromForm)] -pub struct WiFi { - pub ssid: String, - pub pass: String, -} - -fn convert_traffic(traffic: Traffic) -> Option { - // modify traffic values & assign measurement unit - // based on received and transmitted values - let (rx, rx_unit) = if traffic.received > 1_047_527_424 { - // convert to GB - (traffic.received / 1_073_741_824, "GB".to_string()) - } else if traffic.received > 0 { - // otherwise, convert it to MB - ((traffic.received / 1024) / 1024, "MB".to_string()) - } else { - (0, "MB".to_string()) - }; - - let (tx, tx_unit) = if traffic.transmitted > 1_047_527_424 { - // convert to GB - (traffic.transmitted / 1_073_741_824, "GB".to_string()) - } else if traffic.transmitted > 0 { - ((traffic.transmitted / 1024) / 1024, "MB".to_string()) - } else { - (0, "MB".to_string()) - }; - - Some(IfaceTraffic { - rx, - rx_unit, - tx, - tx_unit, - }) -} - -#[derive(Debug, Serialize)] -pub struct IfaceTraffic { - pub rx: u64, - pub rx_unit: String, - pub tx: u64, - pub tx_unit: String, -} - -#[derive(Debug, Serialize)] -pub struct NetworkAlertContext { - pub alert: Alert, - pub back: Option, - pub data_total: Option, // combined stored and current wifi traffic in bytes - pub flash_name: Option, - pub flash_msg: Option, - pub threshold: Threshold, - pub title: Option, - pub traffic: Option, // current wifi traffic in bytes (since boot) -} - -impl NetworkAlertContext { - pub fn build() -> NetworkAlertContext { - let alert = monitor::get_alerts().unwrap(); - // stored wifi data values as bytes - let stored_traffic = monitor::get_data().unwrap(); - let threshold = monitor::get_thresholds().unwrap(); - - let (traffic, data_total) = match network::traffic(&*WLAN_IFACE) { - // convert bytes to mb or gb and add appropriate units - Ok(Some(t)) => { - let current_traffic = t.received + t.transmitted; - let traffic = convert_traffic(t); - let total = stored_traffic.total + current_traffic; - let data_total = Data { total }; - (traffic, Some(data_total)) - } - _ => (None, None), - }; - - NetworkAlertContext { - alert, - back: None, - data_total, - flash_name: None, - flash_msg: None, - threshold, - title: None, - traffic, - } - } -} - -#[derive(Debug, Serialize)] -pub struct NetworkDetailContext { - pub back: Option, - pub flash_name: Option, - pub flash_msg: Option, - pub selected: Option, - pub title: Option, - pub saved_aps: Vec, - pub wlan_ip: String, - pub wlan_networks: HashMap, - pub wlan_rssi: Option, - pub wlan_ssid: String, - pub wlan_state: String, - pub wlan_status: Option, - pub wlan_traffic: Option, -} - -impl NetworkDetailContext { - pub fn build() -> NetworkDetailContext { - let wlan_ip = match network::ip(&*WLAN_IFACE) { - Ok(Some(ip)) => ip, - _ => "x.x.x.x".to_string(), - }; - - // list of networks saved in wpa_supplicant.conf - let wlan_list = match network::saved_networks() { - Ok(Some(ssids)) => ssids, - _ => Vec::new(), - }; - - // list of networks saved in wpa_supplicant.conf - let saved_aps = wlan_list.clone(); - - let wlan_rssi = match network::rssi_percent(&*WLAN_IFACE) { - Ok(rssi) => rssi, - Err(_) => None, - }; - - // list of networks currently in range (online & accessible) - let wlan_scan = match network::available_networks(&*WLAN_IFACE) { - Ok(Some(networks)) => networks, - _ => Vec::new(), - }; - - let wlan_ssid = match network::ssid(&*WLAN_IFACE) { - Ok(Some(ssid)) => ssid, - _ => "Not connected".to_string(), - }; - - let wlan_state = match network::state(&*WLAN_IFACE) { - Ok(Some(state)) => state, - _ => "Interface unavailable".to_string(), - }; - - let wlan_status = match network::status(&*WLAN_IFACE) { - Ok(status) => status, - // interface unavailable - _ => None, - }; - - let wlan_traffic = match network::traffic(&*WLAN_IFACE) { - // convert bytes to mb or gb and add appropriate units - Ok(Some(traffic)) => convert_traffic(traffic), - _ => None, - }; - - // create a hashmap to combine wlan_list & wlan_scan without repetition - let mut wlan_networks = HashMap::new(); - - for ap in wlan_scan { - let ssid = ap.ssid.clone(); - let rssi = ap.signal_level.clone(); - // parse the string to a signed integer (for math) - let rssi_parsed = rssi.parse::().unwrap(); - // perform rssi (dBm) to quality (%) conversion - let quality_percent = 2 * (rssi_parsed + 100); - let ap_detail = AccessPoint { - detail: Some(ap), - state: "Available".to_string(), - signal: Some(quality_percent), - }; - wlan_networks.insert(ssid, ap_detail); - } - - for network in wlan_list { - // avoid repetition by checking that ssid is not already in list - if !wlan_networks.contains_key(&network) { - let ssid = network.clone(); - let net_detail = AccessPoint { - detail: None, - state: "Not in range".to_string(), - signal: None, - }; - wlan_networks.insert(ssid, net_detail); - } - } - - NetworkDetailContext { - back: None, - flash_name: None, - flash_msg: None, - selected: None, - title: None, - saved_aps, - wlan_ip, - wlan_networks, - wlan_rssi, - wlan_ssid, - wlan_state, - wlan_status, - wlan_traffic, - } - } -} - -#[derive(Debug, Serialize)] -pub struct NetworkListContext { - pub ap_state: String, - pub back: Option, - pub flash_name: Option, - pub flash_msg: Option, - pub title: Option, - pub wlan_networks: HashMap, - pub wlan_ssid: String, -} - -impl NetworkListContext { - pub fn build() -> NetworkListContext { - // list of networks saved in wpa_supplicant.conf - let wlan_list = match network::saved_networks() { - Ok(Some(ssids)) => ssids, - _ => Vec::new(), - }; - - // list of networks currently in range (online & accessible) - let wlan_scan = match network::available_networks(&*WLAN_IFACE) { - Ok(Some(networks)) => networks, - _ => Vec::new(), - }; - - let wlan_ssid = match network::ssid(&*WLAN_IFACE) { - Ok(Some(ssid)) => ssid, - _ => "Not connected".to_string(), - }; - - // create a hashmap to combine wlan_list & wlan_scan without repetition - let mut wlan_networks = HashMap::new(); - for ap in wlan_scan { - wlan_networks.insert(ap.ssid, "Available".to_string()); - } - for network in wlan_list { - // insert ssid (with state) only if it doesn't already exist - wlan_networks - .entry(network) - .or_insert_with(|| "Not in range".to_string()); - } - - let ap_state = match network::state(&*AP_IFACE) { - Ok(Some(state)) => state, - _ => "Interface unavailable".to_string(), - }; - - NetworkListContext { - ap_state, - back: None, - flash_msg: None, - flash_name: None, - title: None, - wlan_networks, - wlan_ssid, - } - } -} - -#[derive(Debug, Serialize)] -pub struct NetworkStatusContext { - pub ap_ip: String, - pub ap_ssid: String, - pub ap_state: String, - pub ap_traffic: Option, - pub wlan_ip: String, - pub wlan_rssi: Option, - pub wlan_ssid: String, - pub wlan_state: String, - pub wlan_status: Option, - pub wlan_traffic: Option, - pub flash_name: Option, - pub flash_msg: Option, - // passing in the ssid of a chosen access point - pub selected: Option, - pub title: Option, - pub back: Option, -} - -impl NetworkStatusContext { - pub fn build() -> Self { - let ap_ip = match network::ip(&*AP_IFACE) { - Ok(Some(ip)) => ip, - _ => "x.x.x.x".to_string(), - }; - - let ap_ssid = match network::ssid(&*AP_IFACE) { - Ok(Some(ssid)) => ssid, - _ => "Not currently activated".to_string(), - }; - - let ap_state = match network::state(&*AP_IFACE) { - Ok(Some(state)) => state, - _ => "Interface unavailable".to_string(), - }; - - let ap_traffic = match network::traffic(&*AP_IFACE) { - // convert bytes to mb or gb and add appropriate units - Ok(Some(traffic)) => convert_traffic(traffic), - _ => None, - }; - - let wlan_ip = match network::ip(&*WLAN_IFACE) { - Ok(Some(ip)) => ip, - _ => "x.x.x.x".to_string(), - }; - - let wlan_rssi = match network::rssi_percent(&*WLAN_IFACE) { - Ok(rssi) => rssi, - _ => None, - }; - - let wlan_ssid = match network::ssid(&*WLAN_IFACE) { - Ok(Some(ssid)) => ssid, - _ => "Not connected".to_string(), - }; - - let wlan_state = match network::state(&*WLAN_IFACE) { - Ok(Some(state)) => state, - _ => "Interface unavailable".to_string(), - }; - - let wlan_status = match network::status(&*WLAN_IFACE) { - Ok(status) => status, - _ => None, - }; - - let wlan_traffic = match network::traffic(&*WLAN_IFACE) { - // convert bytes to mb or gb and add appropriate units - Ok(Some(traffic)) => convert_traffic(traffic), - _ => None, - }; - - NetworkStatusContext { - ap_ip, - ap_ssid, - ap_state, - ap_traffic, - wlan_ip, - wlan_rssi, - wlan_ssid, - wlan_state, - wlan_status, - wlan_traffic, - flash_name: None, - flash_msg: None, - selected: None, - title: None, - back: None, - } - } -} diff --git a/peach-web/src/context/scuttlebutt.rs b/peach-web/src/context/scuttlebutt.rs deleted file mode 100644 index 9f5ac77..0000000 --- a/peach-web/src/context/scuttlebutt.rs +++ /dev/null @@ -1,570 +0,0 @@ -use std::collections::HashMap; - -use golgi::{api::friends::RelationshipQuery, blobs, messages::SsbMessageValue, Sbot}; -use peach_lib::sbot::{SbotConfig, SbotStatus}; -use rocket::{futures::TryStreamExt, serde::Serialize}; - -use crate::{error::PeachWebError, utils}; - -// HELPER FUNCTIONS - -pub async fn init_sbot_with_config( - sbot_config: &Option, -) -> Result { - // initialise sbot connection with ip:port and shscap from config file - let sbot_client = match sbot_config { - // TODO: panics if we pass `Some(conf.shscap)` as second arg - Some(conf) => { - let ip_port = conf.lis.clone(); - Sbot::init(Some(ip_port), None).await? - } - None => Sbot::init(None, None).await?, - }; - - Ok(sbot_client) -} - -// CONTEXT STRUCTS AND BUILDERS - -#[derive(Debug, Serialize)] -pub struct StatusContext { - pub back: Option, - pub flash_name: Option, - pub flash_msg: Option, - pub title: Option, - pub theme: Option, - pub sbot_config: Option, - pub sbot_status: Option, - // latest sequence number for the local log - pub latest_seq: Option, -} - -impl StatusContext { - pub fn default() -> Self { - StatusContext { - back: Some("/".to_string()), - flash_name: None, - flash_msg: None, - title: Some("Scuttlebutt Status".to_string()), - theme: None, - sbot_config: None, - sbot_status: None, - latest_seq: None, - } - } - - pub async fn build() -> Result { - let mut context = Self::default(); - - // retrieve current ui theme - context.theme = Some(utils::get_theme()); - - // retrieve go-sbot systemd process status - let sbot_status = SbotStatus::read()?; - - // retrieve latest go-sbot configuration parameters - let sbot_config = SbotConfig::read().ok(); - - // we only want to try and interact with the sbot if it's active - if sbot_status.state == Some("active".to_string()) { - let mut sbot_client = init_sbot_with_config(&sbot_config).await?; - - // retrieve the local id - let id = sbot_client.whoami().await?; - - let history_stream = sbot_client.create_history_stream(id).await?; - let mut msgs: Vec = history_stream.try_collect().await?; - - // reverse the list of messages so we can easily reference the latest one - msgs.reverse(); - - // assign the sequence number of the latest msg - context.latest_seq = Some(msgs[0].sequence); - } else { - // the sbot is not currently active; return a helpful message - context.flash_name = Some("warning".to_string()); - context.flash_msg = Some("The Sbot is currently inactive. As a result, status data cannot be retrieved. Visit the Scuttlebutt settings menu to start the Sbot and then try again".to_string()); - } - - context.sbot_config = sbot_config; - context.sbot_status = Some(sbot_status); - - Ok(context) - } -} - -// peers who are blocked by the local account -#[derive(Debug, Serialize)] -pub struct BlocksContext { - pub back: Option, - pub flash_name: Option, - pub flash_msg: Option, - pub title: Option, - pub theme: Option, - pub sbot_config: Option, - pub sbot_status: Option, - pub peers: Option>>, -} - -impl BlocksContext { - pub fn default() -> Self { - BlocksContext { - back: Some("/scuttlebutt/peers".to_string()), - flash_name: None, - flash_msg: None, - title: Some("Blocks".to_string()), - theme: None, - sbot_config: None, - sbot_status: None, - peers: None, - } - } - - pub async fn build() -> Result { - let mut context = Self::default(); - - // retrieve current ui theme - context.theme = Some(utils::get_theme()); - - // retrieve go-sbot systemd process status - let sbot_status = SbotStatus::read()?; - - // we only want to try and interact with the sbot if it's active - if sbot_status.state == Some("active".to_string()) { - // retrieve latest go-sbot configuration parameters - let sbot_config = SbotConfig::read().ok(); - - let mut sbot_client = init_sbot_with_config(&sbot_config).await?; - - let blocks = sbot_client.get_blocks().await?; - - // we'll use this to store the profile info for each peer who follows us - let mut peer_info = Vec::new(); - - if !blocks.is_empty() { - for peer in blocks.iter() { - // trim whitespace (including newline characters) and - // remove the inverted-commas around the id - let key = peer.trim().replace('"', ""); - // retrieve the profile info for the given peer - let mut info = sbot_client.get_profile_info(&key).await?; - // insert the public key of the peer into the info hashmap - info.insert("id".to_string(), key.to_string()); - // we do not even attempt to find the blob for a blocked peer, - // since it may be vulgar to cause distress to the local peer. - info.insert("blob_exists".to_string(), "false".to_string()); - // push profile info to peer_list vec - peer_info.push(info) - } - - context.peers = Some(peer_info) - } - } else { - // the sbot is not currently active; return a helpful message - context.flash_name = Some("warning".to_string()); - context.flash_msg = Some("The Sbot is currently inactive. As a result, peer data cannot be retrieved. Visit the Scuttlebutt settings menu to start the Sbot and then try again".to_string()); - } - - context.sbot_status = Some(sbot_status); - - Ok(context) - } -} - -// peers who are followed by the local account -#[derive(Debug, Serialize)] -pub struct FollowsContext { - pub back: Option, - pub flash_name: Option, - pub flash_msg: Option, - pub title: Option, - pub theme: Option, - pub sbot_config: Option, - pub sbot_status: Option, - pub peers: Option>>, -} - -impl FollowsContext { - pub fn default() -> Self { - FollowsContext { - back: Some("/scuttlebutt/peers".to_string()), - flash_name: None, - flash_msg: None, - title: Some("Follows".to_string()), - theme: None, - sbot_config: None, - sbot_status: None, - peers: None, - } - } - - pub async fn build() -> Result { - let mut context = Self::default(); - - // retrieve current ui theme - context.theme = Some(utils::get_theme()); - - // retrieve go-sbot systemd process status - let sbot_status = SbotStatus::read()?; - - // we only want to try and interact with the sbot if it's active - if sbot_status.state == Some("active".to_string()) { - // retrieve latest go-sbot configuration parameters - let sbot_config = SbotConfig::read().ok(); - - let mut sbot_client = init_sbot_with_config(&sbot_config).await?; - - let follows = sbot_client.get_follows().await?; - - // we'll use this to store the profile info for each peer who follows us - let mut peer_info = Vec::new(); - - if !follows.is_empty() { - for peer in follows.iter() { - // trim whitespace (including newline characters) and - // remove the inverted-commas around the id - let key = peer.trim().replace('"', ""); - // retrieve the profile info for the given peer - let mut info = sbot_client.get_profile_info(&key).await?; - // insert the public key of the peer into the info hashmap - info.insert("id".to_string(), key.to_string()); - // retrieve the profile image blob id for the given peer - if let Some(blob_id) = info.get("image") { - // look-up the path for the image blob - if let Ok(blob_path) = blobs::get_blob_path(&blob_id) { - // insert the image blob path of the peer into the info hashmap - info.insert("blob_path".to_string(), blob_path.to_string()); - // check if the blob is in the blobstore - // set a flag in the info hashmap - match utils::blob_is_stored_locally(&blob_path).await { - Ok(exists) if exists == true => { - info.insert("blob_exists".to_string(), "true".to_string()) - } - _ => info.insert("blob_exists".to_string(), "false".to_string()), - }; - } - } - // push profile info to peer_list vec - peer_info.push(info) - } - - context.peers = Some(peer_info) - } - } else { - // the sbot is not currently active; return a helpful message - context.flash_name = Some("warning".to_string()); - context.flash_msg = Some("The Sbot is currently inactive. As a result, peer data cannot be retrieved. Visit the Scuttlebutt settings menu to start the Sbot and then try again".to_string()); - } - - context.sbot_status = Some(sbot_status); - - Ok(context) - } -} - -// peers who follow and are followed by the local account (friends) -#[derive(Debug, Serialize)] -pub struct FriendsContext { - pub back: Option, - pub flash_name: Option, - pub flash_msg: Option, - pub title: Option, - pub theme: Option, - pub sbot_config: Option, - pub sbot_status: Option, - pub peers: Option>>, -} - -impl FriendsContext { - pub fn default() -> Self { - FriendsContext { - back: Some("/scuttlebutt/peers".to_string()), - flash_name: None, - flash_msg: None, - title: Some("Friends".to_string()), - theme: None, - sbot_config: None, - sbot_status: None, - peers: None, - } - } - - pub async fn build() -> Result { - let mut context = Self::default(); - - // retrieve current ui theme - context.theme = Some(utils::get_theme()); - - // retrieve go-sbot systemd process status - let sbot_status = SbotStatus::read()?; - - // we only want to try and interact with the sbot if it's active - if sbot_status.state == Some("active".to_string()) { - // retrieve latest go-sbot configuration parameters - let sbot_config = SbotConfig::read().ok(); - - let mut sbot_client = init_sbot_with_config(&sbot_config).await?; - - let local_id = sbot_client.whoami().await?; - - let follows = sbot_client.get_follows().await?; - - // we'll use this to store the profile info for each peer who follows us - let mut peer_info = Vec::new(); - - if !follows.is_empty() { - for peer in follows.iter() { - // trim whitespace (including newline characters) and - // remove the inverted-commas around the id - let peer_id = peer.trim().replace('"', ""); - // retrieve the profile info for the given peer - let mut info = sbot_client.get_profile_info(&peer_id).await?; - // insert the public key of the peer into the info hashmap - info.insert("id".to_string(), peer_id.to_string()); - // retrieve the profile image blob id for the given peer - if let Some(blob_id) = info.get("image") { - // look-up the path for the image blob - if let Ok(blob_path) = blobs::get_blob_path(&blob_id) { - // insert the image blob path of the peer into the info hashmap - info.insert("blob_path".to_string(), blob_path.to_string()); - // check if the blob is in the blobstore - // set a flag in the info hashmap - match utils::blob_is_stored_locally(&blob_path).await { - Ok(exists) if exists == true => { - info.insert("blob_exists".to_string(), "true".to_string()) - } - _ => info.insert("blob_exists".to_string(), "false".to_string()), - }; - } - } - - // check if the peer follows us (making us friends) - let follow_query = RelationshipQuery { - source: peer_id.to_string(), - dest: local_id.clone(), - }; - - // query follow state - match sbot_client.friends_is_following(follow_query).await { - Ok(following) if following == "true" => { - // only push profile info to peer_list vec if they follow us - peer_info.push(info) - } - _ => (), - }; - } - - context.peers = Some(peer_info) - } - } else { - // the sbot is not currently active; return a helpful message - context.flash_name = Some("warning".to_string()); - context.flash_msg = Some("The Sbot is currently inactive. As a result, peer data cannot be retrieved. Visit the Scuttlebutt settings menu to start the Sbot and then try again".to_string()); - } - - context.sbot_status = Some(sbot_status); - - Ok(context) - } -} - -#[derive(Debug, Serialize)] -pub struct ProfileContext { - pub back: Option, - pub flash_name: Option, - pub flash_msg: Option, - pub title: Option, - pub theme: Option, - pub sbot_config: Option, - pub sbot_status: Option, - // is this the local profile or the profile of a peer? - pub is_local_profile: bool, - // an ssb_id which may or may not be the local public key - pub id: Option, - pub name: Option, - pub description: Option, - pub image: Option, - // the path to the blob defined in the `image` field (aka the profile picture) - pub blob_path: Option, - // whether or not the blob exists in the blobstore (ie. is saved on disk) - pub blob_exists: bool, - // relationship state (if the profile being viewed is not for the local public key) - pub following: Option, - pub blocking: Option, -} - -impl ProfileContext { - pub fn default() -> Self { - ProfileContext { - back: Some("/".to_string()), - flash_name: None, - flash_msg: None, - title: Some("Profile".to_string()), - theme: None, - sbot_config: None, - sbot_status: None, - is_local_profile: true, - id: None, - name: None, - description: None, - image: None, - blob_path: None, - blob_exists: false, - following: None, - blocking: None, - } - } - - pub async fn build(ssb_id: Option) -> Result { - let mut context = Self::default(); - - // retrieve current ui theme - context.theme = Some(utils::get_theme()); - - // retrieve go-sbot systemd process status - let sbot_status = SbotStatus::read()?; - - // retrieve latest go-sbot configuration parameters - let sbot_config = SbotConfig::read().ok(); - - let mut sbot_client = init_sbot_with_config(&sbot_config).await?; - - let local_id = sbot_client.whoami().await?; - - // if an ssb_id has been provided to the context builder, we assume that - // the profile info being retrieved is for a peer (ie. not for our local - // profile) - let id = if ssb_id.is_some() { - // we are not dealing with the local profile - context.is_local_profile = false; - - // we're safe to unwrap here because we know it's `Some(id)` - let peer_id = ssb_id.unwrap(); - - // determine relationship between peer and local id - let follow_query = RelationshipQuery { - source: local_id.clone(), - dest: peer_id.clone(), - }; - - // query follow state - context.following = match sbot_client.friends_is_following(follow_query).await { - Ok(following) if following == "true" => Some(true), - Ok(following) if following == "false" => Some(false), - _ => None, - }; - - // TODO: i don't like that we have to instantiate the same query object - // twice. see if we can streamline this in golgi - let block_query = RelationshipQuery { - source: local_id.clone(), - dest: peer_id.clone(), - }; - - // query block state - context.blocking = match sbot_client.friends_is_blocking(block_query).await { - Ok(blocking) if blocking == "true" => Some(true), - Ok(blocking) if blocking == "false" => Some(false), - _ => None, - }; - - peer_id - } else { - // if an ssb_id has not been provided, retrieve the local id using whoami - context.is_local_profile = true; - - local_id - }; - - // retrieve the profile info for the given id - let info = sbot_client.get_profile_info(&id).await?; - // set each context field accordingly - for (key, val) in info { - match key.as_str() { - "name" => context.name = Some(val), - "description" => context.description = Some(val), - "image" => context.image = Some(val), - _ => (), - } - } - - // assign the ssb public key to the context - // (could be for the local profile or a peer) - context.id = Some(id); - - // determine the path to the blob defined by the value of `context.image` - if let Some(ref blob_id) = context.image { - context.blob_path = match blobs::get_blob_path(&blob_id) { - Ok(path) => { - // if we get the path, check if the blob is in the blobstore. - // this allows us to default to a placeholder image in the template - if let Ok(exists) = utils::blob_is_stored_locally(&path).await { - context.blob_exists = exists - }; - - Some(path) - } - Err(_) => None, - } - } - - context.sbot_status = Some(sbot_status); - - Ok(context) - } -} - -#[derive(Debug, Serialize)] -pub struct PrivateContext { - pub back: Option, - pub flash_name: Option, - pub flash_msg: Option, - pub title: Option, - pub theme: Option, - pub sbot_config: Option, - pub sbot_status: Option, - // local peer id (whoami) - pub id: Option, - // id of the peer being messaged - pub recipient_id: Option, -} - -impl PrivateContext { - pub fn default() -> Self { - PrivateContext { - back: Some("/".to_string()), - flash_name: None, - flash_msg: None, - title: Some("Private Messages".to_string()), - theme: None, - sbot_config: None, - sbot_status: None, - id: None, - recipient_id: None, - } - } - - pub async fn build(recipient_id: Option) -> Result { - let mut context = Self::default(); - - // retrieve current ui theme - context.theme = Some(utils::get_theme()); - - // retrieve go-sbot systemd process status - let sbot_status = SbotStatus::read()?; - - // retrieve latest go-sbot configuration parameters - let sbot_config = SbotConfig::read().ok(); - - let mut sbot_client = init_sbot_with_config(&sbot_config).await?; - - context.recipient_id = recipient_id; - - let local_id = sbot_client.whoami().await?; - context.id = Some(local_id); - - context.sbot_status = Some(sbot_status); - - Ok(context) - } -} diff --git a/peach-web/src/tests.rs b/peach-web/src/tests.rs deleted file mode 100644 index 3e6f7c6..0000000 --- a/peach-web/src/tests.rs +++ /dev/null @@ -1,562 +0,0 @@ -use std::fs::File; -use std::io::Read; - -use rocket::http::{ContentType, Status}; -use rocket::local::blocking::Client; -use rocket::{Build, Config, Rocket}; - -use super::init_rocket; - -// define authentication mode -const DISABLE_AUTH: bool = true; - -/// Wrapper around `init_rocket()` to simplify the process of invoking the application with the desired authentication status. This is particularly useful for testing purposes. -fn init_test_rocket(disable_auth: bool) -> Rocket { - // set authentication based on provided `disable_auth` value - Config::figment().merge(("disable_auth", disable_auth)); - - init_rocket() -} - -// helper function to test correct retrieval and content of a file -fn test_query_file(path: &str, file: T, status: Status) -where - T: Into>, -{ - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).unwrap(); - let response = client.get(path).dispatch(); - assert_eq!(response.status(), status); - - 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)); - } -} - -// helper function to return the content of a file, given a path -fn read_file_content(path: &str) -> Vec { - let mut fp = File::open(&path).expect(&format!("Can't open {}", path)); - let mut file_content = vec![]; - - fp.read_to_end(&mut file_content) - .expect(&format!("Reading {} failed.", path)); - file_content -} - -// WEB PAGE ROUTES - -#[test] -fn index_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).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.into_string().unwrap(); - assert!(body.contains("/peers")); - assert!(body.contains("/profile")); - assert!(body.contains("/private")); - assert!(body.contains("/status")); - assert!(body.contains("/help")); - assert!(body.contains("/settings")); -} - -#[test] -fn help_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).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.into_string().unwrap(); - assert!(body.contains("Help")); -} - -#[test] -fn login_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).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.into_string().unwrap(); - assert!(body.contains("Login")); -} - -#[test] -fn logout_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/logout").dispatch(); - // check for 303 status (redirect to "/login") - assert_eq!(response.status(), Status::SeeOther); - assert_eq!(response.content_type(), None); -} - -#[test] -fn power_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/power").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Shutdown Device")); -} - -/* - -NOTE: these tests are comment-out for the moment, due to the fact that they invoke system commands (resulting in a `sudo` password request during test execution). see if we can find a way to test the results without triggering the shutdown or restart. - -#[test] -fn reboot() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/power/reboot").dispatch(); - // check for redirect - assert_eq!(response.status(), Status::SeeOther); -} - -#[test] -fn shutdown() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/power/shutdown").dispatch(); - // check for redirect - assert_eq!(response.status(), Status::SeeOther); -} -*/ - -// SCUTTLEBUTT ROUTES - -#[test] -fn block() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client - .post("/scuttlebutt/block") - .header(ContentType::Form) - .body("key=HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519") - .dispatch(); - assert_eq!(response.status(), Status::SeeOther); -} - -#[test] -fn blocks_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/scuttlebutt/blocks").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Blocks")); -} - -#[test] -fn follow() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client - .post("/scuttlebutt/follow") - .header(ContentType::Form) - .body("key=@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519") - .dispatch(); - // ensure we redirect (303) - assert_eq!(response.status(), Status::SeeOther); -} - -#[test] -fn follows_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/scuttlebutt/follows").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Follows")); -} - -#[test] -fn followers_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/scuttlebutt/followers").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Followers")); -} - -#[test] -fn friends_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/scuttlebutt/friends").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Friends")); -} - -#[test] -fn peers_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/scuttlebutt/peers").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Scuttlebutt Peers")); -} - -#[test] -fn private_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/scuttlebutt/private").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Private Messages")); -} - -#[test] -fn profile_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/scuttlebutt/profile").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Profile")); -} - -#[test] -fn publish_post() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client - .post("/scuttlebutt/publish") - .header(ContentType::Form) - .body("text='golden ripples in the meshwork'") - .dispatch(); - assert_eq!(response.status(), Status::SeeOther); -} - -#[test] -fn unfollow() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client - .post("/scuttlebutt/unfollow") - .header(ContentType::Form) - .body("key=@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519") - .dispatch(); - assert_eq!(response.status(), Status::SeeOther); -} - -// ADMIN SETTINGS ROUTES - -#[test] -fn admin_settings_menu_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/admin").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Administrator Settings")); - assert!(body.contains("Change Password")); - assert!(body.contains("Configure Admin")); -} - -#[test] -fn add_admin_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/admin/add").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Add Admin")); - assert!(body.contains("SSB ID")); - assert!(body.contains("Add")); - assert!(body.contains("Cancel")); -} - -#[test] -fn add_admin() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client - .post("/settings/admin/add") - .header(ContentType::Form) - .body("ssb_id=@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519") - .dispatch(); - // check for redirect - assert_eq!(response.status(), Status::SeeOther); -} - -#[test] -fn change_password_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/admin/change_password").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Change Password")); - assert!(body.contains("Current password")); - assert!(body.contains("New password")); - assert!(body.contains("New password duplicate")); - assert!(body.contains("Save")); -} - -#[test] -fn configure_admin_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/admin/configure").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Configure Admin")); - assert!(body.contains("Current Admins")); - assert!(body.contains("Add Admin")); -} - -#[test] -fn forgot_password_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/admin/forgot_password").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Send Password Reset")); -} - -// NETWORK SETTINGS ROUTES - -#[test] -fn network_settings_menu_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/network").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Network Configuration")); -} - -/* - -NOTE: these tests are commented-out for the moment, due to the fact that they -invoke system commands (resulting in a `sudo` password request during -test execution). see if we can find a way to test the results without -triggering the `systemctl` call. - -#[test] -fn deploy_ap() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/network/ap/activate").dispatch(); - // check for 303 status (redirect) - assert_eq!(response.status(), Status::SeeOther); - assert_eq!(response.content_type(), None); -} - -#[test] -fn deploy_client() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/network/wifi/activate").dispatch(); - // check for 303 status (redirect) - assert_eq!(response.status(), Status::SeeOther); - assert_eq!(response.content_type(), None); -} -*/ - -#[test] -fn dns_settings_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/network/dns").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Configure DNS")); - assert!(body.contains("External Domain (optional)")); - assert!(body.contains("Enable Dynamic DNS")); - assert!(body.contains("Dynamic DNS Domain")); - assert!(body.contains("Save")); -} - -#[test] -fn list_aps_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/network/wifi").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("WiFi Networks")); - assert!(body.contains("No saved or available networks found.")); -} - -// TODO: needs further testing once template has been refactored -#[test] -fn ap_details_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/network/wifi?ssid=Home").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - //let body = response.into_string().unwrap(); - //assert!(body.contains("Network not found")); -} - -#[test] -fn add_ap_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/network/wifi/add").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Add WiFi Network")); - assert!(body.contains("SSID")); - assert!(body.contains("Password")); - assert!(body.contains("Add")); - assert!(body.contains("Cancel")); -} - -#[test] -fn add_ap_ssid_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client - .get("/settings/network/wifi/add?ssid=Home") - .dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Add WiFi Network")); - assert!(body.contains("Home")); - assert!(body.contains("Password")); - assert!(body.contains("Add")); - assert!(body.contains("Cancel")); -} - -#[test] -fn add_credentials() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client - .post("/settings/network/wifi/add") - .header(ContentType::Form) - .body("ssid=Home&pass=Password") - .dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); -} - -#[test] -fn forget_wifi() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client - .post("/settings/network/wifi/forget") - .header(ContentType::Form) - .body("ssid=Home") - .dispatch(); - assert_eq!(response.status(), Status::SeeOther); - assert_eq!(response.content_type(), None); -} - -#[test] -fn modify_password() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client - .post("/settings/network/wifi/modify") - .header(ContentType::Form) - .body("ssid=Home&pass=Password") - .dispatch(); - assert_eq!(response.status(), Status::SeeOther); - assert_eq!(response.content_type(), None); -} - -#[test] -fn data_usage_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/network/wifi/usage").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Network Data Usage")); - assert!(body.contains("WARNING THRESHOLD")); - assert!(body.contains("Update")); - assert!(body.contains("Cancel")); -} - -// SCUTTLEBUTT SETTINGS HTML ROUTES - -#[test] -fn scuttlebutt_settings_menu_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/settings/scuttlebutt").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Scuttlebutt Settings")); - assert!(body.contains("Configure Sbot")); -} - -// STATUS HTML ROUTES - -#[test] -fn status_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/status").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Device Status")); - assert!(body.contains("Networking")); - assert!(body.contains("Display")); - assert!(body.contains("Statistics")); -} - -#[test] -fn network_status_html() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); - let response = client.get("/status/network").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.content_type(), Some(ContentType::HTML)); - let body = response.into_string().unwrap(); - assert!(body.contains("Network Status")); - assert!(body.contains("Mode")); - assert!(body.contains("SSID")); - assert!(body.contains("IP")); - assert!(body.contains("DOWNLOAD")); - assert!(body.contains("UPLOAD")); -} -// FILE TESTS - -#[test] -fn nested_file() { - test_query_file( - "/images/placeholder.txt", - "static/images/placeholder.txt", - Status::Ok, - ); - test_query_file( - "/images/placeholder.txt?v=1", - "static/images/placeholder.txt", - Status::Ok, - ); - test_query_file( - "/images/placeholder.txt?v=1&a=b", - "static/images/placeholder.txt", - Status::Ok, - ); -} - -#[test] -fn icon_file() { - test_query_file( - "/icons/peach-icon.png", - "static/icons/peach-icon.png", - Status::Ok, - ); -} - -#[test] -fn invalid_path() { - test_query_file("/thou_shalt_not_exist", None, Status::NotFound); - test_query_file("/thou_shalt_not_exist", None, Status::NotFound); - test_query_file("/thou/shalt/not/exist?a=b&c=d", None, Status::NotFound); -} - -#[test] -fn invalid_get_request() { - let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).unwrap(); - - // try to get a path that doesn't exist - let res = client - .get("/message/99") - .header(ContentType::JSON) - .dispatch(); - assert_eq!(res.status(), Status::NotFound); - - let body = res.into_string().unwrap(); - assert!(body.contains("404: Page Not Found")); - assert!(body.contains("No PeachCloud resource exists for this URL.")); -}