Added image querying
parent
a6fe143075
commit
13d0c9401e
|
@ -1156,6 +1156,7 @@ dependencies = [
|
|||
"j_db",
|
||||
"log",
|
||||
"multer",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"structopt",
|
||||
|
@ -1196,6 +1197,12 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
|
@ -1244,6 +1251,18 @@ version = "0.8.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
|
@ -1252,6 +1271,9 @@ name = "rand_core"
|
|||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
|
|
|
@ -21,6 +21,7 @@ log = { version = "0.4.20", features = [] }
|
|||
env_logger = "0.11.0"
|
||||
multer = "3.0.0"
|
||||
serde_json = "1.0.111"
|
||||
rand = "0.8.5"
|
||||
|
||||
[dependencies.axum]
|
||||
version = "0.7.4"
|
||||
|
|
109
src/main.rs
109
src/main.rs
|
@ -9,7 +9,8 @@ use crate::model::album::Album;
|
|||
use crate::model::image::{Image, ImageData};
|
||||
use crate::state::Context;
|
||||
use crate::storage_manager::{StorageManager, StoreError};
|
||||
use axum::extract::{Multipart, Path, Query, State};
|
||||
use axum::body::Bytes;
|
||||
use axum::extract::{DefaultBodyLimit, Multipart, Path, Query, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
use axum::routing::{get, post};
|
||||
|
@ -18,6 +19,8 @@ use axum_macros::FromRequest;
|
|||
use j_db::database::Database;
|
||||
use j_db::model::JdbModel;
|
||||
use log::info;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -47,6 +50,26 @@ struct AddImage {
|
|||
pub tags: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub enum ImageSort {
|
||||
Random,
|
||||
DateAscending,
|
||||
DateDescending,
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct ImageQuery {
|
||||
pub album: Option<String>,
|
||||
#[serde(default)]
|
||||
pub tags: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub order: ImageSort,
|
||||
#[serde(default)]
|
||||
pub limit: usize,
|
||||
}
|
||||
|
||||
#[derive(FromRequest)]
|
||||
#[from_request(via(axum::Json), rejection(PicOxError))]
|
||||
struct Response<T>(T);
|
||||
|
@ -60,9 +83,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum PicOxError {
|
||||
StoreError(StoreError),
|
||||
DbError(j_db::error::JDbError),
|
||||
AlbumNotFound,
|
||||
ImageNotFound,
|
||||
}
|
||||
|
||||
impl From<StoreError> for PicOxError {
|
||||
|
@ -95,6 +121,14 @@ impl IntoResponse for PicOxError {
|
|||
),
|
||||
},
|
||||
PicOxError::DbError(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()),
|
||||
PicOxError::AlbumNotFound => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"No album found!".to_string(),
|
||||
),
|
||||
PicOxError::ImageNotFound => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Image not found".to_string(),
|
||||
),
|
||||
};
|
||||
|
||||
(status, Response(ErrorResponse { message })).into_response()
|
||||
|
@ -123,6 +157,73 @@ async fn get_album(
|
|||
Ok(Response(album))
|
||||
}
|
||||
|
||||
async fn query_images(
|
||||
image_query: Query<ImageQuery>,
|
||||
State(context): State<PicContext>,
|
||||
) -> Result<Response<Vec<Image>>, PicOxError> {
|
||||
let album_id = if let Some(album) = &image_query.album {
|
||||
Some(
|
||||
Album::find_album_by_query(
|
||||
&context.db,
|
||||
AlbumQuery {
|
||||
album_name: Some(album.to_string()),
|
||||
},
|
||||
)
|
||||
.ok_or(PicOxError::AlbumNotFound)?
|
||||
.id()
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut images: Vec<Image> = context
|
||||
.db
|
||||
.filter(|_, img: &Image| {
|
||||
if let Some(album_id) = album_id {
|
||||
if img.album != album_id {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if !image_query.tags.is_empty() {
|
||||
let mut found = false;
|
||||
for tag in &image_query.tags {
|
||||
if img.tags.contains(tag) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
})?
|
||||
.collect();
|
||||
|
||||
match image_query.order {
|
||||
ImageSort::Random => {
|
||||
images.shuffle(&mut thread_rng());
|
||||
}
|
||||
ImageSort::DateAscending => {
|
||||
images.sort_by(|img_a, img_b| img_a.create_date.cmp(&img_b.create_date))
|
||||
}
|
||||
ImageSort::DateDescending => {
|
||||
images.sort_by(|img_a, img_b| img_b.create_date.cmp(&img_a.create_date))
|
||||
}
|
||||
ImageSort::None => {}
|
||||
}
|
||||
|
||||
if images.len() > image_query.limit {
|
||||
images.drain(image_query.limit..);
|
||||
}
|
||||
|
||||
Ok(Response(images))
|
||||
}
|
||||
|
||||
async fn add_image(
|
||||
State(context): State<PicContext>,
|
||||
mut img_data: Multipart,
|
||||
|
@ -138,7 +239,8 @@ async fn add_image(
|
|||
metadata = Some(serde_json::from_str(&metadata_json).unwrap())
|
||||
} else if field_name == "img_data" {
|
||||
file_name = Some(field.file_name().unwrap().to_string());
|
||||
data.extend_from_slice(field.bytes().await.unwrap().as_ref());
|
||||
let file_segment = field.bytes().await.unwrap_or(Bytes::new());
|
||||
data.extend_from_slice(file_segment.as_ref());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,6 +256,7 @@ async fn add_image(
|
|||
ImageData::Bytes(data),
|
||||
&file_name.unwrap(),
|
||||
0,
|
||||
album.id().unwrap(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -199,6 +302,8 @@ async fn main() {
|
|||
.route("/api/album/:id", get(get_album))
|
||||
.route("/api/album/", get(query_album))
|
||||
.route("/api/image/", post(add_image))
|
||||
.layer(DefaultBodyLimit::max(1024 * 1024 * 1024))
|
||||
.route("/api/image/", get(query_images))
|
||||
.with_state(context);
|
||||
|
||||
// run our app with hyper, listening globally on port 3000
|
||||
|
|
|
@ -17,6 +17,7 @@ pub struct Image {
|
|||
pub link: String,
|
||||
pub created_by: u64,
|
||||
pub storage_location: StorageLocation,
|
||||
pub album: u64,
|
||||
|
||||
id: Option<u64>,
|
||||
}
|
||||
|
@ -28,6 +29,7 @@ impl Image {
|
|||
link: Url,
|
||||
created_by: u64,
|
||||
storage_location: StorageLocation,
|
||||
album: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
filename: filename.to_string(),
|
||||
|
@ -36,6 +38,7 @@ impl Image {
|
|||
link: link.to_string(),
|
||||
created_by,
|
||||
storage_location,
|
||||
album,
|
||||
id: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ pub trait Store: Send {
|
|||
&mut self,
|
||||
img_data: ImageData,
|
||||
file_name: &str,
|
||||
album: u64,
|
||||
created_by: u64,
|
||||
) -> Result<Image, StoreError> {
|
||||
let (url, storage_location) = self.store_img(img_data, file_name).await?;
|
||||
|
@ -58,6 +59,7 @@ pub trait Store: Send {
|
|||
url,
|
||||
created_by,
|
||||
storage_location,
|
||||
album,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -122,6 +124,7 @@ impl StorageManager {
|
|||
img_data: ImageData,
|
||||
file_name: &str,
|
||||
created_by: u64,
|
||||
album: u64,
|
||||
) -> Result<Image, StoreError> {
|
||||
let file_name_path = PathBuf::from(file_name);
|
||||
|
||||
|
@ -151,7 +154,7 @@ impl StorageManager {
|
|||
}
|
||||
|
||||
let img = store_type
|
||||
.create_img(img_data, file_name, created_by)
|
||||
.create_img(img_data, file_name, created_by, album)
|
||||
.await?;
|
||||
|
||||
Ok(db.insert::<Image>(img).unwrap())
|
||||
|
|
Loading…
Reference in New Issue