Improve handling of static files.
Use static files handling from updated ructe rather than local handling, and use rsass rather than sass-rs and sass-sys. Static files are now kept in the binary (no more need to access the build directory at runtime). Also, there is an admin command to save the static files to a directory, for the benefit of fronting web servers and backwards compatibility.
This commit is contained in:
parent
698fb2c23b
commit
8cd215cdc2
@ -6,14 +6,9 @@ authors = ["Rasmus Kaj <kaj@kth.se>"]
|
||||
build = "src/build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
sass-rs = "*"
|
||||
sass-sys = "*"
|
||||
md5 = "*"
|
||||
rustc-serialize = "*"
|
||||
flate2 = "*"
|
||||
brotli2 = "*"
|
||||
diesel_codegen_syntex = { version = "*", features = ["postgres", "syntex"] }
|
||||
ructe = "*"
|
||||
rsass = "*"
|
||||
|
||||
[[bin]]
|
||||
name = "rphotoserver"
|
||||
@ -46,3 +41,5 @@ r2d2-diesel = "*"
|
||||
djangohashers = "*"
|
||||
rand = "*"
|
||||
memcached-rs = "^0.1"
|
||||
flate2 = "*"
|
||||
brotli2 = "*"
|
||||
|
@ -4,3 +4,4 @@ pub mod readkpa;
|
||||
pub mod result;
|
||||
pub mod stats;
|
||||
pub mod users;
|
||||
pub mod storestatics;
|
||||
|
26
src/adm/storestatics.rs
Normal file
26
src/adm/storestatics.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use adm::result::Error;
|
||||
use brotli2::write::BrotliEncoder;
|
||||
use flate2::{Compression, FlateWriteExt};
|
||||
use std::fs::{File, create_dir_all};
|
||||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn to_dir(dir: &str) -> Result<(), Error> {
|
||||
let dir: &Path = dir.as_ref();
|
||||
try!(create_dir_all(&dir));
|
||||
for s in STATICS {
|
||||
try!(File::create(dir.join(s.name))
|
||||
.and_then(|mut f| f.write(s.content)));
|
||||
|
||||
try!(File::create(dir.join(format!("{}.gz", s.name)))
|
||||
.map(|f| f.gz_encode(Compression::Best))
|
||||
.and_then(|mut f| f.write(s.content)));
|
||||
|
||||
try!(File::create(dir.join(format!("{}.br", s.name)))
|
||||
.map(|f| BrotliEncoder::new(f, 11))
|
||||
.and_then(|mut f| f.write(s.content)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/templates/statics.rs"));
|
75
src/build.rs
75
src/build.rs
@ -1,31 +1,23 @@
|
||||
extern crate brotli2;
|
||||
extern crate diesel_codegen_syntex as diesel_codegen;
|
||||
extern crate flate2;
|
||||
extern crate md5;
|
||||
extern crate rustc_serialize as serialize;
|
||||
extern crate sass_rs;
|
||||
extern crate sass_sys;
|
||||
extern crate rsass;
|
||||
extern crate ructe;
|
||||
|
||||
use brotli2::write::BrotliEncoder;
|
||||
use flate2::{Compression, FlateWriteExt};
|
||||
use ructe::compile_templates;
|
||||
use sass_rs::dispatcher::Dispatcher;
|
||||
use sass_rs::sass_context::SassFileContext;
|
||||
use serialize::base64::{self, ToBase64};
|
||||
use rsass::{OutputStyle, compile_scss_file};
|
||||
use ructe::{compile_static_files, compile_templates};
|
||||
use std::env;
|
||||
use std::fs::{File, create_dir_all};
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::thread;
|
||||
|
||||
fn main() {
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
prepare_diesel(&out_dir);
|
||||
do_sassify(&out_dir);
|
||||
let css_dir = out_dir.join("tmpcss");
|
||||
do_sassify(&css_dir);
|
||||
let template_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
|
||||
.join("templates");
|
||||
compile_templates(&template_dir, &out_dir).unwrap();
|
||||
compile_static_files(&css_dir, &out_dir).expect("statics");
|
||||
compile_templates(&template_dir, &out_dir).expect("templates");
|
||||
}
|
||||
|
||||
pub fn prepare_diesel(out_dir: &Path) {
|
||||
@ -38,59 +30,16 @@ pub fn prepare_diesel(out_dir: &Path) {
|
||||
println!("cargo:rerun-if-changed=src/lib.in.rs");
|
||||
}
|
||||
|
||||
pub fn do_sassify(out_dir: &Path) {
|
||||
let static_dir = out_dir.join("static").join("static");
|
||||
pub fn do_sassify(static_dir: &Path) {
|
||||
create_dir_all(&static_dir).unwrap();
|
||||
|
||||
let css = compile("photos.scss").unwrap();
|
||||
let css = css.as_bytes();
|
||||
let filename = format!("style-{}.css", checksum_slug(&css));
|
||||
let css = compile_scss_file("photos.scss".as_ref(),
|
||||
OutputStyle::Compressed).unwrap();
|
||||
|
||||
File::create(static_dir.join(&filename))
|
||||
.and_then(|mut f| f.write(css))
|
||||
File::create(static_dir.join("style.css"))
|
||||
.and_then(|mut f| f.write(&css))
|
||||
.expect("Writing css");
|
||||
File::create(static_dir.join(format!("{}.gz", filename)))
|
||||
.map(|f| f.gz_encode(Compression::Best))
|
||||
.and_then(|mut f| f.write(css))
|
||||
.expect("Writing gzipped css");
|
||||
File::create(static_dir.join(format!("{}.br", filename)))
|
||||
.map(|f| BrotliEncoder::new(f, 11))
|
||||
.and_then(|mut f| f.write(css))
|
||||
.expect("Writing brotli compressed css");
|
||||
|
||||
File::create(&out_dir.join("stylelink"))
|
||||
.and_then(|mut f| {
|
||||
writeln!(f,
|
||||
"\"<link rel='stylesheet' href='/static/{}' \
|
||||
type='text/css'/>\"",
|
||||
filename)
|
||||
})
|
||||
.expect("Writing stylelink");
|
||||
|
||||
// TODO Find any referenced files!
|
||||
println!("cargo:rerun-if-changed=photos.scss");
|
||||
}
|
||||
|
||||
/// A short and url-safe checksum string from string data.
|
||||
fn checksum_slug(data: &[u8]) -> String {
|
||||
md5::compute(data)[9..].to_base64(base64::URL_SAFE)
|
||||
}
|
||||
|
||||
/// Setup the sass environment and compile a file.
|
||||
fn compile(filename: &str) -> Result<String, String> {
|
||||
let mut file_context = SassFileContext::new(filename);
|
||||
// options.set_output_style(COMPRESSED) or similar, when supported.
|
||||
if let Ok(mut opt) = file_context.sass_context.sass_options.write() {
|
||||
unsafe {
|
||||
sass_sys::sass_option_set_output_style(
|
||||
opt.raw.get_mut(),
|
||||
sass_sys::SASS_STYLE_COMPRESSED);
|
||||
}
|
||||
}
|
||||
let options = file_context.sass_context.sass_options.clone();
|
||||
thread::spawn(move || {
|
||||
let dispatcher = Dispatcher::build(vec![], options);
|
||||
while dispatcher.dispatch().is_ok() {}
|
||||
});
|
||||
file_context.compile()
|
||||
}
|
||||
|
21
src/main.rs
21
src/main.rs
@ -31,7 +31,7 @@ use diesel::prelude::*;
|
||||
use dotenv::dotenv;
|
||||
use hyper::header::{Expires, HttpDate};
|
||||
use nickel::{FormBody, Halt, HttpRouter, MediaType, MiddlewareResult, Nickel,
|
||||
Request, Response, StaticFilesHandler};
|
||||
Request, Response};
|
||||
use nickel::extensions::response::Redirect;
|
||||
use nickel::status::StatusCode;
|
||||
use nickel_diesel::{DieselMiddleware, DieselRequestExtensions};
|
||||
@ -100,13 +100,10 @@ fn main() {
|
||||
|
||||
let mut server = Nickel::new();
|
||||
server.utilize(RequestLoggerMiddleware);
|
||||
server.get("/static/:file.:ext", wrap!(static_file: file, ext));
|
||||
server.utilize(MemcacheMiddleware::new
|
||||
(vec![("tcp://127.0.0.1:11211".into(), 1)]));
|
||||
server.utilize(SessionMiddleware::new(&jwt_key()));
|
||||
// TODO This is a "build" location, not an "install" location ...
|
||||
let staticdir = concat!(env!("OUT_DIR"), "/static/");
|
||||
info!("Serving static files from {}", staticdir);
|
||||
server.utilize(StaticFilesHandler::new(staticdir));
|
||||
let dm: DieselMiddleware<PgConnection> =
|
||||
DieselMiddleware::new(&dburl(), 5, Box::new(NopErrorHandler)).unwrap();
|
||||
server.utilize(dm);
|
||||
@ -308,6 +305,20 @@ fn place_all<'mw>(req: &mut Request,
|
||||
query.order(place_name).load(c).expect("List places")))
|
||||
}
|
||||
|
||||
fn static_file<'mw>(_req: &mut Request,
|
||||
mut res: Response<'mw>,
|
||||
name: String,
|
||||
ext: String)
|
||||
-> MiddlewareResult<'mw> {
|
||||
use templates::statics::StaticFile;
|
||||
if let Some(s) = StaticFile::get(&format!("{}.{}", name, ext)) {
|
||||
res.set(ext.parse().unwrap_or(MediaType::Bin));
|
||||
res.set(Expires(HttpDate(time::now() + Duration::days(300))));
|
||||
return res.send(s.content);
|
||||
}
|
||||
res.error(StatusCode::NotFound, "No such file")
|
||||
}
|
||||
|
||||
fn place_one<'mw>(req: &mut Request,
|
||||
res: Response<'mw>,
|
||||
tslug: String)
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! Just fooling around with different ways to count images per year.
|
||||
extern crate rphotos;
|
||||
extern crate brotli2;
|
||||
extern crate clap;
|
||||
extern crate chrono;
|
||||
#[macro_use]
|
||||
@ -7,6 +8,7 @@ extern crate diesel;
|
||||
extern crate djangohashers;
|
||||
extern crate dotenv;
|
||||
extern crate env_logger;
|
||||
extern crate flate2;
|
||||
extern crate rand;
|
||||
extern crate rexif;
|
||||
#[macro_use]
|
||||
@ -18,7 +20,7 @@ mod adm;
|
||||
mod env;
|
||||
mod photosdir;
|
||||
|
||||
use adm::{findphotos, makepublic, readkpa, users};
|
||||
use adm::{findphotos, makepublic, readkpa, users, storestatics};
|
||||
use adm::result::Error;
|
||||
use adm::stats::show_stats;
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
@ -66,6 +68,11 @@ fn main() {
|
||||
.help("Image path to make public"))
|
||||
.after_help("The image path(s) are relative to the \
|
||||
image root."))
|
||||
.subcommand(SubCommand::with_name("storestatics")
|
||||
.about("Store statics as files for a web server")
|
||||
.arg(Arg::with_name("DIR")
|
||||
.required(true)
|
||||
.help("Directory to store the files in")))
|
||||
.get_matches();
|
||||
|
||||
match run(args) {
|
||||
@ -122,6 +129,9 @@ fn run(args: ArgMatches) -> Result<(), Error> {
|
||||
("userpass", Some(args)) => {
|
||||
users::passwd(&try!(get_db()), args.value_of("USER").unwrap())
|
||||
}
|
||||
("storestatics", Some(args)) => {
|
||||
storestatics::to_dir(args.value_of("DIR").unwrap())
|
||||
}
|
||||
_ => Ok(println!("No subcommand given.\n\n{}", args.usage())),
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
@use ::Link;
|
||||
@use templates::head;
|
||||
@use templates::statics::style_css;
|
||||
|
||||
@(title: &str, lpath: Vec<Link>, user: Option<String>, content: Content)
|
||||
|
||||
@ -8,7 +9,7 @@
|
||||
<head>
|
||||
<title>@title</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
@Html(include!(concat!(env!("OUT_DIR"), "/stylelink")))
|
||||
<link rel="stylesheet" href="/static/@style_css.name" type="text/css"/>
|
||||
</head>
|
||||
<body>
|
||||
@:head(lpath, user)
|
||||
|
Loading…
Reference in New Issue
Block a user