summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--src/database.rs19
-rw-r--r--src/main.rs64
-rw-r--r--templates/image.hbs35
-rw-r--r--templates/index.hbs6
5 files changed, 103 insertions, 22 deletions
diff --git a/README.md b/README.md
index 7ec8b92..c18c66f 100644
--- a/README.md
+++ b/README.md
@@ -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}}