From aef4403412b64edfc2f109eec3cc5473c206217a Mon Sep 17 00:00:00 2001 From: Nutomic Date: Mon, 28 Oct 2024 16:07:13 +0100 Subject: [PATCH 01/19] Add test for unused errors, move federation errors into separate struct (#5024) * Add test case to ensure all errors are in use * Add test case to find errors that are only used for federation * Move federation errors into separate enum (fixes #4926) * clippy * clippy * remove serde tag * Reduce errors dess (#5138) * Add option to search exclusively by post title (#5015) * Add option to search exclusively by post title * Address format issues * Remove duplicated 'removed' filter * Replace url_search with search_term * Build generic PostQuery before search match * Create default queries. Move title_only to Search struct. Rename Url to PostURL * Revert PostUrl to Url * Upgrading webmention to 0.6.0, removes native-tls (#4976) * Removing embedded pict-rs. (#5023) Some reasons for removing this: - Even as an optional dependency, it locks us to many specific versions of rust deps. - Pict-rs is a large app that can and should be run in on its own. - Violates the philosophy of separation of concerns. * Adding clearurls crate to clean tracking params from links and markdown. (#5018) * Adding clearurls crate to clean tracking params from links and markdown. - Thanks to @jenrdikw for creating this - Fixes #4905 * Upgrading to new version of clearurls * Fix clippy * Remove enable nsfw (#5017) * Remove `local_site.enable_nsfw` in favor of `site.content_warning` (fixes #4627) * cleanup usage of SiteView::read_local * test * uppercase * SSO Support (#4881) * Added OAUTH2 OIDC support * Fixes and improvements based on review feedback * use derive_new::new instead of TypedBuilder * merge migrations into a single file * fixes based on review feedback * remove unnecessary hostname_ui config * improvement based on review feedback * improvements based on review feedback * delete user oauth accounts at account deletion * fixes and improvements based on review feedback * removed auto_approve_application * support registration application with sso * improvements based on review feedback * making the TokenResponse an internal struct as it should be * remove duplicate struct * prevent oauth linking to unverified accounts * switched to manually entered username and removed the oauth name claim * fix cargo fmt * fix compile error * improvements based on review feedback * fixes and improvements based on review feedback --------- Co-authored-by: privacyguard * Adding ability to restore content on user unban. (#4845) * Adding ability to restore content on user unban. - Fixes #4721 * Fixing api tests. * Fix package.json * Fixing lemmy-js-client dep. * Adding API test for restoring content. * Adding a default_comment_sort_type column for local_site and local_user. (#4469) * Adding a default_comment_sort_type column for local_site and local_user. - Renamed SortType to PostSortType in the DB and code. - Renamed references to default_sort_type to default_post_sort_type. - Fixes #4128 * Renaming migration to current date. * Simplifying PostSortType. * Simplify tests using default (#5026) * Feature/custom emoji and tagline views (#4580) * Add custom_emoji list route * Add tagline list route * Apply linting * Remove unecessary TaglineView * Add category filter for custom emoji * Add create tagline endpoint * Add update tagline endpoint * Add delete tagline endpoint * Format through lint.sh * Remove custom_emojis and taglines from site resource * Get random tagline on site requets * Impl Crud for Tagline Remove superfluous properties * Move tagline endpoints under /admin * Impl Crud for CustomEmoji * Remove delete from tagline and custom emoji impls * Check markdown for tagline * Validate markdown on tagline * Make content fields non optional Add error types for tagline validation * Use process_markdown instead of process_markdown_opt * Consolidate Tagline error types * Remove unecessary clone * Updat misleading comments * Remove local_site_id from tagline and custom_emoji * Update TaglineInserForm and TaglineUpdateForm * Add ignore_page_limits for custom emojis EmojiPicker needs to be able to retrieve all emojis in 1 call * Update custom_emoji_view Only keep get_all als helper function calling list with paging ignored Only order on category when filtering on category * Removing pointless get_all fn. * remove tagline length checks * make fields of TaglineInsertForm and TaglineUpdateForm mandatory * move emoji order statement * add comment for GetSiteResponse.tagline --------- Co-authored-by: Freakazoid182 <> Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com> Co-authored-by: Dessalines Co-authored-by: Felix Ableitner * Add category to RSS feeds (fixes #3446) (#5030) * Unittest for Search by title only (#5033) * added test for search by title only * formatted rust files * Upgrading to rust 1.81 (#5032) * Remove TypedBuilder in favor of derive_new (fixes #4863) (#5020) * Remove TypedBuilder in favor of derive_new (fixes #4863) * fix * fix * Ignore zero values when setting rate limits (fixes #4280) (#5029) * Ignore zero values when setting rate limits (fixes #4280) Havent bothered to add an error message for such an uncommon case. * fmt * reorder, add test * Always save remote image data (#4875) * Always save remote image data * cleanup --------- Co-authored-by: Felix Ableitner Co-authored-by: Dessalines * Get rid of a lot of pointless mut form initializations. (#5037) * Get rid of a lot of pointless mut form initializations. - Fixes #5036 * Fix clippy. * Simplify handling of NotFound SQL errors (fixes #4633) (#5031) * Simplify handling of NotFound SQL errors (fixes #4633) * fmt * wip * compiling * clippy * api tests * fix * Adding saved_only, liked_only, and disliked_only filters to search. (#5034) * Adding saved_only, liked_only, and disliked_only filters to search. - Fixes #4547 * Removing duplicate Url return type for search (was actually post). - This now works like the post_title_only filter. * Address PR comments. * Add saved_only post_view test. * Removing a few more Result . (#4977) * Removing a few more Result . * Running taplo fmt. * Running fmt. * Adding email taken test. * Fixing tests. * Adding back in missing admin check. * Rename check_has_local_followers function. * Conditionally hide comments on nsfw posts (fixes #4237) (#5028) * Conditionally hide comments on nsfw posts (fixes #4237) * fix test * Post scheduling (fixes #234) (#5025) * Post scheduling (fixes #234) * clippy * replace map_err with inspect_err * ignore unpublished posts in read queries * add api test * fmt * add some checks * address some review comments * allow updating schedule time * rewrite scheduled task * fmt * machete * compare date in sql, more filters * check for community ban in sql * remove api test (scheduled task only runs every 10 mins) * remove mut * add index * remove Post::read impl * fmt * fix * correctly handle changes to schedule time * normal users can only schedule up to 10 posts * Remove redundant local_user.auto_expand setting. (#5041) - Fixes #4643 Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com> * Add ability to search for Community by its description (or title only). (#5044) - This changes the post_title_only for Search to title_only, since its also used in the community query now. - Fixes #4785 * Cleanup remaining use of Result (fixes #4862) (#5047) * Replace clippy allow annotation with expect (fixes #5012) (#5048) * Add skip_serialize_none to OAuth structs with option fields (#5046) * Add skip_serialize_none to OAuth structs with option fields * PR feedback * Remove serde and ts export from SSO db-only structs * Apply scheduled post limit to future posts instead of past posts, and verify this in test (#5054) * test scheduled_post_count * fix syntax error * fix formatting * fix argument order * fix user_scheduled_post_count function * Avoid breaking changes, keep response fields as deprecated (#5058) * Adding skip_serializing_none to another OAuth API request. (#5060) * Handle partial settings backup (fixes #4307) (#5063) * Handle partial settings backup (fixes #4307) * clippy * Avoid stack overflow when fetching nested comments, reduce max comment depth to 50 (#5009) * Avoid stack overflow when fetching deeply nested comments * add test case * reduce comment depth, add docs * decrease * reduce max comment depth to 50 * fmt * clippy * cleanup * Update Rust crate clap to v4.5.18 (#5066) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update Rust crate reqwest to v0.12.8 (#5068) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update Rust crate sitemap-rs to v0.2.2 (#5069) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update Rust crate tracing-actix-web to v0.7.13 (#5070) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Changing renovate to run on the weekends. (#5067) - Lemmy builds take a long time, and the current schedule only runs for about 4 hours once a month, and renovate also rate-limits itself to ~ 2 per hour. This gives it a large enough window, without clogging up my runner machines. * Update Rust crate async-trait to v0.1.83 (#5065) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update dependency @types/jest to v29.5.13 (#5071) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Attempt to fix test for fetching deeply nested comment (#5072) Co-authored-by: Dessalines * Add modlog entries for bulk removals. (#5050) * Add modlog entries for bulk removals. - Added unit tests for removal / restore to api_common/utils. - Fixes #4699 * Address PR comments. * Combining remove and restore functions. * Trigger build. * Trigger build 2. * Changing allow to expect. * Adding local site settings to reject federated upvotes or downvotes. (#5038) * Adding local site settings to reject federated upvotes or downvotes. - Should help defend against downvote spamming instances. - Fixes #4086 * Adding new vote mode types. * Simpler activitypub vote check. * Adding undo vote for failed vote mode check. * Update crates/api_common/src/utils.rs --------- Co-authored-by: Nutomic * Replace `clippy::unwrap_used` in tests (#5064) * Add LemmyResult to session_middleware tests * Add LemmyResult to inboxes tests * Add LemmyResult to slurs tests * Add LemmyResult to markdown tests * Add LemmyResult to rate_limiter tests * Add LemmyResult to error tests * Add LemmyResult to api_common utils tests * Add LemmyResult to request tests * Add LemmyResult to claims tests * Propagate registration_applications errors * Remove clippy::unwrap_used from community tests * Add LemmyResult to community_view tests * Add LemmyResult to db_schema post tests * Add LemmyResult to site_aggregates tests * Add LemmyResult to private_message tests * Add LemmyResult to activity tests * Add LemmyResult to federation_allowlist tests * Add LemmyResult to comment_aggregates tests * Add LemmyResult to post_report tests * Add LemmyResult to moderator tests * Add LemmyResult to community_aggregates tests * Add LemmyResult to person_aggregates tests * Add LemmyResult to language tests * Add LemmyResult to post_aggregates tests * Add LemmyResult to db_schema comment tests * Add LemmyResult to actor_language tests * Add LemmyResult to vote_view tests * Add LemmyResult to registration_application_view tests * Add LemmyResult to private_message_view tests * Add LemmyResult to private_message_report_view tests * Add LemmyResult to post_report_view tests * Add LemmyResult to comment_report_view tests * Add LemmyResult to sitemap tests * Replace .expect() with .unwrap() * Format code * Remove clippy::unwrap_used from activity tests * Add diesel result in db_schema tests * Format code * Map to_bytes() error to LemmyErrorType * Remove clippy::unwrap_used from error tests * Removing a few more unwraps, and cleaning up language code. * Replace map_err with unwrap_or_default * Replace ok_or with and_then --------- Co-authored-by: Dessalines * Adding a get_random_community endpoint. (#5042) * Adding a get_random_community endpoint. - Fixes #4698 * Fixing issue from main. * Adding ListingType to the query. * More concise query filter. * Resolve links to remote posts into local URL (#5057) * move code to new file * rewrite markdown links (fixes #2987) * add missing file * add helper fn * also convert post.url * simplify search.rs * clippy * also rewrite user/community links in markdown * Call from apub handlers, cleanup * no network requests in test * clippy * fix tests * serial * test * no mut * add api test * fix api test * Update Rust crate clap to v4.5.19 (#5080) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update Rust crate html2text to v0.12.6 (#5081) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update Rust crate rustls to v0.23.14 (#5083) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update rust-futures monorepo to v0.3.31 (#5090) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update Rust crate actix-web-prom to 0.9.0 (#5091) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update Rust crate regex to v1.11.0 (#5093) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update Rust crate serde_with to v3.10.0 (#5094) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update Rust crate serde_with to v3.11.0 (#5095) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update dependency ts-jest to v29.2.5 (#5089) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update dependency typescript to v5.6.2 (#5098) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update dependency eslint to v9.12.0 (#5097) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update docker/dockerfile Docker tag to v1.10 (#5099) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update pnpm to v9.12.0 (#5100) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update Rust crate reqwest to v0.12.8 (#5082) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update dependency @types/node to v22.7.4 (#5096) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update tamasfe/taplo Docker tag to v0.9.3 (#5101) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update tmknom/prettier Docker tag to v3.2.5 (#5102) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Add test case for community.hidden in post_view (ref #5074) (#5106) * Resize post thumbnails (fixes #4053) (#5107) * Resize post thumbnails (fixes #4053) * 256px * Update typescript-eslint monorepo to v8.8.1 (#5103) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update renovate config (#5109) * Update renovate config * ignroe rust updates, run on first day of month * fix * Add community alphabetic sorting (#5056) * Started * Finished? Need to write tests * Formatting * Formatting * Formatting * Write tests * Formatting * Formatting * Formatting * Unnecessary lifetime * Safety * Unwrap * Formatting * Formatting * Fix local_only test * Formatting * Name consistency * Adding lower to community name sort. --------- Co-authored-by: Dessalines Co-authored-by: Dessalines * Support markdown sub/superscript, use external crate for spoilers (#5135) * Use external crate for spoiler tags * Also add other plugins * fix test * Remove comment_like.post_id column which is unnecessary (ref #5122) (#5134) * Allow admins to resolve removed or deleted objects via API (#5061) * Allow admins to resolve removed or deleted objects via API * Removing pointless TestUser. --------- Co-authored-by: Dessalines * Remove individual user/community inboxes (#5124) * Remove endpoints for individual community/user inboxes fixes #4147 fixes #3928 * Remove shared_inbox_url columns * fmt * Fixing errors. --------- Co-authored-by: Carlos Cabello Co-authored-by: Nutomic Co-authored-by: privacyguard <92675882+privacyguard@users.noreply.github.com> Co-authored-by: privacyguard Co-authored-by: Freakazoid182 <5238563+Freakazoid182@users.noreply.github.com> Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com> Co-authored-by: leoseg <70430884+leoseg@users.noreply.github.com> Co-authored-by: Sander Saarend Co-authored-by: Joseph Silva Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: netbrum <130702882+netbrum@users.noreply.github.com> Co-authored-by: Steven Vergenz <1882376+stevenvergenz@users.noreply.github.com> Co-authored-by: Richard Schwab * Search with LemmyErrorType prefix --------- Co-authored-by: Dessalines Co-authored-by: Carlos Cabello Co-authored-by: privacyguard <92675882+privacyguard@users.noreply.github.com> Co-authored-by: privacyguard Co-authored-by: Freakazoid182 <5238563+Freakazoid182@users.noreply.github.com> Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com> Co-authored-by: leoseg <70430884+leoseg@users.noreply.github.com> Co-authored-by: Sander Saarend Co-authored-by: Joseph Silva Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: netbrum <130702882+netbrum@users.noreply.github.com> Co-authored-by: Steven Vergenz <1882376+stevenvergenz@users.noreply.github.com> Co-authored-by: Richard Schwab Co-authored-by: Dessalines --- .../apub/src/activities/block/block_user.rs | 7 +- .../apub/src/activities/community/announce.rs | 6 +- crates/apub/src/activities/deletion/delete.rs | 4 +- .../src/activities/deletion/undo_delete.rs | 4 +- crates/apub/src/activities/mod.rs | 10 +-- crates/apub/src/fetcher/post_or_comment.rs | 6 +- crates/apub/src/http/mod.rs | 8 +- crates/apub/src/lib.rs | 23 ++--- crates/apub/src/mentions.rs | 7 +- crates/apub/src/objects/comment.rs | 4 +- crates/apub/src/objects/instance.rs | 9 +- crates/apub/src/objects/private_message.rs | 4 +- .../src/protocol/activities/voting/vote.rs | 4 +- crates/apub/src/protocol/objects/page.rs | 4 +- crates/utils/src/error.rs | 84 +++++++++++-------- crates/utils/tests/test_errors_used.rs | 45 ++++++++++ 16 files changed, 145 insertions(+), 84 deletions(-) create mode 100644 crates/utils/tests/test_errors_used.rs diff --git a/crates/apub/src/activities/block/block_user.rs b/crates/apub/src/activities/block/block_user.rs index e291cc4a4..64d5e7816 100644 --- a/crates/apub/src/activities/block/block_user.rs +++ b/crates/apub/src/activities/block/block_user.rs @@ -39,10 +39,7 @@ use lemmy_db_schema::{ }, traits::{Bannable, Crud, Followable}, }; -use lemmy_utils::{ - error::{LemmyError, LemmyResult}, - LemmyErrorType, -}; +use lemmy_utils::error::{FederationError, LemmyError, LemmyResult}; use url::Url; impl BlockUser { @@ -135,7 +132,7 @@ impl ActivityHandler for BlockUser { .object .inner() .domain() - .ok_or(LemmyErrorType::UrlWithoutDomain)?; + .ok_or(FederationError::UrlWithoutDomain)?; if context.settings().hostname == domain { return Err( anyhow!("Site bans from remote instance can't affect user's home instance").into(), diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index 0a506a791..e374d2874 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -26,7 +26,7 @@ use lemmy_db_schema::{ source::{activity::ActivitySendTargets, community::CommunityFollower}, CommunityVisibility, }; -use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult}; use serde_json::Value; use url::Url; @@ -54,7 +54,7 @@ impl ActivityHandler for RawAnnouncableActivities { // This is only for sending, not receiving so we reject it. if let AnnouncableActivities::Page(_) = activity { - Err(LemmyErrorType::CannotReceivePage)? + Err(FederationError::CannotReceivePage)? } // Need to treat community as optional here because `Delete/PrivateMessage` gets routed through @@ -165,7 +165,7 @@ impl ActivityHandler for AnnounceActivity { // This is only for sending, not receiving so we reject it. if let AnnouncableActivities::Page(_) = object { - Err(LemmyErrorType::CannotReceivePage)? + Err(FederationError::CannotReceivePage)? } let community = object.community(context).await?; diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index ec2a0de95..1ddf642b9 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -27,7 +27,7 @@ use lemmy_db_schema::{ }, traits::{Crud, Reportable}, }; -use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult}; use url::Url; #[async_trait::async_trait] @@ -118,7 +118,7 @@ pub(in crate::activities) async fn receive_remove_action( match DeletableObjects::read_from_db(object, context).await? { DeletableObjects::Community(community) => { if community.local { - Err(LemmyErrorType::OnlyLocalAdminCanRemoveCommunity)? + Err(FederationError::OnlyLocalAdminCanRemoveCommunity)? } let form = ModRemoveCommunityForm { mod_person_id: actor.id, diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index 102c72678..6328bb427 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -25,7 +25,7 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult}; use url::Url; #[async_trait::async_trait] @@ -100,7 +100,7 @@ impl UndoDelete { match DeletableObjects::read_from_db(object, context).await? { DeletableObjects::Community(community) => { if community.local { - Err(LemmyErrorType::OnlyLocalAdminCanRestoreCommunity)? + Err(FederationError::OnlyLocalAdminCanRestoreCommunity)? } let form = ModRemoveCommunityForm { mod_person_id: actor.id, diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 2c371f71c..21723c390 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -42,7 +42,7 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}; use serde::Serialize; use tracing::info; use url::{ParseError, Url}; @@ -81,7 +81,7 @@ pub(crate) async fn verify_person_in_community( ) -> LemmyResult<()> { let person = person_id.dereference(context).await?; if person.banned { - Err(LemmyErrorType::PersonIsBannedFromSite( + Err(FederationError::PersonIsBannedFromSite( person.actor_id.to_string(), ))? } @@ -114,7 +114,7 @@ pub(crate) async fn verify_mod_action( pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> LemmyResult<()> { if ![to, cc].iter().any(|set| set.contains(&public())) { - Err(LemmyErrorType::ObjectIsNotPublic)? + Err(FederationError::ObjectIsNotPublic)? } else { Ok(()) } @@ -126,7 +126,7 @@ where { let b: ObjectId = b.into(); if a != &b { - Err(LemmyErrorType::InvalidCommunity)? + Err(FederationError::InvalidCommunity)? } else { Ok(()) } @@ -134,7 +134,7 @@ where pub(crate) fn check_community_deleted_or_removed(community: &Community) -> LemmyResult<()> { if community.deleted || community.removed { - Err(LemmyErrorType::CannotCreatePostOrCommentInDeletedOrRemovedCommunity)? + Err(FederationError::CannotCreatePostOrCommentInDeletedOrRemovedCommunity)? } else { Ok(()) } diff --git a/crates/apub/src/fetcher/post_or_comment.rs b/crates/apub/src/fetcher/post_or_comment.rs index 8502ffb1a..be48e8ebd 100644 --- a/crates/apub/src/fetcher/post_or_comment.rs +++ b/crates/apub/src/fetcher/post_or_comment.rs @@ -26,7 +26,7 @@ pub enum PostOrComment { #[serde(untagged)] pub enum PageOrNote { Page(Box), - Note(Note), + Note(Box), } #[async_trait::async_trait] @@ -61,7 +61,7 @@ impl Object for PostOrComment { async fn into_json(self, data: &Data) -> LemmyResult { Ok(match self { PostOrComment::Post(p) => PageOrNote::Page(Box::new(p.into_json(data).await?)), - PostOrComment::Comment(c) => PageOrNote::Note(c.into_json(data).await?), + PostOrComment::Comment(c) => PageOrNote::Note(Box::new(c.into_json(data).await?)), }) } @@ -81,7 +81,7 @@ impl Object for PostOrComment { async fn from_json(apub: PageOrNote, context: &Data) -> LemmyResult { Ok(match apub { PageOrNote::Page(p) => PostOrComment::Post(ApubPost::from_json(*p, context).await?), - PageOrNote::Note(n) => PostOrComment::Comment(ApubComment::from_json(n, context).await?), + PageOrNote::Note(n) => PostOrComment::Comment(ApubComment::from_json(*n, context).await?), }) } } diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs index dad39881f..bc148eb9c 100644 --- a/crates/apub/src/http/mod.rs +++ b/crates/apub/src/http/mod.rs @@ -17,7 +17,7 @@ use lemmy_db_schema::{ source::{activity::SentActivity, community::Community}, CommunityVisibility, }; -use lemmy_utils::error::{LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{FederationError, LemmyErrorType, LemmyResult}; use serde::{Deserialize, Serialize}; use std::{ops::Deref, time::Duration}; use tokio::time::timeout; @@ -45,7 +45,7 @@ pub async fn shared_inbox( // consider the activity broken and move on. timeout(INCOMING_ACTIVITY_TIMEOUT, receive_fut) .await - .map_err(|_| LemmyErrorType::InboxTimeout)? + .map_err(|_| FederationError::InboxTimeout)? } /// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub @@ -106,7 +106,9 @@ pub(crate) async fn get_activity( info.id ))? .into(); - let activity = SentActivity::read_from_apub_id(&mut context.pool(), &activity_id).await?; + let activity = SentActivity::read_from_apub_id(&mut context.pool(), &activity_id) + .await + .map_err(|_| FederationError::CouldntFindActivity)?; let sensitive = activity.sensitive; if sensitive { diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index c8506da52..a04aec655 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -10,7 +10,7 @@ use lemmy_db_schema::{ utils::{ActualDbPool, DbPool}, }; use lemmy_utils::{ - error::{LemmyError, LemmyErrorType, LemmyResult}, + error::{FederationError, LemmyError, LemmyErrorType, LemmyResult}, CACHE_DURATION_FEDERATION, }; use moka::future::Cache; @@ -51,17 +51,18 @@ impl UrlVerifier for VerifyUrlData { let local_site_data = local_site_data_cached(&mut (&self.0).into()) .await .expect("read local site data"); + use FederationError::*; check_apub_id_valid(url, &local_site_data).map_err(|err| match err { LemmyError { - error_type: LemmyErrorType::FederationDisabled, + error_type: LemmyErrorType::FederationError(Some(FederationDisabled)), .. } => ActivityPubError::Other("Federation disabled".into()), LemmyError { - error_type: LemmyErrorType::DomainBlocked(domain), + error_type: LemmyErrorType::FederationError(Some(DomainBlocked(domain))), .. } => ActivityPubError::Other(format!("Domain {domain:?} is blocked")), LemmyError { - error_type: LemmyErrorType::DomainNotInAllowList(domain), + error_type: LemmyErrorType::FederationError(Some(DomainNotInAllowList(domain))), .. } => ActivityPubError::Other(format!("Domain {domain:?} is not in allowlist")), _ => ActivityPubError::Other("Failed validating apub id".into()), @@ -81,7 +82,7 @@ impl UrlVerifier for VerifyUrlData { fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> LemmyResult<()> { let domain = apub_id .domain() - .ok_or(LemmyErrorType::UrlWithoutDomain)? + .ok_or(FederationError::UrlWithoutDomain)? .to_string(); if !local_site_data @@ -90,7 +91,7 @@ fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> LemmyR .map(|l| l.federation_enabled) .unwrap_or(true) { - Err(LemmyErrorType::FederationDisabled)? + Err(FederationError::FederationDisabled)? } if local_site_data @@ -98,7 +99,7 @@ fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> LemmyR .iter() .any(|i| domain.to_lowercase().eq(&i.domain.to_lowercase())) { - Err(LemmyErrorType::DomainBlocked(domain.clone()))? + Err(FederationError::DomainBlocked(domain.clone()))? } // Only check this if there are instances in the allowlist @@ -108,7 +109,7 @@ fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> LemmyR .iter() .any(|i| domain.to_lowercase().eq(&i.domain.to_lowercase())) { - Err(LemmyErrorType::DomainNotInAllowList(domain))? + Err(FederationError::DomainNotInAllowList(domain))? } Ok(()) @@ -164,7 +165,7 @@ pub(crate) async fn check_apub_id_valid_with_strictness( ) -> LemmyResult<()> { let domain = apub_id .domain() - .ok_or(LemmyErrorType::UrlWithoutDomain)? + .ok_or(FederationError::UrlWithoutDomain)? .to_string(); let local_instance = context .settings() @@ -194,10 +195,10 @@ pub(crate) async fn check_apub_id_valid_with_strictness( let domain = apub_id .domain() - .ok_or(LemmyErrorType::UrlWithoutDomain)? + .ok_or(FederationError::UrlWithoutDomain)? .to_string(); if !allowed_and_local.contains(&domain) { - Err(LemmyErrorType::FederationDisabledByStrictAllowList)? + Err(FederationError::FederationDisabledByStrictAllowList)? } } Ok(()) diff --git a/crates/apub/src/mentions.rs b/crates/apub/src/mentions.rs index 6aa1d97fb..cb46be52a 100644 --- a/crates/apub/src/mentions.rs +++ b/crates/apub/src/mentions.rs @@ -11,7 +11,10 @@ use lemmy_db_schema::{ traits::Crud, utils::DbPool, }; -use lemmy_utils::{error::LemmyResult, utils::mention::scrape_text_for_mentions, LemmyErrorType}; +use lemmy_utils::{ + error::{FederationError, LemmyResult}, + utils::mention::scrape_text_for_mentions, +}; use serde::{Deserialize, Serialize}; use serde_json::Value; use url::Url; @@ -57,7 +60,7 @@ pub async fn collect_non_local_mentions( &parent_creator .id() .domain() - .ok_or(LemmyErrorType::UrlWithoutDomain)? + .ok_or(FederationError::UrlWithoutDomain)? )), kind: MentionType::Mention, }; diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index b6b411ec6..403ecbf94 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -33,7 +33,7 @@ use lemmy_db_schema::{ utils::naive_now, }; use lemmy_utils::{ - error::{LemmyError, LemmyErrorType, LemmyResult}, + error::{FederationError, LemmyError, LemmyResult}, utils::markdown::markdown_to_html, }; use std::ops::Deref; @@ -162,7 +162,7 @@ impl Object for ApubComment { .await .is_ok(); if post.locked && !is_mod_or_admin { - Err(LemmyErrorType::PostIsLocked)? + Err(FederationError::PostIsLocked)? } else { Ok(()) } diff --git a/crates/apub/src/objects/instance.rs b/crates/apub/src/objects/instance.rs index 6ee0a41dc..a123c85ba 100644 --- a/crates/apub/src/objects/instance.rs +++ b/crates/apub/src/objects/instance.rs @@ -42,12 +42,11 @@ use lemmy_db_schema::{ utils::naive_now, }; use lemmy_utils::{ - error::{LemmyError, LemmyResult}, + error::{FederationError, LemmyError, LemmyResult}, utils::{ markdown::markdown_to_html, slurs::{check_slurs, check_slurs_opt}, }, - LemmyErrorType, }; use std::ops::Deref; use tracing::debug; @@ -89,7 +88,7 @@ impl Object for ApubSite { } async fn delete(self, _data: &Data) -> LemmyResult<()> { - Err(LemmyErrorType::CantDeleteSite.into()) + Err(FederationError::CantDeleteSite.into()) } #[tracing::instrument(skip_all)] @@ -144,7 +143,7 @@ impl Object for ApubSite { .id .inner() .domain() - .ok_or(LemmyErrorType::UrlWithoutDomain)?; + .ok_or(FederationError::UrlWithoutDomain)?; let instance = DbInstance::read_or_create(&mut context.pool(), domain.to_string()).await?; let local_site = LocalSite::read(&mut context.pool()).await.ok(); @@ -220,7 +219,7 @@ pub(in crate::objects) async fn fetch_instance_actor_for_object + C debug!("Failed to dereference site for {}: {}", &instance_id, e); let domain = instance_id .domain() - .ok_or(LemmyErrorType::UrlWithoutDomain)?; + .ok_or(FederationError::UrlWithoutDomain)?; Ok( DbInstance::read_or_create(&mut context.pool(), domain.to_string()) .await? diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 3ed5b3572..5a191cc66 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -29,7 +29,7 @@ use lemmy_db_schema::{ utils::naive_now, }; use lemmy_utils::{ - error::{LemmyError, LemmyErrorType, LemmyResult}, + error::{FederationError, LemmyError, LemmyErrorType, LemmyResult}, utils::markdown::markdown_to_html, }; use std::ops::Deref; @@ -113,7 +113,7 @@ impl Object for ApubPrivateMessage { check_apub_id_valid_with_strictness(note.id.inner(), false, context).await?; let person = note.attributed_to.dereference(context).await?; if person.banned { - Err(LemmyErrorType::PersonIsBannedFromSite( + Err(FederationError::PersonIsBannedFromSite( person.actor_id.to_string(), ))? } else { diff --git a/crates/apub/src/protocol/activities/voting/vote.rs b/crates/apub/src/protocol/activities/voting/vote.rs index 9fae264a5..883fc85fb 100644 --- a/crates/apub/src/protocol/activities/voting/vote.rs +++ b/crates/apub/src/protocol/activities/voting/vote.rs @@ -6,7 +6,7 @@ use crate::{ }; use activitypub_federation::{config::Data, fetch::object_id::ObjectId}; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{FederationError, LemmyError, LemmyResult}; use serde::{Deserialize, Serialize}; use strum::Display; use url::Url; @@ -35,7 +35,7 @@ impl TryFrom for VoteType { match value { 1 => Ok(VoteType::Like), -1 => Ok(VoteType::Dislike), - _ => Err(LemmyErrorType::InvalidVoteValue.into()), + _ => Err(FederationError::InvalidVoteValue.into()), } } } diff --git a/crates/apub/src/protocol/objects/page.rs b/crates/apub/src/protocol/objects/page.rs index 9a9866cfb..97f767573 100644 --- a/crates/apub/src/protocol/objects/page.rs +++ b/crates/apub/src/protocol/objects/page.rs @@ -20,7 +20,7 @@ use activitypub_federation::{ use chrono::{DateTime, Utc}; use itertools::Itertools; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult}; use serde::{de::Error, Deserialize, Deserializer, Serialize}; use serde_with::skip_serializing_none; use url::Url; @@ -162,7 +162,7 @@ impl Page { .iter() .find(|a| a.kind == PersonOrGroupType::Person) .map(|a| ObjectId::::from(a.id.clone().into_inner())) - .ok_or_else(|| LemmyErrorType::PageDoesNotSpecifyCreator.into()), + .ok_or_else(|| FederationError::PageDoesNotSpecifyCreator.into()), } } } diff --git a/crates/utils/src/error.rs b/crates/utils/src/error.rs index b0add40e7..c95af03e2 100644 --- a/crates/utils/src/error.rs +++ b/crates/utils/src/error.rs @@ -23,30 +23,24 @@ pub enum LemmyErrorType { CouldntUpdateComment, CouldntUpdatePrivateMessage, CannotLeaveAdmin, - NoLinesInHtml, - SiteMetadataPageIsNotDoctypeHtml, + // TODO: also remove the translations of unused errors PictrsResponseError(String), PictrsPurgeResponseError(String), - PictrsCachingDisabled, ImageUrlMissingPathSegments, ImageUrlMissingLastPathSegment, PictrsApiKeyNotProvided, NoContentTypeHeader, NotAnImageType, NotAModOrAdmin, - NoAdmins, - NotTopAdmin, NotTopMod, NotLoggedIn, NotHigherMod, NotHigherAdmin, SiteBan, Deleted, - BannedFromCommunity, PersonIsBlocked, CommunityIsBlocked, InstanceIsBlocked, - VoteNotAllowed, InstanceIsPrivate, /// Password must be between 10 and 60 characters InvalidPassword, @@ -61,7 +55,6 @@ pub enum LemmyErrorType { OnlyAdminsCanCreateCommunities, CommunityAlreadyExists, LanguageNotAllowed, - OnlyModsCanPostInCommunity, CouldntUpdatePost, NoPostEditAllowed, EditPrivateMessageNotAllowed, @@ -73,23 +66,10 @@ pub enum LemmyErrorType { RegistrationUsernameRequired, EmailAlreadyExists, UsernameAlreadyExists, - FederationForbiddenByStrictAllowList, PersonIsBannedFromCommunity, - ObjectIsNotPublic, - InvalidCommunity, - CannotCreatePostOrCommentInDeletedOrRemovedCommunity, - CannotReceivePage, - NewPostCannotBeLocked, - OnlyLocalAdminCanRemoveCommunity, - OnlyLocalAdminCanRestoreCommunity, NoIdGiven, IncorrectLogin, - InvalidQuery, ObjectNotLocal, - PostIsLocked, - PersonIsBannedFromSite(String), - InvalidVoteValue, - PageDoesNotSpecifyCreator, NoEmailSetup, LocalSiteNotSetup, EmailSmtpServerNeedsAPort, @@ -126,7 +106,6 @@ pub enum LemmyErrorType { CouldntUpdateCommunity, CouldntUpdateReplies, CouldntUpdatePersonMentions, - PostTitleTooLong, CouldntCreatePost, CouldntCreatePrivateMessage, CouldntUpdatePrivate, @@ -141,10 +120,6 @@ pub enum LemmyErrorType { EmailSendFailed, Slurs, RegistrationDenied(Option), - FederationDisabled, - DomainBlocked(String), - DomainNotInAllowList(String), - FederationDisabledByStrictAllowList, SiteNameRequired, SiteNameLengthOverflow, PermissiveRegex, @@ -158,23 +133,51 @@ pub enum LemmyErrorType { /// Thrown when an API call is submitted with more than 1000 array elements, see /// [[MAX_API_PARAM_ELEMENTS]] TooManyItems, - CommunityHasNoFollowers, BanExpirationInPast, InvalidUnixTime, InvalidBotAction, CantBlockLocalInstance, - UrlWithoutDomain, - InboxTimeout, + Unknown(String), + UrlLengthOverflow, OauthAuthorizationInvalid, OauthLoginFailed, OauthRegistrationClosed, CouldntDeleteOauthProvider, - Unknown(String), - CantDeleteSite, - UrlLengthOverflow, + NotFound, + CommunityHasNoFollowers, PostScheduleTimeMustBeInFuture, TooManyScheduledPosts, - NotFound, + FederationError(Option), +} + +/// Federation related errors, these dont need to be translated. +#[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, EnumIter, Hash)] +#[cfg_attr(feature = "full", derive(ts_rs::TS))] +#[cfg_attr(feature = "full", ts(export))] +#[non_exhaustive] +pub enum FederationError { + // TODO: merge into a single NotFound error + CouldntFindActivity, + InvalidCommunity, + CannotCreatePostOrCommentInDeletedOrRemovedCommunity, + CannotReceivePage, + OnlyLocalAdminCanRemoveCommunity, + OnlyLocalAdminCanRestoreCommunity, + PostIsLocked, + PersonIsBannedFromSite(String), + InvalidVoteValue, + PageDoesNotSpecifyCreator, + CouldntGetComments, + CouldntGetPosts, + FederationDisabled, + DomainBlocked(String), + DomainNotInAllowList(String), + FederationDisabledByStrictAllowList, + ContradictingFilters, + UrlWithoutDomain, + InboxTimeout, + CantDeleteSite, + ObjectIsNotPublic, } cfg_if! { @@ -255,6 +258,17 @@ cfg_if! { } } + impl From for LemmyError { + fn from(error_type: FederationError) -> Self { + let inner = anyhow::anyhow!("{}", error_type); + LemmyError { + error_type: LemmyErrorType::FederationError(Some(error_type)), + inner, + context: Backtrace::capture(), + } + } + } + pub trait LemmyErrorExt> { fn with_lemmy_type(self, error_type: LemmyErrorType) -> LemmyResult; } @@ -306,12 +320,12 @@ cfg_if! { #[test] fn deserializes_with_message() -> LemmyResult<()> { - let reg_banned = LemmyErrorType::PersonIsBannedFromSite(String::from("reason")); + let reg_banned = LemmyErrorType::PictrsResponseError(String::from("reason")); let err = LemmyError::from(reg_banned).error_response(); let json = String::from_utf8(err.into_body().try_into_bytes().unwrap_or_default().to_vec())?; assert_eq!( &json, - "{\"error\":\"person_is_banned_from_site\",\"message\":\"reason\"}" + "{\"error\":\"pictrs_response_error\",\"message\":\"reason\"}" ); Ok(()) diff --git a/crates/utils/tests/test_errors_used.rs b/crates/utils/tests/test_errors_used.rs new file mode 100644 index 000000000..d0dec489a --- /dev/null +++ b/crates/utils/tests/test_errors_used.rs @@ -0,0 +1,45 @@ +use lemmy_utils::LemmyErrorType; +use std::{env::current_dir, process::Command}; +use strum::IntoEnumIterator; + +#[test] +#[allow(clippy::unwrap_used)] +fn test_errors_used() { + let mut unused_error_found = false; + let mut current_dir = current_dir().unwrap(); + current_dir.pop(); + current_dir.pop(); + for error in LemmyErrorType::iter() { + let search = format!("LemmyErrorType::{error}"); + let mut grep_all = Command::new("grep"); + let grep_all = grep_all + .current_dir(current_dir.clone()) + .arg("-R") + .arg("--exclude=error.rs") + .arg(&search) + .arg("crates/") + .arg("src/"); + let output = grep_all.output().unwrap(); + let grep_all_out = std::str::from_utf8(&output.stdout).unwrap(); + + let mut grep_apub = Command::new("grep"); + let grep_apub = grep_apub + .current_dir(current_dir.clone()) + .arg("-R") + .arg("--exclude-dir=api") + .arg(&search) + .arg("crates/apub/"); + let output = grep_apub.output().unwrap(); + let grep_apub_out = std::str::from_utf8(&output.stdout).unwrap(); + + if grep_all_out.is_empty() { + println!("LemmyErrorType::{} is unused", error); + unused_error_found = true; + } + if search != "LemmyErrorType::FederationError" && grep_all_out == grep_apub_out { + println!("LemmyErrorType::{} is only used for federation", error); + unused_error_found = true; + } + } + assert!(!unused_error_found); +} From d6d01a3b628d199e59821da62da549f660b43b8f Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 28 Oct 2024 11:41:09 -0400 Subject: [PATCH 02/19] Adding cargo shear (#5139) * Adding cargo shear (fail check) * Running cargo shear. * Fixing another missing. * Removing cargo-machete in favor of cargo-shear --- .woodpecker.yml | 6 +++--- Cargo.lock | 2 -- crates/api_common/Cargo.toml | 2 +- crates/api_crud/Cargo.toml | 2 +- crates/apub/Cargo.toml | 1 - crates/db_schema/Cargo.toml | 3 --- crates/db_views_actor/Cargo.toml | 3 --- crates/utils/Cargo.toml | 4 +++- 8 files changed, 8 insertions(+), 15 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index cb6580e32..a2124c1cc 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -73,12 +73,12 @@ steps: when: - event: pull_request - cargo_machete: + cargo_shear: image: *rust_nightly_image commands: - *install_binstall - - cargo binstall -y cargo-machete - - cargo machete + - cargo binstall -y cargo-shear + - cargo shear when: - event: pull_request diff --git a/Cargo.lock b/Cargo.lock index 3cfec53eb..491b7cc94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2570,7 +2570,6 @@ dependencies = [ "futures", "html2md", "html2text", - "http 1.1.0", "itertools 0.13.0", "lemmy_api_common", "lemmy_db_schema", @@ -2815,7 +2814,6 @@ dependencies = [ "markdown-it-sup", "pretty_assertions", "regex", - "reqwest 0.12.8", "reqwest-middleware", "rosetta-build", "rosetta-i18n", diff --git a/crates/api_common/Cargo.toml b/crates/api_common/Cargo.toml index 4eabfe5f9..f939985e8 100644 --- a/crates/api_common/Cargo.toml +++ b/crates/api_common/Cargo.toml @@ -72,7 +72,7 @@ jsonwebtoken = { version = "9.3.0", optional = true } # necessary for wasmt compilation getrandom = { version = "0.2.15", features = ["js"] } -[package.metadata.cargo-machete] +[package.metadata.cargo-shear] ignored = ["getrandom"] [dev-dependencies] diff --git a/crates/api_crud/Cargo.toml b/crates/api_crud/Cargo.toml index 2793beac3..723864705 100644 --- a/crates/api_crud/Cargo.toml +++ b/crates/api_crud/Cargo.toml @@ -34,5 +34,5 @@ serde_json = { workspace = true } serde = { workspace = true } serde_with = { workspace = true } -[package.metadata.cargo-machete] +[package.metadata.cargo-shear] ignored = ["futures"] diff --git a/crates/apub/Cargo.toml b/crates/apub/Cargo.toml index 660489a68..55eadeaf9 100644 --- a/crates/apub/Cargo.toml +++ b/crates/apub/Cargo.toml @@ -33,7 +33,6 @@ tokio = { workspace = true } tracing = { workspace = true } strum = { workspace = true } url = { workspace = true } -http = { workspace = true } futures = { workspace = true } itertools = { workspace = true } uuid = { workspace = true } diff --git a/crates/db_schema/Cargo.toml b/crates/db_schema/Cargo.toml index aeaa55c93..c9b2a7930 100644 --- a/crates/db_schema/Cargo.toml +++ b/crates/db_schema/Cargo.toml @@ -82,6 +82,3 @@ derive-new.workspace = true [dev-dependencies] serial_test = { workspace = true } pretty_assertions = { workspace = true } - -[package.metadata.cargo-machete] -ignored = ["strum"] diff --git a/crates/db_views_actor/Cargo.toml b/crates/db_views_actor/Cargo.toml index c29b7d66d..d623959d5 100644 --- a/crates/db_views_actor/Cargo.toml +++ b/crates/db_views_actor/Cargo.toml @@ -48,6 +48,3 @@ pretty_assertions = { workspace = true } url.workspace = true lemmy_db_views.workspace = true lemmy_utils.workspace = true - -[package.metadata.cargo-machete] -ignored = ["strum"] diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index 1c99e89c2..c22f863c1 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -49,6 +49,9 @@ full = [ "dep:markdown-it", ] +[package.metadata.cargo-shear] +ignored = ["http"] + [dependencies] regex = { workspace = true, optional = true } tracing = { workspace = true, optional = true } @@ -88,7 +91,6 @@ markdown-it-sup = "1.0.0" markdown-it-ruby = "1.0.0" [dev-dependencies] -reqwest = { workspace = true } pretty_assertions = { workspace = true } [build-dependencies] From cdc1cf3bf7afb8406d5eb55ee3a4f8adfebae71c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 28 Oct 2024 11:54:36 -0400 Subject: [PATCH 03/19] Adding community description in addition to sidebar, like site. (#5120) * Adding community description in addition to sidebar, like site. - Also made changes to lemmy's group apub to be similar to its site, which uses content for the sidebar, and summary for the short description. - Fixes #5078 * Fixing tests. * Remove comment. * Fix name for description checker. --- crates/api_common/src/community.rs | 8 ++++-- crates/api_common/src/site.rs | 1 + crates/api_crud/src/community/create.rs | 25 +++++++++++++------ crates/api_crud/src/community/update.rs | 11 +++++--- crates/api_crud/src/site/create.rs | 8 +++--- crates/api_crud/src/site/update.rs | 4 +-- crates/apub/assets/lemmy/objects/group.json | 4 ++- crates/apub/src/objects/community.rs | 25 +++++++++++++------ crates/apub/src/protocol/objects/group.rs | 12 ++++++--- crates/apub/src/protocol/objects/instance.rs | 2 +- crates/db_schema/src/impls/community.rs | 1 + crates/db_schema/src/schema.rs | 4 ++- crates/db_schema/src/source/community.rs | 13 +++++++--- crates/db_views/src/comment_report_view.rs | 1 + crates/db_views/src/comment_view.rs | 1 + crates/db_views/src/post_view.rs | 1 + crates/utils/src/utils/validation.rs | 10 ++++---- .../down.sql | 5 ++++ .../up.sql | 7 ++++++ 19 files changed, 100 insertions(+), 43 deletions(-) create mode 100644 migrations/2024-10-16-141718_add_short_community_description/down.sql create mode 100644 migrations/2024-10-16-141718_add_short_community_description/up.sql diff --git a/crates/api_common/src/community.rs b/crates/api_common/src/community.rs index f8e741a58..1def2111b 100644 --- a/crates/api_common/src/community.rs +++ b/crates/api_common/src/community.rs @@ -48,7 +48,9 @@ pub struct CreateCommunity { pub name: String, /// A longer title. pub title: String, - /// A longer sidebar, or description of your community, in markdown. + /// A sidebar for the community in markdown. + pub sidebar: Option, + /// A shorter, one line description of your community. pub description: Option, /// An icon URL. pub icon: Option, @@ -147,7 +149,9 @@ pub struct EditCommunity { pub community_id: CommunityId, /// A longer title. pub title: Option, - /// A longer sidebar, or description of your community, in markdown. + /// A sidebar for the community in markdown. + pub sidebar: Option, + /// A shorter, one line description of your community. pub description: Option, /// An icon URL. pub icon: Option, diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index 8316b30ee..8fc091e9d 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -221,6 +221,7 @@ pub struct CreateSite { /// Edits a site. pub struct EditSite { pub name: Option, + /// A sidebar for the site, in markdown. pub sidebar: Option, /// A shorter, one line description of your site. pub description: Option, diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs index f02d733e5..cd0fc985e 100644 --- a/crates/api_crud/src/community/create.rs +++ b/crates/api_crud/src/community/create.rs @@ -36,7 +36,11 @@ use lemmy_utils::{ error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{ slurs::check_slurs, - validation::{is_valid_actor_name, is_valid_body_field}, + validation::{ + is_valid_actor_name, + is_valid_body_field, + site_or_community_description_length_check, + }, }, }; @@ -57,8 +61,18 @@ pub async fn create_community( let url_blocklist = get_url_blocklist(&context).await?; check_slurs(&data.name, &slur_regex)?; check_slurs(&data.title, &slur_regex)?; - let description = - process_markdown_opt(&data.description, &slur_regex, &url_blocklist, &context).await?; + let sidebar = process_markdown_opt(&data.sidebar, &slur_regex, &url_blocklist, &context).await?; + + // Ensure that the sidebar has fewer than the max num characters... + if let Some(sidebar) = &sidebar { + is_valid_body_field(sidebar, false)?; + } + + let description = data.description.clone(); + if let Some(desc) = &description { + site_or_community_description_length_check(desc)?; + check_slurs(desc, &slur_regex)?; + } let icon = diesel_url_create(data.icon.as_deref())?; let icon = proxy_image_link_api(icon, &context).await?; @@ -68,10 +82,6 @@ pub async fn create_community( is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?; - if let Some(desc) = &data.description { - is_valid_body_field(desc, false)?; - } - // Double check for duplicate community actor_ids let community_actor_id = generate_local_apub_endpoint( EndpointType::Community, @@ -88,6 +98,7 @@ pub async fn create_community( let keypair = generate_actor_keypair()?; let community_form = CommunityInsertForm { + sidebar, description, icon, banner, diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs index 798f4f4c1..cde8058ee 100644 --- a/crates/api_crud/src/community/update.rs +++ b/crates/api_crud/src/community/update.rs @@ -41,16 +41,18 @@ pub async fn update_community( let url_blocklist = get_url_blocklist(&context).await?; check_slurs_opt(&data.title, &slur_regex)?; - let description = diesel_string_update( - process_markdown_opt(&data.description, &slur_regex, &url_blocklist, &context) + let sidebar = diesel_string_update( + process_markdown_opt(&data.sidebar, &slur_regex, &url_blocklist, &context) .await? .as_deref(), ); - if let Some(Some(desc)) = &description { - is_valid_body_field(desc, false)?; + if let Some(Some(sidebar)) = &sidebar { + is_valid_body_field(sidebar, false)?; } + let description = diesel_string_update(data.description.as_deref()); + let old_community = Community::read(&mut context.pool(), data.community_id).await?; let icon = diesel_url_update(data.icon.as_deref())?; @@ -84,6 +86,7 @@ pub async fn update_community( let community_form = CommunityUpdateForm { title: data.title.clone(), + sidebar, description, icon, banner, diff --git a/crates/api_crud/src/site/create.rs b/crates/api_crud/src/site/create.rs index 733837de7..e1ea1d992 100644 --- a/crates/api_crud/src/site/create.rs +++ b/crates/api_crud/src/site/create.rs @@ -29,13 +29,13 @@ use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_utils::{ error::{LemmyErrorType, LemmyResult}, utils::{ - slurs::{check_slurs, check_slurs_opt}, + slurs::check_slurs, validation::{ build_and_check_regex, check_site_visibility_valid, is_valid_body_field, - site_description_length_check, site_name_length_check, + site_or_community_description_length_check, }, }, }; @@ -167,8 +167,8 @@ fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) -> check_slurs(&create_site.name, &slur_regex)?; if let Some(desc) = &create_site.description { - site_description_length_check(desc)?; - check_slurs_opt(&create_site.description, &slur_regex)?; + site_or_community_description_length_check(desc)?; + check_slurs(desc, &slur_regex)?; } site_default_post_listing_type_check(&create_site.default_post_listing_type)?; diff --git a/crates/api_crud/src/site/update.rs b/crates/api_crud/src/site/update.rs index cce428cc1..085ed69d1 100644 --- a/crates/api_crud/src/site/update.rs +++ b/crates/api_crud/src/site/update.rs @@ -40,8 +40,8 @@ use lemmy_utils::{ check_site_visibility_valid, check_urls_are_valid, is_valid_body_field, - site_description_length_check, site_name_length_check, + site_or_community_description_length_check, }, }, }; @@ -219,7 +219,7 @@ fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> Lemm } if let Some(desc) = &edit_site.description { - site_description_length_check(desc)?; + site_or_community_description_length_check(desc)?; check_slurs_opt(&edit_site.description, &slur_regex)?; } diff --git a/crates/apub/assets/lemmy/objects/group.json b/crates/apub/assets/lemmy/objects/group.json index bd6e44065..226f50c34 100644 --- a/crates/apub/assets/lemmy/objects/group.json +++ b/crates/apub/assets/lemmy/objects/group.json @@ -3,11 +3,13 @@ "type": "Group", "preferredUsername": "tenforward", "name": "Ten Forward", - "summary": "

Lounge and recreation facility

\n
\n

Welcome to the Enterprise!.

\n", + "summary": "A description of ten forward.", + "content": "

Lounge and recreation facility

\n
\n

Welcome to the Enterprise!.

\n", "source": { "content": "Lounge and recreation facility\n\n---\n\nWelcome to the Enterprise!", "mediaType": "text/markdown" }, + "mediaType": "text/html", "sensitive": false, "icon": { "type": "Image", diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index c8479eaba..7ee204ac9 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -13,6 +13,7 @@ use crate::{ use activitypub_federation::{ config::Data, kinds::actor::GroupType, + protocol::values::MediaTypeHtml, traits::{Actor, Object}, }; use chrono::{DateTime, Utc}; @@ -107,8 +108,10 @@ impl Object for ApubCommunity { id: self.id().into(), preferred_username: self.name.clone(), name: Some(self.title.clone()), - summary: self.description.as_ref().map(|b| markdown_to_html(b)), - source: self.description.clone().map(Source::new), + content: self.sidebar.as_ref().map(|d| markdown_to_html(d)), + source: self.sidebar.clone().map(Source::new), + summary: self.description.clone(), + media_type: self.sidebar.as_ref().map(|_| MediaTypeHtml::Html), icon: self.icon.clone().map(ImageObject::new), image: self.banner.clone().map(ImageObject::new), sensitive: Some(self.nsfw), @@ -144,10 +147,9 @@ impl Object for ApubCommunity { let local_site = LocalSite::read(&mut context.pool()).await.ok(); let slur_regex = &local_site_opt_to_slur_regex(&local_site); let url_blocklist = get_url_blocklist(context).await?; - let description = read_from_string_or_source_opt(&group.summary, &None, &group.source); - let description = - process_markdown_opt(&description, slur_regex, &url_blocklist, context).await?; - let description = markdown_rewrite_remote_links_opt(description, context).await; + let sidebar = read_from_string_or_source_opt(&group.content, &None, &group.source); + let sidebar = process_markdown_opt(&sidebar, slur_regex, &url_blocklist, context).await?; + let sidebar = markdown_rewrite_remote_links_opt(sidebar, context).await; let icon = proxy_image_link_opt_apub(group.icon.map(|i| i.url), context).await?; let banner = proxy_image_link_opt_apub(group.image.map(|i| i.url), context).await?; @@ -161,7 +163,8 @@ impl Object for ApubCommunity { last_refreshed_at: Some(naive_now()), icon, banner, - description, + sidebar, + description: group.summary, followers_url: group.followers.clone().map(Into::into), inbox_url: Some( group @@ -299,10 +302,16 @@ pub(crate) mod tests { assert_eq!(community.title, "Ten Forward"); assert!(!community.local); + + // Test the sidebar and description assert_eq!( - community.description.as_ref().map(std::string::String::len), + community.sidebar.as_ref().map(std::string::String::len), Some(63) ); + assert_eq!( + community.description, + Some("A description of ten forward.".into()) + ); Community::delete(&mut context.pool(), community.id).await?; Site::delete(&mut context.pool(), site.id).await?; diff --git a/crates/apub/src/protocol/objects/group.rs b/crates/apub/src/protocol/objects/group.rs index 8f138e001..affafe269 100644 --- a/crates/apub/src/protocol/objects/group.rs +++ b/crates/apub/src/protocol/objects/group.rs @@ -7,7 +7,7 @@ use crate::{ community_outbox::ApubCommunityOutbox, }, local_site_data_cached, - objects::{community::ApubCommunity, read_from_string_or_source_opt}, + objects::community::ApubCommunity, protocol::{ objects::{Endpoints, LanguageTag}, ImageObject, @@ -21,6 +21,7 @@ use activitypub_federation::{ protocol::{ helpers::deserialize_skip_error, public_key::PublicKey, + values::MediaTypeHtml, verification::verify_domains_match, }, }; @@ -50,9 +51,13 @@ pub struct Group { /// title pub(crate) name: Option, - pub(crate) summary: Option, + // sidebar + pub(crate) content: Option, #[serde(deserialize_with = "deserialize_skip_error", default)] pub(crate) source: Option, + pub(crate) media_type: Option, + // short instance description + pub(crate) summary: Option, #[serde(deserialize_with = "deserialize_skip_error", default)] pub(crate) icon: Option, /// banner @@ -86,8 +91,7 @@ impl Group { check_slurs(&self.preferred_username, slur_regex)?; check_slurs_opt(&self.name, slur_regex)?; - let description = read_from_string_or_source_opt(&self.summary, &None, &self.source); - check_slurs_opt(&description, slur_regex)?; + check_slurs_opt(&self.summary, slur_regex)?; Ok(()) } } diff --git a/crates/apub/src/protocol/objects/instance.rs b/crates/apub/src/protocol/objects/instance.rs index 1f21e76da..0eef948e7 100644 --- a/crates/apub/src/protocol/objects/instance.rs +++ b/crates/apub/src/protocol/objects/instance.rs @@ -32,9 +32,9 @@ pub struct Instance { pub(crate) content: Option, #[serde(deserialize_with = "deserialize_skip_error", default)] pub(crate) source: Option, + pub(crate) media_type: Option, // short instance description pub(crate) summary: Option, - pub(crate) media_type: Option, /// instance icon pub(crate) icon: Option, /// instance banner diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs index 355979264..8efc579e9 100644 --- a/crates/db_schema/src/impls/community.rs +++ b/crates/db_schema/src/impls/community.rs @@ -508,6 +508,7 @@ mod tests { id: inserted_community.id, name: "TIL".into(), title: "nada".to_owned(), + sidebar: None, description: None, nsfw: false, removed: false, diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index 544da607b..9f1d00568 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -170,7 +170,7 @@ diesel::table! { name -> Varchar, #[max_length = 255] title -> Varchar, - description -> Nullable, + sidebar -> Nullable, removed -> Bool, published -> Timestamptz, updated -> Nullable, @@ -196,6 +196,8 @@ diesel::table! { #[max_length = 255] featured_url -> Nullable, visibility -> CommunityVisibility, + #[max_length = 150] + description -> Nullable, } } diff --git a/crates/db_schema/src/source/community.rs b/crates/db_schema/src/source/community.rs index 853667f7f..2eb6c143c 100644 --- a/crates/db_schema/src/source/community.rs +++ b/crates/db_schema/src/source/community.rs @@ -24,8 +24,8 @@ pub struct Community { pub name: String, /// A longer title, that can contain other characters, and doesn't have to be unique. pub title: String, - /// A sidebar / markdown description. - pub description: Option, + /// A sidebar for the community in markdown. + pub sidebar: Option, /// Whether the community is removed by a mod. pub removed: bool, pub published: DateTime, @@ -66,6 +66,8 @@ pub struct Community { #[serde(skip)] pub featured_url: Option, pub visibility: CommunityVisibility, + /// A shorter, one-line description of the site. + pub description: Option, } #[derive(Debug, Clone, derive_new::new)] @@ -77,7 +79,7 @@ pub struct CommunityInsertForm { pub title: String, pub public_key: String, #[new(default)] - pub description: Option, + pub sidebar: Option, #[new(default)] pub removed: Option, #[new(default)] @@ -114,6 +116,8 @@ pub struct CommunityInsertForm { pub posting_restricted_to_mods: Option, #[new(default)] pub visibility: Option, + #[new(default)] + pub description: Option, } #[derive(Debug, Clone, Default)] @@ -121,7 +125,7 @@ pub struct CommunityInsertForm { #[cfg_attr(feature = "full", diesel(table_name = community))] pub struct CommunityUpdateForm { pub title: Option, - pub description: Option>, + pub sidebar: Option>, pub removed: Option, pub published: Option>, pub updated: Option>>, @@ -141,6 +145,7 @@ pub struct CommunityUpdateForm { pub hidden: Option, pub posting_restricted_to_mods: Option, pub visibility: Option, + pub description: Option>, } #[derive(PartialEq, Eq, Debug)] diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs index c65e121e2..be5e76562 100644 --- a/crates/db_views/src/comment_report_view.rs +++ b/crates/db_views/src/comment_report_view.rs @@ -391,6 +391,7 @@ mod tests { actor_id: inserted_community.actor_id.clone(), local: true, title: inserted_community.title, + sidebar: None, description: None, updated: None, banner: None, diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index ac0e03f65..ff1405508 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -1092,6 +1092,7 @@ mod tests { actor_id: data.inserted_community.actor_id.clone(), local: true, title: "nada".to_owned(), + sidebar: None, description: None, updated: None, banner: None, diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index 41b730c46..4fa2222ae 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -1834,6 +1834,7 @@ mod tests { actor_id: inserted_community.actor_id.clone(), local: true, title: "nada".to_owned(), + sidebar: None, description: None, updated: None, banner: None, diff --git a/crates/utils/src/utils/validation.rs b/crates/utils/src/utils/validation.rs index 98b9a2a25..f8da6f609 100644 --- a/crates/utils/src/utils/validation.rs +++ b/crates/utils/src/utils/validation.rs @@ -191,8 +191,8 @@ pub fn site_name_length_check(name: &str) -> LemmyResult<()> { ) } -/// Checks the site description length, the limit as defined in the DB. -pub fn site_description_length_check(description: &str) -> LemmyResult<()> { +/// Checks the site / community description length, the limit as defined in the DB. +pub fn site_or_community_description_length_check(description: &str) -> LemmyResult<()> { max_length_check( description, SITE_DESCRIPTION_MAX_LENGTH, @@ -368,8 +368,8 @@ mod tests { is_valid_matrix_id, is_valid_post_title, is_valid_url, - site_description_length_check, site_name_length_check, + site_or_community_description_length_check, BIO_MAX_LENGTH, SITE_DESCRIPTION_MAX_LENGTH, SITE_NAME_MAX_LENGTH, @@ -537,14 +537,14 @@ mod tests { #[test] fn test_valid_site_description() { - assert!(site_description_length_check( + assert!(site_or_community_description_length_check( &(0..SITE_DESCRIPTION_MAX_LENGTH) .map(|_| 'A') .collect::() ) .is_ok()); - let invalid_result = site_description_length_check( + let invalid_result = site_or_community_description_length_check( &(0..SITE_DESCRIPTION_MAX_LENGTH + 1) .map(|_| 'A') .collect::(), diff --git a/migrations/2024-10-16-141718_add_short_community_description/down.sql b/migrations/2024-10-16-141718_add_short_community_description/down.sql new file mode 100644 index 000000000..fa8b39af6 --- /dev/null +++ b/migrations/2024-10-16-141718_add_short_community_description/down.sql @@ -0,0 +1,5 @@ +ALTER TABLE community + DROP COLUMN description; + +ALTER TABLE community RENAME COLUMN sidebar TO description; + diff --git a/migrations/2024-10-16-141718_add_short_community_description/up.sql b/migrations/2024-10-16-141718_add_short_community_description/up.sql new file mode 100644 index 000000000..591542ca4 --- /dev/null +++ b/migrations/2024-10-16-141718_add_short_community_description/up.sql @@ -0,0 +1,7 @@ +-- Renaming description to sidebar +ALTER TABLE community RENAME COLUMN description TO sidebar; + +-- Adding a short description column +ALTER TABLE community + ADD COLUMN description varchar(150); + From a4f63294de1442db1b817660783e4eeea588896b Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 29 Oct 2024 11:01:58 -0400 Subject: [PATCH 04/19] Compress binary with upx. (#5140) * Compress binary with upx. * Changing opt-level from z to 3 * Changing lto from thin to fat for release. --- Cargo.toml | 7 ++++--- docker/Dockerfile | 13 ++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fc22d0d53..5523dcfd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,9 +26,10 @@ workspace = true [profile.release] debug = 0 -lto = "thin" -strip = true # Automatically strip symbols from the binary. -opt-level = "z" # Optimize for size. +lto = "fat" +strip = true # Automatically strip symbols from the binary. +opt-level = 3 # Optimize for speed, not size. +codegen-units = 1 # Reduce parallel code generation. # This profile significantly speeds up build time. If debug info is needed you can comment the line # out temporarily, but make sure to leave this in the main branch. diff --git a/docker/Dockerfile b/docker/Dockerfile index 4503dd402..9701b4ad6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -38,6 +38,12 @@ RUN --mount=type=cache,target=/lemmy/target set -ex; \ cargo clean --release; \ cargo build --features "${CARGO_BUILD_FEATURES}" --release; \ mv target/"${RUST_RELEASE_MODE}"/lemmy_server ./lemmy_server; \ + # + # Compress the binary with upx + wget https://github.com/upx/upx/releases/download/v4.2.4/upx-4.2.4-amd64_linux.tar.xz; \ + tar -xvf upx-4.2.4-amd64_linux.tar.xz; \ + cp upx-4.2.4-amd64_linux/upx /usr/bin; \ + upx --best --lzma lemmy_server; \ fi # ARM64 builder @@ -71,9 +77,14 @@ RUN --mount=type=cache,target=./target,uid=10001,gid=10001 set -ex; \ cargo clean --release; \ cargo build --features "${CARGO_BUILD_FEATURES}" --release; \ mv "./target/$CARGO_BUILD_TARGET/$RUST_RELEASE_MODE/lemmy_server" /home/lemmy/lemmy_server; \ + # + # Compress the binary with upx + wget https://github.com/upx/upx/releases/download/v4.2.4/upx-4.2.4-arm64_linux.tar.xz; \ + tar -xvf upx-4.2.4-arm64_linux.tar.xz; \ + cp upx-4.2.4-arm64_linux/upx /usr/bin; \ + upx --best --lzma /home/lemmy/lemmy_server; \ fi - # amd64 base runner FROM ${AMD_RUNNER_IMAGE} AS runner-linux-amd64 From df07d8e31cb38502cd52b678560ba800c02cf799 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Thu, 31 Oct 2024 13:10:45 +0100 Subject: [PATCH 05/19] Skip api test for fetching nested comment (#5152) --- api_tests/src/comment.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_tests/src/comment.spec.ts b/api_tests/src/comment.spec.ts index 153405820..c3f4b3efe 100644 --- a/api_tests/src/comment.spec.ts +++ b/api_tests/src/comment.spec.ts @@ -860,7 +860,7 @@ test("Dont send a comment reply to a blocked community", async () => { /// Fetching a deeply nested comment can lead to stack overflow as all parent comments are also /// fetched recursively. Ensure that it works properly. -test("Fetch a deeply nested comment", async () => { +test.skip("Fetch a deeply nested comment", async () => { let lastComment; for (let i = 0; i < 50; i++) { let commentRes = await createComment( From 8f88dda28fb5b4a6c4c8858b40daa22ccad530dd Mon Sep 17 00:00:00 2001 From: Integral Date: Thu, 31 Oct 2024 20:12:24 +0800 Subject: [PATCH 06/19] refactor: destructure tuples to enhance readability (#5151) --- crates/api/src/sitemap.rs | 6 +++--- crates/apub/src/objects/comment.rs | 10 +++++----- crates/apub/src/objects/person.rs | 9 ++++++--- crates/apub/src/objects/private_message.rs | 8 ++++---- crates/db_views/src/custom_emoji_view.rs | 8 ++++---- crates/utils/src/utils/markdown/image_links.rs | 17 +++++++---------- src/scheduled_tasks.rs | 6 +++--- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/crates/api/src/sitemap.rs b/crates/api/src/sitemap.rs index c3c3c417c..57b39a5b3 100644 --- a/crates/api/src/sitemap.rs +++ b/crates/api/src/sitemap.rs @@ -14,9 +14,9 @@ async fn generate_urlset( ) -> LemmyResult { let urls = posts .into_iter() - .map_while(|post| { - Url::builder(post.0.to_string()) - .last_modified(post.1.into()) + .map_while(|(url, date_time)| { + Url::builder(url.to_string()) + .last_modified(date_time.into()) .build() .ok() }) diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index 403ecbf94..e6cc2ba85 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -247,13 +247,13 @@ pub(crate) mod tests { } async fn cleanup( - data: (ApubPerson, ApubCommunity, ApubPost, ApubSite), + (person, community, post, site): (ApubPerson, ApubCommunity, ApubPost, ApubSite), context: &LemmyContext, ) -> LemmyResult<()> { - Post::delete(&mut context.pool(), data.2.id).await?; - Community::delete(&mut context.pool(), data.1.id).await?; - Person::delete(&mut context.pool(), data.0.id).await?; - Site::delete(&mut context.pool(), data.3.id).await?; + Post::delete(&mut context.pool(), post.id).await?; + Community::delete(&mut context.pool(), community.id).await?; + Person::delete(&mut context.pool(), person.id).await?; + Site::delete(&mut context.pool(), site.id).await?; LocalSite::delete(&mut context.pool()).await?; Ok(()) } diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index 4e8519f78..737579662 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -285,9 +285,12 @@ pub(crate) mod tests { Ok(()) } - async fn cleanup(data: (ApubPerson, ApubSite), context: &LemmyContext) -> LemmyResult<()> { - DbPerson::delete(&mut context.pool(), data.0.id).await?; - Site::delete(&mut context.pool(), data.1.id).await?; + async fn cleanup( + (person, site): (ApubPerson, ApubSite), + context: &LemmyContext, + ) -> LemmyResult<()> { + DbPerson::delete(&mut context.pool(), person.id).await?; + Site::delete(&mut context.pool(), site.id).await?; Ok(()) } } diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 5a191cc66..3a61eb4a5 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -186,12 +186,12 @@ mod tests { } async fn cleanup( - data: (ApubPerson, ApubPerson, ApubSite), + (person1, person2, site): (ApubPerson, ApubPerson, ApubSite), context: &Data, ) -> LemmyResult<()> { - Person::delete(&mut context.pool(), data.0.id).await?; - Person::delete(&mut context.pool(), data.1.id).await?; - Site::delete(&mut context.pool(), data.2.id).await?; + Person::delete(&mut context.pool(), person1.id).await?; + Person::delete(&mut context.pool(), person2.id).await?; + Site::delete(&mut context.pool(), site.id).await?; Ok(()) } diff --git a/crates/db_views/src/custom_emoji_view.rs b/crates/db_views/src/custom_emoji_view.rs index a346c086d..606e807e9 100644 --- a/crates/db_views/src/custom_emoji_view.rs +++ b/crates/db_views/src/custom_emoji_view.rs @@ -76,16 +76,16 @@ impl CustomEmojiView { fn from_tuple_to_vec(items: Vec) -> Vec { let mut result = Vec::new(); let mut hash: HashMap> = HashMap::new(); - for item in &items { - let emoji_id: CustomEmojiId = item.0.id; + for (emoji, keyword) in &items { + let emoji_id: CustomEmojiId = emoji.id; if let std::collections::hash_map::Entry::Vacant(e) = hash.entry(emoji_id) { e.insert(Vec::new()); result.push(CustomEmojiView { - custom_emoji: item.0.clone(), + custom_emoji: emoji.clone(), keywords: Vec::new(), }) } - if let Some(item_keyword) = &item.1 { + if let Some(item_keyword) = &keyword { if let Some(keywords) = hash.get_mut(&emoji_id) { keywords.push(item_keyword.clone()) } diff --git a/crates/utils/src/utils/markdown/image_links.rs b/crates/utils/src/utils/markdown/image_links.rs index a21bb6f41..7456190e4 100644 --- a/crates/utils/src/utils/markdown/image_links.rs +++ b/crates/utils/src/utils/markdown/image_links.rs @@ -42,13 +42,10 @@ pub fn markdown_rewrite_image_links(mut src: String) -> (String, Vec) { pub fn markdown_handle_title(src: &str, start: usize, end: usize) -> (&str, Option<&str>) { let content = src.get(start..end).unwrap_or_default(); // necessary for custom emojis which look like `![name](url "title")` - let (url, extra) = if content.contains(' ') { - let split = content.split_once(' ').expect("split is valid"); - (split.0, Some(split.1)) - } else { - (content, None) - }; - (url, extra) + match content.split_once(' ') { + Some((a, b)) => (a, Some(b)), + _ => (content, None), + } } pub fn markdown_find_links(src: &str) -> Vec<(usize, usize)> { @@ -61,9 +58,9 @@ fn find_urls(src: &str) -> Vec<(usize, usize)> { let mut links_offsets = vec![]; ast.walk(|node, _depth| { if let Some(image) = node.cast::() { - let node_offsets = node.srcmap.expect("srcmap is none").get_byte_offsets(); - let start_offset = node_offsets.1 - image.url_len() - 1 - image.title_len(); - let end_offset = node_offsets.1 - 1; + let (_, node_offset) = node.srcmap.expect("srcmap is none").get_byte_offsets(); + let start_offset = node_offset - image.url_len() - 1 - image.title_len(); + let end_offset = node_offset - 1; links_offsets.push((start_offset, end_offset)); } diff --git a/src/scheduled_tasks.rs b/src/scheduled_tasks.rs index 2f99fe8a1..75942d3dd 100644 --- a/src/scheduled_tasks.rs +++ b/src/scheduled_tasks.rs @@ -393,10 +393,10 @@ async fn active_counts(pool: &mut DbPool<'_>) { ("6 months", "half_year"), ]; - for i in &intervals { + for (full_form, abbr) in &intervals { let update_site_stmt = format!( "update site_aggregates set users_active_{} = (select * from site_aggregates_activity('{}')) where site_id = 1", - i.1, i.0 + abbr, full_form ); sql_query(update_site_stmt) .execute(&mut conn) @@ -404,7 +404,7 @@ async fn active_counts(pool: &mut DbPool<'_>) { .inspect_err(|e| error!("Failed to update site stats: {e}")) .ok(); - let update_community_stmt = format!("update community_aggregates ca set users_active_{} = mv.count_ from community_aggregates_activity('{}') mv where ca.community_id = mv.community_id_", i.1, i.0); + let update_community_stmt = format!("update community_aggregates ca set users_active_{} = mv.count_ from community_aggregates_activity('{}') mv where ca.community_id = mv.community_id_", abbr, full_form); sql_query(update_community_stmt) .execute(&mut conn) .await From e8875dec99456c4217a3e844eb67bdca756a02ff Mon Sep 17 00:00:00 2001 From: flamingos-cant <45780476+flamingo-cant-draw@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:13:42 +0000 Subject: [PATCH 07/19] Append attachments to comments (#5143) * Append attachments to comments. * fmt * Proxy images + use newlines for separation * Use md for plain links * Use proxy_image_link directly --- crates/api_common/src/utils.rs | 2 +- .../objects/{note.json => note_1.json} | 0 .../apub/assets/mastodon/objects/note_2.json | 79 +++++++++++++++++++ crates/apub/src/objects/comment.rs | 4 +- crates/apub/src/objects/mod.rs | 19 ++++- crates/apub/src/protocol/objects/mod.rs | 3 +- crates/apub/src/protocol/objects/note.rs | 8 +- crates/apub/src/protocol/objects/page.rs | 21 ++++- 8 files changed, 130 insertions(+), 6 deletions(-) rename crates/apub/assets/mastodon/objects/{note.json => note_1.json} (100%) create mode 100644 crates/apub/assets/mastodon/objects/note_2.json diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index 87cdf9eef..e358d483b 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -1110,7 +1110,7 @@ async fn proxy_image_link_internal( /// Rewrite a link to go through `/api/v3/image_proxy` endpoint. This is only for remote urls and /// if image_proxy setting is enabled. -pub(crate) async fn proxy_image_link(link: Url, context: &LemmyContext) -> LemmyResult { +pub async fn proxy_image_link(link: Url, context: &LemmyContext) -> LemmyResult { proxy_image_link_internal( link, context.settings().pictrs_config()?.image_mode(), diff --git a/crates/apub/assets/mastodon/objects/note.json b/crates/apub/assets/mastodon/objects/note_1.json similarity index 100% rename from crates/apub/assets/mastodon/objects/note.json rename to crates/apub/assets/mastodon/objects/note_1.json diff --git a/crates/apub/assets/mastodon/objects/note_2.json b/crates/apub/assets/mastodon/objects/note_2.json new file mode 100644 index 000000000..b8c22b976 --- /dev/null +++ b/crates/apub/assets/mastodon/objects/note_2.json @@ -0,0 +1,79 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount", + "blurhash": "toot:blurhash", + "focalPoint": { + "@container": "@list", + "@id": "toot:focalPoint" + } + } + ], + "id": "https://floss.social/users/kde/statuses/113306831140126616", + "type": "Note", + "summary": null, + "inReplyTo": "https://floss.social/users/kde/statuses/113306824627995724", + "published": "2024-10-14T16:57:15Z", + "url": "https://floss.social/@kde/113306831140126616", + "attributedTo": "https://floss.social/users/kde", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "cc": [ + "https://floss.social/users/kde/followers", + "https://lemmy.kde.social/c/kde", + "https://lemmy.kde.social/c/kde/followers" + ], + "sensitive": false, + "atomUri": "https://floss.social/users/kde/statuses/113306831140126616", + "inReplyToAtomUri": "https://floss.social/users/kde/statuses/113306824627995724", + "conversation": "tag:floss.social,2024-10-14:objectId=71424279:objectType=Conversation", + "content": "

@kde@lemmy.kde.social

We also need funding 💶 to keep the gears turning! Please support us with a donation:

https://kde.org/donate/

[3/3]

", + "contentMap": { + "en": "

@kde@lemmy.kde.social

We also need funding 💶 to keep the gears turning! Please support us with a donation:

https://kde.org/donate/

[3/3]

" + }, + "attachment": [ + { + "type": "Document", + "mediaType": "image/jpeg", + "url": "https://cdn.masto.host/floss/media_attachments/files/113/306/826/682/985/891/original/c8d906a2f2ab2334.jpg", + "name": "The KDE dragons Katie and Konqi stand on either side of a pot filling up with gold coins. Donate!", + "blurhash": "USQv:h-W-qI-^,W;RPs=^-R%NZxbo#sDobSc", + "focalPoint": [0.0, 0.0], + "width": 1500, + "height": 1095 + } + ], + "tag": [ + { + "type": "Mention", + "href": "https://lemmy.kde.social/c/kde", + "name": "@kde@lemmy.kde.social" + } + ], + "replies": { + "id": "https://floss.social/users/kde/statuses/113306831140126616/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "https://floss.social/users/kde/statuses/113306831140126616/replies?only_other_accounts=true&page=true", + "partOf": "https://floss.social/users/kde/statuses/113306831140126616/replies", + "items": [] + } + }, + "likes": { + "id": "https://floss.social/users/kde/statuses/113306831140126616/likes", + "type": "Collection", + "totalItems": 39 + }, + "shares": { + "id": "https://floss.social/users/kde/statuses/113306831140126616/shares", + "type": "Collection", + "totalItems": 24 + } +} diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index e6cc2ba85..6e13afc91 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -3,7 +3,7 @@ use crate::{ check_apub_id_valid_with_strictness, fetcher::markdown_links::markdown_rewrite_remote_links, mentions::collect_non_local_mentions, - objects::{read_from_string_or_source, verify_is_remote_object}, + objects::{append_attachments_to_comment, read_from_string_or_source, verify_is_remote_object}, protocol::{ objects::{note::Note, LanguageTag}, InCommunity, @@ -124,6 +124,7 @@ impl Object for ApubComment { distinguished: Some(self.distinguished), language, audience: Some(community.actor_id.into()), + attachment: vec![], }; Ok(note) @@ -181,6 +182,7 @@ impl Object for ApubComment { let local_site = LocalSite::read(&mut context.pool()).await.ok(); let slur_regex = &local_site_opt_to_slur_regex(&local_site); let url_blocklist = get_url_blocklist(context).await?; + let content = append_attachments_to_comment(content, ¬e.attachment, context).await?; let content = process_markdown(&content, slur_regex, &url_blocklist, context).await?; let content = markdown_rewrite_remote_links(content, context).await; let language_id = Some( diff --git a/crates/apub/src/objects/mod.rs b/crates/apub/src/objects/mod.rs index e199ebfad..f837f7ad3 100644 --- a/crates/apub/src/objects/mod.rs +++ b/crates/apub/src/objects/mod.rs @@ -1,4 +1,4 @@ -use crate::protocol::Source; +use crate::protocol::{objects::page::Attachment, Source}; use activitypub_federation::{ config::Data, fetch::object_id::ObjectId, @@ -46,6 +46,23 @@ pub(crate) fn read_from_string_or_source_opt( .map(|content| read_from_string_or_source(content, media_type, source)) } +pub(crate) async fn append_attachments_to_comment( + content: String, + attachments: &[Attachment], + context: &Data, +) -> LemmyResult { + let mut content = content; + // Don't modify comments with no attachments + if !attachments.is_empty() { + content += "\n"; + for attachment in attachments { + content = content + "\n" + &attachment.as_markdown(context).await?; + } + } + + Ok(content) +} + /// When for example a Post is made in a remote community, the community will send it back, /// wrapped in Announce. If we simply receive this like any other federated object, overwrite the /// existing, local Post. In particular, it will set the field local = false, so that the object diff --git a/crates/apub/src/protocol/objects/mod.rs b/crates/apub/src/protocol/objects/mod.rs index dbba1bb8a..00fe26d2b 100644 --- a/crates/apub/src/protocol/objects/mod.rs +++ b/crates/apub/src/protocol/objects/mod.rs @@ -145,7 +145,8 @@ mod tests { #[test] fn test_parse_objects_mastodon() -> LemmyResult<()> { test_json::("assets/mastodon/objects/person.json")?; - test_json::("assets/mastodon/objects/note.json")?; + test_json::("assets/mastodon/objects/note_1.json")?; + test_json::("assets/mastodon/objects/note_2.json")?; test_json::("assets/mastodon/objects/page.json")?; Ok(()) } diff --git a/crates/apub/src/protocol/objects/note.rs b/crates/apub/src/protocol/objects/note.rs index e3e204254..21b5220f5 100644 --- a/crates/apub/src/protocol/objects/note.rs +++ b/crates/apub/src/protocol/objects/note.rs @@ -3,7 +3,11 @@ use crate::{ fetcher::post_or_comment::PostOrComment, mentions::MentionOrValue, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, - protocol::{objects::LanguageTag, InCommunity, Source}, + protocol::{ + objects::{page::Attachment, LanguageTag}, + InCommunity, + Source, + }, }; use activitypub_federation::{ config::Data, @@ -50,6 +54,8 @@ pub struct Note { pub(crate) distinguished: Option, pub(crate) language: Option, pub(crate) audience: Option>, + #[serde(default)] + pub(crate) attachment: Vec, } impl Note { diff --git a/crates/apub/src/protocol/objects/page.rs b/crates/apub/src/protocol/objects/page.rs index 97f767573..3ce720bc0 100644 --- a/crates/apub/src/protocol/objects/page.rs +++ b/crates/apub/src/protocol/objects/page.rs @@ -19,7 +19,7 @@ use activitypub_federation::{ }; use chrono::{DateTime, Utc}; use itertools::Itertools; -use lemmy_api_common::context::LemmyContext; +use lemmy_api_common::{context::LemmyContext, utils::proxy_image_link}; use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult}; use serde::{de::Error, Deserialize, Deserializer, Serialize}; use serde_with::skip_serializing_none; @@ -93,6 +93,7 @@ pub(crate) struct Document { #[serde(rename = "type")] kind: DocumentType, url: Url, + media_type: Option, /// Used for alt_text name: Option, } @@ -124,6 +125,24 @@ impl Attachment { _ => None, } } + + pub(crate) async fn as_markdown(&self, context: &Data) -> LemmyResult { + let (url, name, media_type) = match self { + Attachment::Image(i) => (i.url.clone(), i.name.clone(), Some(String::from("image"))), + Attachment::Document(d) => (d.url.clone(), d.name.clone(), d.media_type.clone()), + Attachment::Link(l) => (l.href.clone(), None, l.media_type.clone()), + }; + + let is_image = + media_type.is_some_and(|media| media.starts_with("video") || media.starts_with("image")); + + if is_image { + let url = proxy_image_link(url, context).await?; + Ok(format!("![{}]({url})", name.unwrap_or_default())) + } else { + Ok(format!("[{url}]({url})")) + } + } } #[derive(Clone, Debug, Deserialize, Serialize)] From 22e2290d7b20cc2c700769def062d36bcdcb4d72 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 22:47:04 -0400 Subject: [PATCH 08/19] chore(deps): update npm (#5153) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- api_tests/package.json | 2 +- api_tests/pnpm-lock.yaml | 436 +++++++++++++++++---------------------- 2 files changed, 189 insertions(+), 249 deletions(-) diff --git a/api_tests/package.json b/api_tests/package.json index 63c212d01..81e518ea4 100644 --- a/api_tests/package.json +++ b/api_tests/package.json @@ -6,7 +6,7 @@ "repository": "https://github.com/LemmyNet/lemmy", "author": "Dessalines", "license": "AGPL-3.0", - "packageManager": "pnpm@9.12.0", + "packageManager": "pnpm@9.12.3", "scripts": { "lint": "tsc --noEmit && eslint --report-unused-disable-directives && prettier --check 'src/**/*.ts'", "fix": "prettier --write src && eslint --fix src", diff --git a/api_tests/pnpm-lock.yaml b/api_tests/pnpm-lock.yaml index 6807f43f8..dd357d248 100644 --- a/api_tests/pnpm-lock.yaml +++ b/api_tests/pnpm-lock.yaml @@ -10,25 +10,25 @@ importers: devDependencies: '@types/jest': specifier: ^29.5.12 - version: 29.5.13 + version: 29.5.14 '@types/node': specifier: ^22.3.0 - version: 22.7.4 + version: 22.8.6 '@typescript-eslint/eslint-plugin': specifier: ^8.1.0 - version: 8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.6.2))(eslint@9.12.0)(typescript@5.6.2) + version: 8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3) '@typescript-eslint/parser': specifier: ^8.1.0 - version: 8.8.1(eslint@9.12.0)(typescript@5.6.2) + version: 8.12.2(eslint@9.13.0)(typescript@5.6.3) eslint: specifier: ^9.9.0 - version: 9.12.0 + version: 9.13.0 eslint-plugin-prettier: specifier: ^5.1.3 - version: 5.2.1(eslint@9.12.0)(prettier@3.3.3) + version: 5.2.1(eslint@9.13.0)(prettier@3.3.3) jest: specifier: ^29.5.0 - version: 29.7.0(@types/node@22.7.4) + version: 29.7.0(@types/node@22.8.6) lemmy-js-client: specifier: 0.20.0-alpha.11 version: 0.20.0-alpha.11 @@ -37,13 +37,13 @@ importers: version: 3.3.3 ts-jest: specifier: ^29.1.0 - version: 29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.7.4))(typescript@5.6.2) + version: 29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.8.6))(typescript@5.6.3) typescript: specifier: ^5.5.4 - version: 5.6.2 + version: 5.6.3 typescript-eslint: specifier: ^8.1.0 - version: 8.8.1(eslint@9.12.0)(typescript@5.6.2) + version: 8.12.2(eslint@9.13.0)(typescript@5.6.3) packages: @@ -51,8 +51,8 @@ packages: resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} engines: {node: '>=6.0.0'} - '@babel/code-frame@7.24.7': - resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} '@babel/compat-data@7.23.5': @@ -113,8 +113,8 @@ packages: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.24.7': - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.23.5': @@ -125,10 +125,6 @@ packages: resolution: {integrity: sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.24.7': - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} - engines: {node: '>=6.9.0'} - '@babel/parser@7.23.9': resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==} engines: {node: '>=6.0.0'} @@ -222,46 +218,46 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@eslint-community/eslint-utils@4.4.0': - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + '@eslint-community/eslint-utils@4.4.1': + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.11.1': - resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==} + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/config-array@0.18.0': resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.6.0': - resolution: {integrity: sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==} + '@eslint/core@0.7.0': + resolution: {integrity: sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.1.0': resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.12.0': - resolution: {integrity: sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==} + '@eslint/js@9.13.0': + resolution: {integrity: sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.4': resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.0': - resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==} + '@eslint/plugin-kit@0.2.2': + resolution: {integrity: sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@humanfs/core@0.19.0': - resolution: {integrity: sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==} + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.5': - resolution: {integrity: sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==} + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': @@ -416,14 +412,14 @@ packages: '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - '@types/jest@29.5.13': - resolution: {integrity: sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==} + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@22.7.4': - resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==} + '@types/node@22.8.6': + resolution: {integrity: sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==} '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -434,8 +430,8 @@ packages: '@types/yargs@17.0.32': resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} - '@typescript-eslint/eslint-plugin@8.8.1': - resolution: {integrity: sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==} + '@typescript-eslint/eslint-plugin@8.12.2': + resolution: {integrity: sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 @@ -445,8 +441,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.8.1': - resolution: {integrity: sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==} + '@typescript-eslint/parser@8.12.2': + resolution: {integrity: sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -455,12 +451,12 @@ packages: typescript: optional: true - '@typescript-eslint/scope-manager@8.8.1': - resolution: {integrity: sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==} + '@typescript-eslint/scope-manager@8.12.2': + resolution: {integrity: sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.8.1': - resolution: {integrity: sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==} + '@typescript-eslint/type-utils@8.12.2': + resolution: {integrity: sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -468,12 +464,12 @@ packages: typescript: optional: true - '@typescript-eslint/types@8.8.1': - resolution: {integrity: sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==} + '@typescript-eslint/types@8.12.2': + resolution: {integrity: sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.8.1': - resolution: {integrity: sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==} + '@typescript-eslint/typescript-estree@8.12.2': + resolution: {integrity: sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -481,14 +477,14 @@ packages: typescript: optional: true - '@typescript-eslint/utils@8.8.1': - resolution: {integrity: sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==} + '@typescript-eslint/utils@8.12.2': + resolution: {integrity: sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - '@typescript-eslint/visitor-keys@8.8.1': - resolution: {integrity: sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==} + '@typescript-eslint/visitor-keys@8.12.2': + resolution: {integrity: sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} acorn-jsx@5.3.2: @@ -496,8 +492,8 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} hasBin: true @@ -512,10 +508,6 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -609,10 +601,6 @@ packages: caniuse-lite@1.0.30001581: resolution: {integrity: sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ==} - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -639,16 +627,10 @@ packages: collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -721,10 +703,6 @@ packages: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} @@ -747,20 +725,20 @@ packages: eslint-config-prettier: optional: true - eslint-scope@8.1.0: - resolution: {integrity: sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==} + eslint-scope@8.2.0: + resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.1.0: - resolution: {integrity: sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==} + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.12.0: - resolution: {integrity: sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==} + eslint@9.13.0: + resolution: {integrity: sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -769,8 +747,8 @@ packages: jiti: optional: true - espree@10.2.0: - resolution: {integrity: sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==} + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esprima@4.0.1: @@ -911,10 +889,6 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1330,8 +1304,8 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -1480,10 +1454,6 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1518,8 +1488,8 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + ts-api-utils@1.4.0: + resolution: {integrity: sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' @@ -1563,8 +1533,8 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - typescript-eslint@8.8.1: - resolution: {integrity: sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ==} + typescript-eslint@8.12.2: + resolution: {integrity: sha512-UbuVUWSrHVR03q9CWx+JDHeO6B/Hr9p4U5lRH++5tq/EbFq1faYZe50ZSBePptgfIKLEti0aPQ3hFgnPVcd8ZQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -1572,8 +1542,8 @@ packages: typescript: optional: true - typescript@5.6.2: - resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + typescript@5.6.3: + resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} engines: {node: '>=14.17'} hasBin: true @@ -1642,17 +1612,18 @@ snapshots: '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.22 - '@babel/code-frame@7.24.7': + '@babel/code-frame@7.26.2': dependencies: - '@babel/highlight': 7.24.7 - picocolors: 1.1.0 + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 '@babel/compat-data@7.23.5': {} '@babel/core@7.23.9': dependencies: '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.26.2 '@babel/generator': 7.23.6 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9) @@ -1706,7 +1677,7 @@ snapshots: '@babel/helper-module-imports': 7.22.15 '@babel/helper-simple-access': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 + '@babel/helper-validator-identifier': 7.25.9 '@babel/helper-plugin-utils@7.22.5': {} @@ -1722,7 +1693,7 @@ snapshots: '@babel/helper-validator-identifier@7.22.20': {} - '@babel/helper-validator-identifier@7.24.7': {} + '@babel/helper-validator-identifier@7.25.9': {} '@babel/helper-validator-option@7.23.5': {} @@ -1734,13 +1705,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/highlight@7.24.7': - dependencies: - '@babel/helper-validator-identifier': 7.24.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.1.0 - '@babel/parser@7.23.9': dependencies: '@babel/types': 7.23.9 @@ -1817,13 +1781,13 @@ snapshots: '@babel/template@7.23.9': dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.26.2 '@babel/parser': 7.23.9 '@babel/types': 7.23.9 '@babel/traverse@7.23.9': dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.26.2 '@babel/generator': 7.23.6 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 @@ -1844,12 +1808,12 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@eslint-community/eslint-utils@4.4.0(eslint@9.12.0)': + '@eslint-community/eslint-utils@4.4.1(eslint@9.13.0)': dependencies: - eslint: 9.12.0 + eslint: 9.13.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.11.1': {} + '@eslint-community/regexpp@4.12.1': {} '@eslint/config-array@0.18.0': dependencies: @@ -1859,13 +1823,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/core@0.6.0': {} + '@eslint/core@0.7.0': {} '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 debug: 4.3.7 - espree: 10.2.0 + espree: 10.3.0 globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.0 @@ -1875,19 +1839,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.12.0': {} + '@eslint/js@9.13.0': {} '@eslint/object-schema@2.1.4': {} - '@eslint/plugin-kit@0.2.0': + '@eslint/plugin-kit@0.2.2': dependencies: levn: 0.4.1 - '@humanfs/core@0.19.0': {} + '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.5': + '@humanfs/node@0.16.6': dependencies: - '@humanfs/core': 0.19.0 + '@humanfs/core': 0.19.1 '@humanwhocodes/retry': 0.3.1 '@humanwhocodes/module-importer@1.0.1': {} @@ -1907,7 +1871,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.7.4 + '@types/node': 22.8.6 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -1920,14 +1884,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.7.4 + '@types/node': 22.8.6 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.7.4) + jest-config: 29.7.0(@types/node@22.8.6) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -1952,7 +1916,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.7.4 + '@types/node': 22.8.6 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -1970,7 +1934,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.7.4 + '@types/node': 22.8.6 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -1992,7 +1956,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.22 - '@types/node': 22.7.4 + '@types/node': 22.8.6 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -2062,7 +2026,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.7.4 + '@types/node': 22.8.6 '@types/yargs': 17.0.32 chalk: 4.1.2 @@ -2132,7 +2096,7 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.7.4 + '@types/node': 22.8.6 '@types/istanbul-lib-coverage@2.0.6': {} @@ -2144,14 +2108,14 @@ snapshots: dependencies: '@types/istanbul-lib-report': 3.0.3 - '@types/jest@29.5.13': + '@types/jest@29.5.14': dependencies: expect: 29.7.0 pretty-format: 29.7.0 '@types/json-schema@7.0.15': {} - '@types/node@22.7.4': + '@types/node@22.8.6': dependencies: undici-types: 6.19.8 @@ -2163,92 +2127,92 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.6.2))(eslint@9.12.0)(typescript@5.6.2)': + '@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)': dependencies: - '@eslint-community/regexpp': 4.11.1 - '@typescript-eslint/parser': 8.8.1(eslint@9.12.0)(typescript@5.6.2) - '@typescript-eslint/scope-manager': 8.8.1 - '@typescript-eslint/type-utils': 8.8.1(eslint@9.12.0)(typescript@5.6.2) - '@typescript-eslint/utils': 8.8.1(eslint@9.12.0)(typescript@5.6.2) - '@typescript-eslint/visitor-keys': 8.8.1 - eslint: 9.12.0 + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.12.2(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/scope-manager': 8.12.2 + '@typescript-eslint/type-utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.12.2 + eslint: 9.13.0 graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.6.2) + ts-api-utils: 1.4.0(typescript@5.6.3) optionalDependencies: - typescript: 5.6.2 + typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.6.2)': + '@typescript-eslint/parser@8.12.2(eslint@9.13.0)(typescript@5.6.3)': dependencies: - '@typescript-eslint/scope-manager': 8.8.1 - '@typescript-eslint/types': 8.8.1 - '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.2) - '@typescript-eslint/visitor-keys': 8.8.1 + '@typescript-eslint/scope-manager': 8.12.2 + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.12.2 debug: 4.3.7 - eslint: 9.12.0 + eslint: 9.13.0 optionalDependencies: - typescript: 5.6.2 + typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.8.1': + '@typescript-eslint/scope-manager@8.12.2': dependencies: - '@typescript-eslint/types': 8.8.1 - '@typescript-eslint/visitor-keys': 8.8.1 + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/visitor-keys': 8.12.2 - '@typescript-eslint/type-utils@8.8.1(eslint@9.12.0)(typescript@5.6.2)': + '@typescript-eslint/type-utils@8.12.2(eslint@9.13.0)(typescript@5.6.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.2) - '@typescript-eslint/utils': 8.8.1(eslint@9.12.0)(typescript@5.6.2) + '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3) + '@typescript-eslint/utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3) debug: 4.3.7 - ts-api-utils: 1.3.0(typescript@5.6.2) + ts-api-utils: 1.4.0(typescript@5.6.3) optionalDependencies: - typescript: 5.6.2 + typescript: 5.6.3 transitivePeerDependencies: - eslint - supports-color - '@typescript-eslint/types@8.8.1': {} + '@typescript-eslint/types@8.12.2': {} - '@typescript-eslint/typescript-estree@8.8.1(typescript@5.6.2)': + '@typescript-eslint/typescript-estree@8.12.2(typescript@5.6.3)': dependencies: - '@typescript-eslint/types': 8.8.1 - '@typescript-eslint/visitor-keys': 8.8.1 + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/visitor-keys': 8.12.2 debug: 4.3.7 fast-glob: 3.3.2 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.6.2) + ts-api-utils: 1.4.0(typescript@5.6.3) optionalDependencies: - typescript: 5.6.2 + typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.8.1(eslint@9.12.0)(typescript@5.6.2)': + '@typescript-eslint/utils@8.12.2(eslint@9.13.0)(typescript@5.6.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0) - '@typescript-eslint/scope-manager': 8.8.1 - '@typescript-eslint/types': 8.8.1 - '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.2) - eslint: 9.12.0 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.13.0) + '@typescript-eslint/scope-manager': 8.12.2 + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3) + eslint: 9.13.0 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/visitor-keys@8.8.1': + '@typescript-eslint/visitor-keys@8.12.2': dependencies: - '@typescript-eslint/types': 8.8.1 + '@typescript-eslint/types': 8.12.2 eslint-visitor-keys: 3.4.3 - acorn-jsx@5.3.2(acorn@8.12.1): + acorn-jsx@5.3.2(acorn@8.14.0): dependencies: - acorn: 8.12.1 + acorn: 8.14.0 - acorn@8.12.1: {} + acorn@8.14.0: {} ajv@6.12.6: dependencies: @@ -2263,10 +2227,6 @@ snapshots: ansi-regex@5.0.1: {} - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -2382,12 +2342,6 @@ snapshots: caniuse-lite@1.0.30001581: {} - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -2409,29 +2363,23 @@ snapshots: collect-v8-coverage@1.0.2: {} - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - color-convert@2.0.1: dependencies: color-name: 1.1.4 - color-name@1.1.3: {} - color-name@1.1.4: {} concat-map@0.0.1: {} convert-source-map@2.0.0: {} - create-jest@29.7.0(@types/node@22.7.4): + create-jest@29.7.0(@types/node@22.8.6): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.7.4) + jest-config: 29.7.0(@types/node@22.8.6) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -2476,38 +2424,36 @@ snapshots: escalade@3.1.1: {} - escape-string-regexp@1.0.5: {} - escape-string-regexp@2.0.0: {} escape-string-regexp@4.0.0: {} - eslint-plugin-prettier@5.2.1(eslint@9.12.0)(prettier@3.3.3): + eslint-plugin-prettier@5.2.1(eslint@9.13.0)(prettier@3.3.3): dependencies: - eslint: 9.12.0 + eslint: 9.13.0 prettier: 3.3.3 prettier-linter-helpers: 1.0.0 synckit: 0.9.1 - eslint-scope@8.1.0: + eslint-scope@8.2.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.1.0: {} + eslint-visitor-keys@4.2.0: {} - eslint@9.12.0: + eslint@9.13.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0) - '@eslint-community/regexpp': 4.11.1 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.13.0) + '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.18.0 - '@eslint/core': 0.6.0 + '@eslint/core': 0.7.0 '@eslint/eslintrc': 3.1.0 - '@eslint/js': 9.12.0 - '@eslint/plugin-kit': 0.2.0 - '@humanfs/node': 0.16.5 + '@eslint/js': 9.13.0 + '@eslint/plugin-kit': 0.2.2 + '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.3.1 '@types/estree': 1.0.6 @@ -2517,9 +2463,9 @@ snapshots: cross-spawn: 7.0.3 debug: 4.3.7 escape-string-regexp: 4.0.0 - eslint-scope: 8.1.0 - eslint-visitor-keys: 4.1.0 - espree: 10.2.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -2538,11 +2484,11 @@ snapshots: transitivePeerDependencies: - supports-color - espree@10.2.0: + espree@10.3.0: dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) - eslint-visitor-keys: 4.1.0 + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 4.2.0 esprima@4.0.1: {} @@ -2677,8 +2623,6 @@ snapshots: graphemer@1.4.0: {} - has-flag@3.0.0: {} - has-flag@4.0.0: {} hasown@2.0.0: @@ -2792,7 +2736,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.7.4 + '@types/node': 22.8.6 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -2812,16 +2756,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.7.4): + jest-cli@29.7.0(@types/node@22.8.6): dependencies: '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.7.4) + create-jest: 29.7.0(@types/node@22.8.6) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@22.7.4) + jest-config: 29.7.0(@types/node@22.8.6) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -2831,7 +2775,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.7.4): + jest-config@29.7.0(@types/node@22.8.6): dependencies: '@babel/core': 7.23.9 '@jest/test-sequencer': 29.7.0 @@ -2856,7 +2800,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.7.4 + '@types/node': 22.8.6 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -2885,7 +2829,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.7.4 + '@types/node': 22.8.6 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -2895,7 +2839,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.7.4 + '@types/node': 22.8.6 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -2921,7 +2865,7 @@ snapshots: jest-message-util@29.7.0: dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.26.2 '@jest/types': 29.6.3 '@types/stack-utils': 2.0.3 chalk: 4.1.2 @@ -2934,7 +2878,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.7.4 + '@types/node': 22.8.6 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -2969,7 +2913,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.7.4 + '@types/node': 22.8.6 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -2997,7 +2941,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.7.4 + '@types/node': 22.8.6 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.2 @@ -3043,7 +2987,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.7.4 + '@types/node': 22.8.6 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -3062,7 +3006,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.7.4 + '@types/node': 22.8.6 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -3071,17 +3015,17 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 22.7.4 + '@types/node': 22.8.6 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.7.4): + jest@29.7.0(@types/node@22.8.6): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@22.7.4) + jest-cli: 29.7.0(@types/node@22.8.6) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -3237,7 +3181,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.26.2 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -3250,7 +3194,7 @@ snapshots: path-parse@1.0.7: {} - picocolors@1.1.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -3363,10 +3307,6 @@ snapshots: strip-json-comments@3.1.1: {} - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -3398,22 +3338,22 @@ snapshots: dependencies: is-number: 7.0.0 - ts-api-utils@1.3.0(typescript@5.6.2): + ts-api-utils@1.4.0(typescript@5.6.3): dependencies: - typescript: 5.6.2 + typescript: 5.6.3 - ts-jest@29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.7.4))(typescript@5.6.2): + ts-jest@29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.8.6))(typescript@5.6.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.7.4) + jest: 29.7.0(@types/node@22.8.6) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.6.3 - typescript: 5.6.2 + typescript: 5.6.3 yargs-parser: 21.1.1 optionalDependencies: '@babel/core': 7.23.9 @@ -3431,18 +3371,18 @@ snapshots: type-fest@0.21.3: {} - typescript-eslint@8.8.1(eslint@9.12.0)(typescript@5.6.2): + typescript-eslint@8.12.2(eslint@9.13.0)(typescript@5.6.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.6.2))(eslint@9.12.0)(typescript@5.6.2) - '@typescript-eslint/parser': 8.8.1(eslint@9.12.0)(typescript@5.6.2) - '@typescript-eslint/utils': 8.8.1(eslint@9.12.0)(typescript@5.6.2) + '@typescript-eslint/eslint-plugin': 8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/parser': 8.12.2(eslint@9.13.0)(typescript@5.6.3) + '@typescript-eslint/utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3) optionalDependencies: - typescript: 5.6.2 + typescript: 5.6.3 transitivePeerDependencies: - eslint - supports-color - typescript@5.6.2: {} + typescript@5.6.3: {} undici-types@6.19.8: {} @@ -3450,7 +3390,7 @@ snapshots: dependencies: browserslist: 4.22.3 escalade: 3.1.1 - picocolors: 1.1.0 + picocolors: 1.1.1 uri-js@4.4.1: dependencies: From 556191ef1650f38240097473506d24be81ab0c90 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 23:02:50 -0400 Subject: [PATCH 09/19] chore(deps): update docker (#5154) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .woodpecker.yml | 2 +- docker/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index a2124c1cc..b1ab18c97 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -2,7 +2,7 @@ # See https://github.com/woodpecker-ci/woodpecker/issues/1677 variables: - - &rust_image "rust:1.81" + - &rust_image "rust:1.82" - &rust_nightly_image "rustlang/rust:nightly" - &install_pnpm "corepack enable pnpm" - &install_binstall "wget -O- https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz | tar -xvz -C /usr/local/cargo/bin" diff --git a/docker/Dockerfile b/docker/Dockerfile index 9701b4ad6..9e6bb23b4 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.10 +# syntax=docker/dockerfile:1.11 ARG RUST_VERSION=1.81 ARG CARGO_BUILD_FEATURES=default ARG RUST_RELEASE_MODE=debug From 30951a37b6b9d9172084e8737e12127f01902181 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 1 Nov 2024 09:34:12 -0400 Subject: [PATCH 10/19] Revert "chore(deps): update docker (#5154)" (#5156) This reverts commit 556191ef1650f38240097473506d24be81ab0c90. --- .woodpecker.yml | 2 +- docker/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index b1ab18c97..a2124c1cc 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -2,7 +2,7 @@ # See https://github.com/woodpecker-ci/woodpecker/issues/1677 variables: - - &rust_image "rust:1.82" + - &rust_image "rust:1.81" - &rust_nightly_image "rustlang/rust:nightly" - &install_pnpm "corepack enable pnpm" - &install_binstall "wget -O- https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz | tar -xvz -C /usr/local/cargo/bin" diff --git a/docker/Dockerfile b/docker/Dockerfile index 9e6bb23b4..9701b4ad6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.11 +# syntax=docker/dockerfile:1.10 ARG RUST_VERSION=1.81 ARG CARGO_BUILD_FEATURES=default ARG RUST_RELEASE_MODE=debug From 02ba54c58964cc9b7d4928241716a6e0942c029b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 09:57:33 -0400 Subject: [PATCH 11/19] chore(deps): update node.js to v22 (#5155) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .woodpecker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index a2124c1cc..885796cac 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -215,7 +215,7 @@ steps: when: *slow_check_paths run_federation_tests: - image: node:20-bookworm-slim + image: node:22-bookworm-slim environment: LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432 DO_WRITE_HOSTS_FILE: "1" From 18bf9843bce48b33cf9b350724520665279e4edb Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 4 Nov 2024 04:44:58 -0500 Subject: [PATCH 12/19] Fixing LemmyError imports. (#5157) --- crates/api/src/post/get_link_metadata.rs | 5 +---- crates/api/src/site/registration_applications/tests.rs | 5 ++++- crates/api_common/src/lib.rs | 2 +- crates/api_crud/src/post/mod.rs | 2 +- crates/apub/src/activity_lists.rs | 2 +- crates/apub/src/api/resolve_object.rs | 2 +- crates/apub/src/http/community.rs | 2 +- crates/apub/src/http/person.rs | 2 +- crates/apub/src/protocol/objects/note.rs | 5 ++++- crates/db_schema/src/impls/captcha_answer.rs | 2 +- crates/db_schema/src/impls/community_block.rs | 2 +- crates/db_schema/src/impls/instance_block.rs | 2 +- crates/db_schema/src/impls/login_token.rs | 2 +- crates/db_schema/src/impls/person.rs | 2 +- crates/db_schema/src/impls/person_block.rs | 2 +- crates/db_schema/src/impls/site.rs | 2 +- crates/db_views/src/site_view.rs | 2 +- crates/db_views_actor/src/community_moderator_view.rs | 2 +- crates/db_views_actor/src/community_person_ban_view.rs | 2 +- crates/db_views_actor/src/community_view.rs | 2 +- crates/utils/src/lib.rs | 1 - crates/utils/src/utils/markdown/mod.rs | 2 +- crates/utils/tests/test_errors_used.rs | 2 +- src/scheduled_tasks.rs | 5 ++++- 24 files changed, 32 insertions(+), 27 deletions(-) diff --git a/crates/api/src/post/get_link_metadata.rs b/crates/api/src/post/get_link_metadata.rs index e469b51c7..a777cab17 100644 --- a/crates/api/src/post/get_link_metadata.rs +++ b/crates/api/src/post/get_link_metadata.rs @@ -5,10 +5,7 @@ use lemmy_api_common::{ request::fetch_link_metadata, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::{ - error::{LemmyErrorExt, LemmyResult}, - LemmyErrorType, -}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use url::Url; #[tracing::instrument(skip(context))] diff --git a/crates/api/src/site/registration_applications/tests.rs b/crates/api/src/site/registration_applications/tests.rs index 022cbf236..bdfb1535e 100644 --- a/crates/api/src/site/registration_applications/tests.rs +++ b/crates/api/src/site/registration_applications/tests.rs @@ -31,7 +31,10 @@ use lemmy_db_schema::{ RegistrationMode, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::{error::LemmyResult, LemmyErrorType, CACHE_DURATION_API}; +use lemmy_utils::{ + error::{LemmyErrorType, LemmyResult}, + CACHE_DURATION_API, +}; use serial_test::serial; async fn create_test_site(context: &Data) -> LemmyResult<(Instance, LocalUserView)> { diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs index 68eeadecc..6e09d904d 100644 --- a/crates/api_common/src/lib.rs +++ b/crates/api_common/src/lib.rs @@ -26,7 +26,7 @@ pub extern crate lemmy_db_views_actor; pub extern crate lemmy_db_views_moderator; pub extern crate lemmy_utils; -pub use lemmy_utils::LemmyErrorType; +pub use lemmy_utils::error::LemmyErrorType; use serde::{Deserialize, Serialize}; use std::{cmp::min, time::Duration}; diff --git a/crates/api_crud/src/post/mod.rs b/crates/api_crud/src/post/mod.rs index 95df9663c..5db0ad5d0 100644 --- a/crates/api_crud/src/post/mod.rs +++ b/crates/api_crud/src/post/mod.rs @@ -2,7 +2,7 @@ use chrono::{DateTime, TimeZone, Utc}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::post::Post; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; pub mod create; pub mod delete; diff --git a/crates/apub/src/activity_lists.rs b/crates/apub/src/activity_lists.rs index 9262236d8..1ba31b9b4 100644 --- a/crates/apub/src/activity_lists.rs +++ b/crates/apub/src/activity_lists.rs @@ -26,7 +26,7 @@ use crate::{ }; use activitypub_federation::{config::Data, traits::ActivityHandler}; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use serde::{Deserialize, Serialize}; use url::Url; diff --git a/crates/apub/src/api/resolve_object.rs b/crates/apub/src/api/resolve_object.rs index d9d50e69e..04d489592 100644 --- a/crates/apub/src/api/resolve_object.rs +++ b/crates/apub/src/api/resolve_object.rs @@ -86,7 +86,7 @@ mod tests { traits::Crud, }; use lemmy_db_views::structs::LocalUserView; - use lemmy_utils::{error::LemmyResult, LemmyErrorType}; + use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use serial_test::serial; #[tokio::test] diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index 37482aedb..2516020d3 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -15,7 +15,7 @@ use activitypub_federation::{ use actix_web::{web, HttpResponse}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{source::community::Community, traits::ApubActor}; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use serde::Deserialize; #[derive(Deserialize, Clone)] diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index 0f628c497..f8afceb94 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -7,7 +7,7 @@ use activitypub_federation::{config::Data, traits::Object}; use actix_web::{web, HttpResponse}; use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url}; use lemmy_db_schema::{source::person::Person, traits::ApubActor}; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use serde::Deserialize; #[derive(Deserialize)] diff --git a/crates/apub/src/protocol/objects/note.rs b/crates/apub/src/protocol/objects/note.rs index 21b5220f5..fc38b9b5e 100644 --- a/crates/apub/src/protocol/objects/note.rs +++ b/crates/apub/src/protocol/objects/note.rs @@ -24,7 +24,10 @@ use lemmy_db_schema::{ source::{community::Community, post::Post}, traits::Crud, }; -use lemmy_utils::{error::LemmyResult, LemmyErrorType, MAX_COMMENT_DEPTH_LIMIT}; +use lemmy_utils::{ + error::{LemmyErrorType, LemmyResult}, + MAX_COMMENT_DEPTH_LIMIT, +}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use url::Url; diff --git a/crates/db_schema/src/impls/captcha_answer.rs b/crates/db_schema/src/impls/captcha_answer.rs index d7183e4fb..e7ba86d39 100644 --- a/crates/db_schema/src/impls/captcha_answer.rs +++ b/crates/db_schema/src/impls/captcha_answer.rs @@ -13,7 +13,7 @@ use diesel::{ QueryDsl, }; use diesel_async::RunQueryDsl; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; impl CaptchaAnswer { pub async fn insert(pool: &mut DbPool<'_>, captcha: &CaptchaAnswerForm) -> Result { diff --git a/crates/db_schema/src/impls/community_block.rs b/crates/db_schema/src/impls/community_block.rs index cd541cd8b..c78953d27 100644 --- a/crates/db_schema/src/impls/community_block.rs +++ b/crates/db_schema/src/impls/community_block.rs @@ -16,7 +16,7 @@ use diesel::{ QueryDsl, }; use diesel_async::RunQueryDsl; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; impl CommunityBlock { pub async fn read( diff --git a/crates/db_schema/src/impls/instance_block.rs b/crates/db_schema/src/impls/instance_block.rs index 1eb6e8f04..1b70f0e08 100644 --- a/crates/db_schema/src/impls/instance_block.rs +++ b/crates/db_schema/src/impls/instance_block.rs @@ -16,7 +16,7 @@ use diesel::{ QueryDsl, }; use diesel_async::RunQueryDsl; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; impl InstanceBlock { pub async fn read( diff --git a/crates/db_schema/src/impls/login_token.rs b/crates/db_schema/src/impls/login_token.rs index c8c44c506..f4f7a7aae 100644 --- a/crates/db_schema/src/impls/login_token.rs +++ b/crates/db_schema/src/impls/login_token.rs @@ -7,7 +7,7 @@ use crate::{ }; use diesel::{delete, dsl::exists, insert_into, result::Error, select}; use diesel_async::RunQueryDsl; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; impl LoginToken { pub async fn create(pool: &mut DbPool<'_>, form: LoginTokenCreateForm) -> Result { diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs index a5f8ae1a0..fb287ef77 100644 --- a/crates/db_schema/src/impls/person.rs +++ b/crates/db_schema/src/impls/person.rs @@ -21,7 +21,7 @@ use diesel::{ QueryDsl, }; use diesel_async::RunQueryDsl; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; #[async_trait] impl Crud for Person { diff --git a/crates/db_schema/src/impls/person_block.rs b/crates/db_schema/src/impls/person_block.rs index 7f2286616..44c83b3f8 100644 --- a/crates/db_schema/src/impls/person_block.rs +++ b/crates/db_schema/src/impls/person_block.rs @@ -17,7 +17,7 @@ use diesel::{ QueryDsl, }; use diesel_async::RunQueryDsl; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; impl PersonBlock { pub async fn read( diff --git a/crates/db_schema/src/impls/site.rs b/crates/db_schema/src/impls/site.rs index 8f57647a3..e993639fa 100644 --- a/crates/db_schema/src/impls/site.rs +++ b/crates/db_schema/src/impls/site.rs @@ -10,7 +10,7 @@ use crate::{ }; use diesel::{dsl::insert_into, result::Error, ExpressionMethods, OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use url::Url; #[async_trait] diff --git a/crates/db_views/src/site_view.rs b/crates/db_views/src/site_view.rs index 6014ad964..ed9aeb498 100644 --- a/crates/db_views/src/site_view.rs +++ b/crates/db_views/src/site_view.rs @@ -5,7 +5,7 @@ use lemmy_db_schema::{ schema::{local_site, local_site_rate_limit, site, site_aggregates}, utils::{get_conn, DbPool}, }; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; impl SiteView { pub async fn read_local(pool: &mut DbPool<'_>) -> LemmyResult { diff --git a/crates/db_views_actor/src/community_moderator_view.rs b/crates/db_views_actor/src/community_moderator_view.rs index ebcdcbd25..7126af1f6 100644 --- a/crates/db_views_actor/src/community_moderator_view.rs +++ b/crates/db_views_actor/src/community_moderator_view.rs @@ -8,7 +8,7 @@ use lemmy_db_schema::{ source::local_user::LocalUser, utils::{get_conn, DbPool}, }; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; impl CommunityModeratorView { pub async fn check_is_community_moderator( diff --git a/crates/db_views_actor/src/community_person_ban_view.rs b/crates/db_views_actor/src/community_person_ban_view.rs index 5543222f3..9bfa0704c 100644 --- a/crates/db_views_actor/src/community_person_ban_view.rs +++ b/crates/db_views_actor/src/community_person_ban_view.rs @@ -11,7 +11,7 @@ use lemmy_db_schema::{ schema::community_person_ban, utils::{get_conn, DbPool}, }; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; impl CommunityPersonBanView { pub async fn check( diff --git a/crates/db_views_actor/src/community_view.rs b/crates/db_views_actor/src/community_view.rs index 9ff6fadce..de749fff3 100644 --- a/crates/db_views_actor/src/community_view.rs +++ b/crates/db_views_actor/src/community_view.rs @@ -35,7 +35,7 @@ use lemmy_db_schema::{ ListingType, PostSortType, }; -use lemmy_utils::{error::LemmyResult, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; fn queries<'a>() -> Queries< impl ReadFn<'a, CommunityView, (CommunityId, Option<&'a LocalUser>, bool)>, diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 7f0691496..1e0cbefbf 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -13,7 +13,6 @@ cfg_if! { } pub mod error; -pub use error::LemmyErrorType; use std::time::Duration; pub type ConnectionId = usize; diff --git a/crates/utils/src/utils/markdown/mod.rs b/crates/utils/src/utils/markdown/mod.rs index a51b507ce..9d34e8a69 100644 --- a/crates/utils/src/utils/markdown/mod.rs +++ b/crates/utils/src/utils/markdown/mod.rs @@ -1,4 +1,4 @@ -use crate::{error::LemmyResult, LemmyErrorType}; +use crate::error::{LemmyErrorType, LemmyResult}; use markdown_it::MarkdownIt; use regex::RegexSet; use std::sync::LazyLock; diff --git a/crates/utils/tests/test_errors_used.rs b/crates/utils/tests/test_errors_used.rs index d0dec489a..bfd70bdad 100644 --- a/crates/utils/tests/test_errors_used.rs +++ b/crates/utils/tests/test_errors_used.rs @@ -1,4 +1,4 @@ -use lemmy_utils::LemmyErrorType; +use lemmy_utils::error::LemmyErrorType; use std::{env::current_dir, process::Command}; use strum::IntoEnumIterator; diff --git a/src/scheduled_tasks.rs b/src/scheduled_tasks.rs index 75942d3dd..e7c8a676c 100644 --- a/src/scheduled_tasks.rs +++ b/src/scheduled_tasks.rs @@ -609,7 +609,10 @@ mod tests { use crate::scheduled_tasks::build_update_instance_form; use lemmy_api_common::request::client_builder; - use lemmy_utils::{error::LemmyResult, settings::structs::Settings, LemmyErrorType}; + use lemmy_utils::{ + error::{LemmyErrorType, LemmyResult}, + settings::structs::Settings, + }; use pretty_assertions::assert_eq; use reqwest_middleware::ClientBuilder; use serial_test::serial; From 4690aff1e58311aa323a427e1cd620b9aa43dd0d Mon Sep 17 00:00:00 2001 From: Nutomic Date: Mon, 4 Nov 2024 14:16:54 +0100 Subject: [PATCH 13/19] Run analyze after changing post.url type (ref #4983) (#5148) * Run analyze after changing post.url type (ref #4983) * rename back --------- Co-authored-by: Dessalines --- .../2024-08-03-155932_increase_post_url_max_length/down.sql | 2 ++ .../2024-08-03-155932_increase_post_url_max_length/up.sql | 2 ++ 2 files changed, 4 insertions(+) diff --git a/migrations/2024-08-03-155932_increase_post_url_max_length/down.sql b/migrations/2024-08-03-155932_increase_post_url_max_length/down.sql index d25918578..7e4feef3d 100644 --- a/migrations/2024-08-03-155932_increase_post_url_max_length/down.sql +++ b/migrations/2024-08-03-155932_increase_post_url_max_length/down.sql @@ -1,3 +1,5 @@ ALTER TABLE post ALTER COLUMN url TYPE varchar(512); +ANALYZE post (url); + diff --git a/migrations/2024-08-03-155932_increase_post_url_max_length/up.sql b/migrations/2024-08-03-155932_increase_post_url_max_length/up.sql index 7c6818d22..8e5a3a9ce 100644 --- a/migrations/2024-08-03-155932_increase_post_url_max_length/up.sql +++ b/migrations/2024-08-03-155932_increase_post_url_max_length/up.sql @@ -3,3 +3,5 @@ ALTER TABLE post ALTER COLUMN url TYPE varchar(2000); +ANALYZE post (url); + From 9f4038756920de340f866abf3cad042d1ee8eac7 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 5 Nov 2024 11:45:58 -0500 Subject: [PATCH 14/19] Fixing sample image to fix unit tests. (#5167) Apparently yahoo.com doesnt want to return metatags for my local CI runners anymore. --- api_tests/src/shared.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index 8ec4b29ed..017dad903 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -83,7 +83,7 @@ export const fetchFunction = fetch; export const imageFetchLimit = 50; export const sampleImage = "https://i.pinimg.com/originals/df/5f/5b/df5f5b1b174a2b4b6026cc6c8f9395c1.jpg"; -export const sampleSite = "https://yahoo.com"; +export const sampleSite = "https://google.com"; export const alphaUrl = "http://127.0.0.1:8541"; export const betaUrl = "http://127.0.0.1:8551"; From 298c8fa52176b1a263ce9b4252e88e6526cc5c30 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 5 Nov 2024 12:09:25 -0500 Subject: [PATCH 15/19] Add filter to hide posts with comments. (#5158) * Add filter to hide posts with comments. - Useful for Q/A type communities. - Fixes #1106 * Changing to no_comments_only --- crates/api_common/src/post.rs | 2 ++ crates/apub/src/api/list_posts.rs | 2 ++ crates/db_views/src/post_view.rs | 36 +++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/crates/api_common/src/post.rs b/crates/api_common/src/post.rs index fa45459e2..22d5e1431 100644 --- a/crates/api_common/src/post.rs +++ b/crates/api_common/src/post.rs @@ -85,6 +85,8 @@ pub struct GetPosts { pub show_read: Option, /// If true, then show the nsfw posts (even if your user setting is to hide them) pub show_nsfw: Option, + /// If true, then only show posts with no comments + pub no_comments_only: Option, pub page_cursor: Option, } diff --git a/crates/apub/src/api/list_posts.rs b/crates/apub/src/api/list_posts.rs index d75a82d3b..cdf24dbaa 100644 --- a/crates/apub/src/api/list_posts.rs +++ b/crates/apub/src/api/list_posts.rs @@ -42,6 +42,7 @@ pub async fn list_posts( let show_hidden = data.show_hidden; let show_read = data.show_read; let show_nsfw = data.show_nsfw; + let no_comments_only = data.no_comments_only; let liked_only = data.liked_only; let disliked_only = data.disliked_only; @@ -82,6 +83,7 @@ pub async fn list_posts( show_hidden, show_read, show_nsfw, + no_comments_only, ..Default::default() } .list(&local_site.site, &mut context.pool()) diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index 4fa2222ae..13520f1cf 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -401,6 +401,11 @@ fn queries<'a>() -> Queries< query = query.filter(person::bot_account.eq(false)); }; + // Filter to show only posts with no comments + if options.no_comments_only.unwrap_or_default() { + query = query.filter(post_aggregates::comments.eq(0)); + }; + // If its saved only, then filter, and order by the saved time, not the comment creation time. if options.saved_only.unwrap_or_default() { query = query @@ -617,6 +622,7 @@ pub struct PostQuery<'a> { pub show_hidden: Option, pub show_read: Option, pub show_nsfw: Option, + pub no_comments_only: Option, } impl<'a> PostQuery<'a> { @@ -1988,4 +1994,34 @@ mod tests { cleanup(data, pool).await } + + #[tokio::test] + #[serial] + async fn post_listings_no_comments_only() -> LemmyResult<()> { + let pool = &build_db_pool().await?; + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // Create a comment for a post + let comment_form = CommentInsertForm::new( + data.local_user_view.person.id, + data.inserted_post.id, + "a comment".to_owned(), + ); + Comment::create(pool, &comment_form, None).await?; + + // Make sure it doesnt come back with the no_comments option + let post_listings_no_comments = PostQuery { + sort: Some(PostSortType::New), + no_comments_only: Some(true), + local_user: Some(&data.local_user_view.local_user), + ..Default::default() + } + .list(&data.site, pool) + .await?; + + assert_eq!(vec![POST_BY_BOT], names(&post_listings_no_comments)); + + cleanup(data, pool).await + } } From d162ec1638dadd7b8f6a7a3b0c0443db05c8e270 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 6 Nov 2024 04:24:49 -0500 Subject: [PATCH 16/19] Removing strip = true from release profile. (#5166) * Adding abort on panic to release profile to make binary smaller. * Remove strip and panic from release profile. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5523dcfd6..99e6a53e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,10 +24,10 @@ doctest = false [lints] workspace = true +# See https://github.com/johnthagen/min-sized-rust for additional optimizations [profile.release] debug = 0 lto = "fat" -strip = true # Automatically strip symbols from the binary. opt-level = 3 # Optimize for speed, not size. codegen-units = 1 # Reduce parallel code generation. From df664d9d9ae3f40b70e924b77758cd2396053071 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 6 Nov 2024 09:50:13 -0500 Subject: [PATCH 17/19] Upgrading ts_rs to 10.0.0 (#5163) * Upgrading ts_rs to 10.0.0 * Adding ts_option directives, and woodpecker test. * Fixing ts_options. --- .woodpecker.yml | 9 ++ Cargo.lock | 17 +-- Cargo.toml | 3 +- crates/api_common/src/comment.rs | 24 ++++ crates/api_common/src/community.rs | 32 +++++ crates/api_common/src/custom_emoji.rs | 4 + crates/api_common/src/oauth_provider.rs | 18 ++- crates/api_common/src/person.rs | 62 ++++++++ crates/api_common/src/post.rs | 48 +++++++ crates/api_common/src/private_message.rs | 7 + crates/api_common/src/site.rs | 133 ++++++++++++++++++ crates/api_common/src/tagline.rs | 2 + crates/api_common/src/utils.rs | 4 +- crates/apub/src/lib.rs | 15 +- crates/db_schema/src/newtypes.rs | 16 +-- crates/db_schema/src/sensitive.rs | 19 +-- crates/db_schema/src/source/comment.rs | 1 + crates/db_schema/src/source/comment_report.rs | 2 + crates/db_schema/src/source/community.rs | 5 + crates/db_schema/src/source/custom_emoji.rs | 1 + .../src/source/federation_queue_state.rs | 3 + crates/db_schema/src/source/images.rs | 1 + crates/db_schema/src/source/instance.rs | 3 + crates/db_schema/src/source/local_site.rs | 4 + .../src/source/local_site_rate_limit.rs | 1 + .../src/source/local_site_url_blocklist.rs | 1 + crates/db_schema/src/source/local_user.rs | 1 + crates/db_schema/src/source/login_token.rs | 2 + crates/db_schema/src/source/moderator.rs | 12 ++ crates/db_schema/src/source/oauth_account.rs | 1 + crates/db_schema/src/source/oauth_provider.rs | 1 + crates/db_schema/src/source/person.rs | 7 + crates/db_schema/src/source/post.rs | 14 +- crates/db_schema/src/source/post_report.rs | 4 + .../db_schema/src/source/private_message.rs | 1 + .../src/source/private_message_report.rs | 2 + .../src/source/registration_application.rs | 2 + crates/db_schema/src/source/site.rs | 6 + crates/db_schema/src/source/tagline.rs | 1 + crates/db_views/src/structs.rs | 9 ++ crates/db_views_actor/src/structs.rs | 2 + crates/db_views_moderator/src/structs.rs | 22 +++ crates/utils/src/error.rs | 12 +- scripts/ts_bindings_check.sh | 14 ++ 44 files changed, 492 insertions(+), 56 deletions(-) create mode 100755 scripts/ts_bindings_check.sh diff --git a/.woodpecker.yml b/.woodpecker.yml index 885796cac..16cd49375 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -181,6 +181,15 @@ steps: - cargo test --workspace --no-fail-fast when: *slow_check_paths + check_ts_bindings: + image: *rust_image + environment: + CARGO_HOME: .cargo_home + commands: + - ./scripts/ts_bindings_check.sh + when: + - event: pull_request + check_diesel_migration: # TODO: use willsquire/diesel-cli image when shared libraries become optional in lemmy_server image: *rust_image diff --git a/Cargo.lock b/Cargo.lock index 491b7cc94..edf237ab3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" - [[package]] name = "accept-language" version = "3.1.0" @@ -5255,22 +5249,23 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "ts-rs" -version = "7.1.1" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2cae1fc5d05d47aa24b64f9a4f7cba24cdc9187a2084dd97ac57bef5eccae6" +checksum = "3a2f31991cee3dce1ca4f929a8a04fdd11fd8801aac0f2030b0fa8a0a3fef6b9" dependencies = [ "chrono", + "lazy_static", "thiserror", "ts-rs-macros", + "url", ] [[package]] name = "ts-rs-macros" -version = "7.1.1" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f7f9b821696963053a89a7bd8b292dc34420aea8294d7b225274d488f3ec92" +checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e" dependencies = [ - "Inflector", "proc-macro2", "quote", "syn 2.0.77", diff --git a/Cargo.toml b/Cargo.toml index 99e6a53e2..0373c4e51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,10 +142,11 @@ itertools = "0.13.0" futures = "0.3.30" http = "1.1" rosetta-i18n = "0.1.3" -ts-rs = { version = "7.1.1", features = [ +ts-rs = { version = "10.0.0", features = [ "serde-compat", "chrono-impl", "no-serde-warnings", + "url-impl", ] } rustls = { version = "0.23.12", features = ["ring"] } futures-util = "0.3.30" diff --git a/crates/api_common/src/comment.rs b/crates/api_common/src/comment.rs index 48800cf8d..e08365789 100644 --- a/crates/api_common/src/comment.rs +++ b/crates/api_common/src/comment.rs @@ -17,7 +17,9 @@ use ts_rs::TS; pub struct CreateComment { pub content: String, pub post_id: PostId, + #[cfg_attr(feature = "full", ts(optional))] pub parent_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub language_id: Option, } @@ -37,7 +39,9 @@ pub struct GetComment { /// Edit a comment. pub struct EditComment { pub comment_id: CommentId, + #[cfg_attr(feature = "full", ts(optional))] pub content: Option, + #[cfg_attr(feature = "full", ts(optional))] pub language_id: Option, } @@ -69,6 +73,7 @@ pub struct DeleteComment { pub struct RemoveComment { pub comment_id: CommentId, pub removed: bool, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, } @@ -107,17 +112,29 @@ pub struct CreateCommentLike { #[cfg_attr(feature = "full", ts(export))] /// Get a list of comments. pub struct GetComments { + #[cfg_attr(feature = "full", ts(optional))] pub type_: Option, + #[cfg_attr(feature = "full", ts(optional))] pub sort: Option, + #[cfg_attr(feature = "full", ts(optional))] pub max_depth: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, + #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub community_name: Option, + #[cfg_attr(feature = "full", ts(optional))] pub post_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub parent_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub saved_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub liked_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub disliked_only: Option, } @@ -161,12 +178,17 @@ pub struct ResolveCommentReport { #[cfg_attr(feature = "full", ts(export))] /// List comment reports. pub struct ListCommentReports { + #[cfg_attr(feature = "full", ts(optional))] pub comment_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, /// Only shows the unresolved reports + #[cfg_attr(feature = "full", ts(optional))] pub unresolved_only: Option, /// if no community is given, it returns reports for all communities moderated by the auth user + #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, } @@ -185,7 +207,9 @@ pub struct ListCommentReportsResponse { /// List comment likes. Admins-only. pub struct ListCommentLikes { pub comment_id: CommentId, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, } diff --git a/crates/api_common/src/community.rs b/crates/api_common/src/community.rs index 1def2111b..2fab2d05c 100644 --- a/crates/api_common/src/community.rs +++ b/crates/api_common/src/community.rs @@ -19,10 +19,13 @@ use ts_rs::TS; #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] +// TODO make this into a tagged enum /// Get a community. Must provide either an id, or a name. pub struct GetCommunity { + #[cfg_attr(feature = "full", ts(optional))] pub id: Option, /// Example: star_trek , or star_trek@xyz.tld + #[cfg_attr(feature = "full", ts(optional))] pub name: Option, } @@ -33,6 +36,7 @@ pub struct GetCommunity { /// The community response. pub struct GetCommunityResponse { pub community_view: CommunityView, + #[cfg_attr(feature = "full", ts(optional))] pub site: Option, pub moderators: Vec, pub discussion_languages: Vec, @@ -49,18 +53,26 @@ pub struct CreateCommunity { /// A longer title. pub title: String, /// A sidebar for the community in markdown. + #[cfg_attr(feature = "full", ts(optional))] pub sidebar: Option, /// A shorter, one line description of your community. + #[cfg_attr(feature = "full", ts(optional))] pub description: Option, /// An icon URL. + #[cfg_attr(feature = "full", ts(optional))] pub icon: Option, /// A banner URL. + #[cfg_attr(feature = "full", ts(optional))] pub banner: Option, /// Whether its an NSFW community. + #[cfg_attr(feature = "full", ts(optional))] pub nsfw: Option, /// Whether to restrict posting only to moderators. + #[cfg_attr(feature = "full", ts(optional))] pub posting_restricted_to_mods: Option, + #[cfg_attr(feature = "full", ts(optional))] pub discussion_languages: Option>, + #[cfg_attr(feature = "full", ts(optional))] pub visibility: Option, } @@ -79,10 +91,15 @@ pub struct CommunityResponse { #[cfg_attr(feature = "full", ts(export))] /// Fetches a list of communities. pub struct ListCommunities { + #[cfg_attr(feature = "full", ts(optional))] pub type_: Option, + #[cfg_attr(feature = "full", ts(optional))] pub sort: Option, + #[cfg_attr(feature = "full", ts(optional))] pub show_nsfw: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, } @@ -105,11 +122,14 @@ pub struct BanFromCommunity { pub ban: bool, /// Optionally remove or restore all their data. Useful for new troll accounts. /// If ban is true, then this means remove. If ban is false, it means restore. + #[cfg_attr(feature = "full", ts(optional))] pub remove_or_restore_data: Option, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, /// A time that the ban will expire, in unix epoch seconds. /// /// An i64 unix timestamp is used for a simpler API client implementation. + #[cfg_attr(feature = "full", ts(optional))] pub expires: Option, } @@ -148,20 +168,29 @@ pub struct AddModToCommunityResponse { pub struct EditCommunity { pub community_id: CommunityId, /// A longer title. + #[cfg_attr(feature = "full", ts(optional))] pub title: Option, /// A sidebar for the community in markdown. + #[cfg_attr(feature = "full", ts(optional))] pub sidebar: Option, /// A shorter, one line description of your community. + #[cfg_attr(feature = "full", ts(optional))] pub description: Option, /// An icon URL. + #[cfg_attr(feature = "full", ts(optional))] pub icon: Option, /// A banner URL. + #[cfg_attr(feature = "full", ts(optional))] pub banner: Option, /// Whether its an NSFW community. + #[cfg_attr(feature = "full", ts(optional))] pub nsfw: Option, /// Whether to restrict posting only to moderators. + #[cfg_attr(feature = "full", ts(optional))] pub posting_restricted_to_mods: Option, + #[cfg_attr(feature = "full", ts(optional))] pub discussion_languages: Option>, + #[cfg_attr(feature = "full", ts(optional))] pub visibility: Option, } @@ -173,6 +202,7 @@ pub struct EditCommunity { pub struct HideCommunity { pub community_id: CommunityId, pub hidden: bool, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, } @@ -194,6 +224,7 @@ pub struct DeleteCommunity { pub struct RemoveCommunity { pub community_id: CommunityId, pub removed: bool, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, } @@ -240,5 +271,6 @@ pub struct TransferCommunity { #[cfg_attr(feature = "full", ts(export))] /// Fetches a random community pub struct GetRandomCommunity { + #[cfg_attr(feature = "full", ts(optional))] pub type_: Option, } diff --git a/crates/api_common/src/custom_emoji.rs b/crates/api_common/src/custom_emoji.rs index 3804b71af..76bc9c9e2 100644 --- a/crates/api_common/src/custom_emoji.rs +++ b/crates/api_common/src/custom_emoji.rs @@ -62,8 +62,12 @@ pub struct ListCustomEmojisResponse { #[cfg_attr(feature = "full", ts(export))] /// Fetches a list of custom emojis. pub struct ListCustomEmojis { + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, + #[cfg_attr(feature = "full", ts(optional))] pub category: Option, + #[cfg_attr(feature = "full", ts(optional))] pub ignore_page_limits: Option, } diff --git a/crates/api_common/src/oauth_provider.rs b/crates/api_common/src/oauth_provider.rs index 14847edf1..36fef3b18 100644 --- a/crates/api_common/src/oauth_provider.rs +++ b/crates/api_common/src/oauth_provider.rs @@ -20,8 +20,11 @@ pub struct CreateOAuthProvider { pub client_id: String, pub client_secret: String, pub scopes: String, + #[cfg_attr(feature = "full", ts(optional))] pub auto_verify_email: Option, + #[cfg_attr(feature = "full", ts(optional))] pub account_linking_enabled: Option, + #[cfg_attr(feature = "full", ts(optional))] pub enabled: Option, } @@ -32,15 +35,25 @@ pub struct CreateOAuthProvider { /// Edit an external auth method. pub struct EditOAuthProvider { pub id: OAuthProviderId, + #[cfg_attr(feature = "full", ts(optional))] pub display_name: Option, + #[cfg_attr(feature = "full", ts(optional))] pub authorization_endpoint: Option, + #[cfg_attr(feature = "full", ts(optional))] pub token_endpoint: Option, + #[cfg_attr(feature = "full", ts(optional))] pub userinfo_endpoint: Option, + #[cfg_attr(feature = "full", ts(optional))] pub id_claim: Option, + #[cfg_attr(feature = "full", ts(optional))] pub client_secret: Option, + #[cfg_attr(feature = "full", ts(optional))] pub scopes: Option, + #[cfg_attr(feature = "full", ts(optional))] pub auto_verify_email: Option, + #[cfg_attr(feature = "full", ts(optional))] pub account_linking_enabled: Option, + #[cfg_attr(feature = "full", ts(optional))] pub enabled: Option, } @@ -59,13 +72,14 @@ pub struct DeleteOAuthProvider { /// Logging in with an OAuth 2.0 authorization pub struct AuthenticateWithOauth { pub code: String, - #[cfg_attr(feature = "full", ts(type = "string"))] pub oauth_provider_id: OAuthProviderId, - #[cfg_attr(feature = "full", ts(type = "string"))] pub redirect_uri: Url, + #[cfg_attr(feature = "full", ts(optional))] pub show_nsfw: Option, /// Username is mandatory at registration time + #[cfg_attr(feature = "full", ts(optional))] pub username: Option, /// An answer is mandatory if require application is enabled on the server + #[cfg_attr(feature = "full", ts(optional))] pub answer: Option, } diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs index 6f1ddfe43..6b64d8467 100644 --- a/crates/api_common/src/person.rs +++ b/crates/api_common/src/person.rs @@ -28,6 +28,7 @@ pub struct Login { pub username_or_email: SensitiveString, pub password: SensitiveString, /// May be required, if totp is enabled for their account. + #[cfg_attr(feature = "full", ts(optional))] pub totp_2fa_token: Option, } @@ -40,16 +41,22 @@ pub struct Register { pub username: String, pub password: SensitiveString, pub password_verify: SensitiveString, + #[cfg_attr(feature = "full", ts(optional))] pub show_nsfw: Option, /// email is mandatory if email verification is enabled on the server + #[cfg_attr(feature = "full", ts(optional))] pub email: Option, /// The UUID of the captcha item. + #[cfg_attr(feature = "full", ts(optional))] pub captcha_uuid: Option, /// Your captcha answer. + #[cfg_attr(feature = "full", ts(optional))] pub captcha_answer: Option, /// A form field to trick signup bots. Should be None. + #[cfg_attr(feature = "full", ts(optional))] pub honeypot: Option, /// An answer is mandatory if require application is enabled on the server + #[cfg_attr(feature = "full", ts(optional))] pub answer: Option, } @@ -60,6 +67,7 @@ pub struct Register { /// A wrapper for the captcha response. pub struct GetCaptchaResponse { /// Will be None if captchas are disabled. + #[cfg_attr(feature = "full", ts(optional))] pub ok: Option, } @@ -83,60 +91,89 @@ pub struct CaptchaResponse { /// Saves settings for your user. pub struct SaveUserSettings { /// Show nsfw posts. + #[cfg_attr(feature = "full", ts(optional))] pub show_nsfw: Option, /// Blur nsfw posts. + #[cfg_attr(feature = "full", ts(optional))] pub blur_nsfw: Option, /// Your user's theme. + #[cfg_attr(feature = "full", ts(optional))] pub theme: Option, /// The default post listing type, usually "local" + #[cfg_attr(feature = "full", ts(optional))] pub default_listing_type: Option, /// A post-view mode that changes how multiple post listings look. + #[cfg_attr(feature = "full", ts(optional))] pub post_listing_mode: Option, /// The default post sort, usually "active" + #[cfg_attr(feature = "full", ts(optional))] pub default_post_sort_type: Option, /// The default comment sort, usually "hot" + #[cfg_attr(feature = "full", ts(optional))] pub default_comment_sort_type: Option, /// The language of the lemmy interface + #[cfg_attr(feature = "full", ts(optional))] pub interface_language: Option, /// A URL for your avatar. + #[cfg_attr(feature = "full", ts(optional))] pub avatar: Option, /// A URL for your banner. + #[cfg_attr(feature = "full", ts(optional))] pub banner: Option, /// Your display name, which can contain strange characters, and does not need to be unique. + #[cfg_attr(feature = "full", ts(optional))] pub display_name: Option, /// Your email. + #[cfg_attr(feature = "full", ts(optional))] pub email: Option, /// Your bio / info, in markdown. + #[cfg_attr(feature = "full", ts(optional))] pub bio: Option, /// Your matrix user id. Ex: @my_user:matrix.org + #[cfg_attr(feature = "full", ts(optional))] pub matrix_user_id: Option, /// Whether to show or hide avatars. + #[cfg_attr(feature = "full", ts(optional))] pub show_avatars: Option, /// Sends notifications to your email. + #[cfg_attr(feature = "full", ts(optional))] pub send_notifications_to_email: Option, /// Whether this account is a bot account. Users can hide these accounts easily if they wish. + #[cfg_attr(feature = "full", ts(optional))] pub bot_account: Option, /// Whether to show bot accounts. + #[cfg_attr(feature = "full", ts(optional))] pub show_bot_accounts: Option, /// Whether to show read posts. + #[cfg_attr(feature = "full", ts(optional))] pub show_read_posts: Option, /// A list of languages you are able to see discussion in. + #[cfg_attr(feature = "full", ts(optional))] pub discussion_languages: Option>, /// Open links in a new tab + #[cfg_attr(feature = "full", ts(optional))] pub open_links_in_new_tab: Option, /// Enable infinite scroll + #[cfg_attr(feature = "full", ts(optional))] pub infinite_scroll_enabled: Option, /// Whether to allow keyboard navigation (for browsing and interacting with posts and comments). + #[cfg_attr(feature = "full", ts(optional))] pub enable_keyboard_navigation: Option, /// Whether user avatars or inline images in the UI that are gifs should be allowed to play or /// should be paused + #[cfg_attr(feature = "full", ts(optional))] pub enable_animated_images: Option, /// Whether to auto-collapse bot comments. + #[cfg_attr(feature = "full", ts(optional))] pub collapse_bot_comments: Option, /// Some vote display mode settings + #[cfg_attr(feature = "full", ts(optional))] pub show_scores: Option, + #[cfg_attr(feature = "full", ts(optional))] pub show_upvotes: Option, + #[cfg_attr(feature = "full", ts(optional))] pub show_downvotes: Option, + #[cfg_attr(feature = "full", ts(optional))] pub show_upvote_percentage: Option, } @@ -158,6 +195,7 @@ pub struct ChangePassword { pub struct LoginResponse { /// This is None in response to `Register` if email verification is enabled, or the server /// requires registration applications. + #[cfg_attr(feature = "full", ts(optional))] pub jwt: Option, /// If registration applications are required, this will return true for a signup response. pub registration_created: bool, @@ -173,13 +211,20 @@ pub struct LoginResponse { /// /// Either person_id, or username are required. pub struct GetPersonDetails { + #[cfg_attr(feature = "full", ts(optional))] pub person_id: Option, /// Example: dessalines , or dessalines@xyz.tld + #[cfg_attr(feature = "full", ts(optional))] pub username: Option, + #[cfg_attr(feature = "full", ts(optional))] pub sort: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, + #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub saved_only: Option, } @@ -190,6 +235,7 @@ pub struct GetPersonDetails { /// A person's details response. pub struct GetPersonDetailsResponse { pub person_view: PersonView, + #[cfg_attr(feature = "full", ts(optional))] pub site: Option, pub comments: Vec, pub posts: Vec, @@ -223,11 +269,14 @@ pub struct BanPerson { pub ban: bool, /// Optionally remove or restore all their data. Useful for new troll accounts. /// If ban is true, then this means remove. If ban is false, it means restore. + #[cfg_attr(feature = "full", ts(optional))] pub remove_or_restore_data: Option, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, /// A time that the ban will expire, in unix epoch seconds. /// /// An i64 unix timestamp is used for a simpler API client implementation. + #[cfg_attr(feature = "full", ts(optional))] pub expires: Option, } @@ -273,9 +322,13 @@ pub struct BlockPersonResponse { #[cfg_attr(feature = "full", ts(export))] /// Get comment replies. pub struct GetReplies { + #[cfg_attr(feature = "full", ts(optional))] pub sort: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, + #[cfg_attr(feature = "full", ts(optional))] pub unread_only: Option, } @@ -294,9 +347,13 @@ pub struct GetRepliesResponse { #[cfg_attr(feature = "full", ts(export))] /// Get mentions for your user. pub struct GetPersonMentions { + #[cfg_attr(feature = "full", ts(optional))] pub sort: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, + #[cfg_attr(feature = "full", ts(optional))] pub unread_only: Option, } @@ -375,6 +432,7 @@ pub struct PasswordChangeAfterReset { #[cfg_attr(feature = "full", ts(export))] /// Get a count of the number of reports. pub struct GetReportCount { + #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, } @@ -384,9 +442,11 @@ pub struct GetReportCount { #[cfg_attr(feature = "full", ts(export))] /// A response for the number of reports. pub struct GetReportCountResponse { + #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, pub comment_reports: i64, pub post_reports: i64, + #[cfg_attr(feature = "full", ts(optional))] pub private_message_reports: Option, } @@ -436,7 +496,9 @@ pub struct UpdateTotpResponse { #[cfg_attr(feature = "full", ts(export))] /// Get your user's image / media uploads. pub struct ListMedia { + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, } diff --git a/crates/api_common/src/post.rs b/crates/api_common/src/post.rs index 22d5e1431..ca4f53e9d 100644 --- a/crates/api_common/src/post.rs +++ b/crates/api_common/src/post.rs @@ -19,18 +19,26 @@ use ts_rs::TS; pub struct CreatePost { pub name: String, pub community_id: CommunityId, + #[cfg_attr(feature = "full", ts(optional))] pub url: Option, /// An optional body for the post in markdown. + #[cfg_attr(feature = "full", ts(optional))] pub body: Option, /// An optional alt_text, usable for image posts. + #[cfg_attr(feature = "full", ts(optional))] pub alt_text: Option, /// A honeypot to catch bots. Should be None. + #[cfg_attr(feature = "full", ts(optional))] pub honeypot: Option, + #[cfg_attr(feature = "full", ts(optional))] pub nsfw: Option, + #[cfg_attr(feature = "full", ts(optional))] pub language_id: Option, /// Instead of fetching a thumbnail, use a custom one. + #[cfg_attr(feature = "full", ts(optional))] pub custom_thumbnail: Option, /// Time when this post should be scheduled. Null means publish immediately. + #[cfg_attr(feature = "full", ts(optional))] pub scheduled_publish_time: Option, } @@ -45,9 +53,12 @@ pub struct PostResponse { #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] +// TODO this should be made into a tagged enum /// Get a post. Needs either the post id, or comment_id. pub struct GetPost { + #[cfg_attr(feature = "full", ts(optional))] pub id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub comment_id: Option, } @@ -70,23 +81,37 @@ pub struct GetPostResponse { #[cfg_attr(feature = "full", ts(export))] /// Get a list of posts. pub struct GetPosts { + #[cfg_attr(feature = "full", ts(optional))] pub type_: Option, + #[cfg_attr(feature = "full", ts(optional))] pub sort: Option, /// DEPRECATED, use page_cursor + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, + #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub community_name: Option, + #[cfg_attr(feature = "full", ts(optional))] pub saved_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub liked_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub disliked_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub show_hidden: Option, /// If true, then show the read posts (even if your user setting is to hide them) + #[cfg_attr(feature = "full", ts(optional))] pub show_read: Option, /// If true, then show the nsfw posts (even if your user setting is to hide them) + #[cfg_attr(feature = "full", ts(optional))] pub show_nsfw: Option, + #[cfg_attr(feature = "full", ts(optional))] /// If true, then only show posts with no comments pub no_comments_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page_cursor: Option, } @@ -98,6 +123,7 @@ pub struct GetPosts { pub struct GetPostsResponse { pub posts: Vec, /// the pagination cursor to use to fetch the next page + #[cfg_attr(feature = "full", ts(optional))] pub next_page: Option, } @@ -118,17 +144,25 @@ pub struct CreatePostLike { /// Edit a post. pub struct EditPost { pub post_id: PostId, + #[cfg_attr(feature = "full", ts(optional))] pub name: Option, + #[cfg_attr(feature = "full", ts(optional))] pub url: Option, /// An optional body for the post in markdown. + #[cfg_attr(feature = "full", ts(optional))] pub body: Option, /// An optional alt_text, usable for image posts. + #[cfg_attr(feature = "full", ts(optional))] pub alt_text: Option, + #[cfg_attr(feature = "full", ts(optional))] pub nsfw: Option, + #[cfg_attr(feature = "full", ts(optional))] pub language_id: Option, /// Instead of fetching a thumbnail, use a custom one. + #[cfg_attr(feature = "full", ts(optional))] pub custom_thumbnail: Option, /// Time when this post should be scheduled. Null means publish immediately. + #[cfg_attr(feature = "full", ts(optional))] pub scheduled_publish_time: Option, } @@ -149,6 +183,7 @@ pub struct DeletePost { pub struct RemovePost { pub post_id: PostId, pub removed: bool, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, } @@ -232,12 +267,18 @@ pub struct ResolvePostReport { #[cfg_attr(feature = "full", ts(export))] /// List post reports. pub struct ListPostReports { + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, /// Only shows the unresolved reports + #[cfg_attr(feature = "full", ts(optional))] pub unresolved_only: Option, + // TODO make into tagged enum at some point /// if no community is given, it returns reports for all communities moderated by the auth user + #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub post_id: Option, } @@ -273,6 +314,7 @@ pub struct GetSiteMetadataResponse { pub struct LinkMetadata { #[serde(flatten)] pub opengraph_data: OpenGraphData, + #[cfg_attr(feature = "full", ts(optional))] pub content_type: Option, } @@ -282,9 +324,13 @@ pub struct LinkMetadata { #[cfg_attr(feature = "full", ts(export))] /// Site metadata, from its opengraph tags. pub struct OpenGraphData { + #[cfg_attr(feature = "full", ts(optional))] pub title: Option, + #[cfg_attr(feature = "full", ts(optional))] pub description: Option, + #[cfg_attr(feature = "full", ts(optional))] pub(crate) image: Option, + #[cfg_attr(feature = "full", ts(optional))] pub embed_video_url: Option, } @@ -295,7 +341,9 @@ pub struct OpenGraphData { /// List post likes. Admins-only. pub struct ListPostLikes { pub post_id: PostId, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, } diff --git a/crates/api_common/src/private_message.rs b/crates/api_common/src/private_message.rs index 429d68643..666fe3865 100644 --- a/crates/api_common/src/private_message.rs +++ b/crates/api_common/src/private_message.rs @@ -47,9 +47,13 @@ pub struct MarkPrivateMessageAsRead { #[cfg_attr(feature = "full", ts(export))] /// Get your private messages. pub struct GetPrivateMessages { + #[cfg_attr(feature = "full", ts(optional))] pub unread_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, + #[cfg_attr(feature = "full", ts(optional))] pub creator_id: Option, } @@ -102,9 +106,12 @@ pub struct ResolvePrivateMessageReport { /// List private message reports. // TODO , perhaps GetReports should be a tagged enum list too. pub struct ListPrivateMessageReports { + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, /// Only shows the unresolved reports + #[cfg_attr(feature = "full", ts(optional))] pub unresolved_only: Option, } diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index 8fc091e9d..40a5cc42d 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -71,18 +71,31 @@ use ts_rs::TS; /// Searches the site, given a query string, and some optional filters. pub struct Search { pub q: String, + #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub community_name: Option, + #[cfg_attr(feature = "full", ts(optional))] pub creator_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub type_: Option, + #[cfg_attr(feature = "full", ts(optional))] pub sort: Option, + #[cfg_attr(feature = "full", ts(optional))] pub listing_type: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, + #[cfg_attr(feature = "full", ts(optional))] pub title_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub post_url_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub saved_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub liked_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub disliked_only: Option, } @@ -115,9 +128,13 @@ pub struct ResolveObject { // TODO Change this to an enum /// The response of an apub object fetch. pub struct ResolveObjectResponse { + #[cfg_attr(feature = "full", ts(optional))] pub comment: Option, + #[cfg_attr(feature = "full", ts(optional))] pub post: Option, + #[cfg_attr(feature = "full", ts(optional))] pub community: Option, + #[cfg_attr(feature = "full", ts(optional))] pub person: Option, } @@ -127,13 +144,21 @@ pub struct ResolveObjectResponse { #[cfg_attr(feature = "full", ts(export))] /// Fetches the modlog. pub struct GetModlog { + #[cfg_attr(feature = "full", ts(optional))] pub mod_person_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, + #[cfg_attr(feature = "full", ts(optional))] pub type_: Option, + #[cfg_attr(feature = "full", ts(optional))] pub other_person_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub post_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub comment_id: Option, } @@ -167,50 +192,95 @@ pub struct GetModlogResponse { /// Creates a site. Should be done after first running lemmy. pub struct CreateSite { pub name: String, + #[cfg_attr(feature = "full", ts(optional))] pub sidebar: Option, + #[cfg_attr(feature = "full", ts(optional))] pub description: Option, + #[cfg_attr(feature = "full", ts(optional))] pub icon: Option, + #[cfg_attr(feature = "full", ts(optional))] pub banner: Option, + #[cfg_attr(feature = "full", ts(optional))] pub enable_nsfw: Option, + #[cfg_attr(feature = "full", ts(optional))] pub community_creation_admin_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub require_email_verification: Option, + #[cfg_attr(feature = "full", ts(optional))] pub application_question: Option, + #[cfg_attr(feature = "full", ts(optional))] pub private_instance: Option, + #[cfg_attr(feature = "full", ts(optional))] pub default_theme: Option, + #[cfg_attr(feature = "full", ts(optional))] pub default_post_listing_type: Option, + #[cfg_attr(feature = "full", ts(optional))] pub default_post_listing_mode: Option, + #[cfg_attr(feature = "full", ts(optional))] pub default_post_sort_type: Option, + #[cfg_attr(feature = "full", ts(optional))] pub default_comment_sort_type: Option, + #[cfg_attr(feature = "full", ts(optional))] pub legal_information: Option, + #[cfg_attr(feature = "full", ts(optional))] pub application_email_admins: Option, + #[cfg_attr(feature = "full", ts(optional))] pub hide_modlog_mod_names: Option, + #[cfg_attr(feature = "full", ts(optional))] pub discussion_languages: Option>, + #[cfg_attr(feature = "full", ts(optional))] pub slur_filter_regex: Option, + #[cfg_attr(feature = "full", ts(optional))] pub actor_name_max_length: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_message: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_message_per_second: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_post: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_post_per_second: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_register: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_register_per_second: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_image: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_image_per_second: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_comment: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_comment_per_second: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_search: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_search_per_second: Option, + #[cfg_attr(feature = "full", ts(optional))] pub federation_enabled: Option, + #[cfg_attr(feature = "full", ts(optional))] pub federation_debug: Option, + #[cfg_attr(feature = "full", ts(optional))] pub captcha_enabled: Option, + #[cfg_attr(feature = "full", ts(optional))] pub captcha_difficulty: Option, + #[cfg_attr(feature = "full", ts(optional))] pub allowed_instances: Option>, + #[cfg_attr(feature = "full", ts(optional))] pub blocked_instances: Option>, + #[cfg_attr(feature = "full", ts(optional))] pub registration_mode: Option, + #[cfg_attr(feature = "full", ts(optional))] pub oauth_registration: Option, + #[cfg_attr(feature = "full", ts(optional))] pub content_warning: Option, + #[cfg_attr(feature = "full", ts(optional))] pub post_upvotes: Option, + #[cfg_attr(feature = "full", ts(optional))] pub post_downvotes: Option, + #[cfg_attr(feature = "full", ts(optional))] pub comment_upvotes: Option, + #[cfg_attr(feature = "full", ts(optional))] pub comment_downvotes: Option, } @@ -220,94 +290,142 @@ pub struct CreateSite { #[cfg_attr(feature = "full", ts(export))] /// Edits a site. pub struct EditSite { + #[cfg_attr(feature = "full", ts(optional))] pub name: Option, /// A sidebar for the site, in markdown. + #[cfg_attr(feature = "full", ts(optional))] pub sidebar: Option, /// A shorter, one line description of your site. + #[cfg_attr(feature = "full", ts(optional))] pub description: Option, /// A url for your site's icon. + #[cfg_attr(feature = "full", ts(optional))] pub icon: Option, /// A url for your site's banner. + #[cfg_attr(feature = "full", ts(optional))] pub banner: Option, /// Whether to enable NSFW. + #[cfg_attr(feature = "full", ts(optional))] pub enable_nsfw: Option, /// Limits community creation to admins only. + #[cfg_attr(feature = "full", ts(optional))] pub community_creation_admin_only: Option, /// Whether to require email verification. + #[cfg_attr(feature = "full", ts(optional))] pub require_email_verification: Option, /// Your application question form. This is in markdown, and can be many questions. + #[cfg_attr(feature = "full", ts(optional))] pub application_question: Option, /// Whether your instance is public, or private. + #[cfg_attr(feature = "full", ts(optional))] pub private_instance: Option, /// The default theme. Usually "browser" + #[cfg_attr(feature = "full", ts(optional))] pub default_theme: Option, /// The default post listing type, usually "local" + #[cfg_attr(feature = "full", ts(optional))] pub default_post_listing_type: Option, /// Default value for listing mode, usually "list" + #[cfg_attr(feature = "full", ts(optional))] pub default_post_listing_mode: Option, /// The default post sort, usually "active" + #[cfg_attr(feature = "full", ts(optional))] pub default_post_sort_type: Option, /// The default comment sort, usually "hot" + #[cfg_attr(feature = "full", ts(optional))] pub default_comment_sort_type: Option, /// An optional page of legal information + #[cfg_attr(feature = "full", ts(optional))] pub legal_information: Option, /// Whether to email admins when receiving a new application. + #[cfg_attr(feature = "full", ts(optional))] pub application_email_admins: Option, /// Whether to hide moderator names from the modlog. + #[cfg_attr(feature = "full", ts(optional))] pub hide_modlog_mod_names: Option, /// A list of allowed discussion languages. + #[cfg_attr(feature = "full", ts(optional))] pub discussion_languages: Option>, /// A regex string of items to filter. + #[cfg_attr(feature = "full", ts(optional))] pub slur_filter_regex: Option, /// The max length of actor names. + #[cfg_attr(feature = "full", ts(optional))] pub actor_name_max_length: Option, /// The number of messages allowed in a given time frame. + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_message: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_message_per_second: Option, /// The number of posts allowed in a given time frame. + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_post: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_post_per_second: Option, /// The number of registrations allowed in a given time frame. + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_register: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_register_per_second: Option, /// The number of image uploads allowed in a given time frame. + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_image: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_image_per_second: Option, /// The number of comments allowed in a given time frame. + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_comment: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_comment_per_second: Option, /// The number of searches allowed in a given time frame. + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_search: Option, + #[cfg_attr(feature = "full", ts(optional))] pub rate_limit_search_per_second: Option, /// Whether to enable federation. + #[cfg_attr(feature = "full", ts(optional))] pub federation_enabled: Option, /// Enables federation debugging. + #[cfg_attr(feature = "full", ts(optional))] pub federation_debug: Option, /// Whether to enable captchas for signups. + #[cfg_attr(feature = "full", ts(optional))] pub captcha_enabled: Option, /// The captcha difficulty. Can be easy, medium, or hard + #[cfg_attr(feature = "full", ts(optional))] pub captcha_difficulty: Option, /// A list of allowed instances. If none are set, federation is open. + #[cfg_attr(feature = "full", ts(optional))] pub allowed_instances: Option>, /// A list of blocked instances. + #[cfg_attr(feature = "full", ts(optional))] pub blocked_instances: Option>, /// A list of blocked URLs + #[cfg_attr(feature = "full", ts(optional))] pub blocked_urls: Option>, + #[cfg_attr(feature = "full", ts(optional))] pub registration_mode: Option, /// Whether to email admins for new reports. + #[cfg_attr(feature = "full", ts(optional))] pub reports_email_admins: Option, /// If present, nsfw content is visible by default. Should be displayed by frontends/clients /// when the site is first opened by a user. + #[cfg_attr(feature = "full", ts(optional))] pub content_warning: Option, /// Whether or not external auth methods can auto-register users. + #[cfg_attr(feature = "full", ts(optional))] pub oauth_registration: Option, /// What kind of post upvotes your site allows. + #[cfg_attr(feature = "full", ts(optional))] pub post_upvotes: Option, /// What kind of post downvotes your site allows. + #[cfg_attr(feature = "full", ts(optional))] pub post_downvotes: Option, /// What kind of comment upvotes your site allows. + #[cfg_attr(feature = "full", ts(optional))] pub comment_upvotes: Option, /// What kind of comment downvotes your site allows. + #[cfg_attr(feature = "full", ts(optional))] pub comment_downvotes: Option, } @@ -330,6 +448,7 @@ pub struct GetSiteResponse { pub site_view: SiteView, pub admins: Vec, pub version: String, + #[cfg_attr(feature = "full", ts(optional))] pub my_user: Option, pub all_languages: Vec, pub discussion_languages: Vec, @@ -338,9 +457,12 @@ pub struct GetSiteResponse { /// deprecated, use /api/v3/custom_emoji/list pub custom_emojis: Vec<()>, /// If the site has any taglines, a random one is included here for displaying + #[cfg_attr(feature = "full", ts(optional))] pub tagline: Option, /// A list of external auth methods your site supports. + #[cfg_attr(feature = "full", ts(optional))] pub oauth_providers: Option>, + #[cfg_attr(feature = "full", ts(optional))] pub admin_oauth_providers: Option>, pub blocked_urls: Vec, } @@ -352,6 +474,7 @@ pub struct GetSiteResponse { /// A response of federated instances. pub struct GetFederatedInstancesResponse { /// Optional, because federation may be disabled. + #[cfg_attr(feature = "full", ts(optional))] pub federated_instances: Option, } @@ -387,6 +510,7 @@ pub struct ReadableFederationState { #[serde(flatten)] internal_state: FederationQueueState, /// timestamp of the next retry attempt (null if fail count is 0) + #[cfg_attr(feature = "full", ts(optional))] next_retry: Option>, } @@ -411,6 +535,7 @@ pub struct InstanceWithFederationState { pub instance: Instance, /// if federation to this instance is or was active, show state of outgoing federation to this /// instance + #[cfg_attr(feature = "full", ts(optional))] pub federation_state: Option, } @@ -421,6 +546,7 @@ pub struct InstanceWithFederationState { /// Purges a person from the database. This will delete all content attached to that person. pub struct PurgePerson { pub person_id: PersonId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, } @@ -431,6 +557,7 @@ pub struct PurgePerson { /// Purges a community from the database. This will delete all content attached to that community. pub struct PurgeCommunity { pub community_id: CommunityId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, } @@ -441,6 +568,7 @@ pub struct PurgeCommunity { /// Purges a post from the database. This will delete all content attached to that post. pub struct PurgePost { pub post_id: PostId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, } @@ -451,6 +579,7 @@ pub struct PurgePost { /// Purges a comment from the database. This will delete all content attached to that comment. pub struct PurgeComment { pub comment_id: CommentId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, } @@ -461,8 +590,11 @@ pub struct PurgeComment { /// Fetches a list of registration applications. pub struct ListRegistrationApplications { /// Only shows the unread applications (IE those without an admin actor) + #[cfg_attr(feature = "full", ts(optional))] pub unread_only: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, } @@ -491,6 +623,7 @@ pub struct GetRegistrationApplication { pub struct ApproveRegistrationApplication { pub id: RegistrationApplicationId, pub approve: bool, + #[cfg_attr(feature = "full", ts(optional))] pub deny_reason: Option, } diff --git a/crates/api_common/src/tagline.rs b/crates/api_common/src/tagline.rs index 3090a2678..528d37947 100644 --- a/crates/api_common/src/tagline.rs +++ b/crates/api_common/src/tagline.rs @@ -50,6 +50,8 @@ pub struct ListTaglinesResponse { #[cfg_attr(feature = "full", ts(export))] /// Fetches a list of taglines. pub struct ListTaglines { + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, } diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index e358d483b..ddddd35e9 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -216,7 +216,9 @@ pub async fn check_registration_application( let local_user_id = local_user_view.local_user.id; let registration = RegistrationApplication::find_by_local_user_id(pool, local_user_id).await?; if registration.admin_id.is_some() { - Err(LemmyErrorType::RegistrationDenied(registration.deny_reason))? + Err(LemmyErrorType::RegistrationDenied { + reason: registration.deny_reason, + })? } else { Err(LemmyErrorType::RegistrationApplicationIsPending)? } diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index a04aec655..e11475d6c 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -54,15 +54,24 @@ impl UrlVerifier for VerifyUrlData { use FederationError::*; check_apub_id_valid(url, &local_site_data).map_err(|err| match err { LemmyError { - error_type: LemmyErrorType::FederationError(Some(FederationDisabled)), + error_type: + LemmyErrorType::FederationError { + error: Some(FederationDisabled), + }, .. } => ActivityPubError::Other("Federation disabled".into()), LemmyError { - error_type: LemmyErrorType::FederationError(Some(DomainBlocked(domain))), + error_type: + LemmyErrorType::FederationError { + error: Some(DomainBlocked(domain)), + }, .. } => ActivityPubError::Other(format!("Domain {domain:?} is blocked")), LemmyError { - error_type: LemmyErrorType::FederationError(Some(DomainNotInAllowList(domain))), + error_type: + LemmyErrorType::FederationError { + error: Some(DomainNotInAllowList(domain)), + }, .. } => ActivityPubError::Other(format!("Domain {domain:?} is not in allowlist")), _ => ActivityPubError::Other("Failed validating apub id".into()), diff --git a/crates/db_schema/src/newtypes.rs b/crates/db_schema/src/newtypes.rs index fe1febef5..c28be8222 100644 --- a/crates/db_schema/src/newtypes.rs +++ b/crates/db_schema/src/newtypes.rs @@ -174,8 +174,9 @@ pub struct LtreeDef(pub String); #[repr(transparent)] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Hash)] -#[cfg_attr(feature = "full", derive(AsExpression, FromSqlRow))] +#[cfg_attr(feature = "full", derive(AsExpression, FromSqlRow, TS))] #[cfg_attr(feature = "full", diesel(sql_type = diesel::sql_types::Text))] +#[cfg_attr(feature = "full", ts(export))] pub struct DbUrl(pub(crate) Box); impl DbUrl { @@ -248,19 +249,6 @@ impl Deref for DbUrl { } } -#[cfg(feature = "full")] -impl TS for DbUrl { - fn name() -> String { - "string".to_string() - } - fn dependencies() -> Vec { - Vec::new() - } - fn transparent() -> bool { - true - } -} - #[cfg(feature = "full")] impl ToSql for DbUrl { fn to_sql(&self, out: &mut Output) -> diesel::serialize::Result { diff --git a/crates/db_schema/src/sensitive.rs b/crates/db_schema/src/sensitive.rs index 340679e2f..5d1d449fb 100644 --- a/crates/db_schema/src/sensitive.rs +++ b/crates/db_schema/src/sensitive.rs @@ -4,8 +4,9 @@ use std::{fmt::Debug, ops::Deref}; use ts_rs::TS; #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize, Default)] -#[cfg_attr(feature = "full", derive(DieselNewType))] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] #[serde(transparent)] +#[cfg_attr(feature = "full", ts(export))] pub struct SensitiveString(String); impl SensitiveString { @@ -39,19 +40,3 @@ impl From for SensitiveString { SensitiveString(t) } } - -#[cfg(feature = "full")] -impl TS for SensitiveString { - fn name() -> String { - "string".to_string() - } - fn name_with_type_args(_args: Vec) -> String { - "string".to_string() - } - fn dependencies() -> Vec { - Vec::new() - } - fn transparent() -> bool { - true - } -} diff --git a/crates/db_schema/src/source/comment.rs b/crates/db_schema/src/source/comment.rs index 1e5f043f1..7e65638ed 100644 --- a/crates/db_schema/src/source/comment.rs +++ b/crates/db_schema/src/source/comment.rs @@ -30,6 +30,7 @@ pub struct Comment { /// Whether the comment has been removed. pub removed: bool, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, /// Whether the comment has been deleted by its creator. pub deleted: bool, diff --git a/crates/db_schema/src/source/comment_report.rs b/crates/db_schema/src/source/comment_report.rs index 73dadc945..a19b6925a 100644 --- a/crates/db_schema/src/source/comment_report.rs +++ b/crates/db_schema/src/source/comment_report.rs @@ -25,8 +25,10 @@ pub struct CommentReport { pub original_comment_text: String, pub reason: String, pub resolved: bool, + #[cfg_attr(feature = "full", ts(optional))] pub resolver_id: Option, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, } diff --git a/crates/db_schema/src/source/community.rs b/crates/db_schema/src/source/community.rs index 2eb6c143c..870a132f2 100644 --- a/crates/db_schema/src/source/community.rs +++ b/crates/db_schema/src/source/community.rs @@ -25,10 +25,12 @@ pub struct Community { /// A longer title, that can contain other characters, and doesn't have to be unique. pub title: String, /// A sidebar for the community in markdown. + #[cfg_attr(feature = "full", ts(optional))] pub sidebar: Option, /// Whether the community is removed by a mod. pub removed: bool, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, /// Whether the community has been deleted by its creator. pub deleted: bool, @@ -45,8 +47,10 @@ pub struct Community { #[serde(skip)] pub last_refreshed_at: DateTime, /// A URL for an icon. + #[cfg_attr(feature = "full", ts(optional))] pub icon: Option, /// A URL for a banner. + #[cfg_attr(feature = "full", ts(optional))] pub banner: Option, #[cfg_attr(feature = "full", ts(skip))] #[serde(skip)] @@ -67,6 +71,7 @@ pub struct Community { pub featured_url: Option, pub visibility: CommunityVisibility, /// A shorter, one-line description of the site. + #[cfg_attr(feature = "full", ts(optional))] pub description: Option, } diff --git a/crates/db_schema/src/source/custom_emoji.rs b/crates/db_schema/src/source/custom_emoji.rs index f5a92ea46..bb95cb7c8 100644 --- a/crates/db_schema/src/source/custom_emoji.rs +++ b/crates/db_schema/src/source/custom_emoji.rs @@ -21,6 +21,7 @@ pub struct CustomEmoji { pub alt_text: String, pub category: String, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, } diff --git a/crates/db_schema/src/source/federation_queue_state.rs b/crates/db_schema/src/source/federation_queue_state.rs index 134dfe452..27e464d1f 100644 --- a/crates/db_schema/src/source/federation_queue_state.rs +++ b/crates/db_schema/src/source/federation_queue_state.rs @@ -19,10 +19,13 @@ use ts_rs::TS; pub struct FederationQueueState { pub instance_id: InstanceId, /// the last successfully sent activity id + #[cfg_attr(feature = "full", ts(optional))] pub last_successful_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub last_successful_published_time: Option>, /// how many failed attempts have been made to send the next activity pub fail_count: i32, /// timestamp of the last retry attempt (when the last failing activity was resent) + #[cfg_attr(feature = "full", ts(optional))] pub last_retry: Option>, } diff --git a/crates/db_schema/src/source/images.rs b/crates/db_schema/src/source/images.rs index 22f5e6eb4..acd339d8e 100644 --- a/crates/db_schema/src/source/images.rs +++ b/crates/db_schema/src/source/images.rs @@ -23,6 +23,7 @@ use ts_rs::TS; #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", diesel(primary_key(pictrs_alias)))] pub struct LocalImage { + #[cfg_attr(feature = "full", ts(optional))] pub local_user_id: Option, pub pictrs_alias: String, pub pictrs_delete_token: String, diff --git a/crates/db_schema/src/source/instance.rs b/crates/db_schema/src/source/instance.rs index 8c27a2cb6..f622751cc 100644 --- a/crates/db_schema/src/source/instance.rs +++ b/crates/db_schema/src/source/instance.rs @@ -19,8 +19,11 @@ pub struct Instance { pub id: InstanceId, pub domain: String, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, + #[cfg_attr(feature = "full", ts(optional))] pub software: Option, + #[cfg_attr(feature = "full", ts(optional))] pub version: Option, } diff --git a/crates/db_schema/src/source/local_site.rs b/crates/db_schema/src/source/local_site.rs index 5fa57fe3b..b5bcebc58 100644 --- a/crates/db_schema/src/source/local_site.rs +++ b/crates/db_schema/src/source/local_site.rs @@ -33,6 +33,7 @@ pub struct LocalSite { /// Whether emails are required. pub require_email_verification: bool, /// An optional registration application questionnaire in markdown. + #[cfg_attr(feature = "full", ts(optional))] pub application_question: Option, /// Whether the instance is private or public. pub private_instance: bool, @@ -40,12 +41,14 @@ pub struct LocalSite { pub default_theme: String, pub default_post_listing_type: ListingType, /// An optional legal disclaimer page. + #[cfg_attr(feature = "full", ts(optional))] pub legal_information: Option, /// Whether to hide mod names on the modlog. pub hide_modlog_mod_names: bool, /// Whether new applications email admins. pub application_email_admins: bool, /// An optional regex to filter words. + #[cfg_attr(feature = "full", ts(optional))] pub slur_filter_regex: Option, /// The max actor name length. pub actor_name_max_length: i32, @@ -56,6 +59,7 @@ pub struct LocalSite { /// The captcha difficulty. pub captcha_difficulty: String, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, pub registration_mode: RegistrationMode, /// Whether to email admins on new reports. diff --git a/crates/db_schema/src/source/local_site_rate_limit.rs b/crates/db_schema/src/source/local_site_rate_limit.rs index f7f25f5c1..af424a248 100644 --- a/crates/db_schema/src/source/local_site_rate_limit.rs +++ b/crates/db_schema/src/source/local_site_rate_limit.rs @@ -34,6 +34,7 @@ pub struct LocalSiteRateLimit { pub search: i32, pub search_per_second: i32, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, pub import_user_settings: i32, pub import_user_settings_per_second: i32, diff --git a/crates/db_schema/src/source/local_site_url_blocklist.rs b/crates/db_schema/src/source/local_site_url_blocklist.rs index 4ac0893ec..d6127a78a 100644 --- a/crates/db_schema/src/source/local_site_url_blocklist.rs +++ b/crates/db_schema/src/source/local_site_url_blocklist.rs @@ -16,6 +16,7 @@ pub struct LocalSiteUrlBlocklist { pub id: i32, pub url: String, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, } diff --git a/crates/db_schema/src/source/local_user.rs b/crates/db_schema/src/source/local_user.rs index 37da70908..d5bbfbe19 100644 --- a/crates/db_schema/src/source/local_user.rs +++ b/crates/db_schema/src/source/local_user.rs @@ -27,6 +27,7 @@ pub struct LocalUser { pub person_id: PersonId, #[serde(skip)] pub password_encrypted: Option, + #[cfg_attr(feature = "full", ts(optional))] pub email: Option, /// Whether to show NSFW content. pub show_nsfw: bool, diff --git a/crates/db_schema/src/source/login_token.rs b/crates/db_schema/src/source/login_token.rs index 38aac33ef..20d81afb0 100644 --- a/crates/db_schema/src/source/login_token.rs +++ b/crates/db_schema/src/source/login_token.rs @@ -24,7 +24,9 @@ pub struct LoginToken { pub published: DateTime, /// IP address where login was made from, allows invalidating logins by IP address. /// Could be stored in truncated format, or store derived information for better privacy. + #[cfg_attr(feature = "full", ts(optional))] pub ip: Option, + #[cfg_attr(feature = "full", ts(optional))] pub user_agent: Option, } diff --git a/crates/db_schema/src/source/moderator.rs b/crates/db_schema/src/source/moderator.rs index c1f58ebc8..b4fdcc676 100644 --- a/crates/db_schema/src/source/moderator.rs +++ b/crates/db_schema/src/source/moderator.rs @@ -34,6 +34,7 @@ pub struct ModRemovePost { pub id: i32, pub mod_person_id: PersonId, pub post_id: PostId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub removed: bool, pub when_: DateTime, @@ -105,6 +106,7 @@ pub struct ModRemoveComment { pub id: i32, pub mod_person_id: PersonId, pub comment_id: CommentId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub removed: bool, pub when_: DateTime, @@ -130,6 +132,7 @@ pub struct ModRemoveCommunity { pub id: i32, pub mod_person_id: PersonId, pub community_id: CommunityId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub removed: bool, pub when_: DateTime, @@ -156,8 +159,10 @@ pub struct ModBanFromCommunity { pub mod_person_id: PersonId, pub other_person_id: PersonId, pub community_id: CommunityId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub banned: bool, + #[cfg_attr(feature = "full", ts(optional))] pub expires: Option>, pub when_: DateTime, } @@ -184,8 +189,10 @@ pub struct ModBan { pub id: i32, pub mod_person_id: PersonId, pub other_person_id: PersonId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub banned: bool, + #[cfg_attr(feature = "full", ts(optional))] pub expires: Option>, pub when_: DateTime, } @@ -211,6 +218,7 @@ pub struct ModHideCommunity { pub community_id: CommunityId, pub mod_person_id: PersonId, pub when_: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub hidden: bool, } @@ -303,6 +311,7 @@ pub struct ModAddForm { pub struct AdminPurgePerson { pub id: i32, pub admin_person_id: PersonId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub when_: DateTime, } @@ -324,6 +333,7 @@ pub struct AdminPurgePersonForm { pub struct AdminPurgeCommunity { pub id: i32, pub admin_person_id: PersonId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub when_: DateTime, } @@ -346,6 +356,7 @@ pub struct AdminPurgePost { pub id: i32, pub admin_person_id: PersonId, pub community_id: CommunityId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub when_: DateTime, } @@ -369,6 +380,7 @@ pub struct AdminPurgeComment { pub id: i32, pub admin_person_id: PersonId, pub post_id: PostId, + #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub when_: DateTime, } diff --git a/crates/db_schema/src/source/oauth_account.rs b/crates/db_schema/src/source/oauth_account.rs index 83b578e22..b7d190c35 100644 --- a/crates/db_schema/src/source/oauth_account.rs +++ b/crates/db_schema/src/source/oauth_account.rs @@ -19,6 +19,7 @@ pub struct OAuthAccount { pub oauth_provider_id: OAuthProviderId, pub oauth_user_id: String, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, } diff --git a/crates/db_schema/src/source/oauth_provider.rs b/crates/db_schema/src/source/oauth_provider.rs index 75b989805..a70405a5e 100644 --- a/crates/db_schema/src/source/oauth_provider.rs +++ b/crates/db_schema/src/source/oauth_provider.rs @@ -60,6 +60,7 @@ pub struct OAuthProvider { /// switch to enable or disable an oauth provider pub enabled: bool, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, } diff --git a/crates/db_schema/src/source/person.rs b/crates/db_schema/src/source/person.rs index c3aeeb4d7..d8b0a5b1a 100644 --- a/crates/db_schema/src/source/person.rs +++ b/crates/db_schema/src/source/person.rs @@ -22,16 +22,20 @@ pub struct Person { pub id: PersonId, pub name: String, /// A shorter display name. + #[cfg_attr(feature = "full", ts(optional))] pub display_name: Option, /// A URL for an avatar. + #[cfg_attr(feature = "full", ts(optional))] pub avatar: Option, /// Whether the person is banned. pub banned: bool, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, /// The federated actor_id. pub actor_id: DbUrl, /// An optional bio, in markdown. + #[cfg_attr(feature = "full", ts(optional))] pub bio: Option, /// Whether the person is local to our site. pub local: bool, @@ -42,6 +46,7 @@ pub struct Person { #[serde(skip)] pub last_refreshed_at: DateTime, /// A URL for a banner. + #[cfg_attr(feature = "full", ts(optional))] pub banner: Option, /// Whether the person is deleted. pub deleted: bool, @@ -49,10 +54,12 @@ pub struct Person { #[serde(skip, default = "placeholder_apub_url")] pub inbox_url: DbUrl, /// A matrix id, usually given an @person:matrix.org + #[cfg_attr(feature = "full", ts(optional))] pub matrix_user_id: Option, /// Whether the person is a bot account. pub bot_account: bool, /// When their ban, if it exists, expires, if at all. + #[cfg_attr(feature = "full", ts(optional))] pub ban_expires: Option>, pub instance_id: InstanceId, } diff --git a/crates/db_schema/src/source/post.rs b/crates/db_schema/src/source/post.rs index 3819bd773..3417f87b5 100644 --- a/crates/db_schema/src/source/post.rs +++ b/crates/db_schema/src/source/post.rs @@ -17,10 +17,11 @@ use ts_rs::TS; pub struct Post { pub id: PostId, pub name: String, - #[cfg_attr(feature = "full", ts(type = "string"))] /// An optional link / url for the post. + #[cfg_attr(feature = "full", ts(optional))] pub url: Option, /// An optional post body, in markdown. + #[cfg_attr(feature = "full", ts(optional))] pub body: Option, pub creator_id: PersonId, pub community_id: CommunityId, @@ -29,35 +30,40 @@ pub struct Post { /// Whether the post is locked. pub locked: bool, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, /// Whether the post is deleted. pub deleted: bool, /// Whether the post is NSFW. pub nsfw: bool, /// A title for the link. + #[cfg_attr(feature = "full", ts(optional))] pub embed_title: Option, /// A description for the link. + #[cfg_attr(feature = "full", ts(optional))] pub embed_description: Option, - #[cfg_attr(feature = "full", ts(type = "string"))] /// A thumbnail picture url. + #[cfg_attr(feature = "full", ts(optional))] pub thumbnail_url: Option, - #[cfg_attr(feature = "full", ts(type = "string"))] /// The federated activity id / ap_id. pub ap_id: DbUrl, /// Whether the post is local. pub local: bool, - #[cfg_attr(feature = "full", ts(type = "string"))] /// A video url for the link. + #[cfg_attr(feature = "full", ts(optional))] pub embed_video_url: Option, pub language_id: LanguageId, /// Whether the post is featured to its community. pub featured_community: bool, /// Whether the post is featured to its site. pub featured_local: bool, + #[cfg_attr(feature = "full", ts(optional))] pub url_content_type: Option, /// An optional alt_text, usable for image posts. + #[cfg_attr(feature = "full", ts(optional))] pub alt_text: Option, /// Time at which the post will be published. None means publish immediately. + #[cfg_attr(feature = "full", ts(optional))] pub scheduled_publish_time: Option>, } diff --git a/crates/db_schema/src/source/post_report.rs b/crates/db_schema/src/source/post_report.rs index 9aee9ed97..610e495ae 100644 --- a/crates/db_schema/src/source/post_report.rs +++ b/crates/db_schema/src/source/post_report.rs @@ -25,13 +25,17 @@ pub struct PostReport { /// The original post title. pub original_post_name: String, /// The original post url. + #[cfg_attr(feature = "full", ts(optional))] pub original_post_url: Option, /// The original post body. + #[cfg_attr(feature = "full", ts(optional))] pub original_post_body: Option, pub reason: String, pub resolved: bool, + #[cfg_attr(feature = "full", ts(optional))] pub resolver_id: Option, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, } diff --git a/crates/db_schema/src/source/private_message.rs b/crates/db_schema/src/source/private_message.rs index 8afaa14f1..f15373907 100644 --- a/crates/db_schema/src/source/private_message.rs +++ b/crates/db_schema/src/source/private_message.rs @@ -29,6 +29,7 @@ pub struct PrivateMessage { pub deleted: bool, pub read: bool, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, pub ap_id: DbUrl, pub local: bool, diff --git a/crates/db_schema/src/source/private_message_report.rs b/crates/db_schema/src/source/private_message_report.rs index 7b4c8c637..570f55584 100644 --- a/crates/db_schema/src/source/private_message_report.rs +++ b/crates/db_schema/src/source/private_message_report.rs @@ -29,8 +29,10 @@ pub struct PrivateMessageReport { pub original_pm_text: String, pub reason: String, pub resolved: bool, + #[cfg_attr(feature = "full", ts(optional))] pub resolver_id: Option, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, } diff --git a/crates/db_schema/src/source/registration_application.rs b/crates/db_schema/src/source/registration_application.rs index 2ac973f34..f01c042d9 100644 --- a/crates/db_schema/src/source/registration_application.rs +++ b/crates/db_schema/src/source/registration_application.rs @@ -18,7 +18,9 @@ pub struct RegistrationApplication { pub id: RegistrationApplicationId, pub local_user_id: LocalUserId, pub answer: String, + #[cfg_attr(feature = "full", ts(optional))] pub admin_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub deny_reason: Option, pub published: DateTime, } diff --git a/crates/db_schema/src/source/site.rs b/crates/db_schema/src/source/site.rs index 0ec4043e4..0fe33de01 100644 --- a/crates/db_schema/src/source/site.rs +++ b/crates/db_schema/src/source/site.rs @@ -21,14 +21,19 @@ pub struct Site { pub id: SiteId, pub name: String, /// A sidebar for the site in markdown. + #[cfg_attr(feature = "full", ts(optional))] pub sidebar: Option, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, /// An icon URL. + #[cfg_attr(feature = "full", ts(optional))] pub icon: Option, /// A banner url. + #[cfg_attr(feature = "full", ts(optional))] pub banner: Option, /// A shorter, one-line description of the site. + #[cfg_attr(feature = "full", ts(optional))] pub description: Option, /// The federated actor_id. pub actor_id: DbUrl, @@ -43,6 +48,7 @@ pub struct Site { pub instance_id: InstanceId, /// If present, nsfw content is visible by default. Should be displayed by frontends/clients /// when the site is first opened by a user. + #[cfg_attr(feature = "full", ts(optional))] pub content_warning: Option, } diff --git a/crates/db_schema/src/source/tagline.rs b/crates/db_schema/src/source/tagline.rs index 05f7e0520..80c045a0a 100644 --- a/crates/db_schema/src/source/tagline.rs +++ b/crates/db_schema/src/source/tagline.rs @@ -17,6 +17,7 @@ pub struct Tagline { pub id: i32, pub content: String, pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] pub updated: Option>, } diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs index 3c219d63f..4586fbcac 100644 --- a/crates/db_views/src/structs.rs +++ b/crates/db_views/src/structs.rs @@ -48,7 +48,9 @@ pub struct CommentReportView { pub creator_blocked: bool, pub subscribed: SubscribedType, pub saved: bool, + #[cfg_attr(feature = "full", ts(optional))] pub my_vote: Option, + #[cfg_attr(feature = "full", ts(optional))] pub resolver: Option, } @@ -71,6 +73,7 @@ pub struct CommentView { pub subscribed: SubscribedType, pub saved: bool, pub creator_blocked: bool, + #[cfg_attr(feature = "full", ts(optional))] pub my_vote: Option, } @@ -106,9 +109,11 @@ pub struct PostReportView { pub read: bool, pub hidden: bool, pub creator_blocked: bool, + #[cfg_attr(feature = "full", ts(optional))] pub my_vote: Option, pub unread_comments: i64, pub counts: PostAggregates, + #[cfg_attr(feature = "full", ts(optional))] pub resolver: Option, } @@ -131,6 +136,7 @@ pub struct PostView { pub post: Post, pub creator: Person, pub community: Community, + #[cfg_attr(feature = "full", ts(optional))] pub image_details: Option, pub creator_banned_from_community: bool, pub banned_from_community: bool, @@ -142,6 +148,7 @@ pub struct PostView { pub read: bool, pub hidden: bool, pub creator_blocked: bool, + #[cfg_attr(feature = "full", ts(optional))] pub my_vote: Option, pub unread_comments: i64, } @@ -168,6 +175,7 @@ pub struct PrivateMessageReportView { pub private_message: PrivateMessage, pub private_message_creator: Person, pub creator: Person, + #[cfg_attr(feature = "full", ts(optional))] pub resolver: Option, } @@ -181,6 +189,7 @@ pub struct RegistrationApplicationView { pub registration_application: RegistrationApplication, pub creator_local_user: LocalUser, pub creator: Person, + #[cfg_attr(feature = "full", ts(optional))] pub admin: Option, } diff --git a/crates/db_views_actor/src/structs.rs b/crates/db_views_actor/src/structs.rs index ecf9ba11d..db5cb1899 100644 --- a/crates/db_views_actor/src/structs.rs +++ b/crates/db_views_actor/src/structs.rs @@ -109,6 +109,7 @@ pub struct PersonMentionView { pub subscribed: SubscribedType, pub saved: bool, pub creator_blocked: bool, + #[cfg_attr(feature = "full", ts(optional))] pub my_vote: Option, } @@ -133,6 +134,7 @@ pub struct CommentReplyView { pub subscribed: SubscribedType, pub saved: bool, pub creator_blocked: bool, + #[cfg_attr(feature = "full", ts(optional))] pub my_vote: Option, } diff --git a/crates/db_views_moderator/src/structs.rs b/crates/db_views_moderator/src/structs.rs index 10ad78942..27ee82522 100644 --- a/crates/db_views_moderator/src/structs.rs +++ b/crates/db_views_moderator/src/structs.rs @@ -39,6 +39,7 @@ use ts_rs::TS; /// When someone is added as a community moderator. pub struct ModAddCommunityView { pub mod_add_community: ModAddCommunity, + #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub community: Community, pub modded_person: Person, @@ -52,6 +53,7 @@ pub struct ModAddCommunityView { /// When someone is added as a site moderator. pub struct ModAddView { pub mod_add: ModAdd, + #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub modded_person: Person, } @@ -64,6 +66,7 @@ pub struct ModAddView { /// When someone is banned from a community. pub struct ModBanFromCommunityView { pub mod_ban_from_community: ModBanFromCommunity, + #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub community: Community, pub banned_person: Person, @@ -77,6 +80,7 @@ pub struct ModBanFromCommunityView { /// When someone is banned from the site. pub struct ModBanView { pub mod_ban: ModBan, + #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub banned_person: Person, } @@ -89,6 +93,7 @@ pub struct ModBanView { /// When a community is hidden from public view. pub struct ModHideCommunityView { pub mod_hide_community: ModHideCommunity, + #[cfg_attr(feature = "full", ts(optional))] pub admin: Option, pub community: Community, } @@ -101,6 +106,7 @@ pub struct ModHideCommunityView { /// When a moderator locks a post (prevents new comments being made). pub struct ModLockPostView { pub mod_lock_post: ModLockPost, + #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub post: Post, pub community: Community, @@ -114,6 +120,7 @@ pub struct ModLockPostView { /// When a moderator removes a comment. pub struct ModRemoveCommentView { pub mod_remove_comment: ModRemoveComment, + #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub comment: Comment, pub commenter: Person, @@ -129,6 +136,7 @@ pub struct ModRemoveCommentView { /// When a moderator removes a community. pub struct ModRemoveCommunityView { pub mod_remove_community: ModRemoveCommunity, + #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub community: Community, } @@ -141,6 +149,7 @@ pub struct ModRemoveCommunityView { /// When a moderator removes a post. pub struct ModRemovePostView { pub mod_remove_post: ModRemovePost, + #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub post: Post, pub community: Community, @@ -154,6 +163,7 @@ pub struct ModRemovePostView { /// When a moderator features a post on a community (pins it to the top). pub struct ModFeaturePostView { pub mod_feature_post: ModFeaturePost, + #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub post: Post, pub community: Community, @@ -167,6 +177,7 @@ pub struct ModFeaturePostView { /// When a moderator transfers a community to a new owner. pub struct ModTransferCommunityView { pub mod_transfer_community: ModTransferCommunity, + #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub community: Community, pub modded_person: Person, @@ -180,6 +191,7 @@ pub struct ModTransferCommunityView { /// When an admin purges a comment. pub struct AdminPurgeCommentView { pub admin_purge_comment: AdminPurgeComment, + #[cfg_attr(feature = "full", ts(optional))] pub admin: Option, pub post: Post, } @@ -192,6 +204,7 @@ pub struct AdminPurgeCommentView { /// When an admin purges a community. pub struct AdminPurgeCommunityView { pub admin_purge_community: AdminPurgeCommunity, + #[cfg_attr(feature = "full", ts(optional))] pub admin: Option, } @@ -203,6 +216,7 @@ pub struct AdminPurgeCommunityView { /// When an admin purges a person. pub struct AdminPurgePersonView { pub admin_purge_person: AdminPurgePerson, + #[cfg_attr(feature = "full", ts(optional))] pub admin: Option, } @@ -214,6 +228,7 @@ pub struct AdminPurgePersonView { /// When an admin purges a post. pub struct AdminPurgePostView { pub admin_purge_post: AdminPurgePost, + #[cfg_attr(feature = "full", ts(optional))] pub admin: Option, pub community: Community, } @@ -225,12 +240,19 @@ pub struct AdminPurgePostView { #[cfg_attr(feature = "full", ts(export))] /// Querying / filtering the modlog. pub struct ModlogListParams { + #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub mod_person_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub other_person_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub post_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub comment_id: Option, + #[cfg_attr(feature = "full", ts(optional))] pub page: Option, + #[cfg_attr(feature = "full", ts(optional))] pub limit: Option, pub hide_modlog_names: bool, } diff --git a/crates/utils/src/error.rs b/crates/utils/src/error.rs index c95af03e2..75cecc41f 100644 --- a/crates/utils/src/error.rs +++ b/crates/utils/src/error.rs @@ -119,7 +119,10 @@ pub enum LemmyErrorType { InvalidUrl, EmailSendFailed, Slurs, - RegistrationDenied(Option), + RegistrationDenied { + #[cfg_attr(feature = "full", ts(optional))] + reason: Option, + }, SiteNameRequired, SiteNameLengthOverflow, PermissiveRegex, @@ -147,7 +150,10 @@ pub enum LemmyErrorType { CommunityHasNoFollowers, PostScheduleTimeMustBeInFuture, TooManyScheduledPosts, - FederationError(Option), + FederationError { + #[cfg_attr(feature = "full", ts(optional))] + error: Option, + }, } /// Federation related errors, these dont need to be translated. @@ -262,7 +268,7 @@ cfg_if! { fn from(error_type: FederationError) -> Self { let inner = anyhow::anyhow!("{}", error_type); LemmyError { - error_type: LemmyErrorType::FederationError(Some(error_type)), + error_type: LemmyErrorType::FederationError { error: Some(error_type) }, inner, context: Backtrace::capture(), } diff --git a/scripts/ts_bindings_check.sh b/scripts/ts_bindings_check.sh new file mode 100755 index 000000000..a925081c8 --- /dev/null +++ b/scripts/ts_bindings_check.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -e + +# This check is only used for CI. + +CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" + +cd "$CWD/../" + +# Export the ts-rs bindings +cargo test --workspace export_bindings + +# Make sure no rows are returned +! grep -nr --include=\*.ts ' | null' ./crates/ From a55e7fd9fe3c42cdaa3aeb423b1707b393cea2a8 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 6 Nov 2024 10:20:30 -0500 Subject: [PATCH 18/19] Trying to use w3.org as a sample site to fix tests. (#5170) - #5167 --- api_tests/src/shared.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index 017dad903..20cb171c5 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -83,7 +83,7 @@ export const fetchFunction = fetch; export const imageFetchLimit = 50; export const sampleImage = "https://i.pinimg.com/originals/df/5f/5b/df5f5b1b174a2b4b6026cc6c8f9395c1.jpg"; -export const sampleSite = "https://google.com"; +export const sampleSite = "https://w3.org"; export const alphaUrl = "http://127.0.0.1:8541"; export const betaUrl = "http://127.0.0.1:8551"; From 917e408735cd9347096e1d6f1e5a2bcfd3cf745f Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 6 Nov 2024 10:58:40 -0500 Subject: [PATCH 19/19] Fix postgres connection options causing slow query speed. (#5150) * Adding a query speed check. * Fixing slow queries due to connection config options. * Remove pointless set_config sql function. * Removing pointless bool. * Removing comment * Removing test.sh changes. * Add analyze to speed up query * Trying to fix DB perf connection try #1 * Try encoding option * Fix woodpecker * Try to use path character. * Fixing lemmy config location. * Removing pointless connection options. * Use OnceLock to create a once-init psql connection. * Fixing comment. * Fix host encoding for dev DB. * Address PR comments. * Revert query mut change. --- .woodpecker.yml | 4 +- crates/db_schema/src/utils.rs | 56 ++++++++++++++++------------ crates/db_views/src/post_view.rs | 64 +++++++++++++++++++++++++++++++- scripts/start_dev_db.sh | 6 ++- 4 files changed, 100 insertions(+), 30 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 16cd49375..8930c21fc 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -122,7 +122,6 @@ steps: environment: CARGO_HOME: .cargo_home commands: - - export LEMMY_CONFIG_LOCATION=./config/config.hjson - ./scripts/update_config_defaults.sh config/defaults_current.hjson - diff config/defaults.hjson config/defaults_current.hjson when: *slow_check_paths @@ -147,7 +146,6 @@ steps: CARGO_HOME: .cargo_home commands: # same as scripts/db_perf.sh but without creating a new database server - - export LEMMY_CONFIG_LOCATION=config/config.hjson - cargo run --package lemmy_db_perf -- --posts 10 --read-post-pages 1 when: *slow_check_paths @@ -176,8 +174,8 @@ steps: RUST_BACKTRACE: "1" CARGO_HOME: .cargo_home LEMMY_TEST_FAST_FEDERATION: "1" + LEMMY_CONFIG_LOCATION: ../../config/config.hjson commands: - - export LEMMY_CONFIG_LOCATION=../../config/config.hjson - cargo test --workspace --no-fail-fast when: *slow_check_paths diff --git a/crates/db_schema/src/utils.rs b/crates/db_schema/src/utils.rs index 1e56563bc..6c5b792eb 100644 --- a/crates/db_schema/src/utils.rs +++ b/crates/db_schema/src/utils.rs @@ -22,7 +22,6 @@ use diesel_async::{ ManagerConfig, }, AsyncConnection, - RunQueryDsl, }; use futures_util::{future::BoxFuture, Future, FutureExt}; use i_love_jesus::CursorKey; @@ -47,7 +46,7 @@ use rustls::{ }; use std::{ ops::{Deref, DerefMut}, - sync::{Arc, LazyLock}, + sync::{Arc, LazyLock, OnceLock}, time::Duration, }; use tracing::error; @@ -59,6 +58,8 @@ pub const SITEMAP_LIMIT: i64 = 50000; pub const SITEMAP_DAYS: Option = TimeDelta::try_days(31); pub const RANK_DEFAULT: f64 = 0.0001; +/// Some connection options to speed up queries +const CONNECTION_OPTIONS: [&str; 1] = ["geqo_threshold=12"]; pub type ActualDbPool = Pool; /// References a pool or connection. Functions must take `&mut DbPool<'_>` to allow implicit @@ -345,10 +346,37 @@ pub fn diesel_url_create(opt: Option<&str>) -> LemmyResult> { } } +/// Sets a few additional config options necessary for starting lemmy +fn build_config_options_uri_segment(config: &str) -> String { + let mut url = Url::parse(config).expect("Couldn't parse postgres connection URI"); + + // Set `lemmy.protocol_and_hostname` so triggers can use it + let lemmy_protocol_and_hostname_option = + "lemmy.protocol_and_hostname=".to_owned() + &SETTINGS.get_protocol_and_hostname(); + let mut options = CONNECTION_OPTIONS.to_vec(); + options.push(&lemmy_protocol_and_hostname_option); + + // Create the connection uri portion + let options_segments = options + .iter() + .map(|o| "-c ".to_owned() + o) + .collect::>() + .join(" "); + + url.set_query(Some(&format!("options={options_segments}"))); + url.into() +} + fn establish_connection(config: &str) -> BoxFuture> { let fut = async { + /// Use a once_lock to create the postgres connection config, since this config never changes + static POSTGRES_CONFIG_WITH_OPTIONS: OnceLock = OnceLock::new(); + + let config = + POSTGRES_CONFIG_WITH_OPTIONS.get_or_init(|| build_config_options_uri_segment(config)); + // We only support TLS with sslmode=require currently - let mut conn = if config.contains("sslmode=require") { + let conn = if config.contains("sslmode=require") { let rustls_config = DangerousClientConfigBuilder { cfg: ClientConfig::builder(), } @@ -369,24 +397,6 @@ fn establish_connection(config: &str) -> BoxFuture = LazyLock::new(|| { }); pub mod functions { - use diesel::sql_types::{BigInt, Bool, Text, Timestamptz}; + use diesel::sql_types::{BigInt, Text, Timestamptz}; sql_function! { #[sql_name = "r.hot_rank"] @@ -521,8 +531,6 @@ pub mod functions { // really this function is variadic, this just adds the two-argument version sql_function!(fn coalesce(x: diesel::sql_types::Nullable, y: T) -> T); - - sql_function!(fn set_config(setting_name: Text, new_value: Text, is_local: Bool) -> Text); } pub const DELETED_REPLACEMENT_TEXT: &str = "*Permanently Deleted*"; diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index 13520f1cf..dc00b0438 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -734,6 +734,7 @@ mod tests { structs::LocalUserView, }; use chrono::Utc; + use diesel_async::SimpleAsyncConnection; use lemmy_db_schema::{ aggregates::structs::PostAggregates, impls::actor_language::UNDETERMINED_ID, @@ -774,7 +775,7 @@ mod tests { site::Site, }, traits::{Bannable, Blockable, Crud, Followable, Joinable, Likeable, Saveable}, - utils::{build_db_pool, build_db_pool_for_tests, DbPool, RANK_DEFAULT}, + utils::{build_db_pool, build_db_pool_for_tests, get_conn, DbPool, RANK_DEFAULT}, CommunityVisibility, PostSortType, SubscribedType, @@ -782,7 +783,10 @@ mod tests { use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; - use std::{collections::HashSet, time::Duration}; + use std::{ + collections::HashSet, + time::{Duration, Instant}, + }; use url::Url; const POST_WITH_ANOTHER_TITLE: &str = "Another title"; @@ -1995,6 +1999,62 @@ mod tests { cleanup(data, pool).await } + #[tokio::test] + #[serial] + async fn speed_check() -> LemmyResult<()> { + let pool = &build_db_pool().await?; + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // Make sure the post_view query is less than this time + let duration_max = Duration::from_millis(40); + + // Create some dummy posts + let num_posts = 1000; + for x in 1..num_posts { + let name = format!("post_{x}"); + let url = Some(Url::parse(&format!("https://google.com/{name}"))?.into()); + + let post_form = PostInsertForm { + url, + ..PostInsertForm::new( + name, + data.local_user_view.person.id, + data.inserted_community.id, + ) + }; + Post::create(pool, &post_form).await?; + } + + // Manually trigger and wait for a statistics update to ensure consistent and high amount of + // accuracy in the statistics used for query planning + println!("🧮 updating database statistics"); + let conn = &mut get_conn(pool).await?; + conn.batch_execute("ANALYZE;").await?; + + // Time how fast the query took + let now = Instant::now(); + PostQuery { + sort: Some(PostSortType::Active), + local_user: Some(&data.local_user_view.local_user), + ..Default::default() + } + .list(&data.site, pool) + .await?; + + let elapsed = now.elapsed(); + println!("Elapsed: {:.0?}", elapsed); + + assert!( + elapsed.lt(&duration_max), + "Query took {:.0?}, longer than the max of {:.0?}", + elapsed, + duration_max + ); + + cleanup(data, pool).await + } + #[tokio::test] #[serial] async fn post_listings_no_comments_only() -> LemmyResult<()> { diff --git a/scripts/start_dev_db.sh b/scripts/start_dev_db.sh index 5965316ba..1cbe9e16a 100644 --- a/scripts/start_dev_db.sh +++ b/scripts/start_dev_db.sh @@ -2,8 +2,12 @@ export PGDATA="$PWD/dev_pgdata" export PGHOST=$PWD + +# Necessary to encode the dev db path into proper URL params +export ENCODED_HOST=$(printf $PWD | jq -sRr @uri) + export PGUSER=postgres -export DATABASE_URL="postgresql://lemmy:password@/lemmy?host=$PWD" +export DATABASE_URL="postgresql://lemmy:password@$ENCODED_HOST/lemmy" export LEMMY_DATABASE_URL=$DATABASE_URL export PGDATABASE=lemmy