remove rocket config, context and tests
This commit is contained in:
parent
5fc0094146
commit
07147f8a4f
|
@ -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/"
|
|
@ -1,2 +0,0 @@
|
|||
disable_auth=false
|
||||
standalone_mode=true
|
|
@ -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
|
|
@ -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<String>,
|
||||
pub title: Option<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub theme: Option<String>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
//pub mod dns;
|
||||
//pub mod network;
|
||||
//pub mod scuttlebutt;
|
|
@ -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<Scan>,
|
||||
pub signal: Option<i32>,
|
||||
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<IfaceTraffic> {
|
||||
// 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<String>,
|
||||
pub data_total: Option<Data>, // combined stored and current wifi traffic in bytes
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub threshold: Threshold,
|
||||
pub title: Option<String>,
|
||||
pub traffic: Option<IfaceTraffic>, // 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<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub selected: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub saved_aps: Vec<String>,
|
||||
pub wlan_ip: String,
|
||||
pub wlan_networks: HashMap<String, AccessPoint>,
|
||||
pub wlan_rssi: Option<String>,
|
||||
pub wlan_ssid: String,
|
||||
pub wlan_state: String,
|
||||
pub wlan_status: Option<Status>,
|
||||
pub wlan_traffic: Option<IfaceTraffic>,
|
||||
}
|
||||
|
||||
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::<i32>().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<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub wlan_networks: HashMap<String, String>,
|
||||
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<IfaceTraffic>,
|
||||
pub wlan_ip: String,
|
||||
pub wlan_rssi: Option<String>,
|
||||
pub wlan_ssid: String,
|
||||
pub wlan_state: String,
|
||||
pub wlan_status: Option<Status>,
|
||||
pub wlan_traffic: Option<IfaceTraffic>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
// passing in the ssid of a chosen access point
|
||||
pub selected: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub back: Option<String>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<SbotConfig>,
|
||||
) -> Result<Sbot, PeachWebError> {
|
||||
// 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<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub theme: Option<String>,
|
||||
pub sbot_config: Option<SbotConfig>,
|
||||
pub sbot_status: Option<SbotStatus>,
|
||||
// latest sequence number for the local log
|
||||
pub latest_seq: Option<u64>,
|
||||
}
|
||||
|
||||
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<Self, PeachWebError> {
|
||||
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<SsbMessageValue> = 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<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub theme: Option<String>,
|
||||
pub sbot_config: Option<SbotConfig>,
|
||||
pub sbot_status: Option<SbotStatus>,
|
||||
pub peers: Option<Vec<HashMap<String, String>>>,
|
||||
}
|
||||
|
||||
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<Self, PeachWebError> {
|
||||
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<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub theme: Option<String>,
|
||||
pub sbot_config: Option<SbotConfig>,
|
||||
pub sbot_status: Option<SbotStatus>,
|
||||
pub peers: Option<Vec<HashMap<String, String>>>,
|
||||
}
|
||||
|
||||
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<Self, PeachWebError> {
|
||||
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<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub theme: Option<String>,
|
||||
pub sbot_config: Option<SbotConfig>,
|
||||
pub sbot_status: Option<SbotStatus>,
|
||||
pub peers: Option<Vec<HashMap<String, String>>>,
|
||||
}
|
||||
|
||||
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<Self, PeachWebError> {
|
||||
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<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub theme: Option<String>,
|
||||
pub sbot_config: Option<SbotConfig>,
|
||||
pub sbot_status: Option<SbotStatus>,
|
||||
// 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<String>,
|
||||
pub name: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub image: Option<String>,
|
||||
// the path to the blob defined in the `image` field (aka the profile picture)
|
||||
pub blob_path: Option<String>,
|
||||
// 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<bool>,
|
||||
pub blocking: Option<bool>,
|
||||
}
|
||||
|
||||
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<String>) -> Result<Self, PeachWebError> {
|
||||
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<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub theme: Option<String>,
|
||||
pub sbot_config: Option<SbotConfig>,
|
||||
pub sbot_status: Option<SbotStatus>,
|
||||
// local peer id (whoami)
|
||||
pub id: Option<String>,
|
||||
// id of the peer being messaged
|
||||
pub recipient_id: Option<String>,
|
||||
}
|
||||
|
||||
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<String>) -> Result<Self, PeachWebError> {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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<Build> {
|
||||
// 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<T>(path: &str, file: T, status: Status)
|
||||
where
|
||||
T: Into<Option<&'static str>>,
|
||||
{
|
||||
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<u8> {
|
||||
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."));
|
||||
}
|
Loading…
Reference in New Issue