diff --git a/src/endpoints/cats/list.rs b/src/endpoints/cats/list.rs index df8d8a9..21f3bb9 100644 --- a/src/endpoints/cats/list.rs +++ b/src/endpoints/cats/list.rs @@ -1,16 +1,74 @@ -use sea_orm::EntityTrait; +use std::cmp; + +use actix_web::{http::header::CONTENT_TYPE, web::Query}; +use sea_orm::{ + ColumnTrait, EntityTrait, JoinType, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect, + RelationTrait, +}; +use serde::Deserialize; use crate::{ - endpoints::prelude::*, entities::prelude::Cat, models::cat::CatModel, state::AppState, + endpoints::prelude::*, + entities::{cat, prelude::Cat, tag}, + models::cat::CatModel, + state::AppState, utils::errors::GenericError, }; -#[get("/")] -pub async fn handler(state: Data) -> Result { - let users = Cat::find() - .all(&state.db) +#[derive(Deserialize)] +struct ListRequest { + page: Option, + cat_tags: Option>, + camera_tags: Option>, + content_tags: Option>, + include_hidden: Option, +} + +const LIMIT: u64 = 20; + +#[get("")] +pub async fn handler( + state: Data, + data: Query, +) -> Result { + // SeaORM starts at 0, we start at 1 + let page = cmp::max(data.page.unwrap_or(1), 1) - 1; + + let mut query = Cat::find().order_by_desc(cat::Column::Date); + + let include_hidden = data.include_hidden.unwrap_or(false); + + if include_hidden { + query = query.filter(cat::Column::Hidden.eq(false)); + } + + if let Some(cat_tags) = &data.cat_tags { + if !cat_tags.is_empty() { + query = query + .join(JoinType::InnerJoin, cat::Relation::CatTag.def()) + .filter(tag::Column::Id.is_in(cat_tags)) + } + } + + if let Some(content_tags) = &data.camera_tags { + if !content_tags.is_empty() { + query = query.filter(cat::Column::CatCameraTag.is_in(content_tags)); + } + } + + if let Some(content_tags) = &data.content_tags { + if !content_tags.is_empty() { + query = query + .join(JoinType::InnerJoin, cat::Relation::CatContentTags.def()) + .filter(tag::Column::Id.is_in(content_tags)); + } + } + + let cats = query + .paginate(&state.db, LIMIT) + .fetch_page(page) .await .map_err(|e| GenericError::new(e.to_string().as_str()))?; - Ok(Json(CatModel::from_many(users))) + Ok(Json(CatModel::from_many(cats))) } diff --git a/src/entities/prelude.rs b/src/entities/prelude.rs index bd9005c..48ca1ee 100644 --- a/src/entities/prelude.rs +++ b/src/entities/prelude.rs @@ -5,4 +5,5 @@ pub use super::cat_content_tags::Entity as CatContentTags; pub use super::cat_tag::Entity as CatTag; // pub use super::doctrine_migration_versions::Entity as DoctrineMigrationVersions; pub use super::tag::Entity as Tag; +pub use super::tag::TagType; pub use super::user::Entity as User; diff --git a/src/entities/tag.rs b/src/entities/tag.rs index 3d8c06b..07b4b85 100644 --- a/src/entities/tag.rs +++ b/src/entities/tag.rs @@ -1,13 +1,25 @@ //! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.6 use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, EnumIter, DeriveActiveEnum, Serialize, Deserialize, Clone, Eq, PartialEq)] +#[sea_orm(rs_type = "String", db_type = "String(StringLen::N(255))")] +pub enum TagType { + #[sea_orm(string_value = "cat_tag")] + Cat, + #[sea_orm(string_value = "camera_tag")] + Camera, + #[sea_orm(string_value = "content_tag")] + Content, +} #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[sea_orm(table_name = "tag")] pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub r#type: String, + pub r#type: TagType, pub content: String, } diff --git a/src/models/cat.rs b/src/models/cat.rs index 4902737..a321da8 100644 --- a/src/models/cat.rs +++ b/src/models/cat.rs @@ -1,18 +1,21 @@ +use sea_orm::ModelTrait; use serde::Serialize; use crate::utils::strings::create_page_title; +use super::tag::TagModel; + #[derive(Serialize)] pub struct CatModel { pub id: i32, pub image_url: String, pub thumbnail_url: Option, - // cat_tags pub content: Option, pub page_title: Option, pub date: String, - // camera_tag, - // tags + pub cat_tags: Vec, + pub camera_tag: Option, + pub tags: Vec, } impl From for CatModel { @@ -29,6 +32,9 @@ impl From for CatModel { .clone() .map(|content| create_page_title(&content)), date: value.date.and_utc().to_rfc3339(), + cat_tags: vec![], + camera_tag: None, + tags: vec![], } } } diff --git a/src/models/mod.rs b/src/models/mod.rs index f74a290..9c7dfa5 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,2 +1,3 @@ pub mod cat; +pub mod tag; pub mod user; diff --git a/src/models/tag.rs b/src/models/tag.rs new file mode 100644 index 0000000..a2ac740 --- /dev/null +++ b/src/models/tag.rs @@ -0,0 +1,26 @@ +use serde::Serialize; + +use crate::entities::tag::TagType; + +#[derive(Serialize)] +pub struct TagModel { + pub id: i32, + pub r#type: TagType, + pub content: String, +} + +impl From for TagModel { + fn from(value: crate::entities::tag::Model) -> Self { + Self { + id: value.id, + r#type: value.r#type, + content: value.content, + } + } +} + +impl TagModel { + pub fn from_many(values: Vec) -> Vec { + values.into_iter().map(Into::into).collect() + } +}