From 201a37e8d3d0ef4c59b184c0707ec8ddd301a25a Mon Sep 17 00:00:00 2001 From: "Adam T. Carpenter" Date: Fri, 9 Apr 2021 18:50:55 -0400 Subject: spruced up templates, some repo impl --- Cargo.toml | 10 +-- artifacts/ui-demo.html | 119 +++++++++++++++------------ src/config.rs | 2 +- src/handlers/mod.rs | 172 --------------------------------------- src/main.rs | 13 ++- src/repo/constants.rs | 9 -- src/repo/mod.rs | 28 ++++++- src/routes/mod.rs | 185 ++++++++++++++++++++++++++++++++++++++++++ src/templates/base.hbs | 155 +++++++++++++++++++++++++++++++++++ src/templates/index.hbs | 74 +++++++++++++++++ src/templates/index.html | 85 ------------------- src/templates/login.hbs | 7 ++ src/templates/login.html | 6 -- src/templates/mod.rs | 25 ++++-- src/templates/suggestions.hbs | 9 ++ 15 files changed, 550 insertions(+), 349 deletions(-) delete mode 100644 src/handlers/mod.rs create mode 100644 src/routes/mod.rs create mode 100644 src/templates/base.hbs create mode 100644 src/templates/index.hbs delete mode 100644 src/templates/index.html create mode 100644 src/templates/login.hbs delete mode 100644 src/templates/login.html create mode 100644 src/templates/suggestions.hbs diff --git a/Cargo.toml b/Cargo.toml index 465754c..13b4535 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,14 +5,14 @@ authors = ["Adam T. Carpenter "] edition = "2018" [dependencies] -bincode = "1.3" +bincode = "1" env_logger = "0.8" -handlebars = "3.5" +handlebars = "3" hyper = { version = "0.14", default-features = false, features = ["full"] } hyper-rustls = "0.22" log = "0.4" -once_cell = "1.7" -serde = { version = "1.0", features = ["derive"]} +once_cell = "1" +serde = { version = "1", features = ["derive"]} serde_urlencoded = "0.7" sled = "0.34" -tokio = { version = "1.2", default-features = false, features = ["full"] } +tokio = { version = "1", default-features = false, features = ["full"] } diff --git a/artifacts/ui-demo.html b/artifacts/ui-demo.html index 6fe3a18..09dc9cb 100644 --- a/artifacts/ui-demo.html +++ b/artifacts/ui-demo.html @@ -11,6 +11,10 @@ padding: 0; } + .model::before { + content: "➥"; + } + .topbar { background-color: var(--balboa); box-shadow: 0 0 0.5em black; @@ -31,6 +35,10 @@ padding: 14px 14px; } + .topbar li.right { + float: right; + } + .topbar li a { color: white; text-decoration: none; @@ -42,38 +50,46 @@ z-index: -1; position: fixed; top: 3em; - left: -20%; + left: 0; width: 20%; height: 100%; transition: 0.25s; } - .sidebar ul { - list-style-type: none; + .sidebar button { + display: block; + background: none; + border: 0 none; + cursor: pointer; + color: white; } - .sidebar a { - color: white; - text-decoration: none; + .model { + margin-left: 20px; } - #menu:target .sidebar { - left: 0; + .model::before { + content: "➥"; } - .menuToggle a.closeMenu { - display: none; + #hidden:target .sidebar { + left: -20%; } - #menu:target .menuToggle a.closeMenu { - display: block; + + .menuToggle a.openMenu { + display: none; } - #menu:target .menuToggle a.openMenu { + #hidden:target .menuToggle a.closeMenu { display: none; } - #menu:target section { - margin-left: 20%; + #hidden:target .menuToggle a.openMenu { + display: block; + } + + #hidden:target section { + margin-left: 0; } .catalogTable { @@ -105,6 +121,7 @@ } section { + margin-left: 20%; transition: margin-left 0.25s; padding-top: 2em; padding-left: 2em; @@ -112,12 +129,12 @@ } - +
- +
- - - - - - - - + {{#each parts}} - - - - - - - - - - + + {{/each}}
MakeModelEngine CIDYear
HudsonWasp262ci1952
HudsonHornet308ci1953{{ this }}
diff --git a/src/config.rs b/src/config.rs index 7798967..b3c9715 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use once_cell::sync::Lazy; use std::{env::var, error::Error, net::SocketAddr}; -pub static INSTANCE: Lazy = +pub static CONFIG_INSTANCE: Lazy = Lazy::new(|| AppConfig::from_env().expect("Error loading config")); #[derive(Clone, Debug)] diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs deleted file mode 100644 index 3ad7f49..0000000 --- a/src/handlers/mod.rs +++ /dev/null @@ -1,172 +0,0 @@ -use crate::templates; -use handlebars::Handlebars; -use hyper::body::HttpBody as _; -use hyper::Method; -use hyper::StatusCode; -use hyper::{Body, Request, Response}; -use serde::{Deserialize, Serialize}; -use sled::Db; -use std::convert::Infallible; // TODO: - -pub async fn router(req: Request) -> Result, Infallible> { - match (req.method(), req.uri().path()) { - (&Method::GET, "/") | (&Method::GET, "/index.html") => index(req).await, - (&Method::GET, "/login") | (&Method::GET, "/login/index.html") => login().await, - (&Method::GET, "/login/google") => login_google().await, - (&Method::GET, "/login/google/code") => login_google_code(req).await, - _ => Ok(Response::builder() - .status(StatusCode::NOT_FOUND) - .body("Not found.".into()) - .unwrap()), - } -} - -async fn login() -> Result, Infallible> { - Ok(Response::new(templates::LOGIN_T.into())) -} - -async fn login_google() -> Result, Infallible> { - let uri = hyper::Uri::builder() - .scheme("https") - .authority("accounts.google.com") - .path_and_query(format!( - "{}?client_id={}&redirect_uri={}&response_type={}&scope={}&state={}", - "/o/oauth2/v2/auth", - "???", - "http://localhost:3000/login/google/code", - "code", - "openid%20profile%20email", - "BLARGH" - )) - .build() - .unwrap(); - let resp = Response::builder() - .header(hyper::header::LOCATION, uri.to_string()) - .status(StatusCode::TEMPORARY_REDIRECT) - .body("".into()) - .unwrap(); - Ok(resp) -} - -#[derive(Serialize)] -struct AuthRequest { - client_id: String, - client_secret: String, - grant_type: &'static str, - redirect_uri: String, - code: String, -} - -impl Default for AuthRequest { - fn default() -> Self { - Self { - client_id: "???".into(), - client_secret: "???".into(), - grant_type: "authorization_code", - redirect_uri: String::new(), - code: String::new(), - } - } -} - -async fn login_google_code(req: Request) -> Result, Infallible> { - let query = req.uri().query().unwrap_or_default(); - let query = serde_urlencoded::de::from_str::(query).unwrap(); - if query.state != "BLARGH" { - dbg!("tampering?"); - } - - // get access token - let auth_body = AuthRequest { - code: query.code, - redirect_uri: "http://localhost:3000/login/google/code".to_owned(), - ..Default::default() - }; - - let https = hyper_rustls::HttpsConnector::with_native_roots(); - let client: hyper::Client<_, hyper::Body> = hyper::Client::builder().build(https); - - let req = Request::builder() - .method(Method::POST) - .uri("https://oauth2.googleapis.com/token") - .header( - hyper::header::CONTENT_TYPE, - "application/x-www-form-urlencoded", - ) - .body(serde_urlencoded::ser::to_string(auth_body).unwrap().into()) - .unwrap(); - let mut resp = client.request(req).await.unwrap(); - use std::io::prelude::*; - while let Some(chunk) = resp.body_mut().data().await { - std::io::stdout().write_all(&chunk.unwrap()).unwrap(); - } - - let resp = Response::builder() - .header(hyper::header::LOCATION, "/".to_string()) - .status(StatusCode::TEMPORARY_REDIRECT) - .body("Successful login, redirecting...".into()) - .unwrap(); - Ok(resp) -} - -pub struct AccessToken { - access_token: String, - expires_in: String, - scope: String, - token_type: String, -} - -async fn index(req: Request) -> Result, Infallible> { - let query = req.uri().query().unwrap_or_default(); - let filter = serde_urlencoded::de::from_str::(query).unwrap(); - let mut reg = Handlebars::new(); - reg.register_template_string("index", templates::INDEX_T) - .unwrap(); - let mut data = PartsData::default(); - data.makes = Some(vec!["Hudson".into(), "Essex".into(), "Terraplane".into()]); - - if let Some(make) = filter.make { - if make.eq("Hudson") { - data.models = Some(vec!["Hornet".into(), "Wasp".into(), "Jet".into()]); - } else if make.eq("Essex") { - data.models = Some(vec!["Super Six".into()]); - } - data.selected_make = Some(make); - } - - data.parts = Some(Vec::new()); - if let Some(model) = filter.model { - data.parts = Some(vec!["1".into(), "2".into(), "3".into()]); - data.selected_model = Some(model); - } - - let result = reg.render("index", &data).unwrap(); - Ok(Response::new(result.into())) -} - -#[derive(Debug, Deserialize)] -struct GoogleOAuthQuery { - code: String, - state: String, -} - -#[derive(Debug, Deserialize)] -struct PartsQuery { - make: Option, - year: Option, - model: Option, - engine: Option, -} - -#[derive(Debug, Serialize, Default)] -struct PartsData { - makes: Option>, - years: Option>, - models: Option>, - engines: Option>, - selected_make: Option, - selected_year: Option, - selected_model: Option, - selected_engine: Option, - parts: Option>, -} diff --git a/src/main.rs b/src/main.rs index 1947ff8..4a5a8b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,28 +1,27 @@ +use crate::config::CONFIG_INSTANCE; +use crate::error::TwinHError; use hyper::{ service::{make_service_fn, service_fn}, Server, }; -use std::convert::Infallible; // TODO: mod config; mod error; -mod handlers; mod models; mod repo; +mod routes; mod templates; #[tokio::main] async fn main() -> Result<(), Box> { - let addr = config::INSTANCE.addr; + let addr = CONFIG_INSTANCE.addr; - let make_svc = make_service_fn(move |_conn| async { - Ok::<_, Infallible>(service_fn(|req| handlers::router(req))) - }); + let make_svc = + make_service_fn(move |_conn| async { Ok::<_, TwinHError>(service_fn(routes::router)) }); let server = Server::bind(&addr).serve(make_svc); let graceful = server.with_graceful_shutdown(shutdown_signal()); - // run until shutdown signal if let Err(e) = graceful.await { eprintln!("server error: {}", e); } diff --git a/src/repo/constants.rs b/src/repo/constants.rs index e168a77..6da641f 100644 --- a/src/repo/constants.rs +++ b/src/repo/constants.rs @@ -1,11 +1,2 @@ -use once_cell::sync::Lazy; -use sled::{Config, Db}; - pub const PARTS_TREE: &str = "parts"; pub const CARS_TREE: &str = "cars"; -pub static REPO_INSTANCE: Lazy = Lazy::new(|| { - Config::default() - .path("/tmp/twinh") - .open() - .expect("Couldn't open DB!") -}); diff --git a/src/repo/mod.rs b/src/repo/mod.rs index d4c5e44..f0bb95c 100644 --- a/src/repo/mod.rs +++ b/src/repo/mod.rs @@ -1,16 +1,36 @@ +use crate::config::CONFIG_INSTANCE; use crate::error::TwinHError; use crate::models::Car; use crate::models::Part; use bincode::deserialize; use constants::*; +use once_cell::sync::Lazy; +use sled::{Config, Db}; mod constants; +static REPO_INSTANCE: Lazy = Lazy::new(|| { + Config::default() + .path(&CONFIG_INSTANCE.db_uri) + .temporary(true) + .open() + .expect("Couldn't open DB!") +}); + +pub fn create_demo_db() -> Result<(), TwinHError> { + let db = sled::Config::default() + .path(&CONFIG_INSTANCE.db_uri) + .create_new(true) + .open()?; + let cars_tree = db.open_tree(CARS_TREE)?; + Ok(()) +} + pub fn create_new_db() -> Result<(), TwinHError> { - let config = sled::Config::default() - .path("/var/db/twinh") - .create_new(true); - let db = config.open()?; + sled::Config::default() + .path(&CONFIG_INSTANCE.db_uri) + .create_new(true) + .open()?; Ok(()) } diff --git a/src/routes/mod.rs b/src/routes/mod.rs new file mode 100644 index 0000000..1866e49 --- /dev/null +++ b/src/routes/mod.rs @@ -0,0 +1,185 @@ +use crate::error::TwinHError; +use crate::repo; +use crate::templates; +use hyper::body::HttpBody as _; +use hyper::Method; +use hyper::StatusCode; +use hyper::{Body, Request, Response}; +use serde::{Deserialize, Serialize}; + +pub async fn router(req: Request) -> Result, TwinHError> { + match (req.method(), req.uri().path()) { + (&Method::GET, "/") => index(req).await, + (&Method::GET, "/cars") => cars(req).await, + (&Method::GET, "/login") => login().await, + (&Method::GET, "/login/google") => login_google().await, + (&Method::GET, "/login/google/code") => login_google_code(req).await, + (&Method::GET, "/suggestions") => suggestions().await, + _ => Ok(Response::builder() + .status(StatusCode::NOT_FOUND) + .body("Not found.".into()) + .unwrap()), + } +} + +async fn suggestions() -> Result, TwinHError> { + todo!() +} + +async fn login() -> Result, TwinHError> { + Ok(Response::new( + templates::REGISTRY.render("login", &"").unwrap().into(), + )) +} + +async fn login_google() -> Result, TwinHError> { + let uri = hyper::Uri::builder() + .scheme("https") + .authority("accounts.google.com") + .path_and_query(format!( + "{}?client_id={}&redirect_uri={}&response_type={}&scope={}&state={}", + "/o/oauth2/v2/auth", + "???", + "http://localhost:3000/login/google/code", + "code", + "openid%20profile%20email", + "BLARGH" + )) + .build() + .unwrap(); + let resp = Response::builder() + .header(hyper::header::LOCATION, uri.to_string()) + .status(StatusCode::TEMPORARY_REDIRECT) + .body("".into()) + .unwrap(); + Ok(resp) +} + +#[derive(Serialize)] +struct AuthRequest { + client_id: String, + client_secret: String, + grant_type: &'static str, + redirect_uri: String, + code: String, +} + +impl Default for AuthRequest { + fn default() -> Self { + Self { + client_id: "???".into(), + client_secret: "???".into(), + grant_type: "authorization_code", + redirect_uri: String::new(), + code: String::new(), + } + } +} + +async fn login_google_code(req: Request) -> Result, TwinHError> { + let query = req.uri().query().unwrap_or_default(); + let query = serde_urlencoded::de::from_str::(query).unwrap(); + if query.state != "BLARGH" { + dbg!("tampering?"); + } + + // get access token + let auth_body = AuthRequest { + code: query.code, + redirect_uri: "http://localhost:3000/login/google/code".to_owned(), + ..Default::default() + }; + + let https = hyper_rustls::HttpsConnector::with_native_roots(); + let client: hyper::Client<_, hyper::Body> = hyper::Client::builder().build(https); + + let req = Request::builder() + .method(Method::POST) + .uri("https://oauth2.googleapis.com/token") + .header( + hyper::header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ) + .body(serde_urlencoded::ser::to_string(auth_body).unwrap().into()) + .unwrap(); + let mut resp = client.request(req).await.unwrap(); + use std::io::prelude::*; + while let Some(chunk) = resp.body_mut().data().await { + std::io::stdout().write_all(&chunk.unwrap()).unwrap(); + } + + let resp = Response::builder() + .header(hyper::header::LOCATION, "/".to_string()) + .status(StatusCode::TEMPORARY_REDIRECT) + .body("Successful login, redirecting...".into()) + .unwrap(); + Ok(resp) +} + +pub struct AccessToken { + access_token: String, + expires_in: String, + scope: String, + token_type: String, +} + +async fn cars(req: Request) -> Result, TwinHError> { + let cars = repo::get_all_cars().unwrap(); + todo!() +} + +async fn index(req: Request) -> Result, TwinHError> { + let query = req.uri().query().unwrap_or_default(); + let filter = serde_urlencoded::de::from_str::(query).unwrap(); + let mut data = PartsData::default(); + data.makes = Some(vec!["Hudson".into(), "Essex".into(), "Terraplane".into()]); + + if let Some(make) = filter.make { + if make.eq("Hudson") { + data.models = Some(vec!["Hornet".into(), "Wasp".into(), "Jet".into()]); + } else if make.eq("Essex") { + data.models = Some(vec!["Super Six".into()]); + } + data.selected_make = Some(make); + } + + data.parts = Some(Vec::new()); + if let Some(model) = filter.model { + data.parts = Some(vec!["1".into(), "2".into(), "3".into()]); + data.selected_model = Some(model); + } + + let body = templates::REGISTRY.render("index", &data).unwrap(); + let resp = Response::builder() + .header(hyper::header::CONTENT_TYPE, "text/html; charset=utf-8") + .body(body.into()) + .unwrap(); + Ok(resp) +} + +#[derive(Debug, Deserialize)] +struct GoogleOAuthQuery { + code: String, + state: String, +} + +#[derive(Debug, Deserialize)] +struct PartsQuery { + make: Option, + year: Option, + model: Option, + engine: Option, +} + +#[derive(Debug, Serialize, Default)] +struct PartsData { + makes: Option>, + years: Option>, + models: Option>, + engines: Option>, + selected_make: Option, + selected_year: Option, + selected_model: Option, + selected_engine: Option, + parts: Option>, +} diff --git a/src/templates/base.hbs b/src/templates/base.hbs new file mode 100644 index 0000000..df3ba9d --- /dev/null +++ b/src/templates/base.hbs @@ -0,0 +1,155 @@ + + + + + + + + + + + {{~> body}} + + + diff --git a/src/templates/index.hbs b/src/templates/index.hbs new file mode 100644 index 0000000..6bd539b --- /dev/null +++ b/src/templates/index.hbs @@ -0,0 +1,74 @@ +{{#> base}} +{{#*inline "navItem"}} +
  • + +
  • +{{/inline}} +{{#*inline "body"}} + + +
    +
    + {{#if selected_make}} + + {{/if}} + {{#if selected_model}} + + {{/if}} + +
    +
    + +
    + + + {{#each parts}} + + + + {{/each}} + +
    {{ this }}
    +
    +{{/inline}} +{{/base}} diff --git a/src/templates/index.html b/src/templates/index.html deleted file mode 100644 index 4663f5d..0000000 --- a/src/templates/index.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - -
    -
    - - - - -
    - - {{#each parts}} - - - - {{/each}} -
    {{ this }}
    -
    - - diff --git a/src/templates/login.hbs b/src/templates/login.hbs new file mode 100644 index 0000000..84b354b --- /dev/null +++ b/src/templates/login.hbs @@ -0,0 +1,7 @@ +{{#> base}} +{{#*inline "body"}} + + Login with Google + +{{/inline}} +{{/base}} diff --git a/src/templates/login.html b/src/templates/login.html deleted file mode 100644 index 9329a22..0000000 --- a/src/templates/login.html +++ /dev/null @@ -1,6 +0,0 @@ - - - - Login with Google - - diff --git a/src/templates/mod.rs b/src/templates/mod.rs index 3a6eb2b..534ada0 100644 --- a/src/templates/mod.rs +++ b/src/templates/mod.rs @@ -1,9 +1,16 @@ -pub static INDEX_T: &str = include_str!("index.html"); -pub static LOGIN_T: &str = include_str!("login.html"); -pub const CATALOG_L: &str = "/#menu"; -pub const PARTS_L: &str = "/parts"; -pub const PARTS_CSV_L: &str = "/parts.csv"; -pub const CARS_L: &str = "/cars"; -pub const CARS_CSV_L: &str = "/cars.csv"; -pub const SUGGESTIONS_L: &str = "/suggestions"; -pub const LOGIN_L: &str = "/login"; +use handlebars::Handlebars; +use once_cell::sync::Lazy; + +pub static REGISTRY: Lazy = Lazy::new(|| { + let mut handlebars = Handlebars::new(); + handlebars + .register_template_string("index", include_str!("index.hbs")) + .unwrap(); + handlebars + .register_template_string("login", include_str!("login.hbs")) + .unwrap(); + handlebars + .register_template_string("base", include_str!("base.hbs")) + .unwrap(); + handlebars +}); diff --git a/src/templates/suggestions.hbs b/src/templates/suggestions.hbs new file mode 100644 index 0000000..f979be7 --- /dev/null +++ b/src/templates/suggestions.hbs @@ -0,0 +1,9 @@ +{{#> base}} +{{#*inline "body"}} +
    +
    + +
    +
    +{{/inline}} +{{/base}} -- cgit v1.2.3