diff --git a/Cargo.lock b/Cargo.lock index 3011ad9..68d345d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1033,6 +1033,17 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "minify-html" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dedb5736b6b92db14a164678219f6738bc1104bf66d915d7143efc825f551323" +dependencies = [ + "aho-corasick", + "lazy_static", + "memchr", +] + [[package]] name = "miniz_oxide" version = "0.4.3" @@ -1231,6 +1242,7 @@ dependencies = [ "env_logger", "futures", "mime", + "minify-html", "once_cell", "ructe", "serde", diff --git a/Cargo.toml b/Cargo.toml index 321887a..7602911 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ dotenv = "0.15.0" env_logger = "0.8" futures = "0.3" mime = "0.3" +minify-html = "0.3.9" once_cell = "1.4" serde = { version = "1.0", features = ["derive"] } structopt = "0.3.14" diff --git a/src/main.rs b/src/main.rs index de64006..686bc18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use actix_web::{ body::BodyStream, client::Client, + dev::HttpResponseBuilder, http::{ header::{CacheControl, CacheDirective, ContentType, LastModified, LOCATION}, StatusCode, @@ -250,6 +251,9 @@ enum Error { #[error("{0}")] JsonPayload(#[from] awc::error::JsonPayloadError), + + #[error("Failed to minify html: {0}")] + Minify(String), } impl ResponseError for Error { @@ -258,27 +262,21 @@ impl ResponseError for Error { } fn error_response(&self) -> HttpResponse { - let mut builder = HttpResponse::build(self.status_code()); - - let mut cursor = Cursor::new(vec![]); - if let Err(e) = self::templates::error(&mut cursor, &self.to_string()) { - return builder + match render(HttpResponse::build(self.status_code()), |cursor| { + self::templates::error(cursor, &self.to_string()) + }) { + Ok(res) => res, + Err(_) => HttpResponse::build(self.status_code()) .content_type(mime::TEXT_PLAIN.essence_str()) - .body(e.to_string()); + .body(self.to_string()), } - - builder - .content_type(mime::TEXT_HTML.essence_str()) - .body(cursor.into_inner()) } } async fn index() -> Result { - let mut cursor = Cursor::new(vec![]); - self::templates::index(&mut cursor, "/upload", "images[]")?; - Ok(HttpResponse::Ok() - .content_type(mime::TEXT_HTML.essence_str()) - .body(cursor.into_inner())) + render(HttpResponse::Ok(), |cursor| { + self::templates::index(cursor, "/upload", "images[]") + }) } async fn upload( @@ -298,12 +296,9 @@ async fn upload( let images = res.json::().await?; - let mut cursor = Cursor::new(vec![]); - self::templates::images(&mut cursor, images)?; - - Ok(HttpResponse::build(res.status()) - .content_type(mime::TEXT_HTML.essence_str()) - .body(cursor.into_inner())) + render(HttpResponse::build(res.status()), |cursor| { + self::templates::images(cursor, images) + }) } const THUMBNAIL_SIZES: &[u64] = &[40, 50, 80, 100, 200, 400, 800, 1200]; @@ -334,12 +329,9 @@ async fn thumbs( details, }; - let mut cursor = Cursor::new(vec![]); - self::templates::thumbnails(&mut cursor, image, THUMBNAIL_SIZES)?; - - Ok(HttpResponse::Ok() - .content_type(mime::TEXT_HTML.essence_str()) - .body(cursor.into_inner())) + render(HttpResponse::Ok(), |cursor| { + self::templates::thumbnails(cursor, image, THUMBNAIL_SIZES) + }) } async fn image( @@ -390,12 +382,9 @@ async fn view_original( details, }; - let mut cursor = Cursor::new(vec![]); - self::templates::view(&mut cursor, image, None)?; - - Ok(HttpResponse::Ok() - .content_type(mime::TEXT_HTML.essence_str()) - .body(cursor.into_inner())) + render(HttpResponse::Ok(), |cursor| { + self::templates::view(cursor, image, None) + }) } async fn view( @@ -423,12 +412,9 @@ async fn view( details, }; - let mut cursor = Cursor::new(vec![]); - self::templates::view(&mut cursor, image, Some(size))?; - - Ok(HttpResponse::Ok() - .content_type(mime::TEXT_HTML.essence_str()) - .body(cursor.into_inner())) + render(HttpResponse::Ok(), |cursor| { + self::templates::view(cursor, image, Some(size)) + }) } async fn thumbnail( @@ -505,28 +491,27 @@ async fn delete( return Ok(to_404()); } - let mut cursor = Cursor::new(vec![]); if confirm { let url = CONFIG.upstream_delete_url(&token, &file); client.delete(url).send().await?; - self::templates::deleted(&mut cursor, &file)?; + render(HttpResponse::Ok(), |cursor| { + self::templates::deleted(cursor, &file) + }) } else { let details: Details = res.json().await?; - self::templates::confirm_delete( - &mut cursor, - &Image { - file, - delete_token: token, - details, - }, - )?; + render(HttpResponse::Ok(), move |cursor| { + self::templates::confirm_delete( + cursor, + &Image { + file, + delete_token: token, + details, + }, + ) + }) } - - Ok(HttpResponse::Ok() - .content_type(mime::TEXT_HTML.essence_str()) - .body(cursor.into_inner())) } fn to_404() -> HttpResponse { @@ -536,13 +521,9 @@ fn to_404() -> HttpResponse { } async fn not_found() -> Result { - let mut cursor = Cursor::new(vec![]); - - self::templates::not_found(&mut cursor)?; - - Ok(HttpResponse::NotFound() - .content_type(mime::TEXT_HTML.essence_str()) - .body(cursor.into_inner())) + render(HttpResponse::NotFound(), |cursor| { + self::templates::not_found(cursor) + }) } async fn go_home() -> HttpResponse { @@ -551,6 +532,21 @@ async fn go_home() -> HttpResponse { .finish() } +fn render( + mut builder: HttpResponseBuilder, + f: impl FnOnce(&mut Cursor<&mut Vec>) -> Result<(), std::io::Error>, +) -> Result { + let mut bytes = vec![]; + + (f)(&mut Cursor::new(&mut bytes))?; + minify_html::truncate(&mut bytes, &minify_html::Cfg { minify_js: false }) + .map_err(|e| Error::Minify(format!("{:?}", e)))?; + + Ok(builder + .content_type(mime::TEXT_HTML.essence_str()) + .body(bytes)) +} + #[actix_rt::main] async fn main() -> Result<(), anyhow::Error> { dotenv::dotenv().ok(); diff --git a/templates/index.rs.html b/templates/index.rs.html index 9b61ddf..12c856e 100644 --- a/templates/index.rs.html +++ b/templates/index.rs.html @@ -33,7 +33,7 @@ Upload - - + + })