dynamic/static embeds

This commit is contained in:
Robin Appelman 2023-04-12 21:08:09 +02:00
commit cb56c80555
22 changed files with 9068 additions and 665 deletions

View file

@ -1,26 +1,8 @@
use axum::response::IntoResponse;
use demostf_build::Asset;
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) => {
include_str!(concat!(env!("OUT_DIR"), "/", $name, ".hash"))
};
($name:expr, quoted) => {
concat!(r#"""#, crate::asset::saved_asset_hash!($name), r#"""#)
};
}
pub const fn cache_headers(
content_type: &'static str,
etag: &'static str,
@ -35,51 +17,9 @@ pub const fn cache_headers(
]
}
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";
} else if ends_with!(path, "js") {
return "text/javascript";
}
return "text/plain";
pub async fn serve_asset<A: Asset>() -> impl IntoResponse {
let mime = A::mime();
let style = A::content();
let etag = A::etag();
(cache_headers(mime, etag), style.into_owned())
}
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;

View file

@ -5,6 +5,7 @@ mod error;
mod pages;
mod session;
use crate::asset::serve_asset;
pub use crate::config::Config;
use crate::config::Listen;
use crate::data::demo::{Demo, ListDemo};
@ -13,11 +14,10 @@ use crate::data::steam_id::SteamId;
use crate::data::user::User;
use crate::pages::about::AboutPage;
use crate::pages::demo::DemoPage;
use crate::pages::index::Index;
use crate::pages::render;
use crate::pages::upload::UploadPage;
use crate::pages::index::{DemoListScript, Index};
use crate::pages::upload::{UploadPage, UploadScript};
use crate::pages::{render, GlobalStyle};
use crate::session::{SessionData, COOKIE_NAME};
use asset::{serve_compiled, serve_static};
use async_session::{MemoryStore, Session, SessionStore};
use axum::extract::{MatchedPath, Path, RawQuery};
use axum::headers::Cookie;
@ -25,6 +25,7 @@ use axum::http::header::{LOCATION, SET_COOKIE};
use axum::http::{HeaderValue, Request, StatusCode};
use axum::response::IntoResponse;
use axum::{extract::State, routing::get, Router, Server, TypedHeader};
use demostf_build::Asset;
pub use error::Error;
use hyperlocal::UnixServerExt;
use maud::Markup;
@ -50,6 +51,13 @@ struct App {
pub session_store: MemoryStore,
}
#[derive(Asset)]
#[asset(source = "images/logo.png", url = "/images/logo.png")]
struct LogoPng;
#[derive(Asset)]
#[asset(source = "images/logo.svg", url = "/images/logo.svg")]
struct LogoSvg;
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::registry()
@ -75,11 +83,11 @@ async fn main() -> Result<()> {
let app = Router::new()
.route("/", get(index))
.route("/style.css", get(serve_compiled!("style.css")))
.route("/upload.js", get(serve_compiled!("upload.js")))
.route("/demo_list.js", get(serve_compiled!("demo_list.js")))
.route("/images/logo.png", get(serve_static!("../images/logo.png")))
.route("/images/logo.svg", get(serve_static!("../images/logo.svg")))
.route(GlobalStyle::route(), get(serve_asset::<GlobalStyle>))
.route(UploadScript::route(), get(serve_asset::<UploadScript>))
.route(DemoListScript::route(), get(serve_asset::<DemoListScript>))
.route(LogoPng::route(), get(serve_asset::<LogoPng>))
.route(LogoSvg::route(), get(serve_asset::<LogoSvg>))
.route("/about", get(about))
.route("/login/callback", get(login_callback))
.route("/login", get(login))

View file

@ -1,6 +1,6 @@
use crate::asset::saved_asset_url;
use crate::data::demo::ListDemo;
use crate::pages::Page;
use demostf_build::Asset;
use maud::{html, Markup, Render};
use std::borrow::Cow;
@ -10,13 +10,17 @@ pub struct Index<'a> {
pub api: &'a str,
}
#[derive(Asset)]
#[asset(source = "script/demo_list.js", url = "/demo_list.js")]
pub struct DemoListScript;
impl Page for Index<'_> {
fn title(&self) -> Cow<'static, str> {
"Demos - demos.tf".into()
}
fn render(&self) -> Markup {
let script = saved_asset_url!("demo_list.js");
let script = DemoListScript::url();
html! {
h1 { "Demos" }
#filter-bar data-maps = (MapList(&self.maps)) data-api-base = (self.api) {}

View file

@ -4,8 +4,8 @@ pub mod index;
mod plugin_section;
pub mod upload;
use crate::asset::saved_asset_url;
use crate::session::SessionData;
use demostf_build::Asset;
use maud::{html, Markup, DOCTYPE};
use std::borrow::Cow;
@ -14,8 +14,12 @@ pub trait Page {
fn render(&self) -> Markup;
}
#[derive(Asset)]
#[asset(source = "style/style.css", url = "/style.css")]
pub struct GlobalStyle;
pub fn render<T: Page>(page: T, session: SessionData) -> Markup {
let style_url = saved_asset_url!("style.css");
let style_url = GlobalStyle::url();
html! {
(DOCTYPE)
html {

View file

@ -1,6 +1,6 @@
use crate::asset::saved_asset_url;
use crate::pages::plugin_section::PluginSection;
use crate::pages::Page;
use demostf_build::Asset;
use maud::{html, Markup};
use std::borrow::Cow;
@ -17,13 +17,17 @@ impl<'a> UploadPage<'a> {
}
}
#[derive(Asset)]
#[asset(source = "script/upload.ts", url = "/upload.js")]
pub struct UploadScript;
impl Page for UploadPage<'_> {
fn title(&self) -> Cow<'static, str> {
"Upload - demos.tf".into()
}
fn render(&self) -> Markup {
let script = saved_asset_url!("upload.js");
let script = UploadScript::url();
html! {
.upload-page {
section.upload {