feat: add /images image listing page
							parent
							
								
									39a15bb094
								
							
						
					
					
						commit
						fc02399e78
					
				| 
						 | 
					@ -12,7 +12,7 @@ use serde::Deserialize;
 | 
				
			||||||
use std::{error::Error, fmt, path::Path};
 | 
					use std::{error::Error, fmt, path::Path};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use models::event::{Event, NewEvent, EVENT_TYPES};
 | 
					pub use models::event::{Event, NewEvent, EVENT_TYPES};
 | 
				
			||||||
pub use models::image::{Image, NewImage};
 | 
					pub use models::image::{Image, ImageFilter, NewImage};
 | 
				
			||||||
pub use models::plant::{NewPlant, Plant};
 | 
					pub use models::plant::{NewPlant, Plant};
 | 
				
			||||||
pub use models::session::Session;
 | 
					pub use models::session::Session;
 | 
				
			||||||
pub use models::user::{NewUser, User};
 | 
					pub use models::user::{NewUser, User};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,16 @@
 | 
				
			||||||
use axum::{
 | 
					use axum::{
 | 
				
			||||||
    extract::{DefaultBodyLimit, Multipart, Path, Request, State},
 | 
					    extract::{DefaultBodyLimit, Multipart, Path, Query, Request, State},
 | 
				
			||||||
    handler::Handler,
 | 
					    handler::Handler,
 | 
				
			||||||
 | 
					    http::HeaderMap,
 | 
				
			||||||
    response::{Html, IntoResponse},
 | 
					    response::{Html, IntoResponse},
 | 
				
			||||||
    routing::{get, post},
 | 
					    routing::get,
 | 
				
			||||||
    Router,
 | 
					    Router,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use chrono::NaiveDate;
 | 
					use chrono::NaiveDate;
 | 
				
			||||||
use futures::TryStreamExt;
 | 
					use futures::TryStreamExt;
 | 
				
			||||||
use image::{codecs::jpeg::JpegEncoder, ImageReader};
 | 
					use image::{codecs::jpeg::JpegEncoder, ImageReader};
 | 
				
			||||||
use mime::Mime;
 | 
					use mime::Mime;
 | 
				
			||||||
 | 
					use tera::Context;
 | 
				
			||||||
use tokio_util::io::StreamReader;
 | 
					use tokio_util::io::StreamReader;
 | 
				
			||||||
use tower::ServiceExt;
 | 
					use tower::ServiceExt;
 | 
				
			||||||
use tower_http::services::ServeFile;
 | 
					use tower_http::services::ServeFile;
 | 
				
			||||||
| 
						 | 
					@ -17,7 +19,7 @@ use std::{io::BufWriter, path::PathBuf};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::error::AppError;
 | 
					use super::error::AppError;
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    db::{Image, NewImage},
 | 
					    db::{self, Image, NewImage, Pagination},
 | 
				
			||||||
    IMG_DIR,
 | 
					    IMG_DIR,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +29,7 @@ pub fn app() -> axum::Router<crate::Context> {
 | 
				
			||||||
    Router::new()
 | 
					    Router::new()
 | 
				
			||||||
        .route(
 | 
					        .route(
 | 
				
			||||||
            "/",
 | 
					            "/",
 | 
				
			||||||
            post(post_image.layer(DefaultBodyLimit::max(1024 * 1024 * 20))),
 | 
					            get(get_images).post(post_image.layer(DefaultBodyLimit::max(1024 * 1024 * 20))),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .route("/{id}/original", get(get_image_original))
 | 
					        .route("/{id}/original", get(get_image_original))
 | 
				
			||||||
        .route("/{id}/thumb", get(get_image_thumb))
 | 
					        .route("/{id}/thumb", get(get_image_thumb))
 | 
				
			||||||
| 
						 | 
					@ -58,6 +60,27 @@ async fn get_image(
 | 
				
			||||||
    Ok(ServeFile::new_with_mime(path, &mime).oneshot(req).await)
 | 
					    Ok(ServeFile::new_with_mime(path, &mime).oneshot(req).await)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn get_images(
 | 
				
			||||||
 | 
					    State(ctx): State<crate::Context>,
 | 
				
			||||||
 | 
					    Query(page): Query<Pagination>,
 | 
				
			||||||
 | 
					    Query(filter): Query<db::ImageFilter>,
 | 
				
			||||||
 | 
					    headers: HeaderMap,
 | 
				
			||||||
 | 
					) -> super::Result<Html<String>> {
 | 
				
			||||||
 | 
					    let images = tokio::task::spawn_blocking(move || db::Image::page(&ctx.pool, page, filter))
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					        .unwrap()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut context = Context::new();
 | 
				
			||||||
 | 
					    context.insert("images", &images);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(Html(super::render_view(
 | 
				
			||||||
 | 
					        &ctx.tera,
 | 
				
			||||||
 | 
					        "views/images.html",
 | 
				
			||||||
 | 
					        &context,
 | 
				
			||||||
 | 
					        &headers,
 | 
				
			||||||
 | 
					    )?))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async fn get_image_original(
 | 
					async fn get_image_original(
 | 
				
			||||||
    State(ctx): State<crate::Context>,
 | 
					    State(ctx): State<crate::Context>,
 | 
				
			||||||
    Path(id): Path<i32>,
 | 
					    Path(id): Path<i32>,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					<h1>Images</h1>
 | 
				
			||||||
 | 
					{% for image in images %}
 | 
				
			||||||
 | 
					<article>
 | 
				
			||||||
 | 
					    <p>Date taken: {{ image.date_taken }}</p>
 | 
				
			||||||
 | 
					    <p>Note: {{ image.note }}</p>
 | 
				
			||||||
 | 
					    <a href="/images/{{ image.id }}/original" target="_blank">
 | 
				
			||||||
 | 
					        <img src="/images/{{ image.id }}/thumb">
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					</article>
 | 
				
			||||||
 | 
					{% endfor %}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue