Replace Rocket and Tera with Rouille and Maud #88
|
@ -1,8 +1,5 @@
|
|||
*.bak
|
||||
static/icons/optimized/*
|
||||
api_docs.md
|
||||
js_docs.md
|
||||
hashmap_notes
|
||||
notes
|
||||
target
|
||||
**/*.rs.bk
|
||||
leftovers
|
||||
|
|
|
@ -1,333 +0,0 @@
|
|||
use log::info;
|
||||
use rocket::{
|
||||
form::{Form, FromForm},
|
||||
get,
|
||||
http::{Cookie, CookieJar, Status},
|
||||
post,
|
||||
request::{self, FlashMessage, FromRequest, Request},
|
||||
response::{Flash, Redirect},
|
||||
};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
|
||||
use peach_lib::{error::PeachError, password_utils};
|
||||
|
||||
use crate::error::PeachWebError;
|
||||
use crate::utils;
|
||||
use crate::utils::TemplateOrRedirect;
|
||||
use crate::RocketConfig;
|
||||
|
||||
// HELPERS AND STRUCTS FOR AUTHENTICATION WITH COOKIES
|
||||
|
||||
pub const AUTH_COOKIE_KEY: &str = "peachweb_auth";
|
||||
pub const ADMIN_USERNAME: &str = "admin";
|
||||
|
||||
/// Note: Currently we use an empty struct for the Authenticated request guard
|
||||
/// because there is only one user to be authenticated, and no data needs to be stored here.
|
||||
/// In a multi-user authentication scheme, we would store the user_id in this struct,
|
||||
/// and retrieve the correct user via the user_id stored in the cookie.
|
||||
pub struct Authenticated;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LoginError {
|
||||
UserNotLoggedIn,
|
||||
}
|
||||
|
||||
/// Request guard which returns an empty Authenticated struct from the request
|
||||
/// if and only if the user has a cookie which proves they are authenticated with peach-web.
|
||||
///
|
||||
/// Note that cookies.get_private uses encryption, which means that this private cookie
|
||||
/// cannot be inspected, tampered with, or manufactured by clients.
|
||||
#[rocket::async_trait]
|
||||
impl<'r> FromRequest<'r> for Authenticated {
|
||||
type Error = LoginError;
|
||||
|
||||
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||
// retrieve auth state from managed state (returns `Option<bool>`).
|
||||
// this value is read from the Rocket.toml config file on start-up
|
||||
let authentication_is_disabled: bool = *req
|
||||
.rocket()
|
||||
.state::<RocketConfig>()
|
||||
.map(|config| (&config.disable_auth))
|
||||
.unwrap_or(&false);
|
||||
|
||||
if authentication_is_disabled {
|
||||
let auth = Authenticated {};
|
||||
request::Outcome::Success(auth)
|
||||
} else {
|
||||
let authenticated = req
|
||||
.cookies()
|
||||
.get_private(AUTH_COOKIE_KEY)
|
||||
.and_then(|cookie| cookie.value().parse().ok())
|
||||
.map(|_value: String| Authenticated {});
|
||||
match authenticated {
|
||||
Some(auth) => request::Outcome::Success(auth),
|
||||
None => request::Outcome::Failure((Status::Forbidden, LoginError::UserNotLoggedIn)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /login
|
||||
|
||||
#[get("/login")]
|
||||
pub fn login(flash: Option<FlashMessage>) -> Template {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("back", &Some("/".to_string()));
|
||||
context.insert("title", &Some("Login".to_string()));
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||
};
|
||||
|
||||
Template::render("login", &context.into_json())
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct LoginForm {
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
/// Takes in a LoginForm and returns Ok(()) if the password is correct.
|
||||
///
|
||||
/// Note: there is currently only one user, therefore we don't need a username.
|
||||
pub fn verify_login_form(login_form: LoginForm) -> Result<(), PeachError> {
|
||||
password_utils::verify_password(&login_form.password)
|
||||
}
|
||||
|
||||
#[post("/login", data = "<login_form>")]
|
||||
pub fn login_post(login_form: Form<LoginForm>, cookies: &CookieJar<'_>) -> TemplateOrRedirect {
|
||||
match verify_login_form(login_form.into_inner()) {
|
||||
Ok(_) => {
|
||||
// if successful login, add a cookie indicating the user is authenticated
|
||||
// and redirect to home page
|
||||
// NOTE: since we currently have just one user, the value of the cookie
|
||||
// is just admin (this is arbitrary).
|
||||
// If we had multiple users, we could put the user_id here.
|
||||
cookies.add_private(Cookie::new(AUTH_COOKIE_KEY, ADMIN_USERNAME));
|
||||
|
||||
TemplateOrRedirect::Redirect(Redirect::to("/"))
|
||||
}
|
||||
Err(e) => {
|
||||
let err_msg = format!("Invalid password: {}", e);
|
||||
// if unsuccessful login, render /login page again
|
||||
let mut context = Context::new();
|
||||
context.insert("back", &Some("/".to_string()));
|
||||
context.insert("title", &Some("Login".to_string()));
|
||||
context.insert("flash_name", &("error".to_string()));
|
||||
context.insert("flash_msg", &(err_msg));
|
||||
|
||||
TemplateOrRedirect::Template(Template::render("login", &context.into_json()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /logout
|
||||
|
||||
#[get("/logout")]
|
||||
pub fn logout(cookies: &CookieJar<'_>) -> Flash<Redirect> {
|
||||
// logout authenticated user
|
||||
info!("Attempting deauthentication of user.");
|
||||
cookies.remove_private(Cookie::named(AUTH_COOKIE_KEY));
|
||||
Flash::success(Redirect::to("/login"), "Logged out")
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /reset_password
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct ResetPasswordForm {
|
||||
pub temporary_password: String,
|
||||
pub new_password1: String,
|
||||
pub new_password2: String,
|
||||
}
|
||||
|
||||
/// Verify, validate and save the submitted password. This function is publicly exposed for users who have forgotten their password.
|
||||
pub fn save_reset_password_form(password_form: ResetPasswordForm) -> Result<(), PeachWebError> {
|
||||
info!(
|
||||
"reset password!: {} {} {}",
|
||||
password_form.temporary_password, password_form.new_password1, password_form.new_password2
|
||||
);
|
||||
password_utils::verify_temporary_password(&password_form.temporary_password)?;
|
||||
// if the previous line did not throw an error, then the secret_link is correct
|
||||
password_utils::validate_new_passwords(
|
||||
&password_form.new_password1,
|
||||
&password_form.new_password2,
|
||||
)?;
|
||||
// if the previous line did not throw an error, then the new password is valid
|
||||
password_utils::set_new_password(&password_form.new_password1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Password reset request handler. This route is used by a user who is not logged in
|
||||
/// and is specifically for users who have forgotten their password.
|
||||
#[get("/reset_password")]
|
||||
pub fn reset_password(flash: Option<FlashMessage>) -> Template {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("back", &Some("/".to_string()));
|
||||
context.insert("title", &Some("Reset Password".to_string()));
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||
};
|
||||
|
||||
Template::render("settings/admin/reset_password", &context.into_json())
|
||||
}
|
||||
|
||||
/// Password reset form request handler. This route is used by a user who is not logged in
|
||||
/// and is specifically for users who have forgotten their password.
|
||||
#[post("/reset_password", data = "<reset_password_form>")]
|
||||
pub fn reset_password_post(reset_password_form: Form<ResetPasswordForm>) -> Template {
|
||||
let mut context = Context::new();
|
||||
context.insert("back", &Some("/".to_string()));
|
||||
context.insert("title", &Some("Reset Password".to_string()));
|
||||
|
||||
let (flash_name, flash_msg) = match save_reset_password_form(reset_password_form.into_inner()) {
|
||||
Ok(_) => (
|
||||
"success".to_string(),
|
||||
"New password has been saved. Return home to login".to_string(),
|
||||
),
|
||||
Err(err) => (
|
||||
"error".to_string(),
|
||||
format!("Failed to reset password: {}", err),
|
||||
),
|
||||
};
|
||||
|
||||
context.insert("flash_name", &Some(flash_name));
|
||||
context.insert("flash_msg", &Some(flash_msg));
|
||||
|
||||
Template::render("settings/admin/reset_password", &context.into_json())
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /send_password_reset
|
||||
|
||||
/// Page for users who have forgotten their password.
|
||||
/// This route is used by a user who is not logged in
|
||||
/// to initiate the sending of a new password reset.
|
||||
#[get("/forgot_password")]
|
||||
pub fn forgot_password_page(flash: Option<FlashMessage>) -> Template {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("back", &Some("/".to_string()));
|
||||
context.insert("title", &Some("Send Password Reset".to_string()));
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||
};
|
||||
|
||||
Template::render("settings/admin/forgot_password", &context.into_json())
|
||||
}
|
||||
|
||||
/// Send password reset request handler. This route is used by a user who is not logged in
|
||||
/// and is specifically for users who have forgotten their password. A successful request results
|
||||
/// in a Scuttlebutt private message being sent to the account of the device admin.
|
||||
#[post("/send_password_reset")]
|
||||
pub fn send_password_reset_post() -> Template {
|
||||
info!("++ send password reset post");
|
||||
let mut context = Context::new();
|
||||
context.insert("back", &Some("/".to_string()));
|
||||
context.insert("title", &Some("Send Password Reset".to_string()));
|
||||
|
||||
let (flash_name, flash_msg) = match password_utils::send_password_reset() {
|
||||
Ok(_) => (
|
||||
"success".to_string(),
|
||||
"A password reset link has been sent to the admin of this device".to_string(),
|
||||
),
|
||||
Err(err) => (
|
||||
"error".to_string(),
|
||||
format!("Failed to send password reset link: {}", err),
|
||||
),
|
||||
};
|
||||
|
||||
context.insert("flash_name", &Some(flash_name));
|
||||
context.insert("flash_msg", &Some(flash_msg));
|
||||
|
||||
Template::render("settings/admin/forgot_password", &context.into_json())
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /settings/change_password
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct PasswordForm {
|
||||
pub current_password: String,
|
||||
pub new_password1: String,
|
||||
pub new_password2: String,
|
||||
}
|
||||
|
||||
/// Password save form request handler. This function is for use by a user who is already logged in to change their password.
|
||||
pub fn save_password_form(password_form: PasswordForm) -> Result<(), PeachWebError> {
|
||||
info!(
|
||||
"change password!: {} {} {}",
|
||||
password_form.current_password, password_form.new_password1, password_form.new_password2
|
||||
);
|
||||
password_utils::verify_password(&password_form.current_password)?;
|
||||
// if the previous line did not throw an error, then the old password is correct
|
||||
password_utils::validate_new_passwords(
|
||||
&password_form.new_password1,
|
||||
&password_form.new_password2,
|
||||
)?;
|
||||
// if the previous line did not throw an error, then the new password is valid
|
||||
password_utils::set_new_password(&password_form.new_password1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Change password request handler. This is used by a user who is already logged in.
|
||||
#[get("/change_password")]
|
||||
pub fn change_password(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("back", &Some("/settings/admin".to_string()));
|
||||
context.insert("title", &Some("Change Password".to_string()));
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
// add flash message contents to the context object
|
||||
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||
};
|
||||
|
||||
Template::render("settings/admin/change_password", &context.into_json())
|
||||
}
|
||||
|
||||
/// Change password form request handler. This route is used by a user who is already logged in.
|
||||
#[post("/change_password", data = "<password_form>")]
|
||||
pub fn change_password_post(password_form: Form<PasswordForm>, _auth: Authenticated) -> Template {
|
||||
let mut context = Context::new();
|
||||
context.insert("back", &Some("/settings/admin".to_string()));
|
||||
context.insert("title", &Some("Change Password".to_string()));
|
||||
|
||||
let (flash_name, flash_msg) = match save_password_form(password_form.into_inner()) {
|
||||
Ok(_) => (
|
||||
"success".to_string(),
|
||||
"New password has been saved".to_string(),
|
||||
),
|
||||
Err(err) => (
|
||||
"error".to_string(),
|
||||
format!("Failed to save new password: {}", err),
|
||||
),
|
||||
};
|
||||
|
||||
context.insert("flash_name", &Some(flash_name));
|
||||
context.insert("flash_msg", &Some(flash_msg));
|
||||
|
||||
Template::render("settings/admin/change_password", &context.into_json())
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
use log::debug;
|
||||
use rocket::catch;
|
||||
use rocket::response::Redirect;
|
||||
use rocket_dyn_templates::Template;
|
||||
use serde::Serialize;
|
||||
|
||||
// HELPERS AND ROUTES FOR 404 ERROR
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct ErrorContext {
|
||||
pub back: Option<String>,
|
||||
pub flash_name: Option<String>,
|
||||
pub flash_msg: Option<String>,
|
||||
pub title: Option<String>,
|
||||
}
|
||||
|
||||
impl ErrorContext {
|
||||
pub fn build() -> ErrorContext {
|
||||
ErrorContext {
|
||||
back: None,
|
||||
flash_name: None,
|
||||
flash_msg: None,
|
||||
title: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[catch(404)]
|
||||
pub fn not_found() -> Template {
|
||||
debug!("404 Page Not Found");
|
||||
let mut context = ErrorContext::build();
|
||||
context.back = Some("/".to_string());
|
||||
context.title = Some("404: Page Not Found".to_string());
|
||||
context.flash_name = Some("error".to_string());
|
||||
context.flash_msg = Some("No resource found for given URL".to_string());
|
||||
|
||||
Template::render("catchers/not_found", context)
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR 500 ERROR
|
||||
|
||||
#[catch(500)]
|
||||
pub fn internal_error() -> Template {
|
||||
debug!("500 Internal Server Error");
|
||||
let mut context = ErrorContext::build();
|
||||
context.back = Some("/".to_string());
|
||||
context.title = Some("500: Internal Server Error".to_string());
|
||||
context.flash_name = Some("error".to_string());
|
||||
context.flash_msg = Some("Internal server error".to_string());
|
||||
|
||||
Template::render("catchers/internal_error", context)
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR 403 FORBIDDEN
|
||||
|
||||
#[catch(403)]
|
||||
pub fn forbidden() -> Redirect {
|
||||
debug!("403 Forbidden");
|
||||
Redirect::to("/login")
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
use peach_lib::sbot::SbotStatus;
|
||||
use rocket::{get, request::FlashMessage, State};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
|
||||
use crate::routes::authentication::Authenticated;
|
||||
use crate::utils;
|
||||
use crate::RocketConfig;
|
||||
|
||||
// HELPERS AND ROUTES FOR / (HOME PAGE)
|
||||
|
||||
#[get("/")]
|
||||
pub fn home(_auth: Authenticated, config: &State<RocketConfig>) -> Template {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
// retrieve go-sbot systemd process status
|
||||
let sbot_status = SbotStatus::read().ok();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("sbot_status", &sbot_status);
|
||||
context.insert("flash_name", &None::<()>);
|
||||
context.insert("flash_msg", &None::<()>);
|
||||
context.insert("title", &None::<()>);
|
||||
|
||||
// pass in mode from managed state so we can define appropriate urls in template
|
||||
context.insert("standalone_mode", &config.standalone_mode);
|
||||
|
||||
Template::render("home", &context.into_json())
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /guide
|
||||
|
||||
#[get("/guide")]
|
||||
pub fn guide(flash: Option<FlashMessage>) -> Template {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("back", &Some("/".to_string()));
|
||||
context.insert("title", &Some("Guide".to_string()));
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||
};
|
||||
|
||||
Template::render("guide", &context.into_json())
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
use rocket::{
|
||||
form::{Form, FromForm},
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
uri,
|
||||
};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
|
||||
use peach_lib::config_manager;
|
||||
|
||||
use crate::error::PeachWebError;
|
||||
use crate::routes::authentication::Authenticated;
|
||||
use crate::utils;
|
||||
|
||||
// HELPERS AND ROUTES FOR /settings/admin
|
||||
|
||||
/// Administrator settings menu.
|
||||
#[get("/")]
|
||||
pub fn admin_menu(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("back", &Some("/settings".to_string()));
|
||||
context.insert("title", &Some("Administrator Settings".to_string()));
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||
};
|
||||
|
||||
Template::render("settings/admin/menu", &context.into_json())
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /settings/admin/configure
|
||||
|
||||
/// View and delete currently configured admin.
|
||||
#[get("/configure")]
|
||||
pub fn configure_admin(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
||||
// retrieve current ui theme
|
||||
let theme = utils::get_theme();
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("theme", &theme);
|
||||
context.insert("back", &Some("/settings/admin".to_string()));
|
||||
context.insert("title", &Some("Configure Admin".to_string()));
|
||||
|
||||
// check to see if there is a flash message to display
|
||||
if let Some(flash) = flash {
|
||||
context.insert("flash_name", &Some(flash.kind().to_string()));
|
||||
context.insert("flash_msg", &Some(flash.message().to_string()));
|
||||
};
|
||||
|
||||
// load the peach configuration vector
|
||||
match config_manager::load_peach_config() {
|
||||
Ok(config) => {
|
||||
// retrieve the vector of ssb admin ids
|
||||
let ssb_admin_ids = config.ssb_admin_ids;
|
||||
context.insert("ssb_admin_ids", &ssb_admin_ids);
|
||||
}
|
||||
// if load fails, overwrite the flash_name and flash_msg
|
||||
Err(e) => {
|
||||
context.insert("flash_name", &Some("error".to_string()));
|
||||
context.insert(
|
||||
"flash_msg",
|
||||
&Some(format!("Failed to load Peach config: {}", e)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Template::render("settings/admin/configure_admin", &context.into_json())
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /settings/admin/add
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct AddAdminForm {
|
||||
pub ssb_id: String,
|
||||
}
|
||||
|
||||
pub fn save_add_admin_form(admin_form: AddAdminForm) -> Result<(), PeachWebError> {
|
||||
let _result = config_manager::add_ssb_admin_id(&admin_form.ssb_id)?;
|
||||
|
||||
// if the previous line didn't throw an error then it was a success
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[post("/add", data = "<add_admin_form>")]
|
||||
pub fn add_admin_post(add_admin_form: Form<AddAdminForm>, _auth: Authenticated) -> Flash<Redirect> {
|
||||
let result = save_add_admin_form(add_admin_form.into_inner());
|
||||
let url = uri!("/settings/admin/configure");
|
||||
match result {
|
||||
Ok(_) => Flash::success(Redirect::to(url), "Added SSB administrator"),
|
||||
Err(e) => Flash::error(Redirect::to(url), format!("Failed to add new admin: {}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /settings/admin/delete
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct DeleteAdminForm {
|
||||
pub ssb_id: String,
|
||||
}
|
||||
|
||||
#[post("/delete", data = "<delete_admin_form>")]
|
||||
pub fn delete_admin_post(
|
||||
delete_admin_form: Form<DeleteAdminForm>,
|
||||
_auth: Authenticated,
|
||||
) -> Flash<Redirect> {
|
||||
let result = config_manager::delete_ssb_admin_id(&delete_admin_form.ssb_id);
|
||||
let url = uri!("/settings/admin", configure_admin);
|
||||
match result {
|
||||
Ok(_) => Flash::success(Redirect::to(url), "Removed SSB administrator"),
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to remove admin id: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -1,37 +0,0 @@
|
|||
use rocket::{get, State};
|
||||
use rocket_dyn_templates::Template;
|
||||
|
||||
use crate::routes::authentication::Authenticated;
|
||||
use crate::{context::scuttlebutt::StatusContext, RocketConfig};
|
||||
|
||||
// HELPERS AND ROUTES FOR /status/scuttlebutt
|
||||
|
||||
#[get("/scuttlebutt")]
|
||||
pub async fn scuttlebutt_status(_auth: Authenticated, config: &State<RocketConfig>) -> Template {
|
||||
let context = StatusContext::build().await;
|
||||
|
||||
let back = if config.standalone_mode {
|
||||
// return to home page
|
||||
Some("/".to_string())
|
||||
} else {
|
||||
// return to status menu
|
||||
Some("/status".to_string())
|
||||
};
|
||||
|
||||
match context {
|
||||
Ok(mut context) => {
|
||||
// define back arrow url based on mode
|
||||
context.back = back;
|
||||
|
||||
Template::render("status/scuttlebutt", &context)
|
||||
}
|
||||
Err(_) => {
|
||||
let mut context = StatusContext::default();
|
||||
|
||||
// define back arrow url based on mode
|
||||
context.back = back;
|
||||
|
||||
Template::render("status/scuttlebutt", &context)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,198 +0,0 @@
|
|||
// Monitor data transmission totals, set thresholds and check alert flags
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
use nest::{Error, Store, Value};
|
||||
use rocket::form::FromForm;
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
/// Network traffic data total
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Data {
|
||||
pub total: u64, // total traffic in bytes
|
||||
}
|
||||
|
||||
impl Data {
|
||||
/// Retrieve network traffic data values from the store
|
||||
fn get(store: &Store) -> Data {
|
||||
// retrieve previous network traffic statistics
|
||||
let data_stored = match store.get(&["net", "traffic", "total"]) {
|
||||
Ok(total) => total,
|
||||
// return 0 if no value exists
|
||||
Err(_) => Value::Uint(u64::MIN),
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
// retrieve u64 from Value type
|
||||
if let Value::Uint(total) = data_stored {
|
||||
data.push(total);
|
||||
};
|
||||
|
||||
Data { total: data[0] }
|
||||
}
|
||||
}
|
||||
|
||||
/// Network traffic notification thresholds and flags (user-defined)
|
||||
#[derive(Debug, Deserialize, Serialize, FromForm)]
|
||||
pub struct Threshold {
|
||||
warn: u64, // traffic warning threshold
|
||||
cut: u64, // traffic cutoff threshold
|
||||
warn_flag: bool, // traffic warning notification flag
|
||||
cut_flag: bool, // traffic cutoff notification flag
|
||||
}
|
||||
|
||||
impl Threshold {
|
||||
/// Retrieve notification thresholds and flags from the store
|
||||
fn get(store: &Store) -> Threshold {
|
||||
let mut threshold = Vec::new();
|
||||
|
||||
let warn_val = store
|
||||
.get(&["net", "notify", "warn"])
|
||||
.unwrap_or(Value::Uint(0));
|
||||
if let Value::Uint(val) = warn_val {
|
||||
threshold.push(val);
|
||||
};
|
||||
|
||||
let cut_val = store
|
||||
.get(&["net", "notify", "cut"])
|
||||
.unwrap_or(Value::Uint(0));
|
||||
if let Value::Uint(val) = cut_val {
|
||||
threshold.push(val);
|
||||
};
|
||||
|
||||
let mut flag = Vec::new();
|
||||
|
||||
let warn_flag = store
|
||||
.get(&["net", "notify", "warn_flag"])
|
||||
.unwrap_or(Value::Bool(false));
|
||||
if let Value::Bool(state) = warn_flag {
|
||||
flag.push(state);
|
||||
}
|
||||
|
||||
let cut_flag = store
|
||||
.get(&["net", "notify", "cut_flag"])
|
||||
.unwrap_or(Value::Bool(false));
|
||||
if let Value::Bool(state) = cut_flag {
|
||||
flag.push(state);
|
||||
}
|
||||
|
||||
Threshold {
|
||||
warn: threshold[0],
|
||||
cut: threshold[1],
|
||||
warn_flag: flag[0],
|
||||
cut_flag: flag[1],
|
||||
}
|
||||
}
|
||||
|
||||
/// Store notification flags from user data
|
||||
fn set(self, store: &Store) {
|
||||
store
|
||||
.set(&["net", "notify", "warn"], &Value::Uint(self.warn))
|
||||
.unwrap();
|
||||
store
|
||||
.set(&["net", "notify", "cut"], &Value::Uint(self.cut))
|
||||
.unwrap();
|
||||
store
|
||||
.set(
|
||||
&["net", "notify", "warn_flag"],
|
||||
&Value::Bool(self.warn_flag),
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.set(&["net", "notify", "cut_flag"], &Value::Bool(self.cut_flag))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Warning and cutoff network traffic alert flags (programatically-defined)
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Alert {
|
||||
warn: bool,
|
||||
cut: bool,
|
||||
}
|
||||
|
||||
impl Alert {
|
||||
/// Retrieve latest alert flags from the store
|
||||
fn get(store: &Store) -> Alert {
|
||||
let mut alert = Vec::new();
|
||||
|
||||
let warn_flag = store
|
||||
.get(&["net", "alert", "warn"])
|
||||
.unwrap_or(Value::Bool(false));
|
||||
if let Value::Bool(flag) = warn_flag {
|
||||
alert.push(flag);
|
||||
}
|
||||
|
||||
let cut_flag = store
|
||||
.get(&["net", "alert", "cut"])
|
||||
.unwrap_or(Value::Bool(false));
|
||||
if let Value::Bool(flag) = cut_flag {
|
||||
alert.push(flag);
|
||||
}
|
||||
|
||||
Alert {
|
||||
warn: alert[0],
|
||||
cut: alert[1],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_store() -> std::result::Result<Store, Error> {
|
||||
// define the path
|
||||
let path = xdg::BaseDirectories::new()
|
||||
.unwrap()
|
||||
.create_data_directory("peachcloud")
|
||||
.unwrap();
|
||||
|
||||
// define the schema
|
||||
let schema = json!({
|
||||
"net": {
|
||||
"traffic": "json",
|
||||
"alert": "json",
|
||||
"notify": "json",
|
||||
}
|
||||
})
|
||||
.try_into()?;
|
||||
|
||||
// create the data store
|
||||
let store = Store::new(path, schema);
|
||||
|
||||
Ok(store)
|
||||
}
|
||||
|
||||
pub fn get_alerts() -> std::result::Result<Alert, Error> {
|
||||
let store = create_store()?;
|
||||
let alerts = Alert::get(&store);
|
||||
|
||||
Ok(alerts)
|
||||
}
|
||||
|
||||
pub fn get_data() -> std::result::Result<Data, Error> {
|
||||
let store = create_store()?;
|
||||
let data = Data::get(&store);
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn get_thresholds() -> std::result::Result<Threshold, Error> {
|
||||
let store = create_store()?;
|
||||
let thresholds = Threshold::get(&store);
|
||||
|
||||
Ok(thresholds)
|
||||
}
|
||||
|
||||
// set stored traffic total to 0
|
||||
pub fn reset_data() -> std::result::Result<(), Error> {
|
||||
let store = create_store()?;
|
||||
store.set(&["net", "traffic", "total"], &Value::Uint(0))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_store(threshold: Threshold) -> std::result::Result<(), Error> {
|
||||
let store = create_store()?;
|
||||
Threshold::set(threshold, &store);
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue