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