home template is working
This commit is contained in:
parent
b7cf3c1aab
commit
23d6870f77
File diff suppressed because it is too large
Load Diff
|
@ -38,21 +38,13 @@ maintenance = { status = "actively-developed" }
|
|||
base64 = "0.13.0"
|
||||
dirs = "4.0.0"
|
||||
env_logger = "0.8"
|
||||
#golgi = "0.1.0"
|
||||
golgi = { path = "/home/glyph/Projects/playground/rust/golgi" }
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
nest = "1.0.0"
|
||||
maud = "0.23.0"
|
||||
peach-lib = { path = "../peach-lib" }
|
||||
peach-network = { path = "../peach-network", features = ["serde_support"] }
|
||||
peach-stats = { path = "../peach-stats", features = ["serde_support"] }
|
||||
rocket = { version = "0.5.0-rc.1", features = ["json", "secrets"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
peach-network = { path = "../peach-network" }
|
||||
peach-stats = { path = "../peach-stats" }
|
||||
rouille = "3.5.0"
|
||||
temporary = "0.6.4"
|
||||
tera = { version = "1.12.1", features = ["builtins"] }
|
||||
xdg = "2.2.0"
|
||||
|
||||
[dependencies.rocket_dyn_templates]
|
||||
version = "0.1.0-rc.1"
|
||||
features = ["tera"]
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
|
||||
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
|
||||
|
||||
[ tasks ]
|
||||
|
||||
- write the nav and base templates
|
||||
- get the homepage loading properly
|
||||
- route handler
|
||||
- template
|
||||
- file loading (static assets)
|
|
@ -1,3 +1,3 @@
|
|||
pub mod dns;
|
||||
pub mod network;
|
||||
pub mod scuttlebutt;
|
||||
//pub mod dns;
|
||||
//pub mod network;
|
||||
//pub mod scuttlebutt;
|
||||
|
|
|
@ -8,39 +8,34 @@
|
|||
//! ## Design
|
||||
//!
|
||||
//! `peach-web` is written primarily in Rust and presents a web interface for
|
||||
//! interacting with the device. The stack currently consists of Rocket (Rust
|
||||
//! web framework), Tera (Rust template engine inspired by Jinja2 and the Django
|
||||
//! template language), HTML, CSS and JavaScript. Additional functionality is
|
||||
//! provided by JSON-RPC clients for the `peach-network` and `peach-stats`
|
||||
//! microservices.
|
||||
//!
|
||||
//! HTML is rendered server-side. Request handlers call JSON-RPC microservices
|
||||
//! and serve HTML and assets. A JSON API is exposed for remote calls and
|
||||
//! dynamic client-side content updates via vanilla JavaScript following
|
||||
//! unobstructive design principles. Each Tera template is passed a context
|
||||
//! object. In the case of Rust, this object is a `struct` and must implement
|
||||
//! `Serialize`. The fields of the context object are available in the context
|
||||
//! of the template to be rendered.
|
||||
//! interacting with the device. The stack currently consists of Rouille (Rust
|
||||
//! micro-web-framework), Maud (an HTML template engine for Rust), HTML and
|
||||
//! CSS.
|
||||
|
||||
mod context;
|
||||
//mod context;
|
||||
pub mod error;
|
||||
mod router;
|
||||
pub mod routes;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
//mod router;
|
||||
//pub mod routes;
|
||||
//#[cfg(test)]
|
||||
//mod tests;
|
||||
mod templates;
|
||||
pub mod utils;
|
||||
|
||||
use std::{process, sync::RwLock};
|
||||
use std::sync::RwLock;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use log::{debug, error, info};
|
||||
use peach_lib::{config_manager, config_manager::YAML_PATH as PEACH_CONFIG};
|
||||
use rocket::{fairing::AdHoc, serde::Deserialize, Build, Rocket};
|
||||
//use log::{debug, error, info};
|
||||
use log::info;
|
||||
//use peach_lib::{config_manager, config_manager::YAML_PATH as PEACH_CONFIG};
|
||||
//use rocket::{fairing::AdHoc, serde::Deserialize, Build, Rocket};
|
||||
|
||||
use rouille::{router, Response};
|
||||
|
||||
use utils::Theme;
|
||||
|
||||
pub type BoxError = Box<dyn std::error::Error>;
|
||||
|
||||
/*
|
||||
/// Application configuration parameters.
|
||||
/// These values are extracted from Rocket's default configuration provider:
|
||||
/// `Config::figment()`. As such, the values are drawn from `Rocket.toml` or
|
||||
|
@ -52,14 +47,16 @@ pub struct RocketConfig {
|
|||
disable_auth: bool,
|
||||
standalone_mode: bool,
|
||||
}
|
||||
*/
|
||||
|
||||
lazy_static! {
|
||||
static ref THEME: RwLock<Theme> = RwLock::new(Theme::Light);
|
||||
}
|
||||
|
||||
static WLAN_IFACE: &str = "wlan0";
|
||||
static AP_IFACE: &str = "ap0";
|
||||
//static WLAN_IFACE: &str = "wlan0";
|
||||
//static AP_IFACE: &str = "ap0";
|
||||
|
||||
/*
|
||||
pub fn init_rocket() -> Rocket<Build> {
|
||||
info!("Initializing Rocket");
|
||||
// build a basic rocket instance
|
||||
|
@ -84,13 +81,16 @@ pub fn init_rocket() -> Rocket<Build> {
|
|||
info!("Attaching application configuration to managed state");
|
||||
mounted_rocket.attach(AdHoc::config::<RocketConfig>())
|
||||
}
|
||||
*/
|
||||
|
||||
/// Launch the peach-web rocket server.
|
||||
#[rocket::main]
|
||||
async fn main() {
|
||||
const HOSTNAME_AND_PORT: &str = "localhost:8000";
|
||||
|
||||
/// Launch the peach-web server.
|
||||
fn main() {
|
||||
// initialize logger
|
||||
env_logger::init();
|
||||
|
||||
/*
|
||||
// check if /var/lib/peachcloud/config.yml exists
|
||||
if !std::path::Path::new(PEACH_CONFIG).exists() {
|
||||
info!("PeachCloud configuration file not found; loading default values");
|
||||
|
@ -102,14 +102,29 @@ async fn main() {
|
|||
// this ensures a config file is created if it does not already exist
|
||||
config_manager::save_peach_config(config).expect("peachcloud configuration saving failed");
|
||||
}
|
||||
*/
|
||||
|
||||
// initialize rocket
|
||||
let rocket = init_rocket();
|
||||
info!("Launching web server...");
|
||||
|
||||
// launch rocket
|
||||
info!("Launching Rocket");
|
||||
if let Err(e) = rocket.launch().await {
|
||||
error!("Error in Rocket application: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
// the `start_server` starts listening forever on the given address.
|
||||
rouille::start_server(HOSTNAME_AND_PORT, move |request| {
|
||||
info!("Now listening on {}", HOSTNAME_AND_PORT);
|
||||
|
||||
// static file server
|
||||
// matches on assets in the `static` directory
|
||||
let response = rouille::match_assets(&request, "static");
|
||||
if response.is_success() {
|
||||
return response;
|
||||
}
|
||||
|
||||
router!(request,
|
||||
(GET) (/) => {
|
||||
Response::html(templates::home::build())
|
||||
},
|
||||
|
||||
// The code block is called if none of the other blocks matches the request.
|
||||
// We return an empty response with a 404 status code.
|
||||
_ => Response::empty_404()
|
||||
)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
pub mod authentication;
|
||||
pub mod catchers;
|
||||
//pub mod authentication;
|
||||
//pub mod catchers;
|
||||
pub mod index;
|
||||
pub mod scuttlebutt;
|
||||
pub mod settings;
|
||||
pub mod status;
|
||||
//pub mod scuttlebutt;
|
||||
//pub mod settings;
|
||||
//pub mod status;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
use maud::{html, PreEscaped, DOCTYPE};
|
||||
|
||||
/// Base template builder.
|
||||
///
|
||||
/// Takes an HTML body as input and splices it into the base template.
|
||||
pub fn build(body: PreEscaped<String>) -> PreEscaped<String> {
|
||||
html! {
|
||||
(DOCTYPE)
|
||||
html lang="en" data-theme="light";
|
||||
head {
|
||||
meta charset="utf-8";
|
||||
meta name="description" content="PeachCloud web interface";
|
||||
meta name="author" content="glyph and notplants";
|
||||
meta name="viewport" content="width=devide-width, initial-scale=1.0";
|
||||
link rel="stylesheet" href="/css/peachcloud.css";
|
||||
link rel="stylesheet" href="/css/_variables.css";
|
||||
title { "PeachCloud" }
|
||||
}
|
||||
body {
|
||||
(body)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
use maud::{html, PreEscaped};
|
||||
use peach_lib::sbot::SbotStatus;
|
||||
|
||||
use crate::templates;
|
||||
|
||||
/// Read the state of the go-sbot process and define status-related
|
||||
/// elements accordingly.
|
||||
fn render_status_elements<'a>() -> (&'a str, &'a str, &'a str) {
|
||||
// retrieve go-sbot systemd process status
|
||||
let sbot_status = SbotStatus::read();
|
||||
|
||||
// conditionally render the center circle class, center circle text and
|
||||
// status circle class color based on the go-sbot process state
|
||||
if let Ok(status) = sbot_status {
|
||||
if status.state == Some("active".to_string()) {
|
||||
(
|
||||
"circle circle-large circle-success",
|
||||
"^_^",
|
||||
"circle circle-small border-circle-small border-success",
|
||||
)
|
||||
} else if status.state == Some("inactive".to_string()) {
|
||||
(
|
||||
"circle circle-large circle-warning",
|
||||
"z_z",
|
||||
"circle circle-small border-circle-small border-warning",
|
||||
)
|
||||
} else {
|
||||
(
|
||||
"circle circle-large circle-danger",
|
||||
"x_x",
|
||||
"circle circle-small border-circle-small border-danger",
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(
|
||||
"circle circle-large circle-danger",
|
||||
"x_x",
|
||||
"circle circle-small border-circle-small border-danger",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Home template builder.
|
||||
pub fn build<'a>() -> PreEscaped<String> {
|
||||
let (center_circle_class, center_circle_text, status_circle_class) = render_status_elements();
|
||||
|
||||
// render the home template html
|
||||
let home_template = html! {
|
||||
(PreEscaped("<!-- RADIAL MENU -->"))
|
||||
div class="grid" {
|
||||
(PreEscaped("<!-- top-left -->"))
|
||||
(PreEscaped("<!-- PEERS LINK AND ICON -->"))
|
||||
a class="top-left" href="/scuttlebutt/peers" title="Scuttlebutt Peers" {
|
||||
div class="circle circle-small border-circle-small border-ssb" {
|
||||
img class="icon-medium" src="/icons/users.svg";
|
||||
}
|
||||
}
|
||||
(PreEscaped("<!-- top-middle -->"))
|
||||
(PreEscaped("<!-- CURRENT USER LINK AND ICON -->"))
|
||||
a class="top-middle" href="/scuttlebutt/profile" title="Profile" {
|
||||
div class="circle circle-small border-circle-small border-ssb" {
|
||||
img class="icon-medium" src="/icons/user.svg";
|
||||
}
|
||||
}
|
||||
(PreEscaped("<!-- top-right -->"))
|
||||
(PreEscaped("<!-- MESSAGES LINK AND ICON -->"))
|
||||
a class="top-right" href="/scuttlebutt/private" title="Private Messages" {
|
||||
div class="circle circle-small border-circle-small border-ssb" {
|
||||
img class="icon-medium" src="/icons/envelope.svg";
|
||||
}
|
||||
}
|
||||
(PreEscaped("<!-- middle -->"))
|
||||
a class="middle" {
|
||||
div class=(center_circle_class) {
|
||||
p style="font-size: 4rem; color: var(--near-black);" {
|
||||
(center_circle_text)
|
||||
}
|
||||
}
|
||||
}
|
||||
(PreEscaped("<!-- bottom-left -->"))
|
||||
(PreEscaped("<!-- SYSTEM STATUS LINK AND ICON -->"))
|
||||
a class="bottom-left" href="/status/scuttlebutt" title="Status" {
|
||||
div class=(status_circle_class) {
|
||||
img class="icon-medium" src="/icons/heart-pulse.svg";
|
||||
}
|
||||
}
|
||||
/*
|
||||
TODO: render the path of the status circle button based on the mode
|
||||
{%- if standalone_mode == true -%}
|
||||
<a class="bottom-left" href="/status/scuttlebutt" title="Status">
|
||||
{% else -%}
|
||||
<a class="bottom-left" href="/status" title="Status">
|
||||
{%- endif -%}
|
||||
*/
|
||||
(PreEscaped("<!-- bottom-middle -->"))
|
||||
(PreEscaped("<!-- PEACHCLOUD GUIDEBOOK LINK AND ICON -->"))
|
||||
a class="bottom-middle" href="/guide" title="Guide" {
|
||||
div class="circle circle-small border-circle-small border-info" {
|
||||
img class="icon-medium" src="/icons/book.svg";
|
||||
}
|
||||
}
|
||||
(PreEscaped("<!-- bottom-right -->"))
|
||||
(PreEscaped("<!-- SYSTEM SETTINGS LINK AND ICON -->"))
|
||||
a class="bottom-right" href="/settings" title="Settings" {
|
||||
div class="circle circle-small border-circle-small border-settings" {
|
||||
img class="icon-medium" src="/icons/cog.svg";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// wrap the nav bars around the home template content
|
||||
// title is "" and back button link is `None` because this is the homepage
|
||||
let body = templates::nav::build(home_template, "", None);
|
||||
|
||||
// render the base template with the provided body
|
||||
templates::base::build(body)
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
mod base;
|
||||
pub mod home;
|
||||
pub mod nav;
|
|
@ -0,0 +1,59 @@
|
|||
use maud::{html, PreEscaped};
|
||||
|
||||
use crate::utils;
|
||||
|
||||
/// Navigation template builder.
|
||||
///
|
||||
/// Takes the main HTML content as input and splices it into the navigation template.
|
||||
pub fn build(main: PreEscaped<String>, title: &str, back: Option<&str>) -> PreEscaped<String> {
|
||||
// retrieve the current theme value
|
||||
let theme = utils::get_theme();
|
||||
|
||||
// conditionally render the hermies icon and theme-switcher icon with correct link
|
||||
let (hermies, switcher) = match theme.as_str() {
|
||||
// if we're using the dark theme, render light icons and "light" query param
|
||||
"dark" => (
|
||||
"/icons/hermies_hex_light.svg",
|
||||
html! {
|
||||
a class="nav-item" href="/theme?theme=light" {
|
||||
img class="icon-medium nav-icon-right icon-active" title="Toggle theme" src="/icons/sun.png" alt="Sun";
|
||||
}
|
||||
},
|
||||
),
|
||||
// otherwise, assume we're using light mode
|
||||
_ => (
|
||||
"/icons/hermies_hex.svg",
|
||||
html! {
|
||||
a class="nav-item" href="/theme?theme=dark" {
|
||||
img class="icon-medium nav-icon-right icon-active" title="Toggle theme" src="/icons/moon.png" alt="Moon";
|
||||
}
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
html! {
|
||||
(PreEscaped("<!-- Top navigation bar -->"))
|
||||
nav class="nav-bar" {
|
||||
a class="nav-item" href=[back] title="Back" {
|
||||
img class="icon-medium nav-icon-left icon-active" src="/icons/back.svg" alt="Back";
|
||||
}
|
||||
h1 class="nav-title" { (title) }
|
||||
a class="nav-item" id="logoutButton" href="/logout" title="Logout" {
|
||||
img class="icon-medium nav-icon-right icon-active" src="/icons/enter.svg" alt="Enter";
|
||||
}
|
||||
}
|
||||
(PreEscaped("<!-- Main content container -->"))
|
||||
main { (main) }
|
||||
(PreEscaped("<!-- Bottom navigation bar -->"))
|
||||
nav class="nav-bar" {
|
||||
a class="nav-item" href="https://scuttlebutt.nz/" {
|
||||
img class="icon-medium nav-icon-left" title="Scuttlebutt Website" src=(hermies) alt="Secure Scuttlebutt";
|
||||
}
|
||||
a class="nav-item" href="/" {
|
||||
img class="icon nav-icon-left" src="/icons/peach-icon.png" alt="PeachCloud" title="Home";
|
||||
}
|
||||
// render the pre-defined theme-switcher icon
|
||||
(switcher)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,30 @@
|
|||
use log::info;
|
||||
|
||||
use crate::THEME;
|
||||
|
||||
// THEME FUNCTIONS
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Theme {
|
||||
Light,
|
||||
Dark,
|
||||
}
|
||||
|
||||
pub fn get_theme() -> String {
|
||||
let current_theme = THEME.read().unwrap();
|
||||
match *current_theme {
|
||||
Theme::Dark => "dark".to_string(),
|
||||
_ => "light".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_theme(theme: Theme) {
|
||||
info!("set ui theme to: {:?}", theme);
|
||||
let mut writable_theme = THEME.write().unwrap();
|
||||
*writable_theme = theme;
|
||||
}
|
||||
|
||||
/*
|
||||
pub mod monitor;
|
||||
|
||||
use std::io::prelude::*;
|
||||
|
@ -84,28 +111,6 @@ pub async fn write_blob_to_store(file: &mut TempFile<'_>) -> Result<String, Peac
|
|||
Ok(blob_id)
|
||||
}
|
||||
|
||||
// THEME FUNCTIONS
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Theme {
|
||||
Light,
|
||||
Dark,
|
||||
}
|
||||
|
||||
pub fn get_theme() -> String {
|
||||
let current_theme = THEME.read().unwrap();
|
||||
match *current_theme {
|
||||
Theme::Dark => "dark".to_string(),
|
||||
_ => "light".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_theme(theme: Theme) {
|
||||
info!("set ui theme to: {:?}", theme);
|
||||
let mut writable_theme = THEME.write().unwrap();
|
||||
*writable_theme = theme;
|
||||
}
|
||||
|
||||
// HELPER FUNCTIONS
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
|
@ -122,3 +127,4 @@ pub enum TemplateOrRedirect {
|
|||
Template(Template),
|
||||
Redirect(Redirect),
|
||||
}
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue