mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-10-01 01:36:12 -04:00
Merge branch 'main' into vote_display_mode
This commit is contained in:
commit
3b9e7f10b8
@ -18,7 +18,7 @@ doctest = false
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { workspace = true, features = ["default"] }
|
||||
lemmy_utils = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views_moderator = { workspace = true, features = ["full"] }
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::check_totp_2fa_valid;
|
||||
use crate::{check_totp_2fa_valid, local_user::check_email_verified};
|
||||
use actix_web::{
|
||||
web::{Data, Json},
|
||||
HttpRequest,
|
||||
@ -43,15 +43,7 @@ pub async fn login(
|
||||
Err(LemmyErrorType::IncorrectLogin)?
|
||||
}
|
||||
check_user_valid(&local_user_view.person)?;
|
||||
|
||||
// Check if the user's email is verified if email verification is turned on
|
||||
// However, skip checking verification if the user is an admin
|
||||
if !local_user_view.local_user.admin
|
||||
&& site_view.local_site.require_email_verification
|
||||
&& !local_user_view.local_user.email_verified
|
||||
{
|
||||
Err(LemmyErrorType::EmailNotVerified)?
|
||||
}
|
||||
check_email_verified(&local_user_view, &site_view)?;
|
||||
|
||||
check_registration_application(&local_user_view, &site_view.local_site, &mut context.pool())
|
||||
.await?;
|
||||
|
@ -1,3 +1,6 @@
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||
|
||||
pub mod add_admin;
|
||||
pub mod ban_person;
|
||||
pub mod block;
|
||||
@ -16,3 +19,15 @@ pub mod save_settings;
|
||||
pub mod update_totp;
|
||||
pub mod validate_auth;
|
||||
pub mod verify_email;
|
||||
|
||||
/// Check if the user's email is verified if email verification is turned on
|
||||
/// However, skip checking verification if the user is an admin
|
||||
fn check_email_verified(local_user_view: &LocalUserView, site_view: &SiteView) -> LemmyResult<()> {
|
||||
if !local_user_view.local_user.admin
|
||||
&& site_view.local_site.require_email_verification
|
||||
&& !local_user_view.local_user.email_verified
|
||||
{
|
||||
Err(LemmyErrorType::EmailNotVerified)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::local_user::check_email_verified;
|
||||
use actix_web::web::{Data, Json};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
@ -6,7 +7,7 @@ use lemmy_api_common::{
|
||||
SuccessResponse,
|
||||
};
|
||||
use lemmy_db_schema::source::password_reset_request::PasswordResetRequest;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
@ -29,6 +30,8 @@ pub async fn reset_password(
|
||||
if recent_resets_count >= 3 {
|
||||
Err(LemmyErrorType::PasswordResetLimitReached)?
|
||||
}
|
||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
check_email_verified(&local_user_view, &site_view)?;
|
||||
|
||||
// Email the pure token to the user.
|
||||
send_password_reset_email(&local_user_view, &mut context.pool(), context.settings()).await?;
|
||||
|
34
crates/api/src/post/hide.rs
Normal file
34
crates/api/src/post/hide.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use actix_web::web::{Data, Json};
|
||||
use lemmy_api_common::{context::LemmyContext, post::HidePost, SuccessResponse};
|
||||
use lemmy_db_schema::source::post::PostHide;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, MAX_API_PARAM_ELEMENTS};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn hide_post(
|
||||
data: Json<HidePost>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> Result<Json<SuccessResponse>, LemmyError> {
|
||||
let post_ids = HashSet::from_iter(data.post_ids.clone());
|
||||
|
||||
if post_ids.len() > MAX_API_PARAM_ELEMENTS {
|
||||
Err(LemmyErrorType::TooManyItems)?;
|
||||
}
|
||||
|
||||
let person_id = local_user_view.person.id;
|
||||
|
||||
// Mark the post as hidden / unhidden
|
||||
if data.hide {
|
||||
PostHide::hide(&mut context.pool(), post_ids, person_id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntHidePost)?;
|
||||
} else {
|
||||
PostHide::unhide(&mut context.pool(), post_ids, person_id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntHidePost)?;
|
||||
}
|
||||
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
}
|
@ -11,14 +11,7 @@ pub async fn mark_post_as_read(
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> Result<Json<SuccessResponse>, LemmyError> {
|
||||
let mut post_ids = HashSet::new();
|
||||
if let Some(post_ids_) = &data.post_ids {
|
||||
post_ids.extend(post_ids_.iter().cloned());
|
||||
}
|
||||
|
||||
if let Some(post_id) = data.post_id {
|
||||
post_ids.insert(post_id);
|
||||
}
|
||||
let post_ids = HashSet::from_iter(data.post_ids.clone());
|
||||
|
||||
if post_ids.len() > MAX_API_PARAM_ELEMENTS {
|
||||
Err(LemmyErrorType::TooManyItems)?;
|
||||
|
@ -1,5 +1,6 @@
|
||||
pub mod feature;
|
||||
pub mod get_link_metadata;
|
||||
pub mod hide;
|
||||
pub mod like;
|
||||
pub mod list_post_likes;
|
||||
pub mod lock;
|
||||
|
@ -23,7 +23,7 @@ full = [
|
||||
"lemmy_db_views/full",
|
||||
"lemmy_db_views_actor/full",
|
||||
"lemmy_db_views_moderator/full",
|
||||
"lemmy_utils/default",
|
||||
"lemmy_utils/full",
|
||||
"activitypub_federation",
|
||||
"encoding",
|
||||
"reqwest-middleware",
|
||||
@ -44,7 +44,7 @@ lemmy_db_views = { workspace = true }
|
||||
lemmy_db_views_moderator = { workspace = true }
|
||||
lemmy_db_views_actor = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true }
|
||||
lemmy_utils = { workspace = true, features = ["error-type"] }
|
||||
lemmy_utils = { workspace = true }
|
||||
activitypub_federation = { workspace = true, optional = true }
|
||||
serde = { workspace = true }
|
||||
serde_with = { workspace = true }
|
||||
|
@ -79,6 +79,7 @@ pub struct GetPosts {
|
||||
pub saved_only: Option<bool>,
|
||||
pub liked_only: Option<bool>,
|
||||
pub disliked_only: Option<bool>,
|
||||
pub show_hidden: Option<bool>,
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
}
|
||||
|
||||
@ -148,12 +149,20 @@ pub struct RemovePost {
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Mark a post as read.
|
||||
pub struct MarkPostAsRead {
|
||||
/// TODO: deprecated, send `post_ids` instead
|
||||
pub post_id: Option<PostId>,
|
||||
pub post_ids: Option<Vec<PostId>>,
|
||||
pub post_ids: Vec<PostId>,
|
||||
pub read: bool,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Hide a post from list views
|
||||
pub struct HidePost {
|
||||
pub post_ids: Vec<PostId>,
|
||||
pub hide: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
|
@ -13,7 +13,7 @@ repository.workspace = true
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { workspace = true, features = ["default"] }
|
||||
lemmy_utils = { workspace = true, features = ["full"] }
|
||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views_actor = { workspace = true, features = ["full"] }
|
||||
|
@ -18,7 +18,7 @@ doctest = false
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { workspace = true, features = ["default"] }
|
||||
lemmy_utils = { workspace = true, features = ["full"] }
|
||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views_actor = { workspace = true, features = ["full"] }
|
||||
|
@ -36,6 +36,7 @@ pub async fn list_posts(
|
||||
data.community_id
|
||||
};
|
||||
let saved_only = data.saved_only.unwrap_or_default();
|
||||
let show_hidden = data.show_hidden.unwrap_or_default();
|
||||
|
||||
let liked_only = data.liked_only.unwrap_or_default();
|
||||
let disliked_only = data.disliked_only.unwrap_or_default();
|
||||
@ -75,6 +76,7 @@ pub async fn list_posts(
|
||||
page,
|
||||
page_after,
|
||||
limit,
|
||||
show_hidden,
|
||||
..Default::default()
|
||||
}
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
|
@ -19,6 +19,6 @@ diesel = { workspace = true }
|
||||
diesel-async = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true }
|
||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||
lemmy_utils = { workspace = true, features = ["default"] }
|
||||
lemmy_utils = { workspace = true, features = ["full"] }
|
||||
tokio = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
@ -18,6 +18,7 @@ workspace = true
|
||||
|
||||
[features]
|
||||
full = [
|
||||
"lemmy_utils/full",
|
||||
"diesel",
|
||||
"diesel-derive-newtype",
|
||||
"diesel-derive-enum",
|
||||
@ -48,7 +49,7 @@ strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
activitypub_federation = { workspace = true, optional = true }
|
||||
lemmy_utils = { workspace = true, optional = true, features = ["default"] }
|
||||
lemmy_utils = { workspace = true, optional = true }
|
||||
bcrypt = { workspace = true, optional = true }
|
||||
diesel = { workspace = true, features = [
|
||||
"postgres",
|
||||
|
@ -1,23 +1,10 @@
|
||||
use crate::{
|
||||
newtypes::{CommunityId, DbUrl, PersonId, PostId},
|
||||
schema::post::dsl::{
|
||||
ap_id,
|
||||
body,
|
||||
community_id,
|
||||
creator_id,
|
||||
deleted,
|
||||
featured_community,
|
||||
local,
|
||||
name,
|
||||
post,
|
||||
published,
|
||||
removed,
|
||||
thumbnail_url,
|
||||
updated,
|
||||
url,
|
||||
},
|
||||
schema::{post, post_hide, post_like, post_read, post_saved},
|
||||
source::post::{
|
||||
Post,
|
||||
PostHide,
|
||||
PostHideForm,
|
||||
PostInsertForm,
|
||||
PostLike,
|
||||
PostLikeForm,
|
||||
@ -53,9 +40,9 @@ impl Crud for Post {
|
||||
|
||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(post)
|
||||
insert_into(post::table)
|
||||
.values(form)
|
||||
.on_conflict(ap_id)
|
||||
.on_conflict(post::ap_id)
|
||||
.do_update()
|
||||
.set(form)
|
||||
.get_result::<Self>(conn)
|
||||
@ -68,7 +55,7 @@ impl Crud for Post {
|
||||
new_post: &Self::UpdateForm,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(post.find(post_id))
|
||||
diesel::update(post::table.find(post_id))
|
||||
.set(new_post)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
@ -81,12 +68,12 @@ impl Post {
|
||||
the_community_id: CommunityId,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
post
|
||||
.filter(community_id.eq(the_community_id))
|
||||
.filter(deleted.eq(false))
|
||||
.filter(removed.eq(false))
|
||||
.then_order_by(featured_community.desc())
|
||||
.then_order_by(published.desc())
|
||||
post::table
|
||||
.filter(post::community_id.eq(the_community_id))
|
||||
.filter(post::deleted.eq(false))
|
||||
.filter(post::removed.eq(false))
|
||||
.then_order_by(post::featured_community.desc())
|
||||
.then_order_by(post::published.desc())
|
||||
.limit(FETCH_LIMIT_MAX)
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
@ -97,12 +84,12 @@ impl Post {
|
||||
the_community_id: CommunityId,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
post
|
||||
.filter(community_id.eq(the_community_id))
|
||||
.filter(deleted.eq(false))
|
||||
.filter(removed.eq(false))
|
||||
.filter(featured_community.eq(true))
|
||||
.then_order_by(published.desc())
|
||||
post::table
|
||||
.filter(post::community_id.eq(the_community_id))
|
||||
.filter(post::deleted.eq(false))
|
||||
.filter(post::removed.eq(false))
|
||||
.filter(post::featured_community.eq(true))
|
||||
.then_order_by(post::published.desc())
|
||||
.limit(FETCH_LIMIT_MAX)
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
@ -112,13 +99,13 @@ impl Post {
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> Result<Vec<(DbUrl, chrono::DateTime<Utc>)>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
post
|
||||
.select((ap_id, coalesce(updated, published)))
|
||||
.filter(local.eq(true))
|
||||
.filter(deleted.eq(false))
|
||||
.filter(removed.eq(false))
|
||||
.filter(published.ge(Utc::now().naive_utc() - Duration::days(SITEMAP_DAYS)))
|
||||
.order(published.desc())
|
||||
post::table
|
||||
.select((post::ap_id, coalesce(post::updated, post::published)))
|
||||
.filter(post::local.eq(true))
|
||||
.filter(post::deleted.eq(false))
|
||||
.filter(post::removed.eq(false))
|
||||
.filter(post::published.ge(Utc::now().naive_utc() - Duration::days(SITEMAP_DAYS)))
|
||||
.order(post::published.desc())
|
||||
.limit(SITEMAP_LIMIT)
|
||||
.load::<(DbUrl, chrono::DateTime<Utc>)>(conn)
|
||||
.await
|
||||
@ -130,13 +117,13 @@ impl Post {
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
diesel::update(post.filter(creator_id.eq(for_creator_id)))
|
||||
diesel::update(post::table.filter(post::creator_id.eq(for_creator_id)))
|
||||
.set((
|
||||
name.eq(DELETED_REPLACEMENT_TEXT),
|
||||
url.eq(Option::<&str>::None),
|
||||
body.eq(DELETED_REPLACEMENT_TEXT),
|
||||
deleted.eq(true),
|
||||
updated.eq(naive_now()),
|
||||
post::name.eq(DELETED_REPLACEMENT_TEXT),
|
||||
post::url.eq(Option::<&str>::None),
|
||||
post::body.eq(DELETED_REPLACEMENT_TEXT),
|
||||
post::deleted.eq(true),
|
||||
post::updated.eq(naive_now()),
|
||||
))
|
||||
.get_results::<Self>(conn)
|
||||
.await
|
||||
@ -150,15 +137,15 @@ impl Post {
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
let mut update = diesel::update(post).into_boxed();
|
||||
update = update.filter(creator_id.eq(for_creator_id));
|
||||
let mut update = diesel::update(post::table).into_boxed();
|
||||
update = update.filter(post::creator_id.eq(for_creator_id));
|
||||
|
||||
if let Some(for_community_id) = for_community_id {
|
||||
update = update.filter(community_id.eq(for_community_id));
|
||||
update = update.filter(post::community_id.eq(for_community_id));
|
||||
}
|
||||
|
||||
update
|
||||
.set((removed.eq(new_removed), updated.eq(naive_now())))
|
||||
.set((post::removed.eq(new_removed), post::updated.eq(naive_now())))
|
||||
.get_results::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
@ -174,8 +161,8 @@ impl Post {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let object_id: DbUrl = object_id.into();
|
||||
Ok(
|
||||
post
|
||||
.filter(ap_id.eq(object_id))
|
||||
post::table
|
||||
.filter(post::ap_id.eq(object_id))
|
||||
.first::<Post>(conn)
|
||||
.await
|
||||
.ok()
|
||||
@ -190,9 +177,9 @@ impl Post {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let pictrs_search = "%pictrs/image%";
|
||||
|
||||
post
|
||||
.filter(creator_id.eq(for_creator_id))
|
||||
.filter(url.like(pictrs_search))
|
||||
post::table
|
||||
.filter(post::creator_id.eq(for_creator_id))
|
||||
.filter(post::url.like(pictrs_search))
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
@ -206,13 +193,13 @@ impl Post {
|
||||
let pictrs_search = "%pictrs/image%";
|
||||
|
||||
diesel::update(
|
||||
post
|
||||
.filter(creator_id.eq(for_creator_id))
|
||||
.filter(url.like(pictrs_search)),
|
||||
post::table
|
||||
.filter(post::creator_id.eq(for_creator_id))
|
||||
.filter(post::url.like(pictrs_search)),
|
||||
)
|
||||
.set((
|
||||
url.eq::<Option<String>>(None),
|
||||
thumbnail_url.eq::<Option<String>>(None),
|
||||
post::url.eq::<Option<String>>(None),
|
||||
post::thumbnail_url.eq::<Option<String>>(None),
|
||||
))
|
||||
.get_results::<Self>(conn)
|
||||
.await
|
||||
@ -224,9 +211,9 @@ impl Post {
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let pictrs_search = "%pictrs/image%";
|
||||
post
|
||||
.filter(community_id.eq(for_community_id))
|
||||
.filter(url.like(pictrs_search))
|
||||
post::table
|
||||
.filter(post::community_id.eq(for_community_id))
|
||||
.filter(post::url.like(pictrs_search))
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
@ -240,13 +227,13 @@ impl Post {
|
||||
let pictrs_search = "%pictrs/image%";
|
||||
|
||||
diesel::update(
|
||||
post
|
||||
.filter(community_id.eq(for_community_id))
|
||||
.filter(url.like(pictrs_search)),
|
||||
post::table
|
||||
.filter(post::community_id.eq(for_community_id))
|
||||
.filter(post::url.like(pictrs_search)),
|
||||
)
|
||||
.set((
|
||||
url.eq::<Option<String>>(None),
|
||||
thumbnail_url.eq::<Option<String>>(None),
|
||||
post::url.eq::<Option<String>>(None),
|
||||
post::thumbnail_url.eq::<Option<String>>(None),
|
||||
))
|
||||
.get_results::<Self>(conn)
|
||||
.await
|
||||
@ -258,11 +245,10 @@ impl Likeable for PostLike {
|
||||
type Form = PostLikeForm;
|
||||
type IdType = PostId;
|
||||
async fn like(pool: &mut DbPool<'_>, post_like_form: &PostLikeForm) -> Result<Self, Error> {
|
||||
use crate::schema::post_like::dsl::{person_id, post_id, post_like};
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(post_like)
|
||||
insert_into(post_like::table)
|
||||
.values(post_like_form)
|
||||
.on_conflict((post_id, person_id))
|
||||
.on_conflict((post_like::post_id, post_like::person_id))
|
||||
.do_update()
|
||||
.set(post_like_form)
|
||||
.get_result::<Self>(conn)
|
||||
@ -273,9 +259,8 @@ impl Likeable for PostLike {
|
||||
person_id: PersonId,
|
||||
post_id: PostId,
|
||||
) -> Result<usize, Error> {
|
||||
use crate::schema::post_like::dsl;
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::delete(dsl::post_like.find((person_id, post_id)))
|
||||
diesel::delete(post_like::table.find((person_id, post_id)))
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
@ -285,20 +270,18 @@ impl Likeable for PostLike {
|
||||
impl Saveable for PostSaved {
|
||||
type Form = PostSavedForm;
|
||||
async fn save(pool: &mut DbPool<'_>, post_saved_form: &PostSavedForm) -> Result<Self, Error> {
|
||||
use crate::schema::post_saved::dsl::{person_id, post_id, post_saved};
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(post_saved)
|
||||
insert_into(post_saved::table)
|
||||
.values(post_saved_form)
|
||||
.on_conflict((post_id, person_id))
|
||||
.on_conflict((post_saved::post_id, post_saved::person_id))
|
||||
.do_update()
|
||||
.set(post_saved_form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
async fn unsave(pool: &mut DbPool<'_>, post_saved_form: &PostSavedForm) -> Result<usize, Error> {
|
||||
use crate::schema::post_saved::dsl::post_saved;
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::delete(post_saved.find((post_saved_form.person_id, post_saved_form.post_id)))
|
||||
diesel::delete(post_saved::table.find((post_saved_form.person_id, post_saved_form.post_id)))
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
@ -310,14 +293,13 @@ impl PostRead {
|
||||
post_ids: HashSet<PostId>,
|
||||
person_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
use crate::schema::post_read::dsl::post_read;
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
let forms = post_ids
|
||||
.into_iter()
|
||||
.map(|post_id| PostReadForm { post_id, person_id })
|
||||
.collect::<Vec<PostReadForm>>();
|
||||
insert_into(post_read)
|
||||
insert_into(post_read::table)
|
||||
.values(forms)
|
||||
.on_conflict_do_nothing()
|
||||
.execute(conn)
|
||||
@ -329,13 +311,48 @@ impl PostRead {
|
||||
post_id_: HashSet<PostId>,
|
||||
person_id_: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
use crate::schema::post_read::dsl::{person_id, post_id, post_read};
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
diesel::delete(
|
||||
post_read
|
||||
.filter(post_id.eq_any(post_id_))
|
||||
.filter(person_id.eq(person_id_)),
|
||||
post_read::table
|
||||
.filter(post_read::post_id.eq_any(post_id_))
|
||||
.filter(post_read::person_id.eq(person_id_)),
|
||||
)
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl PostHide {
|
||||
pub async fn hide(
|
||||
pool: &mut DbPool<'_>,
|
||||
post_ids: HashSet<PostId>,
|
||||
person_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
let forms = post_ids
|
||||
.into_iter()
|
||||
.map(|post_id| PostHideForm { post_id, person_id })
|
||||
.collect::<Vec<PostHideForm>>();
|
||||
insert_into(post_hide::table)
|
||||
.values(forms)
|
||||
.on_conflict_do_nothing()
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn unhide(
|
||||
pool: &mut DbPool<'_>,
|
||||
post_id_: HashSet<PostId>,
|
||||
person_id_: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
diesel::delete(
|
||||
post_hide::table
|
||||
.filter(post_hide::post_id.eq_any(post_id_))
|
||||
.filter(post_hide::person_id.eq(person_id_)),
|
||||
)
|
||||
.execute(conn)
|
||||
.await
|
||||
|
@ -736,6 +736,14 @@ diesel::table! {
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
post_hide (person_id, post_id) {
|
||||
post_id -> Int4,
|
||||
person_id -> Int4,
|
||||
published -> Timestamptz,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
post_like (person_id, post_id) {
|
||||
post_id -> Int4,
|
||||
@ -989,6 +997,8 @@ diesel::joinable!(post_aggregates -> community (community_id));
|
||||
diesel::joinable!(post_aggregates -> instance (instance_id));
|
||||
diesel::joinable!(post_aggregates -> person (creator_id));
|
||||
diesel::joinable!(post_aggregates -> post (post_id));
|
||||
diesel::joinable!(post_hide -> person (person_id));
|
||||
diesel::joinable!(post_hide -> post (post_id));
|
||||
diesel::joinable!(post_like -> person (person_id));
|
||||
diesel::joinable!(post_like -> post (post_id));
|
||||
diesel::joinable!(post_read -> person (person_id));
|
||||
@ -1060,6 +1070,7 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||
person_post_aggregates,
|
||||
post,
|
||||
post_aggregates,
|
||||
post_hide,
|
||||
post_like,
|
||||
post_read,
|
||||
post_report,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::newtypes::{CommunityId, DbUrl, LanguageId, PersonId, PostId};
|
||||
#[cfg(feature = "full")]
|
||||
use crate::schema::{post, post_like, post_read, post_saved};
|
||||
use crate::schema::{post, post_hide, post_like, post_read, post_saved};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
@ -182,3 +182,25 @@ pub(crate) struct PostReadForm {
|
||||
pub post_id: PostId,
|
||||
pub person_id: PersonId,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Identifiable, Queryable, Selectable, Associations)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = post_hide))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(post_id, person_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct PostHide {
|
||||
pub post_id: PostId,
|
||||
pub person_id: PersonId,
|
||||
pub published: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = post_hide))]
|
||||
pub(crate) struct PostHideForm {
|
||||
pub post_id: PostId,
|
||||
pub person_id: PersonId,
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ full = [
|
||||
|
||||
[dependencies]
|
||||
lemmy_db_schema = { workspace = true }
|
||||
lemmy_utils = { workspace = true, optional = true, features = ["default"] }
|
||||
lemmy_utils = { workspace = true, optional = true }
|
||||
diesel = { workspace = true, optional = true }
|
||||
diesel-async = { workspace = true, optional = true }
|
||||
diesel_ltree = { workspace = true, optional = true }
|
||||
|
@ -35,6 +35,7 @@ use lemmy_db_schema::{
|
||||
person_post_aggregates,
|
||||
post,
|
||||
post_aggregates,
|
||||
post_hide,
|
||||
post_like,
|
||||
post_read,
|
||||
post_saved,
|
||||
@ -107,6 +108,16 @@ fn queries<'a>() -> Queries<
|
||||
)
|
||||
};
|
||||
|
||||
let is_hidden = |person_id| {
|
||||
exists(
|
||||
post_hide::table.filter(
|
||||
post_aggregates::post_id
|
||||
.eq(post_hide::post_id)
|
||||
.and(post_hide::person_id.eq(person_id)),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
let is_creator_blocked = |person_id| {
|
||||
exists(
|
||||
person_block::table.filter(
|
||||
@ -147,6 +158,13 @@ fn queries<'a>() -> Queries<
|
||||
Box::new(false.into_sql::<sql_types::Bool>())
|
||||
};
|
||||
|
||||
let is_hidden_selection: Box<dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>> =
|
||||
if let Some(person_id) = my_person_id {
|
||||
Box::new(is_hidden(person_id))
|
||||
} else {
|
||||
Box::new(false.into_sql::<sql_types::Bool>())
|
||||
};
|
||||
|
||||
let is_creator_blocked_selection: Box<dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>> =
|
||||
if let Some(person_id) = my_person_id {
|
||||
Box::new(is_creator_blocked(person_id))
|
||||
@ -211,6 +229,7 @@ fn queries<'a>() -> Queries<
|
||||
subscribed_type_selection,
|
||||
is_saved_selection,
|
||||
is_read_selection,
|
||||
is_hidden_selection,
|
||||
is_creator_blocked_selection,
|
||||
score_selection,
|
||||
coalesce(
|
||||
@ -406,6 +425,13 @@ fn queries<'a>() -> Queries<
|
||||
}
|
||||
}
|
||||
|
||||
if !options.show_hidden {
|
||||
// If a creator id isn't given (IE its on home or community pages), hide the hidden posts
|
||||
if let (None, Some(person_id)) = (options.creator_id, my_person_id) {
|
||||
query = query.filter(not(is_hidden(person_id)));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(person_id) = my_person_id {
|
||||
if options.liked_only {
|
||||
query = query.filter(score(person_id).eq(1));
|
||||
@ -593,6 +619,7 @@ pub struct PostQuery<'a> {
|
||||
pub page_after: Option<PaginationCursorData>,
|
||||
pub page_before_or_equal: Option<PaginationCursorData>,
|
||||
pub page_back: bool,
|
||||
pub show_hidden: bool,
|
||||
}
|
||||
|
||||
impl<'a> PostQuery<'a> {
|
||||
@ -726,7 +753,7 @@ mod tests {
|
||||
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||
person::{Person, PersonInsertForm},
|
||||
person_block::{PersonBlock, PersonBlockForm},
|
||||
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostRead, PostUpdateForm},
|
||||
post::{Post, PostHide, PostInsertForm, PostLike, PostLikeForm, PostRead, PostUpdateForm},
|
||||
site::Site,
|
||||
},
|
||||
traits::{Blockable, Crud, Joinable, Likeable},
|
||||
@ -1463,6 +1490,47 @@ mod tests {
|
||||
cleanup(data, pool).await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn post_listings_hide_hidden() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool().await?;
|
||||
let pool = &mut pool.into();
|
||||
let data = init_data(pool).await?;
|
||||
|
||||
// Mark a post as hidden
|
||||
PostHide::hide(
|
||||
pool,
|
||||
HashSet::from([data.inserted_bot_post.id]),
|
||||
data.local_user_view.person.id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Make sure you don't see the hidden post in the results
|
||||
let post_listings_hide_hidden = data.default_post_query().list(&data.site, pool).await?;
|
||||
assert_eq!(vec![POST], names(&post_listings_hide_hidden));
|
||||
|
||||
// Make sure it does come back with the show_hidden option
|
||||
let post_listings_show_hidden = PostQuery {
|
||||
sort: Some(SortType::New),
|
||||
local_user: Some(&data.local_user_view),
|
||||
show_hidden: true,
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
assert_eq!(vec![POST_BY_BOT, POST], names(&post_listings_show_hidden));
|
||||
|
||||
// Make sure that hidden field is true.
|
||||
assert!(
|
||||
&post_listings_show_hidden
|
||||
.first()
|
||||
.expect("first post should exist")
|
||||
.hidden
|
||||
);
|
||||
|
||||
cleanup(data, pool).await
|
||||
}
|
||||
|
||||
async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> {
|
||||
let num_deleted = Post::delete(pool, data.inserted_post.id).await?;
|
||||
Community::delete(pool, data.inserted_community.id).await?;
|
||||
@ -1584,6 +1652,7 @@ mod tests {
|
||||
},
|
||||
subscribed: SubscribedType::NotSubscribed,
|
||||
read: false,
|
||||
hidden: false,
|
||||
saved: false,
|
||||
creator_blocked: false,
|
||||
})
|
||||
|
@ -120,6 +120,7 @@ pub struct PostView {
|
||||
pub subscribed: SubscribedType,
|
||||
pub saved: bool,
|
||||
pub read: bool,
|
||||
pub hidden: bool,
|
||||
pub creator_blocked: bool,
|
||||
pub my_vote: Option<i16>,
|
||||
pub unread_comments: i64,
|
||||
|
@ -16,7 +16,7 @@ doctest = false
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { workspace = true, features = ["default"] }
|
||||
lemmy_utils = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views = { workspace = true }
|
||||
lemmy_db_views_actor = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true }
|
||||
|
@ -16,23 +16,24 @@ doctest = false
|
||||
[[bin]]
|
||||
name = "lemmy_util_bin"
|
||||
path = "src/main.rs"
|
||||
required-features = ["default"]
|
||||
required-features = ["full"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"error-type",
|
||||
"dep:serde_json",
|
||||
"dep:anyhow",
|
||||
"dep:tracing-error",
|
||||
full = [
|
||||
"dep:ts-rs",
|
||||
"dep:diesel",
|
||||
"dep:http",
|
||||
"dep:rosetta-i18n",
|
||||
"dep:actix-web",
|
||||
"dep:reqwest-middleware",
|
||||
"dep:tracing",
|
||||
"dep:actix-web",
|
||||
"dep:serde_json",
|
||||
"dep:anyhow",
|
||||
"dep:tracing-error",
|
||||
"dep:http",
|
||||
"dep:deser-hjson",
|
||||
"dep:regex",
|
||||
"dep:urlencoding",
|
||||
@ -47,27 +48,23 @@ default = [
|
||||
"dep:html2text",
|
||||
"dep:lettre",
|
||||
"dep:uuid",
|
||||
"dep:rosetta-i18n",
|
||||
"dep:itertools",
|
||||
"dep:markdown-it",
|
||||
|
||||
]
|
||||
full = ["default", "dep:ts-rs"]
|
||||
error-type = ["dep:serde", "dep:strum"]
|
||||
|
||||
[dependencies]
|
||||
regex = { workspace = true, optional = true }
|
||||
tracing = { workspace = true, optional = true }
|
||||
tracing-error = { workspace = true, optional = true }
|
||||
itertools = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
once_cell = { workspace = true, optional = true }
|
||||
url = { workspace = true, optional = true }
|
||||
actix-web = { workspace = true, optional = true }
|
||||
anyhow = { workspace = true, optional = true }
|
||||
reqwest-middleware = { workspace = true, optional = true }
|
||||
strum = { workspace = true, optional = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
futures = { workspace = true, optional = true }
|
||||
diesel = { workspace = true, features = ["chrono"], optional = true }
|
||||
|
@ -2,12 +2,10 @@ use cfg_if::cfg_if;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use strum_macros::{Display, EnumIter};
|
||||
#[cfg(feature = "ts-rs")]
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, EnumIter, Hash)]
|
||||
#[cfg_attr(feature = "ts-rs", derive(TS))]
|
||||
#[cfg_attr(feature = "ts-rs", ts(export))]
|
||||
#[cfg_attr(feature = "full", derive(ts_rs::TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
#[serde(tag = "error", content = "message", rename_all = "snake_case")]
|
||||
#[non_exhaustive]
|
||||
// TODO: order these based on the crate they belong to (utils, federation, db, api)
|
||||
@ -124,6 +122,7 @@ pub enum LemmyErrorType {
|
||||
CouldntLikePost,
|
||||
CouldntSavePost,
|
||||
CouldntMarkPostAsRead,
|
||||
CouldntHidePost,
|
||||
CouldntUpdateCommunity,
|
||||
CouldntUpdateReplies,
|
||||
CouldntUpdatePersonMentions,
|
||||
@ -168,7 +167,7 @@ pub enum LemmyErrorType {
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "default")] {
|
||||
if #[cfg(feature = "full")] {
|
||||
|
||||
use tracing_error::SpanTrace;
|
||||
use std::fmt;
|
||||
@ -276,52 +275,52 @@ cfg_if! {
|
||||
self.map_err(|e| e.inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
use super::*;
|
||||
use actix_web::{body::MessageBody, ResponseError};
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::fs::read_to_string;
|
||||
use strum::IntoEnumIterator;
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
use super::*;
|
||||
use actix_web::{body::MessageBody, ResponseError};
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::fs::read_to_string;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
#[test]
|
||||
fn deserializes_no_message() {
|
||||
let err = LemmyError::from(LemmyErrorType::Banned).error_response();
|
||||
let json = String::from_utf8(err.into_body().try_into_bytes().unwrap().to_vec()).unwrap();
|
||||
assert_eq!(&json, "{\"error\":\"banned\"}")
|
||||
}
|
||||
#[test]
|
||||
fn deserializes_no_message() {
|
||||
let err = LemmyError::from(LemmyErrorType::Banned).error_response();
|
||||
let json = String::from_utf8(err.into_body().try_into_bytes().unwrap().to_vec()).unwrap();
|
||||
assert_eq!(&json, "{\"error\":\"banned\"}")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserializes_with_message() {
|
||||
let reg_banned = LemmyErrorType::PersonIsBannedFromSite(String::from("reason"));
|
||||
let err = LemmyError::from(reg_banned).error_response();
|
||||
let json = String::from_utf8(err.into_body().try_into_bytes().unwrap().to_vec()).unwrap();
|
||||
assert_eq!(
|
||||
&json,
|
||||
"{\"error\":\"person_is_banned_from_site\",\"message\":\"reason\"}"
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
fn deserializes_with_message() {
|
||||
let reg_banned = LemmyErrorType::PersonIsBannedFromSite(String::from("reason"));
|
||||
let err = LemmyError::from(reg_banned).error_response();
|
||||
let json = String::from_utf8(err.into_body().try_into_bytes().unwrap().to_vec()).unwrap();
|
||||
assert_eq!(
|
||||
&json,
|
||||
"{\"error\":\"person_is_banned_from_site\",\"message\":\"reason\"}"
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if errors match translations. Disabled because many are not translated at all.
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_translations_match() {
|
||||
#[derive(Deserialize)]
|
||||
struct Err {
|
||||
error: String,
|
||||
/// Check if errors match translations. Disabled because many are not translated at all.
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_translations_match() {
|
||||
#[derive(Deserialize)]
|
||||
struct Err {
|
||||
error: String,
|
||||
}
|
||||
|
||||
let translations = read_to_string("translations/translations/en.json").unwrap();
|
||||
LemmyErrorType::iter().for_each(|e| {
|
||||
let msg = serde_json::to_string(&e).unwrap();
|
||||
let msg: Err = serde_json::from_str(&msg).unwrap();
|
||||
let msg = msg.error;
|
||||
assert!(translations.contains(&format!("\"{msg}\"")), "{msg}");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let translations = read_to_string("translations/translations/en.json").unwrap();
|
||||
LemmyErrorType::iter().for_each(|e| {
|
||||
let msg = serde_json::to_string(&e).unwrap();
|
||||
let msg: Err = serde_json::from_str(&msg).unwrap();
|
||||
let msg = msg.error;
|
||||
assert!(translations.contains(&format!("\"{msg}\"")), "{msg}");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,21 @@
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "default")] {
|
||||
if #[cfg(feature = "full")] {
|
||||
pub mod apub;
|
||||
pub mod cache_header;
|
||||
pub mod email;
|
||||
pub mod error;
|
||||
pub mod rate_limit;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod settings;
|
||||
pub mod utils;
|
||||
pub mod version;
|
||||
} else {
|
||||
mod error;
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "error-type")] {
|
||||
pub use error::LemmyErrorType;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod error;
|
||||
pub use error::LemmyErrorType;
|
||||
use std::time::Duration;
|
||||
|
||||
pub type ConnectionId = usize;
|
||||
@ -41,7 +34,7 @@ macro_rules! location_info {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "default")]
|
||||
#[cfg(feature = "full")]
|
||||
/// tokio::spawn, but accepts a future that may fail and also
|
||||
/// * logs errors
|
||||
/// * attaches the spawned task to the tracing span of the caller for better logging
|
||||
|
@ -1,16 +1,24 @@
|
||||
use doku::json::{AutoComments, CommentsStyle, Formatting, ObjectsStyle};
|
||||
use lemmy_utils::settings::structs::Settings;
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
fn main() {
|
||||
let fmt = Formatting {
|
||||
auto_comments: AutoComments::none(),
|
||||
comments_style: CommentsStyle {
|
||||
separator: "#".to_owned(),
|
||||
},
|
||||
objects_style: ObjectsStyle {
|
||||
surround_keys_with_quotes: false,
|
||||
use_comma_as_separator: false,
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
println!("{}", doku::to_json_fmt_val(&fmt, &Settings::default()));
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "full")] {
|
||||
use doku::json::{AutoComments, CommentsStyle, Formatting, ObjectsStyle};
|
||||
use lemmy_utils::settings::structs::Settings;
|
||||
let fmt = Formatting {
|
||||
auto_comments: AutoComments::none(),
|
||||
comments_style: CommentsStyle {
|
||||
separator: "#".to_owned(),
|
||||
},
|
||||
objects_style: ObjectsStyle {
|
||||
surround_keys_with_quotes: false,
|
||||
use_comma_as_separator: false,
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
println!("{}", doku::to_json_fmt_val(&fmt, &Settings::default()));
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
migrations/2024-02-28-144211_hide_posts/down.sql
Normal file
2
migrations/2024-02-28-144211_hide_posts/down.sql
Normal file
@ -0,0 +1,2 @@
|
||||
DROP TABLE post_hide;
|
||||
|
7
migrations/2024-02-28-144211_hide_posts/up.sql
Normal file
7
migrations/2024-02-28-144211_hide_posts/up.sql
Normal file
@ -0,0 +1,7 @@
|
||||
CREATE TABLE post_hide (
|
||||
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||
person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||
published timestamp with time zone NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (person_id, post_id)
|
||||
);
|
||||
|
@ -3,4 +3,4 @@ set -e
|
||||
|
||||
dest=${1-config/defaults.hjson}
|
||||
|
||||
cargo run --manifest-path crates/utils/Cargo.toml > "$dest"
|
||||
cargo run --manifest-path crates/utils/Cargo.toml --features full > "$dest"
|
||||
|
@ -49,6 +49,7 @@ use lemmy_api::{
|
||||
post::{
|
||||
feature::feature_post,
|
||||
get_link_metadata::get_link_metadata,
|
||||
hide::hide_post,
|
||||
like::like_post,
|
||||
list_post_likes::list_post_likes,
|
||||
lock::lock_post,
|
||||
@ -206,6 +207,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
|
||||
.route("/delete", web::post().to(delete_post))
|
||||
.route("/remove", web::post().to(remove_post))
|
||||
.route("/mark_as_read", web::post().to(mark_post_as_read))
|
||||
.route("/hide", web::post().to(hide_post))
|
||||
.route("/lock", web::post().to(lock_post))
|
||||
.route("/feature", web::post().to(feature_post))
|
||||
.route("/list", web::get().to(list_posts))
|
||||
|
Loading…
Reference in New Issue
Block a user