got basic html templating engine working

This commit is contained in:
Brian Picciano 2023-05-11 19:34:05 +02:00
parent 742523d0a0
commit 21b5b99022
7 changed files with 637 additions and 8 deletions

102
Cargo.lock generated
View File

@ -597,8 +597,11 @@ dependencies = [
"clap", "clap",
"futures", "futures",
"gix", "gix",
"handlebars",
"hex", "hex",
"mime_guess",
"mockall", "mockall",
"rust-embed",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
@ -1251,6 +1254,21 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "handlebars"
version = "4.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d"
dependencies = [
"log",
"pest",
"pest_derive",
"rust-embed",
"serde",
"serde_json",
"thiserror",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
@ -1831,6 +1849,50 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "pest"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70"
dependencies = [
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.15",
]
[[package]]
name = "pest_meta"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411"
dependencies = [
"once_cell",
"pest",
"sha2",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.0.12" version = "1.0.12"
@ -2147,6 +2209,40 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "rust-embed"
version = "6.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b68543d5527e158213414a92832d2aab11a84d2571a5eb021ebe22c43aab066"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "6.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d4e0f0ced47ded9a68374ac145edd65a6c1fa13a96447b873660b2a568a0fd7"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn 1.0.109",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "7.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512b0ab6853f7e14e3c8754acb43d6f748bb9ced66aa5915a6553ac8213f7731"
dependencies = [
"sha2",
"walkdir",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.37.18" version = "0.37.18"
@ -2698,6 +2794,12 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "ucd-trie"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
[[package]] [[package]]
name = "uluru" name = "uluru"
version = "3.0.0" version = "3.0.0"

View File

@ -26,3 +26,6 @@ signal-hook = "0.3.15"
futures = "0.3.28" futures = "0.3.28"
signal-hook-tokio = { version = "0.3.1", features = [ "futures-v0_3" ]} signal-hook-tokio = { version = "0.3.1", features = [ "futures-v0_3" ]}
clap = { version = "4.2.7", features = ["derive", "env"] } clap = { version = "4.2.7", features = ["derive", "env"] }
handlebars = { version = "4.3.7", features = [ "rust-embed" ]}
rust-embed = "6.6.1"
mime_guess = "2.0.4"

View File

@ -65,7 +65,7 @@ async fn main() {
let manager = gateway::domain::manager::new(origin_store, domain_config_store, domain_checker); let manager = gateway::domain::manager::new(origin_store, domain_config_store, domain_checker);
let service = gateway::service::new(manager); let service = gateway::service::new(manager).expect("service initialized");
let (addr, server) = let (addr, server) =
warp::serve(service).bind_with_graceful_shutdown(([127, 0, 0, 1], 3030), async { warp::serve(service).bind_with_graceful_shutdown(([127, 0, 0, 1], 3030), async {

View File

@ -1,5 +1,12 @@
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::sync;
use warp::Filter; use warp::Filter;
use crate::domain;
pub mod http_tpl;
/* /*
* POST /domain/init (domain, config) -> token * POST /domain/init (domain, config) -> token
* POST /domain/config (domain, config) * POST /domain/config (domain, config)
@ -7,11 +14,55 @@ use warp::Filter;
* GET /domains * GET /domains
*/ */
pub fn new( type Handlebars<'a> = sync::Arc<handlebars::Handlebars<'a>>;
_manager: impl crate::domain::manager::Manager,
) -> impl warp::Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone { fn render<'a, T: 'a>(hbs: Handlebars<'a>, name: &'a str, value: &'a T) -> impl warp::Reply
warp::path("hello") where
.and(warp::path("world")) T: Serialize,
.and(warp::path::param()) {
.map(|name: String| format!("Hello, {}!", name)) // TODO deal with 404
let render = hbs
.render(name, value)
.unwrap_or_else(|err| err.to_string());
let content_type = mime_guess::from_path(name)
.first_or_octet_stream()
.to_string();
warp::reply::with_header(warp::reply::html(render), "Content-Type", content_type)
}
#[derive(Deserialize)]
struct DomainInitRequest {
//config: domain::config::Config,
}
pub fn new(
_manager: impl domain::manager::Manager,
) -> Result<
impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone,
Box<dyn Error>,
> {
let hbs = sync::Arc::new(self::http_tpl::get()?);
let with_hbs = warp::any().map(move || hbs.clone());
let index = warp::get()
.and(warp::path::end())
.and(with_hbs.clone())
.map(|hbs: Handlebars<'_>| render(hbs, "/index.html", &()));
let static_dir = warp::get()
.and(warp::path("static"))
.and(warp::path::full())
.and(with_hbs.clone())
.map(|full: warp::path::FullPath, hbs: Handlebars<'_>| render(hbs, full.as_str(), &()));
//filter = warp::path!("domain" / "init").and(warp::post())
// .and(warp::query::<DomainInitRequest>())
// .map(|q: DomainInitRequest| {
// let config_hash = q.config.hash().expect("TODO");
// warp::reply::html(config_hash)
// });
Ok(index.or(static_dir))
} }

12
src/service/http_tpl.rs Normal file
View File

@ -0,0 +1,12 @@
use handlebars::{Handlebars, TemplateError};
#[derive(rust_embed::RustEmbed)]
#[folder = "src/service/http_tpl"]
#[prefix = "/"]
struct Dir;
pub fn get() -> Result<Handlebars<'static>, TemplateError> {
let mut reg = Handlebars::new();
reg.register_embed_templates::<Dir>()?;
Ok(reg)
}

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Hello, world!</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<!--
<meta name="description" content="" />
<link rel="icon" href="favicon.png">
-->
<link rel="stylesheet" type="text/css" href="/static/new.css" />
</head>
<body>
<h1>OK</h1>
</body>
</html>

View File

@ -0,0 +1,432 @@
:root {
--nc-font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
--nc-font-mono: Consolas, monaco, 'Ubuntu Mono', 'Liberation Mono', 'Courier New', Courier, monospace;
--nc-tx-1: #000000;
--nc-tx-2: #1A1A1A;
--nc-bg-1: #FFFFFF;
--nc-bg-2: #F6F8FA;
--nc-bg-3: #E5E7EB;
--nc-lk-1: #0070F3;
--nc-lk-2: #0366D6;
--nc-lk-tx: #FFFFFF;
--nc-ac-1: #79FFE1;
--nc-ac-tx: #0C4047;
}
@media (prefers-color-scheme: dark) {
:root {
--nc-tx-1: #ffffff;
--nc-tx-2: #eeeeee;
--nc-bg-1: #000000;
--nc-bg-2: #111111;
--nc-bg-3: #222222;
--nc-lk-1: #3291FF;
--nc-lk-2: #0070F3;
--nc-lk-tx: #FFFFFF;
--nc-ac-1: #7928CA;
--nc-ac-tx: #FFFFFF;
}
}
* {
/* Reset margins and padding */
margin: 0;
padding: 0;
}
address,
area,
article,
aside,
audio,
blockquote,
datalist,
details,
dl,
fieldset,
figure,
form,
input,
iframe,
img,
meter,
nav,
ol,
optgroup,
option,
output,
p,
pre,
progress,
ruby,
section,
table,
textarea,
ul,
video {
/* Margins for most elements */
margin-bottom: 1rem;
}
html,input,select,button {
/* Set body font family and some finicky elements */
font-family: var(--nc-font-sans);
}
body {
/* Center body in page */
margin: 0 auto;
max-width: 750px;
padding: 2rem;
border-radius: 6px;
overflow-x: hidden;
word-break: break-word;
overflow-wrap: break-word;
background: var(--nc-bg-1);
/* Main body text */
color: var(--nc-tx-2);
font-size: 1.03rem;
line-height: 1.5;
}
::selection {
/* Set background color for selected text */
background: var(--nc-ac-1);
color: var(--nc-ac-tx);
}
h1,h2,h3,h4,h5,h6 {
line-height: 1;
color: var(--nc-tx-1);
padding-top: .875rem;
}
h1,
h2,
h3 {
color: var(--nc-tx-1);
padding-bottom: 2px;
margin-bottom: 8px;
border-bottom: 1px solid var(--nc-bg-2);
}
h4,
h5,
h6 {
margin-bottom: .3rem;
}
h1 {
font-size: 2.25rem;
}
h2 {
font-size: 1.85rem;
}
h3 {
font-size: 1.55rem;
}
h4 {
font-size: 1.25rem;
}
h5 {
font-size: 1rem;
}
h6 {
font-size: .875rem;
}
a {
color: var(--nc-lk-1);
}
a:hover {
color: var(--nc-lk-2);
}
abbr:hover {
/* Set the '?' cursor while hovering an abbreviation */
cursor: help;
}
blockquote {
padding: 1.5rem;
background: var(--nc-bg-2);
border-left: 5px solid var(--nc-bg-3);
}
abbr {
cursor: help;
}
blockquote *:last-child {
padding-bottom: 0;
margin-bottom: 0;
}
header {
background: var(--nc-bg-2);
border-bottom: 1px solid var(--nc-bg-3);
padding: 2rem 1.5rem;
/* This sets the right and left margins to cancel out the body's margins. It's width is still the same, but the background stretches across the page's width. */
margin: -2rem calc(0px - (50vw - 50%)) 2rem;
/* Shorthand for:
margin-top: -2rem;
margin-bottom: 2rem;
margin-left: calc(0px - (50vw - 50%));
margin-right: calc(0px - (50vw - 50%)); */
padding-left: calc(50vw - 50%);
padding-right: calc(50vw - 50%);
}
header h1,
header h2,
header h3 {
padding-bottom: 0;
border-bottom: 0;
}
header > *:first-child {
margin-top: 0;
padding-top: 0;
}
header > *:last-child {
margin-bottom: 0;
}
a button,
button,
input[type="submit"],
input[type="reset"],
input[type="button"] {
font-size: 1rem;
display: inline-block;
padding: 6px 12px;
text-align: center;
text-decoration: none;
white-space: nowrap;
background: var(--nc-lk-1);
color: var(--nc-lk-tx);
border: 0;
border-radius: 4px;
box-sizing: border-box;
cursor: pointer;
color: var(--nc-lk-tx);
}
a button[disabled],
button[disabled],
input[type="submit"][disabled],
input[type="reset"][disabled],
input[type="button"][disabled] {
cursor: default;
opacity: .5;
/* Set the [X] cursor while hovering a disabled link */
cursor: not-allowed;
}
.button:focus,
.button:hover,
button:focus,
button:hover,
input[type="submit"]:focus,
input[type="submit"]:hover,
input[type="reset"]:focus,
input[type="reset"]:hover,
input[type="button"]:focus,
input[type="button"]:hover {
background: var(--nc-lk-2);
}
code,
pre,
kbd,
samp {
/* Set the font family for monospaced elements */
font-family: var(--nc-font-mono);
}
code,
samp,
kbd,
pre {
/* The main preformatted style. This is changed slightly across different cases. */
background: var(--nc-bg-2);
border: 1px solid var(--nc-bg-3);
border-radius: 4px;
padding: 3px 6px;
font-size: 0.9rem;
}
kbd {
/* Makes the kbd element look like a keyboard key */
border-bottom: 3px solid var(--nc-bg-3);
}
pre {
padding: 1rem 1.4rem;
max-width: 100%;
overflow: auto;
}
pre code {
/* When <code> is in a <pre>, reset it's formatting to blend in */
background: inherit;
font-size: inherit;
color: inherit;
border: 0;
padding: 0;
margin: 0;
}
code pre {
/* When <pre> is in a <code>, reset it's formatting to blend in */
display: inline;
background: inherit;
font-size: inherit;
color: inherit;
border: 0;
padding: 0;
margin: 0;
}
details {
/* Make the <details> look more "clickable" */
padding: .6rem 1rem;
background: var(--nc-bg-2);
border: 1px solid var(--nc-bg-3);
border-radius: 4px;
}
summary {
/* Makes the <summary> look more like a "clickable" link with the pointer cursor */
cursor: pointer;
font-weight: bold;
}
details[open] {
/* Adjust the <details> padding while open */
padding-bottom: .75rem;
}
details[open] summary {
/* Adjust the <details> padding while open */
margin-bottom: 6px;
}
details[open]>*:last-child {
/* Resets the bottom margin of the last element in the <details> while <details> is opened. This prevents double margins/paddings. */
margin-bottom: 0;
}
dt {
font-weight: bold;
}
dd::before {
/* Add an arrow to data table definitions */
content: '→ ';
}
hr {
/* Reset the border of the <hr> separator, then set a better line */
border: 0;
border-bottom: 1px solid var(--nc-bg-3);
margin: 1rem auto;
}
fieldset {
margin-top: 1rem;
padding: 2rem;
border: 1px solid var(--nc-bg-3);
border-radius: 4px;
}
legend {
padding: auto .5rem;
}
table {
/* border-collapse sets the table's elements to share borders, rather than floating as separate "boxes". */
border-collapse: collapse;
width: 100%
}
td,
th {
border: 1px solid var(--nc-bg-3);
text-align: left;
padding: .5rem;
}
th {
background: var(--nc-bg-2);
}
tr:nth-child(even) {
/* Set every other cell slightly darker. Improves readability. */
background: var(--nc-bg-2);
}
table caption {
font-weight: bold;
margin-bottom: .5rem;
}
textarea {
/* Don't let the <textarea> extend off the screen naturally or when dragged by the user */
max-width: 100%;
}
ol,
ul {
/* Replace the browser default padding */
padding-left: 2rem;
}
li {
margin-top: .4rem;
}
ul ul,
ol ul,
ul ol,
ol ol {
margin-bottom: 0;
}
mark {
padding: 3px 6px;
background: var(--nc-ac-1);
color: var(--nc-ac-tx);
}
textarea,
select,
input {
padding: 6px 12px;
margin-bottom: .5rem;
background: var(--nc-bg-2);
color: var(--nc-tx-2);
border: 1px solid var(--nc-bg-3);
border-radius: 4px;
box-shadow: none;
box-sizing: border-box;
}
img {
max-width: 100%;
}