diff options
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | src/database.rs | 19 | ||||
| -rw-r--r-- | src/main.rs | 64 | ||||
| -rw-r--r-- | templates/image.hbs | 35 | ||||
| -rw-r--r-- | templates/index.hbs | 6 |
5 files changed, 103 insertions, 22 deletions
@@ -81,6 +81,7 @@ Suppose we denote the following shape of JSON object as a `ImageResult`. "id": number, "filename": string, "thumb_filename": string, + "orig_filename": string, "tags": [string, ...], } ``` diff --git a/src/database.rs b/src/database.rs index 055d1ef..54f511f 100644 --- a/src/database.rs +++ b/src/database.rs @@ -128,6 +128,25 @@ impl TagDatabase { .query_row(params![path, file_name], |row| Ok(row.get::<_, i64>(0)))??) } + /// Return the `Image` struct for the image with `id`. + pub fn image_by_id(&self, id: i64) -> Result<Image> { + Ok(self + .0 + .prepare( + "SELECT id, blake2, filename, orig_dir + FROM images + WHERE id = ?", + )? + .query_row(params![id], |row| { + Ok(Image { + id: row.get(0)?, + blake2: row.get(1)?, + filename: row.get(2)?, + orig_dir: row.get(3)?, + }) + })?) + } + /// Associate `tag` with the image specified by `image_id`. pub fn tag_image(&self, image_id: i64, tag: &str) -> Result<()> { self.0.execute( diff --git a/src/main.rs b/src/main.rs index d53d0d0..2a5b440 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,20 +71,25 @@ struct ImageResult { id: i64, filename: String, thumb_filename: String, + orig_filename: String, tags: Vec<String>, } +fn image_store_filename(id: i64, filename: &str) -> String { + if let Some(n) = filename.rfind('.') { + format!("{id}.{ext}", id = id, ext = &filename[n + 1..]) + } else { + id.to_string() + } +} + /// Return the file name of the thumbnail for the image at `filename`. -fn thumb_filename(filename: &str) -> String { - String::from(format!( - "{}_thumb.png", - // Strip extension. - if let Some(n) = filename.rfind('.') { - &filename[..n] - } else { - &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..]) + } else { + format!("{id}_thumb", id = id) + } } #[get("/posts?<tags>&<last>")] @@ -111,16 +116,31 @@ fn get_posts(conn: SiteState, tags: String, last: Option<i64>) -> Json<Vec<Image .iter() .map(|image| ImageResult { id: image.id, - filename: image.filename.clone(), - thumb_filename: thumb_filename(&image.filename), + filename: image_store_filename(image.id, &image.filename), + thumb_filename: thumb_filename(image.id, &image.filename), + orig_filename: image.filename.clone(), tags: tb.tags_for_image(image.id).unwrap(), }) .collect(), ) } +#[get("/posts/<id>")] +fn get_post(conn: SiteState, id: i64) -> Json<ImageResult> { + let tb = &conn.inner().lock().unwrap().tb; + let image = tb.image_by_id(id).unwrap(); + + Json(ImageResult { + id: image.id, + filename: image_store_filename(image.id, &image.filename), + thumb_filename: thumb_filename(image.id, &image.filename), + orig_filename: image.filename.clone(), + tags: tb.tags_for_image(image.id).unwrap(), + }) +} + #[post("/posts/<filename>", data = "<data>")] -fn put_posts(conn: SiteState, filename: String, data: Data) -> Json<&str> { +fn put_post(conn: SiteState, filename: String, data: Data) -> Json<&str> { let conn = conn.inner().lock().unwrap(); let path = Path::new(&conn.image_dir).join(filename); data.stream_to_file(&path).unwrap(); @@ -181,19 +201,25 @@ fn display_posts(conn: SiteState, tags: String, last: Option<i64>) -> Template { Template::render("index", context) } +#[get("/posts/<id>")] +fn display_post(conn: SiteState, id: i64) -> Template { + Template::render("image", get_post(conn, id).into_inner()) +} + /// 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 = Path::new(&conn.image_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 stem = dst.file_stem().unwrap().to_str().unwrap(); - let thumb_path = Path::new(&conn.image_dir).join(&thumb_filename(&stem)); + 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(&Path::new(&src)).unwrap(); + let im = image::open(src).unwrap(); let out = &mut File::create(&thumb_path).unwrap(); im.thumbnail(100, 100) .write_to(out, ImageFormat::Png) @@ -208,8 +234,8 @@ fn main() { rocket::ignite() .mount("/public", StaticFiles::from("./static/")) .mount("/image", StaticFiles::from(&conn.image_dir)) - .mount("/", routes![index, display_posts]) - .mount("/api", routes![get_posts, put_posts]) + .mount("/", routes![index, display_posts, display_post]) + .mount("/api", routes![get_posts, get_post, put_post]) .attach(Template::fairing()) .manage(Mutex::new(conn)) .launch(); diff --git a/templates/image.hbs b/templates/image.hbs new file mode 100644 index 0000000..8c3ceb6 --- /dev/null +++ b/templates/image.hbs @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<head> + <title>бирка-тян - {{ orig_filename }}</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>{{ orig_filename }}</h2> + <img src="/image/{{filename}}"> + </main> + <aside id="tag-view"> + <section id="search"> + <h2>Search</h2> + <form id="search-box" method="GET" action="/posts"> + <input id="tags" name="tags" type="text" autocomplete="off"> + <button id="search-box-submit" type="submit">Search</button> + </form> + </section> + + <section id="tags"> + <h2>Tags</h2> + <ul> + {{#each tags}} + <li><a href="/posts?tags={{ . }}">{{ . }}</a></li> + {{/each}} + </ul> + </section> + </aside> +</body> +</html> diff --git a/templates/index.hbs b/templates/index.hbs index c3311b9..1279a5e 100644 --- a/templates/index.hbs +++ b/templates/index.hbs @@ -7,11 +7,11 @@ </head> <body> <header> - <h1>бирка-тян</h1> + <h1><a href="/">бирка-тян</a></h1> </header> <main> {{#each images}} - <img src="/image/{{thumb_filename}}"> + <a href="/posts/{{ id }}"><img src="/image/{{thumb_filename}}"></a> {{/each}} {{#if next_page}} <p><a href="{{next_page}}">next</a></p> @@ -26,7 +26,7 @@ </form> </section> - <section id="search"> + <section id="tags"> <h2>Tags</h2> <ul> {{#each tags}} |