From 9c1347c7a0545be03607f9cc5375b9fd2ae2bc92 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 14 Jan 2025 09:14:58 -0500 Subject: [PATCH] Adding Combined modlog (#5253) * Combined tables try 2 * Finishing up combined report table. * Fix ts optionals. * Adding tests, triggers, and history updates for report_combined. * Adding profile. * Add cursor pagination to report_combined view (#5244) * add pagination cursor * store timestamp instead of id in cursor (partial) * Revert "store timestamp instead of id in cursor (partial)" This reverts commit 89359dde4bc5fee39fdd2840828330f398444a36. * use paginated query builder * Fixing migration and paged API. * Using dullbananas trigger procedure * Removing pointless list routes, reorganizing tests. * Fixing column XOR check. * Forgot to remove list report actions. * Cleanup. * Use internal tagging. * Fixing api tests. * Adding a few indexes. * Fixing migration name. * Fixing unique constraints. * Addressing PR comments. * Start working on profile combined * Adding views and replaceable schema. * A few changes to profile view. - Separating the profile fetch from its combined content fetch. - Starting to separate saved_only into its own combined view. * Finishing up combined person_saved and person_content. * Fixing api tests. * Moving to api-v4 routes. * Fixing imports. * Update crates/db_views/src/report_combined_view.rs Co-authored-by: dullbananas * Update crates/db_views/src/report_combined_view.rs Co-authored-by: dullbananas * Update crates/db_views/src/report_combined_view.rs Co-authored-by: dullbananas * Update migrations/2024-12-02-181601_add_report_combined_table/up.sql Co-authored-by: dullbananas * Update migrations/2024-12-02-181601_add_report_combined_table/up.sql Co-authored-by: dullbananas * Fixing import and fmt. * Fixing null types in postgres. * Comment out err. * Fixing TS issues. * Adding types, fixing allow and blocklist crud. * Starting to work on combined views. * Using dullbananas trigger procedure * Adding the full combined view queries. * Adding tests. * taplo fmt. * Upgrading package.json deps. * Updating pnpm * Addressing PR comments. * Removing serialization * Removing serialization * Fixing duped trigger. * Remove saved_only test. * Remove pointless post_tags types. * Remove pointless index. * Changing published to saved for person_saved_combined. * Removing comment. * Renaming modlog when_ columns to published. - Fixes #5312 * Adding strum and simplifying imports. * Avoiding clone in map_to_enum * Changing modded_person to other_person. * Update crates/db_views_moderator/src/modlog_combined_view.rs Co-authored-by: dullbananas * Update crates/db_views_moderator/src/modlog_combined_view.rs Co-authored-by: dullbananas * Update crates/db_views_moderator/src/modlog_combined_view.rs Co-authored-by: dullbananas * Addressing PR comments. * Fixing split. * Revert "Adding strum and simplifying imports." This reverts commit 15f167110721429dd6e465f522250c8beb3d4dd7. * Running fmt. * Using assert + matches instead of filter_map. * Adding listPersonContent check. --------- Co-authored-by: dullbananas --- Cargo.lock | 5 + api_tests/package.json | 18 +- api_tests/pnpm-lock.yaml | 410 ++--- api_tests/src/comment.spec.ts | 8 +- api_tests/src/shared.ts | 18 +- api_tests/src/user.spec.ts | 1 - crates/api/src/post/feature.rs | 4 +- crates/api/src/site/admin_allow_instance.rs | 13 +- crates/api/src/site/admin_block_instance.rs | 14 +- crates/api/src/site/mod_log.rs | 179 +-- crates/api_common/src/site.rs | 49 +- crates/api_common/src/utils.rs | 184 ++- .../db_schema/replaceable_schema/triggers.sql | 75 + .../src/impls/federation_allowlist.rs | 34 +- .../src/impls/federation_blocklist.rs | 34 +- crates/db_schema/src/impls/mod_log/admin.rs | 114 +- .../db_schema/src/impls/mod_log/moderator.rs | 212 +-- crates/db_schema/src/lib.rs | 7 + crates/db_schema/src/newtypes.rs | 89 ++ crates/db_schema/src/schema.rs | 76 +- crates/db_schema/src/source/combined/mod.rs | 1 + .../db_schema/src/source/combined/modlog.rs | 57 + crates/db_schema/src/source/mod_log/admin.rs | 38 +- .../db_schema/src/source/mod_log/moderator.rs | 66 +- crates/db_views/src/lib.rs | 7 - .../src/person_content_combined_view.rs | 27 +- .../src/person_saved_combined_view.rs | 19 +- crates/db_views/src/report_combined_view.rs | 31 +- crates/db_views/src/structs.rs | 8 +- crates/db_views_moderator/Cargo.toml | 16 +- .../src/admin_allow_instance.rs | 52 - .../src/admin_block_instance.rs | 52 - .../src/admin_purge_comment_view.rs | 57 - .../src/admin_purge_community_view.rs | 55 - .../src/admin_purge_person_view.rs | 55 - .../src/admin_purge_post_view.rs | 57 - crates/db_views_moderator/src/lib.rs | 34 +- .../src/mod_add_community_view.rs | 69 - crates/db_views_moderator/src/mod_add_view.rs | 61 - .../src/mod_ban_from_community_view.rs | 71 - crates/db_views_moderator/src/mod_ban_view.rs | 61 - .../src/mod_feature_post_view.rs | 72 - .../src/mod_hide_community_view.rs | 62 - .../src/mod_lock_post_view.rs | 73 - .../src/mod_remove_comment_view.rs | 75 - .../src/mod_remove_community_view.rs | 56 - .../src/mod_remove_post_view.rs | 73 - .../src/mod_transfer_community_view.rs | 71 - .../src/modlog_combined_view.rs | 1388 +++++++++++++++++ crates/db_views_moderator/src/structs.rs | 191 ++- .../down.sql | 37 + .../up.sql | 201 +++ 52 files changed, 2774 insertions(+), 1963 deletions(-) create mode 100644 crates/db_schema/src/source/combined/modlog.rs delete mode 100644 crates/db_views_moderator/src/admin_allow_instance.rs delete mode 100644 crates/db_views_moderator/src/admin_block_instance.rs delete mode 100644 crates/db_views_moderator/src/admin_purge_comment_view.rs delete mode 100644 crates/db_views_moderator/src/admin_purge_community_view.rs delete mode 100644 crates/db_views_moderator/src/admin_purge_person_view.rs delete mode 100644 crates/db_views_moderator/src/admin_purge_post_view.rs delete mode 100644 crates/db_views_moderator/src/mod_add_community_view.rs delete mode 100644 crates/db_views_moderator/src/mod_add_view.rs delete mode 100644 crates/db_views_moderator/src/mod_ban_from_community_view.rs delete mode 100644 crates/db_views_moderator/src/mod_ban_view.rs delete mode 100644 crates/db_views_moderator/src/mod_feature_post_view.rs delete mode 100644 crates/db_views_moderator/src/mod_hide_community_view.rs delete mode 100644 crates/db_views_moderator/src/mod_lock_post_view.rs delete mode 100644 crates/db_views_moderator/src/mod_remove_comment_view.rs delete mode 100644 crates/db_views_moderator/src/mod_remove_community_view.rs delete mode 100644 crates/db_views_moderator/src/mod_remove_post_view.rs delete mode 100644 crates/db_views_moderator/src/mod_transfer_community_view.rs create mode 100644 crates/db_views_moderator/src/modlog_combined_view.rs create mode 100644 migrations/2024-12-08-165614_add_modlog_combined_table/down.sql create mode 100644 migrations/2024-12-08-165614_add_modlog_combined_table/up.sql diff --git a/Cargo.lock b/Cargo.lock index 7afc8ec0d..3e37c1d39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2735,9 +2735,14 @@ version = "0.19.6-beta.7" dependencies = [ "diesel", "diesel-async", + "i-love-jesus", "lemmy_db_schema", + "lemmy_utils", + "pretty_assertions", "serde", "serde_with", + "serial_test", + "tokio", "ts-rs", ] diff --git a/api_tests/package.json b/api_tests/package.json index 39deef22f..22721d3fd 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.3", + "packageManager": "pnpm@9.15.0", "scripts": { "lint": "tsc --noEmit && eslint --report-unused-disable-directives && prettier --check 'src/**/*.ts'", "fix": "prettier --write src && eslint --fix src", @@ -22,16 +22,16 @@ }, "devDependencies": { "@types/jest": "^29.5.12", - "@types/node": "^22.9.0", - "@typescript-eslint/eslint-plugin": "^8.13.0", - "@typescript-eslint/parser": "^8.13.0", - "eslint": "^9.14.0", + "@types/node": "^22.10.1", + "@typescript-eslint/eslint-plugin": "^8.18.0", + "@typescript-eslint/parser": "^8.18.0", + "eslint": "^9.16.0", "eslint-plugin-prettier": "^5.1.3", "jest": "^29.5.0", - "lemmy-js-client": "0.20.0-image-api-rework.8", - "prettier": "^3.2.5", + "lemmy-js-client": "0.20.0-modlog-combined.0", + "prettier": "^3.4.2", "ts-jest": "^29.1.0", - "typescript": "^5.5.4", - "typescript-eslint": "^8.13.0" + "typescript": "^5.7.2", + "typescript-eslint": "^8.18.0" } } diff --git a/api_tests/pnpm-lock.yaml b/api_tests/pnpm-lock.yaml index 5aaa59d2c..4e32da73f 100644 --- a/api_tests/pnpm-lock.yaml +++ b/api_tests/pnpm-lock.yaml @@ -12,38 +12,38 @@ importers: specifier: ^29.5.12 version: 29.5.14 '@types/node': - specifier: ^22.9.0 - version: 22.9.0 + specifier: ^22.10.1 + version: 22.10.1 '@typescript-eslint/eslint-plugin': - specifier: ^8.13.0 - version: 8.13.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0)(typescript@5.6.3))(eslint@9.14.0)(typescript@5.6.3) + specifier: ^8.18.0 + version: 8.18.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0)(typescript@5.7.2) '@typescript-eslint/parser': - specifier: ^8.13.0 - version: 8.13.0(eslint@9.14.0)(typescript@5.6.3) + specifier: ^8.18.0 + version: 8.18.0(eslint@9.16.0)(typescript@5.7.2) eslint: - specifier: ^9.14.0 - version: 9.14.0 + specifier: ^9.16.0 + version: 9.16.0 eslint-plugin-prettier: specifier: ^5.1.3 - version: 5.2.1(eslint@9.14.0)(prettier@3.3.3) + version: 5.2.1(eslint@9.16.0)(prettier@3.4.2) jest: specifier: ^29.5.0 - version: 29.7.0(@types/node@22.9.0) + version: 29.7.0(@types/node@22.10.1) lemmy-js-client: - specifier: 0.20.0-image-api-rework.8 - version: 0.20.0-image-api-rework.8 + specifier: 0.20.0-modlog-combined.0 + version: 0.20.0-modlog-combined.0 prettier: - specifier: ^3.2.5 - version: 3.3.3 + specifier: ^3.4.2 + version: 3.4.2 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.9.0))(typescript@5.6.3) + 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.10.1))(typescript@5.7.2) typescript: - specifier: ^5.5.4 - version: 5.6.3 + specifier: ^5.7.2 + version: 5.7.2 typescript-eslint: - specifier: ^8.13.0 - version: 8.13.0(eslint@9.14.0)(typescript@5.6.3) + specifier: ^8.18.0 + version: 8.18.0(eslint@9.16.0)(typescript@5.7.2) packages: @@ -228,28 +228,28 @@ packages: 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==} + '@eslint/config-array@0.19.1': + resolution: {integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.7.0': - resolution: {integrity: sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==} + '@eslint/core@0.9.1': + resolution: {integrity: sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.1.0': - resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + '@eslint/eslintrc@3.2.0': + resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.14.0': - resolution: {integrity: sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==} + '@eslint/js@9.16.0': + resolution: {integrity: sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.4': - resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} + '@eslint/object-schema@2.1.5': + resolution: {integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.2': - resolution: {integrity: sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==} + '@eslint/plugin-kit@0.2.4': + resolution: {integrity: sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanfs/core@0.19.1': @@ -422,8 +422,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@22.9.0': - resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==} + '@types/node@22.10.1': + resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==} '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -434,61 +434,51 @@ packages: '@types/yargs@17.0.32': resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} - '@typescript-eslint/eslint-plugin@8.13.0': - resolution: {integrity: sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==} + '@typescript-eslint/eslint-plugin@8.18.0': + resolution: {integrity: sha512-NR2yS7qUqCL7AIxdJUQf2MKKNDVNaig/dEB0GBLU7D+ZdHgK1NoH/3wsgO3OnPVipn51tG3MAwaODEGil70WEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/parser@8.13.0': - resolution: {integrity: sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==} + '@typescript-eslint/parser@8.18.0': + resolution: {integrity: sha512-hgUZ3kTEpVzKaK3uNibExUYm6SKKOmTU2BOxBSvOYwtJEPdVQ70kZJpPjstlnhCHcuc2WGfSbpKlb/69ttyN5Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/scope-manager@8.13.0': - resolution: {integrity: sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==} + '@typescript-eslint/scope-manager@8.18.0': + resolution: {integrity: sha512-PNGcHop0jkK2WVYGotk/hxj+UFLhXtGPiGtiaWgVBVP1jhMoMCHlTyJA+hEj4rszoSdLTK3fN4oOatrL0Cp+Xw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.13.0': - resolution: {integrity: sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/types@8.13.0': - resolution: {integrity: sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.13.0': - resolution: {integrity: sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/utils@8.13.0': - resolution: {integrity: sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==} + '@typescript-eslint/type-utils@8.18.0': + resolution: {integrity: sha512-er224jRepVAVLnMF2Q7MZJCq5CsdH2oqjP4dT7K6ij09Kyd+R21r7UVJrF0buMVdZS5QRhDzpvzAxHxabQadow==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/visitor-keys@8.13.0': - resolution: {integrity: sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==} + '@typescript-eslint/types@8.18.0': + resolution: {integrity: sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.18.0': + resolution: {integrity: sha512-rqQgFRu6yPkauz+ms3nQpohwejS8bvgbPyIDq13cgEDbkXt4LH4OkDMT0/fN1RUtzG8e8AKJyDBoocuQh8qNeg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/utils@8.18.0': + resolution: {integrity: sha512-p6GLdY383i7h5b0Qrfbix3Vc3+J2k6QWw6UMUeY5JGfm3C5LbZ4QIZzJNoNOfgyRe0uuYKjvVOsO/jD4SJO+xg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/visitor-keys@8.18.0': + resolution: {integrity: sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} acorn-jsx@5.3.2: @@ -666,6 +656,15 @@ packages: supports-color: optional: true + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dedent@1.5.1: resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} peerDependencies: @@ -745,8 +744,8 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.14.0: - resolution: {integrity: sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==} + eslint@9.16.0: + resolution: {integrity: sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1167,8 +1166,8 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - lemmy-js-client@0.20.0-image-api-rework.8: - resolution: {integrity: sha512-Ns/ayfCSm2lHbdAU1tGIZSx6kJ2ZeS7UiXlPuH0IzHQSi8Yuyzj3srDCyHpE6Td3pmXbQlt9N1ziPE4KeRJ3CA==} + lemmy-js-client@0.20.0-modlog-combined.0: + resolution: {integrity: sha512-lb3na39klOSE184hJJObMufKjHtm3Mrk42RHqyVNCYZQ+FAAbQzBFTuYyqv8QJV5TJlMmyFO2v1v/9cH72nLRg==} leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} @@ -1335,8 +1334,8 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - prettier@3.3.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + prettier@3.4.2: + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} engines: {node: '>=14'} hasBin: true @@ -1482,9 +1481,6 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -1502,6 +1498,12 @@ packages: peerDependencies: typescript: '>=4.2.0' + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + ts-jest@29.2.5: resolution: {integrity: sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} @@ -1541,22 +1543,20 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - typescript-eslint@8.13.0: - resolution: {integrity: sha512-vIMpDRJrQd70au2G8w34mPps0ezFSPMEX4pXkTzUkrNbRX+36ais2ksGWN0esZL+ZMaFJEneOBHzCgSqle7DHw==} + typescript-eslint@8.18.0: + resolution: {integrity: sha512-Xq2rRjn6tzVpAyHr3+nmSg1/9k9aIHnJ2iZeOH7cfGOWqTkXTm3kwpQglEuLGdNrYvPF+2gtAs+/KF5rjVo+WQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' - typescript@5.6.3: - resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + typescript@5.7.2: + resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} update-browserslist-db@1.0.13: resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} @@ -1816,24 +1816,26 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@eslint-community/eslint-utils@4.4.1(eslint@9.14.0)': + '@eslint-community/eslint-utils@4.4.1(eslint@9.16.0)': dependencies: - eslint: 9.14.0 + eslint: 9.16.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.18.0': + '@eslint/config-array@0.19.1': dependencies: - '@eslint/object-schema': 2.1.4 + '@eslint/object-schema': 2.1.5 debug: 4.3.7 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/core@0.7.0': {} + '@eslint/core@0.9.1': + dependencies: + '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.1.0': + '@eslint/eslintrc@3.2.0': dependencies: ajv: 6.12.6 debug: 4.3.7 @@ -1847,11 +1849,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.14.0': {} + '@eslint/js@9.16.0': {} - '@eslint/object-schema@2.1.4': {} + '@eslint/object-schema@2.1.5': {} - '@eslint/plugin-kit@0.2.2': + '@eslint/plugin-kit@0.2.4': dependencies: levn: 0.4.1 @@ -1881,7 +1883,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.9.0 + '@types/node': 22.10.1 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -1894,14 +1896,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.9.0 + '@types/node': 22.10.1 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.9.0) + jest-config: 29.7.0(@types/node@22.10.1) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -1926,7 +1928,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.9.0 + '@types/node': 22.10.1 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -1944,7 +1946,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.9.0 + '@types/node': 22.10.1 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -1966,7 +1968,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.22 - '@types/node': 22.9.0 + '@types/node': 22.10.1 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -2036,7 +2038,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.9.0 + '@types/node': 22.10.1 '@types/yargs': 17.0.32 chalk: 4.1.2 @@ -2106,7 +2108,7 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.1 '@types/istanbul-lib-coverage@2.0.6': {} @@ -2125,9 +2127,9 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@22.9.0': + '@types/node@22.10.1': dependencies: - undici-types: 6.19.8 + undici-types: 6.20.0 '@types/stack-utils@2.0.3': {} @@ -2137,86 +2139,82 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.13.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0)(typescript@5.6.3))(eslint@9.14.0)(typescript@5.6.3)': + '@typescript-eslint/eslint-plugin@8.18.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0)(typescript@5.7.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.13.0(eslint@9.14.0)(typescript@5.6.3) - '@typescript-eslint/scope-manager': 8.13.0 - '@typescript-eslint/type-utils': 8.13.0(eslint@9.14.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.13.0(eslint@9.14.0)(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.13.0 - eslint: 9.14.0 + '@typescript-eslint/parser': 8.18.0(eslint@9.16.0)(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.18.0 + '@typescript-eslint/type-utils': 8.18.0(eslint@9.16.0)(typescript@5.7.2) + '@typescript-eslint/utils': 8.18.0(eslint@9.16.0)(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.18.0 + eslint: 9.16.0 graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 + ts-api-utils: 1.4.0(typescript@5.7.2) + typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.13.0(eslint@9.14.0)(typescript@5.6.3)': + '@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2)': dependencies: - '@typescript-eslint/scope-manager': 8.13.0 - '@typescript-eslint/types': 8.13.0 - '@typescript-eslint/typescript-estree': 8.13.0(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.13.0 + '@typescript-eslint/scope-manager': 8.18.0 + '@typescript-eslint/types': 8.18.0 + '@typescript-eslint/typescript-estree': 8.18.0(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.18.0 debug: 4.3.7 - eslint: 9.14.0 - optionalDependencies: - typescript: 5.6.3 + eslint: 9.16.0 + typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.13.0': + '@typescript-eslint/scope-manager@8.18.0': dependencies: - '@typescript-eslint/types': 8.13.0 - '@typescript-eslint/visitor-keys': 8.13.0 + '@typescript-eslint/types': 8.18.0 + '@typescript-eslint/visitor-keys': 8.18.0 - '@typescript-eslint/type-utils@8.13.0(eslint@9.14.0)(typescript@5.6.3)': + '@typescript-eslint/type-utils@8.18.0(eslint@9.16.0)(typescript@5.7.2)': dependencies: - '@typescript-eslint/typescript-estree': 8.13.0(typescript@5.6.3) - '@typescript-eslint/utils': 8.13.0(eslint@9.14.0)(typescript@5.6.3) - debug: 4.3.7 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 + '@typescript-eslint/typescript-estree': 8.18.0(typescript@5.7.2) + '@typescript-eslint/utils': 8.18.0(eslint@9.16.0)(typescript@5.7.2) + debug: 4.4.0 + eslint: 9.16.0 + ts-api-utils: 1.4.0(typescript@5.7.2) + typescript: 5.7.2 transitivePeerDependencies: - - eslint - supports-color - '@typescript-eslint/types@8.13.0': {} + '@typescript-eslint/types@8.18.0': {} - '@typescript-eslint/typescript-estree@8.13.0(typescript@5.6.3)': + '@typescript-eslint/typescript-estree@8.18.0(typescript@5.7.2)': dependencies: - '@typescript-eslint/types': 8.13.0 - '@typescript-eslint/visitor-keys': 8.13.0 + '@typescript-eslint/types': 8.18.0 + '@typescript-eslint/visitor-keys': 8.18.0 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.4.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 + ts-api-utils: 1.4.3(typescript@5.7.2) + typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.13.0(eslint@9.14.0)(typescript@5.6.3)': + '@typescript-eslint/utils@8.18.0(eslint@9.16.0)(typescript@5.7.2)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0) - '@typescript-eslint/scope-manager': 8.13.0 - '@typescript-eslint/types': 8.13.0 - '@typescript-eslint/typescript-estree': 8.13.0(typescript@5.6.3) - eslint: 9.14.0 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.16.0) + '@typescript-eslint/scope-manager': 8.18.0 + '@typescript-eslint/types': 8.18.0 + '@typescript-eslint/typescript-estree': 8.18.0(typescript@5.7.2) + eslint: 9.16.0 + typescript: 5.7.2 transitivePeerDependencies: - supports-color - - typescript - '@typescript-eslint/visitor-keys@8.13.0': + '@typescript-eslint/visitor-keys@8.18.0': dependencies: - '@typescript-eslint/types': 8.13.0 - eslint-visitor-keys: 3.4.3 + '@typescript-eslint/types': 8.18.0 + eslint-visitor-keys: 4.2.0 acorn-jsx@5.3.2(acorn@8.14.0): dependencies: @@ -2383,13 +2381,13 @@ snapshots: convert-source-map@2.0.0: {} - create-jest@29.7.0(@types/node@22.9.0): + create-jest@29.7.0(@types/node@22.10.1): 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.9.0) + jest-config: 29.7.0(@types/node@22.10.1) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -2414,6 +2412,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.0: + dependencies: + ms: 2.1.3 + dedent@1.5.1: {} deep-is@0.1.4: {} @@ -2444,10 +2446,10 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-prettier@5.2.1(eslint@9.14.0)(prettier@3.3.3): + eslint-plugin-prettier@5.2.1(eslint@9.16.0)(prettier@3.4.2): dependencies: - eslint: 9.14.0 - prettier: 3.3.3 + eslint: 9.16.0 + prettier: 3.4.2 prettier-linter-helpers: 1.0.0 synckit: 0.9.1 @@ -2460,15 +2462,15 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.14.0: + eslint@9.16.0: dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.16.0) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.18.0 - '@eslint/core': 0.7.0 - '@eslint/eslintrc': 3.1.0 - '@eslint/js': 9.14.0 - '@eslint/plugin-kit': 0.2.2 + '@eslint/config-array': 0.19.1 + '@eslint/core': 0.9.1 + '@eslint/eslintrc': 3.2.0 + '@eslint/js': 9.16.0 + '@eslint/plugin-kit': 0.2.4 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.1 @@ -2496,7 +2498,6 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 - text-table: 0.2.0 transitivePeerDependencies: - supports-color @@ -2752,7 +2753,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.9.0 + '@types/node': 22.10.1 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -2772,16 +2773,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.9.0): + jest-cli@29.7.0(@types/node@22.10.1): 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.9.0) + create-jest: 29.7.0(@types/node@22.10.1) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@22.9.0) + jest-config: 29.7.0(@types/node@22.10.1) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -2791,7 +2792,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.9.0): + jest-config@29.7.0(@types/node@22.10.1): dependencies: '@babel/core': 7.23.9 '@jest/test-sequencer': 29.7.0 @@ -2816,7 +2817,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.1 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -2845,7 +2846,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.9.0 + '@types/node': 22.10.1 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -2855,7 +2856,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.9.0 + '@types/node': 22.10.1 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -2894,7 +2895,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.9.0 + '@types/node': 22.10.1 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -2929,7 +2930,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.9.0 + '@types/node': 22.10.1 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -2957,7 +2958,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.9.0 + '@types/node': 22.10.1 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.2 @@ -3003,7 +3004,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.9.0 + '@types/node': 22.10.1 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -3022,7 +3023,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.9.0 + '@types/node': 22.10.1 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -3031,17 +3032,17 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.1 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.9.0): + jest@29.7.0(@types/node@22.10.1): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@22.9.0) + jest-cli: 29.7.0(@types/node@22.10.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -3077,7 +3078,7 @@ snapshots: kleur@3.0.3: {} - lemmy-js-client@0.20.0-image-api-rework.8: {} + lemmy-js-client@0.20.0-modlog-combined.0: {} leven@3.1.0: {} @@ -3226,7 +3227,7 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier@3.3.3: {} + prettier@3.4.2: {} pretty-format@29.7.0: dependencies: @@ -3344,8 +3345,6 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 - text-table@0.2.0: {} - tmpl@1.0.5: {} to-fast-properties@2.0.0: {} @@ -3354,22 +3353,26 @@ snapshots: dependencies: is-number: 7.0.0 - ts-api-utils@1.4.0(typescript@5.6.3): + ts-api-utils@1.4.0(typescript@5.7.2): dependencies: - typescript: 5.6.3 + typescript: 5.7.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.9.0))(typescript@5.6.3): + ts-api-utils@1.4.3(typescript@5.7.2): + dependencies: + typescript: 5.7.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.10.1))(typescript@5.7.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.9.0) + jest: 29.7.0(@types/node@22.10.1) 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.3 + typescript: 5.7.2 yargs-parser: 21.1.1 optionalDependencies: '@babel/core': 7.23.9 @@ -3387,20 +3390,19 @@ snapshots: type-fest@0.21.3: {} - typescript-eslint@8.13.0(eslint@9.14.0)(typescript@5.6.3): + typescript-eslint@8.18.0(eslint@9.16.0)(typescript@5.7.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.13.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0)(typescript@5.6.3))(eslint@9.14.0)(typescript@5.6.3) - '@typescript-eslint/parser': 8.13.0(eslint@9.14.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.13.0(eslint@9.14.0)(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 + '@typescript-eslint/eslint-plugin': 8.18.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0)(typescript@5.7.2) + '@typescript-eslint/parser': 8.18.0(eslint@9.16.0)(typescript@5.7.2) + '@typescript-eslint/utils': 8.18.0(eslint@9.16.0)(typescript@5.7.2) + eslint: 9.16.0 + typescript: 5.7.2 transitivePeerDependencies: - - eslint - supports-color - typescript@5.6.3: {} + typescript@5.7.2: {} - undici-types@6.19.8: {} + undici-types@6.20.0: {} update-browserslist-db@1.0.13(browserslist@4.22.3): dependencies: diff --git a/api_tests/src/comment.spec.ts b/api_tests/src/comment.spec.ts index 419e58769..0a937847e 100644 --- a/api_tests/src/comment.spec.ts +++ b/api_tests/src/comment.spec.ts @@ -27,7 +27,6 @@ import { getComments, getCommentParentId, resolveCommunity, - getPersonDetails, getReplies, getUnreadCount, waitUntil, @@ -38,6 +37,7 @@ import { delay, saveUserSettings, listReports, + listPersonContent, } from "./shared"; import { CommentReportView, @@ -210,11 +210,13 @@ test.skip("Remove a comment from admin and community on the same instance", asyn expect(removeCommentRes.comment_view.comment.removed).toBe(true); // Make sure that comment is removed on alpha (it gets pushed since an admin from beta removed it) - let refetchedPostComments = await getPersonDetails( + let refetchedPostComments = await listPersonContent( alpha, commentRes.comment_view.comment.creator_id, + "Comments", ); - expect(refetchedPostComments.comments[0].comment.removed).toBe(true); + let firstRefetchedComment = refetchedPostComments.content[0] as CommentView; + expect(firstRefetchedComment.comment.removed).toBe(true); // beta will unremove the comment let unremoveCommentRes = await removeComment(beta, false, betaCommentId); diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index daa2203bf..771b1dc57 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -23,6 +23,9 @@ import { PrivateMessageReportResponse, SuccessResponse, UserBlockInstanceParams, + ListPersonContentResponse, + ListPersonContent, + PersonContentType, } from "lemmy-js-client"; import { CreatePost } from "lemmy-js-client/dist/types/CreatePost"; import { DeletePost } from "lemmy-js-client/dist/types/DeletePost"; @@ -207,7 +210,7 @@ async function allowInstance(api: LemmyHttp, instance: string) { // Ignore errors from duplicate allows (because setup gets called for each test file) try { await api.adminAllowInstance(params); - } catch (error) { + } catch { // console.error(error); } } @@ -735,6 +738,7 @@ export async function saveUserSettings( ): Promise { return api.saveUserSettings(form); } + export async function getPersonDetails( api: LemmyHttp, person_id: number, @@ -745,6 +749,18 @@ export async function getPersonDetails( return api.getPersonDetails(form); } +export async function listPersonContent( + api: LemmyHttp, + person_id: number, + type_?: PersonContentType, +): Promise { + let form: ListPersonContent = { + person_id, + type_, + }; + return api.listPersonContent(form); +} + export async function deleteUser(api: LemmyHttp): Promise { let form: DeleteAccount = { delete_content: true, diff --git a/api_tests/src/user.spec.ts b/api_tests/src/user.spec.ts index 551677262..edb8a4c83 100644 --- a/api_tests/src/user.spec.ts +++ b/api_tests/src/user.spec.ts @@ -75,7 +75,6 @@ test("Delete user", async () => { let user = await registerUser(alpha, alphaUrl); let user_profile = await getMyUser(user); let person_id = user_profile.local_user_view.person.id; - let actor_id = user_profile.local_user_view.person.actor_id; // make a local post and comment let alphaCommunity = (await resolveCommunity(user, "main@lemmy-alpha:8541")) diff --git a/crates/api/src/post/feature.rs b/crates/api/src/post/feature.rs index 8ede8c31c..7f2415e38 100644 --- a/crates/api/src/post/feature.rs +++ b/crates/api/src/post/feature.rs @@ -60,8 +60,8 @@ pub async fn feature_post( let form = ModFeaturePostForm { mod_person_id: local_user_view.person.id, post_id: data.post_id, - featured: data.featured, - is_featured_community: data.feature_type == PostFeatureType::Community, + featured: Some(data.featured), + is_featured_community: Some(data.feature_type == PostFeatureType::Community), }; ModFeaturePost::create(&mut context.pool(), &form).await?; diff --git a/crates/api/src/site/admin_allow_instance.rs b/crates/api/src/site/admin_allow_instance.rs index 81879ecae..cf3415b5b 100644 --- a/crates/api/src/site/admin_allow_instance.rs +++ b/crates/api/src/site/admin_allow_instance.rs @@ -7,10 +7,13 @@ use lemmy_api_common::{ LemmyErrorType, SuccessResponse, }; -use lemmy_db_schema::source::{ - federation_allowlist::{FederationAllowList, FederationAllowListForm}, - instance::Instance, - mod_log::admin::{AdminAllowInstance, AdminAllowInstanceForm}, +use lemmy_db_schema::{ + source::{ + federation_allowlist::{FederationAllowList, FederationAllowListForm}, + instance::Instance, + mod_log::admin::{AdminAllowInstance, AdminAllowInstanceForm}, + }, + traits::Crud, }; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::error::LemmyResult; @@ -47,7 +50,7 @@ pub async fn admin_allow_instance( reason: data.reason.clone(), allowed: data.allow, }; - AdminAllowInstance::insert(&mut context.pool(), &mod_log_form).await?; + AdminAllowInstance::create(&mut context.pool(), &mod_log_form).await?; Ok(Json(SuccessResponse::default())) } diff --git a/crates/api/src/site/admin_block_instance.rs b/crates/api/src/site/admin_block_instance.rs index 54962ccf3..f7b286ee1 100644 --- a/crates/api/src/site/admin_block_instance.rs +++ b/crates/api/src/site/admin_block_instance.rs @@ -7,10 +7,13 @@ use lemmy_api_common::{ LemmyErrorType, SuccessResponse, }; -use lemmy_db_schema::source::{ - federation_blocklist::{FederationBlockList, FederationBlockListForm}, - instance::Instance, - mod_log::admin::{AdminBlockInstance, AdminBlockInstanceForm}, +use lemmy_db_schema::{ + source::{ + federation_blocklist::{FederationBlockList, FederationBlockListForm}, + instance::Instance, + mod_log::admin::{AdminBlockInstance, AdminBlockInstanceForm}, + }, + traits::Crud, }; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::error::LemmyResult; @@ -48,9 +51,8 @@ pub async fn admin_block_instance( admin_person_id: local_user_view.person.id, blocked: data.block, reason: data.reason.clone(), - when_: data.expires, }; - AdminBlockInstance::insert(&mut context.pool(), &mod_log_form).await?; + AdminBlockInstance::create(&mut context.pool(), &mod_log_form).await?; Ok(Json(SuccessResponse::default())) } diff --git a/crates/api/src/site/mod_log.rs b/crates/api/src/site/mod_log.rs index bbf147666..8c6bfdb50 100644 --- a/crates/api/src/site/mod_log.rs +++ b/crates/api/src/site/mod_log.rs @@ -4,30 +4,10 @@ use lemmy_api_common::{ site::{GetModlog, GetModlogResponse}, utils::{check_community_mod_of_any_or_admin_action, check_private_instance}, }; -use lemmy_db_schema::{source::local_site::LocalSite, ModlogActionType}; +use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_moderator::structs::{ - AdminAllowInstanceView, - AdminBlockInstanceView, - AdminPurgeCommentView, - AdminPurgeCommunityView, - AdminPurgePersonView, - AdminPurgePostView, - ModAddCommunityView, - ModAddView, - ModBanFromCommunityView, - ModBanView, - ModFeaturePostView, - ModHideCommunityView, - ModLockPostView, - ModRemoveCommentView, - ModRemoveCommunityView, - ModRemovePostView, - ModTransferCommunityView, - ModlogListParams, -}; +use lemmy_db_views_moderator::{self, modlog_combined_view::ModlogCombinedQuery}; use lemmy_utils::error::LemmyResult; -use ModlogActionType::*; #[tracing::instrument(skip(context))] pub async fn get_mod_log( @@ -39,7 +19,7 @@ pub async fn get_mod_log( check_private_instance(&local_user_view, &local_site)?; - let type_ = data.type_.unwrap_or(All); + let type_ = data.type_; let community_id = data.community_id; let is_mod_or_admin = if let Some(local_user_view) = local_user_view { @@ -60,146 +40,27 @@ pub async fn get_mod_log( let post_id = data.post_id; let comment_id = data.comment_id; - let params = ModlogListParams { + // parse pagination token + let page_after = if let Some(pa) = &data.page_cursor { + Some(pa.read(&mut context.pool()).await?) + } else { + None + }; + let page_back = data.page_back; + + let modlog = ModlogCombinedQuery { + type_, community_id, mod_person_id, other_person_id, post_id, comment_id, - page: data.page, - limit: data.limit, - hide_modlog_names, - }; - let removed_posts = match type_ { - All | ModRemovePost => ModRemovePostView::list(&mut context.pool(), params).await?, - _ => Default::default(), - }; + hide_modlog_names: Some(hide_modlog_names), + page_after, + page_back, + } + .list(&mut context.pool()) + .await?; - let locked_posts = match type_ { - All | ModLockPost => ModLockPostView::list(&mut context.pool(), params).await?, - _ => Default::default(), - }; - - let featured_posts = match type_ { - All | ModFeaturePost => ModFeaturePostView::list(&mut context.pool(), params).await?, - _ => Default::default(), - }; - - let removed_comments = match type_ { - All | ModRemoveComment => ModRemoveCommentView::list(&mut context.pool(), params).await?, - _ => Default::default(), - }; - - let banned_from_community = match type_ { - All | ModBanFromCommunity => ModBanFromCommunityView::list(&mut context.pool(), params).await?, - _ => Default::default(), - }; - - let added_to_community = match type_ { - All | ModAddCommunity => ModAddCommunityView::list(&mut context.pool(), params).await?, - _ => Default::default(), - }; - - let transferred_to_community = match type_ { - All | ModTransferCommunity => { - ModTransferCommunityView::list(&mut context.pool(), params).await? - } - _ => Default::default(), - }; - - let hidden_communities = match type_ { - All | ModHideCommunity if other_person_id.is_none() => { - ModHideCommunityView::list(&mut context.pool(), params).await? - } - _ => Default::default(), - }; - - // These arrays are only for the full modlog, when a community isn't given - let ( - banned, - added, - removed_communities, - admin_purged_persons, - admin_purged_communities, - admin_purged_posts, - admin_purged_comments, - admin_block_instance, - admin_allow_instance, - ) = if data.community_id.is_none() { - ( - match type_ { - All | ModBan => ModBanView::list(&mut context.pool(), params).await?, - _ => Default::default(), - }, - match type_ { - All | ModAdd => ModAddView::list(&mut context.pool(), params).await?, - _ => Default::default(), - }, - match type_ { - All | ModRemoveCommunity if other_person_id.is_none() => { - ModRemoveCommunityView::list(&mut context.pool(), params).await? - } - _ => Default::default(), - }, - match type_ { - All | AdminPurgePerson if other_person_id.is_none() => { - AdminPurgePersonView::list(&mut context.pool(), params).await? - } - _ => Default::default(), - }, - match type_ { - All | AdminPurgeCommunity if other_person_id.is_none() => { - AdminPurgeCommunityView::list(&mut context.pool(), params).await? - } - _ => Default::default(), - }, - match type_ { - All | AdminPurgePost if other_person_id.is_none() => { - AdminPurgePostView::list(&mut context.pool(), params).await? - } - _ => Default::default(), - }, - match type_ { - All | AdminPurgeComment if other_person_id.is_none() => { - AdminPurgeCommentView::list(&mut context.pool(), params).await? - } - _ => Default::default(), - }, - match type_ { - All | AdminBlockInstance if other_person_id.is_none() => { - AdminBlockInstanceView::list(&mut context.pool(), params).await? - } - _ => Default::default(), - }, - match type_ { - All | AdminAllowInstance if other_person_id.is_none() => { - AdminAllowInstanceView::list(&mut context.pool(), params).await? - } - _ => Default::default(), - }, - ) - } else { - Default::default() - }; - - // Return the jwt - Ok(Json(GetModlogResponse { - removed_posts, - locked_posts, - featured_posts, - removed_comments, - removed_communities, - banned_from_community, - banned, - added_to_community, - added, - transferred_to_community, - admin_purged_persons, - admin_purged_communities, - admin_purged_posts, - admin_purged_comments, - hidden_communities, - admin_block_instance, - admin_allow_instance, - })) + Ok(Json(GetModlogResponse { modlog })) } diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index c0ef01257..083f7af81 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -42,25 +42,7 @@ use lemmy_db_views_actor::structs::{ CommunityView, PersonView, }; -use lemmy_db_views_moderator::structs::{ - AdminAllowInstanceView, - AdminBlockInstanceView, - AdminPurgeCommentView, - AdminPurgeCommunityView, - AdminPurgePersonView, - AdminPurgePostView, - ModAddCommunityView, - ModAddView, - ModBanFromCommunityView, - ModBanView, - ModFeaturePostView, - ModHideCommunityView, - ModLockPostView, - ModRemoveCommentView, - ModRemoveCommunityView, - ModRemovePostView, - ModTransferCommunityView, -}; +use lemmy_db_views_moderator::structs::{ModlogCombinedPaginationCursor, ModlogCombinedView}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; #[cfg(feature = "full")] @@ -139,7 +121,7 @@ pub struct ResolveObjectResponse { } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)] +#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] /// Fetches the modlog. @@ -149,10 +131,6 @@ pub struct GetModlog { #[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, @@ -160,31 +138,18 @@ pub struct GetModlog { pub post_id: Option, #[cfg_attr(feature = "full", ts(optional))] pub comment_id: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub page_cursor: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub page_back: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] /// The modlog fetch response. -// TODO this should be redone as a list of tagged enums pub struct GetModlogResponse { - pub removed_posts: Vec, - pub locked_posts: Vec, - pub featured_posts: Vec, - pub removed_comments: Vec, - pub removed_communities: Vec, - pub banned_from_community: Vec, - pub banned: Vec, - pub added_to_community: Vec, - pub transferred_to_community: Vec, - pub added: Vec, - pub admin_purged_persons: Vec, - pub admin_purged_communities: Vec, - pub admin_purged_posts: Vec, - pub admin_purged_comments: Vec, - pub hidden_communities: Vec, - pub admin_block_instance: Vec, - pub admin_allow_instance: Vec, + pub modlog: Vec, } #[skip_serializing_none] diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index 91acde0a9..885c766d0 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -1157,16 +1157,18 @@ fn build_proxied_image_url( mod tests { use super::*; - use lemmy_db_schema::source::{ - comment::CommentInsertForm, - community::CommunityInsertForm, - person::PersonInsertForm, - post::PostInsertForm, + use lemmy_db_schema::{ + source::{ + comment::CommentInsertForm, + community::CommunityInsertForm, + person::PersonInsertForm, + post::PostInsertForm, + }, + ModlogActionType, }; - use lemmy_db_views_moderator::structs::{ - ModRemoveCommentView, - ModRemovePostView, - ModlogListParams, + use lemmy_db_views_moderator::{ + modlog_combined_view::ModlogCombinedQuery, + structs::{ModRemoveCommentView, ModRemovePostView, ModlogCombinedView}, }; use pretty_assertions::assert_eq; use serial_test::serial; @@ -1302,48 +1304,55 @@ mod tests { .await?; // Verify that their posts and comments are removed. - let params = ModlogListParams { - community_id: None, - mod_person_id: None, - other_person_id: None, - post_id: None, - comment_id: None, - page: None, - limit: None, - hide_modlog_names: false, - }; - // Posts - let post_modlog = ModRemovePostView::list(pool, params).await?; + let post_modlog = ModlogCombinedQuery { + type_: Some(ModlogActionType::ModRemovePost), + ..Default::default() + } + .list(pool) + .await?; assert_eq!(2, post_modlog.len()); - let mod_removed_posts = post_modlog - .iter() - .map(|p| p.mod_remove_post.removed) - .collect::>(); - assert_eq!(vec![true, true], mod_removed_posts); - - let removed_posts = post_modlog - .iter() - .map(|p| p.post.removed) - .collect::>(); - assert_eq!(vec![true, true], removed_posts); + assert!(matches!( + &post_modlog[..], + [ + ModlogCombinedView::ModRemovePost(ModRemovePostView { + mod_remove_post: ModRemovePost { removed: true, .. }, + post: Post { removed: true, .. }, + .. + }), + ModlogCombinedView::ModRemovePost(ModRemovePostView { + mod_remove_post: ModRemovePost { removed: true, .. }, + post: Post { removed: true, .. }, + .. + }), + ], + )); // Comments - let comment_modlog = ModRemoveCommentView::list(pool, params).await?; + let comment_modlog = ModlogCombinedQuery { + type_: Some(ModlogActionType::ModRemoveComment), + ..Default::default() + } + .list(pool) + .await?; assert_eq!(2, comment_modlog.len()); - let mod_removed_comments = comment_modlog - .iter() - .map(|p| p.mod_remove_comment.removed) - .collect::>(); - assert_eq!(vec![true, true], mod_removed_comments); - - let removed_comments = comment_modlog - .iter() - .map(|p| p.comment.removed) - .collect::>(); - assert_eq!(vec![true, true], removed_comments); + assert!(matches!( + &comment_modlog[..], + [ + ModlogCombinedView::ModRemoveComment(ModRemoveCommentView { + mod_remove_comment: ModRemoveComment { removed: true, .. }, + comment: Comment { removed: true, .. }, + .. + }), + ModlogCombinedView::ModRemoveComment(ModRemoveCommentView { + mod_remove_comment: ModRemoveComment { removed: true, .. }, + comment: Comment { removed: true, .. }, + .. + }), + ], + )); // Now restore the content, and make sure it got appended remove_or_restore_user_data( @@ -1356,37 +1365,74 @@ mod tests { .await?; // Posts - let post_modlog = ModRemovePostView::list(pool, params).await?; + let post_modlog = ModlogCombinedQuery { + type_: Some(ModlogActionType::ModRemovePost), + ..Default::default() + } + .list(pool) + .await?; assert_eq!(4, post_modlog.len()); - let mod_restored_posts = post_modlog - .iter() - .map(|p| p.mod_remove_post.removed) - .collect::>(); - assert_eq!(vec![false, false, true, true], mod_restored_posts); - - let restored_posts = post_modlog - .iter() - .map(|p| p.post.removed) - .collect::>(); - // All of these will be false, cause its the current state of the post - assert_eq!(vec![false, false, false, false], restored_posts); + assert!(matches!( + &post_modlog[..], + [ + ModlogCombinedView::ModRemovePost(ModRemovePostView { + mod_remove_post: ModRemovePost { removed: false, .. }, + post: Post { removed: false, .. }, + .. + }), + ModlogCombinedView::ModRemovePost(ModRemovePostView { + mod_remove_post: ModRemovePost { removed: false, .. }, + post: Post { removed: false, .. }, + .. + }), + ModlogCombinedView::ModRemovePost(ModRemovePostView { + mod_remove_post: ModRemovePost { removed: true, .. }, + post: Post { removed: false, .. }, + .. + }), + ModlogCombinedView::ModRemovePost(ModRemovePostView { + mod_remove_post: ModRemovePost { removed: true, .. }, + post: Post { removed: false, .. }, + .. + }), + ], + )); // Comments - let comment_modlog = ModRemoveCommentView::list(pool, params).await?; + let comment_modlog = ModlogCombinedQuery { + type_: Some(ModlogActionType::ModRemoveComment), + ..Default::default() + } + .list(pool) + .await?; assert_eq!(4, comment_modlog.len()); - let mod_restored_comments = comment_modlog - .iter() - .map(|p| p.mod_remove_comment.removed) - .collect::>(); - assert_eq!(vec![false, false, true, true], mod_restored_comments); - - let restored_comments = comment_modlog - .iter() - .map(|p| p.comment.removed) - .collect::>(); - assert_eq!(vec![false, false, false, false], restored_comments); + assert!(matches!( + &comment_modlog[..], + [ + ModlogCombinedView::ModRemoveComment(ModRemoveCommentView { + mod_remove_comment: ModRemoveComment { removed: false, .. }, + comment: Comment { removed: false, .. }, + .. + }), + ModlogCombinedView::ModRemoveComment(ModRemoveCommentView { + mod_remove_comment: ModRemoveComment { removed: false, .. }, + comment: Comment { removed: false, .. }, + .. + }), + ModlogCombinedView::ModRemoveComment(ModRemoveCommentView { + mod_remove_comment: ModRemoveComment { removed: true, .. }, + comment: Comment { removed: false, .. }, + .. + }), + ModlogCombinedView::ModRemoveComment(ModRemoveCommentView { + mod_remove_comment: ModRemoveComment { removed: true, .. }, + comment: Comment { removed: false, .. }, + .. + }), + ], + )); Instance::delete(pool, inserted_instance.id).await?; diff --git a/crates/db_schema/replaceable_schema/triggers.sql b/crates/db_schema/replaceable_schema/triggers.sql index 2f7821412..29ba4c682 100644 --- a/crates/db_schema/replaceable_schema/triggers.sql +++ b/crates/db_schema/replaceable_schema/triggers.sql @@ -782,3 +782,78 @@ CALL r.create_person_saved_combined_trigger ('post'); CALL r.create_person_saved_combined_trigger ('comment'); +-- modlog: (17 tables) +-- admin_allow_instance +-- admin_block_instance +-- admin_purge_comment +-- admin_purge_community +-- admin_purge_person +-- admin_purge_post +-- mod_add +-- mod_add_community +-- mod_ban +-- mod_ban_from_community +-- mod_feature_post +-- mod_hide_community +-- mod_lock_post +-- mod_remove_comment +-- mod_remove_community +-- mod_remove_post +-- mod_transfer_community +CREATE PROCEDURE r.create_modlog_combined_trigger (table_name text) +LANGUAGE plpgsql +AS $a$ +BEGIN + EXECUTE replace($b$ CREATE FUNCTION r.modlog_combined_thing_insert ( ) + RETURNS TRIGGER + LANGUAGE plpgsql + AS $$ + BEGIN + INSERT INTO modlog_combined (published, thing_id) + VALUES (NEW.published, NEW.id); + RETURN NEW; + END $$; + CREATE TRIGGER modlog_combined + AFTER INSERT ON thing + FOR EACH ROW + EXECUTE FUNCTION r.modlog_combined_thing_insert ( ); + $b$, + 'thing', + table_name); +END; +$a$; + +CALL r.create_modlog_combined_trigger ('admin_allow_instance'); + +CALL r.create_modlog_combined_trigger ('admin_block_instance'); + +CALL r.create_modlog_combined_trigger ('admin_purge_comment'); + +CALL r.create_modlog_combined_trigger ('admin_purge_community'); + +CALL r.create_modlog_combined_trigger ('admin_purge_person'); + +CALL r.create_modlog_combined_trigger ('admin_purge_post'); + +CALL r.create_modlog_combined_trigger ('mod_add'); + +CALL r.create_modlog_combined_trigger ('mod_add_community'); + +CALL r.create_modlog_combined_trigger ('mod_ban'); + +CALL r.create_modlog_combined_trigger ('mod_ban_from_community'); + +CALL r.create_modlog_combined_trigger ('mod_feature_post'); + +CALL r.create_modlog_combined_trigger ('mod_hide_community'); + +CALL r.create_modlog_combined_trigger ('mod_lock_post'); + +CALL r.create_modlog_combined_trigger ('mod_remove_comment'); + +CALL r.create_modlog_combined_trigger ('mod_remove_community'); + +CALL r.create_modlog_combined_trigger ('mod_remove_post'); + +CALL r.create_modlog_combined_trigger ('mod_transfer_community'); + diff --git a/crates/db_schema/src/impls/federation_allowlist.rs b/crates/db_schema/src/impls/federation_allowlist.rs index 41ced26f7..d9b1332ab 100644 --- a/crates/db_schema/src/impls/federation_allowlist.rs +++ b/crates/db_schema/src/impls/federation_allowlist.rs @@ -1,43 +1,25 @@ use crate::{ newtypes::InstanceId, - schema::{admin_allow_instance, federation_allowlist}, - source::{ - federation_allowlist::{FederationAllowList, FederationAllowListForm}, - mod_log::admin::{AdminAllowInstance, AdminAllowInstanceForm}, - }, + schema::federation_allowlist, + source::federation_allowlist::{FederationAllowList, FederationAllowListForm}, utils::{get_conn, DbPool}, }; use diesel::{delete, dsl::insert_into, result::Error, ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; -impl AdminAllowInstance { - pub async fn insert(pool: &mut DbPool<'_>, form: &AdminAllowInstanceForm) -> Result<(), Error> { - let conn = &mut get_conn(pool).await?; - insert_into(admin_allow_instance::table) - .values(form) - .execute(conn) - .await?; - - Ok(()) - } -} - impl FederationAllowList { - pub async fn allow(pool: &mut DbPool<'_>, form: &FederationAllowListForm) -> Result<(), Error> { + pub async fn allow(pool: &mut DbPool<'_>, form: &FederationAllowListForm) -> Result { let conn = &mut get_conn(pool).await?; insert_into(federation_allowlist::table) .values(form) - .execute(conn) - .await?; - Ok(()) + .get_result::(conn) + .await } - pub async fn unallow(pool: &mut DbPool<'_>, instance_id_: InstanceId) -> Result<(), Error> { - use federation_allowlist::dsl::instance_id; + pub async fn unallow(pool: &mut DbPool<'_>, instance_id_: InstanceId) -> Result { let conn = &mut get_conn(pool).await?; - delete(federation_allowlist::table.filter(instance_id.eq(instance_id_))) + delete(federation_allowlist::table.filter(federation_allowlist::instance_id.eq(instance_id_))) .execute(conn) - .await?; - Ok(()) + .await } } diff --git a/crates/db_schema/src/impls/federation_blocklist.rs b/crates/db_schema/src/impls/federation_blocklist.rs index 4a42e81b6..e4baf5b3f 100644 --- a/crates/db_schema/src/impls/federation_blocklist.rs +++ b/crates/db_schema/src/impls/federation_blocklist.rs @@ -1,42 +1,24 @@ use crate::{ newtypes::InstanceId, - schema::{admin_block_instance, federation_blocklist}, - source::{ - federation_blocklist::{FederationBlockList, FederationBlockListForm}, - mod_log::admin::{AdminBlockInstance, AdminBlockInstanceForm}, - }, + schema::federation_blocklist, + source::federation_blocklist::{FederationBlockList, FederationBlockListForm}, utils::{get_conn, DbPool}, }; use diesel::{delete, dsl::insert_into, result::Error, ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; -impl AdminBlockInstance { - pub async fn insert(pool: &mut DbPool<'_>, form: &AdminBlockInstanceForm) -> Result<(), Error> { - let conn = &mut get_conn(pool).await?; - insert_into(admin_block_instance::table) - .values(form) - .execute(conn) - .await?; - - Ok(()) - } -} - impl FederationBlockList { - pub async fn block(pool: &mut DbPool<'_>, form: &FederationBlockListForm) -> Result<(), Error> { + pub async fn block(pool: &mut DbPool<'_>, form: &FederationBlockListForm) -> Result { let conn = &mut get_conn(pool).await?; insert_into(federation_blocklist::table) .values(form) - .execute(conn) - .await?; - Ok(()) + .get_result::(conn) + .await } - pub async fn unblock(pool: &mut DbPool<'_>, instance_id_: InstanceId) -> Result<(), Error> { - use federation_blocklist::dsl::instance_id; + pub async fn unblock(pool: &mut DbPool<'_>, instance_id_: InstanceId) -> Result { let conn = &mut get_conn(pool).await?; - delete(federation_blocklist::table.filter(instance_id.eq(instance_id_))) + delete(federation_blocklist::table.filter(federation_blocklist::instance_id.eq(instance_id_))) .execute(conn) - .await?; - Ok(()) + .await } } diff --git a/crates/db_schema/src/impls/mod_log/admin.rs b/crates/db_schema/src/impls/mod_log/admin.rs index c1b2bf69f..7d007ccba 100644 --- a/crates/db_schema/src/impls/mod_log/admin.rs +++ b/crates/db_schema/src/impls/mod_log/admin.rs @@ -1,5 +1,25 @@ use crate::{ + newtypes::{ + AdminAllowInstanceId, + AdminBlockInstanceId, + AdminPurgeCommentId, + AdminPurgeCommunityId, + AdminPurgePersonId, + AdminPurgePostId, + }, + schema::{ + admin_allow_instance, + admin_block_instance, + admin_purge_comment, + admin_purge_community, + admin_purge_person, + admin_purge_post, + }, source::mod_log::admin::{ + AdminAllowInstance, + AdminAllowInstanceForm, + AdminBlockInstance, + AdminBlockInstanceForm, AdminPurgeComment, AdminPurgeCommentForm, AdminPurgeCommunity, @@ -19,12 +39,11 @@ use diesel_async::RunQueryDsl; impl Crud for AdminPurgePerson { type InsertForm = AdminPurgePersonForm; type UpdateForm = AdminPurgePersonForm; - type IdType = i32; + type IdType = AdminPurgePersonId; async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { - use crate::schema::admin_purge_person::dsl::admin_purge_person; let conn = &mut get_conn(pool).await?; - insert_into(admin_purge_person) + insert_into(admin_purge_person::table) .values(form) .get_result::(conn) .await @@ -32,12 +51,11 @@ impl Crud for AdminPurgePerson { async fn update( pool: &mut DbPool<'_>, - from_id: i32, + from_id: Self::IdType, form: &Self::InsertForm, ) -> Result { - use crate::schema::admin_purge_person::dsl::admin_purge_person; let conn = &mut get_conn(pool).await?; - diesel::update(admin_purge_person.find(from_id)) + diesel::update(admin_purge_person::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -48,12 +66,11 @@ impl Crud for AdminPurgePerson { impl Crud for AdminPurgeCommunity { type InsertForm = AdminPurgeCommunityForm; type UpdateForm = AdminPurgeCommunityForm; - type IdType = i32; + type IdType = AdminPurgeCommunityId; async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { - use crate::schema::admin_purge_community::dsl::admin_purge_community; let conn = &mut get_conn(pool).await?; - insert_into(admin_purge_community) + insert_into(admin_purge_community::table) .values(form) .get_result::(conn) .await @@ -61,12 +78,11 @@ impl Crud for AdminPurgeCommunity { async fn update( pool: &mut DbPool<'_>, - from_id: i32, + from_id: Self::IdType, form: &Self::InsertForm, ) -> Result { - use crate::schema::admin_purge_community::dsl::admin_purge_community; let conn = &mut get_conn(pool).await?; - diesel::update(admin_purge_community.find(from_id)) + diesel::update(admin_purge_community::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -77,12 +93,11 @@ impl Crud for AdminPurgeCommunity { impl Crud for AdminPurgePost { type InsertForm = AdminPurgePostForm; type UpdateForm = AdminPurgePostForm; - type IdType = i32; + type IdType = AdminPurgePostId; async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { - use crate::schema::admin_purge_post::dsl::admin_purge_post; let conn = &mut get_conn(pool).await?; - insert_into(admin_purge_post) + insert_into(admin_purge_post::table) .values(form) .get_result::(conn) .await @@ -90,12 +105,11 @@ impl Crud for AdminPurgePost { async fn update( pool: &mut DbPool<'_>, - from_id: i32, + from_id: Self::IdType, form: &Self::InsertForm, ) -> Result { - use crate::schema::admin_purge_post::dsl::admin_purge_post; let conn = &mut get_conn(pool).await?; - diesel::update(admin_purge_post.find(from_id)) + diesel::update(admin_purge_post::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -106,12 +120,11 @@ impl Crud for AdminPurgePost { impl Crud for AdminPurgeComment { type InsertForm = AdminPurgeCommentForm; type UpdateForm = AdminPurgeCommentForm; - type IdType = i32; + type IdType = AdminPurgeCommentId; async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { - use crate::schema::admin_purge_comment::dsl::admin_purge_comment; let conn = &mut get_conn(pool).await?; - insert_into(admin_purge_comment) + insert_into(admin_purge_comment::table) .values(form) .get_result::(conn) .await @@ -119,12 +132,65 @@ impl Crud for AdminPurgeComment { async fn update( pool: &mut DbPool<'_>, - from_id: i32, + from_id: Self::IdType, form: &Self::InsertForm, ) -> Result { - use crate::schema::admin_purge_comment::dsl::admin_purge_comment; let conn = &mut get_conn(pool).await?; - diesel::update(admin_purge_comment.find(from_id)) + diesel::update(admin_purge_comment::table.find(from_id)) + .set(form) + .get_result::(conn) + .await + } +} + +#[async_trait] +impl Crud for AdminAllowInstance { + type InsertForm = AdminAllowInstanceForm; + type UpdateForm = AdminAllowInstanceForm; + type IdType = AdminAllowInstanceId; + + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { + let conn = &mut get_conn(pool).await?; + insert_into(admin_allow_instance::table) + .values(form) + .get_result::(conn) + .await + } + + async fn update( + pool: &mut DbPool<'_>, + from_id: Self::IdType, + form: &Self::InsertForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + diesel::update(admin_allow_instance::table.find(from_id)) + .set(form) + .get_result::(conn) + .await + } +} + +#[async_trait] +impl Crud for AdminBlockInstance { + type InsertForm = AdminBlockInstanceForm; + type UpdateForm = AdminBlockInstanceForm; + type IdType = AdminBlockInstanceId; + + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { + let conn = &mut get_conn(pool).await?; + insert_into(admin_block_instance::table) + .values(form) + .get_result::(conn) + .await + } + + async fn update( + pool: &mut DbPool<'_>, + from_id: Self::IdType, + form: &Self::InsertForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + diesel::update(admin_block_instance::table.find(from_id)) .set(form) .get_result::(conn) .await diff --git a/crates/db_schema/src/impls/mod_log/moderator.rs b/crates/db_schema/src/impls/mod_log/moderator.rs index 37b66480d..e95e2a3e3 100644 --- a/crates/db_schema/src/impls/mod_log/moderator.rs +++ b/crates/db_schema/src/impls/mod_log/moderator.rs @@ -1,4 +1,30 @@ use crate::{ + newtypes::{ + ModAddCommunityId, + ModAddId, + ModBanFromCommunityId, + ModBanId, + ModFeaturePostId, + ModHideCommunityId, + ModLockPostId, + ModRemoveCommentId, + ModRemoveCommunityId, + ModRemovePostId, + ModTransferCommunityId, + }, + schema::{ + mod_add, + mod_add_community, + mod_ban, + mod_ban_from_community, + mod_feature_post, + mod_hide_community, + mod_lock_post, + mod_remove_comment, + mod_remove_community, + mod_remove_post, + mod_transfer_community, + }, source::mod_log::moderator::{ ModAdd, ModAddCommunity, @@ -33,12 +59,11 @@ use diesel_async::RunQueryDsl; impl Crud for ModRemovePost { type InsertForm = ModRemovePostForm; type UpdateForm = ModRemovePostForm; - type IdType = i32; + type IdType = ModRemovePostId; - async fn create(pool: &mut DbPool<'_>, form: &ModRemovePostForm) -> Result { - use crate::schema::mod_remove_post::dsl::mod_remove_post; + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(mod_remove_post) + insert_into(mod_remove_post::table) .values(form) .get_result::(conn) .await @@ -46,12 +71,11 @@ impl Crud for ModRemovePost { async fn update( pool: &mut DbPool<'_>, - from_id: i32, - form: &ModRemovePostForm, + from_id: Self::IdType, + form: &Self::UpdateForm, ) -> Result { - use crate::schema::mod_remove_post::dsl::mod_remove_post; let conn = &mut get_conn(pool).await?; - diesel::update(mod_remove_post.find(from_id)) + diesel::update(mod_remove_post::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -63,9 +87,8 @@ impl ModRemovePost { pool: &mut DbPool<'_>, forms: &Vec, ) -> Result { - use crate::schema::mod_remove_post::dsl::mod_remove_post; let conn = &mut get_conn(pool).await?; - insert_into(mod_remove_post) + insert_into(mod_remove_post::table) .values(forms) .execute(conn) .await @@ -76,12 +99,11 @@ impl ModRemovePost { impl Crud for ModLockPost { type InsertForm = ModLockPostForm; type UpdateForm = ModLockPostForm; - type IdType = i32; + type IdType = ModLockPostId; - async fn create(pool: &mut DbPool<'_>, form: &ModLockPostForm) -> Result { - use crate::schema::mod_lock_post::dsl::mod_lock_post; + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(mod_lock_post) + insert_into(mod_lock_post::table) .values(form) .get_result::(conn) .await @@ -89,12 +111,11 @@ impl Crud for ModLockPost { async fn update( pool: &mut DbPool<'_>, - from_id: i32, - form: &ModLockPostForm, + from_id: Self::IdType, + form: &Self::UpdateForm, ) -> Result { - use crate::schema::mod_lock_post::dsl::mod_lock_post; let conn = &mut get_conn(pool).await?; - diesel::update(mod_lock_post.find(from_id)) + diesel::update(mod_lock_post::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -105,12 +126,11 @@ impl Crud for ModLockPost { impl Crud for ModFeaturePost { type InsertForm = ModFeaturePostForm; type UpdateForm = ModFeaturePostForm; - type IdType = i32; + type IdType = ModFeaturePostId; - async fn create(pool: &mut DbPool<'_>, form: &ModFeaturePostForm) -> Result { - use crate::schema::mod_feature_post::dsl::mod_feature_post; + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(mod_feature_post) + insert_into(mod_feature_post::table) .values(form) .get_result::(conn) .await @@ -118,12 +138,11 @@ impl Crud for ModFeaturePost { async fn update( pool: &mut DbPool<'_>, - from_id: i32, - form: &ModFeaturePostForm, + from_id: Self::IdType, + form: &Self::UpdateForm, ) -> Result { - use crate::schema::mod_feature_post::dsl::mod_feature_post; let conn = &mut get_conn(pool).await?; - diesel::update(mod_feature_post.find(from_id)) + diesel::update(mod_feature_post::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -134,12 +153,11 @@ impl Crud for ModFeaturePost { impl Crud for ModRemoveComment { type InsertForm = ModRemoveCommentForm; type UpdateForm = ModRemoveCommentForm; - type IdType = i32; + type IdType = ModRemoveCommentId; - async fn create(pool: &mut DbPool<'_>, form: &ModRemoveCommentForm) -> Result { - use crate::schema::mod_remove_comment::dsl::mod_remove_comment; + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(mod_remove_comment) + insert_into(mod_remove_comment::table) .values(form) .get_result::(conn) .await @@ -147,12 +165,11 @@ impl Crud for ModRemoveComment { async fn update( pool: &mut DbPool<'_>, - from_id: i32, - form: &ModRemoveCommentForm, + from_id: Self::IdType, + form: &Self::UpdateForm, ) -> Result { - use crate::schema::mod_remove_comment::dsl::mod_remove_comment; let conn = &mut get_conn(pool).await?; - diesel::update(mod_remove_comment.find(from_id)) + diesel::update(mod_remove_comment::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -164,9 +181,8 @@ impl ModRemoveComment { pool: &mut DbPool<'_>, forms: &Vec, ) -> Result { - use crate::schema::mod_remove_comment::dsl::mod_remove_comment; let conn = &mut get_conn(pool).await?; - insert_into(mod_remove_comment) + insert_into(mod_remove_comment::table) .values(forms) .execute(conn) .await @@ -177,12 +193,11 @@ impl ModRemoveComment { impl Crud for ModRemoveCommunity { type InsertForm = ModRemoveCommunityForm; type UpdateForm = ModRemoveCommunityForm; - type IdType = i32; + type IdType = ModRemoveCommunityId; - async fn create(pool: &mut DbPool<'_>, form: &ModRemoveCommunityForm) -> Result { - use crate::schema::mod_remove_community::dsl::mod_remove_community; + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(mod_remove_community) + insert_into(mod_remove_community::table) .values(form) .get_result::(conn) .await @@ -190,12 +205,11 @@ impl Crud for ModRemoveCommunity { async fn update( pool: &mut DbPool<'_>, - from_id: i32, - form: &ModRemoveCommunityForm, + from_id: Self::IdType, + form: &Self::UpdateForm, ) -> Result { - use crate::schema::mod_remove_community::dsl::mod_remove_community; let conn = &mut get_conn(pool).await?; - diesel::update(mod_remove_community.find(from_id)) + diesel::update(mod_remove_community::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -206,12 +220,11 @@ impl Crud for ModRemoveCommunity { impl Crud for ModBanFromCommunity { type InsertForm = ModBanFromCommunityForm; type UpdateForm = ModBanFromCommunityForm; - type IdType = i32; + type IdType = ModBanFromCommunityId; - async fn create(pool: &mut DbPool<'_>, form: &ModBanFromCommunityForm) -> Result { - use crate::schema::mod_ban_from_community::dsl::mod_ban_from_community; + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(mod_ban_from_community) + insert_into(mod_ban_from_community::table) .values(form) .get_result::(conn) .await @@ -219,12 +232,11 @@ impl Crud for ModBanFromCommunity { async fn update( pool: &mut DbPool<'_>, - from_id: i32, - form: &ModBanFromCommunityForm, + from_id: Self::IdType, + form: &Self::UpdateForm, ) -> Result { - use crate::schema::mod_ban_from_community::dsl::mod_ban_from_community; let conn = &mut get_conn(pool).await?; - diesel::update(mod_ban_from_community.find(from_id)) + diesel::update(mod_ban_from_community::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -235,21 +247,23 @@ impl Crud for ModBanFromCommunity { impl Crud for ModBan { type InsertForm = ModBanForm; type UpdateForm = ModBanForm; - type IdType = i32; + type IdType = ModBanId; - async fn create(pool: &mut DbPool<'_>, form: &ModBanForm) -> Result { - use crate::schema::mod_ban::dsl::mod_ban; + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(mod_ban) + insert_into(mod_ban::table) .values(form) .get_result::(conn) .await } - async fn update(pool: &mut DbPool<'_>, from_id: i32, form: &ModBanForm) -> Result { - use crate::schema::mod_ban::dsl::mod_ban; + async fn update( + pool: &mut DbPool<'_>, + from_id: Self::IdType, + form: &Self::UpdateForm, + ) -> Result { let conn = &mut get_conn(pool).await?; - diesel::update(mod_ban.find(from_id)) + diesel::update(mod_ban::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -260,12 +274,11 @@ impl Crud for ModBan { impl Crud for ModHideCommunity { type InsertForm = ModHideCommunityForm; type UpdateForm = ModHideCommunityForm; - type IdType = i32; + type IdType = ModHideCommunityId; - async fn create(pool: &mut DbPool<'_>, form: &ModHideCommunityForm) -> Result { - use crate::schema::mod_hide_community::dsl::mod_hide_community; + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(mod_hide_community) + insert_into(mod_hide_community::table) .values(form) .get_result::(conn) .await @@ -273,12 +286,11 @@ impl Crud for ModHideCommunity { async fn update( pool: &mut DbPool<'_>, - from_id: i32, - form: &ModHideCommunityForm, + from_id: Self::IdType, + form: &Self::UpdateForm, ) -> Result { - use crate::schema::mod_hide_community::dsl::mod_hide_community; let conn = &mut get_conn(pool).await?; - diesel::update(mod_hide_community.find(from_id)) + diesel::update(mod_hide_community::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -289,12 +301,11 @@ impl Crud for ModHideCommunity { impl Crud for ModAddCommunity { type InsertForm = ModAddCommunityForm; type UpdateForm = ModAddCommunityForm; - type IdType = i32; + type IdType = ModAddCommunityId; - async fn create(pool: &mut DbPool<'_>, form: &ModAddCommunityForm) -> Result { - use crate::schema::mod_add_community::dsl::mod_add_community; + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(mod_add_community) + insert_into(mod_add_community::table) .values(form) .get_result::(conn) .await @@ -302,12 +313,11 @@ impl Crud for ModAddCommunity { async fn update( pool: &mut DbPool<'_>, - from_id: i32, - form: &ModAddCommunityForm, + from_id: Self::IdType, + form: &Self::UpdateForm, ) -> Result { - use crate::schema::mod_add_community::dsl::mod_add_community; let conn = &mut get_conn(pool).await?; - diesel::update(mod_add_community.find(from_id)) + diesel::update(mod_add_community::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -318,12 +328,11 @@ impl Crud for ModAddCommunity { impl Crud for ModTransferCommunity { type InsertForm = ModTransferCommunityForm; type UpdateForm = ModTransferCommunityForm; - type IdType = i32; + type IdType = ModTransferCommunityId; - async fn create(pool: &mut DbPool<'_>, form: &ModTransferCommunityForm) -> Result { - use crate::schema::mod_transfer_community::dsl::mod_transfer_community; + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(mod_transfer_community) + insert_into(mod_transfer_community::table) .values(form) .get_result::(conn) .await @@ -331,12 +340,11 @@ impl Crud for ModTransferCommunity { async fn update( pool: &mut DbPool<'_>, - from_id: i32, - form: &ModTransferCommunityForm, + from_id: Self::IdType, + form: &Self::UpdateForm, ) -> Result { - use crate::schema::mod_transfer_community::dsl::mod_transfer_community; let conn = &mut get_conn(pool).await?; - diesel::update(mod_transfer_community.find(from_id)) + diesel::update(mod_transfer_community::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -347,21 +355,23 @@ impl Crud for ModTransferCommunity { impl Crud for ModAdd { type InsertForm = ModAddForm; type UpdateForm = ModAddForm; - type IdType = i32; + type IdType = ModAddId; - async fn create(pool: &mut DbPool<'_>, form: &ModAddForm) -> Result { - use crate::schema::mod_add::dsl::mod_add; + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(mod_add) + insert_into(mod_add::table) .values(form) .get_result::(conn) .await } - async fn update(pool: &mut DbPool<'_>, from_id: i32, form: &ModAddForm) -> Result { - use crate::schema::mod_add::dsl::mod_add; + async fn update( + pool: &mut DbPool<'_>, + from_id: Self::IdType, + form: &Self::UpdateForm, + ) -> Result { let conn = &mut get_conn(pool).await?; - diesel::update(mod_add.find(from_id)) + diesel::update(mod_add::table.find(from_id)) .set(form) .get_result::(conn) .await @@ -441,7 +451,7 @@ mod tests { mod_person_id: inserted_mod.id, reason: None, removed: true, - when_: inserted_mod_remove_post.when_, + published: inserted_mod_remove_post.published, }; // lock post @@ -458,7 +468,7 @@ mod tests { post_id: inserted_post.id, mod_person_id: inserted_mod.id, locked: true, - when_: inserted_mod_lock_post.when_, + published: inserted_mod_lock_post.published, }; // feature post @@ -466,8 +476,8 @@ mod tests { let mod_feature_post_form = ModFeaturePostForm { mod_person_id: inserted_mod.id, post_id: inserted_post.id, - featured: false, - is_featured_community: true, + featured: Some(false), + is_featured_community: Some(true), }; let inserted_mod_feature_post = ModFeaturePost::create(pool, &mod_feature_post_form).await?; let read_mod_feature_post = ModFeaturePost::read(pool, inserted_mod_feature_post.id).await?; @@ -477,7 +487,7 @@ mod tests { mod_person_id: inserted_mod.id, featured: false, is_featured_community: true, - when_: inserted_mod_feature_post.when_, + published: inserted_mod_feature_post.published, }; // comment @@ -498,7 +508,7 @@ mod tests { mod_person_id: inserted_mod.id, reason: None, removed: true, - when_: inserted_mod_remove_comment.when_, + published: inserted_mod_remove_comment.published, }; // community @@ -519,7 +529,7 @@ mod tests { mod_person_id: inserted_mod.id, reason: None, removed: true, - when_: inserted_mod_remove_community.when_, + published: inserted_mod_remove_community.published, }; // ban from community @@ -544,7 +554,7 @@ mod tests { reason: None, banned: true, expires: None, - when_: inserted_mod_ban_from_community.when_, + published: inserted_mod_ban_from_community.published, }; // ban @@ -565,7 +575,7 @@ mod tests { reason: None, banned: true, expires: None, - when_: inserted_mod_ban.when_, + published: inserted_mod_ban.published, }; // mod add community @@ -584,7 +594,7 @@ mod tests { mod_person_id: inserted_mod.id, other_person_id: inserted_person.id, removed: false, - when_: inserted_mod_add_community.when_, + published: inserted_mod_add_community.published, }; // mod add @@ -601,7 +611,7 @@ mod tests { mod_person_id: inserted_mod.id, other_person_id: inserted_person.id, removed: false, - when_: inserted_mod_add.when_, + published: inserted_mod_add.published, }; Comment::delete(pool, inserted_comment.id).await?; diff --git a/crates/db_schema/src/lib.rs b/crates/db_schema/src/lib.rs index 19a106761..4cf0c5030 100644 --- a/crates/db_schema/src/lib.rs +++ b/crates/db_schema/src/lib.rs @@ -280,6 +280,13 @@ pub enum FederationMode { Disable, } +pub trait InternalToCombinedView { + type CombinedView; + + /// Maps the combined DB row to an enum + fn map_to_enum(self) -> Option; +} + /// Wrapper for assert_eq! macro. Checks that vec matches the given length, and prints the /// vec on failure. #[macro_export] diff --git a/crates/db_schema/src/newtypes.rs b/crates/db_schema/src/newtypes.rs index 7b5a00573..75731974e 100644 --- a/crates/db_schema/src/newtypes.rs +++ b/crates/db_schema/src/newtypes.rs @@ -200,6 +200,95 @@ pub struct PersonContentCombinedId(i32); /// The person saved combined id pub struct PersonSavedCombinedId(i32); +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType))] +pub struct ModlogCombinedId(i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct AdminAllowInstanceId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct AdminBlockInstanceId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct AdminPurgePersonId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct AdminPurgeCommunityId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct AdminPurgeCommentId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct AdminPurgePostId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModRemovePostId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModRemoveCommentId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModRemoveCommunityId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModLockPostId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModFeaturePostId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModBanFromCommunityId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModBanId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModHideCommunityId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModAddCommunityId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModTransferCommunityId(pub i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModAddId(pub i32); + impl DbUrl { pub fn inner(&self) -> &Url { &self.0 diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index ff9a1948e..c4698f641 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -49,7 +49,7 @@ diesel::table! { admin_person_id -> Int4, allowed -> Bool, reason -> Nullable, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -61,7 +61,7 @@ diesel::table! { blocked -> Bool, reason -> Nullable, expires -> Nullable, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -71,7 +71,7 @@ diesel::table! { admin_person_id -> Int4, post_id -> Int4, reason -> Nullable, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -80,7 +80,7 @@ diesel::table! { id -> Int4, admin_person_id -> Int4, reason -> Nullable, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -89,7 +89,7 @@ diesel::table! { id -> Int4, admin_person_id -> Int4, reason -> Nullable, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -99,7 +99,7 @@ diesel::table! { admin_person_id -> Int4, community_id -> Int4, reason -> Nullable, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -539,7 +539,7 @@ diesel::table! { mod_person_id -> Int4, other_person_id -> Int4, removed -> Bool, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -550,7 +550,7 @@ diesel::table! { other_person_id -> Int4, community_id -> Int4, removed -> Bool, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -562,7 +562,7 @@ diesel::table! { reason -> Nullable, banned -> Bool, expires -> Nullable, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -575,7 +575,7 @@ diesel::table! { reason -> Nullable, banned -> Bool, expires -> Nullable, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -585,7 +585,7 @@ diesel::table! { mod_person_id -> Int4, post_id -> Int4, featured -> Bool, - when_ -> Timestamptz, + published -> Timestamptz, is_featured_community -> Bool, } } @@ -595,7 +595,7 @@ diesel::table! { id -> Int4, community_id -> Int4, mod_person_id -> Int4, - when_ -> Timestamptz, + published -> Timestamptz, reason -> Nullable, hidden -> Bool, } @@ -607,7 +607,7 @@ diesel::table! { mod_person_id -> Int4, post_id -> Int4, locked -> Bool, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -618,7 +618,7 @@ diesel::table! { comment_id -> Int4, reason -> Nullable, removed -> Bool, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -629,7 +629,7 @@ diesel::table! { community_id -> Int4, reason -> Nullable, removed -> Bool, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -640,7 +640,7 @@ diesel::table! { post_id -> Int4, reason -> Nullable, removed -> Bool, - when_ -> Timestamptz, + published -> Timestamptz, } } @@ -650,7 +650,31 @@ diesel::table! { mod_person_id -> Int4, other_person_id -> Int4, community_id -> Int4, - when_ -> Timestamptz, + published -> Timestamptz, + } +} + +diesel::table! { + modlog_combined (id) { + id -> Int4, + published -> Timestamptz, + admin_allow_instance_id -> Nullable, + admin_block_instance_id -> Nullable, + admin_purge_comment_id -> Nullable, + admin_purge_community_id -> Nullable, + admin_purge_person_id -> Nullable, + admin_purge_post_id -> Nullable, + mod_add_id -> Nullable, + mod_add_community_id -> Nullable, + mod_ban_id -> Nullable, + mod_ban_from_community_id -> Nullable, + mod_feature_post_id -> Nullable, + mod_hide_community_id -> Nullable, + mod_lock_post_id -> Nullable, + mod_remove_comment_id -> Nullable, + mod_remove_community_id -> Nullable, + mod_remove_post_id -> Nullable, + mod_transfer_community_id -> Nullable, } } @@ -1093,6 +1117,23 @@ diesel::joinable!(mod_remove_community -> person (mod_person_id)); diesel::joinable!(mod_remove_post -> person (mod_person_id)); diesel::joinable!(mod_remove_post -> post (post_id)); diesel::joinable!(mod_transfer_community -> community (community_id)); +diesel::joinable!(modlog_combined -> admin_allow_instance (admin_allow_instance_id)); +diesel::joinable!(modlog_combined -> admin_block_instance (admin_block_instance_id)); +diesel::joinable!(modlog_combined -> admin_purge_comment (admin_purge_comment_id)); +diesel::joinable!(modlog_combined -> admin_purge_community (admin_purge_community_id)); +diesel::joinable!(modlog_combined -> admin_purge_person (admin_purge_person_id)); +diesel::joinable!(modlog_combined -> admin_purge_post (admin_purge_post_id)); +diesel::joinable!(modlog_combined -> mod_add (mod_add_id)); +diesel::joinable!(modlog_combined -> mod_add_community (mod_add_community_id)); +diesel::joinable!(modlog_combined -> mod_ban (mod_ban_id)); +diesel::joinable!(modlog_combined -> mod_ban_from_community (mod_ban_from_community_id)); +diesel::joinable!(modlog_combined -> mod_feature_post (mod_feature_post_id)); +diesel::joinable!(modlog_combined -> mod_hide_community (mod_hide_community_id)); +diesel::joinable!(modlog_combined -> mod_lock_post (mod_lock_post_id)); +diesel::joinable!(modlog_combined -> mod_remove_comment (mod_remove_comment_id)); +diesel::joinable!(modlog_combined -> mod_remove_community (mod_remove_community_id)); +diesel::joinable!(modlog_combined -> mod_remove_post (mod_remove_post_id)); +diesel::joinable!(modlog_combined -> mod_transfer_community (mod_transfer_community_id)); diesel::joinable!(oauth_account -> local_user (local_user_id)); diesel::joinable!(oauth_account -> oauth_provider (oauth_provider_id)); diesel::joinable!(password_reset_request -> local_user (local_user_id)); @@ -1178,6 +1219,7 @@ diesel::allow_tables_to_appear_in_same_query!( mod_remove_community, mod_remove_post, mod_transfer_community, + modlog_combined, oauth_account, oauth_provider, password_reset_request, diff --git a/crates/db_schema/src/source/combined/mod.rs b/crates/db_schema/src/source/combined/mod.rs index b2b5e7d8e..6beec3921 100644 --- a/crates/db_schema/src/source/combined/mod.rs +++ b/crates/db_schema/src/source/combined/mod.rs @@ -1,3 +1,4 @@ +pub mod modlog; pub mod person_content; pub mod person_saved; pub mod report; diff --git a/crates/db_schema/src/source/combined/modlog.rs b/crates/db_schema/src/source/combined/modlog.rs new file mode 100644 index 000000000..a5e488042 --- /dev/null +++ b/crates/db_schema/src/source/combined/modlog.rs @@ -0,0 +1,57 @@ +use crate::newtypes::{ + AdminAllowInstanceId, + AdminBlockInstanceId, + AdminPurgeCommentId, + AdminPurgeCommunityId, + AdminPurgePersonId, + AdminPurgePostId, + ModAddCommunityId, + ModAddId, + ModBanFromCommunityId, + ModBanId, + ModFeaturePostId, + ModHideCommunityId, + ModLockPostId, + ModRemoveCommentId, + ModRemoveCommunityId, + ModRemovePostId, + ModTransferCommunityId, + ModlogCombinedId, +}; +#[cfg(feature = "full")] +use crate::schema::modlog_combined; +use chrono::{DateTime, Utc}; +#[cfg(feature = "full")] +use i_love_jesus::CursorKeysModule; +use serde::{Deserialize, Serialize}; + +#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)] +#[cfg_attr( + feature = "full", + derive(Identifiable, Queryable, Selectable, CursorKeysModule) +)] +#[cfg_attr(feature = "full", diesel(table_name = modlog_combined))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", cursor_keys_module(name = modlog_combined_keys))] +/// A combined modlog table. +pub struct ModlogCombined { + pub id: ModlogCombinedId, + pub published: DateTime, + pub admin_allow_instance_id: Option, + pub admin_block_instance_id: Option, + pub admin_purge_comment_id: Option, + pub admin_purge_community_id: Option, + pub admin_purge_person_id: Option, + pub admin_purge_post_id: Option, + pub mod_add_id: Option, + pub mod_add_community_id: Option, + pub mod_ban_id: Option, + pub mod_ban_from_community_id: Option, + pub mod_feature_post_id: Option, + pub mod_hide_community_id: Option, + pub mod_lock_post_id: Option, + pub mod_remove_comment_id: Option, + pub mod_remove_community_id: Option, + pub mod_remove_post_id: Option, + pub mod_transfer_community_id: Option, +} diff --git a/crates/db_schema/src/source/mod_log/admin.rs b/crates/db_schema/src/source/mod_log/admin.rs index d6e48b8ee..fe5a46b3b 100644 --- a/crates/db_schema/src/source/mod_log/admin.rs +++ b/crates/db_schema/src/source/mod_log/admin.rs @@ -1,4 +1,15 @@ -use crate::newtypes::{CommunityId, InstanceId, PersonId, PostId}; +use crate::newtypes::{ + AdminAllowInstanceId, + AdminBlockInstanceId, + AdminPurgeCommentId, + AdminPurgeCommunityId, + AdminPurgePersonId, + AdminPurgePostId, + CommunityId, + InstanceId, + PersonId, + PostId, +}; #[cfg(feature = "full")] use crate::schema::{ admin_allow_instance, @@ -22,11 +33,11 @@ use ts_rs::TS; #[cfg_attr(feature = "full", ts(export))] /// When an admin purges a person. pub struct AdminPurgePerson { - pub id: i32, + pub id: AdminPurgePersonId, pub admin_person_id: PersonId, #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -44,11 +55,11 @@ pub struct AdminPurgePersonForm { #[cfg_attr(feature = "full", ts(export))] /// When an admin purges a community. pub struct AdminPurgeCommunity { - pub id: i32, + pub id: AdminPurgeCommunityId, pub admin_person_id: PersonId, #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -66,12 +77,12 @@ pub struct AdminPurgeCommunityForm { #[cfg_attr(feature = "full", ts(export))] /// When an admin purges a post. pub struct AdminPurgePost { - pub id: i32, + pub id: AdminPurgePostId, pub admin_person_id: PersonId, pub community_id: CommunityId, #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -90,12 +101,12 @@ pub struct AdminPurgePostForm { #[cfg_attr(feature = "full", ts(export))] /// When an admin purges a comment. pub struct AdminPurgeComment { - pub id: i32, + pub id: AdminPurgeCommentId, pub admin_person_id: PersonId, pub post_id: PostId, #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -120,13 +131,13 @@ pub struct AdminPurgeCommentForm { #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] pub struct AdminAllowInstance { - pub id: i32, + pub id: AdminAllowInstanceId, pub instance_id: InstanceId, pub admin_person_id: PersonId, pub allowed: bool, #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, - pub when_: DateTime, + pub published: DateTime, } #[derive(Clone, Default)] @@ -153,7 +164,7 @@ pub struct AdminAllowInstanceForm { #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] pub struct AdminBlockInstance { - pub id: i32, + pub id: AdminBlockInstanceId, pub instance_id: InstanceId, pub admin_person_id: PersonId, pub blocked: bool, @@ -161,7 +172,7 @@ pub struct AdminBlockInstance { pub reason: Option, #[cfg_attr(feature = "full", ts(optional))] pub expires: Option>, - pub when_: DateTime, + pub published: DateTime, } #[derive(Clone, Default)] @@ -172,5 +183,4 @@ pub struct AdminBlockInstanceForm { pub admin_person_id: PersonId, pub blocked: bool, pub reason: Option, - pub when_: Option>, } diff --git a/crates/db_schema/src/source/mod_log/moderator.rs b/crates/db_schema/src/source/mod_log/moderator.rs index 470b643a5..2bb536cf9 100644 --- a/crates/db_schema/src/source/mod_log/moderator.rs +++ b/crates/db_schema/src/source/mod_log/moderator.rs @@ -1,4 +1,20 @@ -use crate::newtypes::{CommentId, CommunityId, PersonId, PostId}; +use crate::newtypes::{ + CommentId, + CommunityId, + ModAddCommunityId, + ModAddId, + ModBanFromCommunityId, + ModBanId, + ModFeaturePostId, + ModHideCommunityId, + ModLockPostId, + ModRemoveCommentId, + ModRemoveCommunityId, + ModRemovePostId, + ModTransferCommunityId, + PersonId, + PostId, +}; #[cfg(feature = "full")] use crate::schema::{ mod_add, @@ -27,13 +43,13 @@ use ts_rs::TS; #[cfg_attr(feature = "full", ts(export))] /// When a moderator removes a post. pub struct ModRemovePost { - pub id: i32, + pub id: ModRemovePostId, pub mod_person_id: PersonId, pub post_id: PostId, #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub removed: bool, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -52,11 +68,11 @@ pub struct ModRemovePostForm { #[cfg_attr(feature = "full", ts(export))] /// When a moderator locks a post (prevents new comments being made). pub struct ModLockPost { - pub id: i32, + pub id: ModLockPostId, pub mod_person_id: PersonId, pub post_id: PostId, pub locked: bool, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -74,11 +90,11 @@ pub struct ModLockPostForm { #[cfg_attr(feature = "full", ts(export))] /// When a moderator features a post on a community (pins it to the top). pub struct ModFeaturePost { - pub id: i32, + pub id: ModFeaturePostId, pub mod_person_id: PersonId, pub post_id: PostId, pub featured: bool, - pub when_: DateTime, + pub published: DateTime, pub is_featured_community: bool, } @@ -87,8 +103,8 @@ pub struct ModFeaturePost { pub struct ModFeaturePostForm { pub mod_person_id: PersonId, pub post_id: PostId, - pub featured: bool, - pub is_featured_community: bool, + pub featured: Option, + pub is_featured_community: Option, } #[skip_serializing_none] @@ -99,13 +115,13 @@ pub struct ModFeaturePostForm { #[cfg_attr(feature = "full", ts(export))] /// When a moderator removes a comment. pub struct ModRemoveComment { - pub id: i32, + pub id: ModRemoveCommentId, pub mod_person_id: PersonId, pub comment_id: CommentId, #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub removed: bool, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -125,13 +141,13 @@ pub struct ModRemoveCommentForm { #[cfg_attr(feature = "full", ts(export))] /// When a moderator removes a community. pub struct ModRemoveCommunity { - pub id: i32, + pub id: ModRemoveCommunityId, pub mod_person_id: PersonId, pub community_id: CommunityId, #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub removed: bool, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -151,7 +167,7 @@ pub struct ModRemoveCommunityForm { #[cfg_attr(feature = "full", ts(export))] /// When someone is banned from a community. pub struct ModBanFromCommunity { - pub id: i32, + pub id: ModBanFromCommunityId, pub mod_person_id: PersonId, pub other_person_id: PersonId, pub community_id: CommunityId, @@ -160,7 +176,7 @@ pub struct ModBanFromCommunity { pub banned: bool, #[cfg_attr(feature = "full", ts(optional))] pub expires: Option>, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -182,7 +198,7 @@ pub struct ModBanFromCommunityForm { #[cfg_attr(feature = "full", ts(export))] /// When someone is banned from the site. pub struct ModBan { - pub id: i32, + pub id: ModBanId, pub mod_person_id: PersonId, pub other_person_id: PersonId, #[cfg_attr(feature = "full", ts(optional))] @@ -190,7 +206,7 @@ pub struct ModBan { pub banned: bool, #[cfg_attr(feature = "full", ts(optional))] pub expires: Option>, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -210,10 +226,10 @@ pub struct ModHideCommunityForm { #[cfg_attr(feature = "full", ts(export))] /// When a community is hidden from public view. pub struct ModHideCommunity { - pub id: i32, + pub id: ModHideCommunityId, pub community_id: CommunityId, pub mod_person_id: PersonId, - pub when_: DateTime, + pub published: DateTime, #[cfg_attr(feature = "full", ts(optional))] pub reason: Option, pub hidden: bool, @@ -236,12 +252,12 @@ pub struct ModBanForm { #[cfg_attr(feature = "full", ts(export))] /// When someone is added as a community moderator. pub struct ModAddCommunity { - pub id: i32, + pub id: ModAddCommunityId, pub mod_person_id: PersonId, pub other_person_id: PersonId, pub community_id: CommunityId, pub removed: bool, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -260,11 +276,11 @@ pub struct ModAddCommunityForm { #[cfg_attr(feature = "full", ts(export))] /// When a moderator transfers a community to a new owner. pub struct ModTransferCommunity { - pub id: i32, + pub id: ModTransferCommunityId, pub mod_person_id: PersonId, pub other_person_id: PersonId, pub community_id: CommunityId, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] @@ -282,11 +298,11 @@ pub struct ModTransferCommunityForm { #[cfg_attr(feature = "full", ts(export))] /// When someone is added as a site moderator. pub struct ModAdd { - pub id: i32, + pub id: ModAddId, pub mod_person_id: PersonId, pub other_person_id: PersonId, pub removed: bool, - pub when_: DateTime, + pub published: DateTime, } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] diff --git a/crates/db_views/src/lib.rs b/crates/db_views/src/lib.rs index c1d5ada60..829870c0a 100644 --- a/crates/db_views/src/lib.rs +++ b/crates/db_views/src/lib.rs @@ -34,10 +34,3 @@ pub mod site_view; pub mod structs; #[cfg(feature = "full")] pub mod vote_view; - -pub trait InternalToCombinedView { - type CombinedView; - - /// Maps the combined DB row to an enum - fn map_to_enum(&self) -> Option; -} diff --git a/crates/db_views/src/person_content_combined_view.rs b/crates/db_views/src/person_content_combined_view.rs index c06712df2..b20447b98 100644 --- a/crates/db_views/src/person_content_combined_view.rs +++ b/crates/db_views/src/person_content_combined_view.rs @@ -1,13 +1,10 @@ -use crate::{ - structs::{ - CommentView, - LocalUserView, - PersonContentCombinedPaginationCursor, - PersonContentCombinedView, - PersonContentViewInternal, - PostView, - }, - InternalToCombinedView, +use crate::structs::{ + CommentView, + LocalUserView, + PersonContentCombinedPaginationCursor, + PersonContentCombinedView, + PersonContentViewInternal, + PostView, }; use diesel::{ result::Error, @@ -45,6 +42,7 @@ use lemmy_db_schema::{ community::CommunityFollower, }, utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool}, + InternalToCombinedView, PersonContentType, }; use lemmy_utils::error::LemmyResult; @@ -240,7 +238,10 @@ impl PersonContentCombinedQuery { let res = query.load::(conn).await?; // Map the query results to the enum - let out = res.into_iter().filter_map(|u| u.map_to_enum()).collect(); + let out = res + .into_iter() + .filter_map(InternalToCombinedView::map_to_enum) + .collect(); Ok(out) } @@ -249,9 +250,9 @@ impl PersonContentCombinedQuery { impl InternalToCombinedView for PersonContentViewInternal { type CombinedView = PersonContentCombinedView; - fn map_to_enum(&self) -> Option { + fn map_to_enum(self) -> Option { // Use for a short alias - let v = self.clone(); + let v = self; if let (Some(comment), Some(counts)) = (v.comment, v.comment_counts) { Some(PersonContentCombinedView::Comment(CommentView { diff --git a/crates/db_views/src/person_saved_combined_view.rs b/crates/db_views/src/person_saved_combined_view.rs index 45879c1f5..4e1ce8df7 100644 --- a/crates/db_views/src/person_saved_combined_view.rs +++ b/crates/db_views/src/person_saved_combined_view.rs @@ -1,11 +1,8 @@ -use crate::{ - structs::{ - LocalUserView, - PersonContentCombinedView, - PersonContentViewInternal, - PersonSavedCombinedPaginationCursor, - }, - InternalToCombinedView, +use crate::structs::{ + LocalUserView, + PersonContentCombinedView, + PersonContentViewInternal, + PersonSavedCombinedPaginationCursor, }; use diesel::{ result::Error, @@ -42,6 +39,7 @@ use lemmy_db_schema::{ community::CommunityFollower, }, utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool}, + InternalToCombinedView, PersonContentType, }; use lemmy_utils::error::LemmyResult; @@ -241,7 +239,10 @@ impl PersonSavedCombinedQuery { let res = query.load::(conn).await?; // Map the query results to the enum - let out = res.into_iter().filter_map(|u| u.map_to_enum()).collect(); + let out = res + .into_iter() + .filter_map(InternalToCombinedView::map_to_enum) + .collect(); Ok(out) } diff --git a/crates/db_views/src/report_combined_view.rs b/crates/db_views/src/report_combined_view.rs index 520a8c057..c082d7f74 100644 --- a/crates/db_views/src/report_combined_view.rs +++ b/crates/db_views/src/report_combined_view.rs @@ -1,15 +1,12 @@ -use crate::{ - structs::{ - CommentReportView, - CommunityReportView, - LocalUserView, - PostReportView, - PrivateMessageReportView, - ReportCombinedPaginationCursor, - ReportCombinedView, - ReportCombinedViewInternal, - }, - InternalToCombinedView, +use crate::structs::{ + CommentReportView, + CommunityReportView, + LocalUserView, + PostReportView, + PrivateMessageReportView, + ReportCombinedPaginationCursor, + ReportCombinedView, + ReportCombinedViewInternal, }; use diesel::{ result::Error, @@ -51,6 +48,7 @@ use lemmy_db_schema::{ community::CommunityFollower, }, utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool, ReverseTimestampKey}, + InternalToCombinedView, }; use lemmy_utils::error::LemmyResult; @@ -363,7 +361,10 @@ impl ReportCombinedQuery { let res = query.load::(conn).await?; // Map the query results to the enum - let out = res.into_iter().filter_map(|u| u.map_to_enum()).collect(); + let out = res + .into_iter() + .filter_map(InternalToCombinedView::map_to_enum) + .collect(); Ok(out) } @@ -372,9 +373,9 @@ impl ReportCombinedQuery { impl InternalToCombinedView for ReportCombinedViewInternal { type CombinedView = ReportCombinedView; - fn map_to_enum(&self) -> Option { + fn map_to_enum(self) -> Option { // Use for a short alias - let v = self.clone(); + let v = self; if let ( Some(post_report), diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs index 434cf7dac..325fbabd0 100644 --- a/crates/db_views/src/structs.rs +++ b/crates/db_views/src/structs.rs @@ -148,25 +148,25 @@ pub struct PostReportView { /// prevent ossification (api users love to make assumptions (e.g. parse stuff that looks like /// numbers as numbers) about apis that aren't part of the spec #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "full", derive(ts_rs::TS))] +#[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] pub struct PaginationCursor(pub String); /// like PaginationCursor but for the report_combined table #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "full", derive(ts_rs::TS))] +#[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] pub struct ReportCombinedPaginationCursor(pub String); /// like PaginationCursor but for the person_content_combined table #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "full", derive(ts_rs::TS))] +#[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] pub struct PersonContentCombinedPaginationCursor(pub String); /// like PaginationCursor but for the person_saved_combined table #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "full", derive(ts_rs::TS))] +#[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] pub struct PersonSavedCombinedPaginationCursor(pub String); diff --git a/crates/db_views_moderator/Cargo.toml b/crates/db_views_moderator/Cargo.toml index ae598fa32..a7257c4f1 100644 --- a/crates/db_views_moderator/Cargo.toml +++ b/crates/db_views_moderator/Cargo.toml @@ -15,10 +15,19 @@ doctest = false workspace = true [features] -full = ["lemmy_db_schema/full", "diesel", "diesel-async", "ts-rs"] +full = [ + "lemmy_db_schema/full", + "lemmy_utils", + "i-love-jesus", + "diesel", + "diesel-async", + "ts-rs", +] [dependencies] lemmy_db_schema = { workspace = true } +lemmy_utils = { workspace = true, optional = true } +i-love-jesus = { workspace = true, optional = true } diesel = { workspace = true, features = [ "chrono", "postgres", @@ -31,3 +40,8 @@ diesel-async = { workspace = true, features = [ serde = { workspace = true } serde_with = { workspace = true } ts-rs = { workspace = true, optional = true } + +[dev-dependencies] +serial_test = { workspace = true } +tokio = { workspace = true } +pretty_assertions = { workspace = true } diff --git a/crates/db_views_moderator/src/admin_allow_instance.rs b/crates/db_views_moderator/src/admin_allow_instance.rs deleted file mode 100644 index 2a0aaad14..000000000 --- a/crates/db_views_moderator/src/admin_allow_instance.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::structs::{AdminAllowInstanceView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{admin_allow_instance, instance, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl AdminAllowInstanceView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = admin_allow_instance::admin_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = admin_allow_instance::table - .left_join(person::table.on(admin_names_join)) - .inner_join(instance::table) - .select(( - admin_allow_instance::all_columns, - instance::all_columns, - person::all_columns.nullable(), - )) - .into_boxed(); - - if let Some(admin_person_id) = params.mod_person_id { - query = query.filter(admin_allow_instance::admin_person_id.eq(admin_person_id)); - }; - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(admin_allow_instance::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/admin_block_instance.rs b/crates/db_views_moderator/src/admin_block_instance.rs deleted file mode 100644 index e9d7c8b0d..000000000 --- a/crates/db_views_moderator/src/admin_block_instance.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::structs::{AdminBlockInstanceView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{admin_block_instance, instance, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl AdminBlockInstanceView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = admin_block_instance::admin_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = admin_block_instance::table - .left_join(person::table.on(admin_names_join)) - .inner_join(instance::table) - .select(( - admin_block_instance::all_columns, - instance::all_columns, - person::all_columns.nullable(), - )) - .into_boxed(); - - if let Some(admin_person_id) = params.mod_person_id { - query = query.filter(admin_block_instance::admin_person_id.eq(admin_person_id)); - }; - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(admin_block_instance::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/admin_purge_comment_view.rs b/crates/db_views_moderator/src/admin_purge_comment_view.rs deleted file mode 100644 index 4c650b6fa..000000000 --- a/crates/db_views_moderator/src/admin_purge_comment_view.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::structs::{AdminPurgeCommentView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{admin_purge_comment, person, post}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl AdminPurgeCommentView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = admin_purge_comment::admin_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - - let mut query = admin_purge_comment::table - .left_join(person::table.on(admin_names_join)) - .inner_join(post::table) - .select(( - admin_purge_comment::all_columns, - person::all_columns.nullable(), - post::all_columns, - )) - .into_boxed(); - - if let Some(admin_person_id) = params.mod_person_id { - query = query.filter(admin_purge_comment::admin_person_id.eq(admin_person_id)); - }; - - // If a post or comment ID is given, then don't find any results - if params.post_id.is_some() || params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(admin_purge_comment::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/admin_purge_community_view.rs b/crates/db_views_moderator/src/admin_purge_community_view.rs deleted file mode 100644 index 5eadb8985..000000000 --- a/crates/db_views_moderator/src/admin_purge_community_view.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::structs::{AdminPurgeCommunityView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{admin_purge_community, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl AdminPurgeCommunityView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = admin_purge_community::admin_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - - let mut query = admin_purge_community::table - .left_join(person::table.on(admin_names_join)) - .select(( - admin_purge_community::all_columns, - person::all_columns.nullable(), - )) - .into_boxed(); - - if let Some(admin_person_id) = params.mod_person_id { - query = query.filter(admin_purge_community::admin_person_id.eq(admin_person_id)); - }; - - // If a post or comment ID is given, then don't find any results - if params.post_id.is_some() || params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(admin_purge_community::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/admin_purge_person_view.rs b/crates/db_views_moderator/src/admin_purge_person_view.rs deleted file mode 100644 index b6dd834c5..000000000 --- a/crates/db_views_moderator/src/admin_purge_person_view.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::structs::{AdminPurgePersonView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{admin_purge_person, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl AdminPurgePersonView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = admin_purge_person::admin_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = admin_purge_person::table - .left_join(person::table.on(admin_names_join)) - .select(( - admin_purge_person::all_columns, - person::all_columns.nullable(), - )) - .into_boxed(); - - if let Some(admin_person_id) = params.mod_person_id { - query = query.filter(admin_purge_person::admin_person_id.eq(admin_person_id)); - }; - - // If a post or comment ID is given, then don't find any results - if params.post_id.is_some() || params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(admin_purge_person::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/admin_purge_post_view.rs b/crates/db_views_moderator/src/admin_purge_post_view.rs deleted file mode 100644 index b77493c25..000000000 --- a/crates/db_views_moderator/src/admin_purge_post_view.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::structs::{AdminPurgePostView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{admin_purge_post, community, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl AdminPurgePostView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = admin_purge_post::admin_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = admin_purge_post::table - .left_join(person::table.on(admin_names_join)) - .inner_join(community::table) - .select(( - admin_purge_post::all_columns, - person::all_columns.nullable(), - community::all_columns, - )) - .into_boxed(); - - if let Some(admin_person_id) = params.mod_person_id { - query = query.filter(admin_purge_post::admin_person_id.eq(admin_person_id)); - }; - - // If a post or comment ID is given, then don't find any results - if params.post_id.is_some() || params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(admin_purge_post::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/lib.rs b/crates/db_views_moderator/src/lib.rs index 5748707c6..1cc21da27 100644 --- a/crates/db_views_moderator/src/lib.rs +++ b/crates/db_views_moderator/src/lib.rs @@ -1,35 +1,3 @@ #[cfg(feature = "full")] -pub mod admin_allow_instance; -#[cfg(feature = "full")] -pub mod admin_block_instance; -#[cfg(feature = "full")] -pub mod admin_purge_comment_view; -#[cfg(feature = "full")] -pub mod admin_purge_community_view; -#[cfg(feature = "full")] -pub mod admin_purge_person_view; -#[cfg(feature = "full")] -pub mod admin_purge_post_view; -#[cfg(feature = "full")] -pub mod mod_add_community_view; -#[cfg(feature = "full")] -pub mod mod_add_view; -#[cfg(feature = "full")] -pub mod mod_ban_from_community_view; -#[cfg(feature = "full")] -pub mod mod_ban_view; -#[cfg(feature = "full")] -pub mod mod_feature_post_view; -#[cfg(feature = "full")] -pub mod mod_hide_community_view; -#[cfg(feature = "full")] -pub mod mod_lock_post_view; -#[cfg(feature = "full")] -pub mod mod_remove_comment_view; -#[cfg(feature = "full")] -pub mod mod_remove_community_view; -#[cfg(feature = "full")] -pub mod mod_remove_post_view; -#[cfg(feature = "full")] -pub mod mod_transfer_community_view; +pub mod modlog_combined_view; pub mod structs; diff --git a/crates/db_views_moderator/src/mod_add_community_view.rs b/crates/db_views_moderator/src/mod_add_community_view.rs deleted file mode 100644 index 1068aba75..000000000 --- a/crates/db_views_moderator/src/mod_add_community_view.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::structs::{ModAddCommunityView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{community, mod_add_community, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl ModAddCommunityView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - let person_alias_1 = diesel::alias!(person as person1); - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = mod_add_community::mod_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = mod_add_community::table - .left_join(person::table.on(admin_names_join)) - .inner_join(community::table) - .inner_join( - person_alias_1.on(mod_add_community::other_person_id.eq(person_alias_1.field(person::id))), - ) - .select(( - mod_add_community::all_columns, - person::all_columns.nullable(), - community::all_columns, - person_alias_1.fields(person::all_columns), - )) - .into_boxed(); - - if let Some(mod_person_id) = params.mod_person_id { - query = query.filter(mod_add_community::mod_person_id.eq(mod_person_id)); - }; - - if let Some(community_id) = params.community_id { - query = query.filter(mod_add_community::community_id.eq(community_id)); - }; - - if let Some(other_person_id) = params.other_person_id { - query = query.filter(person_alias_1.field(person::id).eq(other_person_id)); - }; - - // If a post or comment ID is given, then don't find any results - if params.post_id.is_some() || params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(mod_add_community::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/mod_add_view.rs b/crates/db_views_moderator/src/mod_add_view.rs deleted file mode 100644 index c5612c4ad..000000000 --- a/crates/db_views_moderator/src/mod_add_view.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::structs::{ModAddView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{mod_add, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl ModAddView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - let person_alias_1 = diesel::alias!(person as person1); - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = mod_add::mod_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = mod_add::table - .left_join(person::table.on(admin_names_join)) - .inner_join(person_alias_1.on(mod_add::other_person_id.eq(person_alias_1.field(person::id)))) - .select(( - mod_add::all_columns, - person::all_columns.nullable(), - person_alias_1.fields(person::all_columns), - )) - .into_boxed(); - - if let Some(mod_person_id) = params.mod_person_id { - query = query.filter(mod_add::mod_person_id.eq(mod_person_id)); - }; - - if let Some(other_person_id) = params.other_person_id { - query = query.filter(person_alias_1.field(person::id).eq(other_person_id)); - }; - - // If a post or comment ID is given, then don't find any results - if params.post_id.is_some() || params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(mod_add::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/mod_ban_from_community_view.rs b/crates/db_views_moderator/src/mod_ban_from_community_view.rs deleted file mode 100644 index d2d6038f3..000000000 --- a/crates/db_views_moderator/src/mod_ban_from_community_view.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::structs::{ModBanFromCommunityView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{community, mod_ban_from_community, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl ModBanFromCommunityView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - - let person_alias_1 = diesel::alias!(person as person1); - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = mod_ban_from_community::mod_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = mod_ban_from_community::table - .left_join(person::table.on(admin_names_join)) - .inner_join(community::table) - .inner_join( - person_alias_1 - .on(mod_ban_from_community::other_person_id.eq(person_alias_1.field(person::id))), - ) - .select(( - mod_ban_from_community::all_columns, - person::all_columns.nullable(), - community::all_columns, - person_alias_1.fields(person::all_columns), - )) - .into_boxed(); - - if let Some(mod_person_id) = params.mod_person_id { - query = query.filter(mod_ban_from_community::mod_person_id.eq(mod_person_id)); - }; - - if let Some(community_id) = params.community_id { - query = query.filter(mod_ban_from_community::community_id.eq(community_id)); - }; - - if let Some(other_person_id) = params.other_person_id { - query = query.filter(mod_ban_from_community::other_person_id.eq(other_person_id)); - }; - - // If a post or comment ID is given, then don't find any results - if params.post_id.is_some() || params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(mod_ban_from_community::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/mod_ban_view.rs b/crates/db_views_moderator/src/mod_ban_view.rs deleted file mode 100644 index ca0723e83..000000000 --- a/crates/db_views_moderator/src/mod_ban_view.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::structs::{ModBanView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{mod_ban, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl ModBanView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - let person_alias_1 = diesel::alias!(person as person1); - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = mod_ban::mod_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = mod_ban::table - .left_join(person::table.on(admin_names_join)) - .inner_join(person_alias_1.on(mod_ban::other_person_id.eq(person_alias_1.field(person::id)))) - .select(( - mod_ban::all_columns, - person::all_columns.nullable(), - person_alias_1.fields(person::all_columns), - )) - .into_boxed(); - - if let Some(mod_person_id) = params.mod_person_id { - query = query.filter(mod_ban::mod_person_id.eq(mod_person_id)); - }; - - if let Some(other_person_id) = params.other_person_id { - query = query.filter(person_alias_1.field(person::id).eq(other_person_id)); - }; - - // If a post or comment ID is given, then don't find any results - if params.post_id.is_some() || params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(mod_ban::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/mod_feature_post_view.rs b/crates/db_views_moderator/src/mod_feature_post_view.rs deleted file mode 100644 index 4c0fdb4f7..000000000 --- a/crates/db_views_moderator/src/mod_feature_post_view.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::structs::{ModFeaturePostView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{community, mod_feature_post, person, post}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl ModFeaturePostView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - let person_alias_1 = diesel::alias!(person as person1); - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = mod_feature_post::mod_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = mod_feature_post::table - .left_join(person::table.on(admin_names_join)) - .inner_join(post::table) - .inner_join(person_alias_1.on(post::creator_id.eq(person_alias_1.field(person::id)))) - .inner_join(community::table.on(post::community_id.eq(community::id))) - .select(( - mod_feature_post::all_columns, - person::all_columns.nullable(), - post::all_columns, - community::all_columns, - )) - .into_boxed(); - - if let Some(community_id) = params.community_id { - query = query.filter(post::community_id.eq(community_id)); - }; - - if let Some(mod_person_id) = params.mod_person_id { - query = query.filter(mod_feature_post::mod_person_id.eq(mod_person_id)); - }; - - if let Some(other_person_id) = params.other_person_id { - query = query.filter(person_alias_1.field(person::id).eq(other_person_id)); - }; - - if let Some(post_id) = params.post_id { - query = query.filter(post::id.eq(post_id)); - } - - // If a comment ID is given, then don't find any results - if params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(mod_feature_post::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/mod_hide_community_view.rs b/crates/db_views_moderator/src/mod_hide_community_view.rs deleted file mode 100644 index 3c8a7e627..000000000 --- a/crates/db_views_moderator/src/mod_hide_community_view.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::structs::{ModHideCommunityView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{community, mod_hide_community, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl ModHideCommunityView { - // Pass in mod_id as admin_id because only admins can do this action - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = mod_hide_community::mod_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = mod_hide_community::table - .left_join(person::table.on(admin_names_join)) - .inner_join(community::table.on(mod_hide_community::community_id.eq(community::id))) - .select(( - mod_hide_community::all_columns, - person::all_columns.nullable(), - community::all_columns, - )) - .into_boxed(); - - if let Some(community_id) = params.community_id { - query = query.filter(mod_hide_community::community_id.eq(community_id)); - }; - - if let Some(admin_id) = params.mod_person_id { - query = query.filter(mod_hide_community::mod_person_id.eq(admin_id)); - }; - - // If a post or comment ID is given, then don't find any results - if params.post_id.is_some() || params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(mod_hide_community::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/mod_lock_post_view.rs b/crates/db_views_moderator/src/mod_lock_post_view.rs deleted file mode 100644 index 5a6c753d9..000000000 --- a/crates/db_views_moderator/src/mod_lock_post_view.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::structs::{ModLockPostView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{community, mod_lock_post, person, post}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl ModLockPostView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - - let person_alias_1 = diesel::alias!(person as person1); - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = mod_lock_post::mod_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = mod_lock_post::table - .left_join(person::table.on(admin_names_join)) - .inner_join(post::table) - .inner_join(community::table.on(post::community_id.eq(community::id))) - .inner_join(person_alias_1.on(post::creator_id.eq(person_alias_1.field(person::id)))) - .select(( - mod_lock_post::all_columns, - person::all_columns.nullable(), - post::all_columns, - community::all_columns, - )) - .into_boxed(); - - if let Some(community_id) = params.community_id { - query = query.filter(post::community_id.eq(community_id)); - }; - - if let Some(mod_person_id) = params.mod_person_id { - query = query.filter(mod_lock_post::mod_person_id.eq(mod_person_id)); - }; - - if let Some(other_person_id) = params.other_person_id { - query = query.filter(person_alias_1.field(person::id).eq(other_person_id)); - }; - - if let Some(post_id) = params.post_id { - query = query.filter(post::id.eq(post_id)); - } - - // If a comment ID is given, then don't find any results - if params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(mod_lock_post::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/mod_remove_comment_view.rs b/crates/db_views_moderator/src/mod_remove_comment_view.rs deleted file mode 100644 index cf0ed325c..000000000 --- a/crates/db_views_moderator/src/mod_remove_comment_view.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::structs::{ModRemoveCommentView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{comment, community, mod_remove_comment, person, post}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl ModRemoveCommentView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - let person_alias_1 = diesel::alias!(lemmy_db_schema::schema::person as person1); - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = mod_remove_comment::mod_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = mod_remove_comment::table - .left_join(person::table.on(admin_names_join)) - .inner_join(comment::table) - .inner_join(person_alias_1.on(comment::creator_id.eq(person_alias_1.field(person::id)))) - .inner_join(post::table.on(comment::post_id.eq(post::id))) - .inner_join(community::table.on(post::community_id.eq(community::id))) - .select(( - mod_remove_comment::all_columns, - person::all_columns.nullable(), - comment::all_columns, - person_alias_1.fields(person::all_columns), - post::all_columns, - community::all_columns, - )) - .into_boxed(); - - if let Some(community_id) = params.community_id { - query = query.filter(post::community_id.eq(community_id)); - }; - - if let Some(mod_person_id) = params.mod_person_id { - query = query.filter(mod_remove_comment::mod_person_id.eq(mod_person_id)); - }; - - if let Some(other_person_id) = params.other_person_id { - query = query.filter(person_alias_1.field(person::id).eq(other_person_id)); - }; - - if let Some(comment_id) = params.comment_id { - query = query.filter(comment::id.eq(comment_id)); - } - - // If a post ID is given, then don't find any results - if params.post_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(mod_remove_comment::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/mod_remove_community_view.rs b/crates/db_views_moderator/src/mod_remove_community_view.rs deleted file mode 100644 index ac620ebdb..000000000 --- a/crates/db_views_moderator/src/mod_remove_community_view.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::structs::{ModRemoveCommunityView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{community, mod_remove_community, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl ModRemoveCommunityView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = mod_remove_community::mod_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = mod_remove_community::table - .left_join(person::table.on(admin_names_join)) - .inner_join(community::table) - .select(( - mod_remove_community::all_columns, - person::all_columns.nullable(), - community::all_columns, - )) - .into_boxed(); - - if let Some(mod_person_id) = params.mod_person_id { - query = query.filter(mod_remove_community::mod_person_id.eq(mod_person_id)); - }; - - // If a post or comment ID is given, then don't find any results - if params.post_id.is_some() || params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(mod_remove_community::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/mod_remove_post_view.rs b/crates/db_views_moderator/src/mod_remove_post_view.rs deleted file mode 100644 index 98504a8e7..000000000 --- a/crates/db_views_moderator/src/mod_remove_post_view.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::structs::{ModRemovePostView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{community, mod_remove_post, person, post}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl ModRemovePostView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - - let person_alias_1 = diesel::alias!(person as person1); - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = mod_remove_post::mod_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = mod_remove_post::table - .left_join(person::table.on(admin_names_join)) - .inner_join(post::table) - .inner_join(community::table.on(post::community_id.eq(community::id))) - .inner_join(person_alias_1.on(post::creator_id.eq(person_alias_1.field(person::id)))) - .select(( - mod_remove_post::all_columns, - person::all_columns.nullable(), - post::all_columns, - community::all_columns, - )) - .into_boxed(); - - if let Some(community_id) = params.community_id { - query = query.filter(post::community_id.eq(community_id)); - }; - - if let Some(mod_person_id) = params.mod_person_id { - query = query.filter(mod_remove_post::mod_person_id.eq(mod_person_id)); - }; - - if let Some(other_person_id) = params.other_person_id { - query = query.filter(person_alias_1.field(person::id).eq(other_person_id)); - }; - - if let Some(post_id) = params.post_id { - query = query.filter(post::id.eq(post_id)); - } - - // If a comment ID is given, then don't find any results - if params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(mod_remove_post::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/mod_transfer_community_view.rs b/crates/db_views_moderator/src/mod_transfer_community_view.rs deleted file mode 100644 index 6d62d347a..000000000 --- a/crates/db_views_moderator/src/mod_transfer_community_view.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::structs::{ModTransferCommunityView, ModlogListParams}; -use diesel::{ - result::Error, - BoolExpressionMethods, - ExpressionMethods, - IntoSql, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_db_schema::{ - newtypes::PersonId, - schema::{community, mod_transfer_community, person}, - utils::{get_conn, limit_and_offset, DbPool}, -}; - -impl ModTransferCommunityView { - pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - - let person_alias_1 = diesel::alias!(person as person1); - let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); - let show_mod_names = !params.hide_modlog_names; - let show_mod_names_expr = show_mod_names.as_sql::(); - - let admin_names_join = mod_transfer_community::mod_person_id - .eq(person::id) - .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); - let mut query = mod_transfer_community::table - .left_join(person::table.on(admin_names_join)) - .inner_join(community::table) - .inner_join( - person_alias_1 - .on(mod_transfer_community::other_person_id.eq(person_alias_1.field(person::id))), - ) - .select(( - mod_transfer_community::all_columns, - person::all_columns.nullable(), - community::all_columns, - person_alias_1.fields(person::all_columns), - )) - .into_boxed(); - - if let Some(mod_person_id) = params.mod_person_id { - query = query.filter(mod_transfer_community::mod_person_id.eq(mod_person_id)); - }; - - if let Some(community_id) = params.community_id { - query = query.filter(mod_transfer_community::community_id.eq(community_id)); - }; - - if let Some(other_person_id) = params.other_person_id { - query = query.filter(person_alias_1.field(person::id).eq(other_person_id)); - }; - - // If a post or comment ID is given, then don't find any results - if params.post_id.is_some() || params.comment_id.is_some() { - return Ok(vec![]); - } - - let (limit, offset) = limit_and_offset(params.page, params.limit)?; - - query - .limit(limit) - .offset(offset) - .order_by(mod_transfer_community::when_.desc()) - .load::(conn) - .await - } -} diff --git a/crates/db_views_moderator/src/modlog_combined_view.rs b/crates/db_views_moderator/src/modlog_combined_view.rs new file mode 100644 index 000000000..4c6e619ea --- /dev/null +++ b/crates/db_views_moderator/src/modlog_combined_view.rs @@ -0,0 +1,1388 @@ +use crate::structs::{ + AdminAllowInstanceView, + AdminBlockInstanceView, + AdminPurgeCommentView, + AdminPurgeCommunityView, + AdminPurgePersonView, + AdminPurgePostView, + ModAddCommunityView, + ModAddView, + ModBanFromCommunityView, + ModBanView, + ModFeaturePostView, + ModHideCommunityView, + ModLockPostView, + ModRemoveCommentView, + ModRemoveCommunityView, + ModRemovePostView, + ModTransferCommunityView, + ModlogCombinedPaginationCursor, + ModlogCombinedView, + ModlogCombinedViewInternal, +}; +use diesel::{ + result::Error, + BoolExpressionMethods, + ExpressionMethods, + IntoSql, + JoinOnDsl, + NullableExpressionMethods, + QueryDsl, + SelectableHelper, +}; +use diesel_async::RunQueryDsl; +use i_love_jesus::PaginatedQueryBuilder; +use lemmy_db_schema::{ + aliases, + newtypes::{CommentId, CommunityId, PersonId, PostId}, + schema::{ + admin_allow_instance, + admin_block_instance, + admin_purge_comment, + admin_purge_community, + admin_purge_person, + admin_purge_post, + comment, + community, + instance, + mod_add, + mod_add_community, + mod_ban, + mod_ban_from_community, + mod_feature_post, + mod_hide_community, + mod_lock_post, + mod_remove_comment, + mod_remove_community, + mod_remove_post, + mod_transfer_community, + modlog_combined, + person, + post, + }, + source::combined::modlog::{modlog_combined_keys as key, ModlogCombined}, + utils::{get_conn, DbPool}, + InternalToCombinedView, + ModlogActionType, +}; +use lemmy_utils::error::LemmyResult; + +impl ModlogCombinedPaginationCursor { + // get cursor for page that starts immediately after the given post + pub fn after_post(view: &ModlogCombinedView) -> ModlogCombinedPaginationCursor { + let (prefix, id) = match view { + ModlogCombinedView::AdminAllowInstance(v) => { + ("AdminAllowInstance", v.admin_allow_instance.id.0) + } + ModlogCombinedView::AdminBlockInstance(v) => { + ("AdminBlockInstance", v.admin_block_instance.id.0) + } + ModlogCombinedView::AdminPurgeComment(v) => ("AdminPurgeComment", v.admin_purge_comment.id.0), + ModlogCombinedView::AdminPurgeCommunity(v) => { + ("AdminPurgeCommunity", v.admin_purge_community.id.0) + } + ModlogCombinedView::AdminPurgePerson(v) => ("AdminPurgePerson", v.admin_purge_person.id.0), + ModlogCombinedView::AdminPurgePost(v) => ("AdminPurgePost", v.admin_purge_post.id.0), + ModlogCombinedView::ModAdd(v) => ("ModAdd", v.mod_add.id.0), + ModlogCombinedView::ModAddCommunity(v) => ("ModAddCommunity", v.mod_add_community.id.0), + ModlogCombinedView::ModBan(v) => ("ModBan", v.mod_ban.id.0), + ModlogCombinedView::ModBanFromCommunity(v) => { + ("ModBanFromCommunity", v.mod_ban_from_community.id.0) + } + ModlogCombinedView::ModFeaturePost(v) => ("ModFeaturePost", v.mod_feature_post.id.0), + ModlogCombinedView::ModHideCommunity(v) => ("ModHideCommunity", v.mod_hide_community.id.0), + ModlogCombinedView::ModLockPost(v) => ("ModLockPost", v.mod_lock_post.id.0), + ModlogCombinedView::ModRemoveComment(v) => ("ModRemoveComment", v.mod_remove_comment.id.0), + ModlogCombinedView::ModRemoveCommunity(v) => { + ("ModRemoveCommunity", v.mod_remove_community.id.0) + } + ModlogCombinedView::ModRemovePost(v) => ("ModRemovePost", v.mod_remove_post.id.0), + ModlogCombinedView::ModTransferCommunity(v) => { + ("ModTransferCommunity", v.mod_transfer_community.id.0) + } + }; + // hex encoding to prevent ossification + ModlogCombinedPaginationCursor(format!("{prefix}-{id:x}")) + } + + pub async fn read(&self, pool: &mut DbPool<'_>) -> Result { + let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into()); + let mut query = modlog_combined::table + .select(ModlogCombined::as_select()) + .into_boxed(); + let (prefix, id_str) = self.0.split_once('-').ok_or_else(err_msg)?; + let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?; + query = match prefix { + "AdminAllowInstance" => query.filter(modlog_combined::admin_allow_instance_id.eq(id)), + "AdminBlockInstance" => query.filter(modlog_combined::admin_block_instance_id.eq(id)), + "AdminPurgeComment" => query.filter(modlog_combined::admin_purge_comment_id.eq(id)), + "AdminPurgeCommunity" => query.filter(modlog_combined::admin_purge_community_id.eq(id)), + "AdminPurgePerson" => query.filter(modlog_combined::admin_purge_person_id.eq(id)), + "AdminPurgePost" => query.filter(modlog_combined::admin_purge_post_id.eq(id)), + "ModAdd" => query.filter(modlog_combined::mod_add_id.eq(id)), + "ModAddCommunity" => query.filter(modlog_combined::mod_add_community_id.eq(id)), + "ModBan" => query.filter(modlog_combined::mod_ban_id.eq(id)), + "ModBanFromCommunity" => query.filter(modlog_combined::mod_ban_from_community_id.eq(id)), + "ModFeaturePost" => query.filter(modlog_combined::mod_feature_post_id.eq(id)), + "ModHideCommunity" => query.filter(modlog_combined::mod_hide_community_id.eq(id)), + "ModLockPost" => query.filter(modlog_combined::mod_lock_post_id.eq(id)), + "ModRemoveComment" => query.filter(modlog_combined::mod_remove_comment_id.eq(id)), + "ModRemoveCommunity" => query.filter(modlog_combined::mod_remove_community_id.eq(id)), + "ModRemovePost" => query.filter(modlog_combined::mod_remove_post_id.eq(id)), + "ModTransferCommunity" => query.filter(modlog_combined::mod_transfer_community_id.eq(id)), + + _ => return Err(err_msg()), + }; + let token = query.first(&mut get_conn(pool).await?).await?; + + Ok(PaginationCursorData(token)) + } +} + +#[derive(Clone)] +pub struct PaginationCursorData(ModlogCombined); + +#[derive(Default)] +/// Querying / filtering the modlog. +pub struct ModlogCombinedQuery { + pub type_: Option, + pub comment_id: Option, + pub post_id: Option, + pub community_id: Option, + pub hide_modlog_names: Option, + pub mod_person_id: Option, + pub other_person_id: Option, + pub page_after: Option, + pub page_back: Option, +} + +impl ModlogCombinedQuery { + pub async fn list(self, pool: &mut DbPool<'_>) -> LemmyResult> { + let conn = &mut get_conn(pool).await?; + + let mod_person = self.mod_person_id.unwrap_or(PersonId(-1)); + let show_mod_names = !(self.hide_modlog_names.unwrap_or_default()); + let show_mod_names_expr = show_mod_names.as_sql::(); + + // The modded / other person + let other_person = aliases::person1.field(person::id); + + // The query for the admin / mod person + // It needs an OR condition to every mod table + // After this you can use person::id to refer to the moderator + let moderator_names_join = show_mod_names_expr.or(person::id.eq(mod_person)).and( + admin_allow_instance::admin_person_id + .eq(person::id) + .or(admin_block_instance::admin_person_id.eq(person::id)) + .or(admin_purge_comment::admin_person_id.eq(person::id)) + .or(admin_purge_community::admin_person_id.eq(person::id)) + .or(admin_purge_person::admin_person_id.eq(person::id)) + .or(admin_purge_post::admin_person_id.eq(person::id)) + .or(mod_add::mod_person_id.eq(person::id)) + .or(mod_add_community::mod_person_id.eq(person::id)) + .or(mod_ban::mod_person_id.eq(person::id)) + .or(mod_ban_from_community::mod_person_id.eq(person::id)) + .or(mod_feature_post::mod_person_id.eq(person::id)) + .or(mod_hide_community::mod_person_id.eq(person::id)) + .or(mod_lock_post::mod_person_id.eq(person::id)) + .or(mod_remove_comment::mod_person_id.eq(person::id)) + .or(mod_remove_community::mod_person_id.eq(person::id)) + .or(mod_remove_post::mod_person_id.eq(person::id)) + .or(mod_transfer_community::mod_person_id.eq(person::id)), + ); + + let other_person_join = mod_add::other_person_id + .eq(other_person) + .or(mod_add_community::other_person_id.eq(other_person)) + .or(mod_ban::other_person_id.eq(other_person)) + .or(mod_ban_from_community::other_person_id.eq(other_person)) + // Some tables don't have the other_person_id directly, so you need to join + .or( + mod_feature_post::id + .is_not_null() + .and(post::creator_id.eq(other_person)), + ) + .or( + mod_lock_post::id + .is_not_null() + .and(post::creator_id.eq(other_person)), + ) + .or( + mod_remove_comment::id + .is_not_null() + .and(comment::creator_id.eq(other_person)), + ) + .or( + mod_remove_post::id + .is_not_null() + .and(post::creator_id.eq(other_person)), + ) + .or(mod_transfer_community::other_person_id.eq(other_person)); + + let comment_join = mod_remove_comment::comment_id.eq(comment::id); + + let post_join = admin_purge_comment::post_id + .eq(post::id) + .or(mod_feature_post::post_id.eq(post::id)) + .or(mod_lock_post::post_id.eq(post::id)) + .or( + mod_remove_comment::id + .is_not_null() + .and(comment::post_id.eq(post::id)), + ) + .or(mod_remove_post::post_id.eq(post::id)); + + let community_join = admin_purge_post::community_id + .eq(community::id) + .or(mod_add_community::community_id.eq(community::id)) + .or(mod_ban_from_community::community_id.eq(community::id)) + .or( + mod_feature_post::id + .is_not_null() + .and(post::community_id.eq(community::id)), + ) + .or(mod_hide_community::community_id.eq(community::id)) + .or( + mod_lock_post::id + .is_not_null() + .and(post::community_id.eq(community::id)), + ) + .or( + mod_remove_comment::id + .is_not_null() + .and(post::community_id.eq(community::id)), + ) + .or(mod_remove_community::community_id.eq(community::id)) + .or( + mod_remove_post::id + .is_not_null() + .and(post::community_id.eq(community::id)), + ) + .or(mod_transfer_community::community_id.eq(community::id)); + + let instance_join = admin_allow_instance::instance_id + .eq(instance::id) + .or(admin_block_instance::instance_id.eq(instance::id)); + + let mut query = modlog_combined::table + .left_join(admin_allow_instance::table) + .left_join(admin_block_instance::table) + .left_join(admin_purge_comment::table) + .left_join(admin_purge_community::table) + .left_join(admin_purge_person::table) + .left_join(admin_purge_post::table) + .left_join(mod_add::table) + .left_join(mod_add_community::table) + .left_join(mod_ban::table) + .left_join(mod_ban_from_community::table) + .left_join(mod_feature_post::table) + .left_join(mod_hide_community::table) + .left_join(mod_lock_post::table) + .left_join(mod_remove_comment::table) + .left_join(mod_remove_community::table) + .left_join(mod_remove_post::table) + .left_join(mod_transfer_community::table) + // The moderator + .left_join(person::table.on(moderator_names_join)) + // The comment + .left_join(comment::table.on(comment_join)) + // The post + .left_join(post::table.on(post_join)) + // The community + .left_join(community::table.on(community_join)) + // The instance + .left_join(instance::table.on(instance_join)) + // The other / modded person + .left_join(aliases::person1.on(other_person_join)) + .select(( + admin_allow_instance::all_columns.nullable(), + admin_block_instance::all_columns.nullable(), + admin_purge_comment::all_columns.nullable(), + admin_purge_community::all_columns.nullable(), + admin_purge_person::all_columns.nullable(), + admin_purge_post::all_columns.nullable(), + mod_add::all_columns.nullable(), + mod_add_community::all_columns.nullable(), + mod_ban::all_columns.nullable(), + mod_ban_from_community::all_columns.nullable(), + mod_feature_post::all_columns.nullable(), + mod_hide_community::all_columns.nullable(), + mod_lock_post::all_columns.nullable(), + mod_remove_comment::all_columns.nullable(), + mod_remove_community::all_columns.nullable(), + mod_remove_post::all_columns.nullable(), + mod_transfer_community::all_columns.nullable(), + // Shared + person::all_columns.nullable(), + aliases::person1.fields(person::all_columns).nullable(), + instance::all_columns.nullable(), + community::all_columns.nullable(), + post::all_columns.nullable(), + comment::all_columns.nullable(), + )) + .into_boxed(); + + if let Some(mod_person_id) = self.mod_person_id { + query = query.filter(person::id.eq(mod_person_id)); + }; + + if let Some(other_person_id) = self.other_person_id { + query = query.filter(other_person.eq(other_person_id)); + }; + + if let Some(community_id) = self.community_id { + query = query.filter(community::id.eq(community_id)) + } + + if let Some(post_id) = self.post_id { + query = query.filter(post::id.eq(post_id)) + } + + if let Some(comment_id) = self.comment_id { + query = query.filter(comment::id.eq(comment_id)) + } + + if let Some(type_) = self.type_ { + use lemmy_db_schema::ModlogActionType::*; + query = match type_ { + All => query, + ModRemovePost => query.filter(modlog_combined::mod_remove_post_id.is_not_null()), + ModLockPost => query.filter(modlog_combined::mod_lock_post_id.is_not_null()), + ModFeaturePost => query.filter(modlog_combined::mod_feature_post_id.is_not_null()), + ModRemoveComment => query.filter(modlog_combined::mod_remove_comment_id.is_not_null()), + ModRemoveCommunity => query.filter(modlog_combined::mod_remove_community_id.is_not_null()), + ModBanFromCommunity => { + query.filter(modlog_combined::mod_ban_from_community_id.is_not_null()) + } + ModAddCommunity => query.filter(modlog_combined::mod_add_community_id.is_not_null()), + ModTransferCommunity => { + query.filter(modlog_combined::mod_transfer_community_id.is_not_null()) + } + ModAdd => query.filter(modlog_combined::mod_add_id.is_not_null()), + ModBan => query.filter(modlog_combined::mod_ban_id.is_not_null()), + ModHideCommunity => query.filter(modlog_combined::mod_hide_community_id.is_not_null()), + AdminPurgePerson => query.filter(modlog_combined::admin_purge_person_id.is_not_null()), + AdminPurgeCommunity => { + query.filter(modlog_combined::admin_purge_community_id.is_not_null()) + } + AdminPurgePost => query.filter(modlog_combined::admin_purge_post_id.is_not_null()), + AdminPurgeComment => query.filter(modlog_combined::admin_purge_comment_id.is_not_null()), + AdminBlockInstance => query.filter(modlog_combined::admin_block_instance_id.is_not_null()), + AdminAllowInstance => query.filter(modlog_combined::admin_allow_instance_id.is_not_null()), + } + } + + let mut query = PaginatedQueryBuilder::new(query); + + let page_after = self.page_after.map(|c| c.0); + + if self.page_back.unwrap_or_default() { + query = query.before(page_after).limit_and_offset_from_end(); + } else { + query = query.after(page_after); + } + + query = query + .then_desc(key::published) + // Tie breaker + .then_desc(key::id); + + let res = query.load::(conn).await?; + + // Map the query results to the enum + let out = res + .into_iter() + .filter_map(InternalToCombinedView::map_to_enum) + .collect(); + + Ok(out) + } +} + +impl InternalToCombinedView for ModlogCombinedViewInternal { + type CombinedView = ModlogCombinedView; + + fn map_to_enum(self) -> Option { + // Use for a short alias + let v = self; + + if let (Some(admin_allow_instance), Some(instance)) = + (v.admin_allow_instance, v.instance.clone()) + { + Some(ModlogCombinedView::AdminAllowInstance( + AdminAllowInstanceView { + admin_allow_instance, + instance, + admin: v.moderator, + }, + )) + } else if let (Some(admin_block_instance), Some(instance)) = + (v.admin_block_instance, v.instance) + { + Some(ModlogCombinedView::AdminBlockInstance( + AdminBlockInstanceView { + admin_block_instance, + instance, + admin: v.moderator, + }, + )) + } else if let (Some(admin_purge_comment), Some(post)) = (v.admin_purge_comment, v.post.clone()) + { + Some(ModlogCombinedView::AdminPurgeComment( + AdminPurgeCommentView { + admin_purge_comment, + post, + admin: v.moderator, + }, + )) + } else if let Some(admin_purge_community) = v.admin_purge_community { + Some(ModlogCombinedView::AdminPurgeCommunity( + AdminPurgeCommunityView { + admin_purge_community, + admin: v.moderator, + }, + )) + } else if let Some(admin_purge_person) = v.admin_purge_person { + Some(ModlogCombinedView::AdminPurgePerson(AdminPurgePersonView { + admin_purge_person, + admin: v.moderator, + })) + } else if let (Some(admin_purge_post), Some(community)) = + (v.admin_purge_post, v.community.clone()) + { + Some(ModlogCombinedView::AdminPurgePost(AdminPurgePostView { + admin_purge_post, + admin: v.moderator, + community, + })) + } else if let (Some(mod_add), Some(other_person)) = (v.mod_add, v.other_person.clone()) { + Some(ModlogCombinedView::ModAdd(ModAddView { + mod_add, + moderator: v.moderator, + other_person, + })) + } else if let (Some(mod_add_community), Some(other_person), Some(community)) = ( + v.mod_add_community, + v.other_person.clone(), + v.community.clone(), + ) { + Some(ModlogCombinedView::ModAddCommunity(ModAddCommunityView { + mod_add_community, + moderator: v.moderator, + other_person, + community, + })) + } else if let (Some(mod_ban), Some(other_person)) = (v.mod_ban, v.other_person.clone()) { + Some(ModlogCombinedView::ModBan(ModBanView { + mod_ban, + moderator: v.moderator, + other_person, + })) + } else if let (Some(mod_ban_from_community), Some(other_person), Some(community)) = ( + v.mod_ban_from_community, + v.other_person.clone(), + v.community.clone(), + ) { + Some(ModlogCombinedView::ModBanFromCommunity( + ModBanFromCommunityView { + mod_ban_from_community, + moderator: v.moderator, + other_person, + community, + }, + )) + } else if let (Some(mod_feature_post), Some(other_person), Some(community), Some(post)) = ( + v.mod_feature_post, + v.other_person.clone(), + v.community.clone(), + v.post.clone(), + ) { + Some(ModlogCombinedView::ModFeaturePost(ModFeaturePostView { + mod_feature_post, + moderator: v.moderator, + other_person, + community, + post, + })) + } else if let (Some(mod_hide_community), Some(community)) = + (v.mod_hide_community, v.community.clone()) + { + Some(ModlogCombinedView::ModHideCommunity(ModHideCommunityView { + mod_hide_community, + admin: v.moderator, + community, + })) + } else if let (Some(mod_lock_post), Some(other_person), Some(community), Some(post)) = ( + v.mod_lock_post, + v.other_person.clone(), + v.community.clone(), + v.post.clone(), + ) { + Some(ModlogCombinedView::ModLockPost(ModLockPostView { + mod_lock_post, + moderator: v.moderator, + other_person, + community, + post, + })) + } else if let ( + Some(mod_remove_comment), + Some(other_person), + Some(community), + Some(post), + Some(comment), + ) = ( + v.mod_remove_comment, + v.other_person.clone(), + v.community.clone(), + v.post.clone(), + v.comment, + ) { + Some(ModlogCombinedView::ModRemoveComment(ModRemoveCommentView { + mod_remove_comment, + moderator: v.moderator, + other_person, + community, + post, + comment, + })) + } else if let (Some(mod_remove_community), Some(community)) = + (v.mod_remove_community, v.community.clone()) + { + Some(ModlogCombinedView::ModRemoveCommunity( + ModRemoveCommunityView { + mod_remove_community, + moderator: v.moderator, + community, + }, + )) + } else if let (Some(mod_remove_post), Some(other_person), Some(community), Some(post)) = ( + v.mod_remove_post, + v.other_person.clone(), + v.community.clone(), + v.post.clone(), + ) { + Some(ModlogCombinedView::ModRemovePost(ModRemovePostView { + mod_remove_post, + moderator: v.moderator, + other_person, + community, + post, + })) + } else if let (Some(mod_transfer_community), Some(other_person), Some(community)) = ( + v.mod_transfer_community, + v.other_person.clone(), + v.community.clone(), + ) { + Some(ModlogCombinedView::ModTransferCommunity( + ModTransferCommunityView { + mod_transfer_community, + moderator: v.moderator, + other_person, + community, + }, + )) + } else { + None + } + } +} + +#[cfg(test)] +#[expect(clippy::indexing_slicing)] +mod tests { + + use crate::{modlog_combined_view::ModlogCombinedQuery, structs::ModlogCombinedView}; + use lemmy_db_schema::{ + newtypes::PersonId, + source::{ + comment::{Comment, CommentInsertForm}, + community::{Community, CommunityInsertForm}, + instance::Instance, + mod_log::{ + admin::{ + AdminAllowInstance, + AdminAllowInstanceForm, + AdminBlockInstance, + AdminBlockInstanceForm, + AdminPurgeComment, + AdminPurgeCommentForm, + AdminPurgeCommunity, + AdminPurgeCommunityForm, + AdminPurgePerson, + AdminPurgePersonForm, + AdminPurgePost, + AdminPurgePostForm, + }, + moderator::{ + ModAdd, + ModAddCommunity, + ModAddCommunityForm, + ModAddForm, + ModBan, + ModBanForm, + ModBanFromCommunity, + ModBanFromCommunityForm, + ModFeaturePost, + ModFeaturePostForm, + ModHideCommunity, + ModHideCommunityForm, + ModLockPost, + ModLockPostForm, + ModRemoveComment, + ModRemoveCommentForm, + ModRemoveCommunity, + ModRemoveCommunityForm, + ModRemovePost, + ModRemovePostForm, + ModTransferCommunity, + ModTransferCommunityForm, + }, + }, + person::{Person, PersonInsertForm}, + post::{Post, PostInsertForm}, + }, + traits::Crud, + utils::{build_db_pool_for_tests, DbPool}, + ModlogActionType, + }; + use lemmy_utils::error::LemmyResult; + use pretty_assertions::assert_eq; + use serial_test::serial; + + struct Data { + instance: Instance, + timmy: Person, + sara: Person, + jessica: Person, + community: Community, + community_2: Community, + post: Post, + post_2: Post, + comment: Comment, + comment_2: Comment, + } + + async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult { + let instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; + + let timmy_form = PersonInsertForm::test_form(instance.id, "timmy_rcv"); + let timmy = Person::create(pool, &timmy_form).await?; + + let sara_form = PersonInsertForm::test_form(instance.id, "sara_rcv"); + let sara = Person::create(pool, &sara_form).await?; + + let jessica_form = PersonInsertForm::test_form(instance.id, "jessica_mrv"); + let jessica = Person::create(pool, &jessica_form).await?; + + let community_form = CommunityInsertForm::new( + instance.id, + "test community crv".to_string(), + "nada".to_owned(), + "pubkey".to_string(), + ); + let community = Community::create(pool, &community_form).await?; + + let community_form_2 = CommunityInsertForm::new( + instance.id, + "test community crv 2".to_string(), + "nada".to_owned(), + "pubkey".to_string(), + ); + let community_2 = Community::create(pool, &community_form_2).await?; + + let post_form = PostInsertForm::new("A test post crv".into(), timmy.id, community.id); + let post = Post::create(pool, &post_form).await?; + + let new_post_2 = PostInsertForm::new("A test post crv 2".into(), sara.id, community_2.id); + let post_2 = Post::create(pool, &new_post_2).await?; + + // Timmy creates a comment + let comment_form = CommentInsertForm::new(timmy.id, post.id, "A test comment rv".into()); + let comment = Comment::create(pool, &comment_form, None).await?; + + // jessica creates a comment + let comment_form_2 = + CommentInsertForm::new(jessica.id, post_2.id, "A test comment rv 2".into()); + let comment_2 = Comment::create(pool, &comment_form_2, None).await?; + + Ok(Data { + instance, + timmy, + sara, + jessica, + community, + community_2, + post, + post_2, + comment, + comment_2, + }) + } + + async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> { + Instance::delete(pool, data.instance.id).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn admin_types() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + let form = AdminAllowInstanceForm { + instance_id: data.instance.id, + admin_person_id: data.timmy.id, + allowed: true, + reason: None, + }; + AdminAllowInstance::create(pool, &form).await?; + + let form = AdminBlockInstanceForm { + instance_id: data.instance.id, + admin_person_id: data.timmy.id, + blocked: true, + reason: None, + }; + AdminBlockInstance::create(pool, &form).await?; + + let form = AdminPurgeCommentForm { + admin_person_id: data.timmy.id, + post_id: data.post.id, + reason: None, + }; + AdminPurgeComment::create(pool, &form).await?; + + let form = AdminPurgeCommunityForm { + admin_person_id: data.timmy.id, + reason: None, + }; + AdminPurgeCommunity::create(pool, &form).await?; + + let form = AdminPurgePersonForm { + admin_person_id: data.timmy.id, + reason: None, + }; + AdminPurgePerson::create(pool, &form).await?; + + let form = AdminPurgePostForm { + admin_person_id: data.timmy.id, + community_id: data.community.id, + reason: None, + }; + AdminPurgePost::create(pool, &form).await?; + + let form = ModHideCommunityForm { + mod_person_id: data.timmy.id, + community_id: data.community.id, + hidden: Some(true), + reason: None, + }; + ModHideCommunity::create(pool, &form).await?; + + // A 2nd mod hide community, but to a different community, and with jessica + let form = ModHideCommunityForm { + mod_person_id: data.jessica.id, + community_id: data.community_2.id, + hidden: Some(true), + reason: None, + }; + ModHideCommunity::create(pool, &form).await?; + + let modlog = ModlogCombinedQuery::default().list(pool).await?; + assert_eq!(8, modlog.len()); + + if let ModlogCombinedView::ModHideCommunity(v) = &modlog[0] { + assert_eq!(data.community_2.id, v.mod_hide_community.community_id); + assert_eq!(data.community_2.id, v.community.id); + assert_eq!( + data.jessica.id, + v.admin.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModHideCommunity(v) = &modlog[1] { + assert_eq!(data.community.id, v.mod_hide_community.community_id); + assert_eq!(data.community.id, v.community.id); + assert_eq!( + data.timmy.id, + v.admin.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::AdminPurgePost(v) = &modlog[2] { + assert_eq!(data.community.id, v.admin_purge_post.community_id); + assert_eq!(data.community.id, v.community.id); + assert_eq!( + data.timmy.id, + v.admin.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::AdminPurgePerson(v) = &modlog[3] { + assert_eq!( + data.timmy.id, + v.admin.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::AdminPurgeCommunity(v) = &modlog[4] { + assert_eq!( + data.timmy.id, + v.admin.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::AdminPurgeComment(v) = &modlog[5] { + assert_eq!(data.post.id, v.admin_purge_comment.post_id); + assert_eq!(data.post.id, v.post.id); + assert_eq!( + data.timmy.id, + v.admin.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + // Make sure the report types are correct + if let ModlogCombinedView::AdminBlockInstance(v) = &modlog[6] { + assert_eq!(data.instance.id, v.admin_block_instance.instance_id); + assert_eq!(data.instance.id, v.instance.id); + assert_eq!( + data.timmy.id, + v.admin.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::AdminAllowInstance(v) = &modlog[7] { + assert_eq!(data.instance.id, v.admin_allow_instance.instance_id); + assert_eq!(data.instance.id, v.instance.id); + assert_eq!( + data.timmy.id, + v.admin.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + // Filter by admin + let modlog_admin_filter = ModlogCombinedQuery { + mod_person_id: Some(data.timmy.id), + ..Default::default() + } + .list(pool) + .await?; + // Only one is jessica + assert_eq!(7, modlog_admin_filter.len()); + + // Filter by community + let modlog_community_filter = ModlogCombinedQuery { + community_id: Some(data.community.id), + ..Default::default() + } + .list(pool) + .await?; + + // Should be 2, and not jessicas + assert_eq!(2, modlog_community_filter.len()); + + // Filter by type + let modlog_type_filter = ModlogCombinedQuery { + type_: Some(ModlogActionType::ModHideCommunity), + ..Default::default() + } + .list(pool) + .await?; + + // 2 of these, one is jessicas + assert_eq!(2, modlog_type_filter.len()); + + if let ModlogCombinedView::ModHideCommunity(v) = &modlog_type_filter[0] { + assert_eq!(data.community_2.id, v.mod_hide_community.community_id); + assert_eq!(data.community_2.id, v.community.id); + assert_eq!( + data.jessica.id, + v.admin.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModHideCommunity(v) = &modlog_type_filter[1] { + assert_eq!(data.community.id, v.mod_hide_community.community_id); + assert_eq!(data.community.id, v.community.id); + assert_eq!( + data.timmy.id, + v.admin.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + cleanup(data, pool).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn mod_types() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + let form = ModAddForm { + mod_person_id: data.timmy.id, + other_person_id: data.jessica.id, + removed: Some(false), + }; + ModAdd::create(pool, &form).await?; + + let form = ModAddCommunityForm { + mod_person_id: data.timmy.id, + other_person_id: data.jessica.id, + community_id: data.community.id, + removed: Some(false), + }; + ModAddCommunity::create(pool, &form).await?; + + let form = ModBanForm { + mod_person_id: data.timmy.id, + other_person_id: data.jessica.id, + banned: Some(true), + reason: None, + expires: None, + }; + ModBan::create(pool, &form).await?; + + let form = ModBanFromCommunityForm { + mod_person_id: data.timmy.id, + other_person_id: data.jessica.id, + community_id: data.community.id, + banned: Some(true), + reason: None, + expires: None, + }; + ModBanFromCommunity::create(pool, &form).await?; + + let form = ModFeaturePostForm { + mod_person_id: data.timmy.id, + post_id: data.post.id, + featured: Some(true), + is_featured_community: None, + }; + ModFeaturePost::create(pool, &form).await?; + + let form = ModLockPostForm { + mod_person_id: data.timmy.id, + post_id: data.post.id, + locked: Some(true), + }; + ModLockPost::create(pool, &form).await?; + + let form = ModRemoveCommentForm { + mod_person_id: data.timmy.id, + comment_id: data.comment.id, + removed: Some(true), + reason: None, + }; + ModRemoveComment::create(pool, &form).await?; + + let form = ModRemoveCommunityForm { + mod_person_id: data.timmy.id, + community_id: data.community.id, + removed: Some(true), + reason: None, + }; + ModRemoveCommunity::create(pool, &form).await?; + + let form = ModRemovePostForm { + mod_person_id: data.timmy.id, + post_id: data.post.id, + removed: Some(true), + reason: None, + }; + ModRemovePost::create(pool, &form).await?; + + let form = ModTransferCommunityForm { + mod_person_id: data.timmy.id, + other_person_id: data.jessica.id, + community_id: data.community.id, + }; + ModTransferCommunity::create(pool, &form).await?; + + // A few extra ones to test different filters + let form = ModTransferCommunityForm { + mod_person_id: data.jessica.id, + other_person_id: data.sara.id, + community_id: data.community_2.id, + }; + ModTransferCommunity::create(pool, &form).await?; + + let form = ModRemovePostForm { + mod_person_id: data.jessica.id, + post_id: data.post_2.id, + removed: Some(true), + reason: None, + }; + ModRemovePost::create(pool, &form).await?; + + let form = ModRemoveCommentForm { + mod_person_id: data.jessica.id, + comment_id: data.comment_2.id, + removed: Some(true), + reason: None, + }; + ModRemoveComment::create(pool, &form).await?; + + // The all view + let modlog = ModlogCombinedQuery::default().list(pool).await?; + assert_eq!(13, modlog.len()); + + if let ModlogCombinedView::ModRemoveComment(v) = &modlog[0] { + assert_eq!(data.comment_2.id, v.mod_remove_comment.comment_id); + assert_eq!(data.comment_2.id, v.comment.id); + assert_eq!(data.post_2.id, v.post.id); + assert_eq!(data.community_2.id, v.community.id); + assert_eq!( + data.jessica.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.jessica.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModRemovePost(v) = &modlog[1] { + assert_eq!(data.post_2.id, v.mod_remove_post.post_id); + assert_eq!(data.post_2.id, v.post.id); + assert_eq!(data.sara.id, v.post.creator_id); + assert_eq!(data.community_2.id, v.community.id); + assert_eq!( + data.jessica.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.sara.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModTransferCommunity(v) = &modlog[2] { + assert_eq!(data.community_2.id, v.mod_transfer_community.community_id); + assert_eq!(data.community_2.id, v.community.id); + assert_eq!( + data.jessica.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.sara.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModTransferCommunity(v) = &modlog[3] { + assert_eq!(data.community.id, v.mod_transfer_community.community_id); + assert_eq!(data.community.id, v.community.id); + assert_eq!( + data.timmy.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.jessica.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModRemovePost(v) = &modlog[4] { + assert_eq!(data.post.id, v.mod_remove_post.post_id); + assert_eq!(data.post.id, v.post.id); + assert_eq!(data.timmy.id, v.post.creator_id); + assert_eq!(data.community.id, v.community.id); + assert_eq!( + data.timmy.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.timmy.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModRemoveCommunity(v) = &modlog[5] { + assert_eq!(data.community.id, v.mod_remove_community.community_id); + assert_eq!(data.community.id, v.community.id); + assert_eq!( + data.timmy.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModRemoveComment(v) = &modlog[6] { + assert_eq!(data.comment.id, v.mod_remove_comment.comment_id); + assert_eq!(data.comment.id, v.comment.id); + assert_eq!(data.post.id, v.post.id); + assert_eq!(data.community.id, v.community.id); + assert_eq!( + data.timmy.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.timmy.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModLockPost(v) = &modlog[7] { + assert_eq!(data.post.id, v.mod_lock_post.post_id); + assert!(v.mod_lock_post.locked); + assert_eq!(data.post.id, v.post.id); + assert_eq!(data.timmy.id, v.post.creator_id); + assert_eq!(data.community.id, v.community.id); + assert_eq!( + data.timmy.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.timmy.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModFeaturePost(v) = &modlog[8] { + assert_eq!(data.post.id, v.mod_feature_post.post_id); + assert!(v.mod_feature_post.featured); + assert_eq!(data.post.id, v.post.id); + assert_eq!(data.timmy.id, v.post.creator_id); + assert_eq!(data.community.id, v.community.id); + assert_eq!( + data.timmy.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.timmy.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModBanFromCommunity(v) = &modlog[9] { + assert_eq!(data.community.id, v.mod_ban_from_community.community_id); + assert_eq!(data.community.id, v.community.id); + assert_eq!( + data.timmy.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.jessica.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModBan(v) = &modlog[10] { + assert_eq!( + data.timmy.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.jessica.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModAddCommunity(v) = &modlog[11] { + assert_eq!(data.community.id, v.mod_add_community.community_id); + assert_eq!(data.community.id, v.community.id); + assert_eq!( + data.timmy.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.jessica.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + if let ModlogCombinedView::ModAdd(v) = &modlog[12] { + assert_eq!( + data.timmy.id, + v.moderator.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + assert_eq!(data.jessica.id, v.other_person.id); + } else { + panic!("wrong type"); + } + + // Filter by moderator + let modlog_mod_timmy_filter = ModlogCombinedQuery { + mod_person_id: Some(data.timmy.id), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(10, modlog_mod_timmy_filter.len()); + + let modlog_mod_jessica_filter = ModlogCombinedQuery { + mod_person_id: Some(data.jessica.id), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(3, modlog_mod_jessica_filter.len()); + + // Filter by other_person + // Gets a little complicated because things aren't directly linked, + // you have to go into the item to see who created it. + + let modlog_modded_timmy_filter = ModlogCombinedQuery { + other_person_id: Some(data.timmy.id), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(4, modlog_modded_timmy_filter.len()); + + let modlog_modded_jessica_filter = ModlogCombinedQuery { + other_person_id: Some(data.jessica.id), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(6, modlog_modded_jessica_filter.len()); + + let modlog_modded_sara_filter = ModlogCombinedQuery { + other_person_id: Some(data.sara.id), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(2, modlog_modded_sara_filter.len()); + + // Filter by community + let modlog_community_filter = ModlogCombinedQuery { + community_id: Some(data.community.id), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(8, modlog_community_filter.len()); + + let modlog_community_2_filter = ModlogCombinedQuery { + community_id: Some(data.community_2.id), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(3, modlog_community_2_filter.len()); + + // Filter by post + let modlog_post_filter = ModlogCombinedQuery { + post_id: Some(data.post.id), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(4, modlog_post_filter.len()); + + let modlog_post_2_filter = ModlogCombinedQuery { + post_id: Some(data.post_2.id), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(2, modlog_post_2_filter.len()); + + // Filter by comment + let modlog_comment_filter = ModlogCombinedQuery { + comment_id: Some(data.comment.id), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(1, modlog_comment_filter.len()); + + let modlog_comment_2_filter = ModlogCombinedQuery { + comment_id: Some(data.comment_2.id), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(1, modlog_comment_2_filter.len()); + + // Filter by type + let modlog_type_filter = ModlogCombinedQuery { + type_: Some(ModlogActionType::ModRemoveComment), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(2, modlog_type_filter.len()); + + // Assert that the types are correct + assert!(matches!( + modlog_type_filter[0], + ModlogCombinedView::ModRemoveComment(_) + )); + assert!(matches!( + modlog_type_filter[1], + ModlogCombinedView::ModRemoveComment(_) + )); + + cleanup(data, pool).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn hide_modlog_names() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + let form = AdminAllowInstanceForm { + instance_id: data.instance.id, + admin_person_id: data.timmy.id, + allowed: true, + reason: None, + }; + AdminAllowInstance::create(pool, &form).await?; + + let modlog = ModlogCombinedQuery::default().list(pool).await?; + assert_eq!(1, modlog.len()); + + if let ModlogCombinedView::AdminAllowInstance(v) = &modlog[0] { + assert_eq!( + data.timmy.id, + v.admin.as_ref().map(|a| a.id).unwrap_or(PersonId(-1)) + ); + } else { + panic!("wrong type"); + } + + // Filter out the names + let modlog_hide_names_filter = ModlogCombinedQuery { + hide_modlog_names: Some(true), + ..Default::default() + } + .list(pool) + .await?; + assert_eq!(1, modlog_hide_names_filter.len()); + + if let ModlogCombinedView::AdminAllowInstance(v) = &modlog_hide_names_filter[0] { + assert!(v.admin.is_none()) + } else { + panic!("wrong type"); + } + + cleanup(data, pool).await?; + + Ok(()) + } +} diff --git a/crates/db_views_moderator/src/structs.rs b/crates/db_views_moderator/src/structs.rs index 06e9f099a..513a79705 100644 --- a/crates/db_views_moderator/src/structs.rs +++ b/crates/db_views_moderator/src/structs.rs @@ -1,37 +1,34 @@ #[cfg(feature = "full")] use diesel::Queryable; -use lemmy_db_schema::{ - newtypes::{CommentId, CommunityId, PersonId, PostId}, - source::{ - comment::Comment, - community::Community, - instance::Instance, - mod_log::{ - admin::{ - AdminAllowInstance, - AdminBlockInstance, - AdminPurgeComment, - AdminPurgeCommunity, - AdminPurgePerson, - AdminPurgePost, - }, - moderator::{ - ModAdd, - ModAddCommunity, - ModBan, - ModBanFromCommunity, - ModFeaturePost, - ModHideCommunity, - ModLockPost, - ModRemoveComment, - ModRemoveCommunity, - ModRemovePost, - ModTransferCommunity, - }, +use lemmy_db_schema::source::{ + comment::Comment, + community::Community, + instance::Instance, + mod_log::{ + admin::{ + AdminAllowInstance, + AdminBlockInstance, + AdminPurgeComment, + AdminPurgeCommunity, + AdminPurgePerson, + AdminPurgePost, + }, + moderator::{ + ModAdd, + ModAddCommunity, + ModBan, + ModBanFromCommunity, + ModFeaturePost, + ModHideCommunity, + ModLockPost, + ModRemoveComment, + ModRemoveCommunity, + ModRemovePost, + ModTransferCommunity, }, - person::Person, - post::Post, }, + person::Person, + post::Post, }; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -39,7 +36,7 @@ use serde_with::skip_serializing_none; use ts_rs::TS; #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -49,11 +46,11 @@ pub struct ModAddCommunityView { #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub community: Community, - pub modded_person: Person, + pub other_person: Person, } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -62,11 +59,11 @@ pub struct ModAddView { pub mod_add: ModAdd, #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, - pub modded_person: Person, + pub other_person: Person, } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -76,11 +73,11 @@ pub struct ModBanFromCommunityView { #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub community: Community, - pub banned_person: Person, + pub other_person: Person, } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -89,11 +86,11 @@ pub struct ModBanView { pub mod_ban: ModBan, #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, - pub banned_person: Person, + pub other_person: Person, } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -106,7 +103,7 @@ pub struct ModHideCommunityView { } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -115,12 +112,13 @@ pub struct ModLockPostView { pub mod_lock_post: ModLockPost, #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, + pub other_person: Person, pub post: Post, pub community: Community, } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -129,14 +127,14 @@ pub struct ModRemoveCommentView { pub mod_remove_comment: ModRemoveComment, #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, + pub other_person: Person, pub comment: Comment, - pub commenter: Person, pub post: Post, pub community: Community, } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -149,7 +147,7 @@ pub struct ModRemoveCommunityView { } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -158,12 +156,13 @@ pub struct ModRemovePostView { pub mod_remove_post: ModRemovePost, #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, + pub other_person: Person, pub post: Post, pub community: Community, } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -172,12 +171,13 @@ pub struct ModFeaturePostView { pub mod_feature_post: ModFeaturePost, #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, + pub other_person: Person, pub post: Post, pub community: Community, } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -187,11 +187,11 @@ pub struct ModTransferCommunityView { #[cfg_attr(feature = "full", ts(optional))] pub moderator: Option, pub community: Community, - pub modded_person: Person, + pub other_person: Person, } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -204,7 +204,7 @@ pub struct AdminPurgeCommentView { } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -216,7 +216,7 @@ pub struct AdminPurgeCommunityView { } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -228,7 +228,7 @@ pub struct AdminPurgePersonView { } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -241,7 +241,7 @@ pub struct AdminPurgePostView { } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] @@ -254,38 +254,79 @@ pub struct AdminBlockInstanceView { } #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] /// When an admin purges a post. pub struct AdminAllowInstanceView { - pub admin_block_instance: AdminAllowInstance, + pub admin_allow_instance: AdminAllowInstance, pub instance: Instance, #[cfg_attr(feature = "full", ts(optional))] pub admin: Option, } -#[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize, Clone, Copy)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +/// like PaginationCursor but for the modlog_combined +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "full", derive(TS))] #[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, +pub struct ModlogCombinedPaginationCursor(pub String); + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +/// A combined modlog view +pub struct ModlogCombinedViewInternal { + // Specific + pub admin_allow_instance: Option, + pub admin_block_instance: Option, + pub admin_purge_comment: Option, + pub admin_purge_community: Option, + pub admin_purge_person: Option, + pub admin_purge_post: Option, + pub mod_add: Option, + pub mod_add_community: Option, + pub mod_ban: Option, + pub mod_ban_from_community: Option, + pub mod_feature_post: Option, + pub mod_hide_community: Option, + pub mod_lock_post: Option, + pub mod_remove_comment: Option, + pub mod_remove_community: Option, + pub mod_remove_post: Option, + pub mod_transfer_community: Option, + // Specific fields + + // Shared + pub moderator: Option, + pub other_person: Option, + pub instance: Option, + pub community: Option, + pub post: Option, + pub comment: Option, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +// Use serde's internal tagging, to work easier with javascript libraries +#[serde(tag = "type_")] +pub enum ModlogCombinedView { + AdminAllowInstance(AdminAllowInstanceView), + AdminBlockInstance(AdminBlockInstanceView), + AdminPurgeComment(AdminPurgeCommentView), + AdminPurgeCommunity(AdminPurgeCommunityView), + AdminPurgePerson(AdminPurgePersonView), + AdminPurgePost(AdminPurgePostView), + ModAdd(ModAddView), + ModAddCommunity(ModAddCommunityView), + ModBan(ModBanView), + ModBanFromCommunity(ModBanFromCommunityView), + ModFeaturePost(ModFeaturePostView), + ModHideCommunity(ModHideCommunityView), + ModLockPost(ModLockPostView), + ModRemoveComment(ModRemoveCommentView), + ModRemoveCommunity(ModRemoveCommunityView), + ModRemovePost(ModRemovePostView), + ModTransferCommunity(ModTransferCommunityView), } diff --git a/migrations/2024-12-08-165614_add_modlog_combined_table/down.sql b/migrations/2024-12-08-165614_add_modlog_combined_table/down.sql new file mode 100644 index 000000000..7a0223965 --- /dev/null +++ b/migrations/2024-12-08-165614_add_modlog_combined_table/down.sql @@ -0,0 +1,37 @@ +DROP TABLE modlog_combined; + +-- Rename the columns back to when_ +ALTER TABLE admin_allow_instance RENAME COLUMN published TO when_; + +ALTER TABLE admin_block_instance RENAME COLUMN published TO when_; + +ALTER TABLE admin_purge_comment RENAME COLUMN published TO when_; + +ALTER TABLE admin_purge_community RENAME COLUMN published TO when_; + +ALTER TABLE admin_purge_person RENAME COLUMN published TO when_; + +ALTER TABLE admin_purge_post RENAME COLUMN published TO when_; + +ALTER TABLE mod_add RENAME COLUMN published TO when_; + +ALTER TABLE mod_add_community RENAME COLUMN published TO when_; + +ALTER TABLE mod_ban RENAME COLUMN published TO when_; + +ALTER TABLE mod_ban_from_community RENAME COLUMN published TO when_; + +ALTER TABLE mod_feature_post RENAME COLUMN published TO when_; + +ALTER TABLE mod_hide_community RENAME COLUMN published TO when_; + +ALTER TABLE mod_lock_post RENAME COLUMN published TO when_; + +ALTER TABLE mod_remove_comment RENAME COLUMN published TO when_; + +ALTER TABLE mod_remove_community RENAME COLUMN published TO when_; + +ALTER TABLE mod_remove_post RENAME COLUMN published TO when_; + +ALTER TABLE mod_transfer_community RENAME COLUMN published TO when_; + diff --git a/migrations/2024-12-08-165614_add_modlog_combined_table/up.sql b/migrations/2024-12-08-165614_add_modlog_combined_table/up.sql new file mode 100644 index 000000000..b3db0d0b6 --- /dev/null +++ b/migrations/2024-12-08-165614_add_modlog_combined_table/up.sql @@ -0,0 +1,201 @@ +-- First, rename all the when_ columns on the modlog to published +ALTER TABLE admin_allow_instance RENAME COLUMN when_ TO published; + +ALTER TABLE admin_block_instance RENAME COLUMN when_ TO published; + +ALTER TABLE admin_purge_comment RENAME COLUMN when_ TO published; + +ALTER TABLE admin_purge_community RENAME COLUMN when_ TO published; + +ALTER TABLE admin_purge_person RENAME COLUMN when_ TO published; + +ALTER TABLE admin_purge_post RENAME COLUMN when_ TO published; + +ALTER TABLE mod_add RENAME COLUMN when_ TO published; + +ALTER TABLE mod_add_community RENAME COLUMN when_ TO published; + +ALTER TABLE mod_ban RENAME COLUMN when_ TO published; + +ALTER TABLE mod_ban_from_community RENAME COLUMN when_ TO published; + +ALTER TABLE mod_feature_post RENAME COLUMN when_ TO published; + +ALTER TABLE mod_hide_community RENAME COLUMN when_ TO published; + +ALTER TABLE mod_lock_post RENAME COLUMN when_ TO published; + +ALTER TABLE mod_remove_comment RENAME COLUMN when_ TO published; + +ALTER TABLE mod_remove_community RENAME COLUMN when_ TO published; + +ALTER TABLE mod_remove_post RENAME COLUMN when_ TO published; + +ALTER TABLE mod_transfer_community RENAME COLUMN when_ TO published; + +-- Creates combined tables for +-- modlog: (17 tables) +-- admin_allow_instance +-- admin_block_instance +-- admin_purge_comment +-- admin_purge_community +-- admin_purge_person +-- admin_purge_post +-- mod_add +-- mod_add_community +-- mod_ban +-- mod_ban_from_community +-- mod_feature_post +-- mod_hide_community +-- mod_lock_post +-- mod_remove_comment +-- mod_remove_community +-- mod_remove_post +-- mod_transfer_community +CREATE TABLE modlog_combined ( + id serial PRIMARY KEY, + published timestamptz NOT NULL, + admin_allow_instance_id int UNIQUE REFERENCES admin_allow_instance ON UPDATE CASCADE ON DELETE CASCADE, + admin_block_instance_id int UNIQUE REFERENCES admin_block_instance ON UPDATE CASCADE ON DELETE CASCADE, + admin_purge_comment_id int UNIQUE REFERENCES admin_purge_comment ON UPDATE CASCADE ON DELETE CASCADE, + admin_purge_community_id int UNIQUE REFERENCES admin_purge_community ON UPDATE CASCADE ON DELETE CASCADE, + admin_purge_person_id int UNIQUE REFERENCES admin_purge_person ON UPDATE CASCADE ON DELETE CASCADE, + admin_purge_post_id int UNIQUE REFERENCES admin_purge_post ON UPDATE CASCADE ON DELETE CASCADE, + mod_add_id int UNIQUE REFERENCES mod_add ON UPDATE CASCADE ON DELETE CASCADE, + mod_add_community_id int UNIQUE REFERENCES mod_add_community ON UPDATE CASCADE ON DELETE CASCADE, + mod_ban_id int UNIQUE REFERENCES mod_ban ON UPDATE CASCADE ON DELETE CASCADE, + mod_ban_from_community_id int UNIQUE REFERENCES mod_ban_from_community ON UPDATE CASCADE ON DELETE CASCADE, + mod_feature_post_id int UNIQUE REFERENCES mod_feature_post ON UPDATE CASCADE ON DELETE CASCADE, + mod_hide_community_id int UNIQUE REFERENCES mod_hide_community ON UPDATE CASCADE ON DELETE CASCADE, + mod_lock_post_id int UNIQUE REFERENCES mod_lock_post ON UPDATE CASCADE ON DELETE CASCADE, + mod_remove_comment_id int UNIQUE REFERENCES mod_remove_comment ON UPDATE CASCADE ON DELETE CASCADE, + mod_remove_community_id int UNIQUE REFERENCES mod_remove_community ON UPDATE CASCADE ON DELETE CASCADE, + mod_remove_post_id int UNIQUE REFERENCES mod_remove_post ON UPDATE CASCADE ON DELETE CASCADE, + mod_transfer_community_id int UNIQUE REFERENCES mod_transfer_community ON UPDATE CASCADE ON DELETE CASCADE, + -- Make sure only one of the columns is not null + CHECK (num_nonnulls (admin_allow_instance_id, admin_block_instance_id, admin_purge_comment_id, admin_purge_community_id, admin_purge_person_id, admin_purge_post_id, mod_add_id, mod_add_community_id, mod_ban_id, mod_ban_from_community_id, mod_feature_post_id, mod_hide_community_id, mod_lock_post_id, mod_remove_comment_id, mod_remove_community_id, mod_remove_post_id, mod_transfer_community_id) = 1) +); + +CREATE INDEX idx_modlog_combined_published ON modlog_combined (published DESC, id DESC); + +-- Updating the history +-- Not doing a union all here, because there's way too many null columns +INSERT INTO modlog_combined (published, admin_allow_instance_id) +SELECT + published, + id +FROM + admin_allow_instance; + +INSERT INTO modlog_combined (published, admin_block_instance_id) +SELECT + published, + id +FROM + admin_block_instance; + +INSERT INTO modlog_combined (published, admin_purge_comment_id) +SELECT + published, + id +FROM + admin_purge_comment; + +INSERT INTO modlog_combined (published, admin_purge_community_id) +SELECT + published, + id +FROM + admin_purge_community; + +INSERT INTO modlog_combined (published, admin_purge_person_id) +SELECT + published, + id +FROM + admin_purge_person; + +INSERT INTO modlog_combined (published, admin_purge_post_id) +SELECT + published, + id +FROM + admin_purge_post; + +INSERT INTO modlog_combined (published, mod_add_id) +SELECT + published, + id +FROM + mod_add; + +INSERT INTO modlog_combined (published, mod_add_community_id) +SELECT + published, + id +FROM + mod_add_community; + +INSERT INTO modlog_combined (published, mod_ban_id) +SELECT + published, + id +FROM + mod_ban; + +INSERT INTO modlog_combined (published, mod_ban_from_community_id) +SELECT + published, + id +FROM + mod_ban_from_community; + +INSERT INTO modlog_combined (published, mod_feature_post_id) +SELECT + published, + id +FROM + mod_feature_post; + +INSERT INTO modlog_combined (published, mod_hide_community_id) +SELECT + published, + id +FROM + mod_hide_community; + +INSERT INTO modlog_combined (published, mod_lock_post_id) +SELECT + published, + id +FROM + mod_lock_post; + +INSERT INTO modlog_combined (published, mod_remove_comment_id) +SELECT + published, + id +FROM + mod_remove_comment; + +INSERT INTO modlog_combined (published, mod_remove_community_id) +SELECT + published, + id +FROM + mod_remove_community; + +INSERT INTO modlog_combined (published, mod_remove_post_id) +SELECT + published, + id +FROM + mod_remove_post; + +INSERT INTO modlog_combined (published, mod_transfer_community_id) +SELECT + published, + id +FROM + mod_transfer_community; +