mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-31 23:31:34 +00:00
Merge remote-tracking branch 'upstream/main' into migration-runner
This commit is contained in:
commit
7734ba47a1
106 changed files with 2012 additions and 1586 deletions
|
@ -2,7 +2,7 @@
|
||||||
# See https://github.com/woodpecker-ci/woodpecker/issues/1677
|
# See https://github.com/woodpecker-ci/woodpecker/issues/1677
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
- &rust_image "rust:1.77"
|
- &rust_image "rust:1.78"
|
||||||
- &rust_nightly_image "rustlang/rust:nightly"
|
- &rust_nightly_image "rustlang/rust:nightly"
|
||||||
- &install_pnpm "corepack enable pnpm"
|
- &install_pnpm "corepack enable pnpm"
|
||||||
- &slow_check_paths
|
- &slow_check_paths
|
||||||
|
|
382
Cargo.lock
generated
382
Cargo.lock
generated
|
@ -250,11 +250,9 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.23.4",
|
|
||||||
"tokio-rustls 0.26.0",
|
"tokio-rustls 0.26.0",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
"webpki-roots 0.22.6",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -980,9 +978,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.4"
|
version = "4.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -990,9 +988,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.2"
|
version = "4.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
@ -1002,9 +1000,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.4"
|
version = "4.5.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
|
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -1110,18 +1108,6 @@ dependencies = [
|
||||||
"yaml-rust",
|
"yaml-rust",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "console-api"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c2895653b4d9f1538a83970077cb01dfc77a4810524e51a110944688e916b18e"
|
|
||||||
dependencies = [
|
|
||||||
"prost 0.11.9",
|
|
||||||
"prost-types 0.11.9",
|
|
||||||
"tonic 0.9.2",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console-api"
|
name = "console-api"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -1130,33 +1116,22 @@ checksum = "fd326812b3fd01da5bb1af7d340d0d555fd3d4b641e7f1dfcf5962a902952787"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"prost 0.12.6",
|
"prost 0.12.6",
|
||||||
"prost-types 0.12.6",
|
"prost-types",
|
||||||
"tonic 0.10.2",
|
"tonic 0.10.2",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console-subscriber"
|
name = "console-api"
|
||||||
version = "0.1.10"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4cf42660ac07fcebed809cfe561dd8730bcd35b075215e6479c516bcd0d11cb"
|
checksum = "a257c22cd7e487dd4a13d413beabc512c5052f0bc048db0da6a84c3d8a6142fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"console-api 0.5.0",
|
"futures-core",
|
||||||
"crossbeam-channel",
|
"prost 0.12.6",
|
||||||
"crossbeam-utils",
|
"prost-types",
|
||||||
"futures",
|
"tonic 0.11.0",
|
||||||
"hdrhistogram",
|
|
||||||
"humantime",
|
|
||||||
"prost-types 0.11.9",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thread_local",
|
|
||||||
"tokio",
|
|
||||||
"tokio-stream",
|
|
||||||
"tonic 0.9.2",
|
|
||||||
"tracing",
|
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-subscriber",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1171,7 +1146,7 @@ dependencies = [
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"hdrhistogram",
|
"hdrhistogram",
|
||||||
"humantime",
|
"humantime",
|
||||||
"prost-types 0.12.6",
|
"prost-types",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
|
@ -1183,6 +1158,31 @@ dependencies = [
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console-subscriber"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "31c4cc54bae66f7d9188996404abdf7fdfa23034ef8e43478c8810828abad758"
|
||||||
|
dependencies = [
|
||||||
|
"console-api 0.7.0",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"futures-task",
|
||||||
|
"hdrhistogram",
|
||||||
|
"humantime",
|
||||||
|
"prost 0.12.6",
|
||||||
|
"prost-types",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thread_local",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tonic 0.11.0",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
|
@ -1461,6 +1461,17 @@ dependencies = [
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive-new"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.65",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_builder"
|
name = "derive_builder"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
|
@ -2711,7 +2722,7 @@ dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"pem",
|
"pem",
|
||||||
"ring 0.17.8",
|
"ring",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"simple_asn1",
|
"simple_asn1",
|
||||||
|
@ -2737,7 +2748,7 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_api"
|
name = "lemmy_api"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -2748,6 +2759,7 @@ dependencies = [
|
||||||
"captcha",
|
"captcha",
|
||||||
"chrono",
|
"chrono",
|
||||||
"elementtree",
|
"elementtree",
|
||||||
|
"hound",
|
||||||
"lemmy_api_common",
|
"lemmy_api_common",
|
||||||
"lemmy_db_schema",
|
"lemmy_db_schema",
|
||||||
"lemmy_db_views",
|
"lemmy_db_views",
|
||||||
|
@ -2761,12 +2773,11 @@ dependencies = [
|
||||||
"totp-rs",
|
"totp-rs",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"wav",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_api_common"
|
name = "lemmy_api_common"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -2804,7 +2815,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_api_crud"
|
name = "lemmy_api_crud"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accept-language",
|
"accept-language",
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
|
@ -2827,7 +2838,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_apub"
|
name = "lemmy_apub"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -2865,7 +2876,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_db_perf"
|
name = "lemmy_db_perf"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
@ -2880,7 +2891,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_db_schema"
|
name = "lemmy_db_schema"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -2888,6 +2899,7 @@ dependencies = [
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
"chrono",
|
"chrono",
|
||||||
"deadpool 0.12.1",
|
"deadpool 0.12.1",
|
||||||
|
"derive-new",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel-async",
|
"diesel-async",
|
||||||
"diesel-derive-enum",
|
"diesel-derive-enum",
|
||||||
|
@ -2902,7 +2914,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"regex",
|
"regex",
|
||||||
"rustls 0.23.8",
|
"rustls 0.23.10",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
|
@ -2921,7 +2933,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_db_views"
|
name = "lemmy_db_views"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -2943,12 +2955,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_db_views_actor"
|
name = "lemmy_db_views_actor"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel-async",
|
"diesel-async",
|
||||||
"lemmy_db_schema",
|
"lemmy_db_schema",
|
||||||
|
"lemmy_db_views",
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -2963,7 +2976,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_db_views_moderator"
|
name = "lemmy_db_views_moderator"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel-async",
|
"diesel-async",
|
||||||
|
@ -2975,7 +2988,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_federate"
|
name = "lemmy_federate"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -3000,7 +3013,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_routes"
|
name = "lemmy_routes"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -3025,7 +3038,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_server"
|
name = "lemmy_server"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"actix-cors",
|
"actix-cors",
|
||||||
|
@ -3034,7 +3047,7 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"clokwerk",
|
"clokwerk",
|
||||||
"console-subscriber 0.1.10",
|
"console-subscriber 0.3.0",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel-async",
|
"diesel-async",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
@ -3068,7 +3081,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_utils"
|
name = "lemmy_utils"
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -3353,9 +3366,9 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "metrics"
|
name = "metrics"
|
||||||
version = "0.22.3"
|
version = "0.23.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2be3cbd384d4e955b231c895ce10685e3d8260c5ccffae898c96c723b0772835"
|
checksum = "884adb57038347dfbaf2d5065887b6cf4312330dc8e94bc30a1a839bd79d3261"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
|
@ -3363,9 +3376,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "metrics-exporter-prometheus"
|
name = "metrics-exporter-prometheus"
|
||||||
version = "0.14.0"
|
version = "0.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d58e362dc7206e9456ddbcdbd53c71ba441020e62104703075a69151e38d85f"
|
checksum = "bf0af7a0d7ced10c0151f870e5e3f3f8bc9ffc5992d32873566ca1f9169ae776"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
|
@ -3383,9 +3396,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "metrics-util"
|
name = "metrics-util"
|
||||||
version = "0.16.3"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b07a5eb561b8cbc16be2d216faf7757f9baf3bfb94dbb0fae3df8387a5bb47f"
|
checksum = "4259040465c955f9f2f1a4a8a16dc46726169bca0f88e8fb2dbeced487c3e828"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-epoch",
|
"crossbeam-epoch",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
|
@ -3685,9 +3698,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry"
|
name = "opentelemetry"
|
||||||
version = "0.22.0"
|
version = "0.23.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "900d57987be3f2aeb70d385fff9b27fb74c5723cc9a52d904d4f9c807a0667bf"
|
checksum = "1b69a91d4893e713e06f724597ad630f1fa76057a5e1026c0ca67054a9032a76"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
|
@ -3695,7 +3708,6 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"urlencoding",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3718,17 +3730,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-otlp"
|
name = "opentelemetry-otlp"
|
||||||
version = "0.15.0"
|
version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a016b8d9495c639af2145ac22387dcb88e44118e45320d9238fbf4e7889abcb"
|
checksum = "a94c69209c05319cdf7460c6d4c055ed102be242a0a6245835d7bc42c6ec7f54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
"opentelemetry 0.22.0",
|
"opentelemetry 0.23.0",
|
||||||
"opentelemetry-proto 0.5.0",
|
"opentelemetry-proto 0.6.0",
|
||||||
"opentelemetry-semantic-conventions",
|
"opentelemetry_sdk 0.23.0",
|
||||||
"opentelemetry_sdk 0.22.1",
|
|
||||||
"prost 0.12.6",
|
"prost 0.12.6",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -3750,22 +3761,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-proto"
|
name = "opentelemetry-proto"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a8fddc9b68f5b80dae9d6f510b88e02396f006ad48cac349411fbecc80caae4"
|
checksum = "984806e6cf27f2b49282e2a05e288f30594f3dbc74eb7a6e99422bc48ed78162"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"opentelemetry 0.22.0",
|
"opentelemetry 0.23.0",
|
||||||
"opentelemetry_sdk 0.22.1",
|
"opentelemetry_sdk 0.23.0",
|
||||||
"prost 0.12.6",
|
"prost 0.12.6",
|
||||||
"tonic 0.11.0",
|
"tonic 0.11.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "opentelemetry-semantic-conventions"
|
|
||||||
version = "0.14.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f9ab5bd6c42fb9349dcf28af2ba9a0667f697f9bdcca045d39f2cec5543e2910"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry_api"
|
name = "opentelemetry_api"
|
||||||
version = "0.19.0"
|
version = "0.19.0"
|
||||||
|
@ -3806,18 +3811,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry_sdk"
|
name = "opentelemetry_sdk"
|
||||||
version = "0.22.1"
|
version = "0.23.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e90c7113be649e31e9a0f8b5ee24ed7a16923b322c3c5ab6367469c049d6b7e"
|
checksum = "ae312d58eaa90a82d2e627fd86e075cf5230b3f11794e2ed74199ebbe572d4fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"crossbeam-channel",
|
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-executor",
|
"futures-executor",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"glob",
|
"glob",
|
||||||
|
"lazy_static",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"opentelemetry 0.22.0",
|
"opentelemetry 0.23.0",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -4016,9 +4021,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pict-rs"
|
name = "pict-rs"
|
||||||
version = "0.5.14"
|
version = "0.5.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "27cb4fd629bb8a115b8ae2701e2c355f6e5e9ad6bf22d9d0e7b7b645fb0f81dc"
|
checksum = "5bbee61836cce10f7cf733196b7c0701e7ea6d0b617da68a3e6e4311b6262c2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-form-data",
|
"actix-form-data",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -4041,15 +4046,15 @@ dependencies = [
|
||||||
"metrics",
|
"metrics",
|
||||||
"metrics-exporter-prometheus",
|
"metrics-exporter-prometheus",
|
||||||
"mime",
|
"mime",
|
||||||
"opentelemetry 0.22.0",
|
"opentelemetry 0.23.0",
|
||||||
"opentelemetry-otlp 0.15.0",
|
"opentelemetry-otlp 0.16.0",
|
||||||
"opentelemetry_sdk 0.22.1",
|
"opentelemetry_sdk 0.23.0",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"refinery",
|
"refinery",
|
||||||
"reqwest 0.12.4",
|
"reqwest 0.12.4",
|
||||||
"reqwest-middleware 0.3.1",
|
"reqwest-middleware 0.3.1",
|
||||||
"reqwest-tracing 0.5.0",
|
"reqwest-tracing 0.5.0",
|
||||||
"rustls 0.23.8",
|
"rustls 0.23.10",
|
||||||
"rustls-channel-resolver",
|
"rustls-channel-resolver",
|
||||||
"rustls-pemfile 2.1.2",
|
"rustls-pemfile 2.1.2",
|
||||||
"rusty-s3",
|
"rusty-s3",
|
||||||
|
@ -4072,11 +4077,11 @@ dependencies = [
|
||||||
"tracing-actix-web",
|
"tracing-actix-web",
|
||||||
"tracing-error",
|
"tracing-error",
|
||||||
"tracing-log 0.2.0",
|
"tracing-log 0.2.0",
|
||||||
"tracing-opentelemetry 0.23.0",
|
"tracing-opentelemetry 0.24.0",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"webpki-roots 0.26.1",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4339,15 +4344,6 @@ dependencies = [
|
||||||
"syn 2.0.65",
|
"syn 2.0.65",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "prost-types"
|
|
||||||
version = "0.11.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13"
|
|
||||||
dependencies = [
|
|
||||||
"prost 0.11.9",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-types"
|
name = "prost-types"
|
||||||
version = "0.12.6"
|
version = "0.12.6"
|
||||||
|
@ -4678,7 +4674,7 @@ dependencies = [
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"wasm-streams",
|
"wasm-streams",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"webpki-roots 0.26.1",
|
"webpki-roots",
|
||||||
"winreg 0.52.0",
|
"winreg 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4761,27 +4757,6 @@ dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "riff"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ring"
|
|
||||||
version = "0.16.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"once_cell",
|
|
||||||
"spin 0.5.2",
|
|
||||||
"untrusted 0.7.1",
|
|
||||||
"web-sys",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.8"
|
version = "0.17.8"
|
||||||
|
@ -4792,7 +4767,7 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"libc",
|
"libc",
|
||||||
"spin 0.9.8",
|
"spin",
|
||||||
"untrusted 0.9.0",
|
"untrusted 0.9.0",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
@ -4875,18 +4850,6 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls"
|
|
||||||
version = "0.20.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"ring 0.16.20",
|
|
||||||
"sct",
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.22.4"
|
version = "0.22.4"
|
||||||
|
@ -4894,7 +4857,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
|
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"ring 0.17.8",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"rustls-webpki",
|
"rustls-webpki",
|
||||||
"subtle",
|
"subtle",
|
||||||
|
@ -4903,14 +4866,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.8"
|
version = "0.23.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79adb16721f56eb2d843e67676896a61ce7a0fa622dc18d3e372477a029d2740"
|
checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ring 0.17.8",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"rustls-webpki",
|
"rustls-webpki",
|
||||||
"subtle",
|
"subtle",
|
||||||
|
@ -4924,7 +4887,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fede2a247359da6b4998f7723ec6468c2d6a577a5d8c17e54f21806426ad2290"
|
checksum = "fede2a247359da6b4998f7723ec6468c2d6a577a5d8c17e54f21806426ad2290"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nanorand",
|
"nanorand",
|
||||||
"rustls 0.23.8",
|
"rustls 0.23.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4959,7 +4922,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e"
|
checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"ring 0.17.8",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"untrusted 0.9.0",
|
"untrusted 0.9.0",
|
||||||
]
|
]
|
||||||
|
@ -5038,16 +5001,6 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sct"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
|
||||||
dependencies = [
|
|
||||||
"ring 0.17.8",
|
|
||||||
"untrusted 0.9.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sdd"
|
name = "sdd"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -5096,9 +5049,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.202"
|
version = "1.0.203"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
|
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
@ -5114,9 +5067,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.202"
|
version = "1.0.203"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
|
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -5373,12 +5326,6 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spin"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
|
@ -5487,11 +5434,11 @@ checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum_macros"
|
name = "strum_macros"
|
||||||
version = "0.26.2"
|
version = "0.26.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.4.1",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
|
@ -5727,9 +5674,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.37.0"
|
version = "1.38.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
|
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -5757,9 +5704,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.2.0"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -5809,7 +5756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8e98c31c29b2666fb28720739e11476166be4ead1610a37dcd7414bb124413a"
|
checksum = "c8e98c31c29b2666fb28720739e11476166be4ead1610a37dcd7414bb124413a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"rustls 0.23.8",
|
"rustls 0.23.10",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-postgres",
|
"tokio-postgres",
|
||||||
"tokio-rustls 0.26.0",
|
"tokio-rustls 0.26.0",
|
||||||
|
@ -5822,25 +5769,14 @@ version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04fb792ccd6bbcd4bba408eb8a292f70fc4a3589e5d793626f45190e6454b6ab"
|
checksum = "04fb792ccd6bbcd4bba408eb8a292f70fc4a3589e5d793626f45190e6454b6ab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring 0.17.8",
|
"ring",
|
||||||
"rustls 0.23.8",
|
"rustls 0.23.10",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-postgres",
|
"tokio-postgres",
|
||||||
"tokio-rustls 0.26.0",
|
"tokio-rustls 0.26.0",
|
||||||
"x509-certificate",
|
"x509-certificate",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-rustls"
|
|
||||||
version = "0.23.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
|
||||||
dependencies = [
|
|
||||||
"rustls 0.20.9",
|
|
||||||
"tokio",
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.25.0"
|
version = "0.25.0"
|
||||||
|
@ -5858,7 +5794,7 @@ version = "0.26.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls 0.23.8",
|
"rustls 0.23.10",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
@ -5978,34 +5914,6 @@ dependencies = [
|
||||||
"tracing-futures",
|
"tracing-futures",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tonic"
|
|
||||||
version = "0.9.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"axum",
|
|
||||||
"base64 0.21.7",
|
|
||||||
"bytes",
|
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
|
||||||
"h2 0.3.26",
|
|
||||||
"http 0.2.12",
|
|
||||||
"http-body 0.4.6",
|
|
||||||
"hyper 0.14.28",
|
|
||||||
"hyper-timeout",
|
|
||||||
"percent-encoding",
|
|
||||||
"pin-project",
|
|
||||||
"prost 0.11.9",
|
|
||||||
"tokio",
|
|
||||||
"tokio-stream",
|
|
||||||
"tower",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tonic"
|
name = "tonic"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
|
@ -6122,16 +6030,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-actix-web"
|
name = "tracing-actix-web"
|
||||||
version = "0.7.10"
|
version = "0.7.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa069bd1503dd526ee793bb3fce408895136c95fc86d2edb2acf1c646d7f0684"
|
checksum = "4ee9e39a66d9b615644893ffc1704d2a89b5b315b7fd0228ad3182ca9a306b19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"mutually_exclusive_features",
|
"mutually_exclusive_features",
|
||||||
"opentelemetry 0.22.0",
|
"opentelemetry 0.23.0",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-opentelemetry 0.23.0",
|
"tracing-opentelemetry 0.24.0",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -6227,14 +6135,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-opentelemetry"
|
name = "tracing-opentelemetry"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9be14ba1bbe4ab79e9229f7f89fab8d120b865859f10527f31c033e599d2284"
|
checksum = "f68803492bf28ab40aeccaecc7021096bd256baf7ca77c3d425d89b35a7be4e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"opentelemetry 0.22.0",
|
"opentelemetry 0.23.0",
|
||||||
"opentelemetry_sdk 0.22.1",
|
"opentelemetry_sdk 0.23.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
|
@ -6571,15 +6479,6 @@ dependencies = [
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wav"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "99d97402f69875b579ec37f2aa52d1f455a1d6224251edba32e8c18a5da2698d"
|
|
||||||
dependencies = [
|
|
||||||
"riff",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.69"
|
version = "0.3.69"
|
||||||
|
@ -6628,25 +6527,6 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webpki"
|
|
||||||
version = "0.22.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
|
|
||||||
dependencies = [
|
|
||||||
"ring 0.17.8",
|
|
||||||
"untrusted 0.9.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webpki-roots"
|
|
||||||
version = "0.22.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
|
|
||||||
dependencies = [
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "0.26.1"
|
version = "0.26.1"
|
||||||
|
@ -6930,7 +6810,7 @@ dependencies = [
|
||||||
"der",
|
"der",
|
||||||
"hex",
|
"hex",
|
||||||
"pem",
|
"pem",
|
||||||
"ring 0.17.8",
|
"ring",
|
||||||
"signature",
|
"signature",
|
||||||
"spki",
|
"spki",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|
43
Cargo.toml
43
Cargo.toml
|
@ -1,5 +1,5 @@
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.19.4-rc.3"
|
version = "0.19.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "A link aggregator for the fediverse"
|
description = "A link aggregator for the fediverse"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
@ -88,35 +88,35 @@ unused_self = "deny"
|
||||||
unwrap_used = "deny"
|
unwrap_used = "deny"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
lemmy_api = { version = "=0.19.4-rc.3", path = "./crates/api" }
|
lemmy_api = { version = "=0.19.5", path = "./crates/api" }
|
||||||
lemmy_api_crud = { version = "=0.19.4-rc.3", path = "./crates/api_crud" }
|
lemmy_api_crud = { version = "=0.19.5", path = "./crates/api_crud" }
|
||||||
lemmy_apub = { version = "=0.19.4-rc.3", path = "./crates/apub" }
|
lemmy_apub = { version = "=0.19.5", path = "./crates/apub" }
|
||||||
lemmy_utils = { version = "=0.19.4-rc.3", path = "./crates/utils", default-features = false }
|
lemmy_utils = { version = "=0.19.5", path = "./crates/utils", default-features = false }
|
||||||
lemmy_db_schema = { version = "=0.19.4-rc.3", path = "./crates/db_schema" }
|
lemmy_db_schema = { version = "=0.19.5", path = "./crates/db_schema" }
|
||||||
lemmy_api_common = { version = "=0.19.4-rc.3", path = "./crates/api_common" }
|
lemmy_api_common = { version = "=0.19.5", path = "./crates/api_common" }
|
||||||
lemmy_routes = { version = "=0.19.4-rc.3", path = "./crates/routes" }
|
lemmy_routes = { version = "=0.19.5", path = "./crates/routes" }
|
||||||
lemmy_db_views = { version = "=0.19.4-rc.3", path = "./crates/db_views" }
|
lemmy_db_views = { version = "=0.19.5", path = "./crates/db_views" }
|
||||||
lemmy_db_views_actor = { version = "=0.19.4-rc.3", path = "./crates/db_views_actor" }
|
lemmy_db_views_actor = { version = "=0.19.5", path = "./crates/db_views_actor" }
|
||||||
lemmy_db_views_moderator = { version = "=0.19.4-rc.3", path = "./crates/db_views_moderator" }
|
lemmy_db_views_moderator = { version = "=0.19.5", path = "./crates/db_views_moderator" }
|
||||||
lemmy_federate = { version = "=0.19.4-rc.3", path = "./crates/federate" }
|
lemmy_federate = { version = "=0.19.5", path = "./crates/federate" }
|
||||||
activitypub_federation = { version = "0.5.6", default-features = false, features = [
|
activitypub_federation = { version = "0.5.6", default-features = false, features = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
] }
|
] }
|
||||||
diesel = "2.1.6"
|
diesel = "2.1.6"
|
||||||
diesel_migrations = "2.1.0"
|
diesel_migrations = "2.1.0"
|
||||||
diesel-async = "0.4.1"
|
diesel-async = "0.4.1"
|
||||||
serde = { version = "1.0.202", features = ["derive"] }
|
serde = { version = "1.0.203", features = ["derive"] }
|
||||||
serde_with = "3.8.1"
|
serde_with = "3.8.1"
|
||||||
actix-web = { version = "4.6.0", default-features = false, features = [
|
actix-web = { version = "4.6.0", default-features = false, features = [
|
||||||
"macros",
|
"macros",
|
||||||
"rustls",
|
"rustls-0_23",
|
||||||
"compress-brotli",
|
"compress-brotli",
|
||||||
"compress-gzip",
|
"compress-gzip",
|
||||||
"compress-zstd",
|
"compress-zstd",
|
||||||
"cookies",
|
"cookies",
|
||||||
] }
|
] }
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tracing-actix-web = { version = "0.7.10", default-features = false }
|
tracing-actix-web = { version = "0.7.11", default-features = false }
|
||||||
tracing-error = "0.2.0"
|
tracing-error = "0.2.0"
|
||||||
tracing-log = "0.2.0"
|
tracing-log = "0.2.0"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||||
|
@ -139,13 +139,13 @@ anyhow = { version = "1.0.86", features = [
|
||||||
diesel_ltree = "0.3.1"
|
diesel_ltree = "0.3.1"
|
||||||
typed-builder = "0.18.2"
|
typed-builder = "0.18.2"
|
||||||
serial_test = "3.1.1"
|
serial_test = "3.1.1"
|
||||||
tokio = { version = "1.37.0", features = ["full"] }
|
tokio = { version = "1.38.0", features = ["full"] }
|
||||||
regex = "1.10.4"
|
regex = "1.10.4"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
diesel-derive-newtype = "2.1.2"
|
diesel-derive-newtype = "2.1.2"
|
||||||
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
|
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
|
||||||
strum = "0.26.2"
|
strum = "0.26.2"
|
||||||
strum_macros = "0.26.2"
|
strum_macros = "0.26.4"
|
||||||
itertools = "0.13.0"
|
itertools = "0.13.0"
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
http = "0.2.12"
|
http = "0.2.12"
|
||||||
|
@ -157,7 +157,7 @@ ts-rs = { version = "7.1.1", features = [
|
||||||
"chrono-impl",
|
"chrono-impl",
|
||||||
"no-serde-warnings",
|
"no-serde-warnings",
|
||||||
] }
|
] }
|
||||||
rustls = { version = "0.23.8", features = ["ring"] }
|
rustls = { version = "0.23.9", features = ["ring"] }
|
||||||
futures-util = "0.3.30"
|
futures-util = "0.3.30"
|
||||||
tokio-postgres = "0.7.10"
|
tokio-postgres = "0.7.10"
|
||||||
tokio-postgres-rustls = "0.12.0"
|
tokio-postgres-rustls = "0.12.0"
|
||||||
|
@ -165,8 +165,9 @@ urlencoding = "2.1.3"
|
||||||
enum-map = "2.7"
|
enum-map = "2.7"
|
||||||
moka = { version = "0.12.7", features = ["future"] }
|
moka = { version = "0.12.7", features = ["future"] }
|
||||||
i-love-jesus = { version = "0.1.0" }
|
i-love-jesus = { version = "0.1.0" }
|
||||||
clap = { version = "4.5.4", features = ["derive", "env"] }
|
clap = { version = "4.5.6", features = ["derive", "env"] }
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
|
derive-new = "0.6.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lemmy_api = { workspace = true }
|
lemmy_api = { workspace = true }
|
||||||
|
@ -194,9 +195,9 @@ clokwerk = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tracing-opentelemetry = { workspace = true, optional = true }
|
tracing-opentelemetry = { workspace = true, optional = true }
|
||||||
opentelemetry = { workspace = true, optional = true }
|
opentelemetry = { workspace = true, optional = true }
|
||||||
console-subscriber = { version = "0.1.10", optional = true }
|
console-subscriber = { version = "0.3.0", optional = true }
|
||||||
opentelemetry-otlp = { version = "0.12.0", optional = true }
|
opentelemetry-otlp = { version = "0.12.0", optional = true }
|
||||||
pict-rs = { version = "0.5.14", optional = true }
|
pict-rs = { version = "0.5.15", optional = true }
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
actix-cors = "0.7.0"
|
actix-cors = "0.7.0"
|
||||||
futures-util = { workspace = true }
|
futures-util = { workspace = true }
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
{
|
|
||||||
"root": true,
|
|
||||||
"env": {
|
|
||||||
"browser": true
|
|
||||||
},
|
|
||||||
"plugins": ["@typescript-eslint"],
|
|
||||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"project": "./tsconfig.json",
|
|
||||||
"warnOnUnsupportedTypeScriptVersion": false
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/ban-ts-comment": 0,
|
|
||||||
"@typescript-eslint/no-explicit-any": 0,
|
|
||||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
|
||||||
"@typescript-eslint/no-var-requires": 0,
|
|
||||||
"arrow-body-style": 0,
|
|
||||||
"curly": 0,
|
|
||||||
"eol-last": 0,
|
|
||||||
"eqeqeq": 0,
|
|
||||||
"func-style": 0,
|
|
||||||
"import/no-duplicates": 0,
|
|
||||||
"max-statements": 0,
|
|
||||||
"max-params": 0,
|
|
||||||
"new-cap": 0,
|
|
||||||
"no-console": 0,
|
|
||||||
"no-duplicate-imports": 0,
|
|
||||||
"no-extra-parens": 0,
|
|
||||||
"no-return-assign": 0,
|
|
||||||
"no-throw-literal": 0,
|
|
||||||
"no-trailing-spaces": 0,
|
|
||||||
"no-unused-expressions": 0,
|
|
||||||
"no-useless-constructor": 0,
|
|
||||||
"no-useless-escape": 0,
|
|
||||||
"no-var": 0,
|
|
||||||
"prefer-const": 0,
|
|
||||||
"prefer-rest-params": 0,
|
|
||||||
"quote-props": 0,
|
|
||||||
"unicorn/filename-case": 0
|
|
||||||
}
|
|
||||||
}
|
|
56
api_tests/eslint.config.mjs
Normal file
56
api_tests/eslint.config.mjs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import pluginJs from "@eslint/js";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
pluginJs.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
parser: tseslint.parser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// For some reason this has to be in its own block
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"putTypesInIndex.js",
|
||||||
|
"dist/*",
|
||||||
|
"docs/*",
|
||||||
|
".yalc",
|
||||||
|
"jest.config.js",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["src/**/*"],
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/no-empty-interface": 0,
|
||||||
|
"@typescript-eslint/no-empty-function": 0,
|
||||||
|
"@typescript-eslint/ban-ts-comment": 0,
|
||||||
|
"@typescript-eslint/no-explicit-any": 0,
|
||||||
|
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||||
|
"@typescript-eslint/no-var-requires": 0,
|
||||||
|
"arrow-body-style": 0,
|
||||||
|
curly: 0,
|
||||||
|
"eol-last": 0,
|
||||||
|
eqeqeq: 0,
|
||||||
|
"func-style": 0,
|
||||||
|
"import/no-duplicates": 0,
|
||||||
|
"max-statements": 0,
|
||||||
|
"max-params": 0,
|
||||||
|
"new-cap": 0,
|
||||||
|
"no-console": 0,
|
||||||
|
"no-duplicate-imports": 0,
|
||||||
|
"no-extra-parens": 0,
|
||||||
|
"no-return-assign": 0,
|
||||||
|
"no-throw-literal": 0,
|
||||||
|
"no-trailing-spaces": 0,
|
||||||
|
"no-unused-expressions": 0,
|
||||||
|
"no-useless-constructor": 0,
|
||||||
|
"no-useless-escape": 0,
|
||||||
|
"no-var": 0,
|
||||||
|
"prefer-const": 0,
|
||||||
|
"prefer-rest-params": 0,
|
||||||
|
"quote-props": 0,
|
||||||
|
"unicorn/filename-case": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
|
@ -6,9 +6,9 @@
|
||||||
"repository": "https://github.com/LemmyNet/lemmy",
|
"repository": "https://github.com/LemmyNet/lemmy",
|
||||||
"author": "Dessalines",
|
"author": "Dessalines",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"packageManager": "pnpm@9.1.1+sha256.9551e803dcb7a1839fdf5416153a844060c7bce013218ce823410532504ac10b",
|
"packageManager": "pnpm@9.4.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src && prettier --check 'src/**/*.ts'",
|
"lint": "tsc --noEmit && eslint --report-unused-disable-directives && prettier --check 'src/**/*.ts'",
|
||||||
"fix": "prettier --write src && eslint --fix src",
|
"fix": "prettier --write src && eslint --fix src",
|
||||||
"api-test": "jest -i follow.spec.ts && jest -i image.spec.ts && jest -i user.spec.ts && jest -i private_message.spec.ts && jest -i community.spec.ts && jest -i post.spec.ts && jest -i comment.spec.ts ",
|
"api-test": "jest -i follow.spec.ts && jest -i image.spec.ts && jest -i user.spec.ts && jest -i private_message.spec.ts && jest -i community.spec.ts && jest -i post.spec.ts && jest -i comment.spec.ts ",
|
||||||
"api-test-follow": "jest -i follow.spec.ts",
|
"api-test-follow": "jest -i follow.spec.ts",
|
||||||
|
@ -25,12 +25,13 @@
|
||||||
"@typescript-eslint/eslint-plugin": "^7.5.0",
|
"@typescript-eslint/eslint-plugin": "^7.5.0",
|
||||||
"@typescript-eslint/parser": "^7.5.0",
|
"@typescript-eslint/parser": "^7.5.0",
|
||||||
"download-file-sync": "^1.0.4",
|
"download-file-sync": "^1.0.4",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^9.0.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"lemmy-js-client": "0.19.4-alpha.18",
|
"lemmy-js-client": "0.19.5-alpha.1",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
"typescript": "^5.4.4"
|
"typescript": "^5.4.4",
|
||||||
|
"typescript-eslint": "^7.13.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,7 +15,7 @@ export LEMMY_TEST_FAST_FEDERATION=1 # by default, the persistent federation queu
|
||||||
|
|
||||||
# pictrs setup
|
# pictrs setup
|
||||||
if [ ! -f "api_tests/pict-rs" ]; then
|
if [ ! -f "api_tests/pict-rs" ]; then
|
||||||
curl "https://git.asonix.dog/asonix/pict-rs/releases/download/v0.5.13/pict-rs-linux-amd64" -o api_tests/pict-rs
|
curl "https://git.asonix.dog/asonix/pict-rs/releases/download/v0.5.16/pict-rs-linux-amd64" -o api_tests/pict-rs
|
||||||
chmod +x api_tests/pict-rs
|
chmod +x api_tests/pict-rs
|
||||||
fi
|
fi
|
||||||
./api_tests/pict-rs \
|
./api_tests/pict-rs \
|
||||||
|
|
|
@ -37,8 +37,9 @@ import {
|
||||||
followCommunity,
|
followCommunity,
|
||||||
blockCommunity,
|
blockCommunity,
|
||||||
delay,
|
delay,
|
||||||
|
saveUserSettings,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { CommentView, CommunityView } from "lemmy-js-client";
|
import { CommentView, CommunityView, SaveUserSettings } from "lemmy-js-client";
|
||||||
|
|
||||||
let betaCommunity: CommunityView | undefined;
|
let betaCommunity: CommunityView | undefined;
|
||||||
let postOnAlphaRes: PostResponse;
|
let postOnAlphaRes: PostResponse;
|
||||||
|
@ -443,6 +444,59 @@ test("Reply to a comment from another instance, get notification", async () => {
|
||||||
assertCommentFederation(alphaReply, replyRes.comment_view);
|
assertCommentFederation(alphaReply, replyRes.comment_view);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Bot reply notifications are filtered when bots are hidden", async () => {
|
||||||
|
const newAlphaBot = await registerUser(alpha, alphaUrl);
|
||||||
|
let form: SaveUserSettings = {
|
||||||
|
bot_account: true,
|
||||||
|
};
|
||||||
|
await saveUserSettings(newAlphaBot, form);
|
||||||
|
|
||||||
|
const alphaCommunity = (
|
||||||
|
await resolveCommunity(alpha, "!main@lemmy-alpha:8541")
|
||||||
|
).community;
|
||||||
|
|
||||||
|
if (!alphaCommunity) {
|
||||||
|
throw "Missing alpha community";
|
||||||
|
}
|
||||||
|
|
||||||
|
await alpha.markAllAsRead();
|
||||||
|
form = {
|
||||||
|
show_bot_accounts: false,
|
||||||
|
};
|
||||||
|
await saveUserSettings(alpha, form);
|
||||||
|
const postOnAlphaRes = await createPost(alpha, alphaCommunity.community.id);
|
||||||
|
|
||||||
|
// Bot reply to alpha's post
|
||||||
|
let commentRes = await createComment(
|
||||||
|
newAlphaBot,
|
||||||
|
postOnAlphaRes.post_view.post.id,
|
||||||
|
);
|
||||||
|
expect(commentRes).toBeDefined();
|
||||||
|
|
||||||
|
let alphaUnreadCountRes = await getUnreadCount(alpha);
|
||||||
|
expect(alphaUnreadCountRes.replies).toBe(0);
|
||||||
|
|
||||||
|
let alphaUnreadRepliesRes = await getReplies(alpha, true);
|
||||||
|
expect(alphaUnreadRepliesRes.replies.length).toBe(0);
|
||||||
|
|
||||||
|
// This both restores the original state that may be expected by other tests
|
||||||
|
// implicitly and is used by the next steps to ensure replies are still
|
||||||
|
// returned when a user later decides to show bot accounts again.
|
||||||
|
form = {
|
||||||
|
show_bot_accounts: true,
|
||||||
|
};
|
||||||
|
await saveUserSettings(alpha, form);
|
||||||
|
|
||||||
|
alphaUnreadCountRes = await getUnreadCount(alpha);
|
||||||
|
expect(alphaUnreadCountRes.replies).toBe(1);
|
||||||
|
|
||||||
|
alphaUnreadRepliesRes = await getReplies(alpha, true);
|
||||||
|
expect(alphaUnreadRepliesRes.replies.length).toBe(1);
|
||||||
|
expect(alphaUnreadRepliesRes.replies[0].comment.id).toBe(
|
||||||
|
commentRes.comment_view.comment.id,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("Mention beta from alpha", async () => {
|
test("Mention beta from alpha", async () => {
|
||||||
if (!betaCommunity) throw Error("no community");
|
if (!betaCommunity) throw Error("no community");
|
||||||
const postOnAlphaRes = await createPost(alpha, betaCommunity.community.id);
|
const postOnAlphaRes = await createPost(alpha, betaCommunity.community.id);
|
||||||
|
|
|
@ -160,6 +160,7 @@ test("Purge post, linked image removed", async () => {
|
||||||
upload.url,
|
upload.url,
|
||||||
);
|
);
|
||||||
expect(post.post_view.post.url).toBe(upload.url);
|
expect(post.post_view.post.url).toBe(upload.url);
|
||||||
|
expect(post.post_view.image_details).toBeDefined();
|
||||||
|
|
||||||
// purge post
|
// purge post
|
||||||
const purgeForm: PurgePost = {
|
const purgeForm: PurgePost = {
|
||||||
|
@ -184,6 +185,9 @@ test("Images in remote image post are proxied if setting enabled", async () => {
|
||||||
const post = postRes.post_view.post;
|
const post = postRes.post_view.post;
|
||||||
expect(post).toBeDefined();
|
expect(post).toBeDefined();
|
||||||
|
|
||||||
|
// Make sure it fetched the image details
|
||||||
|
expect(postRes.post_view.image_details).toBeDefined();
|
||||||
|
|
||||||
// remote image gets proxied after upload
|
// remote image gets proxied after upload
|
||||||
expect(
|
expect(
|
||||||
post.thumbnail_url?.startsWith(
|
post.thumbnail_url?.startsWith(
|
||||||
|
|
|
@ -502,7 +502,7 @@ test("Enforce site ban federation for local user", async () => {
|
||||||
}
|
}
|
||||||
let newAlphaUserJwt = await loginUser(alpha, alphaUserPerson.name);
|
let newAlphaUserJwt = await loginUser(alpha, alphaUserPerson.name);
|
||||||
alphaUserHttp.setHeaders({
|
alphaUserHttp.setHeaders({
|
||||||
Authorization: "Bearer " + newAlphaUserJwt.jwt ?? "",
|
Authorization: "Bearer " + newAlphaUserJwt.jwt,
|
||||||
});
|
});
|
||||||
// alpha makes new post in beta community, it federates
|
// alpha makes new post in beta community, it federates
|
||||||
let postRes2 = await createPost(alphaUserHttp, betaCommunity!.community.id);
|
let postRes2 = await createPost(alphaUserHttp, betaCommunity!.community.id);
|
||||||
|
|
|
@ -364,10 +364,13 @@ export async function getUnreadCount(
|
||||||
return api.getUnreadCount();
|
return api.getUnreadCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getReplies(api: LemmyHttp): Promise<GetRepliesResponse> {
|
export async function getReplies(
|
||||||
|
api: LemmyHttp,
|
||||||
|
unread_only: boolean = false,
|
||||||
|
): Promise<GetRepliesResponse> {
|
||||||
let form: GetReplies = {
|
let form: GetReplies = {
|
||||||
sort: "New",
|
sort: "New",
|
||||||
unread_only: false,
|
unread_only,
|
||||||
};
|
};
|
||||||
return api.getReplies(form);
|
return api.getReplies(form);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
fetchFunction,
|
fetchFunction,
|
||||||
alphaImage,
|
alphaImage,
|
||||||
unfollows,
|
unfollows,
|
||||||
|
saveUserSettingsBio,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { LemmyHttp, SaveUserSettings, UploadImage } from "lemmy-js-client";
|
import { LemmyHttp, SaveUserSettings, UploadImage } from "lemmy-js-client";
|
||||||
import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
|
import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
|
||||||
|
@ -186,10 +187,26 @@ test("Set a new avatar, old avatar is deleted", async () => {
|
||||||
expect(upload2.url).toBeDefined();
|
expect(upload2.url).toBeDefined();
|
||||||
|
|
||||||
let form2 = {
|
let form2 = {
|
||||||
avatar: upload1.url,
|
avatar: upload2.url,
|
||||||
};
|
};
|
||||||
await saveUserSettings(alpha, form2);
|
await saveUserSettings(alpha, form2);
|
||||||
// make sure only the new avatar is kept
|
// make sure only the new avatar is kept
|
||||||
const listMediaRes2 = await alphaImage.listMedia();
|
const listMediaRes2 = await alphaImage.listMedia();
|
||||||
expect(listMediaRes2.images.length).toBe(1);
|
expect(listMediaRes2.images.length).toBe(1);
|
||||||
|
|
||||||
|
// Upload that same form2 avatar, make sure it isn't replaced / deleted
|
||||||
|
await saveUserSettings(alpha, form2);
|
||||||
|
// make sure only the new avatar is kept
|
||||||
|
const listMediaRes3 = await alphaImage.listMedia();
|
||||||
|
expect(listMediaRes3.images.length).toBe(1);
|
||||||
|
|
||||||
|
// Now try to save a user settings, with the icon missing,
|
||||||
|
// and make sure it doesn't clear the data, or delete the image
|
||||||
|
await saveUserSettingsBio(alpha);
|
||||||
|
let site = await getSite(alpha);
|
||||||
|
expect(site.my_user?.local_user_view.person.avatar).toBe(upload2.url);
|
||||||
|
|
||||||
|
// make sure only the new avatar is kept
|
||||||
|
const listMediaRes4 = await alphaImage.listMedia();
|
||||||
|
expect(listMediaRes4.images.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,6 +26,7 @@ body = """
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
|
|
||||||
|
{%- if github -%}
|
||||||
{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
|
{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
|
||||||
{% raw %}\n{% endraw -%}
|
{% raw %}\n{% endraw -%}
|
||||||
## New Contributors
|
## New Contributors
|
||||||
|
@ -36,6 +37,7 @@ body = """
|
||||||
[#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
|
[#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
{% if version %}
|
{% if version %}
|
||||||
{% if previous.version %}
|
{% if previous.version %}
|
||||||
|
@ -70,6 +72,7 @@ commit_preprocessors = [
|
||||||
# remove issue numbers from commits
|
# remove issue numbers from commits
|
||||||
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" },
|
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" },
|
||||||
]
|
]
|
||||||
|
commit_parsers = [{ field = "author.name", pattern = "renovate", skip = true }]
|
||||||
# protect breaking changes from being skipped due to matching a skipping commit_parser
|
# protect breaking changes from being skipped due to matching a skipping commit_parser
|
||||||
protect_breaking_commits = false
|
protect_breaking_commits = false
|
||||||
# filter out the commits that are not matched by commit parsers
|
# filter out the commits that are not matched by commit parsers
|
||||||
|
|
|
@ -33,7 +33,7 @@ anyhow = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
wav = "1.0.1"
|
hound = "3.5.1"
|
||||||
sitemap-rs = "0.2.1"
|
sitemap-rs = "0.2.1"
|
||||||
totp-rs = { version = "5.5.1", features = ["gen_secret", "otpauth"] }
|
totp-rs = { version = "5.5.1", features = ["gen_secret", "otpauth"] }
|
||||||
actix-web-httpauth = "0.8.1"
|
actix-web-httpauth = "0.8.1"
|
||||||
|
|
|
@ -43,7 +43,10 @@ pub async fn ban_from_community(
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
is_valid_body_field(&data.reason, false)?;
|
|
||||||
|
if let Some(reason) = &data.reason {
|
||||||
|
is_valid_body_field(reason, false)?;
|
||||||
|
}
|
||||||
|
|
||||||
let community_user_ban_form = CommunityPersonBanForm {
|
let community_user_ban_form = CommunityPersonBanForm {
|
||||||
community_id: data.community_id,
|
community_id: data.community_id,
|
||||||
|
|
|
@ -44,33 +44,38 @@ pub mod site;
|
||||||
pub mod sitemap;
|
pub mod sitemap;
|
||||||
|
|
||||||
/// Converts the captcha to a base64 encoded wav audio file
|
/// Converts the captcha to a base64 encoded wav audio file
|
||||||
#[allow(deprecated)]
|
|
||||||
pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> LemmyResult<String> {
|
pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> LemmyResult<String> {
|
||||||
let letters = captcha.as_wav();
|
let letters = captcha.as_wav();
|
||||||
|
|
||||||
// Decode each wav file, concatenate the samples
|
// Decode each wav file, concatenate the samples
|
||||||
let mut concat_samples: Vec<i16> = Vec::new();
|
let mut concat_samples: Vec<i16> = Vec::new();
|
||||||
let mut any_header: Option<wav::Header> = None;
|
let mut any_header: Option<hound::WavSpec> = None;
|
||||||
for letter in letters {
|
for letter in letters {
|
||||||
let mut cursor = Cursor::new(letter.unwrap_or_default());
|
let mut cursor = Cursor::new(letter.unwrap_or_default());
|
||||||
let (header, samples) = wav::read(&mut cursor)?;
|
let reader = hound::WavReader::new(&mut cursor)?;
|
||||||
any_header = Some(header);
|
any_header = Some(reader.spec());
|
||||||
if let Some(samples16) = samples.as_sixteen() {
|
let samples16 = reader
|
||||||
concat_samples.extend(samples16);
|
.into_samples::<i16>()
|
||||||
} else {
|
.collect::<Result<Vec<_>, _>>()
|
||||||
Err(LemmyErrorType::CouldntCreateAudioCaptcha)?
|
.with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?;
|
||||||
}
|
concat_samples.extend(samples16);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode the concatenated result as a wav file
|
// Encode the concatenated result as a wav file
|
||||||
let mut output_buffer = Cursor::new(vec![]);
|
let mut output_buffer = Cursor::new(vec![]);
|
||||||
if let Some(header) = any_header {
|
if let Some(header) = any_header {
|
||||||
wav::write(
|
let mut writer = hound::WavWriter::new(&mut output_buffer, header)
|
||||||
header,
|
.with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?;
|
||||||
&wav::BitDepth::Sixteen(concat_samples),
|
let mut writer16 = writer.get_i16_writer(concat_samples.len() as u32);
|
||||||
&mut output_buffer,
|
for sample in concat_samples {
|
||||||
)
|
writer16.write_sample(sample);
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?;
|
}
|
||||||
|
writer16
|
||||||
|
.flush()
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?;
|
||||||
|
writer
|
||||||
|
.finalize()
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?;
|
||||||
|
|
||||||
Ok(base64.encode(output_buffer.into_inner()))
|
Ok(base64.encode(output_buffer.into_inner()))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -31,7 +31,9 @@ pub async fn ban_from_site(
|
||||||
// Make sure user is an admin
|
// Make sure user is an admin
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
|
||||||
is_valid_body_field(&data.reason, false)?;
|
if let Some(reason) = &data.reason {
|
||||||
|
is_valid_body_field(reason, false)?;
|
||||||
|
}
|
||||||
|
|
||||||
let expires = check_expire_time(data.expires)?;
|
let expires = check_expire_time(data.expires)?;
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,12 @@ pub async fn unread_count(
|
||||||
) -> LemmyResult<Json<GetUnreadCountResponse>> {
|
) -> LemmyResult<Json<GetUnreadCountResponse>> {
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
|
|
||||||
let replies = CommentReplyView::get_unread_replies(&mut context.pool(), person_id).await?;
|
let replies =
|
||||||
|
CommentReplyView::get_unread_replies(&mut context.pool(), &local_user_view.local_user).await?;
|
||||||
|
|
||||||
let mentions = PersonMentionView::get_unread_mentions(&mut context.pool(), person_id).await?;
|
let mentions =
|
||||||
|
PersonMentionView::get_unread_mentions(&mut context.pool(), &local_user_view.local_user)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let private_messages =
|
let private_messages =
|
||||||
PrivateMessageView::get_unread_messages(&mut context.pool(), person_id).await?;
|
PrivateMessageView::get_unread_messages(&mut context.pool(), person_id).await?;
|
||||||
|
|
|
@ -21,7 +21,7 @@ use lemmy_db_schema::{
|
||||||
person::{Person, PersonUpdateForm},
|
person::{Person, PersonUpdateForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::diesel_option_overwrite,
|
utils::{diesel_string_update, diesel_url_update},
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
|
@ -42,18 +42,24 @@ pub async fn save_user_settings(
|
||||||
|
|
||||||
let slur_regex = local_site_to_slur_regex(&site_view.local_site);
|
let slur_regex = local_site_to_slur_regex(&site_view.local_site);
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
let bio = diesel_option_overwrite(
|
let bio = diesel_string_update(
|
||||||
process_markdown_opt(&data.bio, &slur_regex, &url_blocklist, &context).await?,
|
process_markdown_opt(&data.bio, &slur_regex, &url_blocklist, &context)
|
||||||
|
.await?
|
||||||
|
.as_deref(),
|
||||||
);
|
);
|
||||||
replace_image(&data.avatar, &local_user_view.person.avatar, &context).await?;
|
|
||||||
replace_image(&data.banner, &local_user_view.person.banner, &context).await?;
|
|
||||||
|
|
||||||
let avatar = proxy_image_link_opt_api(&data.avatar, &context).await?;
|
let avatar = diesel_url_update(data.avatar.as_deref())?;
|
||||||
let banner = proxy_image_link_opt_api(&data.banner, &context).await?;
|
replace_image(&avatar, &local_user_view.person.avatar, &context).await?;
|
||||||
let display_name = diesel_option_overwrite(data.display_name.clone());
|
let avatar = proxy_image_link_opt_api(avatar, &context).await?;
|
||||||
let matrix_user_id = diesel_option_overwrite(data.matrix_user_id.clone());
|
|
||||||
|
let banner = diesel_url_update(data.banner.as_deref())?;
|
||||||
|
replace_image(&banner, &local_user_view.person.banner, &context).await?;
|
||||||
|
let banner = proxy_image_link_opt_api(banner, &context).await?;
|
||||||
|
|
||||||
|
let display_name = diesel_string_update(data.display_name.as_deref());
|
||||||
|
let matrix_user_id = diesel_string_update(data.matrix_user_id.as_deref());
|
||||||
let email_deref = data.email.as_deref().map(str::to_lowercase);
|
let email_deref = data.email.as_deref().map(str::to_lowercase);
|
||||||
let email = diesel_option_overwrite(email_deref.clone());
|
let email = diesel_string_update(email_deref.as_deref());
|
||||||
|
|
||||||
if let Some(Some(email)) = &email {
|
if let Some(Some(email)) = &email {
|
||||||
let previous_email = local_user_view.local_user.email.clone().unwrap_or_default();
|
let previous_email = local_user_view.local_user.email.clone().unwrap_or_default();
|
||||||
|
|
|
@ -4,14 +4,19 @@ use lemmy_api_common::{
|
||||||
post::{GetSiteMetadata, GetSiteMetadataResponse},
|
post::{GetSiteMetadata, GetSiteMetadataResponse},
|
||||||
request::fetch_link_metadata,
|
request::fetch_link_metadata,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::{
|
||||||
|
error::{LemmyErrorExt, LemmyResult},
|
||||||
|
LemmyErrorType,
|
||||||
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
#[tracing::instrument(skip(context))]
|
||||||
pub async fn get_link_metadata(
|
pub async fn get_link_metadata(
|
||||||
data: Query<GetSiteMetadata>,
|
data: Query<GetSiteMetadata>,
|
||||||
context: Data<LemmyContext>,
|
context: Data<LemmyContext>,
|
||||||
) -> LemmyResult<Json<GetSiteMetadataResponse>> {
|
) -> LemmyResult<Json<GetSiteMetadataResponse>> {
|
||||||
let metadata = fetch_link_metadata(&data.url, &context).await?;
|
let url = Url::parse(&data.url).with_lemmy_type(LemmyErrorType::InvalidUrl)?;
|
||||||
|
let metadata = fetch_link_metadata(&url, &context).await?;
|
||||||
|
|
||||||
Ok(Json(GetSiteMetadataResponse { metadata }))
|
Ok(Json(GetSiteMetadataResponse { metadata }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use lemmy_db_schema::{
|
||||||
registration_application::{RegistrationApplication, RegistrationApplicationUpdateForm},
|
registration_application::{RegistrationApplication, RegistrationApplicationUpdateForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::diesel_option_overwrite,
|
utils::diesel_string_update,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView};
|
use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView};
|
||||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
@ -26,7 +26,7 @@ pub async fn approve_registration_application(
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
|
||||||
// Update the registration with reason, admin_id
|
// Update the registration with reason, admin_id
|
||||||
let deny_reason = diesel_option_overwrite(data.deny_reason.clone());
|
let deny_reason = diesel_string_update(data.deny_reason.as_deref());
|
||||||
let app_form = RegistrationApplicationUpdateForm {
|
let app_form = RegistrationApplicationUpdateForm {
|
||||||
admin_id: Some(Some(local_user_view.person.id)),
|
admin_id: Some(Some(local_user_view.person.id)),
|
||||||
deny_reason,
|
deny_reason,
|
||||||
|
|
|
@ -112,11 +112,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "Gerry9812");
|
||||||
.name("Gerry9812".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||||
|
@ -20,8 +19,7 @@ use url::Url;
|
||||||
pub struct CreatePost {
|
pub struct CreatePost {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub community_id: CommunityId,
|
pub community_id: CommunityId,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
pub url: Option<String>,
|
||||||
pub url: Option<Url>,
|
|
||||||
/// An optional body for the post in markdown.
|
/// An optional body for the post in markdown.
|
||||||
pub body: Option<String>,
|
pub body: Option<String>,
|
||||||
/// An optional alt_text, usable for image posts.
|
/// An optional alt_text, usable for image posts.
|
||||||
|
@ -30,9 +28,8 @@ pub struct CreatePost {
|
||||||
pub honeypot: Option<String>,
|
pub honeypot: Option<String>,
|
||||||
pub nsfw: Option<bool>,
|
pub nsfw: Option<bool>,
|
||||||
pub language_id: Option<LanguageId>,
|
pub language_id: Option<LanguageId>,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
/// Instead of fetching a thumbnail, use a custom one.
|
/// Instead of fetching a thumbnail, use a custom one.
|
||||||
pub custom_thumbnail: Option<Url>,
|
pub custom_thumbnail: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
@ -82,6 +79,8 @@ pub struct GetPosts {
|
||||||
pub liked_only: Option<bool>,
|
pub liked_only: Option<bool>,
|
||||||
pub disliked_only: Option<bool>,
|
pub disliked_only: Option<bool>,
|
||||||
pub show_hidden: Option<bool>,
|
pub show_hidden: Option<bool>,
|
||||||
|
/// If true, then show the read posts (even if your user setting is to hide them)
|
||||||
|
pub show_read: Option<bool>,
|
||||||
pub page_cursor: Option<PaginationCursor>,
|
pub page_cursor: Option<PaginationCursor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,17 +113,15 @@ pub struct CreatePostLike {
|
||||||
pub struct EditPost {
|
pub struct EditPost {
|
||||||
pub post_id: PostId,
|
pub post_id: PostId,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
pub url: Option<String>,
|
||||||
pub url: Option<Url>,
|
|
||||||
/// An optional body for the post in markdown.
|
/// An optional body for the post in markdown.
|
||||||
pub body: Option<String>,
|
pub body: Option<String>,
|
||||||
/// An optional alt_text, usable for image posts.
|
/// An optional alt_text, usable for image posts.
|
||||||
pub alt_text: Option<String>,
|
pub alt_text: Option<String>,
|
||||||
pub nsfw: Option<bool>,
|
pub nsfw: Option<bool>,
|
||||||
pub language_id: Option<LanguageId>,
|
pub language_id: Option<LanguageId>,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
/// Instead of fetching a thumbnail, use a custom one.
|
/// Instead of fetching a thumbnail, use a custom one.
|
||||||
pub custom_thumbnail: Option<Url>,
|
pub custom_thumbnail: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||||
|
@ -249,8 +246,7 @@ pub struct ListPostReportsResponse {
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// Get metadata for a given site.
|
/// Get metadata for a given site.
|
||||||
pub struct GetSiteMetadata {
|
pub struct GetSiteMetadata {
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
pub url: String,
|
||||||
pub url: Url,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|
|
@ -11,7 +11,7 @@ use encoding_rs::{Encoding, UTF_8};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::DbUrl,
|
newtypes::DbUrl,
|
||||||
source::{
|
source::{
|
||||||
images::{LocalImage, LocalImageForm},
|
images::{ImageDetailsForm, LocalImage, LocalImageForm},
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
post::{Post, PostUpdateForm},
|
post::{Post, PostUpdateForm},
|
||||||
},
|
},
|
||||||
|
@ -209,6 +209,19 @@ pub struct PictrsFileDetails {
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PictrsFileDetails {
|
||||||
|
/// Builds the image form. This should always use the thumbnail_url,
|
||||||
|
/// Because the post_view joins to it
|
||||||
|
pub fn build_image_details_form(&self, thumbnail_url: &Url) -> ImageDetailsForm {
|
||||||
|
ImageDetailsForm {
|
||||||
|
link: thumbnail_url.clone().into(),
|
||||||
|
width: self.width.into(),
|
||||||
|
height: self.height.into(),
|
||||||
|
content_type: self.content_type.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
struct PictrsPurgeResponse {
|
struct PictrsPurgeResponse {
|
||||||
msg: String,
|
msg: String,
|
||||||
|
@ -316,11 +329,52 @@ async fn generate_pictrs_thumbnail(image_url: &Url, context: &LemmyContext) -> L
|
||||||
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
let thumbnail_url = image.thumbnail_url(&protocol_and_hostname)?;
|
let thumbnail_url = image.thumbnail_url(&protocol_and_hostname)?;
|
||||||
|
|
||||||
LocalImage::create(&mut context.pool(), &form).await?;
|
// Also store the details for the image
|
||||||
|
let details_form = image.details.build_image_details_form(&thumbnail_url);
|
||||||
|
LocalImage::create(&mut context.pool(), &form, &details_form).await?;
|
||||||
|
|
||||||
Ok(thumbnail_url)
|
Ok(thumbnail_url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetches the image details for pictrs proxied images
|
||||||
|
///
|
||||||
|
/// We don't need to check for image mode, as that's already been done
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub async fn fetch_pictrs_proxied_image_details(
|
||||||
|
image_url: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> LemmyResult<PictrsFileDetails> {
|
||||||
|
let pictrs_url = context.settings().pictrs_config()?.url;
|
||||||
|
let encoded_image_url = encode(image_url.as_str());
|
||||||
|
|
||||||
|
// Pictrs needs you to fetch the proxied image before you can fetch the details
|
||||||
|
let proxy_url = format!("{pictrs_url}image/original?proxy={encoded_image_url}");
|
||||||
|
|
||||||
|
let res = context
|
||||||
|
.client()
|
||||||
|
.get(&proxy_url)
|
||||||
|
.timeout(REQWEST_TIMEOUT)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.status();
|
||||||
|
if !res.is_success() {
|
||||||
|
Err(LemmyErrorType::NotAnImageType)?
|
||||||
|
}
|
||||||
|
|
||||||
|
let details_url = format!("{pictrs_url}image/details/original?proxy={encoded_image_url}");
|
||||||
|
|
||||||
|
let res = context
|
||||||
|
.client()
|
||||||
|
.get(&details_url)
|
||||||
|
.timeout(REQWEST_TIMEOUT)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: get rid of this by reading content type from db
|
// TODO: get rid of this by reading content type from db
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> LemmyResult<()> {
|
async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> LemmyResult<()> {
|
||||||
|
@ -338,16 +392,19 @@ async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> Lemm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When adding a new avatar or similar image, delete the old one.
|
/// When adding a new avatar, banner or similar image, delete the old one.
|
||||||
pub async fn replace_image(
|
pub async fn replace_image(
|
||||||
new_image: &Option<String>,
|
new_image: &Option<Option<DbUrl>>,
|
||||||
old_image: &Option<DbUrl>,
|
old_image: &Option<DbUrl>,
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
if new_image.is_some() {
|
if let (Some(Some(new_image)), Some(old_image)) = (new_image, old_image) {
|
||||||
// Ignore errors because image may be stored externally.
|
// Note: Oftentimes front ends will include the current image in the form.
|
||||||
if let Some(avatar) = &old_image {
|
// In this case, deleting `old_image` would also be deletion of `new_image`,
|
||||||
let image = LocalImage::delete_by_url(&mut context.pool(), avatar)
|
// so the deletion must be skipped for the image to be kept.
|
||||||
|
if new_image != old_image {
|
||||||
|
// Ignore errors because image may be stored externally.
|
||||||
|
let image = LocalImage::delete_by_url(&mut context.pool(), old_image)
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
if let Some(image) = image {
|
if let Some(image) = image {
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
request::{delete_image_from_pictrs, purge_image_from_pictrs},
|
request::{
|
||||||
|
delete_image_from_pictrs,
|
||||||
|
fetch_pictrs_proxied_image_details,
|
||||||
|
purge_image_from_pictrs,
|
||||||
|
},
|
||||||
site::{FederatedInstances, InstanceWithFederationState},
|
site::{FederatedInstances, InstanceWithFederationState},
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Days, Local, TimeZone, Utc};
|
use chrono::{DateTime, Days, Local, TimeZone, Utc};
|
||||||
|
@ -949,7 +953,18 @@ pub async fn process_markdown(
|
||||||
|
|
||||||
if context.settings().pictrs_config()?.image_mode() == PictrsImageMode::ProxyAllImages {
|
if context.settings().pictrs_config()?.image_mode() == PictrsImageMode::ProxyAllImages {
|
||||||
let (text, links) = markdown_rewrite_image_links(text);
|
let (text, links) = markdown_rewrite_image_links(text);
|
||||||
RemoteImage::create(&mut context.pool(), links).await?;
|
|
||||||
|
// Create images and image detail rows
|
||||||
|
for link in links {
|
||||||
|
// Insert image details for the remote image
|
||||||
|
let details_res = fetch_pictrs_proxied_image_details(&link, context).await;
|
||||||
|
if let Ok(details) = details_res {
|
||||||
|
let proxied =
|
||||||
|
build_proxied_image_url(&link, &context.settings().get_protocol_and_hostname())?;
|
||||||
|
let details_form = details.build_image_details_form(&proxied);
|
||||||
|
RemoteImage::create(&mut context.pool(), &details_form).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(text)
|
Ok(text)
|
||||||
} else {
|
} else {
|
||||||
Ok(text)
|
Ok(text)
|
||||||
|
@ -984,8 +999,14 @@ async fn proxy_image_link_internal(
|
||||||
Ok(link.into())
|
Ok(link.into())
|
||||||
} else if image_mode == PictrsImageMode::ProxyAllImages {
|
} else if image_mode == PictrsImageMode::ProxyAllImages {
|
||||||
let proxied = build_proxied_image_url(&link, &context.settings().get_protocol_and_hostname())?;
|
let proxied = build_proxied_image_url(&link, &context.settings().get_protocol_and_hostname())?;
|
||||||
|
// This should fail softly, since pictrs might not even be running
|
||||||
|
let details_res = fetch_pictrs_proxied_image_details(&link, context).await;
|
||||||
|
|
||||||
|
if let Ok(details) = details_res {
|
||||||
|
let details_form = details.build_image_details_form(&proxied);
|
||||||
|
RemoteImage::create(&mut context.pool(), &details_form).await?;
|
||||||
|
};
|
||||||
|
|
||||||
RemoteImage::create(&mut context.pool(), vec![link]).await?;
|
|
||||||
Ok(proxied.into())
|
Ok(proxied.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(link.into())
|
Ok(link.into())
|
||||||
|
@ -1004,26 +1025,25 @@ pub(crate) async fn proxy_image_link(link: Url, context: &LemmyContext) -> Lemmy
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn proxy_image_link_opt_api(
|
pub async fn proxy_image_link_opt_api(
|
||||||
link: &Option<String>,
|
link: Option<Option<DbUrl>>,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> LemmyResult<Option<Option<DbUrl>>> {
|
) -> LemmyResult<Option<Option<DbUrl>>> {
|
||||||
proxy_image_link_api(link, context).await.map(Some)
|
if let Some(Some(link)) = link {
|
||||||
|
proxy_image_link(link.into(), context)
|
||||||
|
.await
|
||||||
|
.map(Some)
|
||||||
|
.map(Some)
|
||||||
|
} else {
|
||||||
|
Ok(link)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn proxy_image_link_api(
|
pub async fn proxy_image_link_api(
|
||||||
link: &Option<String>,
|
link: Option<DbUrl>,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> LemmyResult<Option<DbUrl>> {
|
) -> LemmyResult<Option<DbUrl>> {
|
||||||
let link: Option<DbUrl> = match link.as_ref().map(String::as_str) {
|
if let Some(link) = link {
|
||||||
// An empty string is an erase
|
proxy_image_link(link.into(), context).await.map(Some)
|
||||||
Some("") => None,
|
|
||||||
Some(str_url) => Url::parse(str_url)
|
|
||||||
.map(|u| Some(u.into()))
|
|
||||||
.with_lemmy_type(LemmyErrorType::InvalidUrl)?,
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
if let Some(l) = link {
|
|
||||||
proxy_image_link(l.into(), context).await.map(Some)
|
|
||||||
} else {
|
} else {
|
||||||
Ok(link)
|
Ok(link)
|
||||||
}
|
}
|
||||||
|
@ -1124,35 +1144,13 @@ mod tests {
|
||||||
"https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Flemmy-beta%2Fimage.png",
|
"https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Flemmy-beta%2Fimage.png",
|
||||||
proxied.as_str()
|
proxied.as_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// This fails, because the details can't be fetched without pictrs running,
|
||||||
|
// And a remote image won't be inserted.
|
||||||
assert!(
|
assert!(
|
||||||
RemoteImage::validate(&mut context.pool(), remote_image.into())
|
RemoteImage::validate(&mut context.pool(), remote_image.into())
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
|
||||||
async fn test_diesel_option_overwrite_to_url() {
|
|
||||||
let context = LemmyContext::init_test_context().await;
|
|
||||||
|
|
||||||
assert!(matches!(
|
|
||||||
proxy_image_link_api(&None, &context).await,
|
|
||||||
Ok(None)
|
|
||||||
));
|
|
||||||
assert!(matches!(
|
|
||||||
proxy_image_link_opt_api(&Some(String::new()), &context).await,
|
|
||||||
Ok(Some(None))
|
|
||||||
));
|
|
||||||
assert!(
|
|
||||||
proxy_image_link_opt_api(&Some("invalid_url".to_string()), &context)
|
|
||||||
.await
|
.await
|
||||||
.is_err()
|
.is_err()
|
||||||
);
|
);
|
||||||
let example_url = "https://lemmy-alpha/image.png";
|
|
||||||
assert!(matches!(
|
|
||||||
proxy_image_link_opt_api(&Some(example_url.to_string()), &context).await,
|
|
||||||
Ok(Some(Some(url))) if url == Url::parse(example_url).unwrap().into()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ pub async fn create_comment(
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?;
|
let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?;
|
||||||
is_valid_body_field(&Some(content.clone()), false)?;
|
is_valid_body_field(&content, false)?;
|
||||||
|
|
||||||
// Check for a community ban
|
// Check for a community ban
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
|
|
|
@ -63,7 +63,9 @@ pub async fn update_comment(
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
let content = process_markdown_opt(&data.content, &slur_regex, &url_blocklist, &context).await?;
|
let content = process_markdown_opt(&data.content, &slur_regex, &url_blocklist, &context).await?;
|
||||||
is_valid_body_field(&content, false)?;
|
if let Some(content) = &content {
|
||||||
|
is_valid_body_field(content, false)?;
|
||||||
|
}
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
let comment_id = data.comment_id;
|
||||||
let form = CommentUpdateForm {
|
let form = CommentUpdateForm {
|
||||||
|
|
|
@ -30,6 +30,7 @@ use lemmy_db_schema::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
traits::{ApubActor, Crud, Followable, Joinable},
|
traits::{ApubActor, Crud, Followable, Joinable},
|
||||||
|
utils::diesel_url_create,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
|
@ -61,11 +62,18 @@ pub async fn create_community(
|
||||||
check_slurs(&data.title, &slur_regex)?;
|
check_slurs(&data.title, &slur_regex)?;
|
||||||
let description =
|
let description =
|
||||||
process_markdown_opt(&data.description, &slur_regex, &url_blocklist, &context).await?;
|
process_markdown_opt(&data.description, &slur_regex, &url_blocklist, &context).await?;
|
||||||
let icon = proxy_image_link_api(&data.icon, &context).await?;
|
|
||||||
let banner = proxy_image_link_api(&data.banner, &context).await?;
|
let icon = diesel_url_create(data.icon.as_deref())?;
|
||||||
|
let icon = proxy_image_link_api(icon, &context).await?;
|
||||||
|
|
||||||
|
let banner = diesel_url_create(data.banner.as_deref())?;
|
||||||
|
let banner = proxy_image_link_api(banner, &context).await?;
|
||||||
|
|
||||||
is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?;
|
is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?;
|
||||||
is_valid_body_field(&data.description, false)?;
|
|
||||||
|
if let Some(desc) = &data.description {
|
||||||
|
is_valid_body_field(desc, false)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Double check for duplicate community actor_ids
|
// Double check for duplicate community actor_ids
|
||||||
let community_actor_id = generate_local_apub_endpoint(
|
let community_actor_id = generate_local_apub_endpoint(
|
||||||
|
|
|
@ -21,7 +21,7 @@ use lemmy_db_schema::{
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::{diesel_option_overwrite, naive_now},
|
utils::{diesel_string_update, diesel_url_update, naive_now},
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::LocalUserView;
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
|
@ -40,18 +40,28 @@ pub async fn update_community(
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
check_slurs_opt(&data.title, &slur_regex)?;
|
check_slurs_opt(&data.title, &slur_regex)?;
|
||||||
let description =
|
|
||||||
process_markdown_opt(&data.description, &slur_regex, &url_blocklist, &context).await?;
|
let description = diesel_string_update(
|
||||||
is_valid_body_field(&data.description, false)?;
|
process_markdown_opt(&data.description, &slur_regex, &url_blocklist, &context)
|
||||||
|
.await?
|
||||||
|
.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(Some(desc)) = &description {
|
||||||
|
is_valid_body_field(desc, false)?;
|
||||||
|
}
|
||||||
|
|
||||||
let old_community = Community::read(&mut context.pool(), data.community_id)
|
let old_community = Community::read(&mut context.pool(), data.community_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or(LemmyErrorType::CouldntFindCommunity)?;
|
.ok_or(LemmyErrorType::CouldntFindCommunity)?;
|
||||||
replace_image(&data.icon, &old_community.icon, &context).await?;
|
|
||||||
replace_image(&data.banner, &old_community.banner, &context).await?;
|
|
||||||
|
|
||||||
let description = diesel_option_overwrite(description);
|
let icon = diesel_url_update(data.icon.as_deref())?;
|
||||||
let icon = proxy_image_link_opt_api(&data.icon, &context).await?;
|
replace_image(&icon, &old_community.icon, &context).await?;
|
||||||
let banner = proxy_image_link_opt_api(&data.banner, &context).await?;
|
let icon = proxy_image_link_opt_api(icon, &context).await?;
|
||||||
|
|
||||||
|
let banner = diesel_url_update(data.banner.as_deref())?;
|
||||||
|
replace_image(&banner, &old_community.banner, &context).await?;
|
||||||
|
let banner = proxy_image_link_opt_api(banner, &context).await?;
|
||||||
|
|
||||||
// Verify its a mod (only mods can edit it)
|
// Verify its a mod (only mods can edit it)
|
||||||
check_community_mod_action(
|
check_community_mod_action(
|
||||||
|
|
|
@ -26,6 +26,7 @@ use lemmy_db_schema::{
|
||||||
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm},
|
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm},
|
||||||
},
|
},
|
||||||
traits::{Crud, Likeable},
|
traits::{Crud, Likeable},
|
||||||
|
utils::diesel_url_create,
|
||||||
CommunityVisibility,
|
CommunityVisibility,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::LocalUserView;
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
@ -37,7 +38,6 @@ use lemmy_utils::{
|
||||||
slurs::check_slurs,
|
slurs::check_slurs,
|
||||||
validation::{
|
validation::{
|
||||||
check_url_scheme,
|
check_url_scheme,
|
||||||
clean_url_params,
|
|
||||||
is_url_blocked,
|
is_url_blocked,
|
||||||
is_valid_alt_text_field,
|
is_valid_alt_text_field,
|
||||||
is_valid_body_field,
|
is_valid_body_field,
|
||||||
|
@ -64,16 +64,27 @@ pub async fn create_post(
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
|
|
||||||
let body = process_markdown_opt(&data.body, &slur_regex, &url_blocklist, &context).await?;
|
let body = process_markdown_opt(&data.body, &slur_regex, &url_blocklist, &context).await?;
|
||||||
let data_url = data.url.as_ref();
|
let url = diesel_url_create(data.url.as_deref())?;
|
||||||
let url = data_url.map(clean_url_params); // TODO no good way to handle a "clear"
|
let custom_thumbnail = diesel_url_create(data.custom_thumbnail.as_deref())?;
|
||||||
let custom_thumbnail = data.custom_thumbnail.as_ref().map(clean_url_params);
|
|
||||||
|
|
||||||
is_valid_post_title(&data.name)?;
|
is_valid_post_title(&data.name)?;
|
||||||
is_valid_body_field(&body, true)?;
|
|
||||||
is_valid_alt_text_field(&data.alt_text)?;
|
if let Some(url) = &url {
|
||||||
is_url_blocked(&url, &url_blocklist)?;
|
is_url_blocked(url, &url_blocklist)?;
|
||||||
check_url_scheme(&url)?;
|
check_url_scheme(url)?;
|
||||||
check_url_scheme(&custom_thumbnail)?;
|
}
|
||||||
|
|
||||||
|
if let Some(custom_thumbnail) = &custom_thumbnail {
|
||||||
|
check_url_scheme(custom_thumbnail)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(alt_text) = &data.alt_text {
|
||||||
|
is_valid_alt_text_field(alt_text)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(body) = &body {
|
||||||
|
is_valid_body_field(body, true)?;
|
||||||
|
}
|
||||||
|
|
||||||
check_community_user_action(
|
check_community_user_action(
|
||||||
&local_user_view.person,
|
&local_user_view.person,
|
||||||
|
@ -156,7 +167,7 @@ pub async fn create_post(
|
||||||
|
|
||||||
generate_post_link_metadata(
|
generate_post_link_metadata(
|
||||||
updated_post.clone(),
|
updated_post.clone(),
|
||||||
custom_thumbnail,
|
custom_thumbnail.map(Into::into),
|
||||||
|post| Some(SendActivityData::CreatePost(post)),
|
|post| Some(SendActivityData::CreatePost(post)),
|
||||||
Some(local_site),
|
Some(local_site),
|
||||||
context.reset_request_count(),
|
context.reset_request_count(),
|
||||||
|
|
|
@ -83,11 +83,13 @@ pub async fn get_post(
|
||||||
.ok_or(LemmyErrorType::CouldntFindCommunity)?;
|
.ok_or(LemmyErrorType::CouldntFindCommunity)?;
|
||||||
|
|
||||||
let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
|
let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
|
||||||
|
let local_user = local_user_view.as_ref().map(|u| &u.local_user);
|
||||||
|
|
||||||
// Fetch the cross_posts
|
// Fetch the cross_posts
|
||||||
let cross_posts = if let Some(url) = &post_view.post.url {
|
let cross_posts = if let Some(url) = &post_view.post.url {
|
||||||
let mut x_posts = PostQuery {
|
let mut x_posts = PostQuery {
|
||||||
url_search: Some(url.inner().as_str().into()),
|
url_search: Some(url.inner().as_str().into()),
|
||||||
|
local_user,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(&local_site.site, &mut context.pool())
|
.list(&local_site.site, &mut context.pool())
|
||||||
|
|
|
@ -20,16 +20,15 @@ use lemmy_db_schema::{
|
||||||
post::{Post, PostUpdateForm},
|
post::{Post, PostUpdateForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::{diesel_option_overwrite, naive_now},
|
utils::{diesel_string_update, diesel_url_update, naive_now},
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::LocalUserView;
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||||
utils::{
|
utils::{
|
||||||
slurs::check_slurs_opt,
|
slurs::check_slurs,
|
||||||
validation::{
|
validation::{
|
||||||
check_url_scheme,
|
check_url_scheme,
|
||||||
clean_url_params,
|
|
||||||
is_url_blocked,
|
is_url_blocked,
|
||||||
is_valid_alt_text_field,
|
is_valid_alt_text_field,
|
||||||
is_valid_body_field,
|
is_valid_body_field,
|
||||||
|
@ -47,26 +46,43 @@ pub async fn update_post(
|
||||||
) -> LemmyResult<Json<PostResponse>> {
|
) -> LemmyResult<Json<PostResponse>> {
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
|
||||||
// TODO No good way to handle a clear.
|
let url = diesel_url_update(data.url.as_deref())?;
|
||||||
// Issue link: https://github.com/LemmyNet/lemmy/issues/2287
|
|
||||||
let url = data.url.as_ref().map(clean_url_params);
|
let custom_thumbnail = diesel_url_update(data.custom_thumbnail.as_deref())?;
|
||||||
let custom_thumbnail = data.custom_thumbnail.as_ref().map(clean_url_params);
|
|
||||||
|
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
|
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
check_slurs_opt(&data.name, &slur_regex)?;
|
|
||||||
let body = process_markdown_opt(&data.body, &slur_regex, &url_blocklist, &context).await?;
|
let body = diesel_string_update(
|
||||||
|
process_markdown_opt(&data.body, &slur_regex, &url_blocklist, &context)
|
||||||
|
.await?
|
||||||
|
.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let alt_text = diesel_string_update(data.alt_text.as_deref());
|
||||||
|
|
||||||
if let Some(name) = &data.name {
|
if let Some(name) = &data.name {
|
||||||
is_valid_post_title(name)?;
|
is_valid_post_title(name)?;
|
||||||
|
check_slurs(name, &slur_regex)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_valid_body_field(&body, true)?;
|
if let Some(Some(body)) = &body {
|
||||||
is_valid_alt_text_field(&data.alt_text)?;
|
is_valid_body_field(body, true)?;
|
||||||
is_url_blocked(&url, &url_blocklist)?;
|
}
|
||||||
check_url_scheme(&url)?;
|
|
||||||
check_url_scheme(&custom_thumbnail)?;
|
if let Some(Some(alt_text)) = &alt_text {
|
||||||
|
is_valid_alt_text_field(alt_text)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Some(url)) = &url {
|
||||||
|
is_url_blocked(url, &url_blocklist)?;
|
||||||
|
check_url_scheme(url)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Some(custom_thumbnail)) = &custom_thumbnail {
|
||||||
|
check_url_scheme(custom_thumbnail)?;
|
||||||
|
}
|
||||||
|
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
let orig_post = Post::read(&mut context.pool(), post_id)
|
let orig_post = Post::read(&mut context.pool(), post_id)
|
||||||
|
@ -95,9 +111,9 @@ pub async fn update_post(
|
||||||
|
|
||||||
let post_form = PostUpdateForm {
|
let post_form = PostUpdateForm {
|
||||||
name: data.name.clone(),
|
name: data.name.clone(),
|
||||||
url: Some(url.map(Into::into)),
|
url,
|
||||||
body: diesel_option_overwrite(body),
|
body,
|
||||||
alt_text: diesel_option_overwrite(data.alt_text.clone()),
|
alt_text,
|
||||||
nsfw: data.nsfw,
|
nsfw: data.nsfw,
|
||||||
language_id: data.language_id,
|
language_id: data.language_id,
|
||||||
updated: Some(Some(naive_now())),
|
updated: Some(Some(naive_now())),
|
||||||
|
@ -111,7 +127,7 @@ pub async fn update_post(
|
||||||
|
|
||||||
generate_post_link_metadata(
|
generate_post_link_metadata(
|
||||||
updated_post.clone(),
|
updated_post.clone(),
|
||||||
custom_thumbnail,
|
custom_thumbnail.flatten().map(Into::into),
|
||||||
|post| Some(SendActivityData::UpdatePost(post)),
|
|post| Some(SendActivityData::UpdatePost(post)),
|
||||||
Some(local_site),
|
Some(local_site),
|
||||||
context.reset_request_count(),
|
context.reset_request_count(),
|
||||||
|
|
|
@ -39,7 +39,7 @@ pub async fn create_private_message(
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?;
|
let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?;
|
||||||
is_valid_body_field(&Some(content.clone()), false)?;
|
is_valid_body_field(&content, false)?;
|
||||||
|
|
||||||
check_person_block(
|
check_person_block(
|
||||||
local_user_view.person.id,
|
local_user_view.person.id,
|
||||||
|
|
|
@ -41,7 +41,7 @@ pub async fn update_private_message(
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?;
|
let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?;
|
||||||
is_valid_body_field(&Some(content.clone()), false)?;
|
is_valid_body_field(&content, false)?;
|
||||||
|
|
||||||
let private_message_id = data.private_message_id;
|
let private_message_id = data.private_message_id;
|
||||||
PrivateMessage::update(
|
PrivateMessage::update(
|
||||||
|
|
|
@ -11,7 +11,7 @@ use lemmy_api_common::{
|
||||||
local_site_rate_limit_to_rate_limit_config,
|
local_site_rate_limit_to_rate_limit_config,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
process_markdown_opt,
|
process_markdown_opt,
|
||||||
proxy_image_link_opt_api,
|
proxy_image_link_api,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -23,7 +23,7 @@ use lemmy_db_schema::{
|
||||||
tagline::Tagline,
|
tagline::Tagline,
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::{diesel_option_overwrite, naive_now},
|
utils::{diesel_string_update, diesel_url_create, naive_now},
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
|
@ -61,21 +61,25 @@ pub async fn create_site(
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
let sidebar = process_markdown_opt(&data.sidebar, &slur_regex, &url_blocklist, &context).await?;
|
let sidebar = process_markdown_opt(&data.sidebar, &slur_regex, &url_blocklist, &context).await?;
|
||||||
let icon = proxy_image_link_opt_api(&data.icon, &context).await?;
|
|
||||||
let banner = proxy_image_link_opt_api(&data.banner, &context).await?;
|
let icon = diesel_url_create(data.icon.as_deref())?;
|
||||||
|
let icon = proxy_image_link_api(icon, &context).await?;
|
||||||
|
|
||||||
|
let banner = diesel_url_create(data.banner.as_deref())?;
|
||||||
|
let banner = proxy_image_link_api(banner, &context).await?;
|
||||||
|
|
||||||
let site_form = SiteUpdateForm {
|
let site_form = SiteUpdateForm {
|
||||||
name: Some(data.name.clone()),
|
name: Some(data.name.clone()),
|
||||||
sidebar: diesel_option_overwrite(sidebar),
|
sidebar: diesel_string_update(sidebar.as_deref()),
|
||||||
description: diesel_option_overwrite(data.description.clone()),
|
description: diesel_string_update(data.description.as_deref()),
|
||||||
icon,
|
icon: Some(icon),
|
||||||
banner,
|
banner: Some(banner),
|
||||||
actor_id: Some(actor_id),
|
actor_id: Some(actor_id),
|
||||||
last_refreshed_at: Some(naive_now()),
|
last_refreshed_at: Some(naive_now()),
|
||||||
inbox_url,
|
inbox_url,
|
||||||
private_key: Some(Some(keypair.private_key)),
|
private_key: Some(Some(keypair.private_key)),
|
||||||
public_key: Some(keypair.public_key),
|
public_key: Some(keypair.public_key),
|
||||||
content_warning: diesel_option_overwrite(data.content_warning.clone()),
|
content_warning: diesel_string_update(data.content_warning.as_deref()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,16 +95,16 @@ pub async fn create_site(
|
||||||
enable_nsfw: data.enable_nsfw,
|
enable_nsfw: data.enable_nsfw,
|
||||||
community_creation_admin_only: data.community_creation_admin_only,
|
community_creation_admin_only: data.community_creation_admin_only,
|
||||||
require_email_verification: data.require_email_verification,
|
require_email_verification: data.require_email_verification,
|
||||||
application_question: diesel_option_overwrite(data.application_question.clone()),
|
application_question: diesel_string_update(data.application_question.as_deref()),
|
||||||
private_instance: data.private_instance,
|
private_instance: data.private_instance,
|
||||||
default_theme: data.default_theme.clone(),
|
default_theme: data.default_theme.clone(),
|
||||||
default_post_listing_type: data.default_post_listing_type,
|
default_post_listing_type: data.default_post_listing_type,
|
||||||
default_sort_type: data.default_sort_type,
|
default_sort_type: data.default_sort_type,
|
||||||
legal_information: diesel_option_overwrite(data.legal_information.clone()),
|
legal_information: diesel_string_update(data.legal_information.as_deref()),
|
||||||
application_email_admins: data.application_email_admins,
|
application_email_admins: data.application_email_admins,
|
||||||
hide_modlog_mod_names: data.hide_modlog_mod_names,
|
hide_modlog_mod_names: data.hide_modlog_mod_names,
|
||||||
updated: Some(Some(naive_now())),
|
updated: Some(Some(naive_now())),
|
||||||
slur_filter_regex: diesel_option_overwrite(data.slur_filter_regex.clone()),
|
slur_filter_regex: diesel_string_update(data.slur_filter_regex.as_deref()),
|
||||||
actor_name_max_length: data.actor_name_max_length,
|
actor_name_max_length: data.actor_name_max_length,
|
||||||
federation_enabled: data.federation_enabled,
|
federation_enabled: data.federation_enabled,
|
||||||
captcha_enabled: data.captcha_enabled,
|
captcha_enabled: data.captcha_enabled,
|
||||||
|
@ -179,7 +183,9 @@ fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) ->
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Ensure that the sidebar has fewer than the max num characters...
|
// Ensure that the sidebar has fewer than the max num characters...
|
||||||
is_valid_body_field(&create_site.sidebar, false)?;
|
if let Some(body) = &create_site.sidebar {
|
||||||
|
is_valid_body_field(body, false)?;
|
||||||
|
}
|
||||||
|
|
||||||
application_question_check(
|
application_question_check(
|
||||||
&local_site.application_question,
|
&local_site.application_question,
|
||||||
|
|
|
@ -27,7 +27,7 @@ use lemmy_db_schema::{
|
||||||
tagline::Tagline,
|
tagline::Tagline,
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::{diesel_option_overwrite, naive_now},
|
utils::{diesel_string_update, diesel_url_update, naive_now},
|
||||||
RegistrationMode,
|
RegistrationMode,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||||
|
@ -67,22 +67,29 @@ pub async fn update_site(
|
||||||
SiteLanguage::update(&mut context.pool(), discussion_languages.clone(), &site).await?;
|
SiteLanguage::update(&mut context.pool(), discussion_languages.clone(), &site).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
replace_image(&data.icon, &site.icon, &context).await?;
|
|
||||||
replace_image(&data.banner, &site.banner, &context).await?;
|
|
||||||
|
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
let sidebar = process_markdown_opt(&data.sidebar, &slur_regex, &url_blocklist, &context).await?;
|
let sidebar = diesel_string_update(
|
||||||
let icon = proxy_image_link_opt_api(&data.icon, &context).await?;
|
process_markdown_opt(&data.sidebar, &slur_regex, &url_blocklist, &context)
|
||||||
let banner = proxy_image_link_opt_api(&data.banner, &context).await?;
|
.await?
|
||||||
|
.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let icon = diesel_url_update(data.icon.as_deref())?;
|
||||||
|
replace_image(&icon, &site.icon, &context).await?;
|
||||||
|
let icon = proxy_image_link_opt_api(icon, &context).await?;
|
||||||
|
|
||||||
|
let banner = diesel_url_update(data.banner.as_deref())?;
|
||||||
|
replace_image(&banner, &site.banner, &context).await?;
|
||||||
|
let banner = proxy_image_link_opt_api(banner, &context).await?;
|
||||||
|
|
||||||
let site_form = SiteUpdateForm {
|
let site_form = SiteUpdateForm {
|
||||||
name: data.name.clone(),
|
name: data.name.clone(),
|
||||||
sidebar: diesel_option_overwrite(sidebar),
|
sidebar,
|
||||||
description: diesel_option_overwrite(data.description.clone()),
|
description: diesel_string_update(data.description.as_deref()),
|
||||||
icon,
|
icon,
|
||||||
banner,
|
banner,
|
||||||
content_warning: diesel_option_overwrite(data.content_warning.clone()),
|
content_warning: diesel_string_update(data.content_warning.as_deref()),
|
||||||
updated: Some(Some(naive_now())),
|
updated: Some(Some(naive_now())),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
@ -99,16 +106,16 @@ pub async fn update_site(
|
||||||
enable_nsfw: data.enable_nsfw,
|
enable_nsfw: data.enable_nsfw,
|
||||||
community_creation_admin_only: data.community_creation_admin_only,
|
community_creation_admin_only: data.community_creation_admin_only,
|
||||||
require_email_verification: data.require_email_verification,
|
require_email_verification: data.require_email_verification,
|
||||||
application_question: diesel_option_overwrite(data.application_question.clone()),
|
application_question: diesel_string_update(data.application_question.as_deref()),
|
||||||
private_instance: data.private_instance,
|
private_instance: data.private_instance,
|
||||||
default_theme: data.default_theme.clone(),
|
default_theme: data.default_theme.clone(),
|
||||||
default_post_listing_type: data.default_post_listing_type,
|
default_post_listing_type: data.default_post_listing_type,
|
||||||
default_sort_type: data.default_sort_type,
|
default_sort_type: data.default_sort_type,
|
||||||
legal_information: diesel_option_overwrite(data.legal_information.clone()),
|
legal_information: diesel_string_update(data.legal_information.as_deref()),
|
||||||
application_email_admins: data.application_email_admins,
|
application_email_admins: data.application_email_admins,
|
||||||
hide_modlog_mod_names: data.hide_modlog_mod_names,
|
hide_modlog_mod_names: data.hide_modlog_mod_names,
|
||||||
updated: Some(Some(naive_now())),
|
updated: Some(Some(naive_now())),
|
||||||
slur_filter_regex: diesel_option_overwrite(data.slur_filter_regex.clone()),
|
slur_filter_regex: diesel_string_update(data.slur_filter_regex.as_deref()),
|
||||||
actor_name_max_length: data.actor_name_max_length,
|
actor_name_max_length: data.actor_name_max_length,
|
||||||
federation_enabled: data.federation_enabled,
|
federation_enabled: data.federation_enabled,
|
||||||
captcha_enabled: data.captcha_enabled,
|
captcha_enabled: data.captcha_enabled,
|
||||||
|
@ -229,7 +236,9 @@ fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> Lemm
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Ensure that the sidebar has fewer than the max num characters...
|
// Ensure that the sidebar has fewer than the max num characters...
|
||||||
is_valid_body_field(&edit_site.sidebar, false)?;
|
if let Some(body) = &edit_site.sidebar {
|
||||||
|
is_valid_body_field(body, false)?;
|
||||||
|
}
|
||||||
|
|
||||||
application_question_check(
|
application_question_check(
|
||||||
&local_site.application_question,
|
&local_site.application_question,
|
||||||
|
|
|
@ -112,15 +112,17 @@ pub async fn register(
|
||||||
// We have to create both a person, and local_user
|
// We have to create both a person, and local_user
|
||||||
|
|
||||||
// Register the new person
|
// Register the new person
|
||||||
let person_form = PersonInsertForm::builder()
|
let person_form = PersonInsertForm {
|
||||||
.name(data.username.clone())
|
actor_id: Some(actor_id.clone()),
|
||||||
.actor_id(Some(actor_id.clone()))
|
inbox_url: Some(generate_inbox_url(&actor_id)?),
|
||||||
.private_key(Some(actor_keypair.private_key))
|
shared_inbox_url: Some(generate_shared_inbox_url(context.settings())?),
|
||||||
.public_key(actor_keypair.public_key)
|
private_key: Some(actor_keypair.private_key),
|
||||||
.inbox_url(Some(generate_inbox_url(&actor_id)?))
|
..PersonInsertForm::new(
|
||||||
.shared_inbox_url(Some(generate_shared_inbox_url(context.settings())?))
|
data.username.clone(),
|
||||||
.instance_id(site_view.site.instance_id)
|
actor_keypair.public_key,
|
||||||
.build();
|
site_view.site.instance_id,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
// insert the person
|
// insert the person
|
||||||
let inserted_person = Person::create(&mut context.pool(), &person_form)
|
let inserted_person = Person::create(&mut context.pool(), &person_form)
|
||||||
|
|
|
@ -37,11 +37,11 @@ pub async fn list_comments(
|
||||||
};
|
};
|
||||||
let sort = data.sort;
|
let sort = data.sort;
|
||||||
let max_depth = data.max_depth;
|
let max_depth = data.max_depth;
|
||||||
let saved_only = data.saved_only.unwrap_or_default();
|
let saved_only = data.saved_only;
|
||||||
|
|
||||||
let liked_only = data.liked_only.unwrap_or_default();
|
let liked_only = data.liked_only;
|
||||||
let disliked_only = data.disliked_only.unwrap_or_default();
|
let disliked_only = data.disliked_only;
|
||||||
if liked_only && disliked_only {
|
if liked_only.unwrap_or_default() && disliked_only.unwrap_or_default() {
|
||||||
return Err(LemmyError::from(LemmyErrorType::ContradictingFilters));
|
return Err(LemmyError::from(LemmyErrorType::ContradictingFilters));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,8 @@ pub async fn list_comments(
|
||||||
|
|
||||||
let parent_path_cloned = parent_path.clone();
|
let parent_path_cloned = parent_path.clone();
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
|
let local_user = local_user_view.as_ref().map(|l| &l.local_user);
|
||||||
|
|
||||||
let comments = CommentQuery {
|
let comments = CommentQuery {
|
||||||
listing_type,
|
listing_type,
|
||||||
sort,
|
sort,
|
||||||
|
@ -80,7 +82,7 @@ pub async fn list_comments(
|
||||||
community_id,
|
community_id,
|
||||||
parent_path: parent_path_cloned,
|
parent_path: parent_path_cloned,
|
||||||
post_id,
|
post_id,
|
||||||
local_user: local_user_view.as_ref(),
|
local_user,
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -40,26 +40,27 @@ pub async fn list_posts(
|
||||||
} else {
|
} else {
|
||||||
data.community_id
|
data.community_id
|
||||||
};
|
};
|
||||||
let saved_only = data.saved_only.unwrap_or_default();
|
let saved_only = data.saved_only;
|
||||||
let show_hidden = data.show_hidden.unwrap_or_default();
|
let show_hidden = data.show_hidden;
|
||||||
|
let show_read = data.show_read;
|
||||||
|
|
||||||
let liked_only = data.liked_only.unwrap_or_default();
|
let liked_only = data.liked_only;
|
||||||
let disliked_only = data.disliked_only.unwrap_or_default();
|
let disliked_only = data.disliked_only;
|
||||||
if liked_only && disliked_only {
|
if liked_only.unwrap_or_default() && disliked_only.unwrap_or_default() {
|
||||||
return Err(LemmyError::from(LemmyErrorType::ContradictingFilters));
|
return Err(LemmyError::from(LemmyErrorType::ContradictingFilters));
|
||||||
}
|
}
|
||||||
|
|
||||||
let local_user_ref = local_user_view.as_ref().map(|u| &u.local_user);
|
let local_user = local_user_view.as_ref().map(|u| &u.local_user);
|
||||||
let listing_type = Some(listing_type_with_default(
|
let listing_type = Some(listing_type_with_default(
|
||||||
data.type_,
|
data.type_,
|
||||||
local_user_ref,
|
local_user,
|
||||||
&local_site.local_site,
|
&local_site.local_site,
|
||||||
community_id,
|
community_id,
|
||||||
));
|
));
|
||||||
|
|
||||||
let sort = Some(sort_type_with_default(
|
let sort = Some(sort_type_with_default(
|
||||||
data.sort,
|
data.sort,
|
||||||
local_user_ref,
|
local_user,
|
||||||
&local_site.local_site,
|
&local_site.local_site,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ pub async fn list_posts(
|
||||||
};
|
};
|
||||||
|
|
||||||
let posts = PostQuery {
|
let posts = PostQuery {
|
||||||
local_user: local_user_view.as_ref(),
|
local_user,
|
||||||
listing_type,
|
listing_type,
|
||||||
sort,
|
sort,
|
||||||
community_id,
|
community_id,
|
||||||
|
@ -82,6 +83,7 @@ pub async fn list_posts(
|
||||||
page_after,
|
page_after,
|
||||||
limit,
|
limit,
|
||||||
show_hidden,
|
show_hidden,
|
||||||
|
show_read,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(&local_site.site, &mut context.pool())
|
.list(&local_site.site, &mut context.pool())
|
||||||
|
|
|
@ -55,20 +55,22 @@ pub async fn read_person(
|
||||||
let sort = data.sort;
|
let sort = data.sort;
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let saved_only = data.saved_only.unwrap_or_default();
|
let saved_only = data.saved_only;
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
// If its saved only, you don't care what creator it was
|
// If its saved only, you don't care what creator it was
|
||||||
// Or, if its not saved, then you only want it for that specific creator
|
// Or, if its not saved, then you only want it for that specific creator
|
||||||
let creator_id = if !saved_only {
|
let creator_id = if !saved_only.unwrap_or_default() {
|
||||||
Some(person_details_id)
|
Some(person_details_id)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let local_user = local_user_view.as_ref().map(|l| &l.local_user);
|
||||||
|
|
||||||
let posts = PostQuery {
|
let posts = PostQuery {
|
||||||
sort,
|
sort,
|
||||||
saved_only,
|
saved_only,
|
||||||
local_user: local_user_view.as_ref(),
|
local_user,
|
||||||
community_id,
|
community_id,
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
|
@ -79,7 +81,7 @@ pub async fn read_person(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let comments = CommentQuery {
|
let comments = CommentQuery {
|
||||||
local_user: local_user_view.as_ref(),
|
local_user,
|
||||||
sort: sort.map(post_to_comment_sort_type),
|
sort: sort.map(post_to_comment_sort_type),
|
||||||
saved_only,
|
saved_only,
|
||||||
community_id,
|
community_id,
|
||||||
|
|
|
@ -55,7 +55,7 @@ pub async fn search(
|
||||||
data.community_id
|
data.community_id
|
||||||
};
|
};
|
||||||
let creator_id = data.creator_id;
|
let creator_id = data.creator_id;
|
||||||
let local_user = local_user_view.as_ref().map(|luv| &luv.local_user);
|
let local_user = local_user_view.as_ref().map(|l| &l.local_user);
|
||||||
|
|
||||||
match search_type {
|
match search_type {
|
||||||
SearchType::Posts => {
|
SearchType::Posts => {
|
||||||
|
@ -64,7 +64,7 @@ pub async fn search(
|
||||||
listing_type: (listing_type),
|
listing_type: (listing_type),
|
||||||
community_id: (community_id),
|
community_id: (community_id),
|
||||||
creator_id: (creator_id),
|
creator_id: (creator_id),
|
||||||
local_user: (local_user_view.as_ref()),
|
local_user,
|
||||||
search_term: (Some(q)),
|
search_term: (Some(q)),
|
||||||
page: (page),
|
page: (page),
|
||||||
limit: (limit),
|
limit: (limit),
|
||||||
|
@ -80,7 +80,7 @@ pub async fn search(
|
||||||
search_term: (Some(q)),
|
search_term: (Some(q)),
|
||||||
community_id: (community_id),
|
community_id: (community_id),
|
||||||
creator_id: (creator_id),
|
creator_id: (creator_id),
|
||||||
local_user: (local_user_view.as_ref()),
|
local_user,
|
||||||
page: (page),
|
page: (page),
|
||||||
limit: (limit),
|
limit: (limit),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -125,7 +125,7 @@ pub async fn search(
|
||||||
listing_type: (listing_type),
|
listing_type: (listing_type),
|
||||||
community_id: (community_id),
|
community_id: (community_id),
|
||||||
creator_id: (creator_id),
|
creator_id: (creator_id),
|
||||||
local_user: (local_user_view.as_ref()),
|
local_user,
|
||||||
search_term: (Some(q)),
|
search_term: (Some(q)),
|
||||||
page: (page),
|
page: (page),
|
||||||
limit: (limit),
|
limit: (limit),
|
||||||
|
@ -142,7 +142,7 @@ pub async fn search(
|
||||||
search_term: (Some(q)),
|
search_term: (Some(q)),
|
||||||
community_id: (community_id),
|
community_id: (community_id),
|
||||||
creator_id: (creator_id),
|
creator_id: (creator_id),
|
||||||
local_user: (local_user_view.as_ref()),
|
local_user,
|
||||||
page: (page),
|
page: (page),
|
||||||
limit: (limit),
|
limit: (limit),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -192,6 +192,7 @@ pub async fn search(
|
||||||
community_id: (community_id),
|
community_id: (community_id),
|
||||||
creator_id: (creator_id),
|
creator_id: (creator_id),
|
||||||
url_search: (Some(q)),
|
url_search: (Some(q)),
|
||||||
|
local_user,
|
||||||
page: (page),
|
page: (page),
|
||||||
limit: (limit),
|
limit: (limit),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -338,13 +338,11 @@ mod tests {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
) -> LemmyResult<LocalUserView> {
|
) -> LemmyResult<LocalUserView> {
|
||||||
let instance = Instance::read_or_create(&mut context.pool(), "example.com".to_string()).await?;
|
let instance = Instance::read_or_create(&mut context.pool(), "example.com".to_string()).await?;
|
||||||
let person_form = PersonInsertForm::builder()
|
let person_form = PersonInsertForm {
|
||||||
.name(name.clone())
|
display_name: Some(name.clone()),
|
||||||
.display_name(Some(name.clone()))
|
bio,
|
||||||
.bio(bio)
|
..PersonInsertForm::test_form(instance.id, &name)
|
||||||
.public_key("asd".to_string())
|
};
|
||||||
.instance_id(instance.id)
|
|
||||||
.build();
|
|
||||||
let person = Person::create(&mut context.pool(), &person_form).await?;
|
let person = Person::create(&mut context.pool(), &person_form).await?;
|
||||||
|
|
||||||
let user_form = LocalUserInsertForm::builder()
|
let user_form = LocalUserInsertForm::builder()
|
||||||
|
|
|
@ -129,11 +129,7 @@ mod tests {
|
||||||
let inserted_instance =
|
let inserted_instance =
|
||||||
Instance::read_or_create(&mut context.pool(), "my_domain.tld".to_string()).await?;
|
Instance::read_or_create(&mut context.pool(), "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
let old_mod = PersonInsertForm::builder()
|
let old_mod = PersonInsertForm::test_form(inserted_instance.id, "holly");
|
||||||
.name("holly".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let old_mod = Person::create(&mut context.pool(), &old_mod).await?;
|
let old_mod = Person::create(&mut context.pool(), &old_mod).await?;
|
||||||
let community_moderator_form = CommunityModeratorForm {
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
|
|
|
@ -219,7 +219,10 @@ impl Object for ApubPost {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
check_url_scheme(&url)?;
|
|
||||||
|
if let Some(url) = &url {
|
||||||
|
check_url_scheme(url)?;
|
||||||
|
}
|
||||||
|
|
||||||
let alt_text = first_attachment.cloned().and_then(Attachment::alt_text);
|
let alt_text = first_attachment.cloned().and_then(Attachment::alt_text);
|
||||||
|
|
||||||
|
|
|
@ -72,11 +72,7 @@ async fn try_main() -> LemmyResult<()> {
|
||||||
println!("🫃 creating {} people", args.people);
|
println!("🫃 creating {} people", args.people);
|
||||||
let mut person_ids = vec![];
|
let mut person_ids = vec![];
|
||||||
for i in 0..args.people.get() {
|
for i in 0..args.people.get() {
|
||||||
let form = PersonInsertForm::builder()
|
let form = PersonInsertForm::test_form(instance.id, &format!("p{i}"));
|
||||||
.name(format!("p{i}"))
|
|
||||||
.public_key("pubkey".to_owned())
|
|
||||||
.instance_id(instance.id)
|
|
||||||
.build();
|
|
||||||
person_ids.push(Person::create(&mut conn.into(), &form).await?.id);
|
person_ids.push(Person::create(&mut conn.into(), &form).await?.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,7 @@ uuid = { workspace = true, features = ["v4"] }
|
||||||
i-love-jesus = { workspace = true, optional = true }
|
i-love-jesus = { workspace = true, optional = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
moka.workspace = true
|
moka.workspace = true
|
||||||
|
derive-new.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = { workspace = true }
|
serial_test = { workspace = true }
|
||||||
|
|
|
@ -5,12 +5,17 @@
|
||||||
-- (even if only other columns are updated) because triggers can run after the deletion of referenced rows and
|
-- (even if only other columns are updated) because triggers can run after the deletion of referenced rows and
|
||||||
-- before the automatic deletion of the row that references it. This is not a problem for insert or delete.
|
-- before the automatic deletion of the row that references it. This is not a problem for insert or delete.
|
||||||
--
|
--
|
||||||
-- After a row update begins, a concurrent update on the same row can't begin until the whole
|
-- Triggers that update multiple tables should use this order: person_aggregates, comment_aggregates,
|
||||||
-- transaction that contains the first update is finished. To reduce this locking, statements in
|
-- post_aggregates, community_aggregates, site_aggregates
|
||||||
-- triggers should be ordered based on the likelihood of concurrent writers. For example, updating
|
-- * The order matters because the updated rows are locked until the end of the transaction, and statements
|
||||||
-- site_aggregates should be done last because the same row is updated for all local stuff. If
|
-- in a trigger don't use separate transactions. This means that updates closer to the beginning cause
|
||||||
-- it were not last, then the locking period for concurrent writers would extend to include the
|
-- longer locks because the duration of each update extends the durations of the locks caused by previous
|
||||||
-- time consumed by statements that come after.
|
-- updates. Long locks are worse on rows that have more concurrent transactions trying to update them. The
|
||||||
|
-- listed order starts with tables that are less likely to have such rows.
|
||||||
|
-- https://www.postgresql.org/docs/16/transaction-iso.html#XACT-READ-COMMITTED
|
||||||
|
-- * Using the same order in every trigger matters because a deadlock is possible if multiple transactions
|
||||||
|
-- update the same rows in a different order.
|
||||||
|
-- https://www.postgresql.org/docs/current/explicit-locking.html#LOCKING-DEADLOCKS
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- Create triggers for both post and comments
|
-- Create triggers for both post and comments
|
||||||
|
@ -481,7 +486,7 @@ BEGIN
|
||||||
INNER JOIN old_post ON old_post.id = new_post.id
|
INNER JOIN old_post ON old_post.id = new_post.id
|
||||||
AND (old_post.featured_community,
|
AND (old_post.featured_community,
|
||||||
old_post.featured_local) != (new_post.featured_community,
|
old_post.featured_local) != (new_post.featured_community,
|
||||||
old_post.featured_local)
|
new_post.featured_local)
|
||||||
WHERE
|
WHERE
|
||||||
post_aggregates.post_id = new_post.id;
|
post_aggregates.post_id = new_post.id;
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
|
|
|
@ -64,19 +64,11 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_comment_agg");
|
||||||
.name("thommy_comment_agg".into())
|
|
||||||
.public_key("pubkey".into())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
let another_person = PersonInsertForm::builder()
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_comment_agg");
|
||||||
.name("jerry_comment_agg".into())
|
|
||||||
.public_key("pubkey".into())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -65,19 +65,11 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
||||||
.name("thommy_community_agg".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
let another_person = PersonInsertForm::builder()
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_community_agg");
|
||||||
.name("jerry_community_agg".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -49,19 +49,11 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_user_agg");
|
||||||
.name("thommy_user_agg".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
let another_person = PersonInsertForm::builder()
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_user_agg");
|
||||||
.name("jerry_user_agg".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -83,19 +83,11 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
||||||
.name("thommy_community_agg".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
let another_person = PersonInsertForm::builder()
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_community_agg");
|
||||||
.name("jerry_community_agg".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
||||||
|
|
||||||
|
@ -229,11 +221,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
||||||
.name("thommy_community_agg".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -42,11 +42,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_site_agg");
|
||||||
.name("thommy_site_agg".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -531,11 +531,7 @@ mod tests {
|
||||||
|
|
||||||
let (site, instance) = create_test_site(pool).await;
|
let (site, instance) = create_test_site(pool).await;
|
||||||
|
|
||||||
let person_form = PersonInsertForm::builder()
|
let person_form = PersonInsertForm::test_form(instance.id, "my test person");
|
||||||
.name("my test person".to_string())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(instance.id)
|
|
||||||
.build();
|
|
||||||
let person = Person::create(pool, &person_form).await.unwrap();
|
let person = Person::create(pool, &person_form).await.unwrap();
|
||||||
let local_user_form = LocalUserInsertForm::builder()
|
let local_user_form = LocalUserInsertForm::builder()
|
||||||
.person_id(person.id)
|
.person_id(person.id)
|
||||||
|
@ -647,11 +643,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let person_form = PersonInsertForm::builder()
|
let person_form = PersonInsertForm::test_form(instance.id, "my test person");
|
||||||
.name("my test person".to_string())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(instance.id)
|
|
||||||
.build();
|
|
||||||
let person = Person::create(pool, &person_form).await.unwrap();
|
let person = Person::create(pool, &person_form).await.unwrap();
|
||||||
let local_user_form = LocalUserInsertForm::builder()
|
let local_user_form = LocalUserInsertForm::builder()
|
||||||
.person_id(person.id)
|
.person_id(person.id)
|
||||||
|
|
|
@ -118,8 +118,9 @@ impl Crud for Comment {
|
||||||
type IdType = CommentId;
|
type IdType = CommentId;
|
||||||
|
|
||||||
/// This is unimplemented, use [[Comment::create]]
|
/// This is unimplemented, use [[Comment::create]]
|
||||||
async fn create(_pool: &mut DbPool<'_>, _comment_form: &Self::InsertForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, comment_form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
unimplemented!();
|
debug_assert!(false);
|
||||||
|
Comment::create(pool, comment_form, None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update(
|
async fn update(
|
||||||
|
@ -233,11 +234,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "terry");
|
||||||
.name("terry".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -434,11 +434,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "bobbee");
|
||||||
.name("bobbee".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::DbUrl,
|
newtypes::DbUrl,
|
||||||
schema::{local_image, remote_image},
|
schema::{image_details, local_image, remote_image},
|
||||||
source::images::{LocalImage, LocalImageForm, RemoteImage, RemoteImageForm},
|
source::images::{
|
||||||
|
ImageDetails,
|
||||||
|
ImageDetailsForm,
|
||||||
|
LocalImage,
|
||||||
|
LocalImageForm,
|
||||||
|
RemoteImage,
|
||||||
|
RemoteImageForm,
|
||||||
|
},
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
|
@ -13,15 +20,29 @@ use diesel::{
|
||||||
NotFound,
|
NotFound,
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::{AsyncPgConnection, RunQueryDsl};
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
impl LocalImage {
|
impl LocalImage {
|
||||||
pub async fn create(pool: &mut DbPool<'_>, form: &LocalImageForm) -> Result<Self, Error> {
|
pub async fn create(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
form: &LocalImageForm,
|
||||||
|
image_details_form: &ImageDetailsForm,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(local_image::table)
|
conn
|
||||||
.values(form)
|
.build_transaction()
|
||||||
.get_result::<Self>(conn)
|
.run(|conn| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let local_insert = insert_into(local_image::table)
|
||||||
|
.values(form)
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
ImageDetails::create(conn, image_details_form).await?;
|
||||||
|
|
||||||
|
local_insert
|
||||||
|
}) as _
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,16 +60,26 @@ impl LocalImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RemoteImage {
|
impl RemoteImage {
|
||||||
pub async fn create(pool: &mut DbPool<'_>, links: Vec<Url>) -> Result<usize, Error> {
|
pub async fn create(pool: &mut DbPool<'_>, form: &ImageDetailsForm) -> Result<usize, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
let forms = links
|
conn
|
||||||
.into_iter()
|
.build_transaction()
|
||||||
.map(|url| RemoteImageForm { link: url.into() })
|
.run(|conn| {
|
||||||
.collect::<Vec<_>>();
|
Box::pin(async move {
|
||||||
insert_into(remote_image::table)
|
let remote_image_form = RemoteImageForm {
|
||||||
.values(forms)
|
link: form.link.clone(),
|
||||||
.on_conflict_do_nothing()
|
};
|
||||||
.execute(conn)
|
let remote_insert = insert_into(remote_image::table)
|
||||||
|
.values(remote_image_form)
|
||||||
|
.on_conflict_do_nothing()
|
||||||
|
.execute(conn)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
ImageDetails::create(conn, form).await?;
|
||||||
|
|
||||||
|
remote_insert
|
||||||
|
}) as _
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,3 +98,16 @@ impl RemoteImage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImageDetails {
|
||||||
|
pub(crate) async fn create(
|
||||||
|
conn: &mut AsyncPgConnection,
|
||||||
|
form: &ImageDetailsForm,
|
||||||
|
) -> Result<usize, Error> {
|
||||||
|
insert_into(image_details::table)
|
||||||
|
.values(form)
|
||||||
|
.on_conflict_do_nothing()
|
||||||
|
.execute(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
||||||
actor_language::LocalUserLanguage,
|
actor_language::LocalUserLanguage,
|
||||||
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||||
local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeInsertForm},
|
local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeInsertForm},
|
||||||
|
site::Site,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
functions::{coalesce, lower},
|
functions::{coalesce, lower},
|
||||||
|
@ -216,6 +217,44 @@ impl LocalUser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds some helper functions for an optional LocalUser
|
||||||
|
pub trait LocalUserOptionHelper {
|
||||||
|
fn person_id(&self) -> Option<PersonId>;
|
||||||
|
fn local_user_id(&self) -> Option<LocalUserId>;
|
||||||
|
fn show_bot_accounts(&self) -> bool;
|
||||||
|
fn show_read_posts(&self) -> bool;
|
||||||
|
fn is_admin(&self) -> bool;
|
||||||
|
fn show_nsfw(&self, site: &Site) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalUserOptionHelper for Option<&LocalUser> {
|
||||||
|
fn person_id(&self) -> Option<PersonId> {
|
||||||
|
self.map(|l| l.person_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn local_user_id(&self) -> Option<LocalUserId> {
|
||||||
|
self.map(|l| l.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_bot_accounts(&self) -> bool {
|
||||||
|
self.map(|l| l.show_bot_accounts).unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_read_posts(&self) -> bool {
|
||||||
|
self.map(|l| l.show_read_posts).unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_admin(&self) -> bool {
|
||||||
|
self.map(|l| l.admin).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_nsfw(&self, site: &Site) -> bool {
|
||||||
|
self
|
||||||
|
.map(|l| l.show_nsfw)
|
||||||
|
.unwrap_or(site.content_warning.is_some())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LocalUserInsertForm {
|
impl LocalUserInsertForm {
|
||||||
pub fn test_form(person_id: PersonId) -> Self {
|
pub fn test_form(person_id: PersonId) -> Self {
|
||||||
Self::builder()
|
Self::builder()
|
||||||
|
|
|
@ -513,19 +513,11 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_mod = PersonInsertForm::builder()
|
let new_mod = PersonInsertForm::test_form(inserted_instance.id, "the mod");
|
||||||
.name("the mod".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_mod = Person::create(pool, &new_mod).await.unwrap();
|
let inserted_mod = Person::create(pool, &new_mod).await.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "jim2");
|
||||||
.name("jim2".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -70,11 +70,7 @@ mod tests {
|
||||||
|
|
||||||
// Setup
|
// Setup
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy prw");
|
||||||
.name("thommy prw".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await?;
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
let new_local_user = LocalUserInsertForm::builder()
|
let new_local_user = LocalUserInsertForm::builder()
|
||||||
.person_id(inserted_person.id)
|
.person_id(inserted_person.id)
|
||||||
|
|
|
@ -12,7 +12,14 @@ use crate::{
|
||||||
traits::{ApubActor, Crud, Followable},
|
traits::{ApubActor, Crud, Followable},
|
||||||
utils::{functions::lower, get_conn, naive_now, DbPool},
|
utils::{functions::lower, get_conn, naive_now, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{dsl::insert_into, result::Error, CombineDsl, ExpressionMethods, JoinOnDsl, QueryDsl};
|
use diesel::{
|
||||||
|
dsl::{insert_into, not},
|
||||||
|
result::Error,
|
||||||
|
CombineDsl,
|
||||||
|
ExpressionMethods,
|
||||||
|
JoinOnDsl,
|
||||||
|
QueryDsl,
|
||||||
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -100,6 +107,8 @@ impl Person {
|
||||||
.inner_join(post::table)
|
.inner_join(post::table)
|
||||||
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
||||||
.filter(community::local.eq(true))
|
.filter(community::local.eq(true))
|
||||||
|
.filter(not(community::deleted))
|
||||||
|
.filter(not(community::removed))
|
||||||
.filter(comment::creator_id.eq(for_creator_id))
|
.filter(comment::creator_id.eq(for_creator_id))
|
||||||
.select(community::id)
|
.select(community::id)
|
||||||
.union(
|
.union(
|
||||||
|
@ -116,11 +125,7 @@ impl Person {
|
||||||
|
|
||||||
impl PersonInsertForm {
|
impl PersonInsertForm {
|
||||||
pub fn test_form(instance_id: InstanceId, name: &str) -> Self {
|
pub fn test_form(instance_id: InstanceId, name: &str) -> Self {
|
||||||
Self::builder()
|
Self::new(name.to_owned(), "pubkey".to_string(), instance_id)
|
||||||
.name(name.to_owned())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(instance_id)
|
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,11 +245,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "holly");
|
||||||
.name("holly".into())
|
|
||||||
.public_key("nada".to_owned())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
|
@ -263,7 +264,7 @@ mod tests {
|
||||||
local: true,
|
local: true,
|
||||||
bot_account: false,
|
bot_account: false,
|
||||||
private_key: None,
|
private_key: None,
|
||||||
public_key: "nada".to_owned(),
|
public_key: "pubkey".to_owned(),
|
||||||
last_refreshed_at: inserted_person.published,
|
last_refreshed_at: inserted_person.published,
|
||||||
inbox_url: inserted_person.inbox_url.clone(),
|
inbox_url: inserted_person.inbox_url.clone(),
|
||||||
shared_inbox_url: None,
|
shared_inbox_url: None,
|
||||||
|
@ -303,17 +304,9 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let person_form_1 = PersonInsertForm::builder()
|
let person_form_1 = PersonInsertForm::test_form(inserted_instance.id, "erich");
|
||||||
.name("erich".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
let person_1 = Person::create(pool, &person_form_1).await.unwrap();
|
let person_1 = Person::create(pool, &person_form_1).await.unwrap();
|
||||||
let person_form_2 = PersonInsertForm::builder()
|
let person_form_2 = PersonInsertForm::test_form(inserted_instance.id, "michele");
|
||||||
.name("michele".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
let person_2 = Person::create(pool, &person_form_2).await.unwrap();
|
let person_2 = Person::create(pool, &person_form_2).await.unwrap();
|
||||||
|
|
||||||
let follow_form = PersonFollowerForm {
|
let follow_form = PersonFollowerForm {
|
||||||
|
|
|
@ -401,11 +401,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "jim");
|
||||||
.name("jim".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -101,11 +101,7 @@ mod tests {
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let person_form = PersonInsertForm::builder()
|
let person_form = PersonInsertForm::test_form(inserted_instance.id, "jim");
|
||||||
.name("jim".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
let person = Person::create(pool, &person_form).await.unwrap();
|
let person = Person::create(pool, &person_form).await.unwrap();
|
||||||
|
|
||||||
let community_form = CommunityInsertForm::builder()
|
let community_form = CommunityInsertForm::builder()
|
||||||
|
|
|
@ -111,19 +111,11 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let creator_form = PersonInsertForm::builder()
|
let creator_form = PersonInsertForm::test_form(inserted_instance.id, "creator_pm");
|
||||||
.name("creator_pm".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_creator = Person::create(pool, &creator_form).await.unwrap();
|
let inserted_creator = Person::create(pool, &creator_form).await.unwrap();
|
||||||
|
|
||||||
let recipient_form = PersonInsertForm::builder()
|
let recipient_form = PersonInsertForm::test_form(inserted_instance.id, "recipient_pm");
|
||||||
.name("recipient_pm".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_recipient = Person::create(pool, &recipient_form).await.unwrap();
|
let inserted_recipient = Person::create(pool, &recipient_form).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -309,6 +309,15 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
image_details (link) {
|
||||||
|
link -> Text,
|
||||||
|
width -> Int4,
|
||||||
|
height -> Int4,
|
||||||
|
content_type -> Text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
instance (id) {
|
instance (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
@ -856,8 +865,7 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
remote_image (id) {
|
remote_image (link) {
|
||||||
id -> Int4,
|
|
||||||
link -> Text,
|
link -> Text,
|
||||||
published -> Timestamptz,
|
published -> Timestamptz,
|
||||||
}
|
}
|
||||||
|
@ -1062,6 +1070,7 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
federation_allowlist,
|
federation_allowlist,
|
||||||
federation_blocklist,
|
federation_blocklist,
|
||||||
federation_queue_state,
|
federation_queue_state,
|
||||||
|
image_details,
|
||||||
instance,
|
instance,
|
||||||
instance_block,
|
instance_block,
|
||||||
language,
|
language,
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use crate::newtypes::{DbUrl, LocalUserId};
|
use crate::newtypes::{DbUrl, LocalUserId};
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
use crate::schema::{local_image, remote_image};
|
use crate::schema::{image_details, local_image, remote_image};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
use typed_builder::TypedBuilder;
|
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||||
|
@ -30,7 +29,7 @@ pub struct LocalImage {
|
||||||
pub published: DateTime<Utc>,
|
pub published: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, TypedBuilder)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = local_image))]
|
#[cfg_attr(feature = "full", diesel(table_name = local_image))]
|
||||||
pub struct LocalImageForm {
|
pub struct LocalImageForm {
|
||||||
|
@ -46,15 +45,39 @@ pub struct LocalImageForm {
|
||||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable))]
|
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = remote_image))]
|
#[cfg_attr(feature = "full", diesel(table_name = remote_image))]
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(primary_key(link)))]
|
||||||
pub struct RemoteImage {
|
pub struct RemoteImage {
|
||||||
pub id: i32,
|
|
||||||
pub link: DbUrl,
|
pub link: DbUrl,
|
||||||
pub published: DateTime<Utc>,
|
pub published: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, TypedBuilder)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = remote_image))]
|
#[cfg_attr(feature = "full", diesel(table_name = remote_image))]
|
||||||
pub struct RemoteImageForm {
|
pub struct RemoteImageForm {
|
||||||
pub link: DbUrl,
|
pub link: DbUrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[skip_serializing_none]
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||||
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = image_details))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(primary_key(link)))]
|
||||||
|
pub struct ImageDetails {
|
||||||
|
pub link: DbUrl,
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32,
|
||||||
|
pub content_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = image_details))]
|
||||||
|
pub struct ImageDetailsForm {
|
||||||
|
pub link: DbUrl,
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32,
|
||||||
|
pub content_type: String,
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
use typed_builder::TypedBuilder;
|
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||||
|
@ -60,33 +59,46 @@ pub struct Person {
|
||||||
pub instance_id: InstanceId,
|
pub instance_id: InstanceId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, TypedBuilder)]
|
#[derive(Clone, derive_new::new)]
|
||||||
#[builder(field_defaults(default))]
|
|
||||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = person))]
|
#[cfg_attr(feature = "full", diesel(table_name = person))]
|
||||||
pub struct PersonInsertForm {
|
pub struct PersonInsertForm {
|
||||||
#[builder(!default)]
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[builder(!default)]
|
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
#[builder(!default)]
|
|
||||||
pub instance_id: InstanceId,
|
pub instance_id: InstanceId,
|
||||||
|
#[new(default)]
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
|
#[new(default)]
|
||||||
pub avatar: Option<DbUrl>,
|
pub avatar: Option<DbUrl>,
|
||||||
|
#[new(default)]
|
||||||
pub banned: Option<bool>,
|
pub banned: Option<bool>,
|
||||||
|
#[new(default)]
|
||||||
pub published: Option<DateTime<Utc>>,
|
pub published: Option<DateTime<Utc>>,
|
||||||
|
#[new(default)]
|
||||||
pub updated: Option<DateTime<Utc>>,
|
pub updated: Option<DateTime<Utc>>,
|
||||||
|
#[new(default)]
|
||||||
pub actor_id: Option<DbUrl>,
|
pub actor_id: Option<DbUrl>,
|
||||||
|
#[new(default)]
|
||||||
pub bio: Option<String>,
|
pub bio: Option<String>,
|
||||||
|
#[new(default)]
|
||||||
pub local: Option<bool>,
|
pub local: Option<bool>,
|
||||||
|
#[new(default)]
|
||||||
pub private_key: Option<String>,
|
pub private_key: Option<String>,
|
||||||
|
#[new(default)]
|
||||||
pub last_refreshed_at: Option<DateTime<Utc>>,
|
pub last_refreshed_at: Option<DateTime<Utc>>,
|
||||||
|
#[new(default)]
|
||||||
pub banner: Option<DbUrl>,
|
pub banner: Option<DbUrl>,
|
||||||
|
#[new(default)]
|
||||||
pub deleted: Option<bool>,
|
pub deleted: Option<bool>,
|
||||||
|
#[new(default)]
|
||||||
pub inbox_url: Option<DbUrl>,
|
pub inbox_url: Option<DbUrl>,
|
||||||
|
#[new(default)]
|
||||||
pub shared_inbox_url: Option<DbUrl>,
|
pub shared_inbox_url: Option<DbUrl>,
|
||||||
|
#[new(default)]
|
||||||
pub matrix_user_id: Option<String>,
|
pub matrix_user_id: Option<String>,
|
||||||
|
#[new(default)]
|
||||||
pub bot_account: Option<bool>,
|
pub bot_account: Option<bool>,
|
||||||
|
#[new(default)]
|
||||||
pub ban_expires: Option<DateTime<Utc>>,
|
pub ban_expires: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
use crate::{newtypes::DbUrl, schema_setup, CommentSortType, SortType};
|
use crate::{
|
||||||
|
diesel::ExpressionMethods,
|
||||||
|
newtypes::{DbUrl, PersonId},
|
||||||
|
schema::community,
|
||||||
|
schema_setup,
|
||||||
|
CommentSortType,
|
||||||
|
CommunityVisibility,
|
||||||
|
SortType,
|
||||||
|
};
|
||||||
use chrono::{DateTime, TimeDelta, Utc};
|
use chrono::{DateTime, TimeDelta, Utc};
|
||||||
use deadpool::Runtime;
|
use deadpool::Runtime;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
|
dsl,
|
||||||
helper_types::AsExprOf,
|
helper_types::AsExprOf,
|
||||||
pg::Pg,
|
pg::Pg,
|
||||||
query_builder::{Query, QueryFragment},
|
query_builder::{Query, QueryFragment},
|
||||||
|
@ -29,6 +38,7 @@ use i_love_jesus::CursorKey;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||||
settings::SETTINGS,
|
settings::SETTINGS,
|
||||||
|
utils::validation::clean_url_params,
|
||||||
};
|
};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
@ -287,37 +297,35 @@ pub fn is_email_regex(test: &str) -> bool {
|
||||||
EMAIL_REGEX.is_match(test)
|
EMAIL_REGEX.is_match(test)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diesel_option_overwrite(opt: Option<String>) -> Option<Option<String>> {
|
/// Takes an API text input, and converts it to an optional diesel DB update.
|
||||||
|
pub fn diesel_string_update(opt: Option<&str>) -> Option<Option<String>> {
|
||||||
match opt {
|
match opt {
|
||||||
// An empty string is an erase
|
// An empty string is an erase
|
||||||
Some(unwrapped) => {
|
Some("") => Some(None),
|
||||||
if !unwrapped.eq("") {
|
Some(str) => Some(Some(str.into())),
|
||||||
Some(Some(unwrapped))
|
|
||||||
} else {
|
|
||||||
Some(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diesel_option_overwrite_to_url(opt: &Option<String>) -> LemmyResult<Option<Option<DbUrl>>> {
|
/// Takes an optional API URL-type input, and converts it to an optional diesel DB update.
|
||||||
match opt.as_ref().map(String::as_str) {
|
/// Also cleans the url params.
|
||||||
|
pub fn diesel_url_update(opt: Option<&str>) -> LemmyResult<Option<Option<DbUrl>>> {
|
||||||
|
match opt {
|
||||||
// An empty string is an erase
|
// An empty string is an erase
|
||||||
Some("") => Ok(Some(None)),
|
Some("") => Ok(Some(None)),
|
||||||
Some(str_url) => Url::parse(str_url)
|
Some(str_url) => Url::parse(str_url)
|
||||||
.map(|u| Some(Some(u.into())))
|
.map(|u| Some(Some(clean_url_params(&u).into())))
|
||||||
.with_lemmy_type(LemmyErrorType::InvalidUrl),
|
.with_lemmy_type(LemmyErrorType::InvalidUrl),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diesel_option_overwrite_to_url_create(opt: &Option<String>) -> LemmyResult<Option<DbUrl>> {
|
/// Takes an optional API URL-type input, and converts it to an optional diesel DB create.
|
||||||
match opt.as_ref().map(String::as_str) {
|
/// Also cleans the url params.
|
||||||
// An empty string is nothing
|
pub fn diesel_url_create(opt: Option<&str>) -> LemmyResult<Option<DbUrl>> {
|
||||||
Some("") => Ok(None),
|
match opt {
|
||||||
Some(str_url) => Url::parse(str_url)
|
Some(str_url) => Url::parse(str_url)
|
||||||
.map(|u| Some(u.into()))
|
.map(|u| Some(clean_url_params(&u).into()))
|
||||||
.with_lemmy_type(LemmyErrorType::InvalidUrl),
|
.with_lemmy_type(LemmyErrorType::InvalidUrl),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
|
@ -325,6 +333,10 @@ pub fn diesel_option_overwrite_to_url_create(opt: &Option<String>) -> LemmyResul
|
||||||
|
|
||||||
fn establish_connection(config: &str) -> BoxFuture<ConnectionResult<AsyncPgConnection>> {
|
fn establish_connection(config: &str) -> BoxFuture<ConnectionResult<AsyncPgConnection>> {
|
||||||
let fut = async {
|
let fut = async {
|
||||||
|
rustls::crypto::ring::default_provider()
|
||||||
|
.install_default()
|
||||||
|
.expect("Failed to install rustls crypto provider");
|
||||||
|
|
||||||
let rustls_config = DangerousClientConfigBuilder {
|
let rustls_config = DangerousClientConfigBuilder {
|
||||||
cfg: ClientConfig::builder(),
|
cfg: ClientConfig::builder(),
|
||||||
}
|
}
|
||||||
|
@ -568,8 +580,21 @@ impl<RF, LF> Queries<RF, LF> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn visible_communities_only<Q>(my_person_id: Option<PersonId>, query: Q) -> Q
|
||||||
|
where
|
||||||
|
Q: diesel::query_dsl::methods::FilterDsl<
|
||||||
|
dsl::Eq<community::visibility, CommunityVisibility>,
|
||||||
|
Output = Q,
|
||||||
|
>,
|
||||||
|
{
|
||||||
|
if my_person_id.is_none() {
|
||||||
|
query.filter(community::visibility.eq(CommunityVisibility::Public))
|
||||||
|
} else {
|
||||||
|
query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[allow(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
@ -593,26 +618,24 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_diesel_option_overwrite() {
|
fn test_diesel_option_overwrite() {
|
||||||
assert_eq!(diesel_option_overwrite(None), None);
|
assert_eq!(diesel_string_update(None), None);
|
||||||
assert_eq!(diesel_option_overwrite(Some(String::new())), Some(None));
|
assert_eq!(diesel_string_update(Some("")), Some(None));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
diesel_option_overwrite(Some("test".to_string())),
|
diesel_string_update(Some("test")),
|
||||||
Some(Some("test".to_string()))
|
Some(Some("test".to_string()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_diesel_option_overwrite_to_url() {
|
fn test_diesel_option_overwrite_to_url() -> LemmyResult<()> {
|
||||||
assert!(matches!(diesel_option_overwrite_to_url(&None), Ok(None)));
|
assert!(matches!(diesel_url_update(None), Ok(None)));
|
||||||
assert!(matches!(
|
assert!(matches!(diesel_url_update(Some("")), Ok(Some(None))));
|
||||||
diesel_option_overwrite_to_url(&Some(String::new())),
|
assert!(diesel_url_update(Some("invalid_url")).is_err());
|
||||||
Ok(Some(None))
|
|
||||||
));
|
|
||||||
assert!(diesel_option_overwrite_to_url(&Some("invalid_url".to_string())).is_err());
|
|
||||||
let example_url = "https://example.com";
|
let example_url = "https://example.com";
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
diesel_option_overwrite_to_url(&Some(example_url.to_string())),
|
diesel_url_update(Some(example_url)),
|
||||||
Ok(Some(Some(url))) if url == Url::parse(example_url).unwrap().into()
|
Ok(Some(Some(url))) if url == Url::parse(example_url)?.into()
|
||||||
));
|
));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,11 +297,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "timmy_crv");
|
||||||
.name("timmy_crv".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
|
let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
|
@ -319,20 +315,12 @@ mod tests {
|
||||||
counts: Default::default(),
|
counts: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_person_2 = PersonInsertForm::builder()
|
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "sara_crv");
|
||||||
.name("sara_crv".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_sara = Person::create(pool, &new_person_2).await.unwrap();
|
let inserted_sara = Person::create(pool, &new_person_2).await.unwrap();
|
||||||
|
|
||||||
// Add a third person, since new ppl can only report something once.
|
// Add a third person, since new ppl can only report something once.
|
||||||
let new_person_3 = PersonInsertForm::builder()
|
let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "jessica_crv");
|
||||||
.name("jessica_crv".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_jessica = Person::create(pool, &new_person_3).await.unwrap();
|
let inserted_jessica = Person::create(pool, &new_person_3).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::structs::{CommentView, LocalUserView};
|
use crate::structs::CommentView;
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use diesel::{
|
use diesel::{
|
||||||
dsl::{exists, not},
|
dsl::{exists, not},
|
||||||
pg::Pg,
|
pg::Pg,
|
||||||
|
@ -17,6 +16,7 @@ use diesel::{
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use diesel_ltree::{nlevel, subpath, Ltree, LtreeExtensions};
|
use diesel_ltree::{nlevel, subpath, Ltree, LtreeExtensions};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
impls::local_user::LocalUserOptionHelper,
|
||||||
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId},
|
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId},
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
|
@ -35,9 +35,18 @@ use lemmy_db_schema::{
|
||||||
person_block,
|
person_block,
|
||||||
post,
|
post,
|
||||||
},
|
},
|
||||||
utils::{fuzzy_search, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
source::local_user::LocalUser,
|
||||||
|
utils::{
|
||||||
|
fuzzy_search,
|
||||||
|
limit_and_offset,
|
||||||
|
visible_communities_only,
|
||||||
|
DbConn,
|
||||||
|
DbPool,
|
||||||
|
ListFn,
|
||||||
|
Queries,
|
||||||
|
ReadFn,
|
||||||
|
},
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
CommunityVisibility,
|
|
||||||
ListingType,
|
ListingType,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,17 +72,6 @@ fn queries<'a>() -> Queries<
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_saved = |person_id| {
|
|
||||||
comment_saved::table
|
|
||||||
.filter(
|
|
||||||
comment::id
|
|
||||||
.eq(comment_saved::comment_id)
|
|
||||||
.and(comment_saved::person_id.eq(person_id)),
|
|
||||||
)
|
|
||||||
.select(comment_saved::published.nullable())
|
|
||||||
.single_value()
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_community_followed = |person_id| {
|
let is_community_followed = |person_id| {
|
||||||
community_follower::table
|
community_follower::table
|
||||||
.filter(
|
.filter(
|
||||||
|
@ -147,14 +145,6 @@ fn queries<'a>() -> Queries<
|
||||||
Box::new(None::<bool>.into_sql::<sql_types::Nullable<sql_types::Bool>>())
|
Box::new(None::<bool>.into_sql::<sql_types::Nullable<sql_types::Bool>>())
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_saved_selection: Box<
|
|
||||||
dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable<sql_types::Timestamptz>>,
|
|
||||||
> = if let Some(person_id) = my_person_id {
|
|
||||||
Box::new(is_saved(person_id))
|
|
||||||
} else {
|
|
||||||
Box::new(None::<DateTime<Utc>>.into_sql::<sql_types::Nullable<sql_types::Timestamptz>>())
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_creator_blocked_selection: Box<dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>> =
|
let is_creator_blocked_selection: Box<dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>> =
|
||||||
if let Some(person_id) = my_person_id {
|
if let Some(person_id) = my_person_id {
|
||||||
Box::new(is_creator_blocked(person_id))
|
Box::new(is_creator_blocked(person_id))
|
||||||
|
@ -167,6 +157,13 @@ fn queries<'a>() -> Queries<
|
||||||
.inner_join(post::table)
|
.inner_join(post::table)
|
||||||
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
||||||
.inner_join(comment_aggregates::table)
|
.inner_join(comment_aggregates::table)
|
||||||
|
.left_join(
|
||||||
|
comment_saved::table.on(
|
||||||
|
comment::id
|
||||||
|
.eq(comment_saved::comment_id)
|
||||||
|
.and(comment_saved::person_id.eq(my_person_id.unwrap_or(PersonId(-1)))),
|
||||||
|
),
|
||||||
|
)
|
||||||
.select((
|
.select((
|
||||||
comment::all_columns,
|
comment::all_columns,
|
||||||
person::all_columns,
|
person::all_columns,
|
||||||
|
@ -178,7 +175,7 @@ fn queries<'a>() -> Queries<
|
||||||
creator_is_moderator,
|
creator_is_moderator,
|
||||||
creator_is_admin,
|
creator_is_admin,
|
||||||
subscribed_type_selection,
|
subscribed_type_selection,
|
||||||
is_saved_selection.is_not_null(),
|
comment_saved::person_id.nullable().is_not_null(),
|
||||||
is_creator_blocked_selection,
|
is_creator_blocked_selection,
|
||||||
score_selection,
|
score_selection,
|
||||||
))
|
))
|
||||||
|
@ -187,22 +184,19 @@ fn queries<'a>() -> Queries<
|
||||||
let read = move |mut conn: DbConn<'a>,
|
let read = move |mut conn: DbConn<'a>,
|
||||||
(comment_id, my_person_id): (CommentId, Option<PersonId>)| async move {
|
(comment_id, my_person_id): (CommentId, Option<PersonId>)| async move {
|
||||||
let mut query = all_joins(comment::table.find(comment_id).into_boxed(), my_person_id);
|
let mut query = all_joins(comment::table.find(comment_id).into_boxed(), my_person_id);
|
||||||
// Hide local only communities from unauthenticated users
|
query = visible_communities_only(my_person_id, query);
|
||||||
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 {
|
let list = move |mut conn: DbConn<'a>, options: CommentQuery<'a>| async move {
|
||||||
let my_person_id = options.local_user.map(|l| l.person.id);
|
|
||||||
let my_local_user_id = options.local_user.map(|l| l.local_user.id);
|
|
||||||
|
|
||||||
// The left join below will return None in this case
|
// The left join below will return None in this case
|
||||||
let person_id_join = my_person_id.unwrap_or(PersonId(-1));
|
let person_id_join = options.local_user.person_id().unwrap_or(PersonId(-1));
|
||||||
let local_user_id_join = my_local_user_id.unwrap_or(LocalUserId(-1));
|
let local_user_id_join = options
|
||||||
|
.local_user
|
||||||
|
.local_user_id()
|
||||||
|
.unwrap_or(LocalUserId(-1));
|
||||||
|
|
||||||
let mut query = all_joins(comment::table.into_boxed(), my_person_id);
|
let mut query = all_joins(comment::table.into_boxed(), options.local_user.person_id());
|
||||||
|
|
||||||
if let Some(creator_id) = options.creator_id {
|
if let Some(creator_id) = options.creator_id {
|
||||||
query = query.filter(comment::creator_id.eq(creator_id));
|
query = query.filter(comment::creator_id.eq(creator_id));
|
||||||
|
@ -258,26 +252,22 @@ fn queries<'a>() -> Queries<
|
||||||
}
|
}
|
||||||
|
|
||||||
// If its saved only, then filter, and order by the saved time, not the comment creation time.
|
// If its saved only, then filter, and order by the saved time, not the comment creation time.
|
||||||
if options.saved_only {
|
if options.saved_only.unwrap_or_default() {
|
||||||
query = query
|
query = query
|
||||||
.filter(is_saved(person_id_join).is_not_null())
|
.filter(comment_saved::person_id.is_not_null())
|
||||||
.then_order_by(is_saved(person_id_join).desc());
|
.then_order_by(comment_saved::published.desc());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(my_id) = my_person_id {
|
if let Some(my_id) = options.local_user.person_id() {
|
||||||
let not_creator_filter = comment::creator_id.ne(my_id);
|
let not_creator_filter = comment::creator_id.ne(my_id);
|
||||||
if options.liked_only {
|
if options.liked_only.unwrap_or_default() {
|
||||||
query = query.filter(not_creator_filter).filter(score(my_id).eq(1));
|
query = query.filter(not_creator_filter).filter(score(my_id).eq(1));
|
||||||
} else if options.disliked_only {
|
} else if options.disliked_only.unwrap_or_default() {
|
||||||
query = query.filter(not_creator_filter).filter(score(my_id).eq(-1));
|
query = query.filter(not_creator_filter).filter(score(my_id).eq(-1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options
|
if !options.local_user.show_bot_accounts() {
|
||||||
.local_user
|
|
||||||
.map(|l| l.local_user.show_bot_accounts)
|
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
|
||||||
query = query.filter(person::bot_account.eq(false));
|
query = query.filter(person::bot_account.eq(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -311,10 +301,7 @@ fn queries<'a>() -> Queries<
|
||||||
query = query.filter(not(is_creator_blocked(person_id_join)));
|
query = query.filter(not(is_creator_blocked(person_id_join)));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hide comments in local only communities from unauthenticated users
|
query = visible_communities_only(options.local_user.person_id(), query);
|
||||||
if options.local_user.is_none() {
|
|
||||||
query = query.filter(community::visibility.eq(CommunityVisibility::Public));
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Max depth given means its a tree fetch
|
// A Max depth given means its a tree fetch
|
||||||
let (limit, offset) = if let Some(max_depth) = options.max_depth {
|
let (limit, offset) = if let Some(max_depth) = options.max_depth {
|
||||||
|
@ -409,11 +396,11 @@ pub struct CommentQuery<'a> {
|
||||||
pub post_id: Option<PostId>,
|
pub post_id: Option<PostId>,
|
||||||
pub parent_path: Option<Ltree>,
|
pub parent_path: Option<Ltree>,
|
||||||
pub creator_id: Option<PersonId>,
|
pub creator_id: Option<PersonId>,
|
||||||
pub local_user: Option<&'a LocalUserView>,
|
pub local_user: Option<&'a LocalUser>,
|
||||||
pub search_term: Option<String>,
|
pub search_term: Option<String>,
|
||||||
pub saved_only: bool,
|
pub saved_only: Option<bool>,
|
||||||
pub liked_only: bool,
|
pub liked_only: Option<bool>,
|
||||||
pub disliked_only: bool,
|
pub disliked_only: Option<bool>,
|
||||||
pub page: Option<i64>,
|
pub page: Option<i64>,
|
||||||
pub limit: Option<i64>,
|
pub limit: Option<i64>,
|
||||||
pub max_depth: Option<i32>,
|
pub max_depth: Option<i32>,
|
||||||
|
@ -501,11 +488,7 @@ mod tests {
|
||||||
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
let timmy_person_form = PersonInsertForm::builder()
|
let timmy_person_form = PersonInsertForm::test_form(inserted_instance.id, "timmy");
|
||||||
.name("timmy".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
let inserted_timmy_person = Person::create(pool, &timmy_person_form).await?;
|
let inserted_timmy_person = Person::create(pool, &timmy_person_form).await?;
|
||||||
let timmy_local_user_form = LocalUserInsertForm::builder()
|
let timmy_local_user_form = LocalUserInsertForm::builder()
|
||||||
.person_id(inserted_timmy_person.id)
|
.person_id(inserted_timmy_person.id)
|
||||||
|
@ -514,11 +497,7 @@ mod tests {
|
||||||
.build();
|
.build();
|
||||||
let inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]).await?;
|
let inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]).await?;
|
||||||
|
|
||||||
let sara_person_form = PersonInsertForm::builder()
|
let sara_person_form = PersonInsertForm::test_form(inserted_instance.id, "sara");
|
||||||
.name("sara".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
let inserted_sara_person = Person::create(pool, &sara_person_form).await?;
|
let inserted_sara_person = Person::create(pool, &sara_person_form).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::builder()
|
let new_community = CommunityInsertForm::builder()
|
||||||
|
@ -680,7 +659,7 @@ mod tests {
|
||||||
let read_comment_views_with_person = CommentQuery {
|
let read_comment_views_with_person = CommentQuery {
|
||||||
sort: (Some(CommentSortType::Old)),
|
sort: (Some(CommentSortType::Old)),
|
||||||
post_id: (Some(data.inserted_post.id)),
|
post_id: (Some(data.inserted_post.id)),
|
||||||
local_user: (Some(&data.timmy_local_user_view)),
|
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(pool)
|
||||||
|
@ -732,8 +711,8 @@ mod tests {
|
||||||
CommentLike::like(pool, &comment_like_form).await?;
|
CommentLike::like(pool, &comment_like_form).await?;
|
||||||
|
|
||||||
let read_liked_comment_views = CommentQuery {
|
let read_liked_comment_views = CommentQuery {
|
||||||
local_user: (Some(&data.timmy_local_user_view)),
|
local_user: Some(&data.timmy_local_user_view.local_user),
|
||||||
liked_only: (true),
|
liked_only: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(pool)
|
||||||
|
@ -748,8 +727,8 @@ mod tests {
|
||||||
assert_length!(1, read_liked_comment_views);
|
assert_length!(1, read_liked_comment_views);
|
||||||
|
|
||||||
let read_disliked_comment_views: Vec<CommentView> = CommentQuery {
|
let read_disliked_comment_views: Vec<CommentView> = CommentQuery {
|
||||||
local_user: (Some(&data.timmy_local_user_view)),
|
local_user: Some(&data.timmy_local_user_view.local_user),
|
||||||
disliked_only: (true),
|
disliked_only: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(pool)
|
||||||
|
@ -843,7 +822,7 @@ mod tests {
|
||||||
// by default, user has all languages enabled and should see all comments
|
// by default, user has all languages enabled and should see all comments
|
||||||
// (except from blocked user)
|
// (except from blocked user)
|
||||||
let all_languages = CommentQuery {
|
let all_languages = CommentQuery {
|
||||||
local_user: (Some(&data.timmy_local_user_view)),
|
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(pool)
|
||||||
|
@ -861,7 +840,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let finnish_comments = CommentQuery {
|
let finnish_comments = CommentQuery {
|
||||||
local_user: (Some(&data.timmy_local_user_view)),
|
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(pool)
|
||||||
|
@ -887,7 +866,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let undetermined_comment = CommentQuery {
|
let undetermined_comment = CommentQuery {
|
||||||
local_user: (Some(&data.timmy_local_user_view)),
|
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(pool)
|
||||||
|
@ -1000,8 +979,8 @@ mod tests {
|
||||||
|
|
||||||
// Fetch the saved comments
|
// Fetch the saved comments
|
||||||
let comments = CommentQuery {
|
let comments = CommentQuery {
|
||||||
local_user: Some(&data.timmy_local_user_view),
|
local_user: Some(&data.timmy_local_user_view.local_user),
|
||||||
saved_only: true,
|
saved_only: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(pool)
|
||||||
|
@ -1179,7 +1158,7 @@ mod tests {
|
||||||
assert_eq!(0, unauthenticated_query.len());
|
assert_eq!(0, unauthenticated_query.len());
|
||||||
|
|
||||||
let authenticated_query = CommentQuery {
|
let authenticated_query = CommentQuery {
|
||||||
local_user: Some(&data.timmy_local_user_view),
|
local_user: Some(&data.timmy_local_user_view.local_user),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(pool)
|
||||||
|
|
|
@ -319,11 +319,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "timmy_prv");
|
||||||
.name("timmy_prv".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
|
let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
|
@ -341,20 +337,12 @@ mod tests {
|
||||||
counts: Default::default(),
|
counts: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_person_2 = PersonInsertForm::builder()
|
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "sara_prv");
|
||||||
.name("sara_prv".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_sara = Person::create(pool, &new_person_2).await.unwrap();
|
let inserted_sara = Person::create(pool, &new_person_2).await.unwrap();
|
||||||
|
|
||||||
// Add a third person, since new ppl can only report something once.
|
// Add a third person, since new ppl can only report something once.
|
||||||
let new_person_3 = PersonInsertForm::builder()
|
let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "jessica_prv");
|
||||||
.name("jessica_prv".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_jessica = Person::create(pool, &new_person_3).await.unwrap();
|
let inserted_jessica = Person::create(pool, &new_person_3).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::structs::{LocalUserView, PaginationCursor, PostView};
|
use crate::structs::{PaginationCursor, PostView};
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use diesel::{
|
use diesel::{
|
||||||
debug_query,
|
debug_query,
|
||||||
dsl::{exists, not, IntervalDsl},
|
dsl::{exists, not, IntervalDsl},
|
||||||
|
@ -21,6 +20,7 @@ use diesel_async::RunQueryDsl;
|
||||||
use i_love_jesus::PaginatedQueryBuilder;
|
use i_love_jesus::PaginatedQueryBuilder;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::{post_aggregates_keys as key, PostAggregates},
|
aggregates::structs::{post_aggregates_keys as key, PostAggregates},
|
||||||
|
impls::local_user::LocalUserOptionHelper,
|
||||||
newtypes::{CommunityId, LocalUserId, PersonId, PostId},
|
newtypes::{CommunityId, LocalUserId, PersonId, PostId},
|
||||||
schema::{
|
schema::{
|
||||||
community,
|
community,
|
||||||
|
@ -28,6 +28,7 @@ use lemmy_db_schema::{
|
||||||
community_follower,
|
community_follower,
|
||||||
community_moderator,
|
community_moderator,
|
||||||
community_person_ban,
|
community_person_ban,
|
||||||
|
image_details,
|
||||||
instance_block,
|
instance_block,
|
||||||
local_user,
|
local_user,
|
||||||
local_user_language,
|
local_user_language,
|
||||||
|
@ -41,13 +42,14 @@ use lemmy_db_schema::{
|
||||||
post_read,
|
post_read,
|
||||||
post_saved,
|
post_saved,
|
||||||
},
|
},
|
||||||
source::site::Site,
|
source::{local_user::LocalUser, site::Site},
|
||||||
utils::{
|
utils::{
|
||||||
functions::coalesce,
|
functions::coalesce,
|
||||||
fuzzy_search,
|
fuzzy_search,
|
||||||
get_conn,
|
get_conn,
|
||||||
limit_and_offset,
|
limit_and_offset,
|
||||||
now,
|
now,
|
||||||
|
visible_communities_only,
|
||||||
Commented,
|
Commented,
|
||||||
DbConn,
|
DbConn,
|
||||||
DbPool,
|
DbPool,
|
||||||
|
@ -56,7 +58,6 @@ use lemmy_db_schema::{
|
||||||
ReadFn,
|
ReadFn,
|
||||||
ReverseTimestampKey,
|
ReverseTimestampKey,
|
||||||
},
|
},
|
||||||
CommunityVisibility,
|
|
||||||
ListingType,
|
ListingType,
|
||||||
SortType,
|
SortType,
|
||||||
};
|
};
|
||||||
|
@ -100,17 +101,6 @@ fn queries<'a>() -> Queries<
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let is_saved = |person_id| {
|
|
||||||
post_saved::table
|
|
||||||
.filter(
|
|
||||||
post_aggregates::post_id
|
|
||||||
.eq(post_saved::post_id)
|
|
||||||
.and(post_saved::person_id.eq(person_id)),
|
|
||||||
)
|
|
||||||
.select(post_saved::published.nullable())
|
|
||||||
.single_value()
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_read = |person_id| {
|
let is_read = |person_id| {
|
||||||
exists(
|
exists(
|
||||||
post_read::table.filter(
|
post_read::table.filter(
|
||||||
|
@ -162,14 +152,6 @@ fn queries<'a>() -> Queries<
|
||||||
Box::new(false.into_sql::<sql_types::Bool>())
|
Box::new(false.into_sql::<sql_types::Bool>())
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_saved_selection: Box<
|
|
||||||
dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable<sql_types::Timestamptz>>,
|
|
||||||
> = if let Some(person_id) = my_person_id {
|
|
||||||
Box::new(is_saved(person_id))
|
|
||||||
} else {
|
|
||||||
Box::new(None::<DateTime<Utc>>.into_sql::<sql_types::Nullable<sql_types::Timestamptz>>())
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_read_selection: Box<dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>> =
|
let is_read_selection: Box<dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>> =
|
||||||
if let Some(person_id) = my_person_id {
|
if let Some(person_id) = my_person_id {
|
||||||
Box::new(is_read(person_id))
|
Box::new(is_read(person_id))
|
||||||
|
@ -237,17 +219,26 @@ fn queries<'a>() -> Queries<
|
||||||
.inner_join(person::table)
|
.inner_join(person::table)
|
||||||
.inner_join(community::table)
|
.inner_join(community::table)
|
||||||
.inner_join(post::table)
|
.inner_join(post::table)
|
||||||
|
.left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
|
||||||
|
.left_join(
|
||||||
|
post_saved::table.on(
|
||||||
|
post_aggregates::post_id
|
||||||
|
.eq(post_saved::post_id)
|
||||||
|
.and(post_saved::person_id.eq(my_person_id.unwrap_or(PersonId(-1)))),
|
||||||
|
),
|
||||||
|
)
|
||||||
.select((
|
.select((
|
||||||
post::all_columns,
|
post::all_columns,
|
||||||
person::all_columns,
|
person::all_columns,
|
||||||
community::all_columns,
|
community::all_columns,
|
||||||
|
image_details::all_columns.nullable(),
|
||||||
is_creator_banned_from_community,
|
is_creator_banned_from_community,
|
||||||
is_local_user_banned_from_community_selection,
|
is_local_user_banned_from_community_selection,
|
||||||
creator_is_moderator,
|
creator_is_moderator,
|
||||||
creator_is_admin,
|
creator_is_admin,
|
||||||
post_aggregates::all_columns,
|
post_aggregates::all_columns,
|
||||||
subscribed_type_selection,
|
subscribed_type_selection,
|
||||||
is_saved_selection.is_not_null(),
|
post_saved::person_id.nullable().is_not_null(),
|
||||||
is_read_selection,
|
is_read_selection,
|
||||||
is_hidden_selection,
|
is_hidden_selection,
|
||||||
is_creator_blocked_selection,
|
is_creator_blocked_selection,
|
||||||
|
@ -298,10 +289,7 @@ fn queries<'a>() -> Queries<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide posts in local only communities from unauthenticated users
|
query = visible_communities_only(my_person_id, query);
|
||||||
if my_person_id.is_none() {
|
|
||||||
query = query.filter(community::visibility.eq(CommunityVisibility::Public));
|
|
||||||
}
|
|
||||||
|
|
||||||
Commented::new(query)
|
Commented::new(query)
|
||||||
.text("PostView::read")
|
.text("PostView::read")
|
||||||
|
@ -310,31 +298,30 @@ fn queries<'a>() -> Queries<
|
||||||
};
|
};
|
||||||
|
|
||||||
let list = move |mut conn: DbConn<'a>, (options, site): (PostQuery<'a>, &'a Site)| async move {
|
let list = move |mut conn: DbConn<'a>, (options, site): (PostQuery<'a>, &'a Site)| async move {
|
||||||
let my_person_id = options.local_user.map(|l| l.person.id);
|
|
||||||
let my_local_user_id = options.local_user.map(|l| l.local_user.id);
|
|
||||||
|
|
||||||
// The left join below will return None in this case
|
// The left join below will return None in this case
|
||||||
let person_id_join = my_person_id.unwrap_or(PersonId(-1));
|
let person_id_join = options.local_user.person_id().unwrap_or(PersonId(-1));
|
||||||
let local_user_id_join = my_local_user_id.unwrap_or(LocalUserId(-1));
|
let local_user_id_join = options
|
||||||
|
.local_user
|
||||||
|
.local_user_id()
|
||||||
|
.unwrap_or(LocalUserId(-1));
|
||||||
|
|
||||||
let mut query = all_joins(post_aggregates::table.into_boxed(), my_person_id);
|
let mut query = all_joins(
|
||||||
|
post_aggregates::table.into_boxed(),
|
||||||
|
options.local_user.person_id(),
|
||||||
|
);
|
||||||
|
|
||||||
// hide posts from deleted communities
|
// hide posts from deleted communities
|
||||||
query = query.filter(community::deleted.eq(false));
|
query = query.filter(community::deleted.eq(false));
|
||||||
|
|
||||||
// only show deleted posts to creator
|
// only show deleted posts to creator
|
||||||
if let Some(person_id) = my_person_id {
|
if let Some(person_id) = options.local_user.person_id() {
|
||||||
query = query.filter(post::deleted.eq(false).or(post::creator_id.eq(person_id)));
|
query = query.filter(post::deleted.eq(false).or(post::creator_id.eq(person_id)));
|
||||||
} else {
|
} else {
|
||||||
query = query.filter(post::deleted.eq(false));
|
query = query.filter(post::deleted.eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_admin = options
|
|
||||||
.local_user
|
|
||||||
.map(|l| l.local_user.admin)
|
|
||||||
.unwrap_or(false);
|
|
||||||
// only show removed posts to admin when viewing user profile
|
// only show removed posts to admin when viewing user profile
|
||||||
if !(options.creator_id.is_some() && is_admin) {
|
if !(options.creator_id.is_some() && options.local_user.is_admin()) {
|
||||||
query = query
|
query = query
|
||||||
.filter(community::removed.eq(false))
|
.filter(community::removed.eq(false))
|
||||||
.filter(post::removed.eq(false));
|
.filter(post::removed.eq(false));
|
||||||
|
@ -348,7 +335,7 @@ fn queries<'a>() -> Queries<
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(listing_type) = options.listing_type {
|
if let Some(listing_type) = options.listing_type {
|
||||||
if let Some(person_id) = my_person_id {
|
if let Some(person_id) = options.local_user.person_id() {
|
||||||
let is_subscribed = exists(
|
let is_subscribed = exists(
|
||||||
community_follower::table.filter(
|
community_follower::table.filter(
|
||||||
post_aggregates::community_id
|
post_aggregates::community_id
|
||||||
|
@ -405,70 +392,56 @@ fn queries<'a>() -> Queries<
|
||||||
.filter(not(post::removed.or(post::deleted)));
|
.filter(not(post::removed.or(post::deleted)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a content warning, show nsfw content by default.
|
if !options.local_user.show_nsfw(site) {
|
||||||
let has_content_warning = site.content_warning.is_some();
|
|
||||||
if !options
|
|
||||||
.local_user
|
|
||||||
.map(|l| l.local_user.show_nsfw)
|
|
||||||
.unwrap_or(has_content_warning)
|
|
||||||
{
|
|
||||||
query = query
|
query = query
|
||||||
.filter(post::nsfw.eq(false))
|
.filter(post::nsfw.eq(false))
|
||||||
.filter(community::nsfw.eq(false));
|
.filter(community::nsfw.eq(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
if !options
|
if !options.local_user.show_bot_accounts() {
|
||||||
.local_user
|
|
||||||
.map(|l| l.local_user.show_bot_accounts)
|
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
|
||||||
query = query.filter(person::bot_account.eq(false));
|
query = query.filter(person::bot_account.eq(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
// If its saved only, then filter, and order by the saved time, not the comment creation time.
|
// If its saved only, then filter, and order by the saved time, not the comment creation time.
|
||||||
if let (true, Some(person_id)) = (options.saved_only, my_person_id) {
|
if options.saved_only.unwrap_or_default() {
|
||||||
query = query
|
query = query
|
||||||
.filter(is_saved(person_id).is_not_null())
|
.filter(post_saved::person_id.is_not_null())
|
||||||
.then_order_by(is_saved(person_id).desc());
|
.then_order_by(post_saved::published.desc());
|
||||||
}
|
}
|
||||||
// Only hide the read posts, if the saved_only is false. Otherwise ppl with the hide_read
|
// Only hide the read posts, if the saved_only is false. Otherwise ppl with the hide_read
|
||||||
// setting wont be able to see saved posts.
|
// setting wont be able to see saved posts.
|
||||||
else if !options
|
else if !options
|
||||||
.local_user
|
.show_read
|
||||||
.map(|l| l.local_user.show_read_posts)
|
.unwrap_or(options.local_user.show_read_posts())
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
{
|
||||||
// Do not hide read posts when it is a user profile view
|
// Do not hide read posts when it is a user profile view
|
||||||
// Or, only hide read posts on non-profile views
|
// Or, only hide read posts on non-profile views
|
||||||
if let (None, Some(person_id)) = (options.creator_id, my_person_id) {
|
if let (None, Some(person_id)) = (options.creator_id, options.local_user.person_id()) {
|
||||||
query = query.filter(not(is_read(person_id)));
|
query = query.filter(not(is_read(person_id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.show_hidden {
|
if !options.show_hidden.unwrap_or_default() {
|
||||||
// If a creator id isn't given (IE its on home or community pages), hide the hidden posts
|
// If a creator id isn't given (IE its on home or community pages), hide the hidden posts
|
||||||
if let (None, Some(person_id)) = (options.creator_id, my_person_id) {
|
if let (None, Some(person_id)) = (options.creator_id, options.local_user.person_id()) {
|
||||||
query = query.filter(not(is_hidden(person_id)));
|
query = query.filter(not(is_hidden(person_id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(my_id) = my_person_id {
|
if let Some(my_id) = options.local_user.person_id() {
|
||||||
let not_creator_filter = post_aggregates::creator_id.ne(my_id);
|
let not_creator_filter = post_aggregates::creator_id.ne(my_id);
|
||||||
if options.liked_only {
|
if options.liked_only.unwrap_or_default() {
|
||||||
query = query.filter(not_creator_filter).filter(score(my_id).eq(1));
|
query = query.filter(not_creator_filter).filter(score(my_id).eq(1));
|
||||||
} else if options.disliked_only {
|
} else if options.disliked_only.unwrap_or_default() {
|
||||||
query = query.filter(not_creator_filter).filter(score(my_id).eq(-1));
|
query = query.filter(not_creator_filter).filter(score(my_id).eq(-1));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hide posts in local only communities from unauthenticated users
|
query = visible_communities_only(options.local_user.person_id(), query);
|
||||||
if options.local_user.is_none() {
|
|
||||||
query = query.filter(community::visibility.eq(CommunityVisibility::Public));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dont filter blocks or missing languages for moderator view type
|
// Dont filter blocks or missing languages for moderator view type
|
||||||
if let (Some(person_id), false) = (
|
if let (Some(person_id), false) = (
|
||||||
my_person_id,
|
options.local_user.person_id(),
|
||||||
options.listing_type.unwrap_or_default() == ListingType::ModeratorView,
|
options.listing_type.unwrap_or_default() == ListingType::ModeratorView,
|
||||||
) {
|
) {
|
||||||
// Filter out the rows with missing languages
|
// Filter out the rows with missing languages
|
||||||
|
@ -506,7 +479,7 @@ fn queries<'a>() -> Queries<
|
||||||
let page_after = options.page_after.map(|c| c.0);
|
let page_after = options.page_after.map(|c| c.0);
|
||||||
let page_before_or_equal = options.page_before_or_equal.map(|c| c.0);
|
let page_before_or_equal = options.page_before_or_equal.map(|c| c.0);
|
||||||
|
|
||||||
if options.page_back {
|
if options.page_back.unwrap_or_default() {
|
||||||
query = query
|
query = query
|
||||||
.before(page_after)
|
.before(page_after)
|
||||||
.after_or_equal(page_before_or_equal)
|
.after_or_equal(page_before_or_equal)
|
||||||
|
@ -631,18 +604,19 @@ pub struct PostQuery<'a> {
|
||||||
// if true, the query should be handled as if community_id was not given except adding the
|
// if true, the query should be handled as if community_id was not given except adding the
|
||||||
// literal filter
|
// literal filter
|
||||||
pub community_id_just_for_prefetch: bool,
|
pub community_id_just_for_prefetch: bool,
|
||||||
pub local_user: Option<&'a LocalUserView>,
|
pub local_user: Option<&'a LocalUser>,
|
||||||
pub search_term: Option<String>,
|
pub search_term: Option<String>,
|
||||||
pub url_search: Option<String>,
|
pub url_search: Option<String>,
|
||||||
pub saved_only: bool,
|
pub saved_only: Option<bool>,
|
||||||
pub liked_only: bool,
|
pub liked_only: Option<bool>,
|
||||||
pub disliked_only: bool,
|
pub disliked_only: Option<bool>,
|
||||||
pub page: Option<i64>,
|
pub page: Option<i64>,
|
||||||
pub limit: Option<i64>,
|
pub limit: Option<i64>,
|
||||||
pub page_after: Option<PaginationCursorData>,
|
pub page_after: Option<PaginationCursorData>,
|
||||||
pub page_before_or_equal: Option<PaginationCursorData>,
|
pub page_before_or_equal: Option<PaginationCursorData>,
|
||||||
pub page_back: bool,
|
pub page_back: Option<bool>,
|
||||||
pub show_hidden: bool,
|
pub show_hidden: Option<bool>,
|
||||||
|
pub show_read: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PostQuery<'a> {
|
impl<'a> PostQuery<'a> {
|
||||||
|
@ -676,11 +650,7 @@ impl<'a> PostQuery<'a> {
|
||||||
"legacy pagination cannot be combined with v2 pagination".into(),
|
"legacy pagination cannot be combined with v2 pagination".into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let self_person_id = self
|
let self_person_id = self.local_user.expect("part of the above if").person_id;
|
||||||
.local_user
|
|
||||||
.expect("part of the above if")
|
|
||||||
.local_user
|
|
||||||
.person_id;
|
|
||||||
let largest_subscribed = {
|
let largest_subscribed = {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
community_follower
|
community_follower
|
||||||
|
@ -717,7 +687,7 @@ impl<'a> PostQuery<'a> {
|
||||||
if (v.len() as i64) < limit {
|
if (v.len() as i64) < limit {
|
||||||
Ok(Some(self.clone()))
|
Ok(Some(self.clone()))
|
||||||
} else {
|
} else {
|
||||||
let item = if self.page_back {
|
let item = if self.page_back.unwrap_or_default() {
|
||||||
// for backward pagination, get first element instead
|
// for backward pagination, get first element instead
|
||||||
v.into_iter().next()
|
v.into_iter().next()
|
||||||
} else {
|
} else {
|
||||||
|
@ -820,7 +790,7 @@ mod tests {
|
||||||
fn default_post_query(&self) -> PostQuery<'_> {
|
fn default_post_query(&self) -> PostQuery<'_> {
|
||||||
PostQuery {
|
PostQuery {
|
||||||
sort: Some(SortType::New),
|
sort: Some(SortType::New),
|
||||||
local_user: Some(&self.local_user_view),
|
local_user: Some(&self.local_user_view.local_user),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1156,7 +1126,7 @@ mod tests {
|
||||||
// Read the liked only
|
// Read the liked only
|
||||||
let read_liked_post_listing = PostQuery {
|
let read_liked_post_listing = PostQuery {
|
||||||
community_id: Some(data.inserted_community.id),
|
community_id: Some(data.inserted_community.id),
|
||||||
liked_only: true,
|
liked_only: Some(true),
|
||||||
..data.default_post_query()
|
..data.default_post_query()
|
||||||
}
|
}
|
||||||
.list(&data.site, pool)
|
.list(&data.site, pool)
|
||||||
|
@ -1167,7 +1137,7 @@ mod tests {
|
||||||
|
|
||||||
let read_disliked_post_listing = PostQuery {
|
let read_disliked_post_listing = PostQuery {
|
||||||
community_id: Some(data.inserted_community.id),
|
community_id: Some(data.inserted_community.id),
|
||||||
disliked_only: true,
|
disliked_only: Some(true),
|
||||||
..data.default_post_query()
|
..data.default_post_query()
|
||||||
}
|
}
|
||||||
.list(&data.site, pool)
|
.list(&data.site, pool)
|
||||||
|
@ -1337,8 +1307,8 @@ mod tests {
|
||||||
// Deleted post is only shown to creator
|
// Deleted post is only shown to creator
|
||||||
for (local_user, expect_contains_deleted) in [
|
for (local_user, expect_contains_deleted) in [
|
||||||
(None, false),
|
(None, false),
|
||||||
(Some(&data.blocked_local_user_view), false),
|
(Some(&data.blocked_local_user_view.local_user), false),
|
||||||
(Some(&data.local_user_view), true),
|
(Some(&data.local_user_view.local_user), true),
|
||||||
] {
|
] {
|
||||||
let contains_deleted = PostQuery {
|
let contains_deleted = PostQuery {
|
||||||
local_user,
|
local_user,
|
||||||
|
@ -1493,7 +1463,7 @@ mod tests {
|
||||||
loop {
|
loop {
|
||||||
let post_listings = PostQuery {
|
let post_listings = PostQuery {
|
||||||
page_after: page_before,
|
page_after: page_before,
|
||||||
page_back: true,
|
page_back: Some(true),
|
||||||
..options.clone()
|
..options.clone()
|
||||||
}
|
}
|
||||||
.list(&data.site, pool)
|
.list(&data.site, pool)
|
||||||
|
@ -1551,6 +1521,26 @@ mod tests {
|
||||||
let post_listings_hide_read = data.default_post_query().list(&data.site, pool).await?;
|
let post_listings_hide_read = data.default_post_query().list(&data.site, pool).await?;
|
||||||
assert_eq!(vec![POST], names(&post_listings_hide_read));
|
assert_eq!(vec![POST], names(&post_listings_hide_read));
|
||||||
|
|
||||||
|
// Test with the show_read override as true
|
||||||
|
let post_listings_show_read_true = PostQuery {
|
||||||
|
show_read: Some(true),
|
||||||
|
..data.default_post_query()
|
||||||
|
}
|
||||||
|
.list(&data.site, pool)
|
||||||
|
.await?;
|
||||||
|
assert_eq!(
|
||||||
|
vec![POST_BY_BOT, POST],
|
||||||
|
names(&post_listings_show_read_true)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with the show_read override as false
|
||||||
|
let post_listings_show_read_false = PostQuery {
|
||||||
|
show_read: Some(false),
|
||||||
|
..data.default_post_query()
|
||||||
|
}
|
||||||
|
.list(&data.site, pool)
|
||||||
|
.await?;
|
||||||
|
assert_eq!(vec![POST], names(&post_listings_show_read_false));
|
||||||
cleanup(data, pool).await
|
cleanup(data, pool).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1576,8 +1566,8 @@ mod tests {
|
||||||
// Make sure it does come back with the show_hidden option
|
// Make sure it does come back with the show_hidden option
|
||||||
let post_listings_show_hidden = PostQuery {
|
let post_listings_show_hidden = PostQuery {
|
||||||
sort: Some(SortType::New),
|
sort: Some(SortType::New),
|
||||||
local_user: Some(&data.local_user_view),
|
local_user: Some(&data.local_user_view.local_user),
|
||||||
show_hidden: true,
|
show_hidden: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(&data.site, pool)
|
.list(&data.site, pool)
|
||||||
|
@ -1668,6 +1658,7 @@ mod tests {
|
||||||
public_key: inserted_person.public_key.clone(),
|
public_key: inserted_person.public_key.clone(),
|
||||||
last_refreshed_at: inserted_person.last_refreshed_at,
|
last_refreshed_at: inserted_person.last_refreshed_at,
|
||||||
},
|
},
|
||||||
|
image_details: None,
|
||||||
creator_banned_from_community: false,
|
creator_banned_from_community: false,
|
||||||
banned_from_community: false,
|
banned_from_community: false,
|
||||||
creator_is_moderator: false,
|
creator_is_moderator: false,
|
||||||
|
@ -1751,7 +1742,7 @@ mod tests {
|
||||||
assert_eq!(0, unauthenticated_query.len());
|
assert_eq!(0, unauthenticated_query.len());
|
||||||
|
|
||||||
let authenticated_query = PostQuery {
|
let authenticated_query = PostQuery {
|
||||||
local_user: Some(&data.local_user_view),
|
local_user: Some(&data.local_user_view.local_user),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(&data.site, pool)
|
.list(&data.site, pool)
|
||||||
|
|
|
@ -140,18 +140,10 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person_1 = PersonInsertForm::builder()
|
let new_person_1 = PersonInsertForm::test_form(inserted_instance.id, "timmy_mrv");
|
||||||
.name("timmy_mrv".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
let inserted_timmy = Person::create(pool, &new_person_1).await.unwrap();
|
let inserted_timmy = Person::create(pool, &new_person_1).await.unwrap();
|
||||||
|
|
||||||
let new_person_2 = PersonInsertForm::builder()
|
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "jessica_mrv");
|
||||||
.name("jessica_mrv".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
let inserted_jessica = Person::create(pool, &new_person_2).await.unwrap();
|
let inserted_jessica = Person::create(pool, &new_person_2).await.unwrap();
|
||||||
|
|
||||||
// timmy sends private message to jessica
|
// timmy sends private message to jessica
|
||||||
|
@ -184,11 +176,7 @@ mod tests {
|
||||||
assert_eq!(pm_report.reason, reports[0].private_message_report.reason);
|
assert_eq!(pm_report.reason, reports[0].private_message_report.reason);
|
||||||
assert_eq!(pm.content, reports[0].private_message.content);
|
assert_eq!(pm.content, reports[0].private_message.content);
|
||||||
|
|
||||||
let new_person_3 = PersonInsertForm::builder()
|
let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "admin_mrv");
|
||||||
.name("admin_mrv".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
let inserted_admin = Person::create(pool, &new_person_3).await.unwrap();
|
let inserted_admin = Person::create(pool, &new_person_3).await.unwrap();
|
||||||
|
|
||||||
// admin resolves the report (after taking appropriate action)
|
// admin resolves the report (after taking appropriate action)
|
||||||
|
|
|
@ -209,27 +209,15 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let timmy_form = PersonInsertForm::builder()
|
let timmy_form = PersonInsertForm::test_form(instance.id, "timmy_rav");
|
||||||
.name("timmy_rav".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let timmy = Person::create(pool, &timmy_form).await.unwrap();
|
let timmy = Person::create(pool, &timmy_form).await.unwrap();
|
||||||
|
|
||||||
let sara_form = PersonInsertForm::builder()
|
let sara_form = PersonInsertForm::test_form(instance.id, "sara_rav");
|
||||||
.name("sara_rav".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let sara = Person::create(pool, &sara_form).await.unwrap();
|
let sara = Person::create(pool, &sara_form).await.unwrap();
|
||||||
|
|
||||||
let jess_form = PersonInsertForm::builder()
|
let jess_form = PersonInsertForm::test_form(instance.id, "jess_rav");
|
||||||
.name("jess_rav".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let jess = Person::create(pool, &jess_form).await.unwrap();
|
let jess = Person::create(pool, &jess_form).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -163,11 +163,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let timmy_person_form = PersonInsertForm::builder()
|
let timmy_person_form = PersonInsertForm::test_form(inserted_instance.id, "timmy_rav");
|
||||||
.name("timmy_rav".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_timmy_person = Person::create(pool, &timmy_person_form).await.unwrap();
|
let inserted_timmy_person = Person::create(pool, &timmy_person_form).await.unwrap();
|
||||||
|
|
||||||
|
@ -181,11 +177,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let sara_person_form = PersonInsertForm::builder()
|
let sara_person_form = PersonInsertForm::test_form(inserted_instance.id, "sara_rav");
|
||||||
.name("sara_rav".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_sara_person = Person::create(pool, &sara_person_form).await.unwrap();
|
let inserted_sara_person = Person::create(pool, &sara_person_form).await.unwrap();
|
||||||
|
|
||||||
|
@ -213,11 +205,7 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let jess_person_form = PersonInsertForm::builder()
|
let jess_person_form = PersonInsertForm::test_form(inserted_instance.id, "jess_rav");
|
||||||
.name("jess_rav".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_jess_person = Person::create(pool, &jess_person_form).await.unwrap();
|
let inserted_jess_person = Person::create(pool, &jess_person_form).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use lemmy_db_schema::{
|
||||||
community::Community,
|
community::Community,
|
||||||
custom_emoji::CustomEmoji,
|
custom_emoji::CustomEmoji,
|
||||||
custom_emoji_keyword::CustomEmojiKeyword,
|
custom_emoji_keyword::CustomEmojiKeyword,
|
||||||
images::LocalImage,
|
images::{ImageDetails, LocalImage},
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
local_site_rate_limit::LocalSiteRateLimit,
|
local_site_rate_limit::LocalSiteRateLimit,
|
||||||
local_user::LocalUser,
|
local_user::LocalUser,
|
||||||
|
@ -131,6 +131,7 @@ pub struct PostView {
|
||||||
pub post: Post,
|
pub post: Post,
|
||||||
pub creator: Person,
|
pub creator: Person,
|
||||||
pub community: Community,
|
pub community: Community,
|
||||||
|
pub image_details: Option<ImageDetails>,
|
||||||
pub creator_banned_from_community: bool,
|
pub creator_banned_from_community: bool,
|
||||||
pub banned_from_community: bool,
|
pub banned_from_community: bool,
|
||||||
pub creator_is_moderator: bool,
|
pub creator_is_moderator: bool,
|
||||||
|
|
|
@ -112,19 +112,11 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "timmy_vv");
|
||||||
.name("timmy_vv".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
|
let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
let new_person_2 = PersonInsertForm::builder()
|
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "sara_vv");
|
||||||
.name("sara_vv".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_sara = Person::create(pool, &new_person_2).await.unwrap();
|
let inserted_sara = Person::create(pool, &new_person_2).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ serial_test = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
pretty_assertions = { workspace = true }
|
pretty_assertions = { workspace = true }
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
|
lemmy_db_views.workspace = true
|
||||||
lemmy_utils.workspace = true
|
lemmy_utils.workspace = true
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
[package.metadata.cargo-machete]
|
||||||
|
|
|
@ -31,6 +31,7 @@ use lemmy_db_schema::{
|
||||||
person_block,
|
person_block,
|
||||||
post,
|
post,
|
||||||
},
|
},
|
||||||
|
source::local_user::LocalUser,
|
||||||
utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
};
|
};
|
||||||
|
@ -193,6 +194,8 @@ fn queries<'a>() -> Queries<
|
||||||
};
|
};
|
||||||
|
|
||||||
let list = move |mut conn: DbConn<'a>, options: CommentReplyQuery| async move {
|
let list = move |mut conn: DbConn<'a>, options: CommentReplyQuery| async move {
|
||||||
|
// These filters need to be kept in sync with the filters in
|
||||||
|
// CommentReplyView::get_unread_replies()
|
||||||
let mut query = all_joins(comment_reply::table.into_boxed(), options.my_person_id);
|
let mut query = all_joins(comment_reply::table.into_boxed(), options.my_person_id);
|
||||||
|
|
||||||
if let Some(recipient_id) = options.recipient_id {
|
if let Some(recipient_id) = options.recipient_id {
|
||||||
|
@ -204,7 +207,7 @@ fn queries<'a>() -> Queries<
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.show_bot_accounts {
|
if !options.show_bot_accounts {
|
||||||
query = query.filter(person::bot_account.eq(false));
|
query = query.filter(not(person::bot_account));
|
||||||
};
|
};
|
||||||
|
|
||||||
query = match options.sort.unwrap_or(CommentSortType::New) {
|
query = match options.sort.unwrap_or(CommentSortType::New) {
|
||||||
|
@ -246,24 +249,33 @@ impl CommentReplyView {
|
||||||
/// Gets the number of unread replies
|
/// Gets the number of unread replies
|
||||||
pub async fn get_unread_replies(
|
pub async fn get_unread_replies(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
my_person_id: PersonId,
|
local_user: &LocalUser,
|
||||||
) -> Result<i64, Error> {
|
) -> Result<i64, Error> {
|
||||||
use diesel::dsl::count;
|
use diesel::dsl::count;
|
||||||
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
comment_reply::table
|
let mut query = comment_reply::table
|
||||||
.inner_join(comment::table)
|
.inner_join(comment::table)
|
||||||
.left_join(
|
.left_join(
|
||||||
person_block::table.on(
|
person_block::table.on(
|
||||||
comment::creator_id
|
comment::creator_id
|
||||||
.eq(person_block::target_id)
|
.eq(person_block::target_id)
|
||||||
.and(person_block::person_id.eq(my_person_id)),
|
.and(person_block::person_id.eq(local_user.person_id)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
// Dont count replies from blocked users
|
.inner_join(person::table.on(comment::creator_id.eq(person::id)))
|
||||||
|
.into_boxed();
|
||||||
|
|
||||||
|
// These filters need to be kept in sync with the filters in queries().list()
|
||||||
|
if !local_user.show_bot_accounts {
|
||||||
|
query = query.filter(not(person::bot_account));
|
||||||
|
}
|
||||||
|
|
||||||
|
query
|
||||||
|
// Don't count replies from blocked users
|
||||||
.filter(person_block::person_id.is_null())
|
.filter(person_block::person_id.is_null())
|
||||||
.filter(comment_reply::recipient_id.eq(my_person_id))
|
.filter(comment_reply::recipient_id.eq(local_user.person_id))
|
||||||
.filter(comment_reply::read.eq(false))
|
.filter(comment_reply::read.eq(false))
|
||||||
.filter(comment::deleted.eq(false))
|
.filter(comment::deleted.eq(false))
|
||||||
.filter(comment::removed.eq(false))
|
.filter(comment::removed.eq(false))
|
||||||
|
@ -301,13 +313,15 @@ mod tests {
|
||||||
comment_reply::{CommentReply, CommentReplyInsertForm, CommentReplyUpdateForm},
|
comment_reply::{CommentReply, CommentReplyInsertForm, CommentReplyUpdateForm},
|
||||||
community::{Community, CommunityInsertForm},
|
community::{Community, CommunityInsertForm},
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
person::{Person, PersonInsertForm},
|
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||||
|
person::{Person, PersonInsertForm, PersonUpdateForm},
|
||||||
person_block::{PersonBlock, PersonBlockForm},
|
person_block::{PersonBlock, PersonBlockForm},
|
||||||
post::{Post, PostInsertForm},
|
post::{Post, PostInsertForm},
|
||||||
},
|
},
|
||||||
traits::{Blockable, Crud},
|
traits::{Blockable, Crud},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
@ -320,22 +334,20 @@ mod tests {
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
let terry_form = PersonInsertForm::builder()
|
let terry_form = PersonInsertForm::test_form(inserted_instance.id, "terrylake");
|
||||||
.name("terrylake".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
let inserted_terry = Person::create(pool, &terry_form).await?;
|
let inserted_terry = Person::create(pool, &terry_form).await?;
|
||||||
|
|
||||||
let recipient_form = PersonInsertForm::builder()
|
let recipient_form = PersonInsertForm {
|
||||||
.name("terrylakes recipient".into())
|
local: Some(true),
|
||||||
.public_key("pubkey".to_string())
|
..PersonInsertForm::test_form(inserted_instance.id, "terrylakes recipient")
|
||||||
.instance_id(inserted_instance.id)
|
};
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_recipient = Person::create(pool, &recipient_form).await?;
|
let inserted_recipient = Person::create(pool, &recipient_form).await?;
|
||||||
let recipient_id = inserted_recipient.id;
|
let recipient_id = inserted_recipient.id;
|
||||||
|
|
||||||
|
let recipient_local_user =
|
||||||
|
LocalUser::create(pool, &LocalUserInsertForm::test_form(recipient_id), vec![]).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::builder()
|
let new_community = CommunityInsertForm::builder()
|
||||||
.name("test community lake".to_string())
|
.name("test community lake".to_string())
|
||||||
.title("nada".to_owned())
|
.title("nada".to_owned())
|
||||||
|
@ -386,7 +398,7 @@ mod tests {
|
||||||
CommentReply::update(pool, inserted_reply.id, &comment_reply_update_form).await?;
|
CommentReply::update(pool, inserted_reply.id, &comment_reply_update_form).await?;
|
||||||
|
|
||||||
// Test to make sure counts and blocks work correctly
|
// Test to make sure counts and blocks work correctly
|
||||||
let unread_replies = CommentReplyView::get_unread_replies(pool, recipient_id).await?;
|
let unread_replies = CommentReplyView::get_unread_replies(pool, &recipient_local_user).await?;
|
||||||
|
|
||||||
let query = CommentReplyQuery {
|
let query = CommentReplyQuery {
|
||||||
recipient_id: Some(recipient_id),
|
recipient_id: Some(recipient_id),
|
||||||
|
@ -409,11 +421,44 @@ mod tests {
|
||||||
PersonBlock::block(pool, &block_form).await?;
|
PersonBlock::block(pool, &block_form).await?;
|
||||||
|
|
||||||
let unread_replies_after_block =
|
let unread_replies_after_block =
|
||||||
CommentReplyView::get_unread_replies(pool, recipient_id).await?;
|
CommentReplyView::get_unread_replies(pool, &recipient_local_user).await?;
|
||||||
let replies_after_block = query.list(pool).await?;
|
let replies_after_block = query.clone().list(pool).await?;
|
||||||
assert_eq!(0, unread_replies_after_block);
|
assert_eq!(0, unread_replies_after_block);
|
||||||
assert_eq!(0, replies_after_block.len());
|
assert_eq!(0, replies_after_block.len());
|
||||||
|
|
||||||
|
// Unblock user so we can reuse the same person
|
||||||
|
PersonBlock::unblock(pool, &block_form).await?;
|
||||||
|
|
||||||
|
// Turn Terry into a bot account
|
||||||
|
let person_update_form = PersonUpdateForm {
|
||||||
|
bot_account: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
Person::update(pool, inserted_terry.id, &person_update_form).await?;
|
||||||
|
|
||||||
|
let recipient_local_user_update_form = LocalUserUpdateForm {
|
||||||
|
show_bot_accounts: Some(false),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
LocalUser::update(
|
||||||
|
pool,
|
||||||
|
recipient_local_user.id,
|
||||||
|
&recipient_local_user_update_form,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let recipient_local_user_view = LocalUserView::read(pool, recipient_local_user.id)
|
||||||
|
.await?
|
||||||
|
.ok_or(LemmyErrorType::CouldntFindLocalUser)?;
|
||||||
|
|
||||||
|
let unread_replies_after_hide_bots =
|
||||||
|
CommentReplyView::get_unread_replies(pool, &recipient_local_user_view.local_user).await?;
|
||||||
|
|
||||||
|
let mut query_without_bots = query.clone();
|
||||||
|
query_without_bots.show_bot_accounts = false;
|
||||||
|
let replies_after_hide_bots = query_without_bots.list(pool).await?;
|
||||||
|
assert_eq!(0, unread_replies_after_hide_bots);
|
||||||
|
assert_eq!(0, replies_after_hide_bots.len());
|
||||||
|
|
||||||
Comment::delete(pool, inserted_comment.id).await?;
|
Comment::delete(pool, inserted_comment.id).await?;
|
||||||
Post::delete(pool, inserted_post.id).await?;
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
Community::delete(pool, inserted_community.id).await?;
|
Community::delete(pool, inserted_community.id).await?;
|
||||||
|
|
|
@ -90,7 +90,7 @@ impl CommunityModeratorView {
|
||||||
.distinct_on(community_moderator::community_id)
|
.distinct_on(community_moderator::community_id)
|
||||||
.order_by((
|
.order_by((
|
||||||
community_moderator::community_id,
|
community_moderator::community_id,
|
||||||
community_moderator::person_id,
|
community_moderator::published,
|
||||||
))
|
))
|
||||||
.load::<CommunityModeratorView>(conn)
|
.load::<CommunityModeratorView>(conn)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -11,6 +11,7 @@ use diesel::{
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
impls::local_user::LocalUserOptionHelper,
|
||||||
newtypes::{CommunityId, PersonId},
|
newtypes::{CommunityId, PersonId},
|
||||||
schema::{
|
schema::{
|
||||||
community,
|
community,
|
||||||
|
@ -19,11 +20,18 @@ use lemmy_db_schema::{
|
||||||
community_follower,
|
community_follower,
|
||||||
community_person_ban,
|
community_person_ban,
|
||||||
instance_block,
|
instance_block,
|
||||||
local_user,
|
|
||||||
},
|
},
|
||||||
source::{community::CommunityFollower, local_user::LocalUser, site::Site},
|
source::{community::CommunityFollower, local_user::LocalUser, site::Site},
|
||||||
utils::{fuzzy_search, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
utils::{
|
||||||
CommunityVisibility,
|
fuzzy_search,
|
||||||
|
limit_and_offset,
|
||||||
|
visible_communities_only,
|
||||||
|
DbConn,
|
||||||
|
DbPool,
|
||||||
|
ListFn,
|
||||||
|
Queries,
|
||||||
|
ReadFn,
|
||||||
|
},
|
||||||
ListingType,
|
ListingType,
|
||||||
SortType,
|
SortType,
|
||||||
};
|
};
|
||||||
|
@ -97,10 +105,7 @@ fn queries<'a>() -> Queries<
|
||||||
query = query.filter(not_removed_or_deleted);
|
query = query.filter(not_removed_or_deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide local only communities from unauthenticated users
|
query = visible_communities_only(my_person_id, query);
|
||||||
if my_person_id.is_none() {
|
|
||||||
query = query.filter(community::visibility.eq(CommunityVisibility::Public));
|
|
||||||
}
|
|
||||||
|
|
||||||
query.first(&mut conn).await
|
query.first(&mut conn).await
|
||||||
};
|
};
|
||||||
|
@ -108,14 +113,14 @@ fn queries<'a>() -> Queries<
|
||||||
let list = move |mut conn: DbConn<'a>, (options, site): (CommunityQuery<'a>, &'a Site)| async move {
|
let list = move |mut conn: DbConn<'a>, (options, site): (CommunityQuery<'a>, &'a Site)| async move {
|
||||||
use SortType::*;
|
use SortType::*;
|
||||||
|
|
||||||
let my_person_id = options.local_user.map(|l| l.person_id);
|
|
||||||
|
|
||||||
// The left join below will return None in this case
|
// The left join below will return None in this case
|
||||||
let person_id_join = my_person_id.unwrap_or(PersonId(-1));
|
let person_id_join = options.local_user.person_id().unwrap_or(PersonId(-1));
|
||||||
|
|
||||||
let mut query = all_joins(community::table.into_boxed(), my_person_id)
|
let mut query = all_joins(
|
||||||
.left_join(local_user::table.on(local_user::person_id.eq(person_id_join)))
|
community::table.into_boxed(),
|
||||||
.select(selection);
|
options.local_user.person_id(),
|
||||||
|
)
|
||||||
|
.select(selection);
|
||||||
|
|
||||||
if let Some(search_term) = options.search_term {
|
if let Some(search_term) = options.search_term {
|
||||||
let searcher = fuzzy_search(&search_term);
|
let searcher = fuzzy_search(&search_term);
|
||||||
|
@ -162,21 +167,14 @@ fn queries<'a>() -> Queries<
|
||||||
|
|
||||||
// Don't show blocked communities and communities on blocked instances. nsfw communities are
|
// Don't show blocked communities and communities on blocked instances. nsfw communities are
|
||||||
// also hidden (based on profile setting)
|
// also hidden (based on profile setting)
|
||||||
if options.local_user.is_some() {
|
query = query.filter(instance_block::person_id.is_null());
|
||||||
query = query.filter(instance_block::person_id.is_null());
|
query = query.filter(community_block::person_id.is_null());
|
||||||
query = query.filter(community_block::person_id.is_null());
|
if !(options.local_user.show_nsfw(site) || options.show_nsfw) {
|
||||||
query = query.filter(community::nsfw.eq(false).or(local_user::show_nsfw.eq(true)));
|
query = query.filter(community::nsfw.eq(false));
|
||||||
} else {
|
|
||||||
// No person in request, only show nsfw communities if show_nsfw is passed into request or if
|
|
||||||
// site has content warning.
|
|
||||||
let has_content_warning = site.content_warning.is_some();
|
|
||||||
if !options.show_nsfw && !has_content_warning {
|
|
||||||
query = query.filter(community::nsfw.eq(false));
|
|
||||||
}
|
|
||||||
// Hide local only communities from unauthenticated users
|
|
||||||
query = query.filter(community::visibility.eq(CommunityVisibility::Public));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query = visible_communities_only(options.local_user.person_id(), query);
|
||||||
|
|
||||||
let (limit, offset) = limit_and_offset(options.page, options.limit)?;
|
let (limit, offset) = limit_and_offset(options.page, options.limit)?;
|
||||||
query
|
query
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
|
@ -286,11 +284,7 @@ mod tests {
|
||||||
|
|
||||||
let person_name = "tegan".to_string();
|
let person_name = "tegan".to_string();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, &person_name);
|
||||||
.name(person_name.clone())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ use lemmy_db_schema::{
|
||||||
person_mention,
|
person_mention,
|
||||||
post,
|
post,
|
||||||
},
|
},
|
||||||
|
source::local_user::LocalUser,
|
||||||
utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
};
|
};
|
||||||
|
@ -192,6 +193,8 @@ fn queries<'a>() -> Queries<
|
||||||
};
|
};
|
||||||
|
|
||||||
let list = move |mut conn: DbConn<'a>, options: PersonMentionQuery| async move {
|
let list = move |mut conn: DbConn<'a>, options: PersonMentionQuery| async move {
|
||||||
|
// These filters need to be kept in sync with the filters in
|
||||||
|
// PersonMentionView::get_unread_mentions()
|
||||||
let mut query = all_joins(person_mention::table.into_boxed(), options.my_person_id);
|
let mut query = all_joins(person_mention::table.into_boxed(), options.my_person_id);
|
||||||
|
|
||||||
if let Some(recipient_id) = options.recipient_id {
|
if let Some(recipient_id) = options.recipient_id {
|
||||||
|
@ -203,7 +206,7 @@ fn queries<'a>() -> Queries<
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.show_bot_accounts {
|
if !options.show_bot_accounts {
|
||||||
query = query.filter(person::bot_account.eq(false));
|
query = query.filter(not(person::bot_account));
|
||||||
};
|
};
|
||||||
|
|
||||||
query = match options.sort.unwrap_or(CommentSortType::Hot) {
|
query = match options.sort.unwrap_or(CommentSortType::Hot) {
|
||||||
|
@ -247,23 +250,32 @@ impl PersonMentionView {
|
||||||
/// Gets the number of unread mentions
|
/// Gets the number of unread mentions
|
||||||
pub async fn get_unread_mentions(
|
pub async fn get_unread_mentions(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
my_person_id: PersonId,
|
local_user: &LocalUser,
|
||||||
) -> Result<i64, Error> {
|
) -> Result<i64, Error> {
|
||||||
use diesel::dsl::count;
|
use diesel::dsl::count;
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
person_mention::table
|
let mut query = person_mention::table
|
||||||
.inner_join(comment::table)
|
.inner_join(comment::table)
|
||||||
.left_join(
|
.left_join(
|
||||||
person_block::table.on(
|
person_block::table.on(
|
||||||
comment::creator_id
|
comment::creator_id
|
||||||
.eq(person_block::target_id)
|
.eq(person_block::target_id)
|
||||||
.and(person_block::person_id.eq(my_person_id)),
|
.and(person_block::person_id.eq(local_user.person_id)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
// Dont count replies from blocked users
|
.inner_join(person::table.on(comment::creator_id.eq(person::id)))
|
||||||
|
.into_boxed();
|
||||||
|
|
||||||
|
// These filters need to be kept in sync with the filters in queries().list()
|
||||||
|
if !local_user.show_bot_accounts {
|
||||||
|
query = query.filter(not(person::bot_account));
|
||||||
|
}
|
||||||
|
|
||||||
|
query
|
||||||
|
// Don't count replies from blocked users
|
||||||
.filter(person_block::person_id.is_null())
|
.filter(person_block::person_id.is_null())
|
||||||
.filter(person_mention::recipient_id.eq(my_person_id))
|
.filter(person_mention::recipient_id.eq(local_user.person_id))
|
||||||
.filter(person_mention::read.eq(false))
|
.filter(person_mention::read.eq(false))
|
||||||
.filter(comment::deleted.eq(false))
|
.filter(comment::deleted.eq(false))
|
||||||
.filter(comment::removed.eq(false))
|
.filter(comment::removed.eq(false))
|
||||||
|
@ -300,7 +312,8 @@ mod tests {
|
||||||
comment::{Comment, CommentInsertForm},
|
comment::{Comment, CommentInsertForm},
|
||||||
community::{Community, CommunityInsertForm},
|
community::{Community, CommunityInsertForm},
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
person::{Person, PersonInsertForm},
|
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||||
|
person::{Person, PersonInsertForm, PersonUpdateForm},
|
||||||
person_block::{PersonBlock, PersonBlockForm},
|
person_block::{PersonBlock, PersonBlockForm},
|
||||||
person_mention::{PersonMention, PersonMentionInsertForm, PersonMentionUpdateForm},
|
person_mention::{PersonMention, PersonMentionInsertForm, PersonMentionUpdateForm},
|
||||||
post::{Post, PostInsertForm},
|
post::{Post, PostInsertForm},
|
||||||
|
@ -308,6 +321,7 @@ mod tests {
|
||||||
traits::{Blockable, Crud},
|
traits::{Blockable, Crud},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
@ -320,23 +334,18 @@ mod tests {
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
let new_person = PersonInsertForm::builder()
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "terrylake");
|
||||||
.name("terrylake".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await?;
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let recipient_form = PersonInsertForm::builder()
|
let recipient_form = PersonInsertForm::test_form(inserted_instance.id, "terrylakes recipient");
|
||||||
.name("terrylakes recipient".into())
|
|
||||||
.public_key("pubkey".to_string())
|
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_recipient = Person::create(pool, &recipient_form).await?;
|
let inserted_recipient = Person::create(pool, &recipient_form).await?;
|
||||||
let recipient_id = inserted_recipient.id;
|
let recipient_id = inserted_recipient.id;
|
||||||
|
|
||||||
|
let recipient_local_user =
|
||||||
|
LocalUser::create(pool, &LocalUserInsertForm::test_form(recipient_id), vec![]).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::builder()
|
let new_community = CommunityInsertForm::builder()
|
||||||
.name("test community lake".to_string())
|
.name("test community lake".to_string())
|
||||||
.title("nada".to_owned())
|
.title("nada".to_owned())
|
||||||
|
@ -387,7 +396,8 @@ mod tests {
|
||||||
PersonMention::update(pool, inserted_mention.id, &person_mention_update_form).await?;
|
PersonMention::update(pool, inserted_mention.id, &person_mention_update_form).await?;
|
||||||
|
|
||||||
// Test to make sure counts and blocks work correctly
|
// Test to make sure counts and blocks work correctly
|
||||||
let unread_mentions = PersonMentionView::get_unread_mentions(pool, recipient_id).await?;
|
let unread_mentions =
|
||||||
|
PersonMentionView::get_unread_mentions(pool, &recipient_local_user).await?;
|
||||||
|
|
||||||
let query = PersonMentionQuery {
|
let query = PersonMentionQuery {
|
||||||
recipient_id: Some(recipient_id),
|
recipient_id: Some(recipient_id),
|
||||||
|
@ -410,11 +420,44 @@ mod tests {
|
||||||
PersonBlock::block(pool, &block_form).await?;
|
PersonBlock::block(pool, &block_form).await?;
|
||||||
|
|
||||||
let unread_mentions_after_block =
|
let unread_mentions_after_block =
|
||||||
PersonMentionView::get_unread_mentions(pool, recipient_id).await?;
|
PersonMentionView::get_unread_mentions(pool, &recipient_local_user).await?;
|
||||||
let mentions_after_block = query.list(pool).await?;
|
let mentions_after_block = query.clone().list(pool).await?;
|
||||||
assert_eq!(0, unread_mentions_after_block);
|
assert_eq!(0, unread_mentions_after_block);
|
||||||
assert_eq!(0, mentions_after_block.len());
|
assert_eq!(0, mentions_after_block.len());
|
||||||
|
|
||||||
|
// Unblock user so we can reuse the same person
|
||||||
|
PersonBlock::unblock(pool, &block_form).await?;
|
||||||
|
|
||||||
|
// Turn Terry into a bot account
|
||||||
|
let person_update_form = PersonUpdateForm {
|
||||||
|
bot_account: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
Person::update(pool, inserted_person.id, &person_update_form).await?;
|
||||||
|
|
||||||
|
let recipient_local_user_update_form = LocalUserUpdateForm {
|
||||||
|
show_bot_accounts: Some(false),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
LocalUser::update(
|
||||||
|
pool,
|
||||||
|
recipient_local_user.id,
|
||||||
|
&recipient_local_user_update_form,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let recipient_local_user_view = LocalUserView::read(pool, recipient_local_user.id)
|
||||||
|
.await?
|
||||||
|
.ok_or(LemmyErrorType::CouldntFindLocalUser)?;
|
||||||
|
|
||||||
|
let unread_mentions_after_hide_bots =
|
||||||
|
PersonMentionView::get_unread_mentions(pool, &recipient_local_user_view.local_user).await?;
|
||||||
|
|
||||||
|
let mut query_without_bots = query.clone();
|
||||||
|
query_without_bots.show_bot_accounts = false;
|
||||||
|
let replies_after_hide_bots = query_without_bots.list(pool).await?;
|
||||||
|
assert_eq!(0, unread_mentions_after_hide_bots);
|
||||||
|
assert_eq!(0, replies_after_hide_bots.len());
|
||||||
|
|
||||||
Comment::delete(pool, inserted_comment.id).await?;
|
Comment::delete(pool, inserted_comment.id).await?;
|
||||||
Post::delete(pool, inserted_post.id).await?;
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
Community::delete(pool, inserted_community.id).await?;
|
Community::delete(pool, inserted_community.id).await?;
|
||||||
|
|
|
@ -191,12 +191,10 @@ mod tests {
|
||||||
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
let alice_form = PersonInsertForm::builder()
|
let alice_form = PersonInsertForm {
|
||||||
.name("alice".to_string())
|
local: Some(true),
|
||||||
.public_key("pubkey".to_string())
|
..PersonInsertForm::test_form(inserted_instance.id, "alice")
|
||||||
.instance_id(inserted_instance.id)
|
};
|
||||||
.local(Some(true))
|
|
||||||
.build();
|
|
||||||
let alice = Person::create(pool, &alice_form).await?;
|
let alice = Person::create(pool, &alice_form).await?;
|
||||||
let alice_local_user_form = LocalUserInsertForm::builder()
|
let alice_local_user_form = LocalUserInsertForm::builder()
|
||||||
.person_id(alice.id)
|
.person_id(alice.id)
|
||||||
|
@ -204,13 +202,11 @@ mod tests {
|
||||||
.build();
|
.build();
|
||||||
let alice_local_user = LocalUser::create(pool, &alice_local_user_form, vec![]).await?;
|
let alice_local_user = LocalUser::create(pool, &alice_local_user_form, vec![]).await?;
|
||||||
|
|
||||||
let bob_form = PersonInsertForm::builder()
|
let bob_form = PersonInsertForm {
|
||||||
.name("bob".to_string())
|
bot_account: Some(true),
|
||||||
.bot_account(Some(true))
|
local: Some(false),
|
||||||
.public_key("pubkey".to_string())
|
..PersonInsertForm::test_form(inserted_instance.id, "bob")
|
||||||
.instance_id(inserted_instance.id)
|
};
|
||||||
.local(Some(false))
|
|
||||||
.build();
|
|
||||||
let bob = Person::create(pool, &bob_form).await?;
|
let bob = Person::create(pool, &bob_form).await?;
|
||||||
let bob_local_user_form = LocalUserInsertForm::builder()
|
let bob_local_user_form = LocalUserInsertForm::builder()
|
||||||
.person_id(bob.id)
|
.person_id(bob.id)
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
use crate::{util::CancellableTask, worker::InstanceWorker};
|
use crate::{util::CancellableTask, worker::InstanceWorker};
|
||||||
use activitypub_federation::config::FederationConfig;
|
use activitypub_federation::config::FederationConfig;
|
||||||
use lemmy_api_common::context::LemmyContext;
|
use lemmy_api_common::context::LemmyContext;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{newtypes::InstanceId, source::instance::Instance};
|
||||||
newtypes::InstanceId,
|
|
||||||
source::{federation_queue_state::FederationQueueState, instance::Instance},
|
|
||||||
};
|
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
use stats::receive_print_stats;
|
use stats::receive_print_stats;
|
||||||
use std::{collections::HashMap, time::Duration};
|
use std::{collections::HashMap, time::Duration};
|
||||||
|
@ -15,6 +12,7 @@ use tokio::{
|
||||||
};
|
};
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
use util::FederationQueueStateWithDomain;
|
||||||
|
|
||||||
mod stats;
|
mod stats;
|
||||||
mod util;
|
mod util;
|
||||||
|
@ -38,12 +36,12 @@ pub struct SendManager {
|
||||||
opts: Opts,
|
opts: Opts,
|
||||||
workers: HashMap<InstanceId, CancellableTask>,
|
workers: HashMap<InstanceId, CancellableTask>,
|
||||||
context: FederationConfig<LemmyContext>,
|
context: FederationConfig<LemmyContext>,
|
||||||
stats_sender: UnboundedSender<(InstanceId, FederationQueueState)>,
|
stats_sender: UnboundedSender<FederationQueueStateWithDomain>,
|
||||||
exit_print: JoinHandle<()>,
|
exit_print: JoinHandle<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SendManager {
|
impl SendManager {
|
||||||
pub fn new(opts: Opts, context: FederationConfig<LemmyContext>) -> Self {
|
fn new(opts: Opts, context: FederationConfig<LemmyContext>) -> Self {
|
||||||
assert!(opts.process_count > 0);
|
assert!(opts.process_count > 0);
|
||||||
assert!(opts.process_index > 0);
|
assert!(opts.process_index > 0);
|
||||||
assert!(opts.process_index <= opts.process_count);
|
assert!(opts.process_index <= opts.process_count);
|
||||||
|
@ -61,11 +59,27 @@ impl SendManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(mut self) -> CancellableTask {
|
pub fn run(opts: Opts, context: FederationConfig<LemmyContext>) -> CancellableTask {
|
||||||
CancellableTask::spawn(WORKER_EXIT_TIMEOUT, move |cancel| async move {
|
CancellableTask::spawn(WORKER_EXIT_TIMEOUT, move |cancel| {
|
||||||
self.do_loop(cancel).await?;
|
let opts = opts.clone();
|
||||||
self.cancel().await?;
|
let context = context.clone();
|
||||||
Ok(())
|
let mut manager = Self::new(opts, context);
|
||||||
|
async move {
|
||||||
|
let result = manager.do_loop(cancel).await;
|
||||||
|
// the loop function will only return if there is (a) an internal error (e.g. db connection
|
||||||
|
// failure) or (b) it was cancelled from outside.
|
||||||
|
if let Err(e) = result {
|
||||||
|
// don't let this error bubble up, just log it, so the below cancel function will run
|
||||||
|
// regardless
|
||||||
|
tracing::error!("SendManager failed: {e}");
|
||||||
|
}
|
||||||
|
// cancel all the dependent workers as well to ensure they don't get orphaned and keep
|
||||||
|
// running.
|
||||||
|
manager.cancel().await?;
|
||||||
|
LemmyResult::Ok(())
|
||||||
|
// if the task was not intentionally cancelled, then this whole lambda will be run again by
|
||||||
|
// CancellableTask after this
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,14 +118,24 @@ impl SendManager {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// create new worker
|
// create new worker
|
||||||
let instance = instance.clone();
|
let context = self.context.clone();
|
||||||
let req_data = self.context.to_request_data();
|
|
||||||
let stats_sender = self.stats_sender.clone();
|
let stats_sender = self.stats_sender.clone();
|
||||||
self.workers.insert(
|
self.workers.insert(
|
||||||
instance.id,
|
instance.id,
|
||||||
CancellableTask::spawn(WORKER_EXIT_TIMEOUT, move |stop| async move {
|
CancellableTask::spawn(WORKER_EXIT_TIMEOUT, move |stop| {
|
||||||
InstanceWorker::init_and_loop(instance, req_data, stop, stats_sender).await?;
|
// if the instance worker ends unexpectedly due to internal/db errors, this lambda is rerun by cancellabletask.
|
||||||
Ok(())
|
let instance = instance.clone();
|
||||||
|
let req_data = context.to_request_data();
|
||||||
|
let stats_sender = stats_sender.clone();
|
||||||
|
async move {
|
||||||
|
InstanceWorker::init_and_loop(
|
||||||
|
instance,
|
||||||
|
req_data,
|
||||||
|
stop,
|
||||||
|
stats_sender,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else if !should_federate {
|
} else if !should_federate {
|
||||||
|
@ -171,7 +195,7 @@ mod test {
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
use tokio::{spawn, time::sleep};
|
use tokio::spawn;
|
||||||
|
|
||||||
struct TestData {
|
struct TestData {
|
||||||
send_manager: SendManager,
|
send_manager: SendManager,
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
use crate::util::get_latest_activity_id;
|
use crate::util::{get_latest_activity_id, FederationQueueStateWithDomain};
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use diesel::result::Error::NotFound;
|
|
||||||
use lemmy_api_common::federate_retry_sleep_duration;
|
use lemmy_api_common::federate_retry_sleep_duration;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::InstanceId,
|
newtypes::InstanceId,
|
||||||
source::{federation_queue_state::FederationQueueState, instance::Instance},
|
|
||||||
utils::{ActualDbPool, DbPool},
|
utils::{ActualDbPool, DbPool},
|
||||||
};
|
};
|
||||||
use lemmy_utils::{error::LemmyResult, CACHE_DURATION_FEDERATION};
|
use lemmy_utils::error::LemmyResult;
|
||||||
use moka::future::Cache;
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use std::{collections::HashMap, time::Duration};
|
use std::{collections::HashMap, time::Duration};
|
||||||
use tokio::{sync::mpsc::UnboundedReceiver, time::interval};
|
use tokio::{sync::mpsc::UnboundedReceiver, time::interval};
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
|
@ -18,7 +14,7 @@ use tracing::{debug, info, warn};
|
||||||
/// dropped)
|
/// dropped)
|
||||||
pub(crate) async fn receive_print_stats(
|
pub(crate) async fn receive_print_stats(
|
||||||
pool: ActualDbPool,
|
pool: ActualDbPool,
|
||||||
mut receiver: UnboundedReceiver<(InstanceId, FederationQueueState)>,
|
mut receiver: UnboundedReceiver<FederationQueueStateWithDomain>,
|
||||||
) {
|
) {
|
||||||
let pool = &mut DbPool::Pool(&pool);
|
let pool = &mut DbPool::Pool(&pool);
|
||||||
let mut printerval = interval(Duration::from_secs(60));
|
let mut printerval = interval(Duration::from_secs(60));
|
||||||
|
@ -28,7 +24,7 @@ pub(crate) async fn receive_print_stats(
|
||||||
ele = receiver.recv() => {
|
ele = receiver.recv() => {
|
||||||
match ele {
|
match ele {
|
||||||
// update stats for instance
|
// update stats for instance
|
||||||
Some((instance_id, ele)) => {stats.insert(instance_id, ele);},
|
Some(ele) => {stats.insert(ele.state.instance_id, ele);},
|
||||||
// receiver closed, print stats and exit
|
// receiver closed, print stats and exit
|
||||||
None => {
|
None => {
|
||||||
print_stats(pool, &stats).await;
|
print_stats(pool, &stats).await;
|
||||||
|
@ -43,7 +39,10 @@ pub(crate) async fn receive_print_stats(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn print_stats(pool: &mut DbPool<'_>, stats: &HashMap<InstanceId, FederationQueueState>) {
|
async fn print_stats(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
stats: &HashMap<InstanceId, FederationQueueStateWithDomain>,
|
||||||
|
) {
|
||||||
let res = print_stats_with_error(pool, stats).await;
|
let res = print_stats_with_error(pool, stats).await;
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
warn!("Failed to print stats: {e}");
|
warn!("Failed to print stats: {e}");
|
||||||
|
@ -52,18 +51,8 @@ async fn print_stats(pool: &mut DbPool<'_>, stats: &HashMap<InstanceId, Federati
|
||||||
|
|
||||||
async fn print_stats_with_error(
|
async fn print_stats_with_error(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
stats: &HashMap<InstanceId, FederationQueueState>,
|
stats: &HashMap<InstanceId, FederationQueueStateWithDomain>,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
static INSTANCE_CACHE: Lazy<Cache<(), Vec<Instance>>> = Lazy::new(|| {
|
|
||||||
Cache::builder()
|
|
||||||
.max_capacity(1)
|
|
||||||
.time_to_live(CACHE_DURATION_FEDERATION)
|
|
||||||
.build()
|
|
||||||
});
|
|
||||||
let instances = INSTANCE_CACHE
|
|
||||||
.try_get_with((), async { Instance::read_all(pool).await })
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let last_id = get_latest_activity_id(pool).await?;
|
let last_id = get_latest_activity_id(pool).await?;
|
||||||
|
|
||||||
// it's expected that the values are a bit out of date, everything < SAVE_STATE_EVERY should be
|
// it's expected that the values are a bit out of date, everything < SAVE_STATE_EVERY should be
|
||||||
|
@ -72,12 +61,9 @@ async fn print_stats_with_error(
|
||||||
// todo: more stats (act/sec, avg http req duration)
|
// todo: more stats (act/sec, avg http req duration)
|
||||||
let mut ok_count = 0;
|
let mut ok_count = 0;
|
||||||
let mut behind_count = 0;
|
let mut behind_count = 0;
|
||||||
for (instance_id, stat) in stats {
|
for ele in stats.values() {
|
||||||
let domain = &instances
|
let stat = &ele.state;
|
||||||
.iter()
|
let domain = &ele.domain;
|
||||||
.find(|i| &i.id == instance_id)
|
|
||||||
.ok_or(NotFound)?
|
|
||||||
.domain;
|
|
||||||
let behind = last_id.0 - stat.last_successful_id.map(|e| e.0).unwrap_or(0);
|
let behind = last_id.0 - stat.last_successful_id.map(|e| e.0).unwrap_or(0);
|
||||||
if stat.fail_count > 0 {
|
if stat.fail_count > 0 {
|
||||||
info!(
|
info!(
|
||||||
|
|
|
@ -11,13 +11,13 @@ use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
activity::{ActorType, SentActivity},
|
activity::{ActorType, SentActivity},
|
||||||
community::Community,
|
community::Community,
|
||||||
|
federation_queue_state::FederationQueueState,
|
||||||
person::Person,
|
person::Person,
|
||||||
site::Site,
|
site::Site,
|
||||||
},
|
},
|
||||||
traits::ApubActor,
|
traits::ApubActor,
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
|
@ -25,7 +25,6 @@ use serde_json::Value;
|
||||||
use std::{fmt::Debug, future::Future, pin::Pin, sync::Arc, time::Duration};
|
use std::{fmt::Debug, future::Future, pin::Pin, sync::Arc, time::Duration};
|
||||||
use tokio::{task::JoinHandle, time::sleep};
|
use tokio::{task::JoinHandle, time::sleep};
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
use tracing::error;
|
|
||||||
|
|
||||||
/// Decrease the delays of the federation queue.
|
/// Decrease the delays of the federation queue.
|
||||||
/// Should only be used for federation tests since it significantly increases CPU and DB load of the
|
/// Should only be used for federation tests since it significantly increases CPU and DB load of the
|
||||||
|
@ -59,26 +58,33 @@ pub struct CancellableTask {
|
||||||
|
|
||||||
impl CancellableTask {
|
impl CancellableTask {
|
||||||
/// spawn a task but with graceful shutdown
|
/// spawn a task but with graceful shutdown
|
||||||
pub fn spawn<F, R: Debug>(
|
pub fn spawn<F, R>(
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
task: impl FnOnce(CancellationToken) -> F + Send + 'static,
|
task: impl Fn(CancellationToken) -> F + Send + 'static,
|
||||||
) -> CancellableTask
|
) -> CancellableTask
|
||||||
where
|
where
|
||||||
F: Future<Output = LemmyResult<R>> + Send + 'static,
|
F: Future<Output = R> + Send + 'static,
|
||||||
R: Send + 'static,
|
R: Send + Debug + 'static,
|
||||||
{
|
{
|
||||||
let stop = CancellationToken::new();
|
let stop = CancellationToken::new();
|
||||||
let stop2 = stop.clone();
|
let stop2 = stop.clone();
|
||||||
let task: JoinHandle<LemmyResult<R>> = tokio::spawn(task(stop2));
|
let task: JoinHandle<()> = tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
let res = task(stop2.clone()).await;
|
||||||
|
if stop2.is_cancelled() {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
tracing::warn!("task exited, restarting: {res:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
let abort = task.abort_handle();
|
let abort = task.abort_handle();
|
||||||
CancellableTask {
|
CancellableTask {
|
||||||
f: Box::pin(async move {
|
f: Box::pin(async move {
|
||||||
stop.cancel();
|
stop.cancel();
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
r = task => {
|
r = task => {
|
||||||
if let Err(ref e) = r? {
|
r.context("CancellableTask failed to cancel cleanly, returned error")?;
|
||||||
error!("CancellableTask threw error: {e}");
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
_ = sleep(timeout) => {
|
_ = sleep(timeout) => {
|
||||||
|
@ -183,3 +189,10 @@ pub(crate) async fn get_latest_activity_id(pool: &mut DbPool<'_>) -> Result<Acti
|
||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow::anyhow!("err getting id: {e:?}"))
|
.map_err(|e| anyhow::anyhow!("err getting id: {e:?}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the domain name is needed for logging, pass it to the stats printer so it doesn't need to look
|
||||||
|
/// up the domain itself
|
||||||
|
pub(crate) struct FederationQueueStateWithDomain {
|
||||||
|
pub domain: String,
|
||||||
|
pub state: FederationQueueState,
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::util::{
|
||||||
get_activity_cached,
|
get_activity_cached,
|
||||||
get_actor_cached,
|
get_actor_cached,
|
||||||
get_latest_activity_id,
|
get_latest_activity_id,
|
||||||
|
FederationQueueStateWithDomain,
|
||||||
LEMMY_TEST_FAST_FEDERATION,
|
LEMMY_TEST_FAST_FEDERATION,
|
||||||
WORK_FINISHED_RECHECK_DELAY,
|
WORK_FINISHED_RECHECK_DELAY,
|
||||||
};
|
};
|
||||||
|
@ -75,7 +76,7 @@ pub(crate) struct InstanceWorker {
|
||||||
followed_communities: HashMap<CommunityId, HashSet<Url>>,
|
followed_communities: HashMap<CommunityId, HashSet<Url>>,
|
||||||
stop: CancellationToken,
|
stop: CancellationToken,
|
||||||
context: Data<LemmyContext>,
|
context: Data<LemmyContext>,
|
||||||
stats_sender: UnboundedSender<(InstanceId, FederationQueueState)>,
|
stats_sender: UnboundedSender<FederationQueueStateWithDomain>,
|
||||||
last_full_communities_fetch: DateTime<Utc>,
|
last_full_communities_fetch: DateTime<Utc>,
|
||||||
last_incremental_communities_fetch: DateTime<Utc>,
|
last_incremental_communities_fetch: DateTime<Utc>,
|
||||||
state: FederationQueueState,
|
state: FederationQueueState,
|
||||||
|
@ -87,7 +88,7 @@ impl InstanceWorker {
|
||||||
instance: Instance,
|
instance: Instance,
|
||||||
context: Data<LemmyContext>,
|
context: Data<LemmyContext>,
|
||||||
stop: CancellationToken,
|
stop: CancellationToken,
|
||||||
stats_sender: UnboundedSender<(InstanceId, FederationQueueState)>,
|
stats_sender: UnboundedSender<FederationQueueStateWithDomain>,
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
let mut pool = context.pool();
|
let mut pool = context.pool();
|
||||||
let state = FederationQueueState::load(&mut pool, instance.id).await?;
|
let state = FederationQueueState::load(&mut pool, instance.id).await?;
|
||||||
|
@ -166,6 +167,14 @@ impl InstanceWorker {
|
||||||
latest_id
|
latest_id
|
||||||
};
|
};
|
||||||
if id >= latest_id {
|
if id >= latest_id {
|
||||||
|
if id > latest_id {
|
||||||
|
tracing::error!(
|
||||||
|
"{}: last successful id {} is higher than latest id {} in database (did the db get cleared?)",
|
||||||
|
self.instance.domain,
|
||||||
|
id.0,
|
||||||
|
latest_id.0
|
||||||
|
);
|
||||||
|
}
|
||||||
// no more work to be done, wait before rechecking
|
// no more work to be done, wait before rechecking
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
() = sleep(*WORK_FINISHED_RECHECK_DELAY) => {},
|
() = sleep(*WORK_FINISHED_RECHECK_DELAY) => {},
|
||||||
|
@ -350,9 +359,10 @@ impl InstanceWorker {
|
||||||
async fn save_and_send_state(&mut self) -> Result<()> {
|
async fn save_and_send_state(&mut self) -> Result<()> {
|
||||||
self.last_state_insert = Utc::now();
|
self.last_state_insert = Utc::now();
|
||||||
FederationQueueState::upsert(&mut self.context.pool(), &self.state).await?;
|
FederationQueueState::upsert(&mut self.context.pool(), &self.state).await?;
|
||||||
self
|
self.stats_sender.send(FederationQueueStateWithDomain {
|
||||||
.stats_sender
|
state: self.state.clone(),
|
||||||
.send((self.instance.id, self.state.clone()))?;
|
domain: self.instance.domain.clone(),
|
||||||
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -355,7 +355,7 @@ async fn get_feed_front(
|
||||||
|
|
||||||
let posts = PostQuery {
|
let posts = PostQuery {
|
||||||
listing_type: (Some(ListingType::Subscribed)),
|
listing_type: (Some(ListingType::Subscribed)),
|
||||||
local_user: (Some(&local_user)),
|
local_user: (Some(&local_user.local_user)),
|
||||||
sort: (Some(*sort_type)),
|
sort: (Some(*sort_type)),
|
||||||
limit: (Some(*limit)),
|
limit: (Some(*limit)),
|
||||||
page: (Some(*page)),
|
page: (Some(*page)),
|
||||||
|
|
|
@ -103,7 +103,13 @@ async fn upload(
|
||||||
pictrs_alias: image.file.to_string(),
|
pictrs_alias: image.file.to_string(),
|
||||||
pictrs_delete_token: image.delete_token.to_string(),
|
pictrs_delete_token: image.delete_token.to_string(),
|
||||||
};
|
};
|
||||||
LocalImage::create(&mut context.pool(), &form).await?;
|
|
||||||
|
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
|
let thumbnail_url = image.thumbnail_url(&protocol_and_hostname)?;
|
||||||
|
|
||||||
|
// Also store the details for the image
|
||||||
|
let details_form = image.details.build_image_details_form(&thumbnail_url);
|
||||||
|
LocalImage::create(&mut context.pool(), &form, &details_form).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,17 +9,21 @@ use lemmy_utils::{
|
||||||
VERSION,
|
VERSION,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
/// A description of the nodeinfo endpoint is here:
|
||||||
|
/// https://github.com/jhass/nodeinfo/blob/main/PROTOCOL.md
|
||||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
cfg
|
cfg
|
||||||
.route(
|
.route(
|
||||||
"/nodeinfo/2.1.json",
|
"/nodeinfo/2.1",
|
||||||
web::get().to(node_info).wrap(cache_1hour()),
|
web::get().to(node_info).wrap(cache_1hour()),
|
||||||
)
|
)
|
||||||
.service(web::redirect("/version", "/nodeinfo/2.1.json"))
|
.service(web::redirect("/version", "/nodeinfo/2.1"))
|
||||||
// For backwards compatibility, can be removed after Lemmy 0.20
|
// For backwards compatibility, can be removed after Lemmy 0.20
|
||||||
.service(web::redirect("/nodeinfo/2.0.json", "/nodeinfo/2.1.json"))
|
.service(web::redirect("/nodeinfo/2.0.json", "/nodeinfo/2.1"))
|
||||||
|
.service(web::redirect("/nodeinfo/2.1.json", "/nodeinfo/2.1"))
|
||||||
.route(
|
.route(
|
||||||
"/.well-known/nodeinfo",
|
"/.well-known/nodeinfo",
|
||||||
web::get().to(node_info_well_known).wrap(cache_3days()),
|
web::get().to(node_info_well_known).wrap(cache_3days()),
|
||||||
|
@ -31,7 +35,7 @@ async fn node_info_well_known(context: web::Data<LemmyContext>) -> LemmyResult<H
|
||||||
links: vec![NodeInfoWellKnownLinks {
|
links: vec![NodeInfoWellKnownLinks {
|
||||||
rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.1")?,
|
rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.1")?,
|
||||||
href: Url::parse(&format!(
|
href: Url::parse(&format!(
|
||||||
"{}/nodeinfo/2.1.json",
|
"{}/nodeinfo/2.1",
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
))?,
|
))?,
|
||||||
}],
|
}],
|
||||||
|
@ -71,19 +75,19 @@ async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Err
|
||||||
inbound: Some(vec![]),
|
inbound: Some(vec![]),
|
||||||
outbound: Some(vec![]),
|
outbound: Some(vec![]),
|
||||||
}),
|
}),
|
||||||
metadata: Some(vec![]),
|
metadata: Some(HashMap::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(json))
|
Ok(HttpResponse::Ok().json(json))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
struct NodeInfoWellKnown {
|
pub struct NodeInfoWellKnown {
|
||||||
pub links: Vec<NodeInfoWellKnownLinks>,
|
pub links: Vec<NodeInfoWellKnownLinks>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
struct NodeInfoWellKnownLinks {
|
pub struct NodeInfoWellKnownLinks {
|
||||||
pub rel: Url,
|
pub rel: Url,
|
||||||
pub href: Url,
|
pub href: Url,
|
||||||
}
|
}
|
||||||
|
@ -99,7 +103,7 @@ pub struct NodeInfo {
|
||||||
pub open_registrations: Option<bool>,
|
pub open_registrations: Option<bool>,
|
||||||
/// These fields are required by the spec for no reason
|
/// These fields are required by the spec for no reason
|
||||||
pub services: Option<NodeInfoServices>,
|
pub services: Option<NodeInfoServices>,
|
||||||
pub metadata: Option<Vec<String>>,
|
pub metadata: Option<HashMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
|
|
@ -11,8 +11,10 @@ static VALID_MATRIX_ID_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||||
});
|
});
|
||||||
// taken from https://en.wikipedia.org/wiki/UTM_parameters
|
// taken from https://en.wikipedia.org/wiki/UTM_parameters
|
||||||
static CLEAN_URL_PARAMS_REGEX: Lazy<Regex> = Lazy::new(|| {
|
static CLEAN_URL_PARAMS_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||||
Regex::new(r"^utm_source|utm_medium|utm_campaign|utm_term|utm_content|gclid|gclsrc|dclid|fbclid$")
|
Regex::new(
|
||||||
.expect("compile regex")
|
r"^(utm_source|utm_medium|utm_campaign|utm_term|utm_content|gclid|gclsrc|dclid|fbclid)=",
|
||||||
|
)
|
||||||
|
.expect("compile regex")
|
||||||
});
|
});
|
||||||
const ALLOWED_POST_URL_SCHEMES: [&str; 3] = ["http", "https", "magnet"];
|
const ALLOWED_POST_URL_SCHEMES: [&str; 3] = ["http", "https", "magnet"];
|
||||||
|
|
||||||
|
@ -158,14 +160,12 @@ pub fn is_valid_post_title(title: &str) -> LemmyResult<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This could be post bodies, comments, or any description field
|
/// This could be post bodies, comments, or any description field
|
||||||
pub fn is_valid_body_field(body: &Option<String>, post: bool) -> LemmyResult<()> {
|
pub fn is_valid_body_field(body: &str, post: bool) -> LemmyResult<()> {
|
||||||
if let Some(body) = body {
|
if post {
|
||||||
if post {
|
max_length_check(body, POST_BODY_MAX_LENGTH, LemmyErrorType::InvalidBodyField)?;
|
||||||
max_length_check(body, POST_BODY_MAX_LENGTH, LemmyErrorType::InvalidBodyField)?;
|
} else {
|
||||||
} else {
|
max_length_check(body, BODY_MAX_LENGTH, LemmyErrorType::InvalidBodyField)?;
|
||||||
max_length_check(body, BODY_MAX_LENGTH, LemmyErrorType::InvalidBodyField)?;
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,16 +173,14 @@ pub fn is_valid_bio_field(bio: &str) -> LemmyResult<()> {
|
||||||
max_length_check(bio, BIO_MAX_LENGTH, LemmyErrorType::BioLengthOverflow)
|
max_length_check(bio, BIO_MAX_LENGTH, LemmyErrorType::BioLengthOverflow)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid_alt_text_field(alt_text: &Option<String>) -> LemmyResult<()> {
|
pub fn is_valid_alt_text_field(alt_text: &str) -> LemmyResult<()> {
|
||||||
if let Some(alt_text) = alt_text {
|
max_length_check(
|
||||||
max_length_check(
|
alt_text,
|
||||||
alt_text,
|
ALT_TEXT_MAX_LENGTH,
|
||||||
ALT_TEXT_MAX_LENGTH,
|
LemmyErrorType::AltTextLengthOverflow,
|
||||||
LemmyErrorType::AltTextLengthOverflow,
|
)?;
|
||||||
)
|
|
||||||
} else {
|
Ok(())
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the site name length, the limit as defined in the DB.
|
/// Checks the site name length, the limit as defined in the DB.
|
||||||
|
@ -260,12 +258,11 @@ pub fn build_and_check_regex(regex_str_opt: &Option<&str>) -> LemmyResult<Option
|
||||||
|
|
||||||
pub fn clean_url_params(url: &Url) -> Url {
|
pub fn clean_url_params(url: &Url) -> Url {
|
||||||
let mut url_out = url.clone();
|
let mut url_out = url.clone();
|
||||||
if url.query().is_some() {
|
if let Some(query) = url.query() {
|
||||||
let new_query = url
|
let new_query = query
|
||||||
.query_pairs()
|
.split_inclusive('&')
|
||||||
.filter(|q| !CLEAN_URL_PARAMS_REGEX.is_match(&q.0))
|
.filter(|q| !CLEAN_URL_PARAMS_REGEX.is_match(q))
|
||||||
.map(|q| format!("{}={}", q.0, q.1))
|
.collect::<String>();
|
||||||
.join("&");
|
|
||||||
url_out.set_query(Some(&new_query));
|
url_out.set_query(Some(&new_query));
|
||||||
}
|
}
|
||||||
url_out
|
url_out
|
||||||
|
@ -287,23 +284,17 @@ pub fn check_site_visibility_valid(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_url_scheme(url: &Option<Url>) -> LemmyResult<()> {
|
pub fn check_url_scheme(url: &Url) -> LemmyResult<()> {
|
||||||
if let Some(url) = url {
|
if !ALLOWED_POST_URL_SCHEMES.contains(&url.scheme()) {
|
||||||
if !ALLOWED_POST_URL_SCHEMES.contains(&url.scheme()) {
|
Err(LemmyErrorType::InvalidUrlScheme)?
|
||||||
Err(LemmyErrorType::InvalidUrlScheme.into())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_url_blocked(url: &Option<Url>, blocklist: &RegexSet) -> LemmyResult<()> {
|
pub fn is_url_blocked(url: &Url, blocklist: &RegexSet) -> LemmyResult<()> {
|
||||||
if let Some(url) = url {
|
if blocklist.is_match(url.as_str()) {
|
||||||
if blocklist.is_match(url.as_str()) {
|
Err(LemmyErrorType::BlockedUrl)?
|
||||||
Err(LemmyErrorType::BlockedUrl)?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -350,12 +341,11 @@ pub fn build_url_str_without_scheme(url_str: &str) -> LemmyResult<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[allow(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::LemmyErrorType,
|
error::{LemmyErrorType, LemmyResult},
|
||||||
utils::validation::{
|
utils::validation::{
|
||||||
build_and_check_regex,
|
build_and_check_regex,
|
||||||
check_site_visibility_valid,
|
check_site_visibility_valid,
|
||||||
|
@ -379,15 +369,17 @@ mod tests {
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_clean_url_params() {
|
fn test_clean_url_params() -> LemmyResult<()> {
|
||||||
let url = Url::parse("https://example.com/path/123?utm_content=buffercf3b2&utm_medium=social&username=randomuser&id=123").unwrap();
|
let url = Url::parse("https://example.com/path/123?utm_content=buffercf3b2&utm_medium=social&user+name=random+user%20&id=123")?;
|
||||||
let cleaned = clean_url_params(&url);
|
let cleaned = clean_url_params(&url);
|
||||||
let expected = Url::parse("https://example.com/path/123?username=randomuser&id=123").unwrap();
|
let expected = Url::parse("https://example.com/path/123?user+name=random+user%20&id=123")?;
|
||||||
assert_eq!(expected.to_string(), cleaned.to_string());
|
assert_eq!(expected.to_string(), cleaned.to_string());
|
||||||
|
|
||||||
let url = Url::parse("https://example.com/path/123").unwrap();
|
let url = Url::parse("https://example.com/path/123")?;
|
||||||
let cleaned = clean_url_params(&url);
|
let cleaned = clean_url_params(&url);
|
||||||
assert_eq!(url.to_string(), cleaned.to_string());
|
assert_eq!(url.to_string(), cleaned.to_string());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -465,7 +457,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_valid_site_name() {
|
fn test_valid_site_name() -> LemmyResult<()> {
|
||||||
let valid_names = [
|
let valid_names = [
|
||||||
(0..SITE_NAME_MAX_LENGTH).map(|_| 'A').collect::<String>(),
|
(0..SITE_NAME_MAX_LENGTH).map(|_| 'A').collect::<String>(),
|
||||||
String::from("A"),
|
String::from("A"),
|
||||||
|
@ -496,12 +488,13 @@ mod tests {
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(
|
assert!(
|
||||||
result.unwrap_err().error_type.eq(&expected_err.clone()),
|
result.is_err_and(|e| e.error_type.eq(&expected_err.clone())),
|
||||||
"Testing {}, expected error {}",
|
"Testing {}, expected error {}",
|
||||||
invalid_name,
|
invalid_name,
|
||||||
expected_err
|
expected_err
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -513,10 +506,7 @@ mod tests {
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
invalid_result.is_err()
|
invalid_result.is_err()
|
||||||
&& invalid_result
|
&& invalid_result.is_err_and(|e| e.error_type.eq(&LemmyErrorType::BioLengthOverflow))
|
||||||
.unwrap_err()
|
|
||||||
.error_type
|
|
||||||
.eq(&LemmyErrorType::BioLengthOverflow)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,10 +527,9 @@ mod tests {
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
invalid_result.is_err()
|
invalid_result.is_err()
|
||||||
&& invalid_result
|
&& invalid_result.is_err_and(|e| e
|
||||||
.unwrap_err()
|
|
||||||
.error_type
|
.error_type
|
||||||
.eq(&LemmyErrorType::SiteDescriptionLengthOverflow)
|
.eq(&LemmyErrorType::SiteDescriptionLengthOverflow))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,7 +559,7 @@ mod tests {
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(
|
assert!(
|
||||||
result.unwrap_err().error_type.eq(&expected_err.clone()),
|
result.is_err_and(|e| e.error_type.eq(&expected_err.clone())),
|
||||||
"Testing regex {:?}, expected error {}",
|
"Testing regex {:?}, expected error {}",
|
||||||
regex_str,
|
regex_str,
|
||||||
expected_err
|
expected_err
|
||||||
|
@ -591,38 +580,38 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_url_scheme() {
|
fn test_check_url_scheme() -> LemmyResult<()> {
|
||||||
assert!(check_url_scheme(&None).is_ok());
|
assert!(check_url_scheme(&Url::parse("http://example.com")?).is_ok());
|
||||||
assert!(check_url_scheme(&Some(Url::parse("http://example.com").unwrap())).is_ok());
|
assert!(check_url_scheme(&Url::parse("https://example.com")?).is_ok());
|
||||||
assert!(check_url_scheme(&Some(Url::parse("https://example.com").unwrap())).is_ok());
|
assert!(check_url_scheme(&Url::parse("https://example.com")?).is_ok());
|
||||||
assert!(check_url_scheme(&Some(Url::parse("https://example.com").unwrap())).is_ok());
|
assert!(check_url_scheme(&Url::parse("ftp://example.com")?).is_err());
|
||||||
assert!(check_url_scheme(&Some(Url::parse("ftp://example.com").unwrap())).is_err());
|
assert!(check_url_scheme(&Url::parse("javascript:void")?).is_err());
|
||||||
assert!(check_url_scheme(&Some(Url::parse("javascript:void").unwrap())).is_err());
|
|
||||||
|
|
||||||
let magnet_link="magnet:?xt=urn:btih:4b390af3891e323778959d5abfff4b726510f14c&dn=Ravel%20Complete%20Piano%20Sheet%20Music%20-%20Public%20Domain&tr=udp%3A%2F%2Fopen.tracker.cl%3A1337%2Fannounce";
|
let magnet_link="magnet:?xt=urn:btih:4b390af3891e323778959d5abfff4b726510f14c&dn=Ravel%20Complete%20Piano%20Sheet%20Music%20-%20Public%20Domain&tr=udp%3A%2F%2Fopen.tracker.cl%3A1337%2Fannounce";
|
||||||
assert!(check_url_scheme(&Some(Url::parse(magnet_link).unwrap())).is_ok());
|
assert!(check_url_scheme(&Url::parse(magnet_link)?).is_ok());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_url_block() {
|
fn test_url_block() -> LemmyResult<()> {
|
||||||
let set = regex::RegexSet::new(vec![
|
let set = regex::RegexSet::new(vec![
|
||||||
r"(https://)?example\.org/page/to/article",
|
r"(https://)?example\.org/page/to/article",
|
||||||
r"(https://)?example\.net/?",
|
r"(https://)?example\.net/?",
|
||||||
r"(https://)?example\.com/?",
|
r"(https://)?example\.com/?",
|
||||||
])
|
])?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(is_url_blocked(&Some(Url::parse("https://example.blog").unwrap()), &set).is_ok());
|
assert!(is_url_blocked(&Url::parse("https://example.blog")?, &set).is_ok());
|
||||||
|
|
||||||
assert!(is_url_blocked(&Some(Url::parse("https://example.org").unwrap()), &set).is_ok());
|
assert!(is_url_blocked(&Url::parse("https://example.org")?, &set).is_ok());
|
||||||
|
|
||||||
assert!(is_url_blocked(&None, &set).is_ok());
|
assert!(is_url_blocked(&Url::parse("https://example.com")?, &set).is_err());
|
||||||
|
|
||||||
assert!(is_url_blocked(&Some(Url::parse("https://example.com").unwrap()), &set).is_err());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_url_parsed() {
|
fn test_url_parsed() -> LemmyResult<()> {
|
||||||
// Make sure the scheme is removed, and uniques also
|
// Make sure the scheme is removed, and uniques also
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&check_urls_are_valid(&vec![
|
&check_urls_are_valid(&vec![
|
||||||
|
@ -630,8 +619,7 @@ mod tests {
|
||||||
"http://example.com".to_string(),
|
"http://example.com".to_string(),
|
||||||
"https://example.com".to_string(),
|
"https://example.com".to_string(),
|
||||||
"https://example.com/test?q=test2&q2=test3#test4".to_string(),
|
"https://example.com/test?q=test2&q2=test3#test4".to_string(),
|
||||||
])
|
])?,
|
||||||
.unwrap(),
|
|
||||||
&vec![
|
&vec![
|
||||||
"example.com".to_string(),
|
"example.com".to_string(),
|
||||||
"example.com/test?q=test2&q2=test3#test4".to_string()
|
"example.com/test?q=test2&q2=test3#test4".to_string()
|
||||||
|
@ -639,5 +627,6 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(check_urls_are_valid(&vec!["https://example .com".to_string()]).is_err());
|
assert!(check_urls_are_valid(&vec!["https://example .com".to_string()]).is_err());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit e9b3b25fa1af7e06c4ffab86624d95da0836ef36
|
Subproject commit ee2cffac809ad466644f061ad79ac577b6c2e4fd
|
|
@ -1,5 +1,5 @@
|
||||||
# syntax=docker/dockerfile:1.7
|
# syntax=docker/dockerfile:1.8
|
||||||
ARG RUST_VERSION=1.77
|
ARG RUST_VERSION=1.78
|
||||||
ARG CARGO_BUILD_FEATURES=default
|
ARG CARGO_BUILD_FEATURES=default
|
||||||
ARG RUST_RELEASE_MODE=debug
|
ARG RUST_RELEASE_MODE=debug
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ services:
|
||||||
|
|
||||||
lemmy:
|
lemmy:
|
||||||
# use "image" to pull down an already compiled lemmy. make sure to comment out "build".
|
# use "image" to pull down an already compiled lemmy. make sure to comment out "build".
|
||||||
# image: dessalines/lemmy:0.19.3
|
# image: dessalines/lemmy:0.19.5
|
||||||
# platform: linux/x86_64 # no arm64 support. uncomment platform if using m1.
|
# platform: linux/x86_64 # no arm64 support. uncomment platform if using m1.
|
||||||
# use "build" to build your local lemmy server image for development. make sure to comment out "image".
|
# use "build" to build your local lemmy server image for development. make sure to comment out "image".
|
||||||
# run: docker compose up --build
|
# run: docker compose up --build
|
||||||
|
@ -53,7 +53,7 @@ services:
|
||||||
|
|
||||||
lemmy-ui:
|
lemmy-ui:
|
||||||
# use "image" to pull down an already compiled lemmy-ui. make sure to comment out "build".
|
# use "image" to pull down an already compiled lemmy-ui. make sure to comment out "build".
|
||||||
image: dessalines/lemmy-ui:0.19.3
|
image: dessalines/lemmy-ui:0.19.5
|
||||||
# platform: linux/x86_64 # no arm64 support. uncomment platform if using m1.
|
# platform: linux/x86_64 # no arm64 support. uncomment platform if using m1.
|
||||||
# use "build" to build your local lemmy ui image for development. make sure to comment out "image".
|
# use "build" to build your local lemmy ui image for development. make sure to comment out "image".
|
||||||
# run: docker compose up --build
|
# run: docker compose up --build
|
||||||
|
@ -75,7 +75,7 @@ services:
|
||||||
init: true
|
init: true
|
||||||
|
|
||||||
pictrs:
|
pictrs:
|
||||||
image: asonix/pictrs:0.5.13
|
image: asonix/pictrs:0.5.16
|
||||||
# this needs to match the pictrs url in lemmy.hjson
|
# this needs to match the pictrs url in lemmy.hjson
|
||||||
hostname: pictrs
|
hostname: pictrs
|
||||||
# we can set options to pictrs like this, here we set max. image size and forced format for conversion
|
# we can set options to pictrs like this, here we set max. image size and forced format for conversion
|
||||||
|
|
|
@ -2,7 +2,7 @@ version: "3.7"
|
||||||
|
|
||||||
x-ui-default: &ui-default
|
x-ui-default: &ui-default
|
||||||
init: true
|
init: true
|
||||||
image: dessalines/lemmy-ui:0.19.3
|
image: dessalines/lemmy-ui:0.19.5
|
||||||
# assuming lemmy-ui is cloned besides lemmy directory
|
# assuming lemmy-ui is cloned besides lemmy directory
|
||||||
# build:
|
# build:
|
||||||
# context: ../../../lemmy-ui
|
# context: ../../../lemmy-ui
|
||||||
|
@ -49,7 +49,7 @@ services:
|
||||||
|
|
||||||
pictrs:
|
pictrs:
|
||||||
restart: always
|
restart: always
|
||||||
image: asonix/pictrs:0.5.13
|
image: asonix/pictrs:0.5.16
|
||||||
user: 991:991
|
user: 991:991
|
||||||
volumes:
|
volumes:
|
||||||
- ./volumes/pictrs_alpha:/mnt:Z
|
- ./volumes/pictrs_alpha:/mnt:Z
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
ALTER TABLE remote_image
|
||||||
|
ADD UNIQUE (link),
|
||||||
|
DROP CONSTRAINT remote_image_pkey,
|
||||||
|
ADD COLUMN id serial PRIMARY KEY;
|
||||||
|
|
||||||
|
DROP TABLE image_details;
|
||||||
|
|
15
migrations/2024-05-05-162540_add_image_detail_table/up.sql
Normal file
15
migrations/2024-05-05-162540_add_image_detail_table/up.sql
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
-- Drop the id column from the remote_image table, just use link
|
||||||
|
ALTER TABLE remote_image
|
||||||
|
DROP COLUMN id,
|
||||||
|
ADD PRIMARY KEY (link),
|
||||||
|
DROP CONSTRAINT remote_image_link_key;
|
||||||
|
|
||||||
|
-- No good way to do references here unfortunately, unless we combine the images tables
|
||||||
|
-- The link should be the URL, not the pictrs_alias, to allow joining from post.thumbnail_url
|
||||||
|
CREATE TABLE image_details (
|
||||||
|
link text PRIMARY KEY,
|
||||||
|
width integer NOT NULL,
|
||||||
|
height integer NOT NULL,
|
||||||
|
content_type text NOT NULL
|
||||||
|
);
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
SELECT
|
||||||
|
;
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
-- Fix rows that were not updated because of the old incorrect trigger
|
||||||
|
UPDATE
|
||||||
|
post_aggregates
|
||||||
|
SET
|
||||||
|
featured_local = post.featured_local
|
||||||
|
FROM
|
||||||
|
post
|
||||||
|
WHERE
|
||||||
|
post.id = post_aggregates.post_id
|
||||||
|
AND post.featured_local != post_aggregates.featured_local;
|
||||||
|
|
|
@ -24,7 +24,7 @@ echo "Removing the old postgres folder"
|
||||||
sudo rm -rf volumes/postgres
|
sudo rm -rf volumes/postgres
|
||||||
|
|
||||||
echo "Updating docker compose to use postgres version 16."
|
echo "Updating docker compose to use postgres version 16."
|
||||||
sudo sed -i "s/image: .*postgres:.*/image: docker.io/postgres:16-alpine/" ./docker-compose.yml
|
sudo sed -i "s/image: .*postgres:.*/image: docker.io\/postgres:16-alpine/" ./docker-compose.yml
|
||||||
|
|
||||||
echo "Starting up new postgres..."
|
echo "Starting up new postgres..."
|
||||||
sudo docker compose up -d postgres
|
sudo docker compose up -d postgres
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue