Parent and child posts
This commit is contained in:
parent
04f888c323
commit
54379b98e0
16 changed files with 334 additions and 58 deletions
21
src/auth.rs
21
src/auth.rs
|
|
@ -123,7 +123,7 @@ impl SessionStore for SessionStorage {
|
|||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
)),
|
||||
expiry_date: Set(record.expiry_date.unix_timestamp().to_string()),
|
||||
expiry_date: Set(record.expiry_date.unix_timestamp()),
|
||||
..Default::default()
|
||||
})
|
||||
.exec(&self.db)
|
||||
|
|
@ -141,7 +141,7 @@ impl SessionStore for SessionStorage {
|
|||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
)),
|
||||
expiry_date: Set(record.expiry_date.unix_timestamp().to_string()),
|
||||
expiry_date: Set(record.expiry_date.unix_timestamp()),
|
||||
..Default::default()
|
||||
})
|
||||
.filter(samey_session::Column::SessionId.eq(record.id.to_string()))
|
||||
|
|
@ -175,20 +175,9 @@ impl SessionStore for SessionStorage {
|
|||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
expiry_date: match session.expiry_date.parse() {
|
||||
Ok(timestamp) => {
|
||||
OffsetDateTime::from_unix_timestamp(timestamp).map_err(|_| {
|
||||
session_store::Error::Backend(
|
||||
"Invalid timestamp for expiry date".into(),
|
||||
)
|
||||
})?
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(session_store::Error::Backend(
|
||||
"Failed to parse session expiry date".into(),
|
||||
));
|
||||
}
|
||||
},
|
||||
expiry_date: OffsetDateTime::from_unix_timestamp(session.expiry_date).map_err(
|
||||
|_| session_store::Error::Backend("Invalid timestamp for expiry date".into()),
|
||||
)?,
|
||||
},
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
pub mod prelude;
|
||||
|
||||
pub mod samey_pool;
|
||||
pub mod samey_pool_post;
|
||||
pub mod samey_post;
|
||||
pub mod samey_post_source;
|
||||
pub mod samey_session;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.8
|
||||
|
||||
pub use super::samey_pool::Entity as SameyPool;
|
||||
pub use super::samey_pool_post::Entity as SameyPoolPost;
|
||||
pub use super::samey_post::Entity as SameyPost;
|
||||
pub use super::samey_post_source::Entity as SameyPostSource;
|
||||
pub use super::samey_session::Entity as SameySession;
|
||||
|
|
|
|||
26
src/entities/samey_pool.rs
Normal file
26
src/entities/samey_pool.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.8
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "samey_pool")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
#[sea_orm(unique)]
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::samey_pool_post::Entity")]
|
||||
SameyPoolPost,
|
||||
}
|
||||
|
||||
impl Related<super::samey_pool_post::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SameyPoolPost.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
48
src/entities/samey_pool_post.rs
Normal file
48
src/entities/samey_pool_post.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.8
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "samey_pool_post")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub pool_id: i32,
|
||||
pub post_id: i32,
|
||||
#[sea_orm(column_type = "Float")]
|
||||
pub position: f32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::samey_pool::Entity",
|
||||
from = "Column::PoolId",
|
||||
to = "super::samey_pool::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
SameyPool,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::samey_post::Entity",
|
||||
from = "Column::PostId",
|
||||
to = "super::samey_post::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
SameyPost,
|
||||
}
|
||||
|
||||
impl Related<super::samey_pool::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SameyPool.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::samey_post::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SameyPost.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
|
@ -24,6 +24,8 @@ pub struct Model {
|
|||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::samey_pool_post::Entity")]
|
||||
SameyPoolPost,
|
||||
#[sea_orm(
|
||||
belongs_to = "Entity",
|
||||
from = "Column::ParentId",
|
||||
|
|
@ -46,6 +48,12 @@ pub enum Relation {
|
|||
SameyUser,
|
||||
}
|
||||
|
||||
impl Related<super::samey_pool_post::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SameyPoolPost.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::samey_post_source::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SameyPostSource.def()
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ use sea_orm::entity::prelude::*;
|
|||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
#[sea_orm(unique)]
|
||||
pub session_id: String,
|
||||
pub data: Json,
|
||||
pub expiry_date: String,
|
||||
pub expiry_date: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
|
|
|||
37
src/query.rs
37
src/query.rs
|
|
@ -16,16 +16,17 @@ use crate::{
|
|||
};
|
||||
|
||||
#[derive(Debug, FromQueryResult)]
|
||||
pub(crate) struct SearchPost {
|
||||
pub(crate) struct PostOverview {
|
||||
pub(crate) id: i32,
|
||||
pub(crate) thumbnail: String,
|
||||
pub(crate) tags: String,
|
||||
pub(crate) rating: String,
|
||||
}
|
||||
|
||||
pub(crate) fn search_posts(
|
||||
tags: Option<&Vec<&str>>,
|
||||
user: Option<User>,
|
||||
) -> Selector<SelectModel<SearchPost>> {
|
||||
user: Option<&User>,
|
||||
) -> Selector<SelectModel<PostOverview>> {
|
||||
let mut include_tags = HashSet::<String>::new();
|
||||
let mut exclude_tags = HashSet::<String>::new();
|
||||
let mut include_ratings = HashSet::<String>::new();
|
||||
|
|
@ -47,11 +48,12 @@ pub(crate) fn search_posts(
|
|||
}
|
||||
}
|
||||
|
||||
let mut query = if include_tags.is_empty() && exclude_tags.is_empty() {
|
||||
let query = if include_tags.is_empty() && exclude_tags.is_empty() {
|
||||
let mut query = SameyPost::find()
|
||||
.select_only()
|
||||
.column(samey_post::Column::Id)
|
||||
.column(samey_post::Column::Thumbnail)
|
||||
.column(samey_post::Column::Rating)
|
||||
.column_as(
|
||||
Expr::cust("GROUP_CONCAT(\"samey_tag\".\"name\", ' ')"),
|
||||
"tags",
|
||||
|
|
@ -73,6 +75,7 @@ pub(crate) fn search_posts(
|
|||
.select_only()
|
||||
.column(samey_post::Column::Id)
|
||||
.column(samey_post::Column::Thumbnail)
|
||||
.column(samey_post::Column::Rating)
|
||||
.column_as(
|
||||
Expr::cust("GROUP_CONCAT(\"samey_tag\".\"name\", ' ')"),
|
||||
"tags",
|
||||
|
|
@ -130,20 +133,10 @@ pub(crate) fn search_posts(
|
|||
query
|
||||
};
|
||||
|
||||
query = match user {
|
||||
None => query.filter(samey_post::Column::IsPublic.into_simple_expr()),
|
||||
Some(user) if !user.is_admin => query.filter(
|
||||
Condition::any()
|
||||
.add(samey_post::Column::IsPublic.into_simple_expr())
|
||||
.add(samey_post::Column::UploaderId.eq(user.id)),
|
||||
),
|
||||
_ => query,
|
||||
};
|
||||
|
||||
query
|
||||
filter_by_user(query, user)
|
||||
.group_by(samey_post::Column::Id)
|
||||
.order_by_desc(samey_post::Column::Id)
|
||||
.into_model::<SearchPost>()
|
||||
.into_model::<PostOverview>()
|
||||
}
|
||||
|
||||
pub(crate) fn get_tags_for_post(post_id: i32) -> Select<SameyTag> {
|
||||
|
|
@ -152,3 +145,15 @@ pub(crate) fn get_tags_for_post(post_id: i32) -> Select<SameyTag> {
|
|||
.filter(samey_tag_post::Column::PostId.eq(post_id))
|
||||
.order_by_asc(samey_tag::Column::Name)
|
||||
}
|
||||
|
||||
pub(crate) fn filter_by_user(query: Select<SameyPost>, user: Option<&User>) -> Select<SameyPost> {
|
||||
match user {
|
||||
None => query.filter(samey_post::Column::IsPublic.into_simple_expr()),
|
||||
Some(user) if !user.is_admin => query.filter(
|
||||
Condition::any()
|
||||
.add(samey_post::Column::IsPublic.into_simple_expr())
|
||||
.add(samey_post::Column::UploaderId.eq(user.id)),
|
||||
),
|
||||
_ => query,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
91
src/views.rs
91
src/views.rs
|
|
@ -30,7 +30,7 @@ use crate::{
|
|||
samey_post, samey_post_source, samey_tag, samey_tag_post,
|
||||
},
|
||||
error::SameyError,
|
||||
query::{SearchPost, get_tags_for_post, search_posts},
|
||||
query::{PostOverview, filter_by_user, get_tags_for_post, search_posts},
|
||||
};
|
||||
|
||||
const MAX_THUMBNAIL_DIMENSION: u32 = 192;
|
||||
|
|
@ -350,7 +350,7 @@ pub(crate) async fn select_tag(
|
|||
struct PostsTemplate<'a> {
|
||||
tags: Option<Vec<&'a str>>,
|
||||
tags_text: Option<String>,
|
||||
posts: Vec<SearchPost>,
|
||||
posts: Vec<PostOverview>,
|
||||
page: u32,
|
||||
page_count: u64,
|
||||
}
|
||||
|
|
@ -378,7 +378,7 @@ pub(crate) async fn posts_page(
|
|||
.tags
|
||||
.as_ref()
|
||||
.map(|tags| tags.split_whitespace().collect::<Vec<_>>());
|
||||
let pagination = search_posts(tags.as_ref(), auth_session.user).paginate(&db, 50);
|
||||
let pagination = search_posts(tags.as_ref(), auth_session.user.as_ref()).paginate(&db, 50);
|
||||
let page_count = pagination.num_pages().await?;
|
||||
let posts = pagination.fetch_page(page.saturating_sub(1) as u64).await?;
|
||||
let posts = posts
|
||||
|
|
@ -386,7 +386,7 @@ pub(crate) async fn posts_page(
|
|||
.map(|post| {
|
||||
let mut tags_vec: Vec<_> = post.tags.split_ascii_whitespace().collect();
|
||||
tags_vec.sort();
|
||||
SearchPost {
|
||||
PostOverview {
|
||||
tags: tags_vec.into_iter().join(" "),
|
||||
..post
|
||||
}
|
||||
|
|
@ -412,8 +412,11 @@ pub(crate) async fn posts_page(
|
|||
struct ViewPostTemplate {
|
||||
post: samey_post::Model,
|
||||
tags: Vec<samey_tag::Model>,
|
||||
tags_text: String,
|
||||
sources: Vec<samey_post_source::Model>,
|
||||
can_edit: bool,
|
||||
parent_post: Option<PostOverview>,
|
||||
children_posts: Vec<PostOverview>,
|
||||
}
|
||||
|
||||
pub(crate) async fn view_post(
|
||||
|
|
@ -421,18 +424,64 @@ pub(crate) async fn view_post(
|
|||
auth_session: AuthSession,
|
||||
Path(post_id): Path<u32>,
|
||||
) -> Result<impl IntoResponse, SameyError> {
|
||||
let tags = get_tags_for_post(post_id as i32).all(&db).await?;
|
||||
let post_id = post_id as i32;
|
||||
let tags = get_tags_for_post(post_id).all(&db).await?;
|
||||
let tags_text = tags.iter().map(|tag| &tag.name).join(" ");
|
||||
|
||||
let sources = SameyPostSource::find()
|
||||
.filter(samey_post_source::Column::PostId.eq(post_id))
|
||||
.all(&db)
|
||||
.await?;
|
||||
|
||||
let post = SameyPost::find_by_id(post_id as i32)
|
||||
let post = SameyPost::find_by_id(post_id)
|
||||
.one(&db)
|
||||
.await?
|
||||
.ok_or(SameyError::NotFound)?;
|
||||
|
||||
let parent_post = if let Some(parent_id) = post.parent_id {
|
||||
match filter_by_user(SameyPost::find_by_id(parent_id), auth_session.user.as_ref())
|
||||
.one(&db)
|
||||
.await?
|
||||
{
|
||||
Some(parent_post) => Some(PostOverview {
|
||||
id: parent_id,
|
||||
thumbnail: parent_post.thumbnail,
|
||||
tags: get_tags_for_post(post_id)
|
||||
.all(&db)
|
||||
.await?
|
||||
.iter()
|
||||
.map(|tag| &tag.name)
|
||||
.join(" "),
|
||||
rating: parent_post.rating,
|
||||
}),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let children_posts_models = filter_by_user(
|
||||
SameyPost::find().filter(samey_post::Column::ParentId.eq(post_id)),
|
||||
auth_session.user.as_ref(),
|
||||
)
|
||||
.all(&db)
|
||||
.await?;
|
||||
let mut children_posts = Vec::with_capacity(children_posts_models.capacity());
|
||||
|
||||
for child_post in children_posts_models.into_iter() {
|
||||
children_posts.push(PostOverview {
|
||||
id: child_post.id,
|
||||
thumbnail: child_post.thumbnail,
|
||||
tags: get_tags_for_post(child_post.id)
|
||||
.all(&db)
|
||||
.await?
|
||||
.iter()
|
||||
.map(|tag| &tag.name)
|
||||
.join(" "),
|
||||
rating: child_post.rating,
|
||||
});
|
||||
}
|
||||
|
||||
let can_edit = match auth_session.user {
|
||||
None => false,
|
||||
Some(user) => user.is_admin || post.uploader_id == user.id,
|
||||
|
|
@ -446,8 +495,11 @@ pub(crate) async fn view_post(
|
|||
ViewPostTemplate {
|
||||
post,
|
||||
tags,
|
||||
tags_text,
|
||||
sources,
|
||||
can_edit,
|
||||
parent_post,
|
||||
children_posts,
|
||||
}
|
||||
.render()?,
|
||||
))
|
||||
|
|
@ -505,12 +557,14 @@ pub(crate) struct SubmitPostDetailsForm {
|
|||
#[serde(rename = "source")]
|
||||
sources: Option<Vec<String>>,
|
||||
tags: String,
|
||||
parent_post: String,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "submit_post_details.html")]
|
||||
struct SubmitPostDetailsTemplate {
|
||||
post: samey_post::Model,
|
||||
parent_post: Option<PostOverview>,
|
||||
sources: Vec<samey_post_source::Model>,
|
||||
tags: Vec<samey_tag::Model>,
|
||||
can_edit: bool,
|
||||
|
|
@ -529,7 +583,7 @@ pub(crate) async fn submit_post_details(
|
|||
.await?
|
||||
.ok_or(SameyError::NotFound)?;
|
||||
|
||||
match auth_session.user {
|
||||
match auth_session.user.as_ref() {
|
||||
None => return Err(SameyError::Forbidden),
|
||||
Some(user) => {
|
||||
if !user.is_admin && post.uploader_id != user.id {
|
||||
|
|
@ -546,6 +600,27 @@ pub(crate) async fn submit_post_details(
|
|||
description if description.is_empty() => None,
|
||||
description => Some(description.to_owned()),
|
||||
};
|
||||
let parent_post = if let Some(parent_id) = body.parent_post.trim().parse().ok() {
|
||||
match filter_by_user(SameyPost::find_by_id(parent_id), auth_session.user.as_ref())
|
||||
.one(&db)
|
||||
.await?
|
||||
{
|
||||
Some(parent_post) => Some(PostOverview {
|
||||
id: parent_id,
|
||||
thumbnail: parent_post.thumbnail,
|
||||
tags: get_tags_for_post(post_id)
|
||||
.all(&db)
|
||||
.await?
|
||||
.iter()
|
||||
.map(|tag| &tag.name)
|
||||
.join(" "),
|
||||
rating: parent_post.rating,
|
||||
}),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let is_public = body.is_public.is_some();
|
||||
let post = SameyPost::update(samey_post::ActiveModel {
|
||||
id: Set(post_id),
|
||||
|
|
@ -553,6 +628,7 @@ pub(crate) async fn submit_post_details(
|
|||
description: Set(description),
|
||||
is_public: Set(is_public),
|
||||
rating: Set(body.rating),
|
||||
parent_id: Set(parent_post.as_ref().map(|post| post.id)),
|
||||
..Default::default()
|
||||
})
|
||||
.exec(&db)
|
||||
|
|
@ -622,6 +698,7 @@ pub(crate) async fn submit_post_details(
|
|||
post,
|
||||
sources,
|
||||
tags: upload_tags,
|
||||
parent_post,
|
||||
can_edit: true,
|
||||
}
|
||||
.render()?,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue