diff options
| -rw-r--r-- | Cargo.lock | 349 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | src/birka-web.rs (renamed from src/main.rs) | 139 | ||||
| -rw-r--r-- | static/css/style.css | 10 | ||||
| -rw-r--r-- | templates/upload.hbs | 22 |
6 files changed, 477 insertions, 51 deletions
@@ -46,6 +46,12 @@ dependencies = [ [[package]] name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "autocfg" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" @@ -90,7 +96,9 @@ dependencies = [ "blake2", "dirs", "image", + "rand 0.7.3", "rocket", + "rocket-multipart-form-data", "rocket_contrib", "rusqlite", "serde", @@ -149,6 +157,16 @@ dependencies = [ ] [[package]] +name = "buf_redux" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" +dependencies = [ + "memchr", + "safemem", +] + +[[package]] name = "byte-tools" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -179,6 +197,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] name = "color_quant" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -228,7 +255,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg", + "autocfg 1.0.0", "cfg-if", "crossbeam-utils", "lazy_static", @@ -253,7 +280,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg", + "autocfg 1.0.0", "cfg-if", "lazy_static", ] @@ -342,6 +369,17 @@ dependencies = [ ] [[package]] +name = "educe" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27c760a73a13abb1ddf417c06bc2b8d14b0607dd19097316e0ca9412a971088" +dependencies = [ + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.25", +] + +[[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -397,6 +435,12 @@ dependencies = [ ] [[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -490,7 +534,7 @@ dependencies = [ "httparse", "language-tags", "log 0.3.9", - "mime", + "mime 0.2.6", "num_cpus", "time", "traitobject", @@ -534,7 +578,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" dependencies = [ - "autocfg", + "autocfg 1.0.0", ] [[package]] @@ -704,7 +748,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" dependencies = [ - "autocfg", + "autocfg 1.0.0", ] [[package]] @@ -717,6 +761,24 @@ dependencies = [ ] [[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "1.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216929a5ee4dd316b1702eedf5e74548c123d370f47841ceaac38ca154690ca3" +dependencies = [ + "mime 0.2.6", + "phf", + "phf_codegen", + "unicase", +] + +[[package]] name = "miniz_oxide" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -769,6 +831,24 @@ dependencies = [ ] [[package]] +name = "multipart" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136eed74cadb9edd2651ffba732b19a450316b680e4f48d6c79e905799e19d01" +dependencies = [ + "buf_redux", + "httparse", + "log 0.4.8", + "mime 0.2.6", + "mime_guess", + "quick-error", + "rand 0.6.5", + "safemem", + "tempfile", + "twoway", +] + +[[package]] name = "net2" version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -803,7 +883,7 @@ version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "autocfg", + "autocfg 1.0.0", "num-traits", ] @@ -813,7 +893,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" dependencies = [ - "autocfg", + "autocfg 1.0.0", "num-integer", "num-traits", ] @@ -824,7 +904,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ - "autocfg", + "autocfg 1.0.0", "num-integer", "num-traits", ] @@ -835,7 +915,7 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ - "autocfg", + "autocfg 1.0.0", ] [[package]] @@ -926,6 +1006,45 @@ dependencies = [ ] [[package]] +name = "phf" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +dependencies = [ + "phf_shared", + "rand 0.6.5", +] + +[[package]] +name = "phf_shared" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" +dependencies = [ + "siphasher", + "unicase", +] + +[[package]] name = "pkg-config" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -944,6 +1063,12 @@ dependencies = [ ] [[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" + +[[package]] name = "proc-macro2" version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -986,6 +1111,153 @@ dependencies = [ ] [[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.7", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi 0.3.8", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.7", + "rand_core 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi 0.3.8", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi 0.3.8", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.7", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] name = "rayon" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1010,6 +1282,15 @@ dependencies = [ ] [[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1045,6 +1326,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" [[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +dependencies = [ + "winapi 0.3.8", +] + +[[package]] name = "ring" version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1078,6 +1368,18 @@ dependencies = [ ] [[package]] +name = "rocket-multipart-form-data" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c38ffbd5126f8df80346e7945028d5dd5ea81f6cf0e4b6310942ce6d7e5db691" +dependencies = [ + "educe", + "mime 0.3.16", + "multipart", + "rocket", +] + +[[package]] name = "rocket_codegen" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1225,6 +1527,12 @@ dependencies = [ ] [[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" + +[[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1271,6 +1579,20 @@ dependencies = [ ] [[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +dependencies = [ + "cfg-if", + "libc", + "rand 0.7.3", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.8", +] + +[[package]] name = "thread_local" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1316,6 +1638,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" [[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +dependencies = [ + "memchr", +] + +[[package]] name = "typeable" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -16,6 +16,8 @@ rusqlite = "0.21.0" # Until RFC #2887 is merged, image and rocket are deps of the CLI tool as well. image = "0.23" +multipart = { package = "rocket-multipart-form-data", version = "0.9" } +rand = "0.7" rocket = "0.4" rocket_contrib = { version = "0.4", default-features = false, features = ["handlebars_templates", "json", "serve"] } serde = "1.0" @@ -108,9 +108,9 @@ GET /api/posts/[id] ImageResult ``` -To upload an image to the database, the blob of data may be POSTED to the -following endpoint, where `filename` is the desired filename of the image (a -string): +To upload an image to the database, post a multipart/form-data encoded message +to the following endpoint with the following fields: `tags`, a comma-separated +list of tags for the files, and `image`, the file to be uploaded. ``` POST /api/posts/[filename] diff --git a/src/main.rs b/src/birka-web.rs index 2a5b440..22ace80 100644 --- a/src/main.rs +++ b/src/birka-web.rs @@ -24,20 +24,27 @@ extern crate image; extern crate rocket; extern crate rocket_contrib; #[macro_use] +extern crate rusqlite; +#[macro_use] extern crate serde_derive; mod database; use anyhow::Result; -use database::{hash_file, Query, TagDatabase}; +use database::{Image, Query, TagDatabase}; use image::ImageFormat; +use multipart::{MultipartFormData, MultipartFormDataField, MultipartFormDataOptions}; +use rand::distributions::Alphanumeric; +use rand::{thread_rng, Rng}; +use rocket::http::ContentType; use rocket::{Data, State}; use rocket_contrib::json::Json; use rocket_contrib::serve::StaticFiles; use rocket_contrib::templates::Template; -use std::env; + use std::fs; use std::fs::File; +use std::io::Write; use std::os::unix::fs::symlink; use std::path::Path; use std::sync::Mutex; @@ -48,7 +55,7 @@ const RESULTS_PER_QUERY: i64 = 50; /// Shorthand for the state that is passed to all handlers. type SiteState<'a> = State<'a, Mutex<DatabaseConnection>>; -/// Collection of data necessary to serve images from the tag database. +/// Everything necessary to serve images from the tag database. struct DatabaseConnection { tb: TagDatabase, image_dir: String, @@ -56,17 +63,18 @@ struct DatabaseConnection { impl DatabaseConnection { fn new() -> Result<Self> { - let bin = env::current_exe()?.canonicalize()?; + // FIXME: It is somewhat of an arbitrary choice that the directory + // containing symbolic links and thumbnails be relative to the binary. + let bin = std::env::current_exe()?.canonicalize()?; let bin_dir = bin.parent().unwrap(); - let image_dir = String::from(bin_dir.join("image/").to_str().unwrap()); - let tb_path = bin_dir.join("birka.db"); - let tb = TagDatabase::new(tb_path.to_str().unwrap())?; + let image_dir = bin_dir.join("image/").to_str().unwrap().to_string(); + let tb = TagDatabase::new(&bin_dir.join("birka.db"))?; Ok(DatabaseConnection { tb, image_dir }) } } /// Information about an image, as returned by the 'posts' endpoint. -#[derive(Serialize)] +#[derive(Debug, Serialize)] struct ImageResult { id: i64, filename: String, @@ -75,6 +83,7 @@ struct ImageResult { tags: Vec<String>, } +/// Return the store file name of the image at `filename`. fn image_store_filename(id: i64, filename: &str) -> String { if let Some(n) = filename.rfind('.') { format!("{id}.{ext}", id = id, ext = &filename[n + 1..]) @@ -83,7 +92,7 @@ fn image_store_filename(id: i64, filename: &str) -> String { } } -/// Return the file name of the thumbnail for the image at `filename`. +/// Return the store file name of the thumbnail for the image at `filename`. fn thumb_filename(id: i64, filename: &str) -> String { if let Some(n) = filename.rfind('.') { format!("{id}_thumb.{ext}", id = id, ext = &filename[n + 1..]) @@ -103,12 +112,9 @@ fn get_posts(conn: SiteState, tags: String, last: Option<i64>) -> Json<Vec<Image } else { vec![] }; + let results = tb - .query(Query { - tags: &tags[..], - last_id: last, - limit: Some(RESULTS_PER_QUERY), - }) + .query(Query::new(&tags[..], last, Some(RESULTS_PER_QUERY))) .unwrap(); Json( @@ -139,16 +145,46 @@ fn get_post(conn: SiteState, id: i64) -> Json<ImageResult> { }) } -#[post("/posts/<filename>", data = "<data>")] -fn put_post(conn: SiteState, filename: String, data: Data) -> Json<&str> { +#[post("/posts", data = "<data>")] +fn put_post(conn: SiteState, content_type: &ContentType, data: Data) -> Json<String> { let conn = conn.inner().lock().unwrap(); - let path = Path::new(&conn.image_dir).join(filename); - data.stream_to_file(&path).unwrap(); - conn.tb - .import_image(&path, &hash_file(&path).unwrap()) - .unwrap(); + let options = MultipartFormDataOptions::with_multipart_form_data_fields(vec![ + MultipartFormDataField::text("tags"), + MultipartFormDataField::raw("image") + .size_limit(32 * 1024 * 1024) + .content_type_by_string(Some(multipart::mime::IMAGE_STAR)) + .unwrap(), + ]); - Json("uploaded!") + let mut data = MultipartFormData::parse(content_type, data, options).unwrap(); + let raw = data.raw.remove("image").unwrap().remove(0); + + let filename = raw + .file_name + .unwrap_or(thread_rng().sample_iter(&Alphanumeric).take(30).collect()); + let path = Path::new(&conn.image_dir).join(&filename); + let mut f = File::create(&path).unwrap(); + f.write_all(&raw.raw).unwrap(); + + let hash = database::hash_file(&path).unwrap(); + let id = conn.tb.import_image(&path, &hash).unwrap(); + add_to_store( + &Image { + id, + blake2: hash, + filename: filename.clone(), + orig_dir: conn.image_dir.clone(), + }, + &conn.image_dir, + ); + + let tags = data.texts.remove("tags").unwrap().remove(0).text; + for tag in tags.split(",") { + conn.tb.add_tag(tag).unwrap(); + conn.tb.tag_image(id, tag).unwrap(); + } + + Json("Uploaded!".into()) } #[get("/")] @@ -156,6 +192,11 @@ fn index(conn: SiteState) -> Template { display_posts(conn, String::from(""), None) } +#[get("/upload")] +fn upload() -> Template { + Template::render("upload", None::<()>) +} + #[get("/posts?<tags>&<last>")] fn display_posts(conn: SiteState, tags: String, last: Option<i64>) -> Template { #[derive(Serialize)] @@ -203,28 +244,48 @@ fn display_posts(conn: SiteState, tags: String, last: Option<i64>) -> Template { #[get("/posts/<id>")] fn display_post(conn: SiteState, id: i64) -> Template { - Template::render("image", get_post(conn, id).into_inner()) + #[derive(Serialize)] + struct Context { + tags: Vec<String>, + filename: String, + orig_filename: String, + } + + let tb = &conn.inner().lock().unwrap().tb; + let image = tb.image_by_id(id).unwrap(); + + Template::render( + "image", + Context { + tags: tb.tags_for_image(image.id).unwrap(), + filename: image_store_filename(image.id, &image.filename), + orig_filename: image.filename, + }, + ) +} + +/// Create a thumbnail for `image` in the store at `image_dir`. +fn add_to_store(image: &Image, image_dir: &str) { + let orig_path = Path::new(&image.orig_dir).join(&image.filename); + let store_path = image_store_filename(image.id, &image.filename); + let store_path = Path::new(image_dir).join(store_path); + symlink(orig_path, &store_path).ok(); + let thumb_path = thumb_filename(image.id, &image.filename); + let thumb_path = Path::new(image_dir).join(&thumb_path); + if !thumb_path.exists() { + let im = image::open(store_path).unwrap(); + let out = &mut File::create(&thumb_path).unwrap(); + im.thumbnail(100, 100) + .write_to(out, ImageFormat::Png) + .unwrap(); + } } /// Maybe initialize the directory for storing image symlinks and thumbnails. fn create_image_directory(conn: &DatabaseConnection) { fs::create_dir(&conn.image_dir).ok(); - for image in conn.tb.images_by_tags(&vec![][..]).unwrap() { - let src = Path::new(&image.orig_dir).join(&image.filename); - let dst = image_store_filename(image.id, &image.filename); - let dst = Path::new(&conn.image_dir).join(dst); - symlink(&src, &dst).ok(); - - let thumb_path = thumb_filename(image.id, &image.filename); - let thumb_path = Path::new(&conn.image_dir).join(&thumb_path); - if !thumb_path.exists() { - let im = image::open(src).unwrap(); - let out = &mut File::create(&thumb_path).unwrap(); - im.thumbnail(100, 100) - .write_to(out, ImageFormat::Png) - .unwrap(); - } + add_to_store(&image, &conn.image_dir); } } @@ -234,7 +295,7 @@ fn main() { rocket::ignite() .mount("/public", StaticFiles::from("./static/")) .mount("/image", StaticFiles::from(&conn.image_dir)) - .mount("/", routes![index, display_posts, display_post]) + .mount("/", routes![index, upload, display_posts, display_post]) .mount("/api", routes![get_posts, get_post, put_post]) .attach(Template::fairing()) .manage(Mutex::new(conn)) diff --git a/static/css/style.css b/static/css/style.css index b32823e..017622a 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -18,3 +18,13 @@ aside { main { grid-column: 2 / 6; grid-row: 2 +} + +#upload { + width: 20%; +} + +#upload input { + min-width: 100%; + margin-bottom: 16px; +} diff --git a/templates/upload.hbs b/templates/upload.hbs new file mode 100644 index 0000000..ca57d4f --- /dev/null +++ b/templates/upload.hbs @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<head> + <title>бирка-тян - Upload File</title> + <link rel="stylesheet" type="text/css" href="/public/css/style.css"> + <meta charset="UTF-8"> +</head> +<body> + <header> + <h1><a href="/">бирка-тян</a></h1> + </header> + <main> + <h2>Upload a file</h2> + <form id="upload" method="post" action="/api/posts" enctype="multipart/form-data"> + <input id="image" name="image" type="file" accept="image/*"> + <label for="tags">Tags:</label> + <input id="tags" name="tags" type="text"> + <button type="submit">Upload</button> + </form> + </main> +</body> +</html> |