Caching (#480)
* add basic caching support * Use hash of static dir instead of rand * Add support for ETag
This commit is contained in:
parent
90baf9beb1
commit
a2b2e37aa0
37
build.rs
37
build.rs
@ -2,6 +2,38 @@ extern crate ructe;
|
|||||||
extern crate rsass;
|
extern crate rsass;
|
||||||
use ructe::*;
|
use ructe::*;
|
||||||
use std::{env, fs::*, io::Write, path::PathBuf};
|
use std::{env, fs::*, io::Write, path::PathBuf};
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
fn compute_static_hash() -> String {
|
||||||
|
//"find static/ -type f ! -path 'static/media/*' | sort | xargs stat --printf='%n %Y\n' | sha256sum"
|
||||||
|
|
||||||
|
let find = Command::new("find")
|
||||||
|
.args(&["static/", "-type", "f", "!", "-path", "static/media/*"])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed find command");
|
||||||
|
|
||||||
|
let sort = Command::new("sort")
|
||||||
|
.stdin(find.stdout.unwrap())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed sort command");
|
||||||
|
|
||||||
|
let xargs = Command::new("xargs")
|
||||||
|
.args(&["stat", "--printf='%n %Y\n'"])
|
||||||
|
.stdin(sort.stdout.unwrap())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed xargs command");
|
||||||
|
|
||||||
|
let sha = Command::new("sha256sum")
|
||||||
|
.stdin(xargs.stdout.unwrap())
|
||||||
|
.output()
|
||||||
|
.expect("failed sha256sum command");
|
||||||
|
|
||||||
|
String::from_utf8(sha.stdout).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
@ -16,8 +48,11 @@ fn main() {
|
|||||||
.expect("Error during SCSS compilation")
|
.expect("Error during SCSS compilation")
|
||||||
).expect("Couldn't write CSS output");
|
).expect("Couldn't write CSS output");
|
||||||
|
|
||||||
|
let cache_id = &compute_static_hash()[..8];
|
||||||
println!("cargo:rerun-if-changed=target/deploy/plume-front.wasm");
|
println!("cargo:rerun-if-changed=target/deploy/plume-front.wasm");
|
||||||
copy("target/deploy/plume-front.wasm", "static/plume-front.wasm")
|
copy("target/deploy/plume-front.wasm", "static/plume-front.wasm")
|
||||||
.and_then(|_| read_to_string("target/deploy/plume-front.js"))
|
.and_then(|_| read_to_string("target/deploy/plume-front.js"))
|
||||||
.and_then(|js| write("static/plume-front.js", js.replace("\"plume-front.wasm\"", "\"/static/plume-front.wasm\""))).ok();
|
.and_then(|js| write("static/plume-front.js", js.replace("\"plume-front.wasm\"", &format!("\"/static/cached/{}/plume-front.wasm\"", cache_id)))).ok();
|
||||||
|
|
||||||
|
println!("cargo:rustc-env=CACHE_ID={}", cache_id)
|
||||||
}
|
}
|
||||||
|
@ -189,6 +189,7 @@ Then try to restart Plume.
|
|||||||
routes::session::password_reset_form,
|
routes::session::password_reset_form,
|
||||||
routes::session::password_reset,
|
routes::session::password_reset,
|
||||||
|
|
||||||
|
routes::plume_static_files,
|
||||||
routes::static_files,
|
routes::static_files,
|
||||||
|
|
||||||
routes::tags::tag,
|
routes::tags::tag,
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
use atom_syndication::{ContentBuilder, Entry, EntryBuilder, LinkBuilder, Person, PersonBuilder};
|
use atom_syndication::{ContentBuilder, Entry, EntryBuilder, LinkBuilder, Person, PersonBuilder};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
http::{RawStr, Status, uri::{FromUriParam, Query}},
|
http::{
|
||||||
|
RawStr, Status, uri::{FromUriParam, Query},
|
||||||
|
hyper::header::{CacheControl, CacheDirective}
|
||||||
|
},
|
||||||
Outcome,
|
Outcome,
|
||||||
request::{self, FromFormValue, FromRequest, Request},
|
request::{self, FromFormValue, FromRequest, Request},
|
||||||
response::NamedFile,
|
response::NamedFile,
|
||||||
@ -101,7 +104,24 @@ pub mod user;
|
|||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod well_known;
|
pub mod well_known;
|
||||||
|
|
||||||
#[get("/static/<file..>", rank = 2)]
|
#[derive(Responder)]
|
||||||
pub fn static_files(file: PathBuf) -> Option<NamedFile> {
|
#[response()]
|
||||||
NamedFile::open(Path::new("static/").join(file)).ok()
|
pub struct CachedFile {
|
||||||
|
inner: NamedFile,
|
||||||
|
cache_control: CacheControl
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/static/cached/<_build_id>/<file..>", rank = 2)]
|
||||||
|
pub fn plume_static_files(file: PathBuf, _build_id: &RawStr) -> Option<CachedFile> {
|
||||||
|
static_files(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/static/<file..>", rank = 3)]
|
||||||
|
pub fn static_files(file: PathBuf) -> Option<CachedFile> {
|
||||||
|
NamedFile::open(Path::new("static/").join(file)).ok()
|
||||||
|
.map(|f|
|
||||||
|
CachedFile {
|
||||||
|
inner: f,
|
||||||
|
cache_control: CacheControl(vec![CacheDirective::MaxAge(60*60*24*30)])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,50 @@
|
|||||||
use plume_models::{Connection, notifications::*, users::User};
|
use plume_models::{Connection, notifications::*, users::User};
|
||||||
use rocket::response::Content;
|
|
||||||
|
use rocket::http::{Method, Status};
|
||||||
|
use rocket::http::hyper::header::{ETag, EntityTag};
|
||||||
|
use rocket::request::Request;
|
||||||
|
use rocket::response::{self, Response, Responder, content::Html as HtmlCt};
|
||||||
use rocket_i18n::Catalog;
|
use rocket_i18n::Catalog;
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::Hasher;
|
||||||
use templates::Html;
|
use templates::Html;
|
||||||
|
|
||||||
pub use askama_escape::escape;
|
pub use askama_escape::escape;
|
||||||
|
|
||||||
|
pub static CACHE_NAME: &str = env!("CACHE_ID");
|
||||||
|
|
||||||
pub type BaseContext<'a> = &'a(&'a Connection, &'a Catalog, Option<User>);
|
pub type BaseContext<'a> = &'a(&'a Connection, &'a Catalog, Option<User>);
|
||||||
|
|
||||||
pub type Ructe = Content<Vec<u8>>;
|
#[derive(Debug)]
|
||||||
|
pub struct Ructe(pub Vec<u8>);
|
||||||
|
|
||||||
|
impl<'r> Responder<'r> for Ructe {
|
||||||
|
fn respond_to(self, r: &Request) -> response::Result<'r> {
|
||||||
|
//if method is not Get or page contain a form, no caching
|
||||||
|
if r.method() != Method::Get || self.0.windows(6).any(|w| w == b"<form ") {
|
||||||
|
return HtmlCt(self.0).respond_to(r);
|
||||||
|
}
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
hasher.write(&self.0);
|
||||||
|
let etag = format!("{:x}", hasher.finish());
|
||||||
|
if r.headers().get("If-None-Match").any(|s| &s[1..s.len()-1] == etag) {
|
||||||
|
Response::build()
|
||||||
|
.status(Status::NotModified)
|
||||||
|
.header(ETag(EntityTag::strong(etag)))
|
||||||
|
.ok()
|
||||||
|
} else {
|
||||||
|
Response::build()
|
||||||
|
.merge(HtmlCt(self.0).respond_to(r)?)
|
||||||
|
.header(ETag(EntityTag::strong(etag)))
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! render {
|
macro_rules! render {
|
||||||
($group:tt :: $page:tt ( $( $param:expr ),* ) ) => {
|
($group:tt :: $page:tt ( $( $param:expr ),* ) ) => {
|
||||||
{
|
{
|
||||||
use rocket::{http::ContentType, response::Content};
|
|
||||||
use templates;
|
use templates;
|
||||||
|
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
@ -23,7 +54,7 @@ macro_rules! render {
|
|||||||
$param
|
$param
|
||||||
),*
|
),*
|
||||||
).unwrap();
|
).unwrap();
|
||||||
Content(ContentType::HTML, res)
|
Ructe(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
/* color palette: https://coolors.co/23f0c7-ef767a-7765e3-6457a6-ffe347 */
|
/* color palette: https://coolors.co/23f0c7-ef767a-7765e3-6457a6-ffe347 */
|
||||||
|
|
||||||
@import url('/static/fonts/Route159/Route159.css');
|
@import url('../fonts/Route159/Route159.css');
|
||||||
@import url('/static/fonts/Lora/Lora.css');
|
@import url('../fonts/Lora/Lora.css');
|
||||||
@import url('/static/fonts/Playfair_Display/PlayfairDisplay.css');
|
@import url('../fonts/Playfair_Display/PlayfairDisplay.css');
|
||||||
|
|
||||||
html {
|
html {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>@title ⋅ @i18n!(ctx.1, "Plume")</title>
|
<title>@title ⋅ @i18n!(ctx.1, "Plume")</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="stylesheet" href="@uri!(static_files: file = "css/main.css")" />
|
<link rel="stylesheet" href="@uri!(plume_static_files: file = "css/main.css", _build_id = CACHE_NAME)" />
|
||||||
<link rel="stylesheet" href="@uri!(static_files: file = "css/feather.css")" />
|
<link rel="stylesheet" href="@uri!(plume_static_files: file = "css/feather.css", _build_id = CACHE_NAME)" />
|
||||||
<link rel="manifest" href="@uri!(instance::web_manifest)" />
|
<link rel="manifest" href="@uri!(instance::web_manifest)" />
|
||||||
<link rel="icon" type="image/png" href="@uri!(static_files: file = "icons/trwnh/feather-filled/plumeFeatherFilled64.png")">
|
<link rel="icon" type="image/png" href="@uri!(plume_static_files: file = "icons/trwnh/feather-filled/plumeFeatherFilled64.png", _build_id = CACHE_NAME)">
|
||||||
@:head()
|
@:head()
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
<div id="content">
|
<div id="content">
|
||||||
<nav>
|
<nav>
|
||||||
<a href="@uri!(instance::index)" class="title">
|
<a href="@uri!(instance::index)" class="title">
|
||||||
<img src="@uri!(static_files: file = "icons/trwnh/feather/plumeFeather256.png")">
|
<img src="@uri!(plume_static_files: file = "icons/trwnh/feather/plumeFeather256.png", _build_id = CACHE_NAME)">
|
||||||
<p>@i18n!(ctx.1, "Plume")</p>
|
<p>@i18n!(ctx.1, "Plume")</p>
|
||||||
</a>
|
</a>
|
||||||
<hr/>
|
<hr/>
|
||||||
@ -79,6 +79,6 @@
|
|||||||
<a href="@uri!(instance::admin)">@i18n!(ctx.1, "Administration")</a>
|
<a href="@uri!(instance::admin)">@i18n!(ctx.1, "Administration")</a>
|
||||||
}
|
}
|
||||||
</footer>
|
</footer>
|
||||||
<script src="@uri!(static_files: file = "plume-front.js")"></script>
|
<script src="@uri!(plume_static_files: file = "plume-front.js", _build_id = CACHE_NAME)"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user