diff --git a/Cargo.lock b/Cargo.lock index c0edf2dcf..97504600b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,9 +16,9 @@ checksum = "8f27d075294830fcab6f66e320dab524bc6d048f4a151698e153205559113772" [[package]] name = "activitypub_federation" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a028034c642d3ed16b535f98f48b3df30397833c183d68852d79de16650d5ed5" +checksum = "6e16130d5914e6483f99bde5a9bb97ca62e1f359e0b9791c8ebd5c7abd50fe8e" dependencies = [ "activitystreams-kinds", "actix-web", @@ -2612,7 +2612,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lemmy_api" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "activitypub_federation", "actix-web", @@ -2641,7 +2641,7 @@ dependencies = [ [[package]] name = "lemmy_api_common" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "activitypub_federation", "actix-web", @@ -2679,7 +2679,7 @@ dependencies = [ [[package]] name = "lemmy_api_crud" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "accept-language", "activitypub_federation", @@ -2702,7 +2702,7 @@ dependencies = [ [[package]] name = "lemmy_apub" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "activitypub_federation", "actix-web", @@ -2740,7 +2740,7 @@ dependencies = [ [[package]] name = "lemmy_db_perf" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "anyhow", "clap", @@ -2755,7 +2755,7 @@ dependencies = [ [[package]] name = "lemmy_db_schema" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "activitypub_federation", "anyhow", @@ -2795,7 +2795,7 @@ dependencies = [ [[package]] name = "lemmy_db_views" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "actix-web", "chrono", @@ -2817,7 +2817,7 @@ dependencies = [ [[package]] name = "lemmy_db_views_actor" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "chrono", "diesel", @@ -2837,7 +2837,7 @@ dependencies = [ [[package]] name = "lemmy_db_views_moderator" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "diesel", "diesel-async", @@ -2849,7 +2849,7 @@ dependencies = [ [[package]] name = "lemmy_federate" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "activitypub_federation", "anyhow", @@ -2872,7 +2872,7 @@ dependencies = [ [[package]] name = "lemmy_routes" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "activitypub_federation", "actix-web", @@ -2897,7 +2897,7 @@ dependencies = [ [[package]] name = "lemmy_server" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "activitypub_federation", "actix-cors", @@ -2940,7 +2940,7 @@ dependencies = [ [[package]] name = "lemmy_utils" -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" dependencies = [ "actix-web", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 554448d02..4d9485ca5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace.package] -version = "0.19.4-beta.2" +version = "0.19.4-beta.3" edition = "2021" description = "A link aggregator for the fediverse" license = "AGPL-3.0" @@ -88,18 +88,18 @@ unused_self = "deny" unwrap_used = "deny" [workspace.dependencies] -lemmy_api = { version = "=0.19.4-beta.2", path = "./crates/api" } -lemmy_api_crud = { version = "=0.19.4-beta.2", path = "./crates/api_crud" } -lemmy_apub = { version = "=0.19.4-beta.2", path = "./crates/apub" } -lemmy_utils = { version = "=0.19.4-beta.2", path = "./crates/utils", default-features = false } -lemmy_db_schema = { version = "=0.19.4-beta.2", path = "./crates/db_schema" } -lemmy_api_common = { version = "=0.19.4-beta.2", path = "./crates/api_common" } -lemmy_routes = { version = "=0.19.4-beta.2", path = "./crates/routes" } -lemmy_db_views = { version = "=0.19.4-beta.2", path = "./crates/db_views" } -lemmy_db_views_actor = { version = "=0.19.4-beta.2", path = "./crates/db_views_actor" } -lemmy_db_views_moderator = { version = "=0.19.4-beta.2", path = "./crates/db_views_moderator" } -lemmy_federate = { version = "=0.19.4-beta.2", path = "./crates/federate" } -activitypub_federation = { version = "0.5.2", default-features = false, features = [ +lemmy_api = { version = "=0.19.4-beta.3", path = "./crates/api" } +lemmy_api_crud = { version = "=0.19.4-beta.3", path = "./crates/api_crud" } +lemmy_apub = { version = "=0.19.4-beta.3", path = "./crates/apub" } +lemmy_utils = { version = "=0.19.4-beta.3", path = "./crates/utils", default-features = false } +lemmy_db_schema = { version = "=0.19.4-beta.3", path = "./crates/db_schema" } +lemmy_api_common = { version = "=0.19.4-beta.3", path = "./crates/api_common" } +lemmy_routes = { version = "=0.19.4-beta.3", path = "./crates/routes" } +lemmy_db_views = { version = "=0.19.4-beta.3", path = "./crates/db_views" } +lemmy_db_views_actor = { version = "=0.19.4-beta.3", path = "./crates/db_views_actor" } +lemmy_db_views_moderator = { version = "=0.19.4-beta.3", path = "./crates/db_views_moderator" } +lemmy_federate = { version = "=0.19.4-beta.3", path = "./crates/federate" } +activitypub_federation = { version = "0.5.4", default-features = false, features = [ "actix-web", ] } diesel = "2.1.4" diff --git a/api_tests/package.json b/api_tests/package.json index 75ef362f8..60adac43c 100644 --- a/api_tests/package.json +++ b/api_tests/package.json @@ -20,16 +20,16 @@ }, "devDependencies": { "@types/jest": "^29.5.12", - "@types/node": "^20.11.27", - "@typescript-eslint/eslint-plugin": "^7.2.0", - "@typescript-eslint/parser": "^7.2.0", + "@types/node": "^20.12.4", + "@typescript-eslint/eslint-plugin": "^7.5.0", + "@typescript-eslint/parser": "^7.5.0", "download-file-sync": "^1.0.4", "eslint": "^8.57.0", "eslint-plugin-prettier": "^5.1.3", "jest": "^29.5.0", - "lemmy-js-client": "0.19.4-alpha.13", + "lemmy-js-client": "0.19.4-alpha.16", "prettier": "^3.2.5", "ts-jest": "^29.1.0", - "typescript": "^5.4.2" + "typescript": "^5.4.4" } } diff --git a/api_tests/pnpm-lock.yaml b/api_tests/pnpm-lock.yaml index 7b8c05328..81508d740 100644 --- a/api_tests/pnpm-lock.yaml +++ b/api_tests/pnpm-lock.yaml @@ -9,14 +9,14 @@ devDependencies: specifier: ^29.5.12 version: 29.5.12 '@types/node': - specifier: ^20.11.27 - version: 20.11.27 + specifier: ^20.12.4 + version: 20.12.4 '@typescript-eslint/eslint-plugin': - specifier: ^7.2.0 - version: 7.2.0(@typescript-eslint/parser@7.2.0)(eslint@8.57.0)(typescript@5.4.2) + specifier: ^7.5.0 + version: 7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.57.0)(typescript@5.4.4) '@typescript-eslint/parser': - specifier: ^7.2.0 - version: 7.2.0(eslint@8.57.0)(typescript@5.4.2) + specifier: ^7.5.0 + version: 7.5.0(eslint@8.57.0)(typescript@5.4.4) download-file-sync: specifier: ^1.0.4 version: 1.0.4 @@ -28,19 +28,19 @@ devDependencies: version: 5.1.3(eslint@8.57.0)(prettier@3.2.5) jest: specifier: ^29.5.0 - version: 29.7.0(@types/node@20.11.27) + version: 29.7.0(@types/node@20.12.4) lemmy-js-client: - specifier: 0.19.4-alpha.13 - version: 0.19.4-alpha.13 + specifier: 0.19.4-alpha.16 + version: 0.19.4-alpha.16 prettier: specifier: ^3.2.5 version: 3.2.5 ts-jest: specifier: ^29.1.0 - version: 29.1.2(@babel/core@7.23.9)(jest@29.7.0)(typescript@5.4.2) + version: 29.1.2(@babel/core@7.23.9)(jest@29.7.0)(typescript@5.4.4) typescript: - specifier: ^5.4.2 - version: 5.4.2 + specifier: ^5.4.4 + version: 5.4.4 packages: @@ -464,7 +464,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.27 + '@types/node': 20.12.4 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -485,14 +485,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.27 + '@types/node': 20.12.4 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@20.11.27) + jest-config: 29.7.0(@types/node@20.12.4) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -520,7 +520,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.27 + '@types/node': 20.12.4 jest-mock: 29.7.0 dev: true @@ -547,7 +547,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.11.27 + '@types/node': 20.12.4 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -580,7 +580,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.22 - '@types/node': 20.11.27 + '@types/node': 20.12.4 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -668,7 +668,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.11.27 + '@types/node': 20.12.4 '@types/yargs': 17.0.32 chalk: 4.1.2 dev: true @@ -777,7 +777,7 @@ packages: /@types/graceful-fs@4.1.9: resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} dependencies: - '@types/node': 20.11.27 + '@types/node': 20.12.4 dev: true /@types/istanbul-lib-coverage@2.0.6: @@ -807,8 +807,8 @@ packages: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true - /@types/node@20.11.27: - resolution: {integrity: sha512-qyUZfMnCg1KEz57r7pzFtSGt49f6RPkPBis3Vo4PbS7roQEDn22hiHzl/Lo1q4i4hDEgBJmBF/NTNg2XR0HbFg==} + /@types/node@20.12.4: + resolution: {integrity: sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==} dependencies: undici-types: 5.26.5 dev: true @@ -831,9 +831,9 @@ packages: '@types/yargs-parser': 21.0.3 dev: true - /@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0)(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/eslint-plugin@7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.57.0)(typescript@5.4.4): + resolution: {integrity: sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: '@typescript-eslint/parser': ^7.0.0 eslint: ^8.56.0 @@ -843,26 +843,26 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.2) - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/type-utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) - '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) - '@typescript-eslint/visitor-keys': 7.2.0 + '@typescript-eslint/parser': 7.5.0(eslint@8.57.0)(typescript@5.4.4) + '@typescript-eslint/scope-manager': 7.5.0 + '@typescript-eslint/type-utils': 7.5.0(eslint@8.57.0)(typescript@5.4.4) + '@typescript-eslint/utils': 7.5.0(eslint@8.57.0)(typescript@5.4.4) + '@typescript-eslint/visitor-keys': 7.5.0 debug: 4.3.4 eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.2) - typescript: 5.4.2 + ts-api-utils: 1.3.0(typescript@5.4.4) + typescript: 5.4.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/parser@7.5.0(eslint@8.57.0)(typescript@5.4.4): + resolution: {integrity: sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 typescript: '*' @@ -870,28 +870,28 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) - '@typescript-eslint/visitor-keys': 7.2.0 + '@typescript-eslint/scope-manager': 7.5.0 + '@typescript-eslint/types': 7.5.0 + '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.4) + '@typescript-eslint/visitor-keys': 7.5.0 debug: 4.3.4 eslint: 8.57.0 - typescript: 5.4.2 + typescript: 5.4.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@7.2.0: - resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/scope-manager@7.5.0: + resolution: {integrity: sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==} + engines: {node: ^18.18.0 || >=20.0.0} dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 + '@typescript-eslint/types': 7.5.0 + '@typescript-eslint/visitor-keys': 7.5.0 dev: true - /@typescript-eslint/type-utils@7.2.0(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/type-utils@7.5.0(eslint@8.57.0)(typescript@5.4.4): + resolution: {integrity: sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 typescript: '*' @@ -899,55 +899,55 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) - '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.4) + '@typescript-eslint/utils': 7.5.0(eslint@8.57.0)(typescript@5.4.4) debug: 4.3.4 eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.4.2) - typescript: 5.4.2 + ts-api-utils: 1.3.0(typescript@5.4.4) + typescript: 5.4.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@7.2.0: - resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/types@7.5.0: + resolution: {integrity: sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==} + engines: {node: ^18.18.0 || >=20.0.0} dev: true - /@typescript-eslint/typescript-estree@7.2.0(typescript@5.4.2): - resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/typescript-estree@7.5.0(typescript@5.4.4): + resolution: {integrity: sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 + '@typescript-eslint/types': 7.5.0 + '@typescript-eslint/visitor-keys': 7.5.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.2) - typescript: 5.4.2 + ts-api-utils: 1.3.0(typescript@5.4.4) + typescript: 5.4.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@7.2.0(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/utils@7.5.0(eslint@8.57.0)(typescript@5.4.4): + resolution: {integrity: sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==} + engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) + '@typescript-eslint/scope-manager': 7.5.0 + '@typescript-eslint/types': 7.5.0 + '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.4) eslint: 8.57.0 semver: 7.6.0 transitivePeerDependencies: @@ -955,11 +955,11 @@ packages: - typescript dev: true - /@typescript-eslint/visitor-keys@7.2.0: - resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/visitor-keys@7.5.0: + resolution: {integrity: sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==} + engines: {node: ^18.18.0 || >=20.0.0} dependencies: - '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/types': 7.5.0 eslint-visitor-keys: 3.4.3 dev: true @@ -1265,7 +1265,7 @@ packages: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true - /create-jest@29.7.0(@types/node@20.11.27): + /create-jest@29.7.0(@types/node@20.12.4): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -1274,7 +1274,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.11.27) + jest-config: 29.7.0(@types/node@20.12.4) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -1906,7 +1906,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.27 + '@types/node': 20.12.4 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -1927,7 +1927,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@20.11.27): + /jest-cli@29.7.0(@types/node@20.12.4): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -1941,10 +1941,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.11.27) + create-jest: 29.7.0(@types/node@20.12.4) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.11.27) + jest-config: 29.7.0(@types/node@20.12.4) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -1955,7 +1955,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@20.11.27): + /jest-config@29.7.0(@types/node@20.12.4): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -1970,7 +1970,7 @@ packages: '@babel/core': 7.23.9 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.27 + '@types/node': 20.12.4 babel-jest: 29.7.0(@babel/core@7.23.9) chalk: 4.1.2 ci-info: 3.9.0 @@ -2030,7 +2030,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.27 + '@types/node': 20.12.4 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -2046,7 +2046,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.11.27 + '@types/node': 20.12.4 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -2097,7 +2097,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.27 + '@types/node': 20.12.4 jest-util: 29.7.0 dev: true @@ -2152,7 +2152,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.27 + '@types/node': 20.12.4 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -2183,7 +2183,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.27 + '@types/node': 20.12.4 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.2 @@ -2235,7 +2235,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.27 + '@types/node': 20.12.4 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -2260,7 +2260,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.27 + '@types/node': 20.12.4 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -2272,13 +2272,13 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 20.11.27 + '@types/node': 20.12.4 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@20.11.27): + /jest@29.7.0(@types/node@20.12.4): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -2291,7 +2291,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.11.27) + jest-cli: 29.7.0(@types/node@20.12.4) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -2357,8 +2357,8 @@ packages: engines: {node: '>=6'} dev: true - /lemmy-js-client@0.19.4-alpha.13: - resolution: {integrity: sha512-ru1dCqPSfOJdsGq7am5J7P7f+/hpyHGhNbCEV/JAZP2U1lGHul32gLpBkilDnStDNdeq52scjKx+3WskRJFGFA==} + /lemmy-js-client@0.19.4-alpha.16: + resolution: {integrity: sha512-9BKCpZeH5+dDkSuYLVPvJnRGOSa3/jBUqYlQH3r1p8TyCCBrxstIC2I+h9dZZtOg4RmK7ShcuZdk9LSMe1ZMyw==} dev: true /leven@3.1.0: @@ -2890,16 +2890,16 @@ packages: is-number: 7.0.0 dev: true - /ts-api-utils@1.3.0(typescript@5.4.2): + /ts-api-utils@1.3.0(typescript@5.4.4): resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.4.2 + typescript: 5.4.4 dev: true - /ts-jest@29.1.2(@babel/core@7.23.9)(jest@29.7.0)(typescript@5.4.2): + /ts-jest@29.1.2(@babel/core@7.23.9)(jest@29.7.0)(typescript@5.4.4): resolution: {integrity: sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==} engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true @@ -2923,13 +2923,13 @@ packages: '@babel/core': 7.23.9 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.11.27) + jest: 29.7.0(@types/node@20.12.4) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.5.4 - typescript: 5.4.2 + typescript: 5.4.4 yargs-parser: 21.1.1 dev: true @@ -2959,8 +2959,8 @@ packages: engines: {node: '>=10'} dev: true - /typescript@5.4.2: - resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + /typescript@5.4.4: + resolution: {integrity: sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==} engines: {node: '>=14.17'} hasBin: true dev: true diff --git a/api_tests/src/comment.spec.ts b/api_tests/src/comment.spec.ts index de714f687..fc547481c 100644 --- a/api_tests/src/comment.spec.ts +++ b/api_tests/src/comment.spec.ts @@ -53,9 +53,7 @@ beforeAll(async () => { } }); -afterAll(() => { - unfollows(); -}); +afterAll(unfollows); function assertCommentFederation( commentOne?: CommentView, diff --git a/api_tests/src/community.spec.ts b/api_tests/src/community.spec.ts index 02fac3d9a..aa96f563c 100644 --- a/api_tests/src/community.spec.ts +++ b/api_tests/src/community.spec.ts @@ -31,10 +31,12 @@ import { searchPostLocal, longDelay, editCommunity, + unfollows, } from "./shared"; import { EditCommunity, EditSite } from "lemmy-js-client"; beforeAll(setupLogins); +afterAll(unfollows); function assertCommunityFederation( communityOne?: CommunityView, diff --git a/api_tests/src/follow.spec.ts b/api_tests/src/follow.spec.ts index 276213eac..161c7f045 100644 --- a/api_tests/src/follow.spec.ts +++ b/api_tests/src/follow.spec.ts @@ -15,9 +15,7 @@ import { beforeAll(setupLogins); -afterAll(() => { - unfollows(); -}); +afterAll(unfollows); test("Follow local community", async () => { let user = await registerUser(beta, betaUrl); diff --git a/api_tests/src/image.spec.ts b/api_tests/src/image.spec.ts index 3a82b572d..055c498e0 100644 --- a/api_tests/src/image.spec.ts +++ b/api_tests/src/image.spec.ts @@ -15,7 +15,6 @@ import { createCommunity, createPost, deleteAllImages, - delta, epsilon, followCommunity, gamma, @@ -28,14 +27,15 @@ import { setupLogins, waitForPost, unfollows, + editPostThumbnail, + getPost, + waitUntil, } from "./shared"; const downloadFileSync = require("download-file-sync"); beforeAll(setupLogins); -afterAll(() => { - unfollows(); -}); +afterAll(unfollows); test("Upload image and delete it", async () => { // Before running this test, you need to delete all previous images in the DB @@ -252,3 +252,74 @@ test("No image proxying if setting is disabled", async () => { // Make sure the alt text got federated expect(post.post_view.post.alt_text).toBe(betaPost.post.alt_text); }); + +test("Make regular post, and give it a custom thumbnail", async () => { + const uploadForm1: UploadImage = { + image: Buffer.from("testRegular1"), + }; + const upload1 = await alphaImage.uploadImage(uploadForm1); + + const community = await createCommunity(alphaImage); + + // Use wikipedia since it has an opengraph image + const wikipediaUrl = "https://wikipedia.org/"; + + let post = await createPost( + alphaImage, + community.community_view.community.id, + wikipediaUrl, + ); + + // Wait for the metadata to get fetched, since this is backgrounded now + post = await waitUntil( + () => getPost(alphaImage, post.post_view.post.id), + p => p.post_view.post.thumbnail_url != undefined, + ); + expect(post.post_view.post.url).toBe(wikipediaUrl); + expect(post.post_view.post.thumbnail_url).toBeDefined(); + + // Edit the thumbnail + await editPostThumbnail(alphaImage, post.post_view.post, upload1.url!); + + post = await waitUntil( + () => getPost(alphaImage, post.post_view.post.id), + p => p.post_view.post.thumbnail_url == upload1.url, + ); + + // Make sure the thumbnail got edited. + expect(post.post_view.post.thumbnail_url).toBe(upload1.url); +}); + +test("Create an image post, and make sure a custom thumbnail doesnt overwrite it", async () => { + const uploadForm1: UploadImage = { + image: Buffer.from("test1"), + }; + const upload1 = await alphaImage.uploadImage(uploadForm1); + + const uploadForm2: UploadImage = { + image: Buffer.from("test2"), + }; + const upload2 = await alphaImage.uploadImage(uploadForm2); + + const community = await createCommunity(alphaImage); + + let post = await createPost( + alphaImage, + community.community_view.community.id, + upload1.url, + ); + expect(post.post_view.post.url).toBe(upload1.url); + + // Edit the post + await editPostThumbnail(alphaImage, post.post_view.post, upload2.url!); + + // Wait for the metadata to get fetched + post = await waitUntil( + () => getPost(alphaImage, post.post_view.post.id), + p => p.post_view.post.thumbnail_url == upload1.url, + ); + + // Make sure the new custom thumbnail is ignored, and doesn't overwrite the image post + expect(post.post_view.post.url).toBe(upload1.url); + expect(post.post_view.post.thumbnail_url).toBe(upload1.url); +}); diff --git a/api_tests/src/post.spec.ts b/api_tests/src/post.spec.ts index c1640e93e..b2ab50222 100644 --- a/api_tests/src/post.spec.ts +++ b/api_tests/src/post.spec.ts @@ -51,9 +51,7 @@ beforeAll(async () => { await unfollows(); }); -afterAll(() => { - unfollows(); -}); +afterAll(unfollows); async function assertPostFederation(postOne: PostView, postTwo: PostView) { // Link metadata is generated in background task and may not be ready yet at this time, @@ -745,3 +743,23 @@ test("Block post that contains banned URL", async () => { editSiteForm.blocked_urls = []; await epsilon.editSite(editSiteForm); }); + +test("Fetch post with redirect", async () => { + let alphaPost = await createPost(alpha, betaCommunity!.community.id); + expect(alphaPost.post_view.post).toBeDefined(); + + // beta fetches from alpha as usual + let betaPost = await resolvePost(beta, alphaPost.post_view.post); + expect(betaPost.post).toBeDefined(); + + // gamma fetches from beta, and gets redirected to alpha + let gammaPost = await resolvePost(gamma, betaPost.post!.post); + expect(gammaPost.post).toBeDefined(); + + // fetch remote object from local url, which redirects to the original url + let form: ResolveObject = { + q: `http://lemmy-gamma:8561/post/${gammaPost.post!.post.id}`, + }; + let gammaPost2 = await gamma.resolveObject(form); + expect(gammaPost2.post).toBeDefined(); +}); diff --git a/api_tests/src/private_message.spec.ts b/api_tests/src/private_message.spec.ts index 063ee05ee..8fd683ff0 100644 --- a/api_tests/src/private_message.spec.ts +++ b/api_tests/src/private_message.spec.ts @@ -21,9 +21,7 @@ beforeAll(async () => { recipient_id = 3; }); -afterAll(() => { - unfollows(); -}); +afterAll(unfollows); test("Create a private message", async () => { let pmRes = await createPrivateMessage(alpha, recipient_id); diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index 723b63887..e363fffa4 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -226,6 +226,18 @@ export async function editPost( return api.editPost(form); } +export async function editPostThumbnail( + api: LemmyHttp, + post: Post, + customThumbnail: string, +): Promise { + let form: EditPost = { + post_id: post.id, + custom_thumbnail: customThumbnail, + }; + return api.editPost(form); +} + export async function deletePost( api: LemmyHttp, deleted: boolean, diff --git a/api_tests/src/user.spec.ts b/api_tests/src/user.spec.ts index 5af054918..f44f3cc0a 100644 --- a/api_tests/src/user.spec.ts +++ b/api_tests/src/user.spec.ts @@ -20,11 +20,13 @@ import { getComments, fetchFunction, alphaImage, + unfollows, } from "./shared"; import { LemmyHttp, SaveUserSettings, UploadImage } from "lemmy-js-client"; import { GetPosts } from "lemmy-js-client/dist/types/GetPosts"; beforeAll(setupLogins); +afterAll(unfollows); let apShortname: string; diff --git a/crates/api/src/comment/distinguish.rs b/crates/api/src/comment/distinguish.rs index a346bf4ca..dfd850e89 100644 --- a/crates/api/src/comment/distinguish.rs +++ b/crates/api/src/comment/distinguish.rs @@ -9,15 +9,17 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::{CommentView, LocalUserView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn distinguish_comment( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { - let orig_comment = CommentView::read(&mut context.pool(), data.comment_id, None).await?; +) -> LemmyResult> { + let orig_comment = CommentView::read(&mut context.pool(), data.comment_id, None) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; check_community_user_action( &local_user_view.person, @@ -54,7 +56,8 @@ pub async fn distinguish_comment( data.comment_id, Some(local_user_view.person.id), ) - .await?; + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; Ok(Json(CommentResponse { comment_view, diff --git a/crates/api/src/comment/like.rs b/crates/api/src/comment/like.rs index e15d74422..d0aa4a6c2 100644 --- a/crates/api/src/comment/like.rs +++ b/crates/api/src/comment/like.rs @@ -17,7 +17,7 @@ use lemmy_db_schema::{ traits::Likeable, }; use lemmy_db_views::structs::{CommentView, LocalUserView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use std::ops::Deref; #[tracing::instrument(skip(context))] @@ -25,7 +25,7 @@ pub async fn like_comment( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; let mut recipient_ids = Vec::::new(); @@ -35,7 +35,9 @@ pub async fn like_comment( check_bot_account(&local_user_view.person)?; let comment_id = data.comment_id; - let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; + let orig_comment = CommentView::read(&mut context.pool(), comment_id, None) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; check_community_user_action( &local_user_view.person, @@ -46,9 +48,10 @@ pub async fn like_comment( // Add parent poster or commenter to recipients let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await; - if let Ok(reply) = comment_reply { + if let Ok(Some(reply)) = comment_reply { let recipient_id = reply.recipient_id; - if let Ok(local_recipient) = LocalUserView::read_person(&mut context.pool(), recipient_id).await + if let Ok(Some(local_recipient)) = + LocalUserView::read_person(&mut context.pool(), recipient_id).await { recipient_ids.push(local_recipient.local_user.id); } diff --git a/crates/api/src/comment/list_comment_likes.rs b/crates/api/src/comment/list_comment_likes.rs index b442cce23..8c2c9dd32 100644 --- a/crates/api/src/comment/list_comment_likes.rs +++ b/crates/api/src/comment/list_comment_likes.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ utils::is_mod_or_admin, }; use lemmy_db_views::structs::{CommentView, LocalUserView, VoteView}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; /// Lists likes for a comment #[tracing::instrument(skip(context))] @@ -13,13 +13,15 @@ pub async fn list_comment_likes( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let comment_view = CommentView::read( &mut context.pool(), data.comment_id, Some(local_user_view.person.id), ) - .await?; + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; + is_mod_or_admin( &mut context.pool(), &local_user_view.person, diff --git a/crates/api/src/comment/save.rs b/crates/api/src/comment/save.rs index 95c08e701..f9d649e48 100644 --- a/crates/api/src/comment/save.rs +++ b/crates/api/src/comment/save.rs @@ -8,14 +8,14 @@ use lemmy_db_schema::{ traits::Saveable, }; use lemmy_db_views::structs::{CommentView, LocalUserView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn save_comment( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let comment_saved_form = CommentSavedForm { comment_id: data.comment_id, person_id: local_user_view.person.id, @@ -33,7 +33,9 @@ pub async fn save_comment( let comment_id = data.comment_id; let person_id = local_user_view.person.id; - let comment_view = CommentView::read(&mut context.pool(), comment_id, Some(person_id)).await?; + let comment_view = CommentView::read(&mut context.pool(), comment_id, Some(person_id)) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; Ok(Json(CommentResponse { comment_view, diff --git a/crates/api/src/comment_report/create.rs b/crates/api/src/comment_report/create.rs index f8075460f..c008d1df2 100644 --- a/crates/api/src/comment_report/create.rs +++ b/crates/api/src/comment_report/create.rs @@ -19,7 +19,7 @@ use lemmy_db_schema::{ traits::Reportable, }; use lemmy_db_views::structs::{CommentReportView, CommentView, LocalUserView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; /// Creates a comment report and notifies the moderators of the community #[tracing::instrument(skip(context))] @@ -27,7 +27,7 @@ pub async fn create_comment_report( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; let reason = data.reason.trim().to_string(); @@ -35,7 +35,9 @@ pub async fn create_comment_report( let person_id = local_user_view.person.id; let comment_id = data.comment_id; - let comment_view = CommentView::read(&mut context.pool(), comment_id, None).await?; + let comment_view = CommentView::read(&mut context.pool(), comment_id, None) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; check_community_user_action( &local_user_view.person, @@ -58,8 +60,9 @@ pub async fn create_comment_report( .await .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?; - let comment_report_view = - CommentReportView::read(&mut context.pool(), report.id, person_id).await?; + let comment_report_view = CommentReportView::read(&mut context.pool(), report.id, person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommentReport)?; // Email the admins if local_site.reports_email_admins { diff --git a/crates/api/src/comment_report/list.rs b/crates/api/src/comment_report/list.rs index f1577a77f..d2f723819 100644 --- a/crates/api/src/comment_report/list.rs +++ b/crates/api/src/comment_report/list.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ utils::check_community_mod_of_any_or_admin_action, }; use lemmy_db_views::{comment_report_view::CommentReportQuery, structs::LocalUserView}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; /// Lists comment reports for a community if an id is supplied /// or returns all comment reports for communities a user moderates @@ -14,7 +14,7 @@ pub async fn list_comment_reports( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let community_id = data.community_id; let comment_id = data.comment_id; let unresolved_only = data.unresolved_only.unwrap_or_default(); diff --git a/crates/api/src/comment_report/resolve.rs b/crates/api/src/comment_report/resolve.rs index 70e7d89f6..40aad9569 100644 --- a/crates/api/src/comment_report/resolve.rs +++ b/crates/api/src/comment_report/resolve.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable}; use lemmy_db_views::structs::{CommentReportView, LocalUserView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; /// Resolves or unresolves a comment report and notifies the moderators of the community #[tracing::instrument(skip(context))] @@ -14,10 +14,12 @@ pub async fn resolve_comment_report( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let report_id = data.report_id; let person_id = local_user_view.person.id; - let report = CommentReportView::read(&mut context.pool(), report_id, person_id).await?; + let report = CommentReportView::read(&mut context.pool(), report_id, person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommentReport)?; let person_id = local_user_view.person.id; check_community_mod_action( @@ -39,8 +41,9 @@ pub async fn resolve_comment_report( } let report_id = data.report_id; - let comment_report_view = - CommentReportView::read(&mut context.pool(), report_id, person_id).await?; + let comment_report_view = CommentReportView::read(&mut context.pool(), report_id, person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommentReport)?; Ok(Json(CommentReportResponse { comment_report_view, diff --git a/crates/api/src/community/add_mod.rs b/crates/api/src/community/add_mod.rs index 69692e58c..df6b3fbe4 100644 --- a/crates/api/src/community/add_mod.rs +++ b/crates/api/src/community/add_mod.rs @@ -15,14 +15,14 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::CommunityModeratorView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn add_mod_to_community( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let community_id = data.community_id; // Verify that only mods or admins can add mod @@ -33,7 +33,9 @@ pub async fn add_mod_to_community( &mut context.pool(), ) .await?; - let community = Community::read(&mut context.pool(), community_id).await?; + let community = Community::read(&mut context.pool(), community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; if local_user_view.local_user.admin && !community.local { Err(LemmyErrorType::NotAModerator)? } diff --git a/crates/api/src/community/ban.rs b/crates/api/src/community/ban.rs index 91cb8884f..93cf00415 100644 --- a/crates/api/src/community/ban.rs +++ b/crates/api/src/community/ban.rs @@ -21,7 +21,7 @@ use lemmy_db_schema::{ use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::PersonView; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::validation::is_valid_body_field, }; @@ -30,7 +30,7 @@ pub async fn ban_from_community( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let banned_person_id = data.person_id; let remove_data = data.remove_data.unwrap_or(false); let expires = check_expire_time(data.expires)?; @@ -89,7 +89,9 @@ pub async fn ban_from_community( ModBanFromCommunity::create(&mut context.pool(), &form).await?; - let person_view = PersonView::read(&mut context.pool(), data.person_id).await?; + let person_view = PersonView::read(&mut context.pool(), data.person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; ActivityChannel::submit_activity( SendActivityData::BanFromCommunity { diff --git a/crates/api/src/community/block.rs b/crates/api/src/community/block.rs index fd4a5a01b..449addf32 100644 --- a/crates/api/src/community/block.rs +++ b/crates/api/src/community/block.rs @@ -14,14 +14,14 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::CommunityView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn block_community( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let community_id = data.community_id; let person_id = local_user_view.person.id; let community_block_form = CommunityBlockForm { @@ -51,7 +51,9 @@ pub async fn block_community( } let community_view = - CommunityView::read(&mut context.pool(), community_id, Some(person_id), false).await?; + CommunityView::read(&mut context.pool(), community_id, Some(person_id), false) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; ActivityChannel::submit_activity( SendActivityData::FollowCommunity( diff --git a/crates/api/src/community/follow.rs b/crates/api/src/community/follow.rs index bb7b80f00..853cfde14 100644 --- a/crates/api/src/community/follow.rs +++ b/crates/api/src/community/follow.rs @@ -15,15 +15,17 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::CommunityView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn follow_community( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { - let community = Community::read(&mut context.pool(), data.community_id).await?; +) -> LemmyResult> { + let community = Community::read(&mut context.pool(), data.community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; let mut community_follower_form = CommunityFollowerForm { community_id: community.id, person_id: local_user_view.person.id, @@ -62,7 +64,10 @@ pub async fn follow_community( let community_id = data.community_id; let person_id = local_user_view.person.id; let community_view = - CommunityView::read(&mut context.pool(), community_id, Some(person_id), false).await?; + CommunityView::read(&mut context.pool(), community_id, Some(person_id), false) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; + let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?; Ok(Json(CommunityResponse { diff --git a/crates/api/src/community/hide.rs b/crates/api/src/community/hide.rs index 27919a42b..997d88de3 100644 --- a/crates/api/src/community/hide.rs +++ b/crates/api/src/community/hide.rs @@ -15,14 +15,14 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn hide_community( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Verify its a admin (only admin can hide or unhide it) is_admin(&local_user_view)?; diff --git a/crates/api/src/community/transfer.rs b/crates/api/src/community/transfer.rs index 340bb6b63..5f3a6032e 100644 --- a/crates/api/src/community/transfer.rs +++ b/crates/api/src/community/transfer.rs @@ -15,7 +15,7 @@ use lemmy_db_schema::{ use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, location_info, }; @@ -26,7 +26,7 @@ pub async fn transfer_community( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let community_id = data.community_id; let mut community_mods = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?; @@ -79,8 +79,8 @@ pub async fn transfer_community( let person_id = local_user_view.person.id; let community_view = CommunityView::read(&mut context.pool(), community_id, Some(person_id), false) - .await - .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?; + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; let community_id = data.community_id; let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id) diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index d65ae0a28..4eee772c9 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -26,7 +26,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult}, + error::{LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult}, utils::slurs::check_slurs, }; use std::io::Cursor; @@ -44,7 +44,7 @@ pub mod site; pub mod sitemap; /// Converts the captcha to a base64 encoded wav audio file -pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> Result { +pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> LemmyResult { let letters = captcha.as_wav(); // Decode each wav file, concatenate the samples @@ -78,7 +78,7 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> Result Result<(), LemmyError> { +pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> LemmyResult<()> { let slur_regex = &local_site_to_slur_regex(local_site); check_slurs(reason, slur_regex)?; @@ -91,7 +91,7 @@ pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Resul } } -pub fn read_auth_token(req: &HttpRequest) -> Result, LemmyError> { +pub fn read_auth_token(req: &HttpRequest) -> LemmyResult> { // Try reading jwt from auth header if let Ok(header) = Authorization::::parse(req) { Ok(Some(header.as_ref().token().to_string())) @@ -135,7 +135,7 @@ pub(crate) fn generate_totp_2fa_secret() -> String { Secret::generate_secret().to_string() } -fn build_totp_2fa(hostname: &str, username: &str, secret: &str) -> Result { +fn build_totp_2fa(hostname: &str, username: &str, secret: &str) -> LemmyResult { let sec = Secret::Raw(secret.as_bytes().to_vec()); let sec_bytes = sec .to_bytes() @@ -248,11 +248,13 @@ pub(crate) async fn ban_nonlocal_user_from_local_communities( pub async fn local_user_view_from_jwt( jwt: &str, context: &LemmyContext, -) -> Result { +) -> LemmyResult { let local_user_id = Claims::validate(jwt, context) .await .with_lemmy_type(LemmyErrorType::NotLoggedIn)?; - let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?; + let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id) + .await? + .ok_or(LemmyErrorType::CouldntFindLocalUser)?; check_user_valid(&local_user_view.person)?; Ok(local_user_view) diff --git a/crates/api/src/local_user/add_admin.rs b/crates/api/src/local_user/add_admin.rs index 502335876..cd827454e 100644 --- a/crates/api/src/local_user/add_admin.rs +++ b/crates/api/src/local_user/add_admin.rs @@ -13,21 +13,21 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::PersonView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn add_admin( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Make sure user is an admin is_admin(&local_user_view)?; // Make sure that the person_id added is local let added_local_user = LocalUserView::read_person(&mut context.pool(), data.person_id) - .await - .with_lemmy_type(LemmyErrorType::ObjectNotLocal)?; + .await? + .ok_or(LemmyErrorType::ObjectNotLocal)?; let added_admin = LocalUser::update( &mut context.pool(), diff --git a/crates/api/src/local_user/ban_person.rs b/crates/api/src/local_user/ban_person.rs index f039c9a0c..c31940fba 100644 --- a/crates/api/src/local_user/ban_person.rs +++ b/crates/api/src/local_user/ban_person.rs @@ -18,7 +18,7 @@ use lemmy_db_schema::{ use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::PersonView; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::validation::is_valid_body_field, }; @@ -27,7 +27,7 @@ pub async fn ban_from_site( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Make sure user is an admin is_admin(&local_user_view)?; @@ -49,7 +49,7 @@ pub async fn ban_from_site( // if its a local user, invalidate logins let local_user = LocalUserView::read_person(&mut context.pool(), person.id).await; - if let Ok(local_user) = local_user { + if let Ok(Some(local_user)) = local_user { LoginToken::invalidate_all(&mut context.pool(), local_user.local_user.id).await?; } @@ -70,7 +70,9 @@ pub async fn ban_from_site( ModBan::create(&mut context.pool(), &form).await?; - let person_view = PersonView::read(&mut context.pool(), person.id).await?; + let person_view = PersonView::read(&mut context.pool(), person.id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; ban_nonlocal_user_from_local_communities( &local_user_view, diff --git a/crates/api/src/local_user/block.rs b/crates/api/src/local_user/block.rs index cb345616b..698703a9b 100644 --- a/crates/api/src/local_user/block.rs +++ b/crates/api/src/local_user/block.rs @@ -9,14 +9,14 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::PersonView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn block_person( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let target_id = data.person_id; let person_id = local_user_view.person.id; @@ -30,8 +30,12 @@ pub async fn block_person( target_id, }; - let target_user = LocalUserView::read_person(&mut context.pool(), target_id).await; - if target_user.map(|t| t.local_user.admin) == Ok(true) { + let target_user = LocalUserView::read_person(&mut context.pool(), target_id) + .await + .ok() + .flatten(); + + if target_user.is_some_and(|t| t.local_user.admin) { Err(LemmyErrorType::CantBlockAdmin)? } @@ -45,7 +49,9 @@ pub async fn block_person( .with_lemmy_type(LemmyErrorType::PersonBlockAlreadyExists)?; } - let person_view = PersonView::read(&mut context.pool(), target_id).await?; + let person_view = PersonView::read(&mut context.pool(), target_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; Ok(Json(BlockPersonResponse { person_view, blocked: data.block, diff --git a/crates/api/src/local_user/change_password.rs b/crates/api/src/local_user/change_password.rs index ab5b32dd9..50ee10bb6 100644 --- a/crates/api/src/local_user/change_password.rs +++ b/crates/api/src/local_user/change_password.rs @@ -11,7 +11,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::source::{local_user::LocalUser, login_token::LoginToken}; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn change_password( @@ -19,7 +19,7 @@ pub async fn change_password( req: HttpRequest, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { password_length_check(&data.new_password)?; // Make sure passwords match diff --git a/crates/api/src/local_user/change_password_after_reset.rs b/crates/api/src/local_user/change_password_after_reset.rs index 50a267d6a..9d693a750 100644 --- a/crates/api/src/local_user/change_password_after_reset.rs +++ b/crates/api/src/local_user/change_password_after_reset.rs @@ -10,18 +10,19 @@ use lemmy_db_schema::source::{ login_token::LoginToken, password_reset_request::PasswordResetRequest, }; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn change_password_after_reset( data: Json, context: Data, -) -> Result, LemmyError> { +) -> LemmyResult> { // Fetch the user_id from the token let token = data.token.clone(); let local_user_id = PasswordResetRequest::read_from_token(&mut context.pool(), &token) - .await - .map(|p| p.local_user_id)?; + .await? + .ok_or(LemmyErrorType::TokenNotFound)? + .local_user_id; password_length_check(&data.password)?; diff --git a/crates/api/src/local_user/generate_totp_secret.rs b/crates/api/src/local_user/generate_totp_secret.rs index 342a90a78..e8bb0284c 100644 --- a/crates/api/src/local_user/generate_totp_secret.rs +++ b/crates/api/src/local_user/generate_totp_secret.rs @@ -8,7 +8,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::source::local_user::{LocalUser, LocalUserUpdateForm}; use lemmy_db_views::structs::{LocalUserView, SiteView}; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; /// Generate a new secret for two-factor-authentication. Afterwards you need to call [toggle_totp] /// to enable it. This can only be called if 2FA is currently disabled. @@ -16,8 +16,10 @@ use lemmy_utils::error::{LemmyError, LemmyErrorType}; pub async fn generate_totp_secret( local_user_view: LocalUserView, context: Data, -) -> Result, LemmyError> { - let site_view = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult> { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; if local_user_view.local_user.totp_2fa_enabled { return Err(LemmyErrorType::TotpAlreadyEnabled)?; diff --git a/crates/api/src/local_user/get_captcha.rs b/crates/api/src/local_user/get_captcha.rs index 3d93a793c..ac64fa07c 100644 --- a/crates/api/src/local_user/get_captcha.rs +++ b/crates/api/src/local_user/get_captcha.rs @@ -17,10 +17,10 @@ use lemmy_db_schema::source::{ captcha_answer::{CaptchaAnswer, CaptchaAnswerForm}, local_site::LocalSite, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] -pub async fn get_captcha(context: Data) -> Result { +pub async fn get_captcha(context: Data) -> LemmyResult { let local_site = LocalSite::read(&mut context.pool()).await?; let mut res = HttpResponseBuilder::new(StatusCode::OK); res.insert_header(CacheControl(vec![CacheDirective::NoStore])); diff --git a/crates/api/src/local_user/list_banned.rs b/crates/api/src/local_user/list_banned.rs index 5c76d89a8..ba2c0d403 100644 --- a/crates/api/src/local_user/list_banned.rs +++ b/crates/api/src/local_user/list_banned.rs @@ -2,12 +2,12 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{context::LemmyContext, person::BannedPersonsResponse, utils::is_admin}; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::PersonView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; pub async fn list_banned_users( context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Make sure user is an admin is_admin(&local_user_view)?; diff --git a/crates/api/src/local_user/list_logins.rs b/crates/api/src/local_user/list_logins.rs index f1ae76be5..013236dcd 100644 --- a/crates/api/src/local_user/list_logins.rs +++ b/crates/api/src/local_user/list_logins.rs @@ -2,12 +2,12 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::login_token::LoginToken; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; pub async fn list_logins( context: Data, local_user_view: LocalUserView, -) -> Result>, LemmyError> { +) -> LemmyResult>> { let logins = LoginToken::list(&mut context.pool(), local_user_view.local_user.id).await?; Ok(Json(logins)) diff --git a/crates/api/src/local_user/list_media.rs b/crates/api/src/local_user/list_media.rs index 25df8a4c2..bf69c4a34 100644 --- a/crates/api/src/local_user/list_media.rs +++ b/crates/api/src/local_user/list_media.rs @@ -5,14 +5,14 @@ use lemmy_api_common::{ }; use lemmy_db_schema::source::images::LocalImage; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn list_media( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let page = data.page; let limit = data.limit; let images = LocalImage::get_all_paged_by_local_user_id( diff --git a/crates/api/src/local_user/login.rs b/crates/api/src/local_user/login.rs index 4eae762be..19f84f703 100644 --- a/crates/api/src/local_user/login.rs +++ b/crates/api/src/local_user/login.rs @@ -16,22 +16,24 @@ use lemmy_db_schema::{ RegistrationMode, }; use lemmy_db_views::structs::{LocalUserView, SiteView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn login( data: Json, req: HttpRequest, context: Data, -) -> Result, LemmyError> { - let site_view = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult> { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; // Fetch that username / email let username_or_email = data.username_or_email.clone(); let local_user_view = LocalUserView::find_by_email_or_name(&mut context.pool(), &username_or_email) - .await - .with_lemmy_type(LemmyErrorType::IncorrectLogin)?; + .await? + .ok_or(LemmyErrorType::IncorrectLogin)?; // Verify the password let valid: bool = verify( @@ -70,7 +72,7 @@ async fn check_registration_application( local_user_view: &LocalUserView, local_site: &LocalSite, pool: &mut DbPool<'_>, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { if (local_site.registration_mode == RegistrationMode::RequireApplication || local_site.registration_mode == RegistrationMode::Closed) && !local_user_view.local_user.accepted_application @@ -79,7 +81,9 @@ async fn check_registration_application( // Fetch the registration application. If no admin id is present its still pending. Otherwise it // was processed (either accepted or denied). let local_user_id = local_user_view.local_user.id; - let registration = RegistrationApplication::find_by_local_user_id(pool, local_user_id).await?; + let registration = RegistrationApplication::find_by_local_user_id(pool, local_user_id) + .await? + .ok_or(LemmyErrorType::CouldntFindRegistrationApplication)?; if registration.admin_id.is_some() { Err(LemmyErrorType::RegistrationDenied(registration.deny_reason))? } else { diff --git a/crates/api/src/local_user/notifications/list_mentions.rs b/crates/api/src/local_user/notifications/list_mentions.rs index 9f9ee3ae8..bf3cd8e0d 100644 --- a/crates/api/src/local_user/notifications/list_mentions.rs +++ b/crates/api/src/local_user/notifications/list_mentions.rs @@ -5,14 +5,14 @@ use lemmy_api_common::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::person_mention_view::PersonMentionQuery; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn list_mentions( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let sort = data.sort; let page = data.page; let limit = data.limit; diff --git a/crates/api/src/local_user/notifications/list_replies.rs b/crates/api/src/local_user/notifications/list_replies.rs index 555989721..d88595d96 100644 --- a/crates/api/src/local_user/notifications/list_replies.rs +++ b/crates/api/src/local_user/notifications/list_replies.rs @@ -5,14 +5,14 @@ use lemmy_api_common::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::comment_reply_view::CommentReplyQuery; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn list_replies( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let sort = data.sort; let page = data.page; let limit = data.limit; diff --git a/crates/api/src/local_user/notifications/mark_all_read.rs b/crates/api/src/local_user/notifications/mark_all_read.rs index d3667460b..558d276f7 100644 --- a/crates/api/src/local_user/notifications/mark_all_read.rs +++ b/crates/api/src/local_user/notifications/mark_all_read.rs @@ -6,13 +6,13 @@ use lemmy_db_schema::source::{ private_message::PrivateMessage, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn mark_all_notifications_read( context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let person_id = local_user_view.person.id; // Mark all comment_replies as read diff --git a/crates/api/src/local_user/notifications/mark_mention_read.rs b/crates/api/src/local_user/notifications/mark_mention_read.rs index 4cce598ac..90c8efb6e 100644 --- a/crates/api/src/local_user/notifications/mark_mention_read.rs +++ b/crates/api/src/local_user/notifications/mark_mention_read.rs @@ -9,16 +9,18 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::PersonMentionView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn mark_person_mention_as_read( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let person_mention_id = data.person_mention_id; - let read_person_mention = PersonMention::read(&mut context.pool(), person_mention_id).await?; + let read_person_mention = PersonMention::read(&mut context.pool(), person_mention_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPersonMention)?; if local_user_view.person.id != read_person_mention.recipient_id { Err(LemmyErrorType::CouldntUpdateComment)? @@ -37,7 +39,9 @@ pub async fn mark_person_mention_as_read( let person_mention_id = read_person_mention.id; let person_id = local_user_view.person.id; let person_mention_view = - PersonMentionView::read(&mut context.pool(), person_mention_id, Some(person_id)).await?; + PersonMentionView::read(&mut context.pool(), person_mention_id, Some(person_id)) + .await? + .ok_or(LemmyErrorType::CouldntFindPersonMention)?; Ok(Json(PersonMentionResponse { person_mention_view, diff --git a/crates/api/src/local_user/notifications/mark_reply_read.rs b/crates/api/src/local_user/notifications/mark_reply_read.rs index f7b259c94..fdcfa5727 100644 --- a/crates/api/src/local_user/notifications/mark_reply_read.rs +++ b/crates/api/src/local_user/notifications/mark_reply_read.rs @@ -9,16 +9,18 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::CommentReplyView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn mark_reply_as_read( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let comment_reply_id = data.comment_reply_id; - let read_comment_reply = CommentReply::read(&mut context.pool(), comment_reply_id).await?; + let read_comment_reply = CommentReply::read(&mut context.pool(), comment_reply_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommentReply)?; if local_user_view.person.id != read_comment_reply.recipient_id { Err(LemmyErrorType::CouldntUpdateComment)? @@ -38,7 +40,9 @@ pub async fn mark_reply_as_read( let comment_reply_id = read_comment_reply.id; let person_id = local_user_view.person.id; let comment_reply_view = - CommentReplyView::read(&mut context.pool(), comment_reply_id, Some(person_id)).await?; + CommentReplyView::read(&mut context.pool(), comment_reply_id, Some(person_id)) + .await? + .ok_or(LemmyErrorType::CouldntFindCommentReply)?; Ok(Json(CommentReplyResponse { comment_reply_view })) } diff --git a/crates/api/src/local_user/notifications/unread_count.rs b/crates/api/src/local_user/notifications/unread_count.rs index c0b1f0f2e..9d06f7c62 100644 --- a/crates/api/src/local_user/notifications/unread_count.rs +++ b/crates/api/src/local_user/notifications/unread_count.rs @@ -2,13 +2,13 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{context::LemmyContext, person::GetUnreadCountResponse}; use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; use lemmy_db_views_actor::structs::{CommentReplyView, PersonMentionView}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn unread_count( context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let person_id = local_user_view.person.id; let replies = CommentReplyView::get_unread_replies(&mut context.pool(), person_id).await?; diff --git a/crates/api/src/local_user/report_count.rs b/crates/api/src/local_user/report_count.rs index 3352f64eb..32448dcaa 100644 --- a/crates/api/src/local_user/report_count.rs +++ b/crates/api/src/local_user/report_count.rs @@ -10,14 +10,14 @@ use lemmy_db_views::structs::{ PostReportView, PrivateMessageReportView, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn report_count( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let person_id = local_user_view.person.id; let admin = local_user_view.local_user.admin; let community_id = data.community_id; diff --git a/crates/api/src/local_user/reset_password.rs b/crates/api/src/local_user/reset_password.rs index 414f506ba..edb5adee6 100644 --- a/crates/api/src/local_user/reset_password.rs +++ b/crates/api/src/local_user/reset_password.rs @@ -8,7 +8,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::source::password_reset_request::PasswordResetRequest; use lemmy_db_views::structs::{LocalUserView, SiteView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn reset_password( @@ -18,8 +18,8 @@ pub async fn reset_password( // Fetch that email let email = data.email.to_lowercase(); let local_user_view = LocalUserView::find_by_email(&mut context.pool(), &email) - .await - .with_lemmy_type(LemmyErrorType::IncorrectLogin)?; + .await? + .ok_or(LemmyErrorType::IncorrectLogin)?; // Check for too many attempts (to limit potential abuse) let recent_resets_count = PasswordResetRequest::get_recent_password_resets_count( @@ -30,7 +30,9 @@ pub async fn reset_password( if recent_resets_count >= 3 { Err(LemmyErrorType::PasswordResetLimitReached)? } - let site_view = SiteView::read_local(&mut context.pool()).await?; + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; check_email_verified(&local_user_view, &site_view)?; // Email the pure token to the user. diff --git a/crates/api/src/local_user/save_settings.rs b/crates/api/src/local_user/save_settings.rs index 972760a00..0805eb697 100644 --- a/crates/api/src/local_user/save_settings.rs +++ b/crates/api/src/local_user/save_settings.rs @@ -25,7 +25,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_utils::{ - error::{LemmyError, LemmyErrorType}, + error::{LemmyErrorType, LemmyResult}, utils::validation::{is_valid_bio_field, is_valid_display_name, is_valid_matrix_id}, }; @@ -34,8 +34,10 @@ pub async fn save_user_settings( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { - let site_view = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult> { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let slur_regex = local_site_to_slur_regex(&site_view.local_site); let url_blocklist = get_url_blocklist(&context).await?; diff --git a/crates/api/src/local_user/update_totp.rs b/crates/api/src/local_user/update_totp.rs index c8ca9f64e..c28ac7228 100644 --- a/crates/api/src/local_user/update_totp.rs +++ b/crates/api/src/local_user/update_totp.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::source::local_user::{LocalUser, LocalUserUpdateForm}; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; /// Enable or disable two-factor-authentication. The current setting is determined from /// [LocalUser.totp_2fa_enabled]. @@ -21,7 +21,7 @@ pub async fn update_totp( data: Json, local_user_view: LocalUserView, context: Data, -) -> Result, LemmyError> { +) -> LemmyResult> { check_totp_2fa_valid( &local_user_view, &Some(data.totp_token.clone()), diff --git a/crates/api/src/local_user/validate_auth.rs b/crates/api/src/local_user/validate_auth.rs index d95195dc9..36d31ff01 100644 --- a/crates/api/src/local_user/validate_auth.rs +++ b/crates/api/src/local_user/validate_auth.rs @@ -4,7 +4,7 @@ use actix_web::{ HttpRequest, }; use lemmy_api_common::{context::LemmyContext, SuccessResponse}; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; /// Returns an error message if the auth token is invalid for any reason. Necessary because other /// endpoints silently treat any call with invalid auth as unauthenticated. @@ -12,7 +12,7 @@ use lemmy_utils::error::{LemmyError, LemmyErrorType}; pub async fn validate_auth( req: HttpRequest, context: Data, -) -> Result, LemmyError> { +) -> LemmyResult> { let jwt = read_auth_token(&req)?; if let Some(jwt) = jwt { local_user_view_from_jwt(&jwt, &context).await?; diff --git a/crates/api/src/local_user/verify_email.rs b/crates/api/src/local_user/verify_email.rs index 94ddb373a..da490bf63 100644 --- a/crates/api/src/local_user/verify_email.rs +++ b/crates/api/src/local_user/verify_email.rs @@ -15,17 +15,19 @@ use lemmy_db_schema::{ RegistrationMode, }; use lemmy_db_views::structs::SiteView; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; pub async fn verify_email( data: Json, context: Data, ) -> LemmyResult> { - let site_view = SiteView::read_local(&mut context.pool()).await?; + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let token = data.token.clone(); let verification = EmailVerification::read_for_token(&mut context.pool(), &token) - .await - .with_lemmy_type(LemmyErrorType::TokenNotFound)?; + .await? + .ok_or(LemmyErrorType::TokenNotFound)?; let form = LocalUserUpdateForm { // necessary in case this is a new signup @@ -44,7 +46,10 @@ pub async fn verify_email( if site_view.local_site.registration_mode == RegistrationMode::RequireApplication && site_view.local_site.application_email_admins { - let person = Person::read(&mut context.pool(), local_user.person_id).await?; + let person = Person::read(&mut context.pool(), local_user.person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; + send_new_applicant_email_to_admins(&person.name, &mut context.pool(), context.settings()) .await?; } diff --git a/crates/api/src/post/feature.rs b/crates/api/src/post/feature.rs index 8c4b4978f..40cbf6794 100644 --- a/crates/api/src/post/feature.rs +++ b/crates/api/src/post/feature.rs @@ -16,16 +16,18 @@ use lemmy_db_schema::{ PostFeatureType, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; #[tracing::instrument(skip(context))] pub async fn feature_post( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let post_id = data.post_id; - let orig_post = Post::read(&mut context.pool(), post_id).await?; + let orig_post = Post::read(&mut context.pool(), post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; check_community_mod_action( &local_user_view.person, diff --git a/crates/api/src/post/get_link_metadata.rs b/crates/api/src/post/get_link_metadata.rs index a6a0c973b..8bc425125 100644 --- a/crates/api/src/post/get_link_metadata.rs +++ b/crates/api/src/post/get_link_metadata.rs @@ -4,13 +4,13 @@ use lemmy_api_common::{ post::{GetSiteMetadata, GetSiteMetadataResponse}, request::fetch_link_metadata, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn get_link_metadata( data: Query, context: Data, -) -> Result, LemmyError> { +) -> LemmyResult> { let metadata = fetch_link_metadata(&data.url, false, &context).await?; Ok(Json(GetSiteMetadataResponse { metadata })) diff --git a/crates/api/src/post/hide.rs b/crates/api/src/post/hide.rs index 1adfa110d..f7c21ef31 100644 --- a/crates/api/src/post/hide.rs +++ b/crates/api/src/post/hide.rs @@ -2,7 +2,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{context::LemmyContext, post::HidePost, SuccessResponse}; use lemmy_db_schema::source::post::PostHide; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, MAX_API_PARAM_ELEMENTS}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS}; use std::collections::HashSet; #[tracing::instrument(skip(context))] @@ -10,7 +10,7 @@ pub async fn hide_post( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let post_ids = HashSet::from_iter(data.post_ids.clone()); if post_ids.len() > MAX_API_PARAM_ELEMENTS { diff --git a/crates/api/src/post/like.rs b/crates/api/src/post/like.rs index d99d2a5e2..707874ded 100644 --- a/crates/api/src/post/like.rs +++ b/crates/api/src/post/like.rs @@ -21,7 +21,7 @@ use lemmy_db_schema::{ traits::{Crud, Likeable}, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use std::ops::Deref; #[tracing::instrument(skip(context))] @@ -29,7 +29,7 @@ pub async fn like_post( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; // Don't do a downvote if site has downvotes disabled @@ -38,7 +38,9 @@ pub async fn like_post( // Check for a community ban let post_id = data.post_id; - let post = Post::read(&mut context.pool(), post_id).await?; + let post = Post::read(&mut context.pool(), post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; check_community_user_action( &local_user_view.person, @@ -69,11 +71,15 @@ pub async fn like_post( // Mark the post as read mark_post_as_read(person_id, post_id, &mut context.pool()).await?; + let community = Community::read(&mut context.pool(), post.community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; + ActivityChannel::submit_activity( SendActivityData::LikePostOrComment { object_id: post.ap_id, actor: local_user_view.person.clone(), - community: Community::read(&mut context.pool(), post.community_id).await?, + community, score: data.score, }, &context, diff --git a/crates/api/src/post/list_post_likes.rs b/crates/api/src/post/list_post_likes.rs index 84690a41b..b9b2106b7 100644 --- a/crates/api/src/post/list_post_likes.rs +++ b/crates/api/src/post/list_post_likes.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{source::post::Post, traits::Crud}; use lemmy_db_views::structs::{LocalUserView, VoteView}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; /// Lists likes for a post #[tracing::instrument(skip(context))] @@ -14,8 +14,10 @@ pub async fn list_post_likes( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { - let post = Post::read(&mut context.pool(), data.post_id).await?; +) -> LemmyResult> { + let post = Post::read(&mut context.pool(), data.post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; is_mod_or_admin( &mut context.pool(), &local_user_view.person, diff --git a/crates/api/src/post/lock.rs b/crates/api/src/post/lock.rs index b581f37a2..05db8ebbb 100644 --- a/crates/api/src/post/lock.rs +++ b/crates/api/src/post/lock.rs @@ -15,16 +15,18 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; #[tracing::instrument(skip(context))] pub async fn lock_post( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let post_id = data.post_id; - let orig_post = Post::read(&mut context.pool(), post_id).await?; + let orig_post = Post::read(&mut context.pool(), post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; check_community_mod_action( &local_user_view.person, diff --git a/crates/api/src/post/mark_read.rs b/crates/api/src/post/mark_read.rs index bfc455f4f..3e534675a 100644 --- a/crates/api/src/post/mark_read.rs +++ b/crates/api/src/post/mark_read.rs @@ -2,7 +2,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{context::LemmyContext, post::MarkPostAsRead, SuccessResponse}; use lemmy_db_schema::source::post::PostRead; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, MAX_API_PARAM_ELEMENTS}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS}; use std::collections::HashSet; #[tracing::instrument(skip(context))] @@ -10,7 +10,7 @@ pub async fn mark_post_as_read( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let post_ids = HashSet::from_iter(data.post_ids.clone()); if post_ids.len() > MAX_API_PARAM_ELEMENTS { diff --git a/crates/api/src/post/save.rs b/crates/api/src/post/save.rs index 164840770..0876992ad 100644 --- a/crates/api/src/post/save.rs +++ b/crates/api/src/post/save.rs @@ -9,14 +9,14 @@ use lemmy_db_schema::{ traits::Saveable, }; use lemmy_db_views::structs::{LocalUserView, PostView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn save_post( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let post_saved_form = PostSavedForm { post_id: data.post_id, person_id: local_user_view.person.id, @@ -34,7 +34,9 @@ pub async fn save_post( let post_id = data.post_id; let person_id = local_user_view.person.id; - let post_view = PostView::read(&mut context.pool(), post_id, Some(person_id), false).await?; + let post_view = PostView::read(&mut context.pool(), post_id, Some(person_id), false) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; // Mark the post as read mark_post_as_read(person_id, post_id, &mut context.pool()).await?; diff --git a/crates/api/src/post_report/create.rs b/crates/api/src/post_report/create.rs index 1327d7bb9..72a40f70d 100644 --- a/crates/api/src/post_report/create.rs +++ b/crates/api/src/post_report/create.rs @@ -19,7 +19,7 @@ use lemmy_db_schema::{ traits::Reportable, }; use lemmy_db_views::structs::{LocalUserView, PostReportView, PostView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; /// Creates a post report and notifies the moderators of the community #[tracing::instrument(skip(context))] @@ -27,7 +27,7 @@ pub async fn create_post_report( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; let reason = data.reason.trim().to_string(); @@ -35,7 +35,9 @@ pub async fn create_post_report( let person_id = local_user_view.person.id; let post_id = data.post_id; - let post_view = PostView::read(&mut context.pool(), post_id, None, false).await?; + let post_view = PostView::read(&mut context.pool(), post_id, None, false) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; check_community_user_action( &local_user_view.person, @@ -59,7 +61,9 @@ pub async fn create_post_report( .await .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?; - let post_report_view = PostReportView::read(&mut context.pool(), report.id, person_id).await?; + let post_report_view = PostReportView::read(&mut context.pool(), report.id, person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPostReport)?; // Email the admins if local_site.reports_email_admins { diff --git a/crates/api/src/post_report/list.rs b/crates/api/src/post_report/list.rs index 1f1aa9653..7d1d50b0b 100644 --- a/crates/api/src/post_report/list.rs +++ b/crates/api/src/post_report/list.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ utils::check_community_mod_of_any_or_admin_action, }; use lemmy_db_views::{post_report_view::PostReportQuery, structs::LocalUserView}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; /// Lists post reports for a community if an id is supplied /// or returns all post reports for communities a user moderates @@ -14,7 +14,7 @@ pub async fn list_post_reports( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let community_id = data.community_id; let post_id = data.post_id; let unresolved_only = data.unresolved_only.unwrap_or_default(); diff --git a/crates/api/src/post_report/resolve.rs b/crates/api/src/post_report/resolve.rs index ab6688012..428619674 100644 --- a/crates/api/src/post_report/resolve.rs +++ b/crates/api/src/post_report/resolve.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable}; use lemmy_db_views::structs::{LocalUserView, PostReportView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; /// Resolves or unresolves a post report and notifies the moderators of the community #[tracing::instrument(skip(context))] @@ -14,10 +14,12 @@ pub async fn resolve_post_report( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let report_id = data.report_id; let person_id = local_user_view.person.id; - let report = PostReportView::read(&mut context.pool(), report_id, person_id).await?; + let report = PostReportView::read(&mut context.pool(), report_id, person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPostReport)?; let person_id = local_user_view.person.id; check_community_mod_action( @@ -38,7 +40,9 @@ pub async fn resolve_post_report( .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?; } - let post_report_view = PostReportView::read(&mut context.pool(), report_id, person_id).await?; + let post_report_view = PostReportView::read(&mut context.pool(), report_id, person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPostReport)?; Ok(Json(PostReportResponse { post_report_view })) } diff --git a/crates/api/src/private_message/mark_read.rs b/crates/api/src/private_message/mark_read.rs index 6b089c0ab..07e06fe21 100644 --- a/crates/api/src/private_message/mark_read.rs +++ b/crates/api/src/private_message/mark_read.rs @@ -8,17 +8,19 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn mark_pm_as_read( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Checking permissions let private_message_id = data.private_message_id; - let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?; + let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPrivateMessage)?; if local_user_view.person.id != orig_private_message.recipient_id { Err(LemmyErrorType::CouldntUpdatePrivateMessage)? } @@ -37,7 +39,9 @@ pub async fn mark_pm_as_read( .await .with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?; - let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?; + let view = PrivateMessageView::read(&mut context.pool(), private_message_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPrivateMessage)?; Ok(Json(PrivateMessageResponse { private_message_view: view, })) diff --git a/crates/api/src/private_message_report/create.rs b/crates/api/src/private_message_report/create.rs index 7aca9661b..41ac592ae 100644 --- a/crates/api/src/private_message_report/create.rs +++ b/crates/api/src/private_message_report/create.rs @@ -14,14 +14,14 @@ use lemmy_db_schema::{ traits::{Crud, Reportable}, }; use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn create_pm_report( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; let reason = data.reason.trim().to_string(); @@ -29,7 +29,9 @@ pub async fn create_pm_report( let person_id = local_user_view.person.id; let private_message_id = data.private_message_id; - let private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?; + let private_message = PrivateMessage::read(&mut context.pool(), private_message_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPrivateMessage)?; // Make sure that only the recipient of the private message can create a report if person_id != private_message.recipient_id { @@ -47,8 +49,9 @@ pub async fn create_pm_report( .await .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?; - let private_message_report_view = - PrivateMessageReportView::read(&mut context.pool(), report.id).await?; + let private_message_report_view = PrivateMessageReportView::read(&mut context.pool(), report.id) + .await? + .ok_or(LemmyErrorType::CouldntFindPrivateMessageReport)?; // Email the admins if local_site.reports_email_admins { diff --git a/crates/api/src/private_message_report/list.rs b/crates/api/src/private_message_report/list.rs index 2dc3e6efc..79ef53e1c 100644 --- a/crates/api/src/private_message_report/list.rs +++ b/crates/api/src/private_message_report/list.rs @@ -8,14 +8,14 @@ use lemmy_db_views::{ private_message_report_view::PrivateMessageReportQuery, structs::LocalUserView, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn list_pm_reports( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { is_admin(&local_user_view)?; let unresolved_only = data.unresolved_only.unwrap_or_default(); diff --git a/crates/api/src/private_message_report/resolve.rs b/crates/api/src/private_message_report/resolve.rs index 202fdcd29..27847eeaf 100644 --- a/crates/api/src/private_message_report/resolve.rs +++ b/crates/api/src/private_message_report/resolve.rs @@ -6,14 +6,14 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{source::private_message_report::PrivateMessageReport, traits::Reportable}; use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn resolve_pm_report( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { is_admin(&local_user_view)?; let report_id = data.report_id; @@ -28,8 +28,9 @@ pub async fn resolve_pm_report( .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?; } - let private_message_report_view = - PrivateMessageReportView::read(&mut context.pool(), report_id).await?; + let private_message_report_view = PrivateMessageReportView::read(&mut context.pool(), report_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPrivateMessageReport)?; Ok(Json(PrivateMessageReportResponse { private_message_report_view, diff --git a/crates/api/src/site/block.rs b/crates/api/src/site/block.rs index 7d28e43d5..823dda612 100644 --- a/crates/api/src/site/block.rs +++ b/crates/api/src/site/block.rs @@ -9,14 +9,14 @@ use lemmy_db_schema::{ traits::Blockable, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn block_instance( data: Json, local_user_view: LocalUserView, context: Data, -) -> Result, LemmyError> { +) -> LemmyResult> { let instance_id = data.instance_id; let person_id = local_user_view.person.id; if local_user_view.person.instance_id == instance_id { diff --git a/crates/api/src/site/federated_instances.rs b/crates/api/src/site/federated_instances.rs index 8f224b2eb..66b0ff2c1 100644 --- a/crates/api/src/site/federated_instances.rs +++ b/crates/api/src/site/federated_instances.rs @@ -5,13 +5,15 @@ use lemmy_api_common::{ utils::build_federated_instances, }; use lemmy_db_views::structs::SiteView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; #[tracing::instrument(skip(context))] pub async fn get_federated_instances( context: Data, -) -> Result, LemmyError> { - let site_view = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult> { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let federated_instances = build_federated_instances(&site_view.local_site, &mut context.pool()).await?; diff --git a/crates/api/src/site/leave_admin.rs b/crates/api/src/site/leave_admin.rs index 57a134238..9548a2450 100644 --- a/crates/api/src/site/leave_admin.rs +++ b/crates/api/src/site/leave_admin.rs @@ -14,7 +14,7 @@ use lemmy_db_schema::{ use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_db_views_actor::structs::PersonView; use lemmy_utils::{ - error::{LemmyError, LemmyErrorType}, + error::{LemmyErrorType, LemmyResult}, VERSION, }; @@ -22,7 +22,7 @@ use lemmy_utils::{ pub async fn leave_admin( context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { is_admin(&local_user_view)?; // Make sure there isn't just one admin (so if one leaves, there will still be one left) @@ -55,7 +55,9 @@ pub async fn leave_admin( ModAdd::create(&mut context.pool(), &form).await?; // Reread site and admins - let site_view = SiteView::read_local(&mut context.pool()).await?; + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let admins = PersonView::admins(&mut context.pool()).await?; let all_languages = Language::read_all(&mut context.pool()).await?; diff --git a/crates/api/src/site/list_all_media.rs b/crates/api/src/site/list_all_media.rs index 495e72e48..49132cd64 100644 --- a/crates/api/src/site/list_all_media.rs +++ b/crates/api/src/site/list_all_media.rs @@ -6,14 +6,14 @@ use lemmy_api_common::{ }; use lemmy_db_schema::source::images::LocalImage; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn list_all_media( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Only let admins view all media is_admin(&local_user_view)?; diff --git a/crates/api/src/site/mod_log.rs b/crates/api/src/site/mod_log.rs index 1a4148d89..8f5538566 100644 --- a/crates/api/src/site/mod_log.rs +++ b/crates/api/src/site/mod_log.rs @@ -24,7 +24,7 @@ use lemmy_db_views_moderator::structs::{ ModTransferCommunityView, ModlogListParams, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use ModlogActionType::*; #[tracing::instrument(skip(context))] @@ -32,7 +32,7 @@ pub async fn get_mod_log( data: Query, context: Data, local_user_view: Option, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; check_private_instance(&local_user_view, &local_site)?; diff --git a/crates/api/src/site/purge/comment.rs b/crates/api/src/site/purge/comment.rs index a06085f24..70d95e160 100644 --- a/crates/api/src/site/purge/comment.rs +++ b/crates/api/src/site/purge/comment.rs @@ -15,21 +15,23 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::{CommentView, LocalUserView}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; #[tracing::instrument(skip(context))] pub async fn purge_comment( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Only let admin purge an item is_admin(&local_user_view)?; let comment_id = data.comment_id; // Read the comment to get the post_id and community - let comment_view = CommentView::read(&mut context.pool(), comment_id, None).await?; + let comment_view = CommentView::read(&mut context.pool(), comment_id, None) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; let post_id = comment_view.comment.post_id; diff --git a/crates/api/src/site/purge/community.rs b/crates/api/src/site/purge/community.rs index 61e58ba04..14b250681 100644 --- a/crates/api/src/site/purge/community.rs +++ b/crates/api/src/site/purge/community.rs @@ -16,19 +16,21 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; #[tracing::instrument(skip(context))] pub async fn purge_community( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Only let admin purge an item is_admin(&local_user_view)?; // Read the community to get its images - let community = Community::read(&mut context.pool(), data.community_id).await?; + let community = Community::read(&mut context.pool(), data.community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; if let Some(banner) = &community.banner { purge_image_from_pictrs(banner, &context).await.ok(); diff --git a/crates/api/src/site/purge/person.rs b/crates/api/src/site/purge/person.rs index 6023d7b41..1b38752c7 100644 --- a/crates/api/src/site/purge/person.rs +++ b/crates/api/src/site/purge/person.rs @@ -16,18 +16,20 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; #[tracing::instrument(skip(context))] pub async fn purge_person( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Only let admin purge an item is_admin(&local_user_view)?; - let person = Person::read(&mut context.pool(), data.person_id).await?; + let person = Person::read(&mut context.pool(), data.person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; ban_nonlocal_user_from_local_communities( &local_user_view, &person, diff --git a/crates/api/src/site/purge/post.rs b/crates/api/src/site/purge/post.rs index 28e6668ff..75cd021d1 100644 --- a/crates/api/src/site/purge/post.rs +++ b/crates/api/src/site/purge/post.rs @@ -16,19 +16,21 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; #[tracing::instrument(skip(context))] pub async fn purge_post( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Only let admin purge an item is_admin(&local_user_view)?; // Read the post to get the community_id - let post = Post::read(&mut context.pool(), data.post_id).await?; + let post = Post::read(&mut context.pool(), data.post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; // Purge image if let Some(url) = &post.url { diff --git a/crates/api/src/site/registration_applications/approve.rs b/crates/api/src/site/registration_applications/approve.rs index 036a60e00..0fb55ffc8 100644 --- a/crates/api/src/site/registration_applications/approve.rs +++ b/crates/api/src/site/registration_applications/approve.rs @@ -13,13 +13,13 @@ use lemmy_db_schema::{ utils::diesel_option_overwrite, }; use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; pub async fn approve_registration_application( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let app_id = data.id; // Only let admins do this @@ -45,8 +45,9 @@ pub async fn approve_registration_application( LocalUser::update(&mut context.pool(), approved_user_id, &local_user_form).await?; if data.approve { - let approved_local_user_view = - LocalUserView::read(&mut context.pool(), approved_user_id).await?; + let approved_local_user_view = LocalUserView::read(&mut context.pool(), approved_user_id) + .await? + .ok_or(LemmyErrorType::CouldntFindLocalUser)?; if approved_local_user_view.local_user.email.is_some() { send_application_approved_email(&approved_local_user_view, context.settings()).await?; @@ -54,8 +55,9 @@ pub async fn approve_registration_application( } // Read the view - let registration_application = - RegistrationApplicationView::read(&mut context.pool(), app_id).await?; + let registration_application = RegistrationApplicationView::read(&mut context.pool(), app_id) + .await? + .ok_or(LemmyErrorType::CouldntFindRegistrationApplication)?; Ok(Json(RegistrationApplicationResponse { registration_application, diff --git a/crates/api/src/site/registration_applications/list.rs b/crates/api/src/site/registration_applications/list.rs index 30ce9aaf2..df86b11d5 100644 --- a/crates/api/src/site/registration_applications/list.rs +++ b/crates/api/src/site/registration_applications/list.rs @@ -9,14 +9,14 @@ use lemmy_db_views::{ registration_application_view::RegistrationApplicationQuery, structs::LocalUserView, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; /// Lists registration applications, filterable by undenied only. pub async fn list_registration_applications( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; // Make sure user is an admin diff --git a/crates/api/src/site/registration_applications/unread_count.rs b/crates/api/src/site/registration_applications/unread_count.rs index 255859198..a12ecb1d3 100644 --- a/crates/api/src/site/registration_applications/unread_count.rs +++ b/crates/api/src/site/registration_applications/unread_count.rs @@ -6,12 +6,12 @@ use lemmy_api_common::{ }; use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; pub async fn get_unread_registration_application_count( context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; // Only let admins do this diff --git a/crates/api_common/src/build_response.rs b/crates/api_common/src/build_response.rs index 990b96544..f77a90882 100644 --- a/crates/api_common/src/build_response.rs +++ b/crates/api_common/src/build_response.rs @@ -25,8 +25,9 @@ use lemmy_db_schema::{ use lemmy_db_views::structs::{CommentView, LocalUserView, PostView}; use lemmy_db_views_actor::structs::CommunityView; use lemmy_utils::{ - error::LemmyError, + error::LemmyResult, utils::{markdown::markdown_to_html, mention::MentionData}, + LemmyErrorType, }; pub async fn build_comment_response( @@ -34,9 +35,11 @@ pub async fn build_comment_response( comment_id: CommentId, local_user_view: Option, recipient_ids: Vec, -) -> Result { +) -> LemmyResult { let person_id = local_user_view.map(|l| l.person.id); - let comment_view = CommentView::read(&mut context.pool(), comment_id, person_id).await?; + let comment_view = CommentView::read(&mut context.pool(), comment_id, person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; Ok(CommentResponse { comment_view, recipient_ids, @@ -47,7 +50,7 @@ pub async fn build_community_response( context: &LemmyContext, local_user_view: LocalUserView, community_id: CommunityId, -) -> Result, LemmyError> { +) -> LemmyResult> { let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &local_user_view.person, community_id) .await .is_ok(); @@ -58,7 +61,8 @@ pub async fn build_community_response( Some(person_id), is_mod_or_admin, ) - .await?; + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?; Ok(Json(CommunityResponse { @@ -72,7 +76,7 @@ pub async fn build_post_response( community_id: CommunityId, person: &Person, post_id: PostId, -) -> Result, LemmyError> { +) -> LemmyResult> { let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), person, community_id) .await .is_ok(); @@ -82,7 +86,8 @@ pub async fn build_post_response( Some(person.id), is_mod_or_admin, ) - .await?; + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; Ok(Json(PostResponse { post_view })) } @@ -94,12 +99,14 @@ pub async fn send_local_notifs( person: &Person, do_send_email: bool, context: &LemmyContext, -) -> Result, LemmyError> { +) -> LemmyResult> { let mut recipient_ids = Vec::new(); let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname()); // Read the comment view to get extra info - let comment_view = CommentView::read(&mut context.pool(), comment_id, None).await?; + let comment_view = CommentView::read(&mut context.pool(), comment_id, None) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; let comment = comment_view.comment; let post = comment_view.post; let community = comment_view.community; @@ -111,7 +118,7 @@ pub async fn send_local_notifs( { let mention_name = mention.name.clone(); let user_view = LocalUserView::read_from_name(&mut context.pool(), &mention_name).await; - if let Ok(mention_user_view) = user_view { + if let Ok(Some(mention_user_view)) = user_view { // TODO // At some point, make it so you can't tag the parent creator either // Potential duplication of notifications, one for reply and the other for mention, is handled below by checking recipient ids @@ -146,7 +153,9 @@ pub async fn send_local_notifs( // Send comment_reply to the parent commenter / poster if let Some(parent_comment_id) = comment.parent_comment_id() { - let parent_comment = Comment::read(&mut context.pool(), parent_comment_id).await?; + let parent_comment = Comment::read(&mut context.pool(), parent_comment_id) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; // Get the parent commenter local_user let parent_creator_id = parent_comment.creator_id; @@ -165,7 +174,7 @@ pub async fn send_local_notifs( // Don't send a notif to yourself if parent_comment.creator_id != person.id && !check_blocks { let user_view = LocalUserView::read_person(&mut context.pool(), parent_creator_id).await; - if let Ok(parent_user_view) = user_view { + if let Ok(Some(parent_user_view)) = user_view { // Don't duplicate notif if already mentioned by checking recipient ids if !recipient_ids.contains(&parent_user_view.local_user.id) { recipient_ids.push(parent_user_view.local_user.id); @@ -212,7 +221,7 @@ pub async fn send_local_notifs( if post.creator_id != person.id && !check_blocks { let creator_id = post.creator_id; let parent_user = LocalUserView::read_person(&mut context.pool(), creator_id).await; - if let Ok(parent_user_view) = parent_user { + if let Ok(Some(parent_user_view)) = parent_user { if !recipient_ids.contains(&parent_user_view.local_user.id) { recipient_ids.push(parent_user_view.local_user.id); diff --git a/crates/api_common/src/claims.rs b/crates/api_common/src/claims.rs index 19145488a..a926cb0ba 100644 --- a/crates/api_common/src/claims.rs +++ b/crates/api_common/src/claims.rs @@ -99,7 +99,7 @@ mod tests { async fn test_should_not_validate_user_token_after_password_change() { let pool_ = build_db_pool_for_tests().await; let pool = &mut (&pool_).into(); - let secret = Secret::init(pool).await.unwrap(); + let secret = Secret::init(pool).await.unwrap().unwrap(); let context = LemmyContext::create( pool_.clone(), ClientBuilder::new(Client::default()).build(), diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs index b3a5293df..78881deb8 100644 --- a/crates/api_common/src/lib.rs +++ b/crates/api_common/src/lib.rs @@ -28,7 +28,7 @@ pub extern crate lemmy_utils; pub use lemmy_utils::LemmyErrorType; use serde::{Deserialize, Serialize}; -use std::time::Duration; +use std::{cmp::min, time::Duration}; #[derive(Debug, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(ts_rs::TS))] @@ -44,7 +44,39 @@ impl Default for SuccessResponse { } } -/// how long to sleep based on how many retries have already happened +// TODO: use from_days once stabilized +// https://github.com/rust-lang/rust/issues/120301 +const DAY: Duration = Duration::from_secs(24 * 60 * 60); + +/// Calculate how long to sleep until next federation send based on how many +/// retries have already happened. Uses exponential backoff with maximum of one day. The first +/// error is ignored. pub fn federate_retry_sleep_duration(retry_count: i32) -> Duration { - Duration::from_secs_f64(2.0_f64.powf(f64::from(retry_count))) + debug_assert!(retry_count != 0); + if retry_count == 1 { + return Duration::from_secs(0); + } + let retry_count = retry_count - 1; + let pow = 1.25_f64.powf(retry_count.into()); + let pow = Duration::try_from_secs_f64(pow).unwrap_or(DAY); + min(DAY, pow) +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + + #[test] + fn test_federate_retry_sleep_duration() { + assert_eq!(Duration::from_secs(0), federate_retry_sleep_duration(1)); + assert_eq!( + Duration::new(1, 250000000), + federate_retry_sleep_duration(2) + ); + assert_eq!( + Duration::new(2, 441406250), + federate_retry_sleep_duration(5) + ); + assert_eq!(DAY, federate_retry_sleep_duration(100)); + } } diff --git a/crates/api_common/src/request.rs b/crates/api_common/src/request.rs index d77ea2daa..912fbc67e 100644 --- a/crates/api_common/src/request.rs +++ b/crates/api_common/src/request.rs @@ -16,7 +16,7 @@ use lemmy_db_schema::{ }, }; use lemmy_utils::{ - error::{LemmyError, LemmyErrorType}, + error::{LemmyError, LemmyErrorType, LemmyResult}, settings::structs::{PictrsImageMode, Settings}, spawn_try_task, REQWEST_TIMEOUT, @@ -46,7 +46,7 @@ pub async fn fetch_link_metadata( url: &Url, generate_thumbnail: bool, context: &LemmyContext, -) -> Result { +) -> LemmyResult { info!("Fetching site metadata for url: {}", url); let response = context.client().get(url.as_str()).send().await?; @@ -97,29 +97,46 @@ pub fn generate_post_link_metadata( context: Data, ) { spawn_try_task(async move { + // Decide if the thumbnail should be generated let allow_sensitive = local_site_opt_to_sensitive(&local_site); let page_is_sensitive = post.nsfw; let allow_generate_thumbnail = allow_sensitive || !page_is_sensitive; - let mut thumbnail_url = custom_thumbnail.or_else(|| post.thumbnail_url.map(Into::into)); - let do_generate_thumbnail = thumbnail_url.is_none() && allow_generate_thumbnail; + let do_generate_thumbnail = + allow_generate_thumbnail && custom_thumbnail.is_none() && post.thumbnail_url.is_none(); // Generate local thumbnail only if no thumbnail was federated and 'sensitive' attributes allow it. let metadata = fetch_link_metadata_opt( - post.url.map(Into::into).as_ref(), + post.url.as_ref().map(DbUrl::inner), do_generate_thumbnail, &context, ) .await; - if let Some(thumbnail_url_) = metadata.thumbnail { - thumbnail_url = Some(thumbnail_url_.into()); - } - let thumbnail_url = proxy_image_link_opt_apub(thumbnail_url, &context).await?; + + // If its an image post, it needs to overwrite the thumbnail, and take precedence + let image_url = if metadata + .content_type + .as_ref() + .is_some_and(|content_type| content_type.starts_with("image")) + { + post.url.map(Into::into) + } else { + None + }; + + // Build the thumbnail url based on either the post image url, custom thumbnail, metadata fetch, or existing thumbnail. + let thumbnail_url = image_url + .or(custom_thumbnail) + .or(metadata.thumbnail.map(Into::into)) + .or(post.thumbnail_url.map(Into::into)); + + // Proxy the image fetch if necessary + let proxied_thumbnail_url = proxy_image_link_opt_apub(thumbnail_url, &context).await?; let form = PostUpdateForm { embed_title: Some(metadata.opengraph_data.title), embed_description: Some(metadata.opengraph_data.description), embed_video_url: Some(metadata.opengraph_data.embed_video_url), - thumbnail_url: Some(thumbnail_url), + thumbnail_url: Some(proxied_thumbnail_url), url_content_type: Some(metadata.content_type), ..Default::default() }; @@ -132,7 +149,7 @@ pub fn generate_post_link_metadata( } /// Extract site metadata from HTML Opengraph attributes. -fn extract_opengraph_data(html_bytes: &[u8], url: &Url) -> Result { +fn extract_opengraph_data(html_bytes: &[u8], url: &Url) -> LemmyResult { let html = String::from_utf8_lossy(html_bytes); // Make sure the first line is doctype html @@ -240,10 +257,7 @@ struct PictrsPurgeResponse { /// - It might fail due to image being not local /// - It might not be an image /// - Pictrs might not be set up -pub async fn purge_image_from_pictrs( - image_url: &Url, - context: &LemmyContext, -) -> Result<(), LemmyError> { +pub async fn purge_image_from_pictrs(image_url: &Url, context: &LemmyContext) -> LemmyResult<()> { is_image_content_type(context.client(), image_url).await?; let alias = image_url @@ -278,7 +292,7 @@ pub async fn delete_image_from_pictrs( alias: &str, delete_token: &str, context: &LemmyContext, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let pictrs_config = context.settings().pictrs_config()?; let url = format!( "{}image/delete/{}/{}", @@ -296,15 +310,16 @@ pub async fn delete_image_from_pictrs( /// Retrieves the image with local pict-rs and generates a thumbnail. Returns the thumbnail url. #[tracing::instrument(skip_all)] -async fn generate_pictrs_thumbnail( - image_url: &Url, - context: &LemmyContext, -) -> Result { +async fn generate_pictrs_thumbnail(image_url: &Url, context: &LemmyContext) -> LemmyResult { let pictrs_config = context.settings().pictrs_config()?; - if pictrs_config.image_mode() == PictrsImageMode::ProxyAllImages { - return Ok(proxy_image_link(image_url.clone(), context).await?.into()); - } + match pictrs_config.image_mode() { + PictrsImageMode::None => return Ok(image_url.clone()), + PictrsImageMode::ProxyAllImages => { + return Ok(proxy_image_link(image_url.clone(), context).await?.into()) + } + _ => {} + }; // fetch remote non-pictrs images for persistent thumbnail link // TODO: should limit size once supported by pictrs @@ -345,7 +360,7 @@ async fn generate_pictrs_thumbnail( // TODO: get rid of this by reading content type from db #[tracing::instrument(skip_all)] -async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> Result<(), LemmyError> { +async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> LemmyResult<()> { let response = client.get(url.as_str()).send().await?; if response .headers() @@ -365,7 +380,7 @@ pub async fn replace_image( new_image: &Option, old_image: &Option, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { if new_image.is_some() { // Ignore errors because image may be stored externally. if let Some(avatar) = &old_image { diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index dd0a8c0c6..d75469904 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -42,7 +42,7 @@ use lemmy_utils::{ markdown::{markdown_check_for_blocked_urls, markdown_rewrite_image_links}, slurs::{build_slur_regex, remove_slurs}, }, - CACHE_DURATION_SHORT, + CACHE_DURATION_FEDERATION, }; use moka::future::Cache; use once_cell::sync::Lazy; @@ -60,7 +60,7 @@ pub async fn is_mod_or_admin( pool: &mut DbPool<'_>, person: &Person, community_id: CommunityId, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { check_user_valid(person)?; let is_mod_or_admin = CommunityView::is_mod_or_admin(pool, person.id, community_id).await?; @@ -76,7 +76,7 @@ pub async fn is_mod_or_admin_opt( pool: &mut DbPool<'_>, local_user_view: Option<&LocalUserView>, community_id: Option, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { if let Some(local_user_view) = local_user_view { if let Some(community_id) = community_id { is_mod_or_admin(pool, &local_user_view.person, community_id).await @@ -108,7 +108,7 @@ pub async fn check_community_mod_of_any_or_admin_action( } } -pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> { +pub fn is_admin(local_user_view: &LocalUserView) -> LemmyResult<()> { check_user_valid(&local_user_view.person)?; if !local_user_view.local_user.admin { Err(LemmyErrorType::NotAnAdmin)? @@ -122,7 +122,7 @@ pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> { pub fn is_top_mod( local_user_view: &LocalUserView, community_mods: &[CommunityModeratorView], -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { check_user_valid(&local_user_view.person)?; if local_user_view.person.id != community_mods @@ -137,10 +137,10 @@ pub fn is_top_mod( } #[tracing::instrument(skip_all)] -pub async fn get_post(post_id: PostId, pool: &mut DbPool<'_>) -> Result { +pub async fn get_post(post_id: PostId, pool: &mut DbPool<'_>) -> LemmyResult { Post::read(pool, post_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntFindPost) + .await? + .ok_or(LemmyErrorType::CouldntFindPost.into()) } #[tracing::instrument(skip_all)] @@ -148,14 +148,14 @@ pub async fn mark_post_as_read( person_id: PersonId, post_id: PostId, pool: &mut DbPool<'_>, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { PostRead::mark_as_read(pool, HashSet::from([post_id]), person_id) .await .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)?; Ok(()) } -pub fn check_user_valid(person: &Person) -> Result<(), LemmyError> { +pub fn check_user_valid(person: &Person) -> LemmyResult<()> { // Check for a site ban if person.banned { Err(LemmyErrorType::SiteBan)? @@ -188,8 +188,8 @@ async fn check_community_deleted_removed( pool: &mut DbPool<'_>, ) -> LemmyResult<()> { let community = Community::read(pool, community_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?; + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; if community.deleted || community.removed { Err(LemmyErrorType::Deleted)? } @@ -230,7 +230,7 @@ pub async fn check_community_mod_action( } /// Don't allow creating reports for removed / deleted posts -pub fn check_post_deleted_or_removed(post: &Post) -> Result<(), LemmyError> { +pub fn check_post_deleted_or_removed(post: &Post) -> LemmyResult<()> { if post.deleted || post.removed { Err(LemmyErrorType::Deleted)? } else { @@ -238,7 +238,7 @@ pub fn check_post_deleted_or_removed(post: &Post) -> Result<(), LemmyError> { } } -pub fn check_comment_deleted_or_removed(comment: &Comment) -> Result<(), LemmyError> { +pub fn check_comment_deleted_or_removed(comment: &Comment) -> LemmyResult<()> { if comment.deleted || comment.removed { Err(LemmyErrorType::Deleted)? } else { @@ -252,7 +252,7 @@ pub async fn check_person_block( my_id: PersonId, potential_blocker_id: PersonId, pool: &mut DbPool<'_>, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let is_blocked = PersonBlock::read(pool, potential_blocker_id, my_id).await?; if is_blocked { Err(LemmyErrorType::PersonIsBlocked)? @@ -267,7 +267,7 @@ async fn check_community_block( community_id: CommunityId, person_id: PersonId, pool: &mut DbPool<'_>, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let is_blocked = CommunityBlock::read(pool, person_id, community_id).await?; if is_blocked { Err(LemmyErrorType::CommunityIsBlocked)? @@ -282,7 +282,7 @@ async fn check_instance_block( instance_id: InstanceId, person_id: PersonId, pool: &mut DbPool<'_>, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let is_blocked = InstanceBlock::read(pool, person_id, instance_id).await?; if is_blocked { Err(LemmyErrorType::InstanceIsBlocked)? @@ -298,7 +298,7 @@ pub async fn check_person_instance_community_block( community_instance_id: InstanceId, community_id: CommunityId, pool: &mut DbPool<'_>, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { check_person_block(my_id, potential_blocker_id, pool).await?; check_instance_block(community_instance_id, potential_blocker_id, pool).await?; check_community_block(community_id, potential_blocker_id, pool).await?; @@ -306,7 +306,7 @@ pub async fn check_person_instance_community_block( } #[tracing::instrument(skip_all)] -pub fn check_downvotes_enabled(score: i16, local_site: &LocalSite) -> Result<(), LemmyError> { +pub fn check_downvotes_enabled(score: i16, local_site: &LocalSite) -> LemmyResult<()> { if score == -1 && !local_site.enable_downvotes { Err(LemmyErrorType::DownvotesAreDisabled)? } else { @@ -316,7 +316,7 @@ pub fn check_downvotes_enabled(score: i16, local_site: &LocalSite) -> Result<(), /// Dont allow bots to do certain actions, like voting #[tracing::instrument(skip_all)] -pub fn check_bot_account(person: &Person) -> Result<(), LemmyError> { +pub fn check_bot_account(person: &Person) -> LemmyResult<()> { if person.bot_account { Err(LemmyErrorType::InvalidBotAction)? } else { @@ -328,7 +328,7 @@ pub fn check_bot_account(person: &Person) -> Result<(), LemmyError> { pub fn check_private_instance( local_user_view: &Option, local_site: &LocalSite, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { if local_user_view.is_none() && local_site.private_instance { Err(LemmyErrorType::InstanceIsPrivate)? } else { @@ -340,7 +340,7 @@ pub fn check_private_instance( pub async fn build_federated_instances( local_site: &LocalSite, pool: &mut DbPool<'_>, -) -> Result, LemmyError> { +) -> LemmyResult> { if local_site.federation_enabled { let mut linked = Vec::new(); let mut allowed = Vec::new(); @@ -375,7 +375,7 @@ pub async fn build_federated_instances( } /// Checks the password length -pub fn password_length_check(pass: &str) -> Result<(), LemmyError> { +pub fn password_length_check(pass: &str) -> LemmyResult<()> { if !(10..=60).contains(&pass.chars().count()) { Err(LemmyErrorType::InvalidPassword)? } else { @@ -384,7 +384,7 @@ pub fn password_length_check(pass: &str) -> Result<(), LemmyError> { } /// Checks for a honeypot. If this field is filled, fail the rest of the function -pub fn honeypot_check(honeypot: &Option) -> Result<(), LemmyError> { +pub fn honeypot_check(honeypot: &Option) -> LemmyResult<()> { if honeypot.is_some() && honeypot != &Some(String::new()) { Err(LemmyErrorType::HoneypotFailed)? } else { @@ -422,7 +422,7 @@ pub async fn send_password_reset_email( user: &LocalUserView, pool: &mut DbPool<'_>, settings: &Settings, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { // Generate a random token let token = uuid::Uuid::new_v4().to_string(); @@ -447,7 +447,7 @@ pub async fn send_verification_email( new_email: &str, pool: &mut DbPool<'_>, settings: &Settings, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let form = EmailVerificationForm { local_user_id: user.local_user.id, email: new_email.to_string(), @@ -524,7 +524,7 @@ pub async fn get_url_blocklist(context: &LemmyContext) -> LemmyResult static URL_BLOCKLIST: Lazy> = Lazy::new(|| { Cache::builder() .max_capacity(1) - .time_to_live(CACHE_DURATION_SHORT) + .time_to_live(CACHE_DURATION_FEDERATION) .build() }); @@ -564,7 +564,7 @@ pub async fn get_url_blocklist(context: &LemmyContext) -> LemmyResult pub async fn send_application_approved_email( user: &LocalUserView, settings: &Settings, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let email = &user.local_user.email.clone().expect("email"); let lang = get_interface_language(user); let subject = lang.registration_approved_subject(&user.person.actor_id); @@ -577,7 +577,7 @@ pub async fn send_new_applicant_email_to_admins( applicant_username: &str, pool: &mut DbPool<'_>, settings: &Settings, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { // Collect the admins with emails let admins = LocalUserView::list_admins_with_emails(pool).await?; @@ -602,7 +602,7 @@ pub async fn send_new_report_email_to_admins( reported_username: &str, pool: &mut DbPool<'_>, settings: &Settings, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { // Collect the admins with emails let admins = LocalUserView::list_admins_with_emails(pool).await?; @@ -618,9 +618,7 @@ pub async fn send_new_report_email_to_admins( Ok(()) } -pub fn check_private_instance_and_federation_enabled( - local_site: &LocalSite, -) -> Result<(), LemmyError> { +pub fn check_private_instance_and_federation_enabled(local_site: &LocalSite) -> LemmyResult<()> { if local_site.private_instance && local_site.federation_enabled { Err(LemmyErrorType::CantEnablePrivateInstanceAndFederationTogether)? } else { @@ -634,7 +632,7 @@ pub fn check_private_instance_and_federation_enabled( pub async fn read_site_for_actor( actor_id: DbUrl, context: &LemmyContext, -) -> Result, LemmyError> { +) -> LemmyResult> { let site_id = Site::instance_actor_id_from_url(actor_id.clone().into()); let site = Site::read_from_apub_id(&mut context.pool(), &site_id.into()).await?; Ok(site) @@ -643,7 +641,7 @@ pub async fn read_site_for_actor( pub async fn purge_image_posts_for_person( banned_person_id: PersonId, context: &LemmyContext, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let pool = &mut context.pool(); let posts = Post::fetch_pictrs_posts_for_creator(pool, banned_person_id).await?; for post in posts { @@ -661,11 +659,8 @@ pub async fn purge_image_posts_for_person( } /// Delete a local_user's images -async fn delete_local_user_images( - person_id: PersonId, - context: &LemmyContext, -) -> Result<(), LemmyError> { - if let Ok(local_user) = LocalUserView::read_person(&mut context.pool(), person_id).await { +async fn delete_local_user_images(person_id: PersonId, context: &LemmyContext) -> LemmyResult<()> { + if let Ok(Some(local_user)) = LocalUserView::read_person(&mut context.pool(), person_id).await { let pictrs_uploads = LocalImage::get_all_by_local_user_id(&mut context.pool(), local_user.local_user.id).await?; @@ -682,7 +677,7 @@ async fn delete_local_user_images( pub async fn purge_image_posts_for_community( banned_community_id: CommunityId, context: &LemmyContext, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let pool = &mut context.pool(); let posts = Post::fetch_pictrs_posts_for_community(pool, banned_community_id).await?; for post in posts { @@ -702,10 +697,12 @@ pub async fn purge_image_posts_for_community( pub async fn remove_user_data( banned_person_id: PersonId, context: &LemmyContext, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let pool = &mut context.pool(); // Purge user images - let person = Person::read(pool, banned_person_id).await?; + let person = Person::read(pool, banned_person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; if let Some(avatar) = person.avatar { purge_image_from_pictrs(&avatar, context).await.ok(); } @@ -785,7 +782,7 @@ pub async fn remove_user_data_in_community( community_id: CommunityId, banned_person_id: PersonId, pool: &mut DbPool<'_>, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { // Posts Post::update_removed_for_creator(pool, banned_person_id, Some(community_id), true).await?; @@ -815,13 +812,12 @@ pub async fn remove_user_data_in_community( Ok(()) } -pub async fn purge_user_account( - person_id: PersonId, - context: &LemmyContext, -) -> Result<(), LemmyError> { +pub async fn purge_user_account(person_id: PersonId, context: &LemmyContext) -> LemmyResult<()> { let pool = &mut context.pool(); - let person = Person::read(pool, person_id).await?; + let person = Person::read(pool, person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; // Delete their local images, if they're a local user delete_local_user_images(person_id, context).await.ok(); @@ -888,7 +884,7 @@ pub fn generate_inbox_url(actor_id: &DbUrl) -> Result { Ok(Url::parse(&format!("{actor_id}/inbox"))?.into()) } -pub fn generate_shared_inbox_url(settings: &Settings) -> Result { +pub fn generate_shared_inbox_url(settings: &Settings) -> LemmyResult { let url = format!("{}/inbox", settings.get_protocol_and_hostname()); Ok(Url::parse(&url)?.into()) } @@ -901,7 +897,7 @@ pub fn generate_featured_url(actor_id: &DbUrl) -> Result { Ok(Url::parse(&format!("{actor_id}/featured"))?.into()) } -pub fn generate_moderators_url(community_id: &DbUrl) -> Result { +pub fn generate_moderators_url(community_id: &DbUrl) -> LemmyResult { Ok(Url::parse(&format!("{community_id}/moderators"))?.into()) } diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index f8092268f..6493d6803 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -30,7 +30,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{mention::scrape_text_for_mentions, validation::is_valid_body_field}, }; @@ -41,7 +41,7 @@ pub async fn create_comment( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; let slur_regex = local_site_to_slur_regex(&local_site); @@ -70,7 +70,8 @@ pub async fn create_comment( Comment::read(&mut context.pool(), parent_id).await.ok() } else { None - }; + } + .flatten(); // If there's a parent_id, check to make sure that comment is in that post // Strange issue where sometimes the post ID of the parent comment is incorrect @@ -172,7 +173,7 @@ pub async fn create_comment( let parent_id = parent.id; let comment_reply = CommentReply::read_by_comment_and_person(&mut context.pool(), parent_id, person_id).await; - if let Ok(reply) = comment_reply { + if let Ok(Some(reply)) = comment_reply { CommentReply::update( &mut context.pool(), reply.id, @@ -185,7 +186,7 @@ pub async fn create_comment( // If the parent has PersonMentions mark them as read too let person_mention = PersonMention::read_by_comment_and_person(&mut context.pool(), parent_id, person_id).await; - if let Ok(mention) = person_mention { + if let Ok(Some(mention)) = person_mention { PersonMention::update( &mut context.pool(), mention.id, @@ -207,7 +208,7 @@ pub async fn create_comment( )) } -pub fn check_comment_depth(comment: &Comment) -> Result<(), LemmyError> { +pub fn check_comment_depth(comment: &Comment) -> LemmyResult<()> { let path = &comment.path.0; let length = path.split('.').count(); if length > MAX_COMMENT_DEPTH_LIMIT { diff --git a/crates/api_crud/src/comment/delete.rs b/crates/api_crud/src/comment/delete.rs index 9db1ed034..2b1a20f89 100644 --- a/crates/api_crud/src/comment/delete.rs +++ b/crates/api_crud/src/comment/delete.rs @@ -12,16 +12,18 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::{CommentView, LocalUserView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn delete_comment( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let comment_id = data.comment_id; - let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; + let orig_comment = CommentView::read(&mut context.pool(), comment_id, None) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; // Dont delete it if its already been deleted. if orig_comment.comment.deleted == data.deleted { diff --git a/crates/api_crud/src/comment/read.rs b/crates/api_crud/src/comment/read.rs index 733d08682..39852081f 100644 --- a/crates/api_crud/src/comment/read.rs +++ b/crates/api_crud/src/comment/read.rs @@ -7,14 +7,14 @@ use lemmy_api_common::{ }; use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn get_comment( data: Query, context: Data, local_user_view: Option, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; check_private_instance(&local_user_view, &local_site)?; diff --git a/crates/api_crud/src/comment/remove.rs b/crates/api_crud/src/comment/remove.rs index d735c4462..02ae7b9fd 100644 --- a/crates/api_crud/src/comment/remove.rs +++ b/crates/api_crud/src/comment/remove.rs @@ -16,16 +16,18 @@ use lemmy_db_schema::{ traits::{Crud, Reportable}, }; use lemmy_db_views::structs::{CommentView, LocalUserView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn remove_comment( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let comment_id = data.comment_id; - let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; + let orig_comment = CommentView::read(&mut context.pool(), comment_id, None) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; check_community_mod_action( &local_user_view.person, diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs index b35333ec5..695ededfe 100644 --- a/crates/api_crud/src/comment/update.rs +++ b/crates/api_crud/src/comment/update.rs @@ -23,7 +23,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::{CommentView, LocalUserView}; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{mention::scrape_text_for_mentions, validation::is_valid_body_field}, }; @@ -32,11 +32,13 @@ pub async fn update_comment( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; let comment_id = data.comment_id; - let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; + let orig_comment = CommentView::read(&mut context.pool(), comment_id, None) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; check_community_user_action( &local_user_view.person, diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs index 679655078..b0b6bea0e 100644 --- a/crates/api_crud/src/community/create.rs +++ b/crates/api_crud/src/community/create.rs @@ -33,7 +33,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{ slurs::check_slurs, validation::{is_valid_actor_name, is_valid_body_field}, @@ -45,8 +45,10 @@ pub async fn create_community( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { - let site_view = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult> { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let local_site = site_view.local_site; if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() { diff --git a/crates/api_crud/src/community/delete.rs b/crates/api_crud/src/community/delete.rs index 60b79fd79..a2ceaff50 100644 --- a/crates/api_crud/src/community/delete.rs +++ b/crates/api_crud/src/community/delete.rs @@ -13,14 +13,14 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::CommunityModeratorView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn delete_community( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Fetch the community mods let community_id = data.community_id; let community_mods = diff --git a/crates/api_crud/src/community/list.rs b/crates/api_crud/src/community/list.rs index 7990352fc..587b5cdfa 100644 --- a/crates/api_crud/src/community/list.rs +++ b/crates/api_crud/src/community/list.rs @@ -6,15 +6,17 @@ use lemmy_api_common::{ }; use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_db_views_actor::community_view::CommunityQuery; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; #[tracing::instrument(skip(context))] pub async fn list_communities( data: Query, context: Data, local_user_view: Option, -) -> Result, LemmyError> { - let local_site = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult> { + let local_site = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let is_admin = local_user_view .as_ref() .map(|luv| is_admin(luv).is_ok()) diff --git a/crates/api_crud/src/community/remove.rs b/crates/api_crud/src/community/remove.rs index d7be60927..f4271565d 100644 --- a/crates/api_crud/src/community/remove.rs +++ b/crates/api_crud/src/community/remove.rs @@ -15,14 +15,14 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn remove_community( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { check_community_mod_action( &local_user_view.person, data.community_id, diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs index 51c57e1c8..33c6a47dd 100644 --- a/crates/api_crud/src/community/update.rs +++ b/crates/api_crud/src/community/update.rs @@ -25,7 +25,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{slurs::check_slurs_opt, validation::is_valid_body_field}, }; @@ -34,7 +34,7 @@ pub async fn update_community( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; let slur_regex = local_site_to_slur_regex(&local_site); @@ -43,7 +43,9 @@ pub async fn update_community( let description = process_markdown_opt(&data.description, &slur_regex, &url_blocklist, &context).await?; is_valid_body_field(&data.description, false)?; - let old_community = Community::read(&mut context.pool(), data.community_id).await?; + let old_community = Community::read(&mut context.pool(), data.community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; replace_image(&data.icon, &old_community.icon, &context).await?; replace_image(&data.banner, &old_community.banner, &context).await?; diff --git a/crates/api_crud/src/custom_emoji/create.rs b/crates/api_crud/src/custom_emoji/create.rs index 5846c1243..654b75631 100644 --- a/crates/api_crud/src/custom_emoji/create.rs +++ b/crates/api_crud/src/custom_emoji/create.rs @@ -13,14 +13,14 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::{CustomEmojiView, LocalUserView}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn create_custom_emoji( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Make sure user is an admin is_admin(&local_user_view)?; diff --git a/crates/api_crud/src/custom_emoji/delete.rs b/crates/api_crud/src/custom_emoji/delete.rs index 9165a2a19..818fd4d88 100644 --- a/crates/api_crud/src/custom_emoji/delete.rs +++ b/crates/api_crud/src/custom_emoji/delete.rs @@ -8,14 +8,14 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{source::custom_emoji::CustomEmoji, traits::Crud}; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn delete_custom_emoji( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Make sure user is an admin is_admin(&local_user_view)?; diff --git a/crates/api_crud/src/custom_emoji/update.rs b/crates/api_crud/src/custom_emoji/update.rs index 4119ed944..62d1d819d 100644 --- a/crates/api_crud/src/custom_emoji/update.rs +++ b/crates/api_crud/src/custom_emoji/update.rs @@ -13,14 +13,14 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::{CustomEmojiView, LocalUserView}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn update_custom_emoji( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Make sure user is an admin is_admin(&local_user_view)?; diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index 6a61c032b..266f964fa 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -32,7 +32,7 @@ use lemmy_db_schema::{ use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, spawn_try_task, utils::{ slurs::check_slurs, @@ -55,7 +55,7 @@ pub async fn create_post( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; honeypot_check(&data.honeypot)?; @@ -85,7 +85,9 @@ pub async fn create_post( .await?; let community_id = data.community_id; - let community = Community::read(&mut context.pool(), community_id).await?; + let community = Community::read(&mut context.pool(), community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; if community.posting_restricted_to_mods { let community_id = data.community_id; let is_mod = CommunityModeratorView::is_community_moderator( diff --git a/crates/api_crud/src/post/delete.rs b/crates/api_crud/src/post/delete.rs index a8fce28fc..696566c8e 100644 --- a/crates/api_crud/src/post/delete.rs +++ b/crates/api_crud/src/post/delete.rs @@ -12,16 +12,18 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn delete_post( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let post_id = data.post_id; - let orig_post = Post::read(&mut context.pool(), post_id).await?; + let orig_post = Post::read(&mut context.pool(), post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; // Dont delete it if its already been deleted. if orig_post.deleted == data.deleted { diff --git a/crates/api_crud/src/post/read.rs b/crates/api_crud/src/post/read.rs index e701008b7..2a567187f 100644 --- a/crates/api_crud/src/post/read.rs +++ b/crates/api_crud/src/post/read.rs @@ -14,15 +14,17 @@ use lemmy_db_views::{ structs::{LocalUserView, PostView, SiteView}, }; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn get_post( data: Query, context: Data, local_user_view: Option, -) -> Result, LemmyError> { - let local_site = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult> { + let local_site = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; check_private_instance(&local_user_view, &local_site.local_site)?; @@ -33,15 +35,19 @@ pub async fn get_post( id } else if let Some(comment_id) = data.comment_id { Comment::read(&mut context.pool(), comment_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntFindPost)? + .await? + .ok_or(LemmyErrorType::CouldntFindComment)? .post_id } else { Err(LemmyErrorType::CouldntFindPost)? }; // Check to see if the person is a mod or admin, to show deleted / removed - let community_id = Post::read(&mut context.pool(), post_id).await?.community_id; + let community_id = Post::read(&mut context.pool(), post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)? + .community_id; + let is_mod_or_admin = is_mod_or_admin_opt( &mut context.pool(), local_user_view.as_ref(), @@ -51,8 +57,8 @@ pub async fn get_post( .is_ok(); let post_view = PostView::read(&mut context.pool(), post_id, person_id, is_mod_or_admin) - .await - .with_lemmy_type(LemmyErrorType::CouldntFindPost)?; + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; // Mark the post as read let post_id = post_view.post.id; @@ -67,8 +73,8 @@ pub async fn get_post( person_id, is_mod_or_admin, ) - .await - .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?; + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; // Insert into PersonPostAggregates // to update the read_comments count diff --git a/crates/api_crud/src/post/remove.rs b/crates/api_crud/src/post/remove.rs index cbcf069b6..682ed75d3 100644 --- a/crates/api_crud/src/post/remove.rs +++ b/crates/api_crud/src/post/remove.rs @@ -16,16 +16,18 @@ use lemmy_db_schema::{ traits::{Crud, Reportable}, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; #[tracing::instrument(skip(context))] pub async fn remove_post( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let post_id = data.post_id; - let orig_post = Post::read(&mut context.pool(), post_id).await?; + let orig_post = Post::read(&mut context.pool(), post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; check_community_mod_action( &local_user_view.person, diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs index c08f35307..8be97f3c1 100644 --- a/crates/api_crud/src/post/update.rs +++ b/crates/api_crud/src/post/update.rs @@ -25,7 +25,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{ slurs::check_slurs_opt, validation::{ @@ -45,7 +45,7 @@ pub async fn update_post( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; // TODO No good way to handle a clear. @@ -70,7 +70,9 @@ pub async fn update_post( check_url_scheme(&custom_thumbnail)?; let post_id = data.post_id; - let orig_post = Post::read(&mut context.pool(), post_id).await?; + let orig_post = Post::read(&mut context.pool(), post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; check_community_user_action( &local_user_view.person, diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs index 32d8b99e6..e977a6c86 100644 --- a/crates/api_crud/src/private_message/create.rs +++ b/crates/api_crud/src/private_message/create.rs @@ -24,7 +24,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{markdown::markdown_to_html, validation::is_valid_body_field}, }; @@ -33,7 +33,7 @@ pub async fn create_private_message( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; let slur_regex = local_site_to_slur_regex(&local_site); @@ -76,12 +76,16 @@ pub async fn create_private_message( .await .with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?; - let view = PrivateMessageView::read(&mut context.pool(), inserted_private_message.id).await?; + let view = PrivateMessageView::read(&mut context.pool(), inserted_private_message.id) + .await? + .ok_or(LemmyErrorType::CouldntFindPrivateMessage)?; // Send email to the local recipient, if one exists if view.recipient.local { let recipient_id = data.recipient_id; - let local_recipient = LocalUserView::read_person(&mut context.pool(), recipient_id).await?; + let local_recipient = LocalUserView::read_person(&mut context.pool(), recipient_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; let lang = get_interface_language(&local_recipient); let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname()); let sender_name = &local_user_view.person.name; diff --git a/crates/api_crud/src/private_message/delete.rs b/crates/api_crud/src/private_message/delete.rs index ef0864d70..dc028ff41 100644 --- a/crates/api_crud/src/private_message/delete.rs +++ b/crates/api_crud/src/private_message/delete.rs @@ -10,17 +10,19 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn delete_private_message( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { // Checking permissions let private_message_id = data.private_message_id; - let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?; + let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPrivateMessage)?; if local_user_view.person.id != orig_private_message.creator_id { Err(LemmyErrorType::EditPrivateMessageNotAllowed)? } @@ -45,7 +47,9 @@ pub async fn delete_private_message( ) .await?; - let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?; + let view = PrivateMessageView::read(&mut context.pool(), private_message_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPrivateMessage)?; Ok(Json(PrivateMessageResponse { private_message_view: view, })) diff --git a/crates/api_crud/src/private_message/read.rs b/crates/api_crud/src/private_message/read.rs index 9f289c911..7558b97fc 100644 --- a/crates/api_crud/src/private_message/read.rs +++ b/crates/api_crud/src/private_message/read.rs @@ -4,14 +4,14 @@ use lemmy_api_common::{ private_message::{GetPrivateMessages, PrivateMessagesResponse}, }; use lemmy_db_views::{private_message_view::PrivateMessageQuery, structs::LocalUserView}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn get_private_message( data: Query, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let person_id = local_user_view.person.id; let page = data.page; diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs index 29063fd10..2842fea65 100644 --- a/crates/api_crud/src/private_message/update.rs +++ b/crates/api_crud/src/private_message/update.rs @@ -16,7 +16,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::validation::is_valid_body_field, }; @@ -25,12 +25,14 @@ pub async fn update_private_message( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; // Checking permissions let private_message_id = data.private_message_id; - let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?; + let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPrivateMessage)?; if local_user_view.person.id != orig_private_message.creator_id { Err(LemmyErrorType::EditPrivateMessageNotAllowed)? } @@ -54,7 +56,9 @@ pub async fn update_private_message( .await .with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?; - let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?; + let view = PrivateMessageView::read(&mut context.pool(), private_message_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPrivateMessage)?; ActivityChannel::submit_activity( SendActivityData::UpdatePrivateMessage(view.clone()), diff --git a/crates/api_crud/src/site/create.rs b/crates/api_crud/src/site/create.rs index b917cc999..5e89c7197 100644 --- a/crates/api_crud/src/site/create.rs +++ b/crates/api_crud/src/site/create.rs @@ -26,7 +26,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_utils::{ - error::{LemmyError, LemmyErrorType, LemmyResult}, + error::{LemmyErrorType, LemmyResult}, utils::{ slurs::{check_slurs, check_slurs_opt}, validation::{ @@ -45,7 +45,7 @@ pub async fn create_site( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; // Make sure user is an admin; other types of users should not create site data... @@ -128,7 +128,9 @@ pub async fn create_site( LocalSiteRateLimit::update(&mut context.pool(), &local_site_rate_limit_form).await?; - let site_view = SiteView::read_local(&mut context.pool()).await?; + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let rate_limit_config = local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit); diff --git a/crates/api_crud/src/site/read.rs b/crates/api_crud/src/site/read.rs index bbead78a0..a29eed0a4 100644 --- a/crates/api_crud/src/site/read.rs +++ b/crates/api_crud/src/site/read.rs @@ -19,8 +19,8 @@ use lemmy_db_views_actor::structs::{ PersonView, }; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, - CACHE_DURATION_SHORT, + error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}, + CACHE_DURATION_API, VERSION, }; use moka::future::Cache; @@ -30,18 +30,20 @@ use once_cell::sync::Lazy; pub async fn get_site( local_user_view: Option, context: Data, -) -> Result, LemmyError> { +) -> LemmyResult> { static CACHE: Lazy> = Lazy::new(|| { Cache::builder() .max_capacity(1) - .time_to_live(CACHE_DURATION_SHORT) + .time_to_live(CACHE_DURATION_API) .build() }); // This data is independent from the user account so we can cache it across requests let mut site_response = CACHE .try_get_with::<_, LemmyError>((), async { - let site_view = SiteView::read_local(&mut context.pool()).await?; + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let admins = PersonView::admins(&mut context.pool()).await?; let all_languages = Language::read_all(&mut context.pool()).await?; let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?; diff --git a/crates/api_crud/src/site/update.rs b/crates/api_crud/src/site/update.rs index 25914bbf5..c247d464d 100644 --- a/crates/api_crud/src/site/update.rs +++ b/crates/api_crud/src/site/update.rs @@ -31,7 +31,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{ slurs::check_slurs_opt, validation::{ @@ -50,8 +50,10 @@ pub async fn update_site( data: Json, context: Data, local_user_view: LocalUserView, -) -> Result, LemmyError> { - let site_view = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult> { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let local_site = site_view.local_site; let site = site_view.site; @@ -177,7 +179,9 @@ pub async fn update_site( .with_lemmy_type(LemmyErrorType::CouldntSetAllEmailVerified)?; } - let site_view = SiteView::read_local(&mut context.pool()).await?; + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let rate_limit_config = local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit); diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index d24a287db..6640e9e53 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -31,7 +31,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{ slurs::{check_slurs, check_slurs_opt}, validation::is_valid_actor_name, @@ -44,8 +44,10 @@ pub async fn register( data: Json, req: HttpRequest, context: Data, -) -> Result, LemmyError> { - let site_view = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult> { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let local_site = site_view.local_site; let require_registration_application = local_site.registration_mode == RegistrationMode::RequireApplication; diff --git a/crates/apub/src/activities/block/block_user.rs b/crates/apub/src/activities/block/block_user.rs index a2a1f25bf..f68301be1 100644 --- a/crates/apub/src/activities/block/block_user.rs +++ b/crates/apub/src/activities/block/block_user.rs @@ -39,7 +39,7 @@ use lemmy_db_schema::{ }, traits::{Bannable, Crud, Followable}, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; impl BlockUser { @@ -51,7 +51,7 @@ impl BlockUser { reason: Option, expires: Option>, context: &Data, - ) -> Result { + ) -> LemmyResult { let audience = if let SiteOrCommunity::Community(c) = target { Some(c.id().into()) } else { @@ -85,7 +85,7 @@ impl BlockUser { reason: Option, expires: Option>, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let block = BlockUser::new( target, user, @@ -125,7 +125,7 @@ impl ActivityHandler for BlockUser { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_is_public(&self.to, &self.cc)?; match self.target.dereference(context).await? { SiteOrCommunity::Site(site) => { @@ -148,7 +148,7 @@ impl ActivityHandler for BlockUser { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let expires = self.expires.or(self.end_time).map(Into::into); let mod_person = self.actor.dereference(context).await?; diff --git a/crates/apub/src/activities/block/mod.rs b/crates/apub/src/activities/block/mod.rs index 92fea4ec5..ced50e2de 100644 --- a/crates/apub/src/activities/block/mod.rs +++ b/crates/apub/src/activities/block/mod.rs @@ -23,7 +23,10 @@ use lemmy_db_schema::{ utils::DbPool, }; use lemmy_db_views::structs::SiteView; -use lemmy_utils::error::{LemmyError, LemmyResult}; +use lemmy_utils::{ + error::{LemmyError, LemmyResult}, + LemmyErrorType, +}; use serde::Deserialize; use url::Url; @@ -58,10 +61,7 @@ impl Object for SiteOrCommunity { } #[tracing::instrument(skip_all)] - async fn read_from_id( - object_id: Url, - data: &Data, - ) -> Result, LemmyError> + async fn read_from_id(object_id: Url, data: &Data) -> LemmyResult> where Self: Sized, { @@ -74,11 +74,11 @@ impl Object for SiteOrCommunity { }) } - async fn delete(self, _data: &Data) -> Result<(), LemmyError> { + async fn delete(self, _data: &Data) -> LemmyResult<()> { unimplemented!() } - async fn into_json(self, _data: &Data) -> Result { + async fn into_json(self, _data: &Data) -> LemmyResult { unimplemented!() } @@ -87,7 +87,7 @@ impl Object for SiteOrCommunity { apub: &Self::Kind, expected_domain: &Url, data: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { match apub { InstanceOrGroup::Instance(i) => ApubSite::verify(i, expected_domain, data).await, InstanceOrGroup::Group(g) => ApubCommunity::verify(g, expected_domain, data).await, @@ -95,7 +95,7 @@ impl Object for SiteOrCommunity { } #[tracing::instrument(skip_all)] - async fn from_json(apub: Self::Kind, data: &Data) -> Result + async fn from_json(apub: Self::Kind, data: &Data) -> LemmyResult where Self: Sized, { @@ -117,10 +117,7 @@ impl SiteOrCommunity { } } -async fn generate_cc( - target: &SiteOrCommunity, - pool: &mut DbPool<'_>, -) -> Result, LemmyError> { +async fn generate_cc(target: &SiteOrCommunity, pool: &mut DbPool<'_>) -> LemmyResult> { Ok(match target { SiteOrCommunity::Site(_) => Site::read_remote_sites(pool) .await? @@ -139,8 +136,14 @@ pub(crate) async fn send_ban_from_site( ban: bool, expires: Option, context: Data, -) -> Result<(), LemmyError> { - let site = SiteOrCommunity::Site(SiteView::read_local(&mut context.pool()).await?.site.into()); +) -> LemmyResult<()> { + let site = SiteOrCommunity::Site( + SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)? + .site + .into(), + ); let expires = check_expire_time(expires)?; // if the action affects a local user, federate to other instances @@ -180,6 +183,7 @@ pub(crate) async fn send_ban_from_community( ) -> LemmyResult<()> { let community: ApubCommunity = Community::read(&mut context.pool(), community_id) .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)? .into(); let expires = check_expire_time(data.expires)?; diff --git a/crates/apub/src/activities/block/undo_block_user.rs b/crates/apub/src/activities/block/undo_block_user.rs index 756d0a149..b92320b2d 100644 --- a/crates/apub/src/activities/block/undo_block_user.rs +++ b/crates/apub/src/activities/block/undo_block_user.rs @@ -27,7 +27,7 @@ use lemmy_db_schema::{ }, traits::{Bannable, Crud}, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; impl UndoBlockUser { @@ -38,7 +38,7 @@ impl UndoBlockUser { mod_: &ApubPerson, reason: Option, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let block = BlockUser::new(target, user, mod_, None, reason, None, context).await?; let audience = if let SiteOrCommunity::Community(c) = target { Some(c.id().into()) @@ -88,7 +88,7 @@ impl ActivityHandler for UndoBlockUser { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_is_public(&self.to, &self.cc)?; verify_domains_match(self.actor.inner(), self.object.actor.inner())?; self.object.verify(context).await?; @@ -96,7 +96,7 @@ impl ActivityHandler for UndoBlockUser { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let expires = self.object.expires.or(self.object.end_time).map(Into::into); let mod_person = self.actor.dereference(context).await?; diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index 474afc3b9..9a3928882 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -82,7 +82,7 @@ impl AnnounceActivity { object: RawAnnouncableActivities, community: &ApubCommunity, context: &Data, - ) -> Result { + ) -> LemmyResult { let inner_kind = object .other .get("type") @@ -105,7 +105,7 @@ impl AnnounceActivity { object: RawAnnouncableActivities, community: &ApubCommunity, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let announce = AnnounceActivity::new(object.clone(), community, context)?; let inboxes = ActivitySendTargets::to_local_community_followers(community.id); send_lemmy_activity(context, announce, community, inboxes.clone(), false).await?; @@ -148,13 +148,13 @@ impl ActivityHandler for AnnounceActivity { } #[tracing::instrument(skip_all)] - async fn verify(&self, _context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, _context: &Data) -> LemmyResult<()> { verify_is_public(&self.to, &self.cc)?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let object: AnnouncableActivities = self.object.object(context).await?.try_into()?; diff --git a/crates/apub/src/activities/community/collection_add.rs b/crates/apub/src/activities/community/collection_add.rs index fdd62bdb1..3d5de4128 100644 --- a/crates/apub/src/activities/community/collection_add.rs +++ b/crates/apub/src/activities/community/collection_add.rs @@ -36,7 +36,10 @@ use lemmy_db_schema::{ }, traits::{Crud, Joinable}, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{ + error::{LemmyError, LemmyResult}, + LemmyErrorType, +}; use url::Url; impl CollectionAdd { @@ -46,7 +49,7 @@ impl CollectionAdd { added_mod: &ApubPerson, actor: &ApubPerson, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let id = generate_activity_id( AddType::Add, &context.settings().get_protocol_and_hostname(), @@ -72,7 +75,7 @@ impl CollectionAdd { featured_post: &ApubPost, actor: &ApubPerson, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let id = generate_activity_id( AddType::Add, &context.settings().get_protocol_and_hostname(), @@ -114,7 +117,7 @@ impl ActivityHandler for CollectionAdd { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_is_public(&self.to, &self.cc)?; let community = self.community(context).await?; verify_person_in_community(&self.actor, &community, context).await?; @@ -123,10 +126,12 @@ impl ActivityHandler for CollectionAdd { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let (community, collection_type) = - Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?; + Community::get_by_collection_url(&mut context.pool(), &self.target.into()) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; match collection_type { CollectionType::Moderators => { let new_mod = ObjectId::::from(self.object) @@ -179,13 +184,15 @@ pub(crate) async fn send_add_mod_to_community( updated_mod_id: PersonId, added: bool, context: Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let actor: ApubPerson = actor.into(); let community: ApubCommunity = Community::read(&mut context.pool(), community_id) .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)? .into(); let updated_mod: ApubPerson = Person::read(&mut context.pool(), updated_mod_id) .await? + .ok_or(LemmyErrorType::CouldntFindPerson)? .into(); if added { CollectionAdd::send_add_mod(&community, &updated_mod, &actor, &context).await @@ -199,11 +206,12 @@ pub(crate) async fn send_feature_post( actor: Person, featured: bool, context: Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let actor: ApubPerson = actor.into(); let post: ApubPost = post.into(); let community = Community::read(&mut context.pool(), post.community_id) .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)? .into(); if featured { CollectionAdd::send_add_featured_post(&community, &post, &actor, &context).await diff --git a/crates/apub/src/activities/community/collection_remove.rs b/crates/apub/src/activities/community/collection_remove.rs index 06238a890..634ca526c 100644 --- a/crates/apub/src/activities/community/collection_remove.rs +++ b/crates/apub/src/activities/community/collection_remove.rs @@ -31,7 +31,10 @@ use lemmy_db_schema::{ }, traits::{Crud, Joinable}, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{ + error::{LemmyError, LemmyResult}, + LemmyErrorType, +}; use url::Url; impl CollectionRemove { @@ -41,7 +44,7 @@ impl CollectionRemove { removed_mod: &ApubPerson, actor: &ApubPerson, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let id = generate_activity_id( RemoveType::Remove, &context.settings().get_protocol_and_hostname(), @@ -67,7 +70,7 @@ impl CollectionRemove { featured_post: &ApubPost, actor: &ApubPerson, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let id = generate_activity_id( RemoveType::Remove, &context.settings().get_protocol_and_hostname(), @@ -109,7 +112,7 @@ impl ActivityHandler for CollectionRemove { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_is_public(&self.to, &self.cc)?; let community = self.community(context).await?; verify_person_in_community(&self.actor, &community, context).await?; @@ -118,10 +121,12 @@ impl ActivityHandler for CollectionRemove { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let (community, collection_type) = - Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?; + Community::get_by_collection_url(&mut context.pool(), &self.target.into()) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; match collection_type { CollectionType::Moderators => { let remove_mod = ObjectId::::from(self.object) diff --git a/crates/apub/src/activities/community/lock_page.rs b/crates/apub/src/activities/community/lock_page.rs index db9dfa36c..bafb42a4a 100644 --- a/crates/apub/src/activities/community/lock_page.rs +++ b/crates/apub/src/activities/community/lock_page.rs @@ -31,7 +31,10 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{ + error::{LemmyError, LemmyResult}, + LemmyErrorType, +}; use url::Url; #[async_trait::async_trait] @@ -106,9 +109,10 @@ pub(crate) async fn send_lock_post( actor: Person, locked: bool, context: Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let community: ApubCommunity = Community::read(&mut context.pool(), post.community_id) .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)? .into(); let id = generate_activity_id( LockType::Lock, diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index 612fd8a7a..a47dec2bd 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -10,7 +10,7 @@ use lemmy_db_schema::{ source::{activity::ActivitySendTargets, person::PersonFollower}, CommunityVisibility, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; pub mod announce; pub mod collection_add; @@ -39,7 +39,7 @@ pub(crate) async fn send_activity_in_community( extra_inboxes: ActivitySendTargets, is_mod_action: bool, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { // If community is local only, don't send anything out if community.visibility != CommunityVisibility::Public { return Ok(()); diff --git a/crates/apub/src/activities/community/report.rs b/crates/apub/src/activities/community/report.rs index 6b1fce066..d1bec0b75 100644 --- a/crates/apub/src/activities/community/report.rs +++ b/crates/apub/src/activities/community/report.rs @@ -29,7 +29,10 @@ use lemmy_db_schema::{ }, traits::{Crud, Reportable}, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{ + error::{LemmyError, LemmyResult}, + LemmyErrorType, +}; use url::Url; impl Report { @@ -40,7 +43,7 @@ impl Report { community: Community, reason: String, context: Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let actor: ApubPerson = actor.into(); let community: ApubCommunity = community.into(); let kind = FlagType::Flag; @@ -67,7 +70,9 @@ impl Report { PostOrComment::Post(p) => p.creator_id, PostOrComment::Comment(c) => c.creator_id, }; - let object_creator = Person::read(&mut context.pool(), object_creator_id).await?; + let object_creator = Person::read(&mut context.pool(), object_creator_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; let object_creator_site: Option = Site::read_from_instance_id(&mut context.pool(), object_creator.instance_id) .await? @@ -94,14 +99,14 @@ impl ActivityHandler for Report { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { let community = self.community(context).await?; verify_person_in_community(&self.actor, &community, context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let actor = self.actor.dereference(context).await?; let reason = self.reason()?; diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index ca293d002..3457e6c24 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -26,14 +26,14 @@ use lemmy_db_schema::{ traits::Crud, utils::naive_now, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; pub(crate) async fn send_update_community( community: Community, actor: Person, context: Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let community: ApubCommunity = community.into(); let actor: ApubPerson = actor.into(); let id = generate_activity_id( @@ -76,7 +76,7 @@ impl ActivityHandler for UpdateCommunity { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_is_public(&self.to, &self.cc)?; let community = self.community(context).await?; verify_person_in_community(&self.actor, &community, context).await?; @@ -86,7 +86,7 @@ impl ActivityHandler for UpdateCommunity { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let community = self.community(context).await?; diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs index ea6af2fcf..7f1532087 100644 --- a/crates/apub/src/activities/create_or_update/comment.rs +++ b/crates/apub/src/activities/create_or_update/comment.rs @@ -39,7 +39,11 @@ use lemmy_db_schema::{ }, traits::{Crud, Likeable}, }; -use lemmy_utils::{error::LemmyError, utils::mention::scrape_text_for_mentions}; +use lemmy_utils::{ + error::{LemmyError, LemmyResult}, + utils::mention::scrape_text_for_mentions, + LemmyErrorType, +}; use url::Url; impl CreateOrUpdateNote { @@ -49,14 +53,20 @@ impl CreateOrUpdateNote { person_id: PersonId, kind: CreateOrUpdateType, context: Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { // TODO: might be helpful to add a comment method to retrieve community directly let post_id = comment.post_id; - let post = Post::read(&mut context.pool(), post_id).await?; + let post = Post::read(&mut context.pool(), post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; let community_id = post.community_id; - let person: ApubPerson = Person::read(&mut context.pool(), person_id).await?.into(); + let person: ApubPerson = Person::read(&mut context.pool(), person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)? + .into(); let community: ApubCommunity = Community::read(&mut context.pool(), community_id) .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)? .into(); let id = generate_activity_id( @@ -114,7 +124,7 @@ impl ActivityHandler for CreateOrUpdateNote { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_is_public(&self.to, &self.cc)?; let post = self.object.get_parents(context).await?.0; let community = self.community(context).await?; @@ -129,7 +139,7 @@ impl ActivityHandler for CreateOrUpdateNote { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; // Need to do this check here instead of Note::from_json because we need the person who // send the activity, not the comment author. diff --git a/crates/apub/src/activities/create_or_update/post.rs b/crates/apub/src/activities/create_or_update/post.rs index 8b2fdbdb4..2ca7e52cc 100644 --- a/crates/apub/src/activities/create_or_update/post.rs +++ b/crates/apub/src/activities/create_or_update/post.rs @@ -33,7 +33,7 @@ use lemmy_db_schema::{ }, traits::{Crud, Likeable}, }; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use url::Url; impl CreateOrUpdatePage { @@ -43,7 +43,7 @@ impl CreateOrUpdatePage { community: &ApubCommunity, kind: CreateOrUpdateType, context: &Data, - ) -> Result { + ) -> LemmyResult { let id = generate_activity_id( kind.clone(), &context.settings().get_protocol_and_hostname(), @@ -65,12 +65,16 @@ impl CreateOrUpdatePage { person_id: PersonId, kind: CreateOrUpdateType, context: Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let post = ApubPost(post); let community_id = post.community_id; - let person: ApubPerson = Person::read(&mut context.pool(), person_id).await?.into(); + let person: ApubPerson = Person::read(&mut context.pool(), person_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)? + .into(); let community: ApubCommunity = Community::read(&mut context.pool(), community_id) .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)? .into(); let create_or_update = @@ -104,7 +108,7 @@ impl ActivityHandler for CreateOrUpdatePage { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_is_public(&self.to, &self.cc)?; let community = self.community(context).await?; verify_person_in_community(&self.actor, &community, context).await?; @@ -138,7 +142,7 @@ impl ActivityHandler for CreateOrUpdatePage { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let post = ApubPost::from_json(self.object, context).await?; diff --git a/crates/apub/src/activities/create_or_update/private_message.rs b/crates/apub/src/activities/create_or_update/private_message.rs index f3ea3ca26..950f4ae99 100644 --- a/crates/apub/src/activities/create_or_update/private_message.rs +++ b/crates/apub/src/activities/create_or_update/private_message.rs @@ -15,14 +15,14 @@ use activitypub_federation::{ use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::activity::ActivitySendTargets; use lemmy_db_views::structs::PrivateMessageView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; pub(crate) async fn send_create_or_update_pm( pm_view: PrivateMessageView, kind: CreateOrUpdateType, context: Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let actor: ApubPerson = pm_view.creator.into(); let recipient: ApubPerson = pm_view.recipient.into(); @@ -57,7 +57,7 @@ impl ActivityHandler for CreateOrUpdateChatMessage { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_person(&self.actor, context).await?; verify_domains_match(self.actor.inner(), self.object.id.inner())?; verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?; @@ -66,7 +66,7 @@ impl ActivityHandler for CreateOrUpdateChatMessage { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; ApubPrivateMessage::from_json(self.object, context).await?; Ok(()) diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 18f8cf6fb..cecc051b4 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -27,7 +27,7 @@ use lemmy_db_schema::{ }, traits::{Crud, Reportable}, }; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use url::Url; #[async_trait::async_trait] @@ -44,13 +44,13 @@ impl ActivityHandler for Delete { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_delete_activity(self, self.summary.is_some(), context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; if let Some(reason) = self.summary { // We set reason to empty string if it doesn't exist, to distinguish between delete and @@ -88,7 +88,7 @@ impl Delete { community: Option<&Community>, summary: Option, context: &Data, - ) -> Result { + ) -> LemmyResult { let id = generate_activity_id( DeleteType::Delete, &context.settings().get_protocol_and_hostname(), @@ -114,7 +114,7 @@ pub(in crate::activities) async fn receive_remove_action( object: &Url, reason: Option, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { match DeletableObjects::read_from_db(object, context).await? { DeletableObjects::Community(community) => { if community.local { diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index 03f01a3b9..c9d268e74 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -39,7 +39,7 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use std::ops::Deref; use url::Url; @@ -56,7 +56,7 @@ pub(crate) async fn send_apub_delete_in_community( reason: Option, deleted: bool, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let actor = ApubPerson::from(actor); let is_mod_action = reason.is_some(); let activity = if deleted { @@ -83,10 +83,11 @@ pub(crate) async fn send_apub_delete_private_message( pm: PrivateMessage, deleted: bool, context: Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let recipient_id = pm.recipient_id; let recipient: ApubPerson = Person::read(&mut context.pool(), recipient_id) .await? + .ok_or(LemmyErrorType::CouldntFindPerson)? .into(); let deletable = DeletableObjects::PrivateMessage(pm.into()); @@ -105,7 +106,7 @@ pub async fn send_apub_delete_user( person: Person, remove_data: bool, context: Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let person: ApubPerson = person.into(); let deletable = DeletableObjects::Person(person.clone()); @@ -131,7 +132,7 @@ impl DeletableObjects { pub(crate) async fn read_from_db( ap_id: &Url, context: &Data, - ) -> Result { + ) -> LemmyResult { if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? { return Ok(DeletableObjects::Community(c)); } @@ -166,7 +167,7 @@ pub(in crate::activities) async fn verify_delete_activity( activity: &Delete, is_mod_action: bool, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let object = DeletableObjects::read_from_db(activity.object.id(), context).await?; match object { DeletableObjects::Community(community) => { @@ -221,7 +222,7 @@ async fn verify_delete_post_or_comment( community: &ApubCommunity, is_mod_action: bool, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { verify_person_in_community(actor, community, context).await?; if is_mod_action { verify_mod_action(actor, community, context).await?; @@ -240,7 +241,7 @@ async fn receive_delete_action( deleted: bool, do_purge_user_account: Option, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { match DeletableObjects::read_from_db(object, context).await? { DeletableObjects::Community(community) => { if community.local { diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index 558d72b57..4f0fad670 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -25,7 +25,7 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use url::Url; #[async_trait::async_trait] @@ -48,7 +48,7 @@ impl ActivityHandler for UndoDelete { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; if self.object.summary.is_some() { UndoDelete::receive_undo_remove_action( @@ -72,7 +72,7 @@ impl UndoDelete { community: Option<&Community>, summary: Option, context: &Data, - ) -> Result { + ) -> LemmyResult { let object = Delete::new(actor, object, to.clone(), community, summary, context)?; let id = generate_activity_id( @@ -96,7 +96,7 @@ impl UndoDelete { actor: &ApubPerson, object: &Url, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { match DeletableObjects::read_from_db(object, context).await? { DeletableObjects::Community(community) => { if community.local { diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index efecef7f1..fa711b904 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -14,12 +14,12 @@ use lemmy_db_schema::{ source::{activity::ActivitySendTargets, community::CommunityFollower}, traits::Followable, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; impl AcceptFollow { #[tracing::instrument(skip_all)] - pub async fn send(follow: Follow, context: &Data) -> Result<(), LemmyError> { + pub async fn send(follow: Follow, context: &Data) -> LemmyResult<()> { let user_or_community = follow.object.dereference_local(context).await?; let person = follow.actor.clone().dereference(context).await?; let accept = AcceptFollow { @@ -52,7 +52,7 @@ impl ActivityHandler for AcceptFollow { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_urls_match(self.actor.inner(), self.object.object.inner())?; self.object.verify(context).await?; if let Some(to) = &self.to { @@ -62,7 +62,7 @@ impl ActivityHandler for AcceptFollow { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let community = self.actor.dereference(context).await?; let person = self.object.actor.dereference(context).await?; diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 6b954ebf2..97227835a 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -26,7 +26,7 @@ use lemmy_db_schema::{ traits::Followable, CommunityVisibility, }; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use url::Url; impl Follow { @@ -34,7 +34,7 @@ impl Follow { actor: &ApubPerson, community: &ApubCommunity, context: &Data, - ) -> Result { + ) -> LemmyResult { Ok(Follow { actor: actor.id().into(), object: community.id().into(), @@ -52,7 +52,7 @@ impl Follow { actor: &ApubPerson, community: &ApubCommunity, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let follow = Follow::new(actor, community, context)?; let inbox = if community.local { ActivitySendTargets::empty() @@ -77,7 +77,7 @@ impl ActivityHandler for Follow { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_person(&self.actor, context).await?; let object = self.object.dereference(context).await?; if let UserOrCommunity::Community(c) = object { @@ -90,7 +90,7 @@ impl ActivityHandler for Follow { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let actor = self.actor.dereference(context).await?; let object = self.object.dereference(context).await?; diff --git a/crates/apub/src/activities/following/mod.rs b/crates/apub/src/activities/following/mod.rs index c4f0bd0b7..7c7163f12 100644 --- a/crates/apub/src/activities/following/mod.rs +++ b/crates/apub/src/activities/following/mod.rs @@ -5,7 +5,7 @@ use crate::{ use activitypub_federation::config::Data; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::{community::Community, person::Person}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; pub mod accept; pub mod follow; @@ -16,7 +16,7 @@ pub async fn send_follow_community( person: Person, follow: bool, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let community: ApubCommunity = community.into(); let actor: ApubPerson = person.into(); if follow { diff --git a/crates/apub/src/activities/following/undo_follow.rs b/crates/apub/src/activities/following/undo_follow.rs index 90dd452f2..ba6253946 100644 --- a/crates/apub/src/activities/following/undo_follow.rs +++ b/crates/apub/src/activities/following/undo_follow.rs @@ -20,7 +20,7 @@ use lemmy_db_schema::{ }, traits::Followable, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; impl UndoFollow { @@ -29,7 +29,7 @@ impl UndoFollow { actor: &ApubPerson, community: &ApubCommunity, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let object = Follow::new(actor, community, context)?; let undo = UndoFollow { actor: actor.id().into(), @@ -64,7 +64,7 @@ impl ActivityHandler for UndoFollow { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { verify_urls_match(self.actor.inner(), self.object.actor.inner())?; verify_person(&self.actor, context).await?; self.object.verify(context).await?; @@ -75,7 +75,7 @@ impl ActivityHandler for UndoFollow { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let person = self.actor.dereference(context).await?; let object = self.object.object.dereference(context).await?; diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 1472d6866..d81e7cabf 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -61,7 +61,7 @@ pub mod voting; async fn verify_person( person_id: &ObjectId, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let person = person_id.dereference(context).await?; if person.banned { Err(anyhow!("Person {} is banned", person_id)) @@ -78,7 +78,7 @@ pub(crate) async fn verify_person_in_community( person_id: &ObjectId, community: &ApubCommunity, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let person = person_id.dereference(context).await?; if person.banned { Err(LemmyErrorType::PersonIsBannedFromSite( @@ -105,7 +105,7 @@ pub(crate) async fn verify_mod_action( mod_id: &ObjectId, community: &Community, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let mod_ = mod_id.dereference(context).await?; let is_mod_or_admin = @@ -124,7 +124,7 @@ pub(crate) async fn verify_mod_action( Err(LemmyErrorType::NotAModerator)? } -pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> Result<(), LemmyError> { +pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> LemmyResult<()> { if ![to, cc].iter().any(|set| set.contains(&public())) { Err(LemmyErrorType::ObjectIsNotPublic)? } else { @@ -132,10 +132,7 @@ pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> Result<(), LemmyError> } } -pub(crate) fn verify_community_matches( - a: &ObjectId, - b: T, -) -> Result<(), LemmyError> +pub(crate) fn verify_community_matches(a: &ObjectId, b: T) -> LemmyResult<()> where T: Into>, { @@ -147,7 +144,7 @@ where } } -pub(crate) fn check_community_deleted_or_removed(community: &Community) -> Result<(), LemmyError> { +pub(crate) fn check_community_deleted_or_removed(community: &Community) -> LemmyResult<()> { if community.deleted || community.removed { Err(LemmyErrorType::CannotCreatePostOrCommentInDeletedOrRemovedCommunity)? } else { @@ -196,7 +193,7 @@ async fn send_lemmy_activity( actor: &ActorT, send_targets: ActivitySendTargets, sensitive: bool, -) -> Result<(), LemmyError> +) -> LemmyResult<()> where Activity: ActivityHandler + Serialize + Send + Sync + Clone, ActorT: Actor + GetActorType, @@ -248,7 +245,9 @@ pub async fn match_outgoing_activities( CreateOrUpdatePage::send(post, creator_id, CreateOrUpdateType::Update, context).await } DeletePost(post, person, data) => { - let community = Community::read(&mut context.pool(), post.community_id).await?; + let community = Community::read(&mut context.pool(), post.community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; send_apub_delete_in_community( person, community, @@ -265,7 +264,9 @@ pub async fn match_outgoing_activities( reason, removed, } => { - let community = Community::read(&mut context.pool(), post.community_id).await?; + let community = Community::read(&mut context.pool(), post.community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; send_apub_delete_in_community( moderator, community, diff --git a/crates/apub/src/activities/voting/mod.rs b/crates/apub/src/activities/voting/mod.rs index 0f9876f1a..2022252c1 100644 --- a/crates/apub/src/activities/voting/mod.rs +++ b/crates/apub/src/activities/voting/mod.rs @@ -21,7 +21,7 @@ use lemmy_db_schema::{ }, traits::Likeable, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; pub mod undo_vote; pub mod vote; @@ -32,7 +32,7 @@ pub(crate) async fn send_like_activity( community: Community, score: i16, context: Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let object_id: ObjectId = object_id.into(); let actor: ApubPerson = actor.into(); let community: ApubCommunity = community.into(); @@ -58,7 +58,7 @@ async fn vote_comment( actor: ApubPerson, comment: &ApubComment, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let comment_id = comment.id; let like_form = CommentLikeForm { comment_id, @@ -78,7 +78,7 @@ async fn vote_post( actor: ApubPerson, post: &ApubPost, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let post_id = post.id; let like_form = PostLikeForm { post_id: post.id, @@ -96,7 +96,7 @@ async fn undo_vote_comment( actor: ApubPerson, comment: &ApubComment, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let comment_id = comment.id; let person_id = actor.id; CommentLike::remove(&mut context.pool(), person_id, comment_id).await?; @@ -108,7 +108,7 @@ async fn undo_vote_post( actor: ApubPerson, post: &ApubPost, context: &Data, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let post_id = post.id; let person_id = actor.id; PostLike::remove(&mut context.pool(), person_id, post_id).await?; diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index f83055620..61875d442 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -19,7 +19,7 @@ use activitypub_federation::{ traits::{ActivityHandler, Actor}, }; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; impl UndoVote { @@ -28,7 +28,7 @@ impl UndoVote { actor: &ApubPerson, community: &ApubCommunity, context: &Data, - ) -> Result { + ) -> LemmyResult { Ok(UndoVote { actor: actor.id().into(), object: vote, @@ -56,7 +56,7 @@ impl ActivityHandler for UndoVote { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { let community = self.community(context).await?; verify_person_in_community(&self.actor, &community, context).await?; verify_urls_match(self.actor.inner(), self.object.actor.inner())?; @@ -65,7 +65,7 @@ impl ActivityHandler for UndoVote { } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let actor = self.actor.dereference(context).await?; let object = self.object.object.dereference(context).await?; diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 5625ea6ba..324c8b300 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -19,7 +19,7 @@ use activitypub_federation::{ }; use lemmy_api_common::{context::LemmyContext, utils::check_bot_account}; use lemmy_db_schema::source::local_site::LocalSite; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; impl Vote { @@ -29,7 +29,7 @@ impl Vote { community: &ApubCommunity, kind: VoteType, context: &Data, - ) -> Result { + ) -> LemmyResult { Ok(Vote { actor: actor.id().into(), object: object_id, @@ -54,14 +54,14 @@ impl ActivityHandler for Vote { } #[tracing::instrument(skip_all)] - async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> LemmyResult<()> { let community = self.community(context).await?; verify_person_in_community(&self.actor, &community, context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive(self, context: &Data) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> LemmyResult<()> { insert_received_activity(&self.id, context).await?; let actor = self.actor.dereference(context).await?; let object = self.object.dereference(context).await?; diff --git a/crates/apub/src/activity_lists.rs b/crates/apub/src/activity_lists.rs index 7b33499c8..3aeb7e45e 100644 --- a/crates/apub/src/activity_lists.rs +++ b/crates/apub/src/activity_lists.rs @@ -26,7 +26,7 @@ use crate::{ }; use activitypub_federation::{config::Data, traits::ActivityHandler}; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use serde::{Deserialize, Serialize}; use url::Url; @@ -101,7 +101,7 @@ pub enum AnnouncableActivities { #[async_trait::async_trait] impl InCommunity for AnnouncableActivities { #[tracing::instrument(skip(self, context))] - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { use AnnouncableActivities::*; match self { CreateOrUpdateComment(a) => a.community(context).await, diff --git a/crates/apub/src/api/list_comments.rs b/crates/apub/src/api/list_comments.rs index c83756f54..25d197007 100644 --- a/crates/apub/src/api/list_comments.rs +++ b/crates/apub/src/api/list_comments.rs @@ -15,14 +15,14 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::{comment_view::CommentQuery, structs::LocalUserView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn list_comments( data: Query, context: Data, local_user_view: Option, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; check_private_instance(&local_user_view, &local_site)?; @@ -58,7 +58,12 @@ pub async fn list_comments( // If a parent_id is given, fetch the comment to get the path let parent_path = if let Some(parent_id) = parent_id { - Some(Comment::read(&mut context.pool(), parent_id).await?.path) + Some( + Comment::read(&mut context.pool(), parent_id) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)? + .path, + ) } else { None }; diff --git a/crates/apub/src/api/list_posts.rs b/crates/apub/src/api/list_posts.rs index 384f1b60e..ec5412de8 100644 --- a/crates/apub/src/api/list_posts.rs +++ b/crates/apub/src/api/list_posts.rs @@ -15,15 +15,17 @@ use lemmy_db_views::{ post_view::PostQuery, structs::{LocalUserView, PaginationCursor, SiteView}, }; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn list_posts( data: Query, context: Data, local_user_view: Option, -) -> Result, LemmyError> { - let local_site = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult> { + let local_site = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; check_private_instance(&local_user_view, &local_site.local_site)?; diff --git a/crates/apub/src/api/read_community.rs b/crates/apub/src/api/read_community.rs index a41deb32c..dae7719ae 100644 --- a/crates/apub/src/api/read_community.rs +++ b/crates/apub/src/api/read_community.rs @@ -13,14 +13,14 @@ use lemmy_db_schema::source::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn get_community( data: Query, context: Data, local_user_view: Option, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; if data.name.is_none() && data.id.is_none() { @@ -56,8 +56,8 @@ pub async fn get_community( person_id, is_mod_or_admin, ) - .await - .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?; + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id) .await diff --git a/crates/apub/src/api/read_person.rs b/crates/apub/src/api/read_person.rs index c779657c8..0d65ab4f7 100644 --- a/crates/apub/src/api/read_person.rs +++ b/crates/apub/src/api/read_person.rs @@ -13,20 +13,22 @@ use lemmy_db_views::{ structs::{LocalUserView, SiteView}, }; use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt2, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn read_person( data: Query, context: Data, local_user_view: Option, -) -> Result, LemmyError> { +) -> LemmyResult> { // Check to make sure a person name or an id is given if data.username.is_none() && data.person_id.is_none() { Err(LemmyErrorType::NoIdGiven)? } - let local_site = SiteView::read_local(&mut context.pool()).await?; + let local_site = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; check_private_instance(&local_user_view, &local_site.local_site)?; @@ -46,7 +48,9 @@ pub async fn read_person( // You don't need to return settings for the user, since this comes back with GetSite // `my_user` - let person_view = PersonView::read(&mut context.pool(), person_details_id).await?; + let person_view = PersonView::read(&mut context.pool(), person_details_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; let sort = data.sort; let page = data.page; diff --git a/crates/apub/src/api/resolve_object.rs b/crates/apub/src/api/resolve_object.rs index 6d672a8cd..47f6c5d06 100644 --- a/crates/apub/src/api/resolve_object.rs +++ b/crates/apub/src/api/resolve_object.rs @@ -13,14 +13,14 @@ use lemmy_api_common::{ use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool}; use lemmy_db_views::structs::{CommentView, LocalUserView, PostView}; use lemmy_db_views_actor::structs::{CommunityView, PersonView}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorExt2, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn resolve_object( data: Query, context: Data, local_user_view: Option, -) -> Result, LemmyError> { +) -> LemmyResult> { let local_site = LocalSite::read(&mut context.pool()).await?; check_private_instance(&local_user_view, &local_site)?; let person_id = local_user_view.map(|v| v.person.id); @@ -46,27 +46,43 @@ async fn convert_response( object: SearchableObjects, user_id: Option, pool: &mut DbPool<'_>, -) -> Result, LemmyError> { +) -> LemmyResult> { use SearchableObjects::*; let removed_or_deleted; let mut res = ResolveObjectResponse::default(); match object { Post(p) => { removed_or_deleted = p.deleted || p.removed; - res.post = Some(PostView::read(pool, p.id, user_id, false).await?) + res.post = Some( + PostView::read(pool, p.id, user_id, false) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?, + ) } Comment(c) => { removed_or_deleted = c.deleted || c.removed; - res.comment = Some(CommentView::read(pool, c.id, user_id).await?) + res.comment = Some( + CommentView::read(pool, c.id, user_id) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?, + ) } PersonOrCommunity(p) => match *p { UserOrCommunity::User(u) => { removed_or_deleted = u.deleted; - res.person = Some(PersonView::read(pool, u.id).await?) + res.person = Some( + PersonView::read(pool, u.id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?, + ) } UserOrCommunity::Community(c) => { removed_or_deleted = c.deleted || c.removed; - res.community = Some(CommunityView::read(pool, c.id, user_id, false).await?) + res.community = Some( + CommunityView::read(pool, c.id, user_id, false) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?, + ) } }, }; diff --git a/crates/apub/src/api/search.rs b/crates/apub/src/api/search.rs index 32128a3c1..f3cd36faf 100644 --- a/crates/apub/src/api/search.rs +++ b/crates/apub/src/api/search.rs @@ -13,15 +13,17 @@ use lemmy_db_views::{ structs::{LocalUserView, SiteView}, }; use lemmy_db_views_actor::{community_view::CommunityQuery, person_view::PersonQuery}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; #[tracing::instrument(skip(context))] pub async fn search( data: Query, context: Data, local_user_view: Option, -) -> Result, LemmyError> { - let local_site = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult> { + let local_site = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; check_private_instance(&local_user_view, &local_site.local_site)?; diff --git a/crates/apub/src/api/user_settings_backup.rs b/crates/apub/src/api/user_settings_backup.rs index 4c1edcbff..57e1d0f97 100644 --- a/crates/apub/src/api/user_settings_backup.rs +++ b/crates/apub/src/api/user_settings_backup.rs @@ -26,7 +26,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::{ - error::{LemmyError, LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS}, + error::{LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS}, spawn_try_task, }; use serde::{Deserialize, Serialize}; @@ -70,7 +70,7 @@ pub struct UserSettingsBackup { pub async fn export_settings( local_user_view: LocalUserView, context: Data, -) -> Result, LemmyError> { +) -> LemmyResult> { let lists = LocalUser::export_backup(&mut context.pool(), local_user_view.person.id).await?; let vec_into = |vec: Vec<_>| vec.into_iter().map(Into::into).collect(); @@ -97,7 +97,7 @@ pub async fn import_settings( data: Json, local_user_view: LocalUserView, context: Data, -) -> Result, LemmyError> { +) -> LemmyResult> { let person_form = PersonUpdateForm { display_name: Some(data.display_name.clone()), bio: Some(data.bio.clone()), @@ -363,7 +363,11 @@ mod tests { .build(); let local_user = LocalUser::create(&mut context.pool(), &user_form, vec![]).await?; - Ok(LocalUserView::read(&mut context.pool(), local_user.id).await?) + Ok( + LocalUserView::read(&mut context.pool(), local_user.id) + .await? + .ok_or(LemmyErrorType::CouldntFindLocalUser)?, + ) } #[tokio::test] @@ -396,8 +400,9 @@ mod tests { // wait for background task to finish sleep(Duration::from_millis(1000)).await; - let import_user_updated = - LocalUserView::read(&mut context.pool(), import_user.local_user.id).await?; + let import_user_updated = LocalUserView::read(&mut context.pool(), import_user.local_user.id) + .await? + .ok_or(LemmyErrorType::CouldntFindLocalUser)?; assert_eq!( export_user.person.display_name, diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs index 0532d0aef..02b912f44 100644 --- a/crates/apub/src/collections/community_moderators.rs +++ b/crates/apub/src/collections/community_moderators.rs @@ -15,7 +15,7 @@ use lemmy_db_schema::{ traits::Joinable, }; use lemmy_db_views_actor::structs::CommunityModeratorView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; #[derive(Clone, Debug)] @@ -29,10 +29,7 @@ impl Collection for ApubCommunityModerators { type Error = LemmyError; #[tracing::instrument(skip_all)] - async fn read_local( - owner: &Self::Owner, - data: &Data, - ) -> Result { + async fn read_local(owner: &Self::Owner, data: &Data) -> LemmyResult { let moderators = CommunityModeratorView::for_community(&mut data.pool(), owner.id).await?; let ordered_items = moderators .into_iter() @@ -50,7 +47,7 @@ impl Collection for ApubCommunityModerators { group_moderators: &GroupModerators, expected_domain: &Url, _data: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { verify_domains_match(&group_moderators.id, expected_domain)?; Ok(()) } @@ -60,7 +57,7 @@ impl Collection for ApubCommunityModerators { apub: Self::Kind, owner: &Self::Owner, data: &Data, - ) -> Result { + ) -> LemmyResult { let community_id = owner.id; let current_moderators = CommunityModeratorView::for_community(&mut data.pool(), community_id).await?; @@ -118,7 +115,6 @@ mod tests { }, traits::Crud, }; - use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index 0799db789..71985f946 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -23,7 +23,10 @@ use lemmy_db_schema::{ traits::Crud, utils::FETCH_LIMIT_MAX, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{ + error::{LemmyError, LemmyResult}, + LemmyErrorType, +}; use url::Url; #[derive(Clone, Debug)] @@ -37,10 +40,7 @@ impl Collection for ApubCommunityOutbox { type Error = LemmyError; #[tracing::instrument(skip_all)] - async fn read_local( - owner: &Self::Owner, - data: &Data, - ) -> Result { + async fn read_local(owner: &Self::Owner, data: &Data) -> LemmyResult { let post_list: Vec = Post::list_for_community(&mut data.pool(), owner.id) .await? .into_iter() @@ -50,6 +50,7 @@ impl Collection for ApubCommunityOutbox { for post in post_list { let person = Person::read(&mut data.pool(), post.creator_id) .await? + .ok_or(LemmyErrorType::CouldntFindPerson)? .into(); let create = CreateOrUpdatePage::new(post, &person, owner, CreateOrUpdateType::Create, data).await?; @@ -71,7 +72,7 @@ impl Collection for ApubCommunityOutbox { group_outbox: &GroupOutbox, expected_domain: &Url, _data: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { verify_domains_match(expected_domain, &group_outbox.id)?; Ok(()) } @@ -81,7 +82,7 @@ impl Collection for ApubCommunityOutbox { apub: Self::Kind, _owner: &Self::Owner, data: &Data, - ) -> Result { + ) -> LemmyResult { let mut outbox_activities = apub.ordered_items; if outbox_activities.len() as i64 > FETCH_LIMIT_MAX { outbox_activities = outbox_activities diff --git a/crates/apub/src/fetcher/mod.rs b/crates/apub/src/fetcher/mod.rs index 4e30b4b16..68fc07d30 100644 --- a/crates/apub/src/fetcher/mod.rs +++ b/crates/apub/src/fetcher/mod.rs @@ -8,7 +8,7 @@ use itertools::Itertools; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::traits::ApubActor; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; pub mod post_or_comment; pub mod search; @@ -25,7 +25,7 @@ pub async fn resolve_actor_identifier( context: &Data, local_user_view: &Option, include_deleted: bool, -) -> Result +) -> LemmyResult where ActorType: Object + Object @@ -42,9 +42,12 @@ where .splitn(2, '@') .collect_tuple() .expect("invalid query"); - let actor = DbActor::read_from_name_and_domain(&mut context.pool(), name, domain).await; - if actor.is_ok() { - Ok(actor?.into()) + let actor = DbActor::read_from_name_and_domain(&mut context.pool(), name, domain) + .await + .ok() + .flatten(); + if let Some(actor) = actor { + Ok(actor.into()) } else if local_user_view.is_some() { // Fetch the actor from its home instance using webfinger let actor: ActorType = webfinger_resolve_actor(&identifier.to_lowercase(), context).await?; @@ -59,6 +62,7 @@ where Ok( DbActor::read_from_name(&mut context.pool(), &identifier, include_deleted) .await? + .ok_or(NotFound)? .into(), ) } diff --git a/crates/apub/src/fetcher/post_or_comment.rs b/crates/apub/src/fetcher/post_or_comment.rs index 31c53864a..083369b9d 100644 --- a/crates/apub/src/fetcher/post_or_comment.rs +++ b/crates/apub/src/fetcher/post_or_comment.rs @@ -12,7 +12,10 @@ use lemmy_db_schema::{ source::{community::Community, post::Post}, traits::Crud, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{ + error::{LemmyError, LemmyResult}, + LemmyErrorType, +}; use serde::Deserialize; use url::Url; @@ -40,10 +43,7 @@ impl Object for PostOrComment { } #[tracing::instrument(skip_all)] - async fn read_from_id( - object_id: Url, - data: &Data, - ) -> Result, LemmyError> { + async fn read_from_id(object_id: Url, data: &Data) -> LemmyResult> { let post = ApubPost::read_from_id(object_id.clone(), data).await?; Ok(match post { Some(o) => Some(PostOrComment::Post(o)), @@ -54,14 +54,14 @@ impl Object for PostOrComment { } #[tracing::instrument(skip_all)] - async fn delete(self, data: &Data) -> Result<(), LemmyError> { + async fn delete(self, data: &Data) -> LemmyResult<()> { match self { PostOrComment::Post(p) => p.delete(data).await, PostOrComment::Comment(c) => c.delete(data).await, } } - async fn into_json(self, _data: &Data) -> Result { + async fn into_json(self, _data: &Data) -> LemmyResult { unimplemented!() } @@ -70,7 +70,7 @@ impl Object for PostOrComment { apub: &Self::Kind, expected_domain: &Url, data: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { match apub { PageOrNote::Page(a) => ApubPost::verify(a, expected_domain, data).await, PageOrNote::Note(a) => ApubComment::verify(a, expected_domain, data).await, @@ -78,7 +78,7 @@ impl Object for PostOrComment { } #[tracing::instrument(skip_all)] - async fn from_json(apub: PageOrNote, context: &Data) -> Result { + async fn from_json(apub: PageOrNote, context: &Data) -> LemmyResult { Ok(match apub { PageOrNote::Page(p) => PostOrComment::Post(ApubPost::from_json(*p, context).await?), PageOrNote::Note(n) => PostOrComment::Comment(ApubComment::from_json(n, context).await?), @@ -88,15 +88,21 @@ impl Object for PostOrComment { #[async_trait::async_trait] impl InCommunity for PostOrComment { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let cid = match self { PostOrComment::Post(p) => p.community_id, PostOrComment::Comment(c) => { Post::read(&mut context.pool(), c.post_id) .await? + .ok_or(LemmyErrorType::CouldntFindPost)? .community_id } }; - Ok(Community::read(&mut context.pool(), cid).await?.into()) + Ok( + Community::read(&mut context.pool(), cid) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)? + .into(), + ) } } diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs index 74d755da0..8c533ba88 100644 --- a/crates/apub/src/fetcher/search.rs +++ b/crates/apub/src/fetcher/search.rs @@ -10,7 +10,7 @@ use activitypub_federation::{ }; use chrono::{DateTime, Utc}; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use serde::Deserialize; use url::Url; @@ -21,7 +21,7 @@ use url::Url; pub(crate) async fn search_query_to_object_id( mut query: String, context: &Data, -) -> Result { +) -> LemmyResult { Ok(match Url::parse(&query) { Ok(url) => { // its already an url, just go with it @@ -46,7 +46,7 @@ pub(crate) async fn search_query_to_object_id( pub(crate) async fn search_query_to_object_id_local( query: &str, context: &Data, -) -> Result { +) -> LemmyResult { let url = Url::parse(query)?; ObjectId::from(url).dereference_local(context).await } @@ -90,7 +90,7 @@ impl Object for SearchableObjects { async fn read_from_id( object_id: Url, context: &Data, - ) -> Result, LemmyError> { + ) -> LemmyResult> { let uc = UserOrCommunity::read_from_id(object_id.clone(), context).await?; if let Some(uc) = uc { return Ok(Some(SearchableObjects::PersonOrCommunity(Box::new(uc)))); @@ -107,7 +107,7 @@ impl Object for SearchableObjects { } #[tracing::instrument(skip_all)] - async fn delete(self, data: &Data) -> Result<(), LemmyError> { + async fn delete(self, data: &Data) -> LemmyResult<()> { match self { SearchableObjects::Post(p) => p.delete(data).await, SearchableObjects::Comment(c) => c.delete(data).await, @@ -118,7 +118,7 @@ impl Object for SearchableObjects { } } - async fn into_json(self, _data: &Data) -> Result { + async fn into_json(self, _data: &Data) -> LemmyResult { unimplemented!() } @@ -127,7 +127,7 @@ impl Object for SearchableObjects { apub: &Self::Kind, expected_domain: &Url, data: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { match apub { SearchableKinds::Page(a) => ApubPost::verify(a, expected_domain, data).await, SearchableKinds::Note(a) => ApubComment::verify(a, expected_domain, data).await, @@ -139,7 +139,7 @@ impl Object for SearchableObjects { } #[tracing::instrument(skip_all)] - async fn from_json(apub: Self::Kind, context: &Data) -> Result { + async fn from_json(apub: Self::Kind, context: &Data) -> LemmyResult { use SearchableKinds as SAT; use SearchableObjects as SO; Ok(match apub { diff --git a/crates/apub/src/fetcher/site_or_community_or_user.rs b/crates/apub/src/fetcher/site_or_community_or_user.rs index 76ee566c9..30b5fd568 100644 --- a/crates/apub/src/fetcher/site_or_community_or_user.rs +++ b/crates/apub/src/fetcher/site_or_community_or_user.rs @@ -9,7 +9,7 @@ use activitypub_federation::{ }; use chrono::{DateTime, Utc}; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use reqwest::Url; use serde::{Deserialize, Serialize}; @@ -44,19 +44,19 @@ impl Object for SiteOrCommunityOrUser { async fn read_from_id( _object_id: Url, _data: &Data, - ) -> Result, LemmyError> { + ) -> LemmyResult> { unimplemented!(); } #[tracing::instrument(skip_all)] - async fn delete(self, data: &Data) -> Result<(), LemmyError> { + async fn delete(self, data: &Data) -> LemmyResult<()> { match self { SiteOrCommunityOrUser::Site(p) => p.delete(data).await, SiteOrCommunityOrUser::UserOrCommunity(p) => p.delete(data).await, } } - async fn into_json(self, _data: &Data) -> Result { + async fn into_json(self, _data: &Data) -> LemmyResult { unimplemented!() } @@ -65,7 +65,7 @@ impl Object for SiteOrCommunityOrUser { apub: &Self::Kind, expected_domain: &Url, data: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { match apub { SiteOrPersonOrGroup::Instance(a) => ApubSite::verify(a, expected_domain, data).await, SiteOrPersonOrGroup::PersonOrGroup(a) => { @@ -75,7 +75,7 @@ impl Object for SiteOrCommunityOrUser { } #[tracing::instrument(skip_all)] - async fn from_json(_apub: Self::Kind, _data: &Data) -> Result { + async fn from_json(_apub: Self::Kind, _data: &Data) -> LemmyResult { unimplemented!(); } } diff --git a/crates/apub/src/fetcher/user_or_community.rs b/crates/apub/src/fetcher/user_or_community.rs index 93e955c7b..d29cbb6b0 100644 --- a/crates/apub/src/fetcher/user_or_community.rs +++ b/crates/apub/src/fetcher/user_or_community.rs @@ -10,7 +10,7 @@ use activitypub_federation::{ use chrono::{DateTime, Utc}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::activity::ActorType; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyResult}; use serde::{Deserialize, Serialize}; use url::Url; @@ -47,10 +47,7 @@ impl Object for UserOrCommunity { } #[tracing::instrument(skip_all)] - async fn read_from_id( - object_id: Url, - data: &Data, - ) -> Result, LemmyError> { + async fn read_from_id(object_id: Url, data: &Data) -> LemmyResult> { let person = ApubPerson::read_from_id(object_id.clone(), data).await?; Ok(match person { Some(o) => Some(UserOrCommunity::User(o)), @@ -61,14 +58,14 @@ impl Object for UserOrCommunity { } #[tracing::instrument(skip_all)] - async fn delete(self, data: &Data) -> Result<(), LemmyError> { + async fn delete(self, data: &Data) -> LemmyResult<()> { match self { UserOrCommunity::User(p) => p.delete(data).await, UserOrCommunity::Community(p) => p.delete(data).await, } } - async fn into_json(self, _data: &Data) -> Result { + async fn into_json(self, _data: &Data) -> LemmyResult { unimplemented!() } @@ -77,7 +74,7 @@ impl Object for UserOrCommunity { apub: &Self::Kind, expected_domain: &Url, data: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { match apub { PersonOrGroup::Person(a) => ApubPerson::verify(a, expected_domain, data).await, PersonOrGroup::Group(a) => ApubCommunity::verify(a, expected_domain, data).await, @@ -85,7 +82,7 @@ impl Object for UserOrCommunity { } #[tracing::instrument(skip_all)] - async fn from_json(apub: Self::Kind, data: &Data) -> Result { + async fn from_json(apub: Self::Kind, data: &Data) -> LemmyResult { Ok(match apub { PersonOrGroup::Person(p) => UserOrCommunity::User(ApubPerson::from_json(p, data).await?), PersonOrGroup::Group(p) => { diff --git a/crates/apub/src/http/comment.rs b/crates/apub/src/http/comment.rs index 200dda8ed..17711817e 100644 --- a/crates/apub/src/http/comment.rs +++ b/crates/apub/src/http/comment.rs @@ -15,7 +15,7 @@ use lemmy_db_schema::{ source::{comment::Comment, community::Community, post::Post}, traits::Crud, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use serde::Deserialize; #[derive(Deserialize)] @@ -28,12 +28,19 @@ pub(crate) struct CommentQuery { pub(crate) async fn get_apub_comment( info: Path, context: Data, -) -> Result { +) -> LemmyResult { let id = CommentId(info.comment_id.parse::()?); // Can't use CommentView here because it excludes deleted/removed/local-only items - let comment: ApubComment = Comment::read(&mut context.pool(), id).await?.into(); - let post = Post::read(&mut context.pool(), comment.post_id).await?; - let community = Community::read(&mut context.pool(), post.community_id).await?; + let comment: ApubComment = Comment::read(&mut context.pool(), id) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)? + .into(); + let post = Post::read(&mut context.pool(), comment.post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; + let community = Community::read(&mut context.pool(), post.community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; check_community_public(&community)?; if !comment.local { diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index cf0d1625d..c7a1f9eda 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -18,7 +18,7 @@ use activitypub_federation::{ use actix_web::{web, web::Bytes, HttpRequest, HttpResponse}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{source::community::Community, traits::ApubActor}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use serde::Deserialize; #[derive(Deserialize, Clone)] @@ -31,10 +31,11 @@ pub(crate) struct CommunityQuery { pub(crate) async fn get_apub_community_http( info: web::Path, context: Data, -) -> Result { +) -> LemmyResult { let community: ApubCommunity = Community::read_from_name(&mut context.pool(), &info.community_name, true) .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)? .into(); if community.deleted || community.removed { @@ -52,7 +53,7 @@ pub async fn community_inbox( request: HttpRequest, body: Bytes, data: Data, -) -> Result { +) -> LemmyResult { receive_activity::, ApubPerson, LemmyContext>( request, body, &data, ) @@ -63,9 +64,10 @@ pub async fn community_inbox( pub(crate) async fn get_apub_community_followers( info: web::Path, context: Data, -) -> Result { - let community = - Community::read_from_name(&mut context.pool(), &info.community_name, false).await?; +) -> LemmyResult { + let community = Community::read_from_name(&mut context.pool(), &info.community_name, false) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; check_community_public(&community)?; let followers = ApubCommunityFollower::read_local(&community.into(), &context).await?; create_apub_response(&followers) @@ -76,10 +78,11 @@ pub(crate) async fn get_apub_community_followers( pub(crate) async fn get_apub_community_outbox( info: web::Path, context: Data, -) -> Result { +) -> LemmyResult { let community: ApubCommunity = Community::read_from_name(&mut context.pool(), &info.community_name, false) .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)? .into(); check_community_public(&community)?; let outbox = ApubCommunityOutbox::read_local(&community, &context).await?; @@ -90,10 +93,11 @@ pub(crate) async fn get_apub_community_outbox( pub(crate) async fn get_apub_community_moderators( info: web::Path, context: Data, -) -> Result { +) -> LemmyResult { let community: ApubCommunity = Community::read_from_name(&mut context.pool(), &info.community_name, false) .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)? .into(); check_community_public(&community)?; let moderators = ApubCommunityModerators::read_local(&community, &context).await?; @@ -104,10 +108,11 @@ pub(crate) async fn get_apub_community_moderators( pub(crate) async fn get_apub_community_featured( info: web::Path, context: Data, -) -> Result { +) -> LemmyResult { let community: ApubCommunity = Community::read_from_name(&mut context.pool(), &info.community_name, false) .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)? .into(); check_community_public(&community)?; let featured = ApubCommunityFeatured::read_local(&community, &context).await?; @@ -127,7 +132,6 @@ pub(crate) mod tests { traits::Crud, CommunityVisibility, }; - use lemmy_utils::error::LemmyResult; use serde::de::DeserializeOwned; use serial_test::serial; @@ -135,7 +139,7 @@ pub(crate) mod tests { deleted: bool, visibility: CommunityVisibility, context: &Data, - ) -> Result<(Instance, Community), LemmyError> { + ) -> LemmyResult<(Instance, Community)> { let instance = Instance::read_or_create(&mut context.pool(), "my_domain.tld".to_string()).await?; let community_form = CommunityInsertForm::builder() @@ -150,7 +154,7 @@ pub(crate) mod tests { Ok((instance, community)) } - async fn decode_response(res: HttpResponse) -> Result { + async fn decode_response(res: HttpResponse) -> LemmyResult { let body = to_bytes(res.into_body()).await.unwrap(); let body = std::str::from_utf8(&body)?; Ok(serde_json::from_str(body)?) diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs index b400e3dab..8aba7832f 100644 --- a/crates/apub/src/http/mod.rs +++ b/crates/apub/src/http/mod.rs @@ -18,7 +18,7 @@ use lemmy_db_schema::{ source::{activity::SentActivity, community::Community}, CommunityVisibility, }; -use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use serde::{Deserialize, Serialize}; use std::ops::Deref; use url::Url; @@ -88,7 +88,7 @@ pub struct ActivityQuery { pub(crate) async fn get_activity( info: web::Path, context: web::Data, -) -> Result { +) -> LemmyResult { let settings = context.settings(); let activity_id = Url::parse(&format!( "{}/activities/{}/{}", @@ -97,7 +97,9 @@ pub(crate) async fn get_activity( info.id ))? .into(); - let activity = SentActivity::read_from_apub_id(&mut context.pool(), &activity_id).await?; + let activity = SentActivity::read_from_apub_id(&mut context.pool(), &activity_id) + .await? + .ok_or(LemmyErrorType::CouldntFindActivity)?; let sensitive = activity.sensitive; if sensitive { diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index 254313634..ba2372fe8 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -14,7 +14,7 @@ use activitypub_federation::{ use actix_web::{web, web::Bytes, HttpRequest, HttpResponse}; use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url}; use lemmy_db_schema::{source::person::Person, traits::ApubActor}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use serde::Deserialize; #[derive(Deserialize)] @@ -27,11 +27,12 @@ pub struct PersonQuery { pub(crate) async fn get_apub_person_http( info: web::Path, context: Data, -) -> Result { +) -> LemmyResult { let user_name = info.into_inner().user_name; // TODO: this needs to be able to read deleted persons, so that it can send tombstones let person: ApubPerson = Person::read_from_name(&mut context.pool(), &user_name, true) .await? + .ok_or(LemmyErrorType::CouldntFindPerson)? .into(); if !person.deleted { @@ -48,7 +49,7 @@ pub async fn person_inbox( request: HttpRequest, body: Bytes, data: Data, -) -> Result { +) -> LemmyResult { receive_activity::, UserOrCommunity, LemmyContext>( request, body, &data, ) @@ -59,8 +60,10 @@ pub async fn person_inbox( pub(crate) async fn get_apub_person_outbox( info: web::Path, context: Data, -) -> Result { - let person = Person::read_from_name(&mut context.pool(), &info.user_name, false).await?; +) -> LemmyResult { + let person = Person::read_from_name(&mut context.pool(), &info.user_name, false) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; let outbox_id = generate_outbox_url(&person.actor_id)?.into(); let outbox = EmptyOutbox::new(outbox_id)?; create_apub_response(&outbox) diff --git a/crates/apub/src/http/post.rs b/crates/apub/src/http/post.rs index 6ca07f684..513cba7ea 100644 --- a/crates/apub/src/http/post.rs +++ b/crates/apub/src/http/post.rs @@ -15,7 +15,7 @@ use lemmy_db_schema::{ source::{community::Community, post::Post}, traits::Crud, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use serde::Deserialize; #[derive(Deserialize)] @@ -28,11 +28,16 @@ pub(crate) struct PostQuery { pub(crate) async fn get_apub_post( info: web::Path, context: Data, -) -> Result { +) -> LemmyResult { let id = PostId(info.post_id.parse::()?); // Can't use PostView here because it excludes deleted/removed/local-only items - let post: ApubPost = Post::read(&mut context.pool(), id).await?.into(); - let community = Community::read(&mut context.pool(), post.community_id).await?; + let post: ApubPost = Post::read(&mut context.pool(), id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)? + .into(); + let community = Community::read(&mut context.pool(), post.community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; check_community_public(&community)?; if !post.local { diff --git a/crates/apub/src/http/site.rs b/crates/apub/src/http/site.rs index 410b29b18..54d3c0e32 100644 --- a/crates/apub/src/http/site.rs +++ b/crates/apub/src/http/site.rs @@ -7,22 +7,22 @@ use activitypub_federation::{config::Data, traits::Object}; use actix_web::HttpResponse; use lemmy_api_common::context::LemmyContext; use lemmy_db_views::structs::SiteView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use url::Url; -pub(crate) async fn get_apub_site_http( - context: Data, -) -> Result { - let site: ApubSite = SiteView::read_local(&mut context.pool()).await?.site.into(); +pub(crate) async fn get_apub_site_http(context: Data) -> LemmyResult { + let site: ApubSite = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)? + .site + .into(); let apub = site.into_json(&context).await?; create_apub_response(&apub) } #[tracing::instrument(skip_all)] -pub(crate) async fn get_apub_site_outbox( - context: Data, -) -> Result { +pub(crate) async fn get_apub_site_outbox(context: Data) -> LemmyResult { let outbox_id = format!( "{}/site_outbox", context.settings().get_protocol_and_hostname() diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index c09a3007c..29c9a12eb 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -11,7 +11,7 @@ use lemmy_db_schema::{ }; use lemmy_utils::{ error::{LemmyError, LemmyErrorType, LemmyResult}, - CACHE_DURATION_SHORT, + CACHE_DURATION_FEDERATION, }; use moka::future::Cache; use once_cell::sync::Lazy; @@ -77,7 +77,7 @@ impl UrlVerifier for VerifyUrlData { /// - URL being in the allowlist (if it is active) /// - URL not being in the blocklist (if it is active) #[tracing::instrument(skip(local_site_data))] -fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> Result<(), LemmyError> { +fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> LemmyResult<()> { let domain = apub_id.domain().expect("apud id has domain").to_string(); if !local_site_data @@ -127,7 +127,7 @@ pub(crate) async fn local_site_data_cached( static CACHE: Lazy>> = Lazy::new(|| { Cache::builder() .max_capacity(1) - .time_to_live(CACHE_DURATION_SHORT) + .time_to_live(CACHE_DURATION_FEDERATION) .build() }); Ok( @@ -157,7 +157,7 @@ pub(crate) async fn check_apub_id_valid_with_strictness( apub_id: &Url, is_strict: bool, context: &LemmyContext, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let domain = apub_id.domain().expect("apud id has domain").to_string(); let local_instance = context .settings() @@ -198,10 +198,7 @@ pub(crate) async fn check_apub_id_valid_with_strictness( /// This ensures that the same activity doesnt get received and processed more than once, which /// would be a waste of resources. #[tracing::instrument(skip(data))] -async fn insert_received_activity( - ap_id: &Url, - data: &Data, -) -> Result<(), LemmyError> { +async fn insert_received_activity(ap_id: &Url, data: &Data) -> LemmyResult<()> { ReceivedActivity::create(&mut data.pool(), &ap_id.clone().into()).await?; Ok(()) } diff --git a/crates/apub/src/mentions.rs b/crates/apub/src/mentions.rs index b088dfd03..4f4edc76d 100644 --- a/crates/apub/src/mentions.rs +++ b/crates/apub/src/mentions.rs @@ -11,7 +11,7 @@ use lemmy_db_schema::{ traits::Crud, utils::DbPool, }; -use lemmy_utils::{error::LemmyError, utils::mention::scrape_text_for_mentions}; +use lemmy_utils::{error::LemmyResult, utils::mention::scrape_text_for_mentions, LemmyErrorType}; use serde::{Deserialize, Serialize}; use serde_json::Value; use url::Url; @@ -44,7 +44,7 @@ pub async fn collect_non_local_mentions( comment: &ApubComment, community_id: ObjectId, context: &Data, -) -> Result { +) -> LemmyResult { let parent_creator = get_comment_parent_creator(&mut context.pool(), comment).await?; let mut addressed_ccs: Vec = vec![community_id.into(), parent_creator.id()]; @@ -94,14 +94,23 @@ pub async fn collect_non_local_mentions( async fn get_comment_parent_creator( pool: &mut DbPool<'_>, comment: &Comment, -) -> Result { +) -> LemmyResult { let parent_creator_id = if let Some(parent_comment_id) = comment.parent_comment_id() { - let parent_comment = Comment::read(pool, parent_comment_id).await?; + let parent_comment = Comment::read(pool, parent_comment_id) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; parent_comment.creator_id } else { let parent_post_id = comment.post_id; - let parent_post = Post::read(pool, parent_post_id).await?; + let parent_post = Post::read(pool, parent_post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; parent_post.creator_id }; - Ok(Person::read(pool, parent_creator_id).await?.into()) + Ok( + Person::read(pool, parent_creator_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)? + .into(), + ) } diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index ba7cc914f..02de96f20 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -29,9 +29,10 @@ use lemmy_db_schema::{ post::Post, }, traits::Crud, + utils::naive_now, }; use lemmy_utils::{ - error::{LemmyError, LemmyErrorType}, + error::{LemmyError, LemmyErrorType, LemmyResult}, utils::markdown::markdown_to_html, }; use std::ops::Deref; @@ -67,7 +68,7 @@ impl Object for ApubComment { async fn read_from_id( object_id: Url, context: &Data, - ) -> Result, LemmyError> { + ) -> LemmyResult> { Ok( Comment::read_from_apub_id(&mut context.pool(), object_id) .await? @@ -76,7 +77,7 @@ impl Object for ApubComment { } #[tracing::instrument(skip_all)] - async fn delete(self, context: &Data) -> Result<(), LemmyError> { + async fn delete(self, context: &Data) -> LemmyResult<()> { if !self.deleted { let form = CommentUpdateForm { deleted: Some(true), @@ -88,17 +89,25 @@ impl Object for ApubComment { } #[tracing::instrument(skip_all)] - async fn into_json(self, context: &Data) -> Result { + async fn into_json(self, context: &Data) -> LemmyResult { let creator_id = self.creator_id; - let creator = Person::read(&mut context.pool(), creator_id).await?; + let creator = Person::read(&mut context.pool(), creator_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; let post_id = self.post_id; - let post = Post::read(&mut context.pool(), post_id).await?; + let post = Post::read(&mut context.pool(), post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; let community_id = post.community_id; - let community = Community::read(&mut context.pool(), community_id).await?; + let community = Community::read(&mut context.pool(), community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; let in_reply_to = if let Some(comment_id) = self.parent_comment_id() { - let parent_comment = Comment::read(&mut context.pool(), comment_id).await?; + let parent_comment = Comment::read(&mut context.pool(), comment_id) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; parent_comment.ap_id.into() } else { post.ap_id.into() @@ -132,15 +141,16 @@ impl Object for ApubComment { note: &Note, expected_domain: &Url, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { verify_domains_match(note.id.inner(), expected_domain)?; verify_domains_match(note.attributed_to.inner(), note.id.inner())?; verify_is_public(¬e.to, ¬e.cc)?; let community = note.community(context).await?; check_apub_id_valid_with_strictness(note.id.inner(), community.local, context).await?; - verify_is_remote_object(note.id.inner(), context.settings())?; + verify_is_remote_object(¬e.id, context)?; verify_person_in_community(¬e.attributed_to, &community, context).await?; + let (post, _) = note.get_parents(context).await?; let creator = note.attributed_to.dereference(context).await?; let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &creator, community.id) @@ -157,7 +167,7 @@ impl Object for ApubComment { /// /// If the parent community, post and comment(s) are not known locally, these are also fetched. #[tracing::instrument(skip_all)] - async fn from_json(note: Note, context: &Data) -> Result { + async fn from_json(note: Note, context: &Data) -> LemmyResult { let creator = note.attributed_to.dereference(context).await?; let (post, parent_comment) = note.get_parents(context).await?; @@ -184,7 +194,14 @@ impl Object for ApubComment { language_id, }; let parent_comment_path = parent_comment.map(|t| t.0.path); - let comment = Comment::create(&mut context.pool(), &form, parent_comment_path.as_ref()).await?; + let timestamp: DateTime = note.updated.or(note.published).unwrap_or_else(naive_now); + let comment = Comment::insert_apub( + &mut context.pool(), + Some(timestamp), + &form, + parent_comment_path.as_ref(), + ) + .await?; Ok(comment.into()) } } @@ -204,7 +221,6 @@ pub(crate) mod tests { use assert_json_diff::assert_json_include; use html2md::parse_html; use lemmy_db_schema::source::site::Site; - use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index 7630d80b2..6638cc68d 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -38,7 +38,11 @@ use lemmy_db_schema::{ utils::naive_now, }; use lemmy_db_views_actor::structs::CommunityFollowerView; -use lemmy_utils::{error::LemmyError, spawn_try_task, utils::markdown::markdown_to_html}; +use lemmy_utils::{ + error::{LemmyError, LemmyResult}, + spawn_try_task, + utils::markdown::markdown_to_html, +}; use std::ops::Deref; use url::Url; @@ -72,7 +76,7 @@ impl Object for ApubCommunity { async fn read_from_id( object_id: Url, context: &Data, - ) -> Result, LemmyError> { + ) -> LemmyResult> { Ok( Community::read_from_apub_id(&mut context.pool(), &object_id.into()) .await? @@ -81,7 +85,7 @@ impl Object for ApubCommunity { } #[tracing::instrument(skip_all)] - async fn delete(self, context: &Data) -> Result<(), LemmyError> { + async fn delete(self, context: &Data) -> LemmyResult<()> { let form = CommunityUpdateForm { deleted: Some(true), ..Default::default() @@ -91,7 +95,7 @@ impl Object for ApubCommunity { } #[tracing::instrument(skip_all)] - async fn into_json(self, data: &Data) -> Result { + async fn into_json(self, data: &Data) -> LemmyResult { let community_id = self.id; let langs = CommunityLanguage::read(&mut data.pool(), community_id).await?; let language = LanguageTag::new_multiple(langs, &mut data.pool()).await?; @@ -128,16 +132,13 @@ impl Object for ApubCommunity { group: &Group, expected_domain: &Url, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { group.verify(expected_domain, context).await } /// Converts a `Group` to `Community`, inserts it into the database and updates moderators. #[tracing::instrument(skip_all)] - async fn from_json( - group: Group, - context: &Data, - ) -> Result { + async fn from_json(group: Group, context: &Data) -> LemmyResult { let instance_id = fetch_instance_actor_for_object(&group.id, context).await?; let local_site = LocalSite::read(&mut context.pool()).await.ok(); @@ -175,7 +176,8 @@ impl Object for ApubCommunity { let languages = LanguageTag::to_language_id_multiple(group.language, &mut context.pool()).await?; - let community = Community::create(&mut context.pool(), &form).await?; + let timestamp = group.updated.or(group.published).unwrap_or_else(naive_now); + let community = Community::insert_apub(&mut context.pool(), timestamp, &form).await?; CommunityLanguage::update(&mut context.pool(), languages, community.id).await?; let community: ApubCommunity = community.into(); @@ -234,10 +236,7 @@ impl GetActorType for ApubCommunity { impl ApubCommunity { /// For a given community, returns the inboxes of all followers. #[tracing::instrument(skip_all)] - pub(crate) async fn get_follower_inboxes( - &self, - context: &LemmyContext, - ) -> Result, LemmyError> { + pub(crate) async fn get_follower_inboxes(&self, context: &LemmyContext) -> LemmyResult> { let id = self.id; let local_site_data = local_site_data_cached(&mut context.pool()).await?; @@ -264,7 +263,6 @@ pub(crate) mod tests { }; use activitypub_federation::fetch::collection_id::CollectionId; use lemmy_db_schema::source::site::Site; - use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; diff --git a/crates/apub/src/objects/instance.rs b/crates/apub/src/objects/instance.rs index 6894643d6..145dc63c2 100644 --- a/crates/apub/src/objects/instance.rs +++ b/crates/apub/src/objects/instance.rs @@ -1,3 +1,4 @@ +use super::verify_is_remote_object; use crate::{ activities::GetActorType, check_apub_id_valid_with_strictness, @@ -39,7 +40,7 @@ use lemmy_db_schema::{ utils::naive_now, }; use lemmy_utils::{ - error::LemmyError, + error::{LemmyError, LemmyResult}, utils::{ markdown::markdown_to_html, slurs::{check_slurs, check_slurs_opt}, @@ -76,10 +77,7 @@ impl Object for ApubSite { } #[tracing::instrument(skip_all)] - async fn read_from_id( - object_id: Url, - data: &Data, - ) -> Result, LemmyError> { + async fn read_from_id(object_id: Url, data: &Data) -> LemmyResult> { Ok( Site::read_from_apub_id(&mut data.pool(), &object_id.into()) .await? @@ -87,12 +85,12 @@ impl Object for ApubSite { ) } - async fn delete(self, _data: &Data) -> Result<(), LemmyError> { + async fn delete(self, _data: &Data) -> LemmyResult<()> { unimplemented!() } #[tracing::instrument(skip_all)] - async fn into_json(self, data: &Data) -> Result { + async fn into_json(self, data: &Data) -> LemmyResult { let site_id = self.id; let langs = SiteLanguage::read(&mut data.pool(), site_id).await?; let language = LanguageTag::new_multiple(langs, &mut data.pool()).await?; @@ -124,9 +122,10 @@ impl Object for ApubSite { apub: &Self::Kind, expected_domain: &Url, data: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { check_apub_id_valid_with_strictness(apub.id.inner(), true, data).await?; verify_domains_match(expected_domain, apub.id.inner())?; + verify_is_remote_object(&apub.id, data)?; let local_site_data = local_site_data_cached(&mut data.pool()).await?; let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site); @@ -137,7 +136,7 @@ impl Object for ApubSite { } #[tracing::instrument(skip_all)] - async fn from_json(apub: Self::Kind, context: &Data) -> Result { + async fn from_json(apub: Self::Kind, context: &Data) -> LemmyResult { let domain = apub.id.inner().domain().expect("group id has domain"); let instance = DbInstance::read_or_create(&mut context.pool(), domain.to_string()).await?; @@ -200,7 +199,7 @@ impl GetActorType for ApubSite { pub(in crate::objects) async fn fetch_instance_actor_for_object + Clone>( object_id: &T, context: &Data, -) -> Result { +) -> LemmyResult { let object_id: Url = object_id.clone().into(); let instance_id = Site::instance_actor_id_from_url(object_id); let site = ObjectId::::from(instance_id.clone()) @@ -225,7 +224,6 @@ pub(in crate::objects) async fn fetch_instance_actor_for_object + C pub(crate) mod tests { use super::*; use crate::protocol::tests::file_to_json_object; - use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; diff --git a/crates/apub/src/objects/mod.rs b/crates/apub/src/objects/mod.rs index cabd07e6d..e199ebfad 100644 --- a/crates/apub/src/objects/mod.rs +++ b/crates/apub/src/objects/mod.rs @@ -1,9 +1,16 @@ use crate::protocol::Source; -use activitypub_federation::protocol::values::MediaTypeMarkdownOrHtml; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + protocol::values::MediaTypeMarkdownOrHtml, + traits::Object, +}; use anyhow::anyhow; use html2md::parse_html; -use lemmy_utils::{error::LemmyError, settings::structs::Settings}; -use url::Url; +use lemmy_api_common::context::LemmyContext; +use lemmy_utils::error::LemmyResult; +use serde::Deserialize; +use std::fmt::Debug; pub mod comment; pub mod community; @@ -43,9 +50,15 @@ pub(crate) fn read_from_string_or_source_opt( /// wrapped in Announce. If we simply receive this like any other federated object, overwrite the /// existing, local Post. In particular, it will set the field local = false, so that the object /// can't be fetched from the Activitypub HTTP endpoint anymore (which only serves local objects). -pub(crate) fn verify_is_remote_object(id: &Url, settings: &Settings) -> Result<(), LemmyError> { - let local_domain = settings.get_hostname_without_port()?; - if id.domain() == Some(&local_domain) { +pub(crate) fn verify_is_remote_object( + id: &ObjectId, + context: &Data, +) -> LemmyResult<()> +where + T: Object + Debug + Send + 'static, + for<'de2> ::Kind: Deserialize<'de2>, +{ + if id.is_local(context) { Err(anyhow!("cant accept local object from remote instance").into()) } else { Ok(()) diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index d4456344f..1aac170d7 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -1,3 +1,4 @@ +use super::verify_is_remote_object; use crate::{ activities::GetActorType, check_apub_id_valid_with_strictness, @@ -38,7 +39,7 @@ use lemmy_db_schema::{ utils::naive_now, }; use lemmy_utils::{ - error::LemmyError, + error::{LemmyError, LemmyResult}, utils::{ markdown::markdown_to_html, slurs::{check_slurs, check_slurs_opt}, @@ -77,7 +78,7 @@ impl Object for ApubPerson { async fn read_from_id( object_id: Url, context: &Data, - ) -> Result, LemmyError> { + ) -> LemmyResult> { Ok( DbPerson::read_from_apub_id(&mut context.pool(), &object_id.into()) .await? @@ -86,7 +87,7 @@ impl Object for ApubPerson { } #[tracing::instrument(skip_all)] - async fn delete(self, context: &Data) -> Result<(), LemmyError> { + async fn delete(self, context: &Data) -> LemmyResult<()> { let form = PersonUpdateForm { deleted: Some(true), ..Default::default() @@ -96,7 +97,7 @@ impl Object for ApubPerson { } #[tracing::instrument(skip_all)] - async fn into_json(self, _context: &Data) -> Result { + async fn into_json(self, _context: &Data) -> LemmyResult { let kind = if self.bot_account { UserTypes::Service } else { @@ -130,13 +131,14 @@ impl Object for ApubPerson { person: &Person, expected_domain: &Url, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { let local_site_data = local_site_data_cached(&mut context.pool()).await?; let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site); check_slurs(&person.preferred_username, slur_regex)?; check_slurs_opt(&person.name, slur_regex)?; verify_domains_match(person.id.inner(), expected_domain)?; + verify_is_remote_object(&person.id, context)?; check_apub_id_valid_with_strictness(person.id.inner(), false, context).await?; let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source); @@ -145,10 +147,7 @@ impl Object for ApubPerson { } #[tracing::instrument(skip_all)] - async fn from_json( - person: Person, - context: &Data, - ) -> Result { + async fn from_json(person: Person, context: &Data) -> LemmyResult { let instance_id = fetch_instance_actor_for_object(&person.id, context).await?; let local_site = LocalSite::read(&mut context.pool()).await.ok(); @@ -228,7 +227,6 @@ pub(crate) mod tests { }; use activitypub_federation::fetch::object_id::ObjectId; use lemmy_db_schema::source::site::Site; - use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 0ddc6d17b..c7fa4acb1 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -41,10 +41,11 @@ use lemmy_db_schema::{ post::{Post, PostInsertForm, PostUpdateForm}, }, traits::Crud, + utils::naive_now, }; use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_utils::{ - error::LemmyError, + error::{LemmyError, LemmyErrorType, LemmyResult}, utils::{markdown::markdown_to_html, slurs::check_slurs_opt, validation::check_url_scheme}, }; use std::ops::Deref; @@ -83,7 +84,7 @@ impl Object for ApubPost { async fn read_from_id( object_id: Url, context: &Data, - ) -> Result, LemmyError> { + ) -> LemmyResult> { Ok( Post::read_from_apub_id(&mut context.pool(), object_id) .await? @@ -92,7 +93,7 @@ impl Object for ApubPost { } #[tracing::instrument(skip_all)] - async fn delete(self, context: &Data) -> Result<(), LemmyError> { + async fn delete(self, context: &Data) -> LemmyResult<()> { if !self.deleted { let form = PostUpdateForm { deleted: Some(true), @@ -105,11 +106,15 @@ impl Object for ApubPost { // Turn a Lemmy post into an ActivityPub page that can be sent out over the network. #[tracing::instrument(skip_all)] - async fn into_json(self, context: &Data) -> Result { + async fn into_json(self, context: &Data) -> LemmyResult { let creator_id = self.creator_id; - let creator = Person::read(&mut context.pool(), creator_id).await?; + let creator = Person::read(&mut context.pool(), creator_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; let community_id = self.community_id; - let community = Community::read(&mut context.pool(), community_id).await?; + let community = Community::read(&mut context.pool(), community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; let language = LanguageTag::new_single(self.language_id, &mut context.pool()).await?; let attachment = self @@ -159,12 +164,12 @@ impl Object for ApubPost { page: &Page, expected_domain: &Url, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { // We can't verify the domain in case of mod action, because the mod may be on a different // instance from the post author. if !page.is_mod_action(context).await? { verify_domains_match(page.id.inner(), expected_domain)?; - verify_is_remote_object(page.id.inner(), context.settings())?; + verify_is_remote_object(&page.id, context)?; }; let community = page.community(context).await?; @@ -181,7 +186,7 @@ impl Object for ApubPost { } #[tracing::instrument(skip_all)] - async fn from_json(page: Page, context: &Data) -> Result { + async fn from_json(page: Page, context: &Data) -> LemmyResult { let creator = page.creator()?.dereference(context).await?; let community = page.community(context).await?; if community.posting_restricted_to_mods { @@ -270,7 +275,8 @@ impl Object for ApubPost { .build() }; - let post = Post::create(&mut context.pool(), &form).await?; + let timestamp = page.updated.or(page.published).unwrap_or_else(naive_now); + let post = Post::insert_apub(&mut context.pool(), timestamp, &form).await?; generate_post_link_metadata( post.clone(), @@ -305,7 +311,6 @@ mod tests { protocol::tests::file_to_json_object, }; use lemmy_db_schema::source::site::Site; - use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 647510802..35f2fe418 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -1,3 +1,4 @@ +use super::verify_is_remote_object; use crate::{ check_apub_id_valid_with_strictness, objects::read_from_string_or_source, @@ -23,9 +24,10 @@ use lemmy_db_schema::{ private_message::{PrivateMessage, PrivateMessageInsertForm}, }, traits::Crud, + utils::naive_now, }; use lemmy_utils::{ - error::{LemmyError, LemmyErrorType}, + error::{LemmyError, LemmyErrorType, LemmyResult}, utils::markdown::markdown_to_html, }; use std::ops::Deref; @@ -61,7 +63,7 @@ impl Object for ApubPrivateMessage { async fn read_from_id( object_id: Url, context: &Data, - ) -> Result, LemmyError> { + ) -> LemmyResult> { Ok( PrivateMessage::read_from_apub_id(&mut context.pool(), object_id) .await? @@ -69,18 +71,22 @@ impl Object for ApubPrivateMessage { ) } - async fn delete(self, _context: &Data) -> Result<(), LemmyError> { + async fn delete(self, _context: &Data) -> LemmyResult<()> { // do nothing, because pm can't be fetched over http unimplemented!() } #[tracing::instrument(skip_all)] - async fn into_json(self, context: &Data) -> Result { + async fn into_json(self, context: &Data) -> LemmyResult { let creator_id = self.creator_id; - let creator = Person::read(&mut context.pool(), creator_id).await?; + let creator = Person::read(&mut context.pool(), creator_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; let recipient_id = self.recipient_id; - let recipient = Person::read(&mut context.pool(), recipient_id).await?; + let recipient = Person::read(&mut context.pool(), recipient_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; let note = ChatMessage { r#type: ChatMessageType::ChatMessage, @@ -101,9 +107,10 @@ impl Object for ApubPrivateMessage { note: &ChatMessage, expected_domain: &Url, context: &Data, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { verify_domains_match(note.id.inner(), expected_domain)?; verify_domains_match(note.attributed_to.inner(), note.id.inner())?; + verify_is_remote_object(¬e.id, context)?; check_apub_id_valid_with_strictness(note.id.inner(), false, context).await?; let person = note.attributed_to.dereference(context).await?; @@ -120,7 +127,7 @@ impl Object for ApubPrivateMessage { async fn from_json( note: ChatMessage, context: &Data, - ) -> Result { + ) -> LemmyResult { let creator = note.attributed_to.dereference(context).await?; let recipient = note.to[0].dereference(context).await?; check_person_block(creator.id, recipient.id, &mut context.pool()).await?; @@ -142,7 +149,8 @@ impl Object for ApubPrivateMessage { ap_id: Some(note.id.into()), local: Some(false), }; - let pm = PrivateMessage::create(&mut context.pool(), &form).await?; + let timestamp = note.updated.or(note.published).unwrap_or_else(naive_now); + let pm = PrivateMessage::insert_apub(&mut context.pool(), timestamp, &form).await?; Ok(pm.into()) } } @@ -159,7 +167,6 @@ mod tests { }; use assert_json_diff::assert_json_include; use lemmy_db_schema::source::site::Site; - use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; diff --git a/crates/apub/src/protocol/activities/block/block_user.rs b/crates/apub/src/protocol/activities/block/block_user.rs index c5cb4306c..c1a4c64c7 100644 --- a/crates/apub/src/protocol/activities/block/block_user.rs +++ b/crates/apub/src/protocol/activities/block/block_user.rs @@ -12,7 +12,7 @@ use activitypub_federation::{ use anyhow::anyhow; use chrono::{DateTime, Utc}; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use url::Url; @@ -45,7 +45,7 @@ pub struct BlockUser { #[async_trait::async_trait] impl InCommunity for BlockUser { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let target = self.target.dereference(context).await?; let community = match target { SiteOrCommunity::Community(c) => c, diff --git a/crates/apub/src/protocol/activities/block/undo_block_user.rs b/crates/apub/src/protocol/activities/block/undo_block_user.rs index 758d3fd4b..491ec7ed1 100644 --- a/crates/apub/src/protocol/activities/block/undo_block_user.rs +++ b/crates/apub/src/protocol/activities/block/undo_block_user.rs @@ -10,7 +10,7 @@ use activitypub_federation::{ protocol::helpers::deserialize_one_or_many, }; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use url::Url; @@ -33,7 +33,7 @@ pub struct UndoBlockUser { #[async_trait::async_trait] impl InCommunity for UndoBlockUser { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let community = self.object.community(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; diff --git a/crates/apub/src/protocol/activities/community/collection_add.rs b/crates/apub/src/protocol/activities/community/collection_add.rs index edf67740a..0e2ab75a6 100644 --- a/crates/apub/src/protocol/activities/community/collection_add.rs +++ b/crates/apub/src/protocol/activities/community/collection_add.rs @@ -11,7 +11,7 @@ use activitypub_federation::{ }; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::community::Community; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use serde::{Deserialize, Serialize}; use url::Url; @@ -33,9 +33,11 @@ pub struct CollectionAdd { #[async_trait::async_trait] impl InCommunity for CollectionAdd { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let (community, _) = - Community::get_by_collection_url(&mut context.pool(), &self.clone().target.into()).await?; + Community::get_by_collection_url(&mut context.pool(), &self.clone().target.into()) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/activities/community/collection_remove.rs b/crates/apub/src/protocol/activities/community/collection_remove.rs index 960951732..51c4761ba 100644 --- a/crates/apub/src/protocol/activities/community/collection_remove.rs +++ b/crates/apub/src/protocol/activities/community/collection_remove.rs @@ -11,7 +11,7 @@ use activitypub_federation::{ }; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::community::Community; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use serde::{Deserialize, Serialize}; use url::Url; @@ -33,9 +33,11 @@ pub struct CollectionRemove { #[async_trait::async_trait] impl InCommunity for CollectionRemove { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let (community, _) = - Community::get_by_collection_url(&mut context.pool(), &self.clone().target.into()).await?; + Community::get_by_collection_url(&mut context.pool(), &self.clone().target.into()) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/activities/community/lock_page.rs b/crates/apub/src/protocol/activities/community/lock_page.rs index 33b108531..c60b86cf8 100644 --- a/crates/apub/src/protocol/activities/community/lock_page.rs +++ b/crates/apub/src/protocol/activities/community/lock_page.rs @@ -11,7 +11,7 @@ use activitypub_federation::{ }; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{source::community::Community, traits::Crud}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use serde::{Deserialize, Serialize}; use strum_macros::Display; use url::Url; @@ -53,9 +53,11 @@ pub struct UndoLockPage { #[async_trait::async_trait] impl InCommunity for LockPage { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let post = self.object.dereference(context).await?; - let community = Community::read(&mut context.pool(), post.community_id).await?; + let community = Community::read(&mut context.pool(), post.community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } @@ -65,7 +67,7 @@ impl InCommunity for LockPage { #[async_trait::async_trait] impl InCommunity for UndoLockPage { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let community = self.object.community(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; diff --git a/crates/apub/src/protocol/activities/community/report.rs b/crates/apub/src/protocol/activities/community/report.rs index adcddfdfc..7698cde50 100644 --- a/crates/apub/src/protocol/activities/community/report.rs +++ b/crates/apub/src/protocol/activities/community/report.rs @@ -11,7 +11,7 @@ use activitypub_federation::{ protocol::helpers::deserialize_one, }; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use serde::{Deserialize, Serialize}; use url::Url; @@ -71,7 +71,7 @@ impl ReportObject { #[async_trait::async_trait] impl InCommunity for Report { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let community = self.to[0].dereference(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; diff --git a/crates/apub/src/protocol/activities/community/update.rs b/crates/apub/src/protocol/activities/community/update.rs index 49ec1f5d6..268f05073 100644 --- a/crates/apub/src/protocol/activities/community/update.rs +++ b/crates/apub/src/protocol/activities/community/update.rs @@ -10,7 +10,7 @@ use activitypub_federation::{ protocol::helpers::deserialize_one_or_many, }; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use serde::{Deserialize, Serialize}; use url::Url; @@ -34,7 +34,7 @@ pub struct UpdateCommunity { #[async_trait::async_trait] impl InCommunity for UpdateCommunity { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let community: ApubCommunity = self.object.id.clone().dereference(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; diff --git a/crates/apub/src/protocol/activities/create_or_update/note.rs b/crates/apub/src/protocol/activities/create_or_update/note.rs index 6fabc0aaa..43ffeb291 100644 --- a/crates/apub/src/protocol/activities/create_or_update/note.rs +++ b/crates/apub/src/protocol/activities/create_or_update/note.rs @@ -11,7 +11,7 @@ use activitypub_federation::{ }; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{source::community::Community, traits::Crud}; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use serde::{Deserialize, Serialize}; use url::Url; @@ -34,9 +34,11 @@ pub struct CreateOrUpdateNote { #[async_trait::async_trait] impl InCommunity for CreateOrUpdateNote { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let post = self.object.get_parents(context).await?.0; - let community = Community::read(&mut context.pool(), post.community_id).await?; + let community = Community::read(&mut context.pool(), post.community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/activities/create_or_update/page.rs b/crates/apub/src/protocol/activities/create_or_update/page.rs index ec64c31a0..21052a9ef 100644 --- a/crates/apub/src/protocol/activities/create_or_update/page.rs +++ b/crates/apub/src/protocol/activities/create_or_update/page.rs @@ -9,7 +9,7 @@ use activitypub_federation::{ protocol::helpers::deserialize_one_or_many, }; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use serde::{Deserialize, Serialize}; use url::Url; @@ -30,7 +30,7 @@ pub struct CreateOrUpdatePage { #[async_trait::async_trait] impl InCommunity for CreateOrUpdatePage { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let community = self.object.community(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; diff --git a/crates/apub/src/protocol/activities/deletion/delete.rs b/crates/apub/src/protocol/activities/deletion/delete.rs index aefcf3f5f..3b9aad079 100644 --- a/crates/apub/src/protocol/activities/deletion/delete.rs +++ b/crates/apub/src/protocol/activities/deletion/delete.rs @@ -15,7 +15,7 @@ use lemmy_db_schema::{ source::{community::Community, post::Post}, traits::Crud, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use url::Url; @@ -47,11 +47,13 @@ pub struct Delete { #[async_trait::async_trait] impl InCommunity for Delete { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let community_id = match DeletableObjects::read_from_db(self.object.id(), context).await? { DeletableObjects::Community(c) => c.id, DeletableObjects::Comment(c) => { - let post = Post::read(&mut context.pool(), c.post_id).await?; + let post = Post::read(&mut context.pool(), c.post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; post.community_id } DeletableObjects::Post(p) => p.community_id, @@ -60,7 +62,9 @@ impl InCommunity for Delete { return Err(anyhow!("Private message is not part of community").into()) } }; - let community = Community::read(&mut context.pool(), community_id).await?; + let community = Community::read(&mut context.pool(), community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/activities/deletion/undo_delete.rs b/crates/apub/src/protocol/activities/deletion/undo_delete.rs index 35d9951b1..508b90113 100644 --- a/crates/apub/src/protocol/activities/deletion/undo_delete.rs +++ b/crates/apub/src/protocol/activities/deletion/undo_delete.rs @@ -10,7 +10,7 @@ use activitypub_federation::{ protocol::helpers::deserialize_one_or_many, }; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use url::Url; @@ -35,7 +35,7 @@ pub struct UndoDelete { #[async_trait::async_trait] impl InCommunity for UndoDelete { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let community = self.object.community(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; diff --git a/crates/apub/src/protocol/activities/voting/undo_vote.rs b/crates/apub/src/protocol/activities/voting/undo_vote.rs index 746ae68df..e9ccbd593 100644 --- a/crates/apub/src/protocol/activities/voting/undo_vote.rs +++ b/crates/apub/src/protocol/activities/voting/undo_vote.rs @@ -5,7 +5,7 @@ use crate::{ }; use activitypub_federation::{config::Data, fetch::object_id::ObjectId, kinds::activity::UndoType}; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use serde::{Deserialize, Serialize}; use url::Url; @@ -22,7 +22,7 @@ pub struct UndoVote { #[async_trait::async_trait] impl InCommunity for UndoVote { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let community = self.object.community(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; diff --git a/crates/apub/src/protocol/activities/voting/vote.rs b/crates/apub/src/protocol/activities/voting/vote.rs index 013c15bfd..b632333c7 100644 --- a/crates/apub/src/protocol/activities/voting/vote.rs +++ b/crates/apub/src/protocol/activities/voting/vote.rs @@ -6,7 +6,7 @@ use crate::{ }; use activitypub_federation::{config::Data, fetch::object_id::ObjectId}; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use serde::{Deserialize, Serialize}; use strum_macros::Display; use url::Url; @@ -51,7 +51,7 @@ impl From<&VoteType> for i16 { #[async_trait::async_trait] impl InCommunity for Vote { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let community = self .object .dereference(context) diff --git a/crates/apub/src/protocol/collections/empty_outbox.rs b/crates/apub/src/protocol/collections/empty_outbox.rs index 3801c04e9..1e7a4c6a8 100644 --- a/crates/apub/src/protocol/collections/empty_outbox.rs +++ b/crates/apub/src/protocol/collections/empty_outbox.rs @@ -1,5 +1,5 @@ use activitypub_federation::kinds::collection::OrderedCollectionType; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use serde::{Deserialize, Serialize}; use url::Url; @@ -14,7 +14,7 @@ pub(crate) struct EmptyOutbox { } impl EmptyOutbox { - pub(crate) fn new(outbox_id: Url) -> Result { + pub(crate) fn new(outbox_id: Url) -> LemmyResult { Ok(EmptyOutbox { r#type: OrderedCollectionType::OrderedCollection, id: outbox_id, diff --git a/crates/apub/src/protocol/mod.rs b/crates/apub/src/protocol/mod.rs index d538ee531..a4774ac1d 100644 --- a/crates/apub/src/protocol/mod.rs +++ b/crates/apub/src/protocol/mod.rs @@ -7,7 +7,7 @@ use activitypub_federation::{ }; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::newtypes::DbUrl; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::collections::HashMap; use url::Url; @@ -71,7 +71,7 @@ impl IdOrNestedObject { IdOrNestedObject::NestedObject(n) => n.object_id(), } } - pub(crate) async fn object(self, context: &Data) -> Result { + pub(crate) async fn object(self, context: &Data) -> LemmyResult { match self { // TODO: move IdOrNestedObject struct to library and make fetch_object_http private IdOrNestedObject::Id(i) => Ok(fetch_object_http(&i, context).await?.object), @@ -83,25 +83,25 @@ impl IdOrNestedObject { #[async_trait::async_trait] pub trait InCommunity { // TODO: after we use audience field and remove backwards compat, it should be possible to change - // this to simply `fn community(&self) -> Result, LemmyError>` - async fn community(&self, context: &Data) -> Result; + // this to simply `fn community(&self) -> LemmyResult>` + async fn community(&self, context: &Data) -> LemmyResult; } #[cfg(test)] pub(crate) mod tests { use activitypub_federation::protocol::context::WithContext; use assert_json_diff::assert_json_include; - use lemmy_utils::error::LemmyError; + use lemmy_utils::error::LemmyResult; use serde::{de::DeserializeOwned, Serialize}; use std::{collections::HashMap, fs::File, io::BufReader}; - pub(crate) fn file_to_json_object(path: &str) -> Result { + pub(crate) fn file_to_json_object(path: &str) -> LemmyResult { let file = File::open(path)?; let reader = BufReader::new(file); Ok(serde_json::from_reader(reader)?) } - pub(crate) fn test_json(path: &str) -> Result, LemmyError> { + pub(crate) fn test_json(path: &str) -> LemmyResult> { file_to_json_object::>(path) } @@ -109,7 +109,7 @@ pub(crate) mod tests { /// Ensures that there are no breaking changes in sent data. pub(crate) fn test_parse_lemmy_item( path: &str, - ) -> Result { + ) -> LemmyResult { // parse file as T let parsed = file_to_json_object::(path)?; diff --git a/crates/apub/src/protocol/objects/group.rs b/crates/apub/src/protocol/objects/group.rs index 2a625a5a7..2ed884484 100644 --- a/crates/apub/src/protocol/objects/group.rs +++ b/crates/apub/src/protocol/objects/group.rs @@ -7,7 +7,7 @@ use crate::{ community_outbox::ApubCommunityOutbox, }, local_site_data_cached, - objects::{community::ApubCommunity, read_from_string_or_source_opt}, + objects::{community::ApubCommunity, read_from_string_or_source_opt, verify_is_remote_object}, protocol::{ objects::{Endpoints, LanguageTag}, ImageObject, @@ -15,6 +15,7 @@ use crate::{ }, }; use activitypub_federation::{ + config::Data, fetch::{collection_id::CollectionId, object_id::ObjectId}, kinds::actor::GroupType, protocol::{ @@ -26,7 +27,7 @@ use activitypub_federation::{ use chrono::{DateTime, Utc}; use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex}; use lemmy_utils::{ - error::LemmyError, + error::LemmyResult, utils::slurs::{check_slurs, check_slurs_opt}, }; use serde::{Deserialize, Serialize}; @@ -75,10 +76,11 @@ impl Group { pub(crate) async fn verify( &self, expected_domain: &Url, - context: &LemmyContext, - ) -> Result<(), LemmyError> { + context: &Data, + ) -> LemmyResult<()> { check_apub_id_valid_with_strictness(self.id.inner(), true, context).await?; verify_domains_match(expected_domain, self.id.inner())?; + verify_is_remote_object(&self.id, context)?; let local_site_data = local_site_data_cached(&mut context.pool()).await?; let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site); diff --git a/crates/apub/src/protocol/objects/mod.rs b/crates/apub/src/protocol/objects/mod.rs index 03229f739..efda3d7ed 100644 --- a/crates/apub/src/protocol/objects/mod.rs +++ b/crates/apub/src/protocol/objects/mod.rs @@ -4,7 +4,7 @@ use lemmy_db_schema::{ source::language::Language, utils::DbPool, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use serde::{Deserialize, Serialize}; use url::Url; @@ -34,7 +34,7 @@ impl LanguageTag { pub(crate) async fn new_single( lang: LanguageId, pool: &mut DbPool<'_>, - ) -> Result, LemmyError> { + ) -> LemmyResult> { let lang = Language::read_from_id(pool, lang).await?; // undetermined @@ -51,7 +51,7 @@ impl LanguageTag { pub(crate) async fn new_multiple( lang_ids: Vec, pool: &mut DbPool<'_>, - ) -> Result, LemmyError> { + ) -> LemmyResult> { let mut langs = Vec::::new(); for l in lang_ids { @@ -71,7 +71,7 @@ impl LanguageTag { pub(crate) async fn to_language_id_single( lang: Option, pool: &mut DbPool<'_>, - ) -> Result, LemmyError> { + ) -> LemmyResult> { let identifier = lang.map(|l| l.identifier); let language = Language::read_id_from_code(pool, identifier.as_deref()).await?; @@ -81,7 +81,7 @@ impl LanguageTag { pub(crate) async fn to_language_id_multiple( langs: Vec, pool: &mut DbPool<'_>, - ) -> Result, LemmyError> { + ) -> LemmyResult> { let mut language_ids = Vec::new(); for l in langs { diff --git a/crates/apub/src/protocol/objects/note.rs b/crates/apub/src/protocol/objects/note.rs index 259a8fcfa..b0ae00037 100644 --- a/crates/apub/src/protocol/objects/note.rs +++ b/crates/apub/src/protocol/objects/note.rs @@ -20,7 +20,7 @@ use lemmy_db_schema::{ source::{community::Community, post::Post}, traits::Crud, }; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use std::ops::Deref; @@ -57,14 +57,16 @@ impl Note { pub(crate) async fn get_parents( &self, context: &Data, - ) -> Result<(ApubPost, Option), LemmyError> { + ) -> LemmyResult<(ApubPost, Option)> { // Fetch parent comment chain in a box, otherwise it can cause a stack overflow. let parent = Box::pin(self.in_reply_to.dereference(context).await?); match parent.deref() { PostOrComment::Post(p) => Ok((p.clone(), None)), PostOrComment::Comment(c) => { let post_id = c.post_id; - let post = Post::read(&mut context.pool(), post_id).await?; + let post = Post::read(&mut context.pool(), post_id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; Ok((post.into(), Some(c.clone()))) } } @@ -73,9 +75,11 @@ impl Note { #[async_trait::async_trait] impl InCommunity for Note { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let (post, _) = self.get_parents(context).await?; - let community = Community::read(&mut context.pool(), post.community_id).await?; + let community = Community::read(&mut context.pool(), post.community_id) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/objects/page.rs b/crates/apub/src/protocol/objects/page.rs index 170778297..acfd8f5fd 100644 --- a/crates/apub/src/protocol/objects/page.rs +++ b/crates/apub/src/protocol/objects/page.rs @@ -20,7 +20,7 @@ use activitypub_federation::{ use chrono::{DateTime, Utc}; use itertools::Itertools; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use serde::{de::Error, Deserialize, Deserializer, Serialize}; use serde_with::skip_serializing_none; use url::Url; @@ -160,10 +160,7 @@ impl Page { /// it is a mod action and needs to be verified as such. /// /// Locked needs to be false on a newly created post (verified in [[CreatePost]]. - pub(crate) async fn is_mod_action( - &self, - context: &Data, - ) -> Result { + pub(crate) async fn is_mod_action(&self, context: &Data) -> LemmyResult { let old_post = self.id.clone().dereference_local(context).await; Ok(Page::is_locked_changed(&old_post, &self.comments_enabled)) } @@ -181,7 +178,7 @@ impl Page { false } - pub(crate) fn creator(&self) -> Result, LemmyError> { + pub(crate) fn creator(&self) -> LemmyResult> { match &self.attributed_to { AttributedTo::Lemmy(l) => Ok(l.clone()), AttributedTo::Peertube(p) => p @@ -224,10 +221,10 @@ impl ActivityHandler for Page { fn actor(&self) -> &Url { unimplemented!() } - async fn verify(&self, data: &Data) -> Result<(), LemmyError> { + async fn verify(&self, data: &Data) -> LemmyResult<()> { ApubPost::verify(self, self.id.inner(), data).await } - async fn receive(self, data: &Data) -> Result<(), LemmyError> { + async fn receive(self, data: &Data) -> LemmyResult<()> { ApubPost::from_json(self, data).await?; Ok(()) } @@ -235,7 +232,7 @@ impl ActivityHandler for Page { #[async_trait::async_trait] impl InCommunity for Page { - async fn community(&self, context: &Data) -> Result { + async fn community(&self, context: &Data) -> LemmyResult { let community = match &self.attributed_to { AttributedTo::Lemmy(_) => { let mut iter = self.to.iter().merge(self.cc.iter()); diff --git a/crates/db_schema/src/aggregates/comment_aggregates.rs b/crates/db_schema/src/aggregates/comment_aggregates.rs index 2120c7f38..915d17b1d 100644 --- a/crates/db_schema/src/aggregates/comment_aggregates.rs +++ b/crates/db_schema/src/aggregates/comment_aggregates.rs @@ -1,5 +1,6 @@ use crate::{ aggregates::structs::CommentAggregates, + diesel::OptionalExtension, newtypes::CommentId, schema::comment_aggregates, utils::{functions::hot_rank, get_conn, DbPool}, @@ -8,12 +9,13 @@ use diesel::{result::Error, ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; impl CommentAggregates { - pub async fn read(pool: &mut DbPool<'_>, comment_id: CommentId) -> Result { + pub async fn read(pool: &mut DbPool<'_>, comment_id: CommentId) -> Result, Error> { let conn = &mut get_conn(pool).await?; comment_aggregates::table .find(comment_id) - .first::(conn) + .first(conn) .await + .optional() } pub async fn update_hot_rank( @@ -125,6 +127,7 @@ mod tests { let comment_aggs_before_delete = CommentAggregates::read(pool, inserted_comment.id) .await + .unwrap() .unwrap(); assert_eq!(1, comment_aggs_before_delete.score); @@ -143,6 +146,7 @@ mod tests { let comment_aggs_after_dislike = CommentAggregates::read(pool, inserted_comment.id) .await + .unwrap() .unwrap(); assert_eq!(0, comment_aggs_after_dislike.score); @@ -155,6 +159,7 @@ mod tests { .unwrap(); let after_like_remove = CommentAggregates::read(pool, inserted_comment.id) .await + .unwrap() .unwrap(); assert_eq!(-1, after_like_remove.score); assert_eq!(0, after_like_remove.upvotes); @@ -164,8 +169,10 @@ mod tests { Post::delete(pool, inserted_post.id).await.unwrap(); // Should be none found, since the post was deleted - let after_delete = CommentAggregates::read(pool, inserted_comment.id).await; - assert!(after_delete.is_err()); + let after_delete = CommentAggregates::read(pool, inserted_comment.id) + .await + .unwrap(); + assert!(after_delete.is_none()); // This should delete all the associated rows, and fire triggers Person::delete(pool, another_inserted_person.id) diff --git a/crates/db_schema/src/aggregates/community_aggregates.rs b/crates/db_schema/src/aggregates/community_aggregates.rs index f1f54663b..0cf63809d 100644 --- a/crates/db_schema/src/aggregates/community_aggregates.rs +++ b/crates/db_schema/src/aggregates/community_aggregates.rs @@ -1,5 +1,6 @@ use crate::{ aggregates::structs::CommunityAggregates, + diesel::OptionalExtension, newtypes::CommunityId, schema::{community_aggregates, community_aggregates::subscribers}, utils::{get_conn, DbPool}, @@ -8,12 +9,16 @@ use diesel::{result::Error, ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; impl CommunityAggregates { - pub async fn read(pool: &mut DbPool<'_>, for_community_id: CommunityId) -> Result { + pub async fn read( + pool: &mut DbPool<'_>, + for_community_id: CommunityId, + ) -> Result, Error> { let conn = &mut get_conn(pool).await?; community_aggregates::table .find(for_community_id) - .first::(conn) + .first(conn) .await + .optional() } pub async fn update_federated_followers( @@ -25,7 +30,7 @@ impl CommunityAggregates { let new_subscribers: i64 = new_subscribers.into(); diesel::update(community_aggregates::table.find(for_community_id)) .set(subscribers.eq(new_subscribers)) - .get_result::(conn) + .get_result(conn) .await } } @@ -153,6 +158,7 @@ mod tests { let community_aggregates_before_delete = CommunityAggregates::read(pool, inserted_community.id) .await + .unwrap() .unwrap(); assert_eq!(2, community_aggregates_before_delete.subscribers); @@ -163,6 +169,7 @@ mod tests { // Test the other community let another_community_aggs = CommunityAggregates::read(pool, another_inserted_community.id) .await + .unwrap() .unwrap(); assert_eq!(1, another_community_aggs.subscribers); assert_eq!(1, another_community_aggs.subscribers_local); @@ -175,6 +182,7 @@ mod tests { .unwrap(); let after_unfollow = CommunityAggregates::read(pool, inserted_community.id) .await + .unwrap() .unwrap(); assert_eq!(1, after_unfollow.subscribers); assert_eq!(1, after_unfollow.subscribers_local); @@ -185,6 +193,7 @@ mod tests { .unwrap(); let after_follow_again = CommunityAggregates::read(pool, inserted_community.id) .await + .unwrap() .unwrap(); assert_eq!(2, after_follow_again.subscribers); assert_eq!(2, after_follow_again.subscribers_local); @@ -193,6 +202,7 @@ mod tests { Post::delete(pool, inserted_post.id).await.unwrap(); let after_parent_post_delete = CommunityAggregates::read(pool, inserted_community.id) .await + .unwrap() .unwrap(); assert_eq!(0, after_parent_post_delete.comments); assert_eq!(0, after_parent_post_delete.posts); @@ -203,6 +213,7 @@ mod tests { .unwrap(); let after_person_delete = CommunityAggregates::read(pool, inserted_community.id) .await + .unwrap() .unwrap(); assert_eq!(1, after_person_delete.subscribers); assert_eq!(1, after_person_delete.subscribers_local); @@ -223,7 +234,9 @@ mod tests { assert_eq!(1, another_community_num_deleted); // Should be none found, since the creator was deleted - let after_delete = CommunityAggregates::read(pool, inserted_community.id).await; - assert!(after_delete.is_err()); + let after_delete = CommunityAggregates::read(pool, inserted_community.id) + .await + .unwrap(); + assert!(after_delete.is_none()); } } diff --git a/crates/db_schema/src/aggregates/person_aggregates.rs b/crates/db_schema/src/aggregates/person_aggregates.rs index 520b45225..03295173f 100644 --- a/crates/db_schema/src/aggregates/person_aggregates.rs +++ b/crates/db_schema/src/aggregates/person_aggregates.rs @@ -1,3 +1,4 @@ +pub(crate) use crate::diesel::OptionalExtension; use crate::{ aggregates::structs::PersonAggregates, newtypes::PersonId, @@ -8,12 +9,13 @@ use diesel::{result::Error, QueryDsl}; use diesel_async::RunQueryDsl; impl PersonAggregates { - pub async fn read(pool: &mut DbPool<'_>, person_id: PersonId) -> Result { + pub async fn read(pool: &mut DbPool<'_>, person_id: PersonId) -> Result, Error> { let conn = &mut get_conn(pool).await?; person_aggregates::table .find(person_id) - .first::(conn) + .first(conn) .await + .optional() } } @@ -127,6 +129,7 @@ mod tests { let person_aggregates_before_delete = PersonAggregates::read(pool, inserted_person.id) .await + .unwrap() .unwrap(); assert_eq!(1, person_aggregates_before_delete.post_count); @@ -140,6 +143,7 @@ mod tests { .unwrap(); let after_post_like_remove = PersonAggregates::read(pool, inserted_person.id) .await + .unwrap() .unwrap(); assert_eq!(0, after_post_like_remove.post_score); @@ -166,6 +170,7 @@ mod tests { let after_parent_comment_removed = PersonAggregates::read(pool, inserted_person.id) .await + .unwrap() .unwrap(); assert_eq!(0, after_parent_comment_removed.comment_count); // TODO: fix person aggregate comment score calculation @@ -178,6 +183,7 @@ mod tests { .unwrap(); let after_parent_comment_delete = PersonAggregates::read(pool, inserted_person.id) .await + .unwrap() .unwrap(); assert_eq!(0, after_parent_comment_delete.comment_count); // TODO: fix person aggregate comment score calculation @@ -193,6 +199,7 @@ mod tests { CommentLike::like(pool, &comment_like).await.unwrap(); let after_comment_add = PersonAggregates::read(pool, inserted_person.id) .await + .unwrap() .unwrap(); assert_eq!(2, after_comment_add.comment_count); // TODO: fix person aggregate comment score calculation @@ -201,6 +208,7 @@ mod tests { Post::delete(pool, inserted_post.id).await.unwrap(); let after_post_delete = PersonAggregates::read(pool, inserted_person.id) .await + .unwrap() .unwrap(); // TODO: fix person aggregate comment score calculation // assert_eq!(0, after_post_delete.comment_score); @@ -222,8 +230,10 @@ mod tests { assert_eq!(1, community_num_deleted); // Should be none found - let after_delete = PersonAggregates::read(pool, inserted_person.id).await; - assert!(after_delete.is_err()); + let after_delete = PersonAggregates::read(pool, inserted_person.id) + .await + .unwrap(); + assert!(after_delete.is_none()); Instance::delete(pool, inserted_instance.id).await.unwrap(); } diff --git a/crates/db_schema/src/aggregates/person_post_aggregates.rs b/crates/db_schema/src/aggregates/person_post_aggregates.rs index 7657dae9e..f6e108ee9 100644 --- a/crates/db_schema/src/aggregates/person_post_aggregates.rs +++ b/crates/db_schema/src/aggregates/person_post_aggregates.rs @@ -1,5 +1,6 @@ use crate::{ aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm}, + diesel::OptionalExtension, newtypes::{PersonId, PostId}, schema::person_post_aggregates::dsl::{person_id, person_post_aggregates, post_id}, utils::{get_conn, DbPool}, @@ -25,11 +26,12 @@ impl PersonPostAggregates { pool: &mut DbPool<'_>, person_id_: PersonId, post_id_: PostId, - ) -> Result { + ) -> Result, Error> { let conn = &mut get_conn(pool).await?; person_post_aggregates .find((person_id_, post_id_)) - .first::(conn) + .first(conn) .await + .optional() } } diff --git a/crates/db_schema/src/aggregates/post_aggregates.rs b/crates/db_schema/src/aggregates/post_aggregates.rs index 7f95dc05a..cb8227795 100644 --- a/crates/db_schema/src/aggregates/post_aggregates.rs +++ b/crates/db_schema/src/aggregates/post_aggregates.rs @@ -1,5 +1,6 @@ use crate::{ aggregates::structs::PostAggregates, + diesel::OptionalExtension, newtypes::PostId, schema::{community_aggregates, post, post_aggregates}, utils::{ @@ -12,12 +13,13 @@ use diesel::{result::Error, ExpressionMethods, JoinOnDsl, QueryDsl}; use diesel_async::RunQueryDsl; impl PostAggregates { - pub async fn read(pool: &mut DbPool<'_>, post_id: PostId) -> Result { + pub async fn read(pool: &mut DbPool<'_>, post_id: PostId) -> Result, Error> { let conn = &mut get_conn(pool).await?; post_aggregates::table .find(post_id) - .first::(conn) + .first(conn) .await + .optional() } pub async fn update_ranks(pool: &mut DbPool<'_>, post_id: PostId) -> Result { @@ -141,7 +143,10 @@ mod tests { PostLike::like(pool, &post_like).await.unwrap(); - let post_aggs_before_delete = PostAggregates::read(pool, inserted_post.id).await.unwrap(); + let post_aggs_before_delete = PostAggregates::read(pool, inserted_post.id) + .await + .unwrap() + .unwrap(); assert_eq!(2, post_aggs_before_delete.comments); assert_eq!(1, post_aggs_before_delete.score); @@ -157,7 +162,10 @@ mod tests { PostLike::like(pool, &post_dislike).await.unwrap(); - let post_aggs_after_dislike = PostAggregates::read(pool, inserted_post.id).await.unwrap(); + let post_aggs_after_dislike = PostAggregates::read(pool, inserted_post.id) + .await + .unwrap() + .unwrap(); assert_eq!(2, post_aggs_after_dislike.comments); assert_eq!(0, post_aggs_after_dislike.score); @@ -169,7 +177,10 @@ mod tests { Comment::delete(pool, inserted_child_comment.id) .await .unwrap(); - let after_comment_delete = PostAggregates::read(pool, inserted_post.id).await.unwrap(); + let after_comment_delete = PostAggregates::read(pool, inserted_post.id) + .await + .unwrap() + .unwrap(); assert_eq!(0, after_comment_delete.comments); assert_eq!(0, after_comment_delete.score); assert_eq!(1, after_comment_delete.upvotes); @@ -179,7 +190,10 @@ mod tests { PostLike::remove(pool, inserted_person.id, inserted_post.id) .await .unwrap(); - let after_like_remove = PostAggregates::read(pool, inserted_post.id).await.unwrap(); + let after_like_remove = PostAggregates::read(pool, inserted_post.id) + .await + .unwrap() + .unwrap(); assert_eq!(0, after_like_remove.comments); assert_eq!(-1, after_like_remove.score); assert_eq!(0, after_like_remove.upvotes); @@ -199,8 +213,8 @@ mod tests { assert_eq!(1, community_num_deleted); // Should be none found, since the creator was deleted - let after_delete = PostAggregates::read(pool, inserted_post.id).await; - assert!(after_delete.is_err()); + let after_delete = PostAggregates::read(pool, inserted_post.id).await.unwrap(); + assert!(after_delete.is_none()); Instance::delete(pool, inserted_instance.id).await.unwrap(); } @@ -248,7 +262,10 @@ mod tests { let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap(); - let post_aggregates_before = PostAggregates::read(pool, inserted_post.id).await.unwrap(); + let post_aggregates_before = PostAggregates::read(pool, inserted_post.id) + .await + .unwrap() + .unwrap(); assert_eq!(1, post_aggregates_before.comments); Comment::update( @@ -262,7 +279,10 @@ mod tests { .await .unwrap(); - let post_aggregates_after_remove = PostAggregates::read(pool, inserted_post.id).await.unwrap(); + let post_aggregates_after_remove = PostAggregates::read(pool, inserted_post.id) + .await + .unwrap() + .unwrap(); assert_eq!(0, post_aggregates_after_remove.comments); Comment::update( @@ -287,7 +307,10 @@ mod tests { .await .unwrap(); - let post_aggregates_after_delete = PostAggregates::read(pool, inserted_post.id).await.unwrap(); + let post_aggregates_after_delete = PostAggregates::read(pool, inserted_post.id) + .await + .unwrap() + .unwrap(); assert_eq!(0, post_aggregates_after_delete.comments); Comment::update( @@ -301,8 +324,10 @@ mod tests { .await .unwrap(); - let post_aggregates_after_delete_remove = - PostAggregates::read(pool, inserted_post.id).await.unwrap(); + let post_aggregates_after_delete_remove = PostAggregates::read(pool, inserted_post.id) + .await + .unwrap() + .unwrap(); assert_eq!(0, post_aggregates_after_delete_remove.comments); Comment::delete(pool, inserted_comment.id).await.unwrap(); diff --git a/crates/db_schema/src/aggregates/site_aggregates.rs b/crates/db_schema/src/aggregates/site_aggregates.rs index b179ff7c7..268a37aac 100644 --- a/crates/db_schema/src/aggregates/site_aggregates.rs +++ b/crates/db_schema/src/aggregates/site_aggregates.rs @@ -1,5 +1,6 @@ use crate::{ aggregates::structs::SiteAggregates, + diesel::OptionalExtension, schema::site_aggregates, utils::{get_conn, DbPool}, }; @@ -7,9 +8,9 @@ use diesel::result::Error; use diesel_async::RunQueryDsl; impl SiteAggregates { - pub async fn read(pool: &mut DbPool<'_>) -> Result { + pub async fn read(pool: &mut DbPool<'_>) -> Result, Error> { let conn = &mut get_conn(pool).await?; - site_aggregates::table.first::(conn).await + site_aggregates::table.first(conn).await.optional() } } @@ -111,7 +112,7 @@ mod tests { .await .unwrap(); - let site_aggregates_before_delete = SiteAggregates::read(pool).await.unwrap(); + let site_aggregates_before_delete = SiteAggregates::read(pool).await.unwrap().unwrap(); // TODO: this is unstable, sometimes it returns 0 users, sometimes 1 //assert_eq!(0, site_aggregates_before_delete.users); @@ -121,7 +122,7 @@ mod tests { // Try a post delete Post::delete(pool, inserted_post.id).await.unwrap(); - let site_aggregates_after_post_delete = SiteAggregates::read(pool).await.unwrap(); + let site_aggregates_after_post_delete = SiteAggregates::read(pool).await.unwrap().unwrap(); assert_eq!(1, site_aggregates_after_post_delete.posts); assert_eq!(0, site_aggregates_after_post_delete.comments); @@ -140,8 +141,8 @@ mod tests { assert!(after_delete_creator.is_ok()); Site::delete(pool, inserted_site.id).await.unwrap(); - let after_delete_site = SiteAggregates::read(pool).await; - assert!(after_delete_site.is_err()); + let after_delete_site = SiteAggregates::read(pool).await.unwrap(); + assert!(after_delete_site.is_none()); Instance::delete(pool, inserted_instance.id).await.unwrap(); } @@ -155,7 +156,7 @@ mod tests { let (inserted_instance, inserted_person, inserted_site, inserted_community) = prepare_site_with_community(pool).await; - let site_aggregates_before = SiteAggregates::read(pool).await.unwrap(); + let site_aggregates_before = SiteAggregates::read(pool).await.unwrap().unwrap(); assert_eq!(1, site_aggregates_before.communities); Community::update( @@ -169,7 +170,7 @@ mod tests { .await .unwrap(); - let site_aggregates_after_delete = SiteAggregates::read(pool).await.unwrap(); + let site_aggregates_after_delete = SiteAggregates::read(pool).await.unwrap().unwrap(); assert_eq!(0, site_aggregates_after_delete.communities); Community::update( @@ -194,7 +195,7 @@ mod tests { .await .unwrap(); - let site_aggregates_after_remove = SiteAggregates::read(pool).await.unwrap(); + let site_aggregates_after_remove = SiteAggregates::read(pool).await.unwrap().unwrap(); assert_eq!(0, site_aggregates_after_remove.communities); Community::update( @@ -208,7 +209,7 @@ mod tests { .await .unwrap(); - let site_aggregates_after_remove_delete = SiteAggregates::read(pool).await.unwrap(); + let site_aggregates_after_remove_delete = SiteAggregates::read(pool).await.unwrap().unwrap(); assert_eq!(0, site_aggregates_after_remove_delete.communities); Community::delete(pool, inserted_community.id) diff --git a/crates/db_schema/src/impls/activity.rs b/crates/db_schema/src/impls/activity.rs index e1802c7c5..adcdf8ad5 100644 --- a/crates/db_schema/src/impls/activity.rs +++ b/crates/db_schema/src/impls/activity.rs @@ -22,18 +22,22 @@ impl SentActivity { .await } - pub async fn read_from_apub_id(pool: &mut DbPool<'_>, object_id: &DbUrl) -> Result { + pub async fn read_from_apub_id( + pool: &mut DbPool<'_>, + object_id: &DbUrl, + ) -> Result, Error> { use crate::schema::sent_activity::dsl::{ap_id, sent_activity}; let conn = &mut get_conn(pool).await?; sent_activity .filter(ap_id.eq(object_id)) - .first::(conn) + .first(conn) .await + .optional() } - pub async fn read(pool: &mut DbPool<'_>, object_id: ActivityId) -> Result { + pub async fn read(pool: &mut DbPool<'_>, object_id: ActivityId) -> Result, Error> { use crate::schema::sent_activity::dsl::sent_activity; let conn = &mut get_conn(pool).await?; - sent_activity.find(object_id).first::(conn).await + sent_activity.find(object_id).first(conn).await.optional() } } @@ -118,7 +122,10 @@ mod tests { SentActivity::create(pool, form).await.unwrap(); - let res = SentActivity::read_from_apub_id(pool, &ap_id).await.unwrap(); + let res = SentActivity::read_from_apub_id(pool, &ap_id) + .await + .unwrap() + .unwrap(); assert_eq!(res.ap_id, ap_id); assert_eq!(res.data, data); assert_eq!(res.sensitive, sensitive); diff --git a/crates/db_schema/src/impls/actor_language.rs b/crates/db_schema/src/impls/actor_language.rs index 0191dd872..fe21cf003 100644 --- a/crates/db_schema/src/impls/actor_language.rs +++ b/crates/db_schema/src/impls/actor_language.rs @@ -26,7 +26,7 @@ use diesel::{ QueryDsl, }; use diesel_async::{AsyncPgConnection, RunQueryDsl}; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use tokio::sync::OnceCell; pub const UNDETERMINED_ID: LanguageId = LanguageId(0); @@ -194,7 +194,7 @@ impl CommunityLanguage { pool: &mut DbPool<'_>, for_language_id: Option, for_community_id: CommunityId, - ) -> Result<(), LemmyError> { + ) -> LemmyResult<()> { use crate::schema::community_language::dsl::community_language; let conn = &mut get_conn(pool).await?; diff --git a/crates/db_schema/src/impls/comment.rs b/crates/db_schema/src/impls/comment.rs index b27d44591..30c058b89 100644 --- a/crates/db_schema/src/impls/comment.rs +++ b/crates/db_schema/src/impls/comment.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::{DecoratableTarget, OptionalExtension}, newtypes::{CommentId, DbUrl, PersonId}, schema::comment, source::comment::{ @@ -11,8 +12,9 @@ use crate::{ CommentUpdateForm, }, traits::{Crud, Likeable, Saveable}, - utils::{get_conn, naive_now, DbPool, DELETED_REPLACEMENT_TEXT}, + utils::{functions::coalesce, get_conn, naive_now, DbPool, DELETED_REPLACEMENT_TEXT}, }; +use chrono::{DateTime, Utc}; use diesel::{ dsl::{insert_into, sql_query}, result::Error, @@ -59,6 +61,15 @@ impl Comment { pool: &mut DbPool<'_>, comment_form: &CommentInsertForm, parent_path: Option<&Ltree>, + ) -> Result { + Self::insert_apub(pool, None, comment_form, parent_path).await + } + + pub async fn insert_apub( + pool: &mut DbPool<'_>, + timestamp: Option>, + comment_form: &CommentInsertForm, + parent_path: Option<&Ltree>, ) -> Result { let conn = &mut get_conn(pool).await?; @@ -67,13 +78,21 @@ impl Comment { .run(|conn| { Box::pin(async move { // Insert, to get the id - let inserted_comment = insert_into(comment::table) - .values(comment_form) - .on_conflict(comment::ap_id) - .do_update() - .set(comment_form) - .get_result::(conn) - .await?; + let inserted_comment = if let Some(timestamp) = timestamp { + insert_into(comment::table) + .values(comment_form) + .on_conflict(comment::ap_id) + .filter_target(coalesce(comment::updated, comment::published).lt(timestamp)) + .do_update() + .set(comment_form) + .get_result::(conn) + .await? + } else { + insert_into(comment::table) + .values(comment_form) + .get_result::(conn) + .await? + }; let comment_id = inserted_comment.id; @@ -129,20 +148,18 @@ where ca.comment_id = c.id" }) .await } + pub async fn read_from_apub_id( pool: &mut DbPool<'_>, object_id: Url, ) -> Result, Error> { let conn = &mut get_conn(pool).await?; let object_id: DbUrl = object_id.into(); - Ok( - comment::table - .filter(comment::ap_id.eq(object_id)) - .first::(conn) - .await - .ok() - .map(Into::into), - ) + comment::table + .filter(comment::ap_id.eq(object_id)) + .first(conn) + .await + .optional() } pub fn parent_comment_id(&self) -> Option { @@ -380,7 +397,10 @@ mod tests { .await .unwrap(); - let read_comment = Comment::read(pool, inserted_comment.id).await.unwrap(); + let read_comment = Comment::read(pool, inserted_comment.id) + .await + .unwrap() + .unwrap(); let like_removed = CommentLike::remove(pool, inserted_person.id, inserted_comment.id) .await .unwrap(); diff --git a/crates/db_schema/src/impls/comment_reply.rs b/crates/db_schema/src/impls/comment_reply.rs index e6d0915e9..4a4a49a13 100644 --- a/crates/db_schema/src/impls/comment_reply.rs +++ b/crates/db_schema/src/impls/comment_reply.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::OptionalExtension, newtypes::{CommentId, CommentReplyId, PersonId}, schema::comment_reply, source::comment_reply::{CommentReply, CommentReplyInsertForm, CommentReplyUpdateForm}, @@ -63,25 +64,27 @@ impl CommentReply { pub async fn read_by_comment( pool: &mut DbPool<'_>, for_comment_id: CommentId, - ) -> Result { + ) -> Result, Error> { let conn = &mut get_conn(pool).await?; comment_reply::table .filter(comment_reply::comment_id.eq(for_comment_id)) - .first::(conn) + .first(conn) .await + .optional() } pub async fn read_by_comment_and_person( pool: &mut DbPool<'_>, for_comment_id: CommentId, for_recipient_id: PersonId, - ) -> Result { + ) -> Result, Error> { let conn = &mut get_conn(pool).await?; comment_reply::table .filter(comment_reply::comment_id.eq(for_comment_id)) .filter(comment_reply::recipient_id.eq(for_recipient_id)) - .first::(conn) + .first(conn) .await + .optional() } } @@ -174,7 +177,10 @@ mod tests { published: inserted_reply.published, }; - let read_reply = CommentReply::read(pool, inserted_reply.id).await.unwrap(); + let read_reply = CommentReply::read(pool, inserted_reply.id) + .await + .unwrap() + .unwrap(); let comment_reply_update_form = CommentReplyUpdateForm { read: Some(false) }; let updated_reply = CommentReply::update(pool, inserted_reply.id, &comment_reply_update_form) diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs index af3bf9bcf..e886098ce 100644 --- a/crates/db_schema/src/impls/community.rs +++ b/crates/db_schema/src/impls/community.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::{DecoratableTarget, OptionalExtension}, newtypes::{CommunityId, DbUrl, PersonId}, schema::{community, community_follower, instance}, source::{ @@ -17,9 +18,14 @@ use crate::{ post::Post, }, traits::{ApubActor, Bannable, Crud, Followable, Joinable}, - utils::{functions::lower, get_conn, DbPool}, + utils::{ + functions::{coalesce, lower}, + get_conn, + DbPool, + }, SubscribedType, }; +use chrono::{DateTime, Utc}; use diesel::{ deserialize, dsl, @@ -44,25 +50,15 @@ impl Crud for Community { type IdType = CommunityId; async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { - let is_new_community = match &form.actor_id { - Some(id) => Community::read_from_apub_id(pool, id).await?.is_none(), - None => true, - }; let conn = &mut get_conn(pool).await?; - // Can't do separate insert/update commands because InsertForm/UpdateForm aren't convertible let community_ = insert_into(community::table) .values(form) - .on_conflict(community::actor_id) - .do_update() - .set(form) .get_result::(conn) .await?; // Initialize languages for new community - if is_new_community { - CommunityLanguage::update(pool, vec![], community_.id).await?; - } + CommunityLanguage::update(pool, vec![], community_.id).await?; Ok(community_) } @@ -116,29 +112,63 @@ pub enum CollectionType { } impl Community { + pub async fn insert_apub( + pool: &mut DbPool<'_>, + timestamp: DateTime, + form: &CommunityInsertForm, + ) -> Result { + let is_new_community = match &form.actor_id { + Some(id) => Community::read_from_apub_id(pool, id).await?.is_none(), + None => true, + }; + let conn = &mut get_conn(pool).await?; + + // Can't do separate insert/update commands because InsertForm/UpdateForm aren't convertible + let community_ = insert_into(community::table) + .values(form) + .on_conflict(community::actor_id) + .filter_target(coalesce(community::updated, community::published).lt(timestamp)) + .do_update() + .set(form) + .get_result::(conn) + .await?; + + // Initialize languages for new community + if is_new_community { + CommunityLanguage::update(pool, vec![], community_.id).await?; + } + + Ok(community_) + } + /// Get the community which has a given moderators or featured url, also return the collection type pub async fn get_by_collection_url( pool: &mut DbPool<'_>, url: &DbUrl, - ) -> Result<(Community, CollectionType), Error> { + ) -> Result, Error> { use crate::schema::community::dsl::{featured_url, moderators_url}; use CollectionType::*; let conn = &mut get_conn(pool).await?; let res = community::table .filter(moderators_url.eq(url)) - .first::(conn) - .await; - if let Ok(c) = res { - return Ok((c, Moderators)); - } - let res = community::table - .filter(featured_url.eq(url)) - .first::(conn) - .await; - if let Ok(c) = res { - return Ok((c, Featured)); + .first(conn) + .await + .optional()?; + + if let Some(c) = res { + Ok(Some((c, Moderators))) + } else { + let res = community::table + .filter(featured_url.eq(url)) + .first(conn) + .await + .optional()?; + if let Some(c) = res { + Ok(Some((c, Featured))) + } else { + Ok(None) + } } - Err(diesel::NotFound) } pub async fn set_featured_posts( @@ -323,21 +353,18 @@ impl ApubActor for Community { object_id: &DbUrl, ) -> Result, Error> { let conn = &mut get_conn(pool).await?; - Ok( - community::table - .filter(community::actor_id.eq(object_id)) - .first::(conn) - .await - .ok() - .map(Into::into), - ) + community::table + .filter(community::actor_id.eq(object_id)) + .first(conn) + .await + .optional() } async fn read_from_name( pool: &mut DbPool<'_>, community_name: &str, include_deleted: bool, - ) -> Result { + ) -> Result, Error> { let conn = &mut get_conn(pool).await?; let mut q = community::table .into_boxed() @@ -348,22 +375,23 @@ impl ApubActor for Community { .filter(community::deleted.eq(false)) .filter(community::removed.eq(false)); } - q.first::(conn).await + q.first(conn).await.optional() } async fn read_from_name_and_domain( pool: &mut DbPool<'_>, community_name: &str, for_domain: &str, - ) -> Result { + ) -> Result, Error> { let conn = &mut get_conn(pool).await?; community::table .inner_join(instance::table) .filter(lower(community::name).eq(community_name.to_lowercase())) .filter(lower(instance::domain).eq(for_domain.to_lowercase())) .select(community::all_columns) - .first::(conn) + .first(conn) .await + .optional() } } @@ -499,7 +527,10 @@ mod tests { expires: None, }; - let read_community = Community::read(pool, inserted_community.id).await.unwrap(); + let read_community = Community::read(pool, inserted_community.id) + .await + .unwrap() + .unwrap(); let update_community_form = CommunityUpdateForm { title: Some("nada".to_owned()), diff --git a/crates/db_schema/src/impls/email_verification.rs b/crates/db_schema/src/impls/email_verification.rs index c5a8792fb..b4951cf73 100644 --- a/crates/db_schema/src/impls/email_verification.rs +++ b/crates/db_schema/src/impls/email_verification.rs @@ -16,6 +16,7 @@ use diesel::{ sql_types::Timestamptz, ExpressionMethods, IntoSql, + OptionalExtension, QueryDsl, }; use diesel_async::RunQueryDsl; @@ -25,17 +26,18 @@ impl EmailVerification { let conn = &mut get_conn(pool).await?; insert_into(email_verification) .values(form) - .get_result::(conn) + .get_result(conn) .await } - pub async fn read_for_token(pool: &mut DbPool<'_>, token: &str) -> Result { + pub async fn read_for_token(pool: &mut DbPool<'_>, token: &str) -> Result, Error> { let conn = &mut get_conn(pool).await?; email_verification .filter(verification_token.eq(token)) .filter(published.gt(now.into_sql::() - 7.days())) - .first::(conn) + .first(conn) .await + .optional() } pub async fn delete_old_tokens_for_local_user( pool: &mut DbPool<'_>, diff --git a/crates/db_schema/src/impls/instance.rs b/crates/db_schema/src/impls/instance.rs index c329d8cc0..2e309efb9 100644 --- a/crates/db_schema/src/impls/instance.rs +++ b/crates/db_schema/src/impls/instance.rs @@ -26,6 +26,7 @@ use diesel::{ result::Error, ExpressionMethods, NullableExpressionMethods, + OptionalExtension, QueryDsl, SelectableHelper, }; @@ -41,11 +42,14 @@ impl Instance { // First try to read the instance row and return directly if found let instance = instance::table .filter(lower(domain).eq(&domain_.to_lowercase())) - .first::(conn) - .await; + .first(conn) + .await + .optional()?; + + // TODO could convert this to unwrap_or_else once async closures are stable match instance { - Ok(i) => Ok(i), - Err(diesel::NotFound) => { + Some(i) => Ok(i), + None => { // Instance not in database yet, insert it let form = InstanceForm::builder() .domain(domain_) @@ -61,7 +65,6 @@ impl Instance { .get_result::(conn) .await } - e => e, } } pub async fn update( diff --git a/crates/db_schema/src/impls/language.rs b/crates/db_schema/src/impls/language.rs index 905221402..6a7b4e9ac 100644 --- a/crates/db_schema/src/impls/language.rs +++ b/crates/db_schema/src/impls/language.rs @@ -1,7 +1,7 @@ use crate::{ diesel::ExpressionMethods, newtypes::LanguageId, - schema::language::dsl::{code, language}, + schema::language, source::language::Language, utils::{get_conn, DbPool}, }; @@ -9,14 +9,14 @@ use diesel::{result::Error, QueryDsl}; use diesel_async::RunQueryDsl; impl Language { - pub async fn read_all(pool: &mut DbPool<'_>) -> Result, Error> { + pub async fn read_all(pool: &mut DbPool<'_>) -> Result, Error> { let conn = &mut get_conn(pool).await?; - language.load::(conn).await + language::table.load(conn).await } - pub async fn read_from_id(pool: &mut DbPool<'_>, id_: LanguageId) -> Result { + pub async fn read_from_id(pool: &mut DbPool<'_>, id_: LanguageId) -> Result { let conn = &mut get_conn(pool).await?; - language.find(id_).first::(conn).await + language::table.find(id_).first(conn).await } /// Attempts to find the given language code and return its ID. If not found, returns none. @@ -27,8 +27,8 @@ impl Language { if let Some(code_) = code_ { let conn = &mut get_conn(pool).await?; Ok( - language - .filter(code.eq(code_)) + language::table + .filter(language::code.eq(code_)) .first::(conn) .await .map(|l| l.id) diff --git a/crates/db_schema/src/impls/local_site.rs b/crates/db_schema/src/impls/local_site.rs index 5a6cf2a53..602dfe1f4 100644 --- a/crates/db_schema/src/impls/local_site.rs +++ b/crates/db_schema/src/impls/local_site.rs @@ -1,47 +1,47 @@ use crate::{ - schema::local_site::dsl::local_site, + schema::local_site, source::local_site::{LocalSite, LocalSiteInsertForm, LocalSiteUpdateForm}, utils::{get_conn, DbPool}, }; use diesel::{dsl::insert_into, result::Error}; use diesel_async::RunQueryDsl; -use lemmy_utils::{error::LemmyError, CACHE_DURATION_SHORT}; +use lemmy_utils::{error::LemmyResult, CACHE_DURATION_API}; use moka::future::Cache; use once_cell::sync::Lazy; impl LocalSite { pub async fn create(pool: &mut DbPool<'_>, form: &LocalSiteInsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(local_site) + insert_into(local_site::table) .values(form) .get_result::(conn) .await } - pub async fn read(pool: &mut DbPool<'_>) -> Result { + pub async fn read(pool: &mut DbPool<'_>) -> LemmyResult { static CACHE: Lazy> = Lazy::new(|| { Cache::builder() .max_capacity(1) - .time_to_live(CACHE_DURATION_SHORT) + .time_to_live(CACHE_DURATION_API) .build() }); Ok( CACHE .try_get_with((), async { let conn = &mut get_conn(pool).await?; - local_site.first::(conn).await + local_site::table.first(conn).await }) .await?, ) } pub async fn update(pool: &mut DbPool<'_>, form: &LocalSiteUpdateForm) -> Result { let conn = &mut get_conn(pool).await?; - diesel::update(local_site) + diesel::update(local_site::table) .set(form) .get_result::(conn) .await } pub async fn delete(pool: &mut DbPool<'_>) -> Result { let conn = &mut get_conn(pool).await?; - diesel::delete(local_site).execute(conn).await + diesel::delete(local_site::table).execute(conn).await } } diff --git a/crates/db_schema/src/impls/local_site_rate_limit.rs b/crates/db_schema/src/impls/local_site_rate_limit.rs index 0c9e96e0b..6ab9ca8b8 100644 --- a/crates/db_schema/src/impls/local_site_rate_limit.rs +++ b/crates/db_schema/src/impls/local_site_rate_limit.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::OptionalExtension, schema::local_site_rate_limit, source::local_site_rate_limit::{ LocalSiteRateLimit, @@ -11,9 +12,9 @@ use diesel::{dsl::insert_into, result::Error}; use diesel_async::RunQueryDsl; impl LocalSiteRateLimit { - pub async fn read(pool: &mut DbPool<'_>) -> Result { + pub async fn read(pool: &mut DbPool<'_>) -> Result, Error> { let conn = &mut get_conn(pool).await?; - local_site_rate_limit::table.first::(conn).await + local_site_rate_limit::table.first(conn).await.optional() } pub async fn create( diff --git a/crates/db_schema/src/impls/local_user_vote_display_mode.rs b/crates/db_schema/src/impls/local_user_vote_display_mode.rs index 4458fc1b6..d77502335 100644 --- a/crates/db_schema/src/impls/local_user_vote_display_mode.rs +++ b/crates/db_schema/src/impls/local_user_vote_display_mode.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::OptionalExtension, newtypes::LocalUserId, schema::local_user_vote_display_mode, source::local_user_vote_display_mode::{ @@ -12,11 +13,12 @@ use diesel::{dsl::insert_into, result::Error, QueryDsl}; use diesel_async::RunQueryDsl; impl LocalUserVoteDisplayMode { - pub async fn read(pool: &mut DbPool<'_>) -> Result { + pub async fn read(pool: &mut DbPool<'_>) -> Result, Error> { let conn = &mut get_conn(pool).await?; local_user_vote_display_mode::table - .first::(conn) + .first(conn) .await + .optional() } pub async fn create( diff --git a/crates/db_schema/src/impls/moderator.rs b/crates/db_schema/src/impls/moderator.rs index 99f117878..125bbcd51 100644 --- a/crates/db_schema/src/impls/moderator.rs +++ b/crates/db_schema/src/impls/moderator.rs @@ -568,6 +568,7 @@ mod tests { .unwrap(); let read_mod_remove_post = ModRemovePost::read(pool, inserted_mod_remove_post.id) .await + .unwrap() .unwrap(); let expected_mod_remove_post = ModRemovePost { id: inserted_mod_remove_post.id, @@ -590,6 +591,7 @@ mod tests { .unwrap(); let read_mod_lock_post = ModLockPost::read(pool, inserted_mod_lock_post.id) .await + .unwrap() .unwrap(); let expected_mod_lock_post = ModLockPost { id: inserted_mod_lock_post.id, @@ -612,6 +614,7 @@ mod tests { .unwrap(); let read_mod_feature_post = ModFeaturePost::read(pool, inserted_mod_feature_post.id) .await + .unwrap() .unwrap(); let expected_mod_feature_post = ModFeaturePost { id: inserted_mod_feature_post.id, @@ -635,6 +638,7 @@ mod tests { .unwrap(); let read_mod_remove_comment = ModRemoveComment::read(pool, inserted_mod_remove_comment.id) .await + .unwrap() .unwrap(); let expected_mod_remove_comment = ModRemoveComment { id: inserted_mod_remove_comment.id, @@ -660,6 +664,7 @@ mod tests { let read_mod_remove_community = ModRemoveCommunity::read(pool, inserted_mod_remove_community.id) .await + .unwrap() .unwrap(); let expected_mod_remove_community = ModRemoveCommunity { id: inserted_mod_remove_community.id, @@ -687,6 +692,7 @@ mod tests { let read_mod_ban_from_community = ModBanFromCommunity::read(pool, inserted_mod_ban_from_community.id) .await + .unwrap() .unwrap(); let expected_mod_ban_from_community = ModBanFromCommunity { id: inserted_mod_ban_from_community.id, @@ -709,7 +715,10 @@ mod tests { expires: None, }; let inserted_mod_ban = ModBan::create(pool, &mod_ban_form).await.unwrap(); - let read_mod_ban = ModBan::read(pool, inserted_mod_ban.id).await.unwrap(); + let read_mod_ban = ModBan::read(pool, inserted_mod_ban.id) + .await + .unwrap() + .unwrap(); let expected_mod_ban = ModBan { id: inserted_mod_ban.id, mod_person_id: inserted_mod.id, @@ -733,6 +742,7 @@ mod tests { .unwrap(); let read_mod_add_community = ModAddCommunity::read(pool, inserted_mod_add_community.id) .await + .unwrap() .unwrap(); let expected_mod_add_community = ModAddCommunity { id: inserted_mod_add_community.id, @@ -751,7 +761,10 @@ mod tests { removed: None, }; let inserted_mod_add = ModAdd::create(pool, &mod_add_form).await.unwrap(); - let read_mod_add = ModAdd::read(pool, inserted_mod_add.id).await.unwrap(); + let read_mod_add = ModAdd::read(pool, inserted_mod_add.id) + .await + .unwrap() + .unwrap(); let expected_mod_add = ModAdd { id: inserted_mod_add.id, mod_person_id: inserted_mod.id, diff --git a/crates/db_schema/src/impls/password_reset_request.rs b/crates/db_schema/src/impls/password_reset_request.rs index 491097135..762aa4656 100644 --- a/crates/db_schema/src/impls/password_reset_request.rs +++ b/crates/db_schema/src/impls/password_reset_request.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::OptionalExtension, newtypes::LocalUserId, schema::password_reset_request::dsl::{local_user_id, password_reset_request, published, token}, source::password_reset_request::{PasswordResetRequest, PasswordResetRequestForm}, @@ -54,16 +55,14 @@ impl PasswordResetRequest { Self::create(pool, &form).await } - pub async fn read_from_token( - pool: &mut DbPool<'_>, - token_: &str, - ) -> Result { + pub async fn read_from_token(pool: &mut DbPool<'_>, token_: &str) -> Result, Error> { let conn = &mut get_conn(pool).await?; password_reset_request .filter(token.eq(token_)) .filter(published.gt(now.into_sql::() - 1.days())) - .first::(conn) + .first(conn) .await + .optional() } pub async fn get_recent_password_resets_count( @@ -141,6 +140,7 @@ mod tests { let read_password_reset_request = PasswordResetRequest::read_from_token(pool, token) .await + .unwrap() .unwrap(); let num_deleted = Person::delete(pool, inserted_person.id).await.unwrap(); Instance::delete(pool, inserted_instance.id).await.unwrap(); diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs index c90f67891..68a9b3d59 100644 --- a/crates/db_schema/src/impls/person.rs +++ b/crates/db_schema/src/impls/person.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::OptionalExtension, newtypes::{CommunityId, DbUrl, InstanceId, PersonId}, schema::{comment, community, instance, local_user, person, person_follower, post}, source::person::{ @@ -19,13 +20,16 @@ impl Crud for Person { type InsertForm = PersonInsertForm; type UpdateForm = PersonUpdateForm; type IdType = PersonId; - async fn read(pool: &mut DbPool<'_>, person_id: PersonId) -> Result { + + // Override this, so that you don't get back deleted + async fn read(pool: &mut DbPool<'_>, person_id: PersonId) -> Result, Error> { let conn = &mut get_conn(pool).await?; person::table .filter(person::deleted.eq(false)) .find(person_id) - .first::(conn) + .first(conn) .await + .optional() } async fn create(pool: &mut DbPool<'_>, form: &PersonInsertForm) -> Result { @@ -126,22 +130,19 @@ impl ApubActor for Person { object_id: &DbUrl, ) -> Result, Error> { let conn = &mut get_conn(pool).await?; - Ok( - person::table - .filter(person::deleted.eq(false)) - .filter(person::actor_id.eq(object_id)) - .first::(conn) - .await - .ok() - .map(Into::into), - ) + person::table + .filter(person::deleted.eq(false)) + .filter(person::actor_id.eq(object_id)) + .first(conn) + .await + .optional() } async fn read_from_name( pool: &mut DbPool<'_>, from_name: &str, include_deleted: bool, - ) -> Result { + ) -> Result, Error> { let conn = &mut get_conn(pool).await?; let mut q = person::table .into_boxed() @@ -150,14 +151,14 @@ impl ApubActor for Person { if !include_deleted { q = q.filter(person::deleted.eq(false)) } - q.first::(conn).await + q.first(conn).await.optional() } async fn read_from_name_and_domain( pool: &mut DbPool<'_>, person_name: &str, for_domain: &str, - ) -> Result { + ) -> Result, Error> { let conn = &mut get_conn(pool).await?; person::table @@ -165,8 +166,9 @@ impl ApubActor for Person { .filter(lower(person::name).eq(person_name.to_lowercase())) .filter(lower(instance::domain).eq(for_domain.to_lowercase())) .select(person::all_columns) - .first::(conn) + .first(conn) .await + .optional() } } @@ -269,7 +271,10 @@ mod tests { instance_id: inserted_instance.id, }; - let read_person = Person::read(pool, inserted_person.id).await.unwrap(); + let read_person = Person::read(pool, inserted_person.id) + .await + .unwrap() + .unwrap(); let update_person_form = PersonUpdateForm { actor_id: Some(inserted_person.actor_id.clone()), diff --git a/crates/db_schema/src/impls/person_mention.rs b/crates/db_schema/src/impls/person_mention.rs index 5524f87ee..8566d31b1 100644 --- a/crates/db_schema/src/impls/person_mention.rs +++ b/crates/db_schema/src/impls/person_mention.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::OptionalExtension, newtypes::{CommentId, PersonId, PersonMentionId}, schema::person_mention, source::person_mention::{PersonMention, PersonMentionInsertForm, PersonMentionUpdateForm}, @@ -63,13 +64,14 @@ impl PersonMention { pool: &mut DbPool<'_>, for_comment_id: CommentId, for_recipient_id: PersonId, - ) -> Result { + ) -> Result, Error> { let conn = &mut get_conn(pool).await?; person_mention::table .filter(person_mention::comment_id.eq(for_comment_id)) .filter(person_mention::recipient_id.eq(for_recipient_id)) - .first::(conn) + .first(conn) .await + .optional() } } @@ -164,6 +166,7 @@ mod tests { let read_mention = PersonMention::read(pool, inserted_mention.id) .await + .unwrap() .unwrap(); let person_mention_update_form = PersonMentionUpdateForm { read: Some(false) }; diff --git a/crates/db_schema/src/impls/post.rs b/crates/db_schema/src/impls/post.rs index 8db871ad5..2d055b1a8 100644 --- a/crates/db_schema/src/impls/post.rs +++ b/crates/db_schema/src/impls/post.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::OptionalExtension, newtypes::{CommunityId, DbUrl, PersonId, PostId}, schema::{post, post_hide, post_like, post_read, post_saved}, source::post::{ @@ -27,8 +28,15 @@ use crate::{ }, }; use ::url::Url; -use chrono::Utc; -use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl, TextExpressionMethods}; +use chrono::{DateTime, Utc}; +use diesel::{ + dsl::insert_into, + result::Error, + DecoratableTarget, + ExpressionMethods, + QueryDsl, + TextExpressionMethods, +}; use diesel_async::RunQueryDsl; use std::collections::HashSet; @@ -42,9 +50,6 @@ impl Crud for Post { let conn = &mut get_conn(pool).await?; insert_into(post::table) .values(form) - .on_conflict(post::ap_id) - .do_update() - .set(form) .get_result::(conn) .await } @@ -63,6 +68,22 @@ impl Crud for Post { } impl Post { + pub async fn insert_apub( + pool: &mut DbPool<'_>, + timestamp: DateTime, + form: &PostInsertForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + insert_into(post::table) + .values(form) + .on_conflict(post::ap_id) + .filter_target(coalesce(post::updated, post::published).lt(timestamp)) + .do_update() + .set(form) + .get_result::(conn) + .await + } + pub async fn list_for_community( pool: &mut DbPool<'_>, the_community_id: CommunityId, @@ -162,14 +183,11 @@ impl Post { ) -> Result, Error> { let conn = &mut get_conn(pool).await?; let object_id: DbUrl = object_id.into(); - Ok( - post::table - .filter(post::ap_id.eq(object_id)) - .first::(conn) - .await - .ok() - .map(Into::into), - ) + post::table + .filter(post::ap_id.eq(object_id)) + .first(conn) + .await + .optional() } pub async fn fetch_pictrs_posts_for_creator( @@ -497,7 +515,7 @@ mod tests { .unwrap(); assert_eq!(2, marked_as_read); - let read_post = Post::read(pool, inserted_post.id).await.unwrap(); + let read_post = Post::read(pool, inserted_post.id).await.unwrap().unwrap(); let new_post_update = PostUpdateForm { name: Some("A test post".into()), diff --git a/crates/db_schema/src/impls/private_message.rs b/crates/db_schema/src/impls/private_message.rs index ee0009189..75c7ce9bc 100644 --- a/crates/db_schema/src/impls/private_message.rs +++ b/crates/db_schema/src/impls/private_message.rs @@ -1,13 +1,14 @@ use crate::{ + diesel::{DecoratableTarget, OptionalExtension}, newtypes::{DbUrl, PersonId, PrivateMessageId}, - schema::private_message::dsl::{ap_id, private_message, read, recipient_id}, + schema::private_message, source::private_message::{PrivateMessage, PrivateMessageInsertForm, PrivateMessageUpdateForm}, traits::Crud, - utils::{get_conn, DbPool}, + utils::{functions::coalesce, get_conn, DbPool}, }; +use chrono::{DateTime, Utc}; use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; -use lemmy_utils::error::LemmyError; use url::Url; #[async_trait] @@ -18,11 +19,8 @@ impl Crud for PrivateMessage { async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(private_message) + insert_into(private_message::table) .values(form) - .on_conflict(ap_id) - .do_update() - .set(form) .get_result::(conn) .await } @@ -33,7 +31,7 @@ impl Crud for PrivateMessage { form: &Self::UpdateForm, ) -> Result { let conn = &mut get_conn(pool).await?; - diesel::update(private_message.find(private_message_id)) + diesel::update(private_message::table.find(private_message_id)) .set(form) .get_result::(conn) .await @@ -41,17 +39,33 @@ impl Crud for PrivateMessage { } impl PrivateMessage { + pub async fn insert_apub( + pool: &mut DbPool<'_>, + timestamp: DateTime, + form: &PrivateMessageInsertForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + insert_into(private_message::table) + .values(form) + .on_conflict(private_message::ap_id) + .filter_target(coalesce(private_message::updated, private_message::published).lt(timestamp)) + .do_update() + .set(form) + .get_result::(conn) + .await + } + pub async fn mark_all_as_read( pool: &mut DbPool<'_>, for_recipient_id: PersonId, ) -> Result, Error> { let conn = &mut get_conn(pool).await?; diesel::update( - private_message - .filter(recipient_id.eq(for_recipient_id)) - .filter(read.eq(false)), + private_message::table + .filter(private_message::recipient_id.eq(for_recipient_id)) + .filter(private_message::read.eq(false)), ) - .set(read.eq(true)) + .set(private_message::read.eq(true)) .get_results::(conn) .await } @@ -59,17 +73,14 @@ impl PrivateMessage { pub async fn read_from_apub_id( pool: &mut DbPool<'_>, object_id: Url, - ) -> Result, LemmyError> { + ) -> Result, Error> { let conn = &mut get_conn(pool).await?; let object_id: DbUrl = object_id.into(); - Ok( - private_message - .filter(ap_id.eq(object_id)) - .first::(conn) - .await - .ok() - .map(Into::into), - ) + private_message::table + .filter(private_message::ap_id.eq(object_id)) + .first(conn) + .await + .optional() } } @@ -141,6 +152,7 @@ mod tests { let read_private_message = PrivateMessage::read(pool, inserted_private_message.id) .await + .unwrap() .unwrap(); let private_message_update_form = PrivateMessageUpdateForm { diff --git a/crates/db_schema/src/impls/registration_application.rs b/crates/db_schema/src/impls/registration_application.rs index c4df7ba69..46b7d4bee 100644 --- a/crates/db_schema/src/impls/registration_application.rs +++ b/crates/db_schema/src/impls/registration_application.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::OptionalExtension, newtypes::LocalUserId, schema::registration_application::dsl::{local_user_id, registration_application}, source::registration_application::{ @@ -43,11 +44,12 @@ impl RegistrationApplication { pub async fn find_by_local_user_id( pool: &mut DbPool<'_>, local_user_id_: LocalUserId, - ) -> Result { + ) -> Result, Error> { let conn = &mut get_conn(pool).await?; registration_application .filter(local_user_id.eq(local_user_id_)) - .first::(conn) + .first(conn) .await + .optional() } } diff --git a/crates/db_schema/src/impls/secret.rs b/crates/db_schema/src/impls/secret.rs index f21c6c487..1365ea838 100644 --- a/crates/db_schema/src/impls/secret.rs +++ b/crates/db_schema/src/impls/secret.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::OptionalExtension, schema::secret::dsl::secret, source::secret::Secret, utils::{get_conn, DbPool}, @@ -9,12 +10,12 @@ use diesel_async::RunQueryDsl; impl Secret { /// Initialize the Secrets from the DB. /// Warning: You should only call this once. - pub async fn init(pool: &mut DbPool<'_>) -> Result { + pub async fn init(pool: &mut DbPool<'_>) -> Result, Error> { Self::read_secrets(pool).await } - async fn read_secrets(pool: &mut DbPool<'_>) -> Result { + async fn read_secrets(pool: &mut DbPool<'_>) -> Result, Error> { let conn = &mut get_conn(pool).await?; - secret.first::(conn).await + secret.first(conn).await.optional() } } diff --git a/crates/db_schema/src/impls/site.rs b/crates/db_schema/src/impls/site.rs index 7e9329afb..a371f9e07 100644 --- a/crates/db_schema/src/impls/site.rs +++ b/crates/db_schema/src/impls/site.rs @@ -1,6 +1,6 @@ use crate::{ newtypes::{DbUrl, InstanceId, SiteId}, - schema::site::dsl::{actor_id, id, instance_id, site}, + schema::site, source::{ actor_language::SiteLanguage, site::{Site, SiteInsertForm, SiteUpdateForm}, @@ -19,7 +19,7 @@ impl Crud for Site { type IdType = SiteId; /// Use SiteView::read_local, or Site::read_from_apub_id instead - async fn read(_pool: &mut DbPool<'_>, _site_id: SiteId) -> Result { + async fn read(_pool: &mut DbPool<'_>, _site_id: SiteId) -> Result, Error> { unimplemented!() } @@ -31,9 +31,9 @@ impl Crud for Site { let conn = &mut get_conn(pool).await?; // Can't do separate insert/update commands because InsertForm/UpdateForm aren't convertible - let site_ = insert_into(site) + let site_ = insert_into(site::table) .values(form) - .on_conflict(actor_id) + .on_conflict(site::actor_id) .do_update() .set(form) .get_result::(conn) @@ -53,7 +53,7 @@ impl Crud for Site { new_site: &Self::UpdateForm, ) -> Result { let conn = &mut get_conn(pool).await?; - diesel::update(site.find(site_id)) + diesel::update(site::table.find(site_id)) .set(new_site) .get_result::(conn) .await @@ -66,9 +66,9 @@ impl Site { _instance_id: InstanceId, ) -> Result, Error> { let conn = &mut get_conn(pool).await?; - site - .filter(instance_id.eq(_instance_id)) - .get_result(conn) + site::table + .filter(site::instance_id.eq(_instance_id)) + .first(conn) .await .optional() } @@ -78,17 +78,20 @@ impl Site { ) -> Result, Error> { let conn = &mut get_conn(pool).await?; - site - .filter(actor_id.eq(object_id)) - .first::(conn) + site::table + .filter(site::actor_id.eq(object_id)) + .first(conn) .await .optional() - .map(Into::into) } pub async fn read_remote_sites(pool: &mut DbPool<'_>) -> Result, Error> { let conn = &mut get_conn(pool).await?; - site.order_by(id).offset(1).get_results::(conn).await + site::table + .order_by(site::id) + .offset(1) + .get_results::(conn) + .await } /// Instance actor is at the root path, so we simply need to clear the path and other unnecessary diff --git a/crates/db_schema/src/source/site.rs b/crates/db_schema/src/source/site.rs index 40ba14f96..83d23c779 100644 --- a/crates/db_schema/src/source/site.rs +++ b/crates/db_schema/src/source/site.rs @@ -36,7 +36,7 @@ pub struct Site { pub inbox_url: DbUrl, #[serde(skip)] pub private_key: Option, - #[serde(skip)] + // TODO: mark as `serde(skip)` in next major release as its not needed for api pub public_key: String, pub instance_id: InstanceId, /// If present, nsfw content is visible by default. Should be displayed by frontends/clients diff --git a/crates/db_schema/src/traits.rs b/crates/db_schema/src/traits.rs index b0434a65c..139ec0e15 100644 --- a/crates/db_schema/src/traits.rs +++ b/crates/db_schema/src/traits.rs @@ -1,4 +1,5 @@ use crate::{ + diesel::OptionalExtension, newtypes::{CommunityId, DbUrl, PersonId}, utils::{get_conn, DbPool}, }; @@ -42,10 +43,10 @@ where async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result; - async fn read(pool: &mut DbPool<'_>, id: Self::IdType) -> Result { + async fn read(pool: &mut DbPool<'_>, id: Self::IdType) -> Result, Error> { let query: Find = Self::table().find(id); let conn = &mut *get_conn(pool).await?; - query.first::(conn).await + query.first(conn).await.optional() } /// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column. @@ -185,14 +186,14 @@ pub trait ApubActor { pool: &mut DbPool<'_>, actor_name: &str, include_deleted: bool, - ) -> Result + ) -> Result, Error> where Self: Sized; async fn read_from_name_and_domain( pool: &mut DbPool<'_>, actor_name: &str, protocol_domain: &str, - ) -> Result + ) -> Result, Error> where Self: Sized; } diff --git a/crates/db_schema/src/utils.rs b/crates/db_schema/src/utils.rs index 465ecb4cd..dcd208881 100644 --- a/crates/db_schema/src/utils.rs +++ b/crates/db_schema/src/utils.rs @@ -13,9 +13,14 @@ use diesel::{ pg::Pg, query_builder::{Query, QueryFragment}, query_dsl::methods::LimitDsl, - result::{ConnectionError, ConnectionResult, Error as DieselError, Error::QueryBuilderError}, + result::{ + ConnectionError, + ConnectionResult, + Error::{self as DieselError, QueryBuilderError}, + }, sql_types::{self, Timestamptz}, IntoSql, + OptionalExtension, PgConnection, }; use diesel_async::{ @@ -31,7 +36,7 @@ use diesel_migrations::EmbeddedMigrations; use futures_util::{future::BoxFuture, Future, FutureExt}; use i_love_jesus::CursorKey; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, settings::SETTINGS, }; use once_cell::sync::Lazy; @@ -292,9 +297,7 @@ pub fn diesel_option_overwrite(opt: Option) -> Option> { } } -pub fn diesel_option_overwrite_to_url( - opt: &Option, -) -> Result>, LemmyError> { +pub fn diesel_option_overwrite_to_url(opt: &Option) -> LemmyResult>> { match opt.as_ref().map(String::as_str) { // An empty string is an erase Some("") => Ok(Some(None)), @@ -305,9 +308,7 @@ pub fn diesel_option_overwrite_to_url( } } -pub fn diesel_option_overwrite_to_url_create( - opt: &Option, -) -> Result, LemmyError> { +pub fn diesel_option_overwrite_to_url_create(opt: &Option) -> LemmyResult> { match opt.as_ref().map(String::as_str) { // An empty string is nothing Some("") => Ok(None), @@ -365,7 +366,7 @@ impl ServerCertVerifier for NoCertVerifier { pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); -fn run_migrations(db_url: &str) -> Result<(), LemmyError> { +fn run_migrations(db_url: &str) -> LemmyResult<()> { // Needs to be a sync connection let mut conn = PgConnection::establish(db_url).with_context(|| "Error connecting to database")?; @@ -378,7 +379,7 @@ fn run_migrations(db_url: &str) -> Result<(), LemmyError> { Ok(()) } -pub async fn build_db_pool() -> Result { +pub async fn build_db_pool() -> LemmyResult { let db_url = SETTINGS.get_database_url(); // We only support TLS with sslmode=require currently let tls_enabled = db_url.contains("sslmode=require"); @@ -514,12 +515,12 @@ impl Queries { self, pool: &'a mut DbPool<'_>, args: Args, - ) -> Result + ) -> Result, DieselError> where RF: ReadFn<'a, T, Args>, { let conn = get_conn(pool).await?; - (self.read_fn)(conn, args).await + (self.read_fn)(conn, args).await.optional() } pub async fn list<'a, T, Args>( diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs index 0da5b3cff..e524e9a30 100644 --- a/crates/db_views/src/comment_report_view.rs +++ b/crates/db_views/src/comment_report_view.rs @@ -134,7 +134,7 @@ fn queries<'a>() -> Queries< comment_report::table.find(report_id).into_boxed(), my_person_id, ) - .first::(&mut conn) + .first(&mut conn) .await }; @@ -191,7 +191,7 @@ impl CommentReportView { pool: &mut DbPool<'_>, report_id: CommentReportId, my_person_id: PersonId, - ) -> Result { + ) -> Result, Error> { queries().read(pool, (report_id, my_person_id)).await } @@ -396,11 +396,13 @@ mod tests { let agg = CommentAggregates::read(pool, inserted_comment.id) .await + .unwrap() .unwrap(); let read_jessica_report_view = CommentReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id) .await + .unwrap() .unwrap(); let expected_jessica_report_view = CommentReportView { comment_report: inserted_jessica_report.clone(), @@ -554,6 +556,7 @@ mod tests { let read_jessica_report_view_after_resolve = CommentReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id) .await + .unwrap() .unwrap(); let mut expected_jessica_report_view_after_resolve = expected_jessica_report_view; diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index 01ff394eb..e8957e1e9 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -191,7 +191,7 @@ fn queries<'a>() -> Queries< if my_person_id.is_none() { query = query.filter(community::visibility.eq(CommunityVisibility::Public)); } - query.first::(&mut conn).await + query.first(&mut conn).await }; let list = move |mut conn: DbConn<'a>, options: CommentQuery<'a>| async move { @@ -375,17 +375,21 @@ impl CommentView { pool: &mut DbPool<'_>, comment_id: CommentId, my_person_id: Option, - ) -> Result { + ) -> Result, Error> { // If a person is given, then my_vote (res.9), if None, should be 0, not null // Necessary to differentiate between other person's votes - let mut res = queries().read(pool, (comment_id, my_person_id)).await?; - if my_person_id.is_some() && res.my_vote.is_none() { - res.my_vote = Some(0); - } - if res.comment.deleted || res.comment.removed { - res.comment.content = String::new(); + if let Ok(Some(res)) = queries().read(pool, (comment_id, my_person_id)).await { + let mut new_view = res.clone(); + if my_person_id.is_some() && res.my_vote.is_none() { + new_view.my_vote = Some(0); + } + if res.comment.deleted || res.comment.removed { + new_view.comment.content = String::new(); + } + Ok(Some(new_view)) + } else { + Ok(None) } - Ok(res) } } @@ -472,7 +476,7 @@ mod tests { CommunityVisibility, SubscribedType, }; - use lemmy_utils::error::LemmyResult; + use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use pretty_assertions::assert_eq; use serial_test::serial; @@ -700,7 +704,8 @@ mod tests { data.inserted_comment_1.id, Some(data.timmy_local_user_view.person.id), ) - .await?; + .await? + .unwrap(); // Make sure block set the creator blocked assert!(read_comment_from_blocked_person.creator_blocked); @@ -1009,7 +1014,9 @@ mod tests { } async fn expected_comment_view(data: &Data, pool: &mut DbPool<'_>) -> LemmyResult { - let agg = CommentAggregates::read(pool, data.inserted_comment_0.id).await?; + let agg = CommentAggregates::read(pool, data.inserted_comment_0.id) + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; Ok(CommentView { creator_banned_from_community: false, banned_from_community: false, @@ -1154,8 +1161,8 @@ mod tests { .await?; assert_eq!(5, authenticated_query.len()); - let unauthenticated_comment = CommentView::read(pool, data.inserted_comment_0.id, None).await; - assert!(unauthenticated_comment.is_err()); + let unauthenticated_comment = CommentView::read(pool, data.inserted_comment_0.id, None).await?; + assert!(unauthenticated_comment.is_none()); let authenticated_comment = CommentView::read( pool, @@ -1202,7 +1209,8 @@ mod tests { data.inserted_comment_0.id, Some(inserted_banned_from_comm_local_user.person_id), ) - .await?; + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; assert!(comment_view.banned_from_community); @@ -1222,7 +1230,8 @@ mod tests { data.inserted_comment_0.id, Some(data.timmy_local_user_view.person.id), ) - .await?; + .await? + .ok_or(LemmyErrorType::CouldntFindComment)?; assert!(!comment_view.banned_from_community); diff --git a/crates/db_views/src/local_user_view.rs b/crates/db_views/src/local_user_view.rs index 96d6a3530..0c13b0a68 100644 --- a/crates/db_views/src/local_user_view.rs +++ b/crates/db_views/src/local_user_view.rs @@ -62,7 +62,7 @@ fn queries<'a>( .inner_join(local_user_vote_display_mode::table) .inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id))) .select(selection) - .first::(&mut conn) + .first(&mut conn) .await }; @@ -86,28 +86,37 @@ fn queries<'a>( } impl LocalUserView { - pub async fn read(pool: &mut DbPool<'_>, local_user_id: LocalUserId) -> Result { + pub async fn read( + pool: &mut DbPool<'_>, + local_user_id: LocalUserId, + ) -> Result, Error> { queries().read(pool, ReadBy::Id(local_user_id)).await } - pub async fn read_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result { + pub async fn read_person( + pool: &mut DbPool<'_>, + person_id: PersonId, + ) -> Result, Error> { queries().read(pool, ReadBy::Person(person_id)).await } - pub async fn read_from_name(pool: &mut DbPool<'_>, name: &str) -> Result { + pub async fn read_from_name(pool: &mut DbPool<'_>, name: &str) -> Result, Error> { queries().read(pool, ReadBy::Name(name)).await } pub async fn find_by_email_or_name( pool: &mut DbPool<'_>, name_or_email: &str, - ) -> Result { + ) -> Result, Error> { queries() .read(pool, ReadBy::NameOrEmail(name_or_email)) .await } - pub async fn find_by_email(pool: &mut DbPool<'_>, from_email: &str) -> Result { + pub async fn find_by_email( + pool: &mut DbPool<'_>, + from_email: &str, + ) -> Result, Error> { queries().read(pool, ReadBy::Email(from_email)).await } diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs index 878937d01..76679df1e 100644 --- a/crates/db_views/src/post_report_view.rs +++ b/crates/db_views/src/post_report_view.rs @@ -163,7 +163,7 @@ fn queries<'a>() -> Queries< post_report::table.find(report_id).into_boxed(), my_person_id, ) - .first::(&mut conn) + .first(&mut conn) .await }; @@ -219,7 +219,7 @@ impl PostReportView { pool: &mut DbPool<'_>, report_id: PostReportId, my_person_id: PersonId, - ) -> Result { + ) -> Result, Error> { queries().read(pool, (report_id, my_person_id)).await } @@ -421,6 +421,7 @@ mod tests { let read_jessica_report_view = PostReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id) .await + .unwrap() .unwrap(); assert_eq!( @@ -458,6 +459,7 @@ mod tests { let read_jessica_report_view_after_resolve = PostReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id) .await + .unwrap() .unwrap(); assert!(read_jessica_report_view_after_resolve.post_report.resolved); assert_eq!( diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index b323437d5..e66912190 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -305,7 +305,7 @@ fn queries<'a>() -> Queries< Commented::new(query) .text("PostView::read") - .first::(&mut conn) + .first(&mut conn) .await }; @@ -581,12 +581,10 @@ impl PostView { post_id: PostId, my_person_id: Option, is_mod_or_admin: bool, - ) -> Result { - let res = queries() + ) -> Result, Error> { + queries() .read(pool, (post_id, my_person_id, is_mod_or_admin)) - .await?; - - Ok(res) + .await } } @@ -597,19 +595,21 @@ impl PaginationCursor { PaginationCursor(format!("P{:x}", view.counts.post_id.0)) } pub async fn read(&self, pool: &mut DbPool<'_>) -> Result { - Ok(PaginationCursorData( - PostAggregates::read( - pool, - PostId( - self - .0 - .get(1..) - .and_then(|e| i32::from_str_radix(e, 16).ok()) - .ok_or_else(|| Error::QueryBuilderError("Could not parse pagination token".into()))?, - ), - ) - .await?, - )) + let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into()); + let token = PostAggregates::read( + pool, + PostId( + self + .0 + .get(1..) + .and_then(|e| i32::from_str_radix(e, 16).ok()) + .ok_or_else(err_msg)?, + ), + ) + .await? + .ok_or_else(err_msg)?; + + Ok(PaginationCursorData(token)) } } @@ -783,7 +783,7 @@ mod tests { SortType, SubscribedType, }; - use lemmy_utils::error::LemmyResult; + use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use pretty_assertions::assert_eq; use serial_test::serial; use std::{collections::HashSet, time::Duration}; @@ -964,7 +964,8 @@ mod tests { Some(data.local_user_view.person.id), false, ) - .await?; + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; let expected_post_listing_with_user = expected_post_view(&data, pool).await?; @@ -1014,7 +1015,9 @@ mod tests { .await?; let read_post_listing_single_no_person = - PostView::read(pool, data.inserted_post.id, None, false).await?; + PostView::read(pool, data.inserted_post.id, None, false) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; let expected_post_listing_no_person = expected_post_view(&data, pool).await?; @@ -1091,7 +1094,8 @@ mod tests { Some(data.local_user_view.person.id), false, ) - .await?; + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; let mut expected_post_with_upvote = expected_post_view(&data, pool).await?; expected_post_with_upvote.my_vote = Some(1); @@ -1573,7 +1577,9 @@ mod tests { &data.inserted_community, &data.inserted_post, ); - let agg = PostAggregates::read(pool, inserted_post.id).await?; + let agg = PostAggregates::read(pool, inserted_post.id) + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; Ok(PostView { post: Post { @@ -1716,8 +1722,8 @@ mod tests { .await?; assert_eq!(2, authenticated_query.len()); - let unauthenticated_post = PostView::read(pool, data.inserted_post.id, None, false).await; - assert!(unauthenticated_post.is_err()); + let unauthenticated_post = PostView::read(pool, data.inserted_post.id, None, false).await?; + assert!(unauthenticated_post.is_none()); let authenticated_post = PostView::read( pool, @@ -1767,7 +1773,8 @@ mod tests { Some(inserted_banned_from_comm_local_user.person_id), false, ) - .await?; + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; assert!(post_view.banned_from_community); @@ -1788,7 +1795,8 @@ mod tests { Some(data.local_user_view.person.id), false, ) - .await?; + .await? + .ok_or(LemmyErrorType::CouldntFindPost)?; assert!(!post_view.banned_from_community); diff --git a/crates/db_views/src/private_message_report_view.rs b/crates/db_views/src/private_message_report_view.rs index dff9820d9..a402d0d4f 100644 --- a/crates/db_views/src/private_message_report_view.rs +++ b/crates/db_views/src/private_message_report_view.rs @@ -42,7 +42,7 @@ fn queries<'a>() -> Queries< let read = move |mut conn: DbConn<'a>, report_id: PrivateMessageReportId| async move { all_joins(private_message_report::table.find(report_id).into_boxed()) - .first::(&mut conn) + .first(&mut conn) .await }; @@ -77,7 +77,7 @@ impl PrivateMessageReportView { pub async fn read( pool: &mut DbPool<'_>, report_id: PrivateMessageReportId, - ) -> Result { + ) -> Result, Error> { queries().read(pool, report_id).await } diff --git a/crates/db_views/src/private_message_view.rs b/crates/db_views/src/private_message_view.rs index 466f3bdf0..764ef1dcb 100644 --- a/crates/db_views/src/private_message_view.rs +++ b/crates/db_views/src/private_message_view.rs @@ -53,7 +53,7 @@ fn queries<'a>() -> Queries< all_joins(private_message::table.find(private_message_id).into_boxed()) .order_by(private_message::published.desc()) .select(selection) - .first::(&mut conn) + .first(&mut conn) .await }; @@ -113,7 +113,7 @@ impl PrivateMessageView { pub async fn read( pool: &mut DbPool<'_>, private_message_id: PrivateMessageId, - ) -> Result { + ) -> Result, Error> { queries().read(pool, private_message_id).await } diff --git a/crates/db_views/src/registration_application_view.rs b/crates/db_views/src/registration_application_view.rs index 85916bb90..7346dcd0d 100644 --- a/crates/db_views/src/registration_application_view.rs +++ b/crates/db_views/src/registration_application_view.rs @@ -42,7 +42,7 @@ fn queries<'a>() -> Queries< .find(registration_application_id) .into_boxed(), ) - .first::(&mut conn) + .first(&mut conn) .await }; @@ -76,7 +76,7 @@ impl RegistrationApplicationView { pub async fn read( pool: &mut DbPool<'_>, registration_application_id: i32, - ) -> Result { + ) -> Result, Error> { queries().read(pool, registration_application_id).await } @@ -209,6 +209,7 @@ mod tests { let read_sara_app_view = RegistrationApplicationView::read(pool, sara_app.id) .await + .unwrap() .unwrap(); let jess_person_form = PersonInsertForm::builder() @@ -240,6 +241,7 @@ mod tests { let read_jess_app_view = RegistrationApplicationView::read(pool, jess_app.id) .await + .unwrap() .unwrap(); let mut expected_sara_app_view = RegistrationApplicationView { @@ -343,6 +345,7 @@ mod tests { let read_sara_app_view_after_approve = RegistrationApplicationView::read(pool, sara_app.id) .await + .unwrap() .unwrap(); // Make sure the columns changed diff --git a/crates/db_views/src/site_view.rs b/crates/db_views/src/site_view.rs index 52074c982..8f0722318 100644 --- a/crates/db_views/src/site_view.rs +++ b/crates/db_views/src/site_view.rs @@ -1,5 +1,5 @@ use crate::structs::SiteView; -use diesel::{result::Error, ExpressionMethods, JoinOnDsl, QueryDsl}; +use diesel::{result::Error, ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; use lemmy_db_schema::{ schema::{local_site, local_site_rate_limit, site, site_aggregates}, @@ -7,7 +7,7 @@ use lemmy_db_schema::{ }; impl SiteView { - pub async fn read_local(pool: &mut DbPool<'_>) -> Result { + pub async fn read_local(pool: &mut DbPool<'_>) -> Result, Error> { let conn = &mut get_conn(pool).await?; site::table .inner_join(local_site::table) @@ -21,7 +21,8 @@ impl SiteView { local_site_rate_limit::all_columns, site_aggregates::all_columns, )) - .first::(conn) + .first(conn) .await + .optional() } } diff --git a/crates/db_views_actor/src/comment_reply_view.rs b/crates/db_views_actor/src/comment_reply_view.rs index 77f744a97..baa4f5601 100644 --- a/crates/db_views_actor/src/comment_reply_view.rs +++ b/crates/db_views_actor/src/comment_reply_view.rs @@ -188,7 +188,7 @@ fn queries<'a>() -> Queries< comment_reply::table.find(comment_reply_id).into_boxed(), my_person_id, ) - .first::(&mut conn) + .first(&mut conn) .await }; @@ -234,7 +234,7 @@ impl CommentReplyView { pool: &mut DbPool<'_>, comment_reply_id: CommentReplyId, my_person_id: Option, - ) -> Result { + ) -> Result, Error> { queries().read(pool, (comment_reply_id, my_person_id)).await } diff --git a/crates/db_views_actor/src/community_view.rs b/crates/db_views_actor/src/community_view.rs index 3107478dc..b5c23c6ef 100644 --- a/crates/db_views_actor/src/community_view.rs +++ b/crates/db_views_actor/src/community_view.rs @@ -102,7 +102,7 @@ fn queries<'a>() -> Queries< query = query.filter(community::visibility.eq(CommunityVisibility::Public)); } - query.first::(&mut conn).await + query.first(&mut conn).await }; let list = move |mut conn: DbConn<'a>, (options, site): (CommunityQuery<'a>, &'a Site)| async move { @@ -194,7 +194,7 @@ impl CommunityView { community_id: CommunityId, my_person_id: Option, is_mod_or_admin: bool, - ) -> Result { + ) -> Result, Error> { queries() .read(pool, (community_id, my_person_id, is_mod_or_admin)) .await @@ -209,9 +209,10 @@ impl CommunityView { CommunityModeratorView::is_community_moderator(pool, community_id, person_id).await?; if is_mod { Ok(true) + } else if let Ok(Some(person_view)) = PersonView::read(pool, person_id).await { + Ok(person_view.is_admin) } else { - let is_admin = PersonView::read(pool, person_id).await?.is_admin; - Ok(is_admin) + Ok(false) } } @@ -223,11 +224,12 @@ impl CommunityView { let is_mod_of_any = CommunityModeratorView::is_community_moderator_of_any(pool, person_id).await?; if is_mod_of_any { - return Ok(true); + Ok(true) + } else if let Ok(Some(person_view)) = PersonView::read(pool, person_id).await { + Ok(person_view.is_admin) + } else { + Ok(false) } - - let is_admin = PersonView::read(pool, person_id).await?.is_admin; - Ok(is_admin) } } @@ -384,8 +386,10 @@ mod tests { assert_eq!(1, authenticated_query.len()); let unauthenticated_community = - CommunityView::read(pool, data.inserted_community.id, None, false).await; - assert!(unauthenticated_community.is_err()); + CommunityView::read(pool, data.inserted_community.id, None, false) + .await + .unwrap(); + assert!(unauthenticated_community.is_none()); let authenticated_community = CommunityView::read( pool, diff --git a/crates/db_views_actor/src/person_mention_view.rs b/crates/db_views_actor/src/person_mention_view.rs index 399850292..65c0bd0e6 100644 --- a/crates/db_views_actor/src/person_mention_view.rs +++ b/crates/db_views_actor/src/person_mention_view.rs @@ -187,7 +187,7 @@ fn queries<'a>() -> Queries< person_mention::table.find(person_mention_id).into_boxed(), my_person_id, ) - .first::(&mut conn) + .first(&mut conn) .await }; @@ -233,7 +233,7 @@ impl PersonMentionView { pool: &mut DbPool<'_>, person_mention_id: PersonMentionId, my_person_id: Option, - ) -> Result { + ) -> Result, Error> { queries() .read(pool, (person_mention_id, my_person_id)) .await diff --git a/crates/db_views_actor/src/person_view.rs b/crates/db_views_actor/src/person_view.rs index b3eac296d..5734bc812 100644 --- a/crates/db_views_actor/src/person_view.rs +++ b/crates/db_views_actor/src/person_view.rs @@ -72,7 +72,7 @@ fn queries<'a>( let read = move |mut conn: DbConn<'a>, person_id: PersonId| async move { all_joins(person::table.find(person_id).into_boxed()) - .first::(&mut conn) + .first(&mut conn) .await }; @@ -134,7 +134,7 @@ fn queries<'a>( } impl PersonView { - pub async fn read(pool: &mut DbPool<'_>, person_id: PersonId) -> Result { + pub async fn read(pool: &mut DbPool<'_>, person_id: PersonId) -> Result, Error> { queries().read(pool, person_id).await } @@ -163,12 +163,10 @@ impl PersonQuery { } #[cfg(test)] -#[allow(clippy::unwrap_used)] #[allow(clippy::indexing_slicing)] mod tests { use super::*; - use diesel::NotFound; use lemmy_db_schema::{ assert_length, source::{ @@ -179,7 +177,7 @@ mod tests { traits::Crud, utils::build_db_pool_for_tests, }; - use lemmy_utils::error::LemmyResult; + use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use pretty_assertions::assert_eq; use serial_test::serial; @@ -254,8 +252,8 @@ mod tests { ) .await?; - let read = PersonView::read(pool, data.alice.id).await; - assert_eq!(read.err(), Some(NotFound)); + let read = PersonView::read(pool, data.alice.id).await?; + assert!(read.is_none()); let list = PersonQuery { sort: Some(SortType::New), @@ -314,10 +312,16 @@ mod tests { assert_length!(1, list); assert_eq!(list[0].person.id, data.alice.id); - let is_admin = PersonView::read(pool, data.alice.id).await?.is_admin; + let is_admin = PersonView::read(pool, data.alice.id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)? + .is_admin; assert!(is_admin); - let is_admin = PersonView::read(pool, data.bob.id).await?.is_admin; + let is_admin = PersonView::read(pool, data.bob.id) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)? + .is_admin; assert!(!is_admin); cleanup(data, pool).await diff --git a/crates/federate/src/util.rs b/crates/federate/src/util.rs index 2809b9bb4..b057ab0fe 100644 --- a/crates/federate/src/util.rs +++ b/crates/federate/src/util.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Context, Result}; use diesel::prelude::*; use diesel_async::RunQueryDsl; -use lemmy_api_common::lemmy_utils::CACHE_DURATION_SHORT; +use lemmy_api_common::lemmy_utils::CACHE_DURATION_FEDERATION; use lemmy_apub::{ activity_lists::SharedInboxActivities, fetcher::{site_or_community_or_user::SiteOrCommunityOrUser, user_or_community::UserOrCommunity}, @@ -151,7 +151,6 @@ pub(crate) async fn get_activity_cached( .try_get_with(activity_id, async { let row = SentActivity::read(pool, activity_id) .await - .optional() .context("could not read activity")?; let Some(mut row) = row else { return anyhow::Result::<_, anyhow::Error>::Ok(None); @@ -169,8 +168,11 @@ pub(crate) async fn get_activity_cached( /// return the most current activity id (with 1 second cache) pub(crate) async fn get_latest_activity_id(pool: &mut DbPool<'_>) -> Result { - static CACHE: Lazy> = - Lazy::new(|| Cache::builder().time_to_live(CACHE_DURATION_SHORT).build()); + static CACHE: Lazy> = Lazy::new(|| { + Cache::builder() + .time_to_live(CACHE_DURATION_FEDERATION) + .build() + }); CACHE .try_get_with((), async { use diesel::dsl::max; diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs index b8ca2d5a6..5e3db357a 100644 --- a/crates/routes/src/feeds.rs +++ b/crates/routes/src/feeds.rs @@ -22,7 +22,7 @@ use lemmy_db_views_actor::{ }; use lemmy_utils::{ cache_header::cache_1hour, - error::{LemmyError, LemmyErrorType}, + error::{LemmyError, LemmyErrorType, LemmyResult}, utils::markdown::{markdown_to_html, sanitize_html}, }; use once_cell::sync::Lazy; @@ -151,8 +151,10 @@ async fn get_feed_data( sort_type: SortType, limit: i64, page: i64, -) -> Result { - let site_view = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; check_private_instance(&None, &site_view.local_site)?; @@ -256,9 +258,13 @@ async fn get_feed_user( limit: &i64, page: &i64, user_name: &str, -) -> Result { - let site_view = SiteView::read_local(&mut context.pool()).await?; - let person = Person::read_from_name(&mut context.pool(), user_name, false).await?; +) -> LemmyResult { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; + let person = Person::read_from_name(&mut context.pool(), user_name, false) + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; check_private_instance(&None, &site_view.local_site)?; @@ -292,9 +298,13 @@ async fn get_feed_community( limit: &i64, page: &i64, community_name: &str, -) -> Result { - let site_view = SiteView::read_local(&mut context.pool()).await?; - let community = Community::read_from_name(&mut context.pool(), community_name, false).await?; +) -> LemmyResult { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; + let community = Community::read_from_name(&mut context.pool(), community_name, false) + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; if community.visibility != CommunityVisibility::Public { return Err(LemmyErrorType::CouldntFindCommunity.into()); } @@ -335,8 +345,10 @@ async fn get_feed_front( limit: &i64, page: &i64, jwt: &str, -) -> Result { - let site_view = SiteView::read_local(&mut context.pool()).await?; +) -> LemmyResult { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let local_user = local_user_view_from_jwt(jwt, context).await?; check_private_instance(&Some(local_user.clone()), &site_view.local_site)?; @@ -370,8 +382,10 @@ async fn get_feed_front( } #[tracing::instrument(skip_all)] -async fn get_feed_inbox(context: &LemmyContext, jwt: &str) -> Result { - let site_view = SiteView::read_local(&mut context.pool()).await?; +async fn get_feed_inbox(context: &LemmyContext, jwt: &str) -> LemmyResult { + let site_view = SiteView::read_local(&mut context.pool()) + .await? + .ok_or(LemmyErrorType::LocalSiteNotSetup)?; let local_user = local_user_view_from_jwt(jwt, context).await?; let person_id = local_user.local_user.person_id; let show_bot_accounts = local_user.local_user.show_bot_accounts; @@ -425,7 +439,7 @@ fn create_reply_and_mention_items( replies: Vec, mentions: Vec, protocol_and_hostname: &str, -) -> Result, LemmyError> { +) -> LemmyResult> { let mut reply_items: Vec = replies .iter() .map(|r| { @@ -438,7 +452,7 @@ fn create_reply_and_mention_items( protocol_and_hostname, ) }) - .collect::, LemmyError>>()?; + .collect::>>()?; let mut mention_items: Vec = mentions .iter() @@ -452,7 +466,7 @@ fn create_reply_and_mention_items( protocol_and_hostname, ) }) - .collect::, LemmyError>>()?; + .collect::>>()?; reply_items.append(&mut mention_items); Ok(reply_items) @@ -465,7 +479,7 @@ fn build_item( url: &str, content: &str, protocol_and_hostname: &str, -) -> Result { +) -> LemmyResult { // TODO add images let author_url = format!("{protocol_and_hostname}/u/{creator_name}"); let guid = Some(Guid { @@ -489,10 +503,7 @@ fn build_item( } #[tracing::instrument(skip_all)] -fn create_post_items( - posts: Vec, - protocol_and_hostname: &str, -) -> Result, LemmyError> { +fn create_post_items(posts: Vec, protocol_and_hostname: &str) -> LemmyResult> { let mut items: Vec = Vec::new(); for p in posts { diff --git a/crates/routes/src/lib.rs b/crates/routes/src/lib.rs index ec28fda45..4f8d60246 100644 --- a/crates/routes/src/lib.rs +++ b/crates/routes/src/lib.rs @@ -1,6 +1,6 @@ use lemmy_api_common::{claims::Claims, context::LemmyContext, utils::check_user_valid}; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::LemmyError; +use lemmy_utils::{error::LemmyResult, LemmyErrorType}; pub mod feeds; pub mod images; @@ -8,12 +8,11 @@ pub mod nodeinfo; pub mod webfinger; #[tracing::instrument(skip_all)] -async fn local_user_view_from_jwt( - jwt: &str, - context: &LemmyContext, -) -> Result { +async fn local_user_view_from_jwt(jwt: &str, context: &LemmyContext) -> LemmyResult { let local_user_id = Claims::validate(jwt, context).await?; - let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?; + let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id) + .await? + .ok_or(LemmyErrorType::CouldntFindLocalUser)?; check_user_valid(&local_user_view.person)?; Ok(local_user_view) diff --git a/crates/routes/src/nodeinfo.rs b/crates/routes/src/nodeinfo.rs index 62082b4c8..dbb2ef68a 100644 --- a/crates/routes/src/nodeinfo.rs +++ b/crates/routes/src/nodeinfo.rs @@ -5,7 +5,7 @@ use lemmy_db_schema::RegistrationMode; use lemmy_db_views::structs::SiteView; use lemmy_utils::{ cache_header::{cache_1hour, cache_3days}, - error::LemmyError, + error::{LemmyError, LemmyResult}, VERSION, }; use serde::{Deserialize, Serialize}; @@ -24,9 +24,7 @@ pub fn config(cfg: &mut web::ServiceConfig) { ); } -async fn node_info_well_known( - context: web::Data, -) -> Result { +async fn node_info_well_known(context: web::Data) -> LemmyResult { let node_info = NodeInfoWellKnown { links: vec![NodeInfoWellKnownLinks { rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.0")?, @@ -42,7 +40,8 @@ async fn node_info_well_known( async fn node_info(context: web::Data) -> Result { let site_view = SiteView::read_local(&mut context.pool()) .await - .map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?; + .map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))? + .ok_or(ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?; let protocols = if site_view.local_site.federation_enabled { Some(vec!["activitypub".to_string()]) diff --git a/crates/routes/src/webfinger.rs b/crates/routes/src/webfinger.rs index 95092b244..6c630df4c 100644 --- a/crates/routes/src/webfinger.rs +++ b/crates/routes/src/webfinger.rs @@ -9,7 +9,7 @@ use lemmy_db_schema::{ traits::ApubActor, CommunityVisibility, }; -use lemmy_utils::{cache_header::cache_3days, error::LemmyError}; +use lemmy_utils::{cache_header::cache_3days, error::LemmyResult}; use serde::Deserialize; use std::collections::HashMap; use url::Url; @@ -35,7 +35,7 @@ pub fn config(cfg: &mut web::ServiceConfig) { async fn get_webfinger_response( info: Query, context: Data, -) -> Result { +) -> LemmyResult { let name = extract_webfinger_name(&info.resource, &context)?; let links = if name == context.settings().hostname { @@ -47,10 +47,12 @@ async fn get_webfinger_response( let user_id: Option = Person::read_from_name(&mut context.pool(), name, false) .await .ok() + .flatten() .map(|c| c.actor_id.into()); let community_id: Option = Community::read_from_name(&mut context.pool(), name, false) .await .ok() + .flatten() .and_then(|c| { if c.visibility == CommunityVisibility::Public { let id: Url = c.actor_id.into(); diff --git a/crates/utils/src/email.rs b/crates/utils/src/email.rs index 1a786b0ef..7bac7ad67 100644 --- a/crates/utils/src/email.rs +++ b/crates/utils/src/email.rs @@ -1,5 +1,5 @@ use crate::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, settings::structs::Settings, }; use html2text; @@ -25,7 +25,7 @@ pub async fn send_email( to_username: &str, html: &str, settings: &Settings, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let email_config = settings.email.clone().ok_or(LemmyErrorType::NoEmailSetup)?; let domain = settings.hostname.clone(); diff --git a/crates/utils/src/error.rs b/crates/utils/src/error.rs index 61bc5091b..468c15b76 100644 --- a/crates/utils/src/error.rs +++ b/crates/utils/src/error.rs @@ -43,6 +43,16 @@ pub enum LemmyErrorType { BannedFromCommunity, CouldntFindCommunity, CouldntFindPerson, + CouldntFindComment, + CouldntFindCommentReport, + CouldntFindPostReport, + CouldntFindPrivateMessageReport, + CouldntFindLocalUser, + CouldntFindPersonMention, + CouldntFindRegistrationApplication, + CouldntFindCommentReply, + CouldntFindPrivateMessage, + CouldntFindActivity, PersonIsBlocked, CommunityIsBlocked, InstanceIsBlocked, @@ -92,6 +102,7 @@ pub enum LemmyErrorType { PageDoesNotSpecifyGroup, NoCommunityFoundInCc, NoEmailSetup, + LocalSiteNotSetup, EmailSmtpServerNeedsAPort, MissingAnEmail, RateLimitError, @@ -245,11 +256,11 @@ cfg_if! { } pub trait LemmyErrorExt> { - fn with_lemmy_type(self, error_type: LemmyErrorType) -> Result; + fn with_lemmy_type(self, error_type: LemmyErrorType) -> LemmyResult; } impl> LemmyErrorExt for Result { - fn with_lemmy_type(self, error_type: LemmyErrorType) -> Result { + fn with_lemmy_type(self, error_type: LemmyErrorType) -> LemmyResult { self.map_err(|error| LemmyError { error_type, inner: error.into(), @@ -258,12 +269,12 @@ cfg_if! { } } pub trait LemmyErrorExt2 { - fn with_lemmy_type(self, error_type: LemmyErrorType) -> Result; + fn with_lemmy_type(self, error_type: LemmyErrorType) -> LemmyResult; fn into_anyhow(self) -> Result; } - impl LemmyErrorExt2 for Result { - fn with_lemmy_type(self, error_type: LemmyErrorType) -> Result { + impl LemmyErrorExt2 for LemmyResult { + fn with_lemmy_type(self, error_type: LemmyErrorType) -> LemmyResult { self.map_err(|mut e| { e.error_type = error_type; e diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 46508eb50..95c1d0144 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -24,9 +24,11 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const REQWEST_TIMEOUT: Duration = Duration::from_secs(10); #[cfg(debug_assertions)] -pub const CACHE_DURATION_SHORT: Duration = Duration::from_millis(500); +pub const CACHE_DURATION_FEDERATION: Duration = Duration::from_millis(500); #[cfg(not(debug_assertions))] -pub const CACHE_DURATION_SHORT: Duration = Duration::from_secs(60); +pub const CACHE_DURATION_FEDERATION: Duration = Duration::from_secs(60); + +pub const CACHE_DURATION_API: Duration = Duration::from_secs(1); #[macro_export] macro_rules! location_info { diff --git a/crates/utils/src/settings/mod.rs b/crates/utils/src/settings/mod.rs index d0f802e5b..6efa3fdd3 100644 --- a/crates/utils/src/settings/mod.rs +++ b/crates/utils/src/settings/mod.rs @@ -1,4 +1,4 @@ -use crate::{error::LemmyError, location_info}; +use crate::{error::LemmyResult, location_info}; use anyhow::{anyhow, Context}; use deser_hjson::from_str; use once_cell::sync::Lazy; @@ -38,7 +38,7 @@ impl Settings { /// Note: The env var `LEMMY_DATABASE_URL` is parsed in /// `lemmy_db_schema/src/lib.rs::get_database_url_from_env()` /// Warning: Only call this once. - pub(crate) fn init() -> Result { + pub(crate) fn init() -> LemmyResult { let config = from_str::(&Self::read_config_file()?)?; if config.hostname == "unset" { Err(anyhow!("Hostname variable is not set!").into()) @@ -108,7 +108,7 @@ impl Settings { WEBFINGER_REGEX.clone() } - pub fn pictrs_config(&self) -> Result { + pub fn pictrs_config(&self) -> LemmyResult { self .pictrs .clone() diff --git a/crates/utils/src/utils/slurs.rs b/crates/utils/src/utils/slurs.rs index e379ae439..c7c8a88e3 100644 --- a/crates/utils/src/utils/slurs.rs +++ b/crates/utils/src/utils/slurs.rs @@ -1,4 +1,4 @@ -use crate::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; +use crate::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use regex::{Regex, RegexBuilder}; pub fn remove_slurs(test: &str, slur_regex: &Option) -> String { @@ -39,7 +39,7 @@ pub fn build_slur_regex(regex_str: Option<&str>) -> Option { }) } -pub fn check_slurs(text: &str, slur_regex: &Option) -> Result<(), LemmyError> { +pub fn check_slurs(text: &str, slur_regex: &Option) -> LemmyResult<()> { if let Err(slurs) = slur_check(text, slur_regex) { Err(anyhow::anyhow!("{}", slurs_vec_to_str(&slurs))).with_lemmy_type(LemmyErrorType::Slurs) } else { @@ -47,10 +47,7 @@ pub fn check_slurs(text: &str, slur_regex: &Option) -> Result<(), LemmyEr } } -pub fn check_slurs_opt( - text: &Option, - slur_regex: &Option, -) -> Result<(), LemmyError> { +pub fn check_slurs_opt(text: &Option, slur_regex: &Option) -> LemmyResult<()> { match text { Some(t) => check_slurs(t, slur_regex), None => Ok(()), diff --git a/crates/utils/translations b/crates/utils/translations index b3131d688..3d268fbca 160000 --- a/crates/utils/translations +++ b/crates/utils/translations @@ -1 +1 @@ -Subproject commit b3131d6881adb639dc0e298cc7c213c5245091f6 +Subproject commit 3d268fbca717648b1153094ba7eac931791a4fef diff --git a/migrations/2024-04-05-153647_alter_vote_display_mode_defaults/down.sql b/migrations/2024-04-05-153647_alter_vote_display_mode_defaults/down.sql new file mode 100644 index 000000000..484cbef5b --- /dev/null +++ b/migrations/2024-04-05-153647_alter_vote_display_mode_defaults/down.sql @@ -0,0 +1,10 @@ +ALTER TABLE local_user_vote_display_mode + DROP COLUMN score, + ADD COLUMN score boolean DEFAULT TRUE NOT NULL, + DROP COLUMN upvotes, + ADD COLUMN upvotes boolean DEFAULT FALSE NOT NULL, + DROP COLUMN downvotes, + ADD COLUMN downvotes boolean DEFAULT FALSE NOT NULL, + DROP COLUMN upvote_percentage, + ADD COLUMN upvote_percentage boolean DEFAULT TRUE NOT NULL; + diff --git a/migrations/2024-04-05-153647_alter_vote_display_mode_defaults/up.sql b/migrations/2024-04-05-153647_alter_vote_display_mode_defaults/up.sql new file mode 100644 index 000000000..39a01eb07 --- /dev/null +++ b/migrations/2024-04-05-153647_alter_vote_display_mode_defaults/up.sql @@ -0,0 +1,14 @@ +-- Based on a poll, update the local_user_vote_display_mode defaults to: +-- Upvotes + Downvotes +-- Rather than +-- Score + upvote_percentage +ALTER TABLE local_user_vote_display_mode + DROP COLUMN score, + ADD COLUMN score boolean DEFAULT FALSE NOT NULL, + DROP COLUMN upvotes, + ADD COLUMN upvotes boolean DEFAULT TRUE NOT NULL, + DROP COLUMN downvotes, + ADD COLUMN downvotes boolean DEFAULT TRUE NOT NULL, + DROP COLUMN upvote_percentage, + ADD COLUMN upvote_percentage boolean DEFAULT FALSE NOT NULL; + diff --git a/scripts/test-with-coverage.sh b/scripts/test-with-coverage.sh new file mode 100755 index 000000000..e4dfcddf8 --- /dev/null +++ b/scripts/test-with-coverage.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -e + +CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" + +cd $CWD/../ + +PACKAGE="$1" +echo "$PACKAGE" + +source scripts/start_dev_db.sh + +# tests are executed in working directory crates/api (or similar), +# so to load the config we need to traverse to the repo root +export LEMMY_CONFIG_LOCATION=../../config/config.hjson +export RUST_BACKTRACE=1 + +cargo install cargo-llvm-cov + +# Create lcov.info file, which is used by things like the Coverage Gutters extension for VS Code +cargo llvm-cov --workspace --all-features --no-fail-fast --lcov --output-path target/lcov.info + +# Add this to do printlns: -- --nocapture + +pg_ctl stop --silent +rm -rf $PGDATA diff --git a/scripts/test.sh b/scripts/test.sh index efe9b1513..3e0581fc7 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -6,7 +6,7 @@ CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" cd $CWD/../ PACKAGE="$1" -echo "$PACKAGE" +TEST="$2" source scripts/start_dev_db.sh @@ -17,7 +17,7 @@ export RUST_BACKTRACE=1 if [ -n "$PACKAGE" ]; then - cargo test -p $PACKAGE --all-features --no-fail-fast + cargo test -p $PACKAGE --all-features --no-fail-fast $TEST else cargo test --workspace --no-fail-fast fi diff --git a/scripts/upgrade_deps.sh b/scripts/upgrade_deps.sh index 0c4ae6f0f..b20e9f35c 100755 --- a/scripts/upgrade_deps.sh +++ b/scripts/upgrade_deps.sh @@ -6,9 +6,9 @@ pushd ../ cargo udeps --all-targets # Upgrade deps -cargo upgrade --workspace +cargo upgrade -# Run check -cargo check +# Run clippy +cargo clippy popd diff --git a/src/code_migrations.rs b/src/code_migrations.rs index cee02075c..05b564f47 100644 --- a/src/code_migrations.rs +++ b/src/code_migrations.rs @@ -34,14 +34,14 @@ use lemmy_db_schema::{ traits::Crud, utils::{get_conn, naive_now, DbPool}, }; -use lemmy_utils::{error::LemmyError, settings::structs::Settings}; +use lemmy_utils::{error::LemmyResult, settings::structs::Settings}; use tracing::info; use url::Url; pub async fn run_advanced_migrations( pool: &mut DbPool<'_>, settings: &Settings, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let protocol_and_hostname = &settings.get_protocol_and_hostname(); user_updates_2020_04_02(pool, protocol_and_hostname).await?; community_updates_2020_04_02(pool, protocol_and_hostname).await?; @@ -60,7 +60,7 @@ pub async fn run_advanced_migrations( async fn user_updates_2020_04_02( pool: &mut DbPool<'_>, protocol_and_hostname: &str, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { use lemmy_db_schema::schema::person::dsl::{actor_id, local, person}; let conn = &mut get_conn(pool).await?; @@ -99,7 +99,7 @@ async fn user_updates_2020_04_02( async fn community_updates_2020_04_02( pool: &mut DbPool<'_>, protocol_and_hostname: &str, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { use lemmy_db_schema::schema::community::dsl::{actor_id, community, local}; let conn = &mut get_conn(pool).await?; @@ -139,7 +139,7 @@ async fn community_updates_2020_04_02( async fn post_updates_2020_04_03( pool: &mut DbPool<'_>, protocol_and_hostname: &str, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { use lemmy_db_schema::schema::post::dsl::{ap_id, local, post}; let conn = &mut get_conn(pool).await?; @@ -177,7 +177,7 @@ async fn post_updates_2020_04_03( async fn comment_updates_2020_04_03( pool: &mut DbPool<'_>, protocol_and_hostname: &str, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { use lemmy_db_schema::schema::comment::dsl::{ap_id, comment, local}; let conn = &mut get_conn(pool).await?; @@ -215,7 +215,7 @@ async fn comment_updates_2020_04_03( async fn private_message_updates_2020_05_05( pool: &mut DbPool<'_>, protocol_and_hostname: &str, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { use lemmy_db_schema::schema::private_message::dsl::{ap_id, local, private_message}; let conn = &mut get_conn(pool).await?; @@ -253,7 +253,7 @@ async fn private_message_updates_2020_05_05( async fn post_thumbnail_url_updates_2020_07_27( pool: &mut DbPool<'_>, protocol_and_hostname: &str, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { use lemmy_db_schema::schema::post::dsl::{post, thumbnail_url}; let conn = &mut get_conn(pool).await?; @@ -282,10 +282,7 @@ async fn post_thumbnail_url_updates_2020_07_27( /// We are setting inbox and follower URLs for local and remote actors alike, because for now /// all federated instances are also Lemmy and use the same URL scheme. -async fn apub_columns_2021_02_02( - pool: &mut DbPool<'_>, - settings: &Settings, -) -> Result<(), LemmyError> { +async fn apub_columns_2021_02_02(pool: &mut DbPool<'_>, settings: &Settings) -> LemmyResult<()> { let conn = &mut get_conn(pool).await?; info!("Running apub_columns_2021_02_02"); { @@ -346,9 +343,9 @@ async fn instance_actor_2022_01_28( pool: &mut DbPool<'_>, protocol_and_hostname: &str, settings: &Settings, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { info!("Running instance_actor_2021_09_29"); - if let Ok(site_view) = SiteView::read_local(pool).await { + if let Ok(Some(site_view)) = SiteView::read_local(pool).await { let site = site_view.site; // if site already has public key, we dont need to do anything here if !site.public_key.is_empty() { @@ -374,7 +371,7 @@ async fn instance_actor_2022_01_28( /// key field is empty, generate a new keypair. It would be possible to regenerate only the pubkey, /// but thats more complicated and has no benefit, as federation is already broken for these actors. /// https://github.com/LemmyNet/lemmy/issues/2347 -async fn regenerate_public_keys_2022_07_05(pool: &mut DbPool<'_>) -> Result<(), LemmyError> { +async fn regenerate_public_keys_2022_07_05(pool: &mut DbPool<'_>) -> LemmyResult<()> { let conn = &mut get_conn(pool).await?; info!("Running regenerate_public_keys_2022_07_05"); @@ -433,7 +430,7 @@ async fn regenerate_public_keys_2022_07_05(pool: &mut DbPool<'_>) -> Result<(), async fn initialize_local_site_2022_10_10( pool: &mut DbPool<'_>, settings: &Settings, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { info!("Running initialize_local_site_2022_10_10"); // Check to see if local_site exists diff --git a/src/lib.rs b/src/lib.rs index 777c5d9ce..12a0f4a7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,6 @@ use actix_web::{ App, HttpResponse, HttpServer, - Result, }; use actix_web_prom::PrometheusMetricsBuilder; use clap::Parser; @@ -45,7 +44,7 @@ use lemmy_db_schema::{source::secret::Secret, utils::build_db_pool}; use lemmy_federate::{start_stop_federation_workers_cancellable, Opts}; use lemmy_routes::{feeds, images, nodeinfo, webfinger}; use lemmy_utils::{ - error::LemmyError, + error::LemmyResult, rate_limit::RateLimitCell, response::jsonify_plain_text_errors, settings::{structs::Settings, SETTINGS}, @@ -107,7 +106,7 @@ pub struct CmdArgs { } /// Placing the main function in lib.rs allows other crates to import it and embed Lemmy -pub async fn start_lemmy_server(args: CmdArgs) -> Result<(), LemmyError> { +pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> { // Print version number to log println!("Lemmy v{VERSION}"); @@ -125,12 +124,12 @@ pub async fn start_lemmy_server(args: CmdArgs) -> Result<(), LemmyError> { // Initialize the secrets let secret = Secret::init(&mut (&pool).into()) - .await + .await? .expect("Couldn't initialize secrets."); // Make sure the local site is set up. let site_view = SiteView::read_local(&mut (&pool).into()) - .await + .await? .expect("local site not set up"); let local_site = site_view.local_site; let federation_enabled = local_site.federation_enabled; @@ -244,7 +243,7 @@ pub async fn start_lemmy_server(args: CmdArgs) -> Result<(), LemmyError> { } /// Creates temporary HTTP server which returns status 503 for all requests. -fn create_startup_server() -> Result { +fn create_startup_server() -> LemmyResult { let startup_server = HttpServer::new(move || { App::new().wrap(ErrorHandlers::new().default_handler(move |req| { let (req, _) = req.into_parts(); @@ -267,7 +266,7 @@ fn create_http_server( federation_config: FederationConfig, settings: Settings, federation_enabled: bool, -) -> Result { +) -> LemmyResult { // this must come before the HttpServer creation // creates a middleware that populates http metrics for each path, method, and status code let prom_api_metrics = PrometheusMetricsBuilder::new("lemmy_api") @@ -349,7 +348,7 @@ fn cors_config(settings: &Settings) -> Cors { } } -pub fn init_logging(opentelemetry_url: &Option) -> Result<(), LemmyError> { +pub fn init_logging(opentelemetry_url: &Option) -> LemmyResult<()> { LogTracer::init()?; let log_description = env::var("RUST_LOG").unwrap_or_else(|_| "info".into()); diff --git a/src/main.rs b/src/main.rs index 1544bcecf..dd17d6eb8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ use clap::Parser; use lemmy_server::{init_logging, start_lemmy_server, CmdArgs}; -use lemmy_utils::{error::LemmyError, settings::SETTINGS}; +use lemmy_utils::{error::LemmyResult, settings::SETTINGS}; #[tokio::main] -pub async fn main() -> Result<(), LemmyError> { +pub async fn main() -> LemmyResult<()> { init_logging(&SETTINGS.opentelemetry_url)?; let args = CmdArgs::parse(); diff --git a/src/scheduled_tasks.rs b/src/scheduled_tasks.rs index e225b96ff..8c4cf9311 100644 --- a/src/scheduled_tasks.rs +++ b/src/scheduled_tasks.rs @@ -29,13 +29,13 @@ use lemmy_db_schema::{ utils::{get_conn, naive_now, now, DbPool, DELETED_REPLACEMENT_TEXT}, }; use lemmy_routes::nodeinfo::NodeInfo; -use lemmy_utils::error::{LemmyError, LemmyResult}; +use lemmy_utils::error::LemmyResult; use reqwest_middleware::ClientWithMiddleware; use std::time::Duration; use tracing::{error, info, warn}; /// Schedules various cleanup tasks for lemmy in a background thread -pub async fn setup(context: LemmyContext) -> Result<(), LemmyError> { +pub async fn setup(context: LemmyContext) -> LemmyResult<()> { // Setup the connections let mut scheduler = AsyncScheduler::new(); startup_jobs(&mut context.pool()).await; diff --git a/src/session_middleware.rs b/src/session_middleware.rs index 2bee64ca0..f4535249c 100644 --- a/src/session_middleware.rs +++ b/src/session_middleware.rs @@ -130,7 +130,7 @@ mod tests { let pool_ = build_db_pool_for_tests().await; let pool = &mut (&pool_).into(); - let secret = Secret::init(pool).await.unwrap(); + let secret = Secret::init(pool).await.unwrap().unwrap(); let context = LemmyContext::create( pool_.clone(), ClientBuilder::new(Client::default()).build(), diff --git a/src/telemetry.rs b/src/telemetry.rs index 2f758e062..a7a1ac809 100644 --- a/src/telemetry.rs +++ b/src/telemetry.rs @@ -1,5 +1,5 @@ use console_subscriber::ConsoleLayer; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::LemmyResult; use opentelemetry::{ sdk::{propagation::TraceContextPropagator, Resource}, KeyValue, @@ -8,11 +8,7 @@ use opentelemetry_otlp::WithExportConfig; use tracing::{subscriber::set_global_default, Subscriber}; use tracing_subscriber::{filter::Targets, layer::SubscriberExt, registry::LookupSpan, Layer}; -pub fn init_tracing( - opentelemetry_url: &str, - subscriber: S, - targets: Targets, -) -> Result<(), LemmyError> +pub fn init_tracing(opentelemetry_url: &str, subscriber: S, targets: Targets) -> LemmyResult<()> where S: Subscriber + for<'a> LookupSpan<'a> + Send + Sync + 'static, {