mirror of
https://codeberg.org/demostf/frontend.git
synced 2026-06-03 18:24:12 +02:00
asset work
This commit is contained in:
parent
6485612898
commit
1648eeffab
6 changed files with 144 additions and 46 deletions
39
Cargo.lock
generated
39
Cargo.lock
generated
|
|
@ -269,6 +269,12 @@ dependencies = [
|
||||||
"yaml-rust",
|
"yaml-rust",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-fnv1a-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-str"
|
name = "const-str"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
|
@ -278,6 +284,12 @@ dependencies = [
|
||||||
"const-str-proc-macro",
|
"const-str-proc-macro",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-str"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b62c6d3ea43cbe0bc5a081f276fd477e4291d168aacc9f9d98073325333c0d4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-str-proc-macro"
|
name = "const-str-proc-macro"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
|
@ -289,6 +301,21 @@ dependencies = [
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const_base"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b64b297eeb71d1ad825d0b78257940c0a4fc10c8ebfc67f38f0daf256f69bdb4"
|
||||||
|
dependencies = [
|
||||||
|
"const_panic",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const_panic"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58baae561b85ca19b3122a9ddd35c8ec40c3bcd14fe89921824eae73f7baffbf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
|
|
@ -582,12 +609,14 @@ dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"base64 0.21.0",
|
"base64 0.21.0",
|
||||||
"config",
|
"config",
|
||||||
|
"const-fnv1a-hash",
|
||||||
|
"const-str 0.5.4",
|
||||||
|
"const_base",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyperlocal",
|
"hyperlocal",
|
||||||
"itertools",
|
"itertools",
|
||||||
"lightningcss",
|
"lightningcss",
|
||||||
"maud",
|
"maud",
|
||||||
"md5",
|
|
||||||
"serde",
|
"serde",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"steamid-ng",
|
"steamid-ng",
|
||||||
|
|
@ -951,7 +980,7 @@ checksum = "6338806873ab8053fa9aac727f603fdd2fe4d8bec192faab114183f8483c67f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"browserslist-rs",
|
"browserslist-rs",
|
||||||
"const-str",
|
"const-str 0.3.2",
|
||||||
"cssparser",
|
"cssparser",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
|
|
@ -1063,12 +1092,6 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "md5"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,12 @@ hyperlocal = "0.8.0"
|
||||||
tower-http = { version = "0.4.0", features = ["trace"] }
|
tower-http = { version = "0.4.0", features = ["trace"] }
|
||||||
steamid-ng = "1.0.0"
|
steamid-ng = "1.0.0"
|
||||||
itertools = "0.10.5"
|
itertools = "0.10.5"
|
||||||
|
const-fnv1a-hash = "1.1.0"
|
||||||
|
const_base = "0.2.0"
|
||||||
|
const-str = "0.5.4"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
lightningcss = { version = "1.0.0-alpha.40", features = ["browserslist", "visitor"] }
|
lightningcss = { version = "1.0.0-alpha.40", features = ["browserslist", "visitor"] }
|
||||||
base64 = "0.21.0"
|
base64 = "0.21.0"
|
||||||
urlencoding = "2.1.2"
|
urlencoding = "2.1.2"
|
||||||
md5 = "0.7.0"
|
const-fnv1a-hash = "1.1.0"
|
||||||
|
|
|
||||||
20
build.rs
20
build.rs
|
|
@ -1,5 +1,6 @@
|
||||||
use base64::engine::general_purpose::STANDARD;
|
use base64::engine::general_purpose::STANDARD;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
|
use const_fnv1a_hash::fnv1a_hash_str_32;
|
||||||
use lightningcss::bundler::{Bundler, FileProvider};
|
use lightningcss::bundler::{Bundler, FileProvider};
|
||||||
use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions};
|
use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions};
|
||||||
use lightningcss::targets::Browsers;
|
use lightningcss::targets::Browsers;
|
||||||
|
|
@ -11,20 +12,23 @@ use std::env::var;
|
||||||
use std::fs::{read, write};
|
use std::fs::{read, write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
fn main() {
|
macro_rules! save_asset {
|
||||||
|
($name:expr, $val:expr) => {
|
||||||
|
let val = $val;
|
||||||
let out_dir = var("OUT_DIR").unwrap();
|
let out_dir = var("OUT_DIR").unwrap();
|
||||||
|
write(format!("{out_dir}/{}", $name), &val).expect("failed to write asset");
|
||||||
|
let hash = fnv1a_hash_str_32(&val);
|
||||||
|
write(format!("{out_dir}/{}.hash", $name), format!("{:x}", hash))
|
||||||
|
.expect("failed to write asset hash");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
println!("cargo:rerun-if-changed=style");
|
println!("cargo:rerun-if-changed=style");
|
||||||
println!("cargo:rerun-if-changed=images");
|
println!("cargo:rerun-if-changed=images");
|
||||||
|
|
||||||
let style = build_style();
|
save_asset!("style.css", build_style());
|
||||||
write(format!("{out_dir}/style.css"), &style).expect("failed to write compiled style");
|
|
||||||
write(
|
|
||||||
format!("{out_dir}/style.md5"),
|
|
||||||
format!("{:x}", md5::compute(&style)),
|
|
||||||
)
|
|
||||||
.expect("failed to write compiled style hash");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_style() -> String {
|
pub fn build_style() -> String {
|
||||||
|
|
|
||||||
87
src/asset.rs
Normal file
87
src/asset.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
use hyper::header::{CACHE_CONTROL, CONTENT_TYPE, ETAG};
|
||||||
|
use hyper::http::{HeaderName, HeaderValue};
|
||||||
|
|
||||||
|
macro_rules! saved_asset {
|
||||||
|
($name:expr) => {
|
||||||
|
include_str!(concat!(env!("OUT_DIR"), "/", $name))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! saved_asset_url {
|
||||||
|
($name:expr) => {
|
||||||
|
concat!("/", $name, "?v=", crate::asset::saved_asset_hash!($name))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! saved_asset_hash {
|
||||||
|
($name:expr) => {
|
||||||
|
concat!(
|
||||||
|
r#"""#,
|
||||||
|
include_str!(concat!(env!("OUT_DIR"), "/", $name, ".hash")),
|
||||||
|
r#"""#
|
||||||
|
)
|
||||||
|
};
|
||||||
|
($name:expr, quoted) => {
|
||||||
|
concat!(r#"""#, crate::asset::saved_asset_hash!($name), r#"""#)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn cache_headers(
|
||||||
|
content_type: &'static str,
|
||||||
|
etag: &'static str,
|
||||||
|
) -> [(HeaderName, HeaderValue); 3] {
|
||||||
|
[
|
||||||
|
(CONTENT_TYPE, HeaderValue::from_static(content_type)),
|
||||||
|
(ETAG, HeaderValue::from_static(etag)),
|
||||||
|
(
|
||||||
|
CACHE_CONTROL,
|
||||||
|
HeaderValue::from_static("public, max-age=2592000, immutable"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn guess_mime(path: &'static str) -> &'static str {
|
||||||
|
use const_str::ends_with;
|
||||||
|
if ends_with!(path, "svg") {
|
||||||
|
return "image/svg+xml";
|
||||||
|
} else if ends_with!(path, "png") {
|
||||||
|
return "image/png";
|
||||||
|
} else if ends_with!(path, "css") {
|
||||||
|
return "text/css";
|
||||||
|
}
|
||||||
|
return "text/plain";
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! serve_static {
|
||||||
|
($name:expr) => {
|
||||||
|
|| async {
|
||||||
|
const CONTENT: &[u8] = include_bytes!($name);
|
||||||
|
const HASH: u32 = const_fnv1a_hash::fnv1a_hash_32(&CONTENT, None);
|
||||||
|
const HASH_S: const_base::ArrayStr<8> = const_base::WrongOutputLength::unwrap(
|
||||||
|
const_base::encode(&HASH.to_le_bytes(), const_base::Config::HEX),
|
||||||
|
);
|
||||||
|
(
|
||||||
|
crate::asset::cache_headers(crate::asset::guess_mime($name), HASH_S.as_str()),
|
||||||
|
CONTENT,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! serve_compiled {
|
||||||
|
($name:expr) => {
|
||||||
|
|| async {
|
||||||
|
let style = crate::asset::saved_asset!($name);
|
||||||
|
let etag = crate::asset::saved_asset_hash!($name, quoted);
|
||||||
|
(
|
||||||
|
crate::asset::cache_headers(crate::asset::guess_mime($name), etag),
|
||||||
|
style,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use saved_asset;
|
||||||
|
pub(crate) use saved_asset_hash;
|
||||||
|
pub(crate) use saved_asset_url;
|
||||||
|
pub(crate) use serve_compiled;
|
||||||
|
pub(crate) use serve_static;
|
||||||
29
src/main.rs
29
src/main.rs
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod asset;
|
||||||
mod config;
|
mod config;
|
||||||
mod data;
|
mod data;
|
||||||
mod error;
|
mod error;
|
||||||
|
|
@ -9,12 +10,12 @@ use crate::data::demo::{Demo, ListDemo};
|
||||||
use crate::pages::demo::DemoPage;
|
use crate::pages::demo::DemoPage;
|
||||||
use crate::pages::index::Index;
|
use crate::pages::index::Index;
|
||||||
use crate::pages::render;
|
use crate::pages::render;
|
||||||
|
use asset::{serve_compiled, serve_static};
|
||||||
use axum::extract::{MatchedPath, Path};
|
use axum::extract::{MatchedPath, Path};
|
||||||
use axum::http::{HeaderValue, Request};
|
use axum::http::Request;
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use axum::{extract::State, routing::get, Router, Server};
|
use axum::{extract::State, routing::get, Router, Server};
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
use hyper::header::{CACHE_CONTROL, CONTENT_TYPE, ETAG};
|
|
||||||
use hyperlocal::UnixServerExt;
|
use hyperlocal::UnixServerExt;
|
||||||
use maud::Markup;
|
use maud::Markup;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
@ -52,7 +53,9 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(index))
|
.route("/", get(index))
|
||||||
.route("/style.:version.css", get(style))
|
.route("/style.css", get(serve_compiled!("style.css")))
|
||||||
|
.route("/images/logo.png", get(serve_static!("../images/logo.png")))
|
||||||
|
.route("/images/logo.svg", get(serve_static!("../images/logo.svg")))
|
||||||
.route("/:id", get(demo))
|
.route("/:id", get(demo))
|
||||||
.layer(
|
.layer(
|
||||||
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
|
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
|
||||||
|
|
@ -94,26 +97,6 @@ async fn main() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn style() -> impl IntoResponse {
|
|
||||||
let style = include_str!(concat!(env!("OUT_DIR"), "/style.css"));
|
|
||||||
let etag = concat!(
|
|
||||||
r#"""#,
|
|
||||||
include_str!(concat!(env!("OUT_DIR"), "/style.md5")),
|
|
||||||
r#"""#
|
|
||||||
);
|
|
||||||
(
|
|
||||||
[
|
|
||||||
(CONTENT_TYPE, HeaderValue::from_static("text/css")),
|
|
||||||
(ETAG, HeaderValue::from_static(etag)),
|
|
||||||
(
|
|
||||||
CACHE_CONTROL,
|
|
||||||
HeaderValue::from_static("public, max-age=2592000, immutable"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
style,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn index(State(app): State<Arc<App>>) -> Result<Markup> {
|
async fn index(State(app): State<Arc<App>>) -> Result<Markup> {
|
||||||
let demos = ListDemo::list(&app.connection, None).await?;
|
let demos = ListDemo::list(&app.connection, None).await?;
|
||||||
Ok(render(Index { demos }))
|
Ok(render(Index { demos }))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
pub mod demo;
|
pub mod demo;
|
||||||
pub mod index;
|
pub mod index;
|
||||||
|
|
||||||
|
use crate::asset::saved_asset_url;
|
||||||
use maud::{html, Markup, DOCTYPE};
|
use maud::{html, Markup, DOCTYPE};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
|
@ -10,17 +11,14 @@ pub trait Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render<T: Page>(page: T) -> Markup {
|
pub fn render<T: Page>(page: T) -> Markup {
|
||||||
let style_url = concat!(
|
let style_url = saved_asset_url!("style.css");
|
||||||
"/style.",
|
|
||||||
include_str!(concat!(env!("OUT_DIR"), "/style.md5")),
|
|
||||||
".css",
|
|
||||||
);
|
|
||||||
html! {
|
html! {
|
||||||
(DOCTYPE)
|
(DOCTYPE)
|
||||||
html {
|
html {
|
||||||
head {
|
head {
|
||||||
title { (page.title()) }
|
title { (page.title()) }
|
||||||
link rel="stylesheet" type="text/css" href=(style_url);
|
link rel="stylesheet" type="text/css" href=(style_url);
|
||||||
|
link rel="shortcut icon" type="image/svg+xml" href="images/logo.svg";
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
header {
|
header {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue