mirror of
https://github.com/AtHeartEngineer/lemmy.git
synced 2026-01-09 20:57:53 -05:00
@@ -6,13 +6,13 @@ variables:
|
||||
# as well. Otherwise release builds can fail if Lemmy or dependencies rely on new Rust
|
||||
# features. In particular the ARM builder image needs to be updated manually in the repo below:
|
||||
# https://github.com/raskyld/lemmy-cross-toolchains
|
||||
- &rust_image "rust:1.83"
|
||||
- &rust_image "rust:1.81"
|
||||
- &rust_nightly_image "rustlang/rust:nightly"
|
||||
- &install_pnpm "npm install -g corepack@latest && corepack enable pnpm"
|
||||
- &install_binstall "wget -O- https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz | tar -xvz -C /usr/local/cargo/bin"
|
||||
- install_diesel_cli: &install_diesel_cli
|
||||
- apt-get update && apt-get install -y postgresql-client
|
||||
- cargo install diesel_cli --no-default-features --features postgres
|
||||
- cargo install --locked diesel_cli --no-default-features --features postgres
|
||||
- export PATH="$CARGO_HOME/bin:$PATH"
|
||||
- &slow_check_paths
|
||||
- event: pull_request
|
||||
|
||||
241
Cargo.lock
generated
241
Cargo.lock
generated
@@ -34,7 +34,7 @@ dependencies = [
|
||||
"moka",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"reqwest 0.12.12",
|
||||
"reqwest-middleware",
|
||||
@@ -121,7 +121,7 @@ dependencies = [
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"sha1",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
@@ -333,10 +333,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -484,9 +484,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.85"
|
||||
version = "0.1.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
|
||||
checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -584,13 +584,13 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b1866ecef4f2d06a0bb77880015fdf2b89e25a1c2e5addacb87e459c86dc67e"
|
||||
checksum = "92758ad6077e4c76a6cadbce5005f666df70d4f13b19976b1a8062eef880040f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"blowfish",
|
||||
"getrandom",
|
||||
"getrandom 0.3.1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -737,7 +737,7 @@ dependencies = [
|
||||
"hound",
|
||||
"image",
|
||||
"lodepng",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
@@ -832,9 +832,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.27"
|
||||
version = "4.5.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796"
|
||||
checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -842,9 +842,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.27"
|
||||
version = "4.5.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
|
||||
checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -854,9 +854,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.24"
|
||||
version = "4.5.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
|
||||
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -1122,9 +1122,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deadpool"
|
||||
version = "0.12.1"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6541a3916932fe57768d4be0b1ffb5ec7cbf74ca8c903fdfd5c0fe8aa958f0ed"
|
||||
checksum = "5ed5957ff93768adf7a65ab167a17835c3d2c3c50d084fe305174c112f468e2f"
|
||||
dependencies = [
|
||||
"deadpool-runtime",
|
||||
"num_cpus",
|
||||
@@ -1251,9 +1251,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "diesel"
|
||||
version = "2.2.6"
|
||||
version = "2.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf1bedf64cdb9643204a36dd15b19a6ce8e7aa7f7b105868e9f1fad5ffa7d12"
|
||||
checksum = "04001f23ba8843dc315804fa324000376084dfb1c30794ff68dd279e6e5696d5"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"byteorder",
|
||||
@@ -1545,7 +1545,7 @@ checksum = "2e1f6c3800b304a6be0012039e2a45a322a093539c45ab818d9e6895a39c90fe"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
@@ -1791,10 +1791,22 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.13.3+wasi-0.2.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
@@ -1921,13 +1933,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "html2text"
|
||||
version = "0.13.6"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bf7722c2ffdd62628b6e13065b6ab6cf154a236bd476c6e89af1352d745b83e"
|
||||
checksum = "eacd0d94e37b02109daef505556923edda7785047f24d9634b84835a8122da7a"
|
||||
dependencies = [
|
||||
"html5ever 0.29.0",
|
||||
"markup5ever 0.14.0",
|
||||
"nom",
|
||||
"tendril",
|
||||
"thiserror 2.0.11",
|
||||
"unicode-width 0.2.0",
|
||||
@@ -2136,7 +2147,7 @@ dependencies = [
|
||||
"http 1.2.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper-util",
|
||||
"rustls 0.23.21",
|
||||
"rustls 0.23.23",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.1",
|
||||
@@ -2404,9 +2415,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "infer"
|
||||
version = "0.16.0"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847"
|
||||
checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7"
|
||||
dependencies = [
|
||||
"cfb",
|
||||
]
|
||||
@@ -2506,11 +2517,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jsonwebtoken"
|
||||
version = "9.3.0"
|
||||
version = "9.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f"
|
||||
checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"base64 0.22.1",
|
||||
"js-sys",
|
||||
"pem",
|
||||
"ring",
|
||||
@@ -2591,7 +2602,6 @@ dependencies = [
|
||||
"regex",
|
||||
"reqwest 0.12.12",
|
||||
"reqwest-middleware",
|
||||
"rosetta-i18n",
|
||||
"serde",
|
||||
"serde_with",
|
||||
"serial_test",
|
||||
@@ -2702,7 +2712,7 @@ dependencies = [
|
||||
"lemmy_utils",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"rustls 0.23.21",
|
||||
"rustls 0.23.23",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
@@ -2824,7 +2834,7 @@ dependencies = [
|
||||
"mimalloc",
|
||||
"reqwest-middleware",
|
||||
"reqwest-tracing",
|
||||
"rustls 0.23.21",
|
||||
"rustls 0.23.23",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -2875,9 +2885,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lettre"
|
||||
version = "0.11.11"
|
||||
version = "0.11.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab4c9a167ff73df98a5ecc07e8bf5ce90b583665da3d1762eb1f775ad4d0d6f5"
|
||||
checksum = "e882e1489810a45919477602194312b1a7df0e5acc30a6188be7b520268f63f8"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
@@ -2893,7 +2903,7 @@ dependencies = [
|
||||
"nom",
|
||||
"percent-encoding",
|
||||
"quoted_printable",
|
||||
"rustls 0.23.21",
|
||||
"rustls 0.23.23",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
"socket2",
|
||||
@@ -3283,7 +3293,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
@@ -3395,7 +3405,7 @@ dependencies = [
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -3572,7 +3582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
|
||||
dependencies = [
|
||||
"phf_shared 0.10.0",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3582,7 +3592,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.3",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3696,9 +3706,9 @@ checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
||||
|
||||
[[package]]
|
||||
name = "postgres-protocol"
|
||||
version = "0.6.7"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23"
|
||||
checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"byteorder",
|
||||
@@ -3707,16 +3717,16 @@ dependencies = [
|
||||
"hmac",
|
||||
"md-5",
|
||||
"memchr",
|
||||
"rand",
|
||||
"rand 0.9.0",
|
||||
"sha2",
|
||||
"stringprep",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postgres-types"
|
||||
version = "0.2.8"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f"
|
||||
checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fallible-iterator",
|
||||
@@ -3735,7 +3745,7 @@ version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3893,7 +3903,7 @@ dependencies = [
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls 0.23.21",
|
||||
"rustls 0.23.23",
|
||||
"socket2",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
@@ -3907,10 +3917,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"ring",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls 0.23.21",
|
||||
"rustls 0.23.23",
|
||||
"slab",
|
||||
"thiserror 1.0.69",
|
||||
"tinyvec",
|
||||
@@ -3952,8 +3962,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.0",
|
||||
"zerocopy 0.8.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3963,7 +3984,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3972,7 +4003,17 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
|
||||
dependencies = [
|
||||
"getrandom 0.3.1",
|
||||
"zerocopy 0.8.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4112,7 +4153,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls 0.23.21",
|
||||
"rustls 0.23.23",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
@@ -4156,7 +4197,7 @@ checksum = "73e6153390585f6961341b50e5a1931d6be6dee4292283635903c26ef9d980d2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
"http 1.2.0",
|
||||
"matchit",
|
||||
"reqwest 0.12.12",
|
||||
@@ -4181,7 +4222,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
"libc",
|
||||
"spin",
|
||||
"untrusted",
|
||||
@@ -4221,7 +4262,7 @@ dependencies = [
|
||||
"num-traits",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"signature",
|
||||
"spki",
|
||||
"subtle",
|
||||
@@ -4294,9 +4335,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.21"
|
||||
version = "0.23.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8"
|
||||
checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"log",
|
||||
@@ -4461,9 +4502,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.137"
|
||||
version = "1.0.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
|
||||
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
|
||||
dependencies = [
|
||||
"indexmap 2.7.0",
|
||||
"itoa",
|
||||
@@ -4601,7 +4642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4778,18 +4819,18 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.3"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
checksum = "ce1475c515a4f03a8a7129bb5228b81a781a86cb0b3fbbc19e1c556d491a401f"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.4"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||
checksum = "9688894b43459159c82bfa5a5fa0435c19cbe3c9b427fa1dd7b1ce0c279b18a7"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -4929,9 +4970,9 @@ checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
|
||||
|
||||
[[package]]
|
||||
name = "test-context"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6676ab8513edfd2601a108621103fdb45cac9098305ca25ec93f7023b06b05d9"
|
||||
checksum = "cb69cce03e432993e2dc1f93f7899b952300fcb6dc44191a1b830b60b8c3c8aa"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"test-context-macros",
|
||||
@@ -4939,9 +4980,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "test-context-macros"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ea17a2dc368aeca6f554343ced1b1e31f76d63683fa8016e5844bd7a5144a1"
|
||||
checksum = "97e0639209021e54dbe19cafabfc0b5574b078c37358945e6d473eabe39bb974"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5112,9 +5153,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-postgres"
|
||||
version = "0.7.12"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb"
|
||||
checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"byteorder",
|
||||
@@ -5129,7 +5170,7 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"postgres-protocol",
|
||||
"postgres-types",
|
||||
"rand",
|
||||
"rand 0.9.0",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -5144,7 +5185,7 @@ checksum = "27d684bad428a0f2481f42241f821db42c54e2dc81d8c00db8536c506b0a0144"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"ring",
|
||||
"rustls 0.23.21",
|
||||
"rustls 0.23.23",
|
||||
"tokio",
|
||||
"tokio-postgres",
|
||||
"tokio-rustls 0.26.1",
|
||||
@@ -5167,7 +5208,7 @@ version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
|
||||
dependencies = [
|
||||
"rustls 0.23.21",
|
||||
"rustls 0.23.23",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -5227,7 +5268,7 @@ dependencies = [
|
||||
"base32",
|
||||
"constant_time_eq",
|
||||
"hmac",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"url",
|
||||
@@ -5526,11 +5567,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.12.1"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
||||
checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.3.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -5577,6 +5618,15 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasite"
|
||||
version = "0.1.0"
|
||||
@@ -6024,6 +6074,15 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
@@ -6122,7 +6181,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
"zerocopy-derive 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.8.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6136,6 +6204,17 @@ dependencies = [
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.5"
|
||||
|
||||
18
Cargo.toml
18
Cargo.toml
@@ -92,7 +92,7 @@ lemmy_federate = { version = "=1.0.0-alpha.0", path = "./crates/federate" }
|
||||
activitypub_federation = { version = "0.6.2", default-features = false, features = [
|
||||
"actix-web",
|
||||
] }
|
||||
diesel = "2.2.6"
|
||||
diesel = "2.2.7"
|
||||
diesel_migrations = "2.2.0"
|
||||
diesel-async = "0.5.2"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
@@ -119,15 +119,15 @@ reqwest-middleware = "0.3.3"
|
||||
reqwest-tracing = "0.5.5"
|
||||
clokwerk = "0.4.0"
|
||||
doku = { version = "0.21.1", features = ["url-2"] }
|
||||
bcrypt = "0.16.0"
|
||||
bcrypt = "0.17.0"
|
||||
chrono = { version = "0.4.39", features = [
|
||||
"now",
|
||||
"serde",
|
||||
], default-features = false }
|
||||
serde_json = { version = "1.0.137", features = ["preserve_order"] }
|
||||
serde_json = { version = "1.0.138", features = ["preserve_order"] }
|
||||
base64 = "0.22.1"
|
||||
uuid = { version = "1.12.1", features = ["serde"] }
|
||||
async-trait = "0.1.85"
|
||||
uuid = { version = "1.13.1", features = ["serde"] }
|
||||
async-trait = "0.1.86"
|
||||
captcha = "0.0.9"
|
||||
anyhow = { version = "1.0.95", features = ["backtrace"] }
|
||||
diesel_ltree = "0.4.0"
|
||||
@@ -137,7 +137,7 @@ regex = "1.11.1"
|
||||
diesel-derive-newtype = "2.1.2"
|
||||
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
|
||||
enum-map = { version = "2.7" }
|
||||
strum = { version = "0.26.3", features = ["derive"] }
|
||||
strum = { version = "0.27.0", features = ["derive"] }
|
||||
itertools = "0.14.0"
|
||||
futures = "0.3.31"
|
||||
http = "1.2"
|
||||
@@ -147,14 +147,14 @@ ts-rs = { version = "10.1.0", features = [
|
||||
"no-serde-warnings",
|
||||
"url-impl",
|
||||
] }
|
||||
rustls = { version = "0.23.21", features = ["ring"] }
|
||||
rustls = { version = "0.23.23", features = ["ring"] }
|
||||
futures-util = "0.3.31"
|
||||
tokio-postgres = "0.7.12"
|
||||
tokio-postgres = "0.7.13"
|
||||
tokio-postgres-rustls = "0.13.0"
|
||||
urlencoding = "2.1.3"
|
||||
moka = { version = "0.12.10", features = ["future"] }
|
||||
i-love-jesus = { version = "0.1.0" }
|
||||
clap = { version = "4.5.27", features = ["derive", "env"] }
|
||||
clap = { version = "4.5.29", features = ["derive", "env"] }
|
||||
pretty_assertions = "1.4.1"
|
||||
derive-new = "0.7.0"
|
||||
tuplex = "0.1.2"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"repository": "https://github.com/LemmyNet/lemmy",
|
||||
"author": "Dessalines",
|
||||
"license": "AGPL-3.0",
|
||||
"packageManager": "pnpm@9.15.0",
|
||||
"packageManager": "pnpm@10.2.1+sha512.398035c7bd696d0ba0b10a688ed558285329d27ea994804a52bad9167d8e3a72bcb993f9699585d3ca25779ac64949ef422757a6c31102c12ab932e5cbe5cc92",
|
||||
"scripts": {
|
||||
"lint": "tsc --noEmit && eslint --report-unused-disable-directives && prettier --check 'src/**/*.ts'",
|
||||
"fix": "prettier --write src && eslint --fix src",
|
||||
@@ -22,16 +22,18 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^22.10.7",
|
||||
"@typescript-eslint/eslint-plugin": "^8.21.0",
|
||||
"@typescript-eslint/parser": "^8.21.0",
|
||||
"eslint": "^9.18.0",
|
||||
"@types/joi": "^17.2.3",
|
||||
"@types/node": "^22.13.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.24.0",
|
||||
"@typescript-eslint/parser": "^8.24.0",
|
||||
"eslint": "^9.20.0",
|
||||
"eslint-plugin-prettier": "^5.2.3",
|
||||
"jest": "^29.5.0",
|
||||
"lemmy-js-client": "0.20.0-search-combined.1",
|
||||
"prettier": "^3.4.2",
|
||||
"lemmy-js-client": "0.20.0-show-mod-reports.2",
|
||||
"prettier": "^3.5.0",
|
||||
"ts-jest": "^29.1.0",
|
||||
"tsoa": "^6.6.0",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.21.0"
|
||||
"typescript-eslint": "^8.24.0"
|
||||
}
|
||||
}
|
||||
|
||||
1564
api_tests/pnpm-lock.yaml
generated
1564
api_tests/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -14,20 +14,22 @@ export RUST_LOG="warn,lemmy_server=$LEMMY_LOG_LEVEL,lemmy_federate=$LEMMY_LOG_LE
|
||||
export LEMMY_TEST_FAST_FEDERATION=1 # by default, the persistent federation queue has delays in the scale of 30s-5min
|
||||
|
||||
PICTRS_PATH="api_tests/pict-rs"
|
||||
PICTRS_EXPECTED_HASH="8feb52c0dee1dd0b41caa0e92afd9d5e597fbb4d7174d7bba22a1ba72fa01dbc pict-rs"
|
||||
PICTRS_EXPECTED_HASH="7f7ac2a45ef9b13403ee139b7512135be6b060ff2f6460e0c800e18e1b49d2fd api_tests/pict-rs"
|
||||
|
||||
# Pictrs setup. Download file with hash check and up to 3 retries.
|
||||
if [ ! -f "$PICTRS_PATH" ]; then
|
||||
retry=true
|
||||
count=0
|
||||
while $retry && [ "$count" -lt 3 ]
|
||||
while [ ! -f "$PICTRS_PATH" ] && [ "$count" -lt 3 ]
|
||||
do
|
||||
# This one sometimes goes down
|
||||
# curl "https://git.asonix.dog/asonix/pict-rs/releases/download/v0.5.16/pict-rs-linux-amd64" -o "$PICTRS_PATH"
|
||||
curl "https://codeberg.org/asonix/pict-rs/releases/download/v0.5.5/pict-rs-linux-amd64" -o "$PICTRS_PATH"
|
||||
curl "https://git.asonix.dog/asonix/pict-rs/releases/download/v0.5.17-pre.9/pict-rs-linux-amd64" -o "$PICTRS_PATH"
|
||||
# curl "https://codeberg.org/asonix/pict-rs/releases/download/v0.5.5/pict-rs-linux-amd64" -o "$PICTRS_PATH"
|
||||
PICTRS_HASH=$(sha256sum "$PICTRS_PATH")
|
||||
[[ "$PICTRS_HASH" != "$PICTRS_EXPECTED_HASH" ]] && retry=true || retry=false
|
||||
let count=count+1
|
||||
if [[ "$PICTRS_HASH" != "$PICTRS_EXPECTED_HASH" ]]; then
|
||||
echo "Pictrs binary hash mismatch, was $PICTRS_HASH but expected $PICTRS_EXPECTED_HASH"
|
||||
rm "$PICTRS_PATH"
|
||||
let count=count+1
|
||||
fi
|
||||
done
|
||||
chmod +x "$PICTRS_PATH"
|
||||
fi
|
||||
|
||||
@@ -69,7 +69,7 @@ function assertCommentFederation(
|
||||
expect(commentOne?.comment.ap_id).toBe(commentTwo?.comment.ap_id);
|
||||
expect(commentOne?.comment.content).toBe(commentTwo?.comment.content);
|
||||
expect(commentOne?.creator.name).toBe(commentTwo?.creator.name);
|
||||
expect(commentOne?.community.actor_id).toBe(commentTwo?.community.actor_id);
|
||||
expect(commentOne?.community.ap_id).toBe(commentTwo?.community.ap_id);
|
||||
expect(commentOne?.comment.published).toBe(commentTwo?.comment.published);
|
||||
expect(commentOne?.comment.updated).toBe(commentOne?.comment.updated);
|
||||
expect(commentOne?.comment.deleted).toBe(commentOne?.comment.deleted);
|
||||
@@ -581,7 +581,7 @@ test("A and G subscribe to B (center) A posts, G mentions B, it gets announced t
|
||||
// follow community from beta so that it accepts the mention
|
||||
let betaCommunity = await resolveCommunity(
|
||||
beta,
|
||||
alphaCommunity.community.actor_id,
|
||||
alphaCommunity.community.ap_id,
|
||||
);
|
||||
await followCommunity(beta, true, betaCommunity.community!.community.id);
|
||||
|
||||
|
||||
@@ -44,9 +44,7 @@ function assertCommunityFederation(
|
||||
communityOne?: CommunityView,
|
||||
communityTwo?: CommunityView,
|
||||
) {
|
||||
expect(communityOne?.community.actor_id).toBe(
|
||||
communityTwo?.community.actor_id,
|
||||
);
|
||||
expect(communityOne?.community.ap_id).toBe(communityTwo?.community.ap_id);
|
||||
expect(communityOne?.community.name).toBe(communityTwo?.community.name);
|
||||
expect(communityOne?.community.title).toBe(communityTwo?.community.title);
|
||||
expect(communityOne?.community.description).toBe(
|
||||
@@ -198,7 +196,7 @@ test("Admin actions in remote community are not federated to origin", async () =
|
||||
|
||||
// gamma follows community and posts in it
|
||||
let gammaCommunity = (
|
||||
await resolveCommunity(gamma, communityRes.community.actor_id)
|
||||
await resolveCommunity(gamma, communityRes.community.ap_id)
|
||||
).community;
|
||||
if (!gammaCommunity) {
|
||||
throw "Missing gamma community";
|
||||
@@ -206,7 +204,7 @@ test("Admin actions in remote community are not federated to origin", async () =
|
||||
await followCommunity(gamma, true, gammaCommunity.community.id);
|
||||
gammaCommunity = (
|
||||
await waitUntil(
|
||||
() => resolveCommunity(gamma, communityRes.community.actor_id),
|
||||
() => resolveCommunity(gamma, communityRes.community.ap_id),
|
||||
g => g.community?.subscribed === "Subscribed",
|
||||
)
|
||||
).community;
|
||||
@@ -221,7 +219,7 @@ test("Admin actions in remote community are not federated to origin", async () =
|
||||
|
||||
// admin of beta decides to ban gamma from community
|
||||
let betaCommunity = (
|
||||
await resolveCommunity(beta, communityRes.community.actor_id)
|
||||
await resolveCommunity(beta, communityRes.community.ap_id)
|
||||
).community;
|
||||
if (!betaCommunity) {
|
||||
throw "Missing beta community";
|
||||
@@ -230,7 +228,7 @@ test("Admin actions in remote community are not federated to origin", async () =
|
||||
if (!bannedUserInfo1) {
|
||||
throw "Missing banned user 1";
|
||||
}
|
||||
let bannedUserInfo2 = (await resolvePerson(beta, bannedUserInfo1.actor_id))
|
||||
let bannedUserInfo2 = (await resolvePerson(beta, bannedUserInfo1.ap_id))
|
||||
.person;
|
||||
if (!bannedUserInfo2) {
|
||||
throw "Missing banned user 2";
|
||||
@@ -383,7 +381,7 @@ test("User blocks instance, communities are hidden", async () => {
|
||||
test.skip("Community follower count is federated", async () => {
|
||||
// Follow the beta community from alpha
|
||||
let community = await createCommunity(beta);
|
||||
let communityActorId = community.community_view.community.actor_id;
|
||||
let communityActorId = community.community_view.community.ap_id;
|
||||
let resolved = await resolveCommunity(alpha, communityActorId);
|
||||
if (!resolved.community) {
|
||||
throw "Missing beta community";
|
||||
@@ -441,7 +439,7 @@ test("Dont receive community activities after unsubscribe", async () => {
|
||||
expect(communityRes.community_view.counts.subscribers).toBe(1);
|
||||
|
||||
let betaCommunity = (
|
||||
await resolveCommunity(beta, communityRes.community_view.community.actor_id)
|
||||
await resolveCommunity(beta, communityRes.community_view.community.ap_id)
|
||||
).community;
|
||||
assertCommunityFederation(betaCommunity, communityRes.community_view);
|
||||
|
||||
@@ -503,13 +501,12 @@ test("Fetch community, includes posts", async () => {
|
||||
expect(postRes.post_view.post).toBeDefined();
|
||||
|
||||
let resolvedCommunity = await waitUntil(
|
||||
() =>
|
||||
resolveCommunity(beta, communityRes.community_view.community.actor_id),
|
||||
() => resolveCommunity(beta, communityRes.community_view.community.ap_id),
|
||||
c => c.community?.community.id != undefined,
|
||||
);
|
||||
let betaCommunity = resolvedCommunity.community;
|
||||
expect(betaCommunity?.community.actor_id).toBe(
|
||||
communityRes.community_view.community.actor_id,
|
||||
expect(betaCommunity?.community.ap_id).toBe(
|
||||
communityRes.community_view.community.ap_id,
|
||||
);
|
||||
|
||||
await longDelay();
|
||||
@@ -530,7 +527,7 @@ test("Content in local-only community doesn't federate", async () => {
|
||||
|
||||
// cant resolve the community from another instance
|
||||
await expect(
|
||||
resolveCommunity(beta, communityRes.actor_id),
|
||||
resolveCommunity(beta, communityRes.ap_id),
|
||||
).rejects.toStrictEqual(Error("not_found"));
|
||||
|
||||
// create a post, also cant resolve it
|
||||
@@ -545,7 +542,7 @@ test("Remote mods can edit communities", async () => {
|
||||
|
||||
let betaCommunity = await resolveCommunity(
|
||||
beta,
|
||||
communityRes.community_view.community.actor_id,
|
||||
communityRes.community_view.community.ap_id,
|
||||
);
|
||||
if (!betaCommunity.community) {
|
||||
throw "Missing beta community";
|
||||
@@ -584,7 +581,7 @@ test("Community name with non-ascii chars", async () => {
|
||||
|
||||
let betaCommunity1 = await resolveCommunity(
|
||||
beta,
|
||||
communityRes.community_view.community.actor_id,
|
||||
communityRes.community_view.community.ap_id,
|
||||
);
|
||||
expect(betaCommunity1.community!.community.name).toBe(name);
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ test("Upload image and delete it", async () => {
|
||||
expect(listAllMediaRes.images.length).toBe(previousThumbnails);
|
||||
|
||||
// Make sure the uploader is correct
|
||||
expect(listMediaRes.images[0].person.actor_id).toBe(
|
||||
expect(listMediaRes.images[0].person.ap_id).toBe(
|
||||
`http://lemmy-alpha:8541/u/lemmy_alpha`,
|
||||
);
|
||||
|
||||
@@ -268,7 +268,7 @@ test("No image proxying if setting is disabled", async () => {
|
||||
let community = await createCommunity(alpha);
|
||||
let betaCommunity = await resolveCommunity(
|
||||
beta,
|
||||
community.community_view.community.actor_id,
|
||||
community.community_view.community.ap_id,
|
||||
);
|
||||
await followCommunity(beta, true, betaCommunity.community!.community.id);
|
||||
|
||||
|
||||
@@ -39,14 +39,12 @@ import {
|
||||
listReports,
|
||||
getMyUser,
|
||||
listInbox,
|
||||
allowInstance,
|
||||
} from "./shared";
|
||||
import { PostView } from "lemmy-js-client/dist/types/PostView";
|
||||
import { AdminBlockInstanceParams } from "lemmy-js-client/dist/types/AdminBlockInstanceParams";
|
||||
import {
|
||||
AddModToCommunity,
|
||||
EditSite,
|
||||
LemmyHttp,
|
||||
PersonPostMentionView,
|
||||
PostReport,
|
||||
PostReportView,
|
||||
@@ -98,7 +96,7 @@ async function assertPostFederation(
|
||||
expect(postOne?.post.embed_description).toBe(postTwo?.post.embed_description);
|
||||
expect(postOne?.post.embed_video_url).toBe(postTwo?.post.embed_video_url);
|
||||
expect(postOne?.post.published).toBe(postTwo?.post.published);
|
||||
expect(postOne?.community.actor_id).toBe(postTwo?.community.actor_id);
|
||||
expect(postOne?.community.ap_id).toBe(postTwo?.community.ap_id);
|
||||
expect(postOne?.post.locked).toBe(postTwo?.post.locked);
|
||||
expect(postOne?.post.removed).toBe(postTwo?.post.removed);
|
||||
expect(postOne?.post.deleted).toBe(postTwo?.post.deleted);
|
||||
@@ -262,7 +260,7 @@ test("Collection of featured posts gets federated", async () => {
|
||||
// fetch the community, ensure that post is also fetched and marked as featured
|
||||
let betaCommunity = await resolveCommunity(
|
||||
beta,
|
||||
community.community_view.community.actor_id,
|
||||
community.community_view.community.ap_id,
|
||||
);
|
||||
expect(betaCommunity).toBeDefined();
|
||||
|
||||
@@ -368,7 +366,7 @@ test("Remove a post from admin and community on different instance", async () =>
|
||||
}
|
||||
|
||||
let gammaCommunity = (
|
||||
await resolveCommunity(gamma, betaCommunity.community.actor_id)
|
||||
await resolveCommunity(gamma, betaCommunity.community.ap_id)
|
||||
).community?.community;
|
||||
if (!gammaCommunity) {
|
||||
throw "Missing gamma community";
|
||||
@@ -407,7 +405,7 @@ test("Remove a post from admin and community on same instance", async () => {
|
||||
await followBeta(alpha);
|
||||
let gammaCommunity = await resolveCommunity(
|
||||
gamma,
|
||||
betaCommunity.community.actor_id,
|
||||
betaCommunity.community.ap_id,
|
||||
);
|
||||
let postRes = await createPost(gamma, gammaCommunity.community!.community.id);
|
||||
expect(postRes.post_view.post).toBeDefined();
|
||||
@@ -469,7 +467,7 @@ test("Enforce site ban federation for local user", async () => {
|
||||
// create a test user
|
||||
let alphaUserHttp = await registerUser(alpha, alphaUrl);
|
||||
let alphaUserPerson = (await getMyUser(alphaUserHttp)).local_user_view.person;
|
||||
let alphaUserActorId = alphaUserPerson?.actor_id;
|
||||
let alphaUserActorId = alphaUserPerson?.ap_id;
|
||||
if (!alphaUserActorId) {
|
||||
throw "Missing alpha user actor id";
|
||||
}
|
||||
@@ -549,7 +547,7 @@ test("Enforce site ban federation for federated user", async () => {
|
||||
// create a test user
|
||||
let alphaUserHttp = await registerUser(alpha, alphaUrl);
|
||||
let alphaUserPerson = (await getMyUser(alphaUserHttp)).local_user_view.person;
|
||||
let alphaUserActorId = alphaUserPerson?.actor_id;
|
||||
let alphaUserActorId = alphaUserPerson?.ap_id;
|
||||
if (!alphaUserActorId) {
|
||||
throw "Missing alpha user actor id";
|
||||
}
|
||||
@@ -733,11 +731,12 @@ test("Report a post", async () => {
|
||||
expect(betaReport.reason).toBe(gammaReport.reason);
|
||||
await unfollowRemotes(alpha);
|
||||
|
||||
// Report was federated to poster's instance
|
||||
// Report was federated to poster's instance. Alpha is not a community mod and doesnt see
|
||||
// the report by default, so we need to pass show_mod_reports = true.
|
||||
let alphaReport = (
|
||||
(await waitUntil(
|
||||
() =>
|
||||
listReports(alpha).then(p =>
|
||||
listReports(alpha, true).then(p =>
|
||||
p.reports.find(r => {
|
||||
return checkPostReportName(r, gammaReport);
|
||||
}),
|
||||
|
||||
@@ -47,7 +47,7 @@ test("Follow a private community", async () => {
|
||||
// follow as new user
|
||||
const user = await registerUser(beta, betaUrl);
|
||||
const betaCommunity = (
|
||||
await resolveCommunity(user, community.community_view.community.actor_id)
|
||||
await resolveCommunity(user, community.community_view.community.ap_id)
|
||||
).community;
|
||||
expect(betaCommunity).toBeDefined();
|
||||
expect(betaCommunity?.community.visibility).toBe("Private");
|
||||
@@ -134,7 +134,7 @@ test("Only followers can view and interact with private community content", asyn
|
||||
// user is not following the community and cannot view nor create posts
|
||||
const user = await registerUser(beta, betaUrl);
|
||||
const betaCommunity = (
|
||||
await resolveCommunity(user, community.community_view.community.actor_id)
|
||||
await resolveCommunity(user, community.community_view.community.ap_id)
|
||||
).community!.community;
|
||||
await expect(resolvePost(user, post0.post_view.post)).rejects.toStrictEqual(
|
||||
Error("not_found"),
|
||||
@@ -179,7 +179,7 @@ test("Reject follower", async () => {
|
||||
// user is not following the community and cannot view nor create posts
|
||||
const user = await registerUser(beta, betaUrl);
|
||||
const betaCommunity1 = (
|
||||
await resolveCommunity(user, community.community_view.community.actor_id)
|
||||
await resolveCommunity(user, community.community_view.community.ap_id)
|
||||
).community!.community;
|
||||
|
||||
// follow the community and reject
|
||||
@@ -216,7 +216,7 @@ test("Follow a private community and receive activities", async () => {
|
||||
|
||||
// follow with users from beta and gamma
|
||||
const betaCommunity = (
|
||||
await resolveCommunity(beta, community.community_view.community.actor_id)
|
||||
await resolveCommunity(beta, community.community_view.community.ap_id)
|
||||
).community;
|
||||
expect(betaCommunity).toBeDefined();
|
||||
const betaCommunityId = betaCommunity!.community.id;
|
||||
@@ -228,7 +228,7 @@ test("Follow a private community and receive activities", async () => {
|
||||
await approveFollower(alpha, alphaCommunityId);
|
||||
|
||||
const gammaCommunityId = (
|
||||
await resolveCommunity(gamma, community.community_view.community.actor_id)
|
||||
await resolveCommunity(gamma, community.community_view.community.ap_id)
|
||||
).community!.community.id;
|
||||
const follow_form_gamma: FollowCommunity = {
|
||||
community_id: gammaCommunityId,
|
||||
@@ -281,7 +281,7 @@ test("Fetch remote content in private community", async () => {
|
||||
const alphaCommunityId = community.community_view.community.id;
|
||||
|
||||
const betaCommunityId = (
|
||||
await resolveCommunity(beta, community.community_view.community.actor_id)
|
||||
await resolveCommunity(beta, community.community_view.community.ap_id)
|
||||
).community!.community.id;
|
||||
const follow_form_beta: FollowCommunity = {
|
||||
community_id: betaCommunityId,
|
||||
@@ -312,7 +312,7 @@ test("Fetch remote content in private community", async () => {
|
||||
|
||||
// create gamma user
|
||||
const gammaCommunityId = (
|
||||
await resolveCommunity(gamma, community.community_view.community.actor_id)
|
||||
await resolveCommunity(gamma, community.community_view.community.ap_id)
|
||||
).community!.community.id;
|
||||
const follow_form: FollowCommunity = {
|
||||
community_id: gammaCommunityId,
|
||||
|
||||
@@ -83,6 +83,7 @@ import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
|
||||
import { GetPersonDetailsResponse } from "lemmy-js-client/dist/types/GetPersonDetailsResponse";
|
||||
import { GetPersonDetails } from "lemmy-js-client/dist/types/GetPersonDetails";
|
||||
import { ListingType } from "lemmy-js-client/dist/types/ListingType";
|
||||
import { GetCommunityPendingFollowsCountI } from "lemmy-js-client/dist/other_types";
|
||||
|
||||
export const fetchFunction = fetch;
|
||||
export const imageFetchLimit = 50;
|
||||
@@ -800,8 +801,9 @@ export async function reportPost(
|
||||
|
||||
export async function listReports(
|
||||
api: LemmyHttp,
|
||||
show_community_rule_violations: boolean = false,
|
||||
): Promise<ListReportsResponse> {
|
||||
let form: ListReports = {};
|
||||
let form: ListReports = { show_community_rule_violations };
|
||||
return api.listReports(form);
|
||||
}
|
||||
|
||||
@@ -882,7 +884,8 @@ export function getCommunityPendingFollowsCount(
|
||||
api: LemmyHttp,
|
||||
community_id: CommunityId,
|
||||
): Promise<GetCommunityPendingFollowsCountResponse> {
|
||||
return api.getCommunityPendingFollowsCount(community_id);
|
||||
let form: GetCommunityPendingFollowsCountI = { community_id };
|
||||
return api.getCommunityPendingFollowsCount(form);
|
||||
}
|
||||
|
||||
export function approveCommunityPendingFollow(
|
||||
|
||||
@@ -41,7 +41,7 @@ function assertUserFederation(userOne?: PersonView, userTwo?: PersonView) {
|
||||
expect(userOne?.person.name).toBe(userTwo?.person.name);
|
||||
expect(userOne?.person.display_name).toBe(userTwo?.person.display_name);
|
||||
expect(userOne?.person.bio).toBe(userTwo?.person.bio);
|
||||
expect(userOne?.person.actor_id).toBe(userTwo?.person.actor_id);
|
||||
expect(userOne?.person.ap_id).toBe(userTwo?.person.ap_id);
|
||||
expect(userOne?.person.avatar).toBe(userTwo?.person.avatar);
|
||||
expect(userOne?.person.banner).toBe(userTwo?.person.banner);
|
||||
expect(userOne?.person.published).toBe(userTwo?.person.published);
|
||||
|
||||
@@ -14,6 +14,7 @@ pub mod login;
|
||||
pub mod logout;
|
||||
pub mod notifications;
|
||||
pub mod report_count;
|
||||
pub mod resend_verification_email;
|
||||
pub mod reset_password;
|
||||
pub mod save_settings;
|
||||
pub mod update_totp;
|
||||
|
||||
30
crates/api/src/local_user/resend_verification_email.rs
Normal file
30
crates/api/src/local_user/resend_verification_email.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use actix_web::web::{Data, Json};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
person::ResendVerificationEmail,
|
||||
utils::send_verification_email_if_required,
|
||||
SuccessResponse,
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
pub async fn resend_verification_email(
|
||||
data: Json<ResendVerificationEmail>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
let email = data.email.to_string();
|
||||
|
||||
// Fetch that email
|
||||
let local_user_view = LocalUserView::find_by_email(&mut context.pool(), &email).await?;
|
||||
|
||||
send_verification_email_if_required(
|
||||
&context,
|
||||
&site_view.local_site,
|
||||
&local_user_view.local_user,
|
||||
&local_user_view.person,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
}
|
||||
@@ -19,7 +19,7 @@ use lemmy_db_schema::{
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::diesel_string_update,
|
||||
utils::{diesel_opt_number_update, diesel_string_update},
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_utils::{
|
||||
@@ -54,7 +54,9 @@ pub async fn save_user_settings(
|
||||
if previous_email.deref() != email {
|
||||
LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
|
||||
send_verification_email(
|
||||
&local_user_view,
|
||||
&site_view.local_site,
|
||||
&local_user_view.local_user,
|
||||
&local_user_view.person,
|
||||
email,
|
||||
&mut context.pool(),
|
||||
context.settings(),
|
||||
@@ -90,6 +92,8 @@ pub async fn save_user_settings(
|
||||
let person_id = local_user_view.person.id;
|
||||
let default_listing_type = data.default_listing_type;
|
||||
let default_post_sort_type = data.default_post_sort_type;
|
||||
let default_post_time_range_seconds =
|
||||
diesel_opt_number_update(data.default_post_time_range_seconds);
|
||||
let default_comment_sort_type = data.default_comment_sort_type;
|
||||
|
||||
let person_form = PersonUpdateForm {
|
||||
@@ -119,6 +123,7 @@ pub async fn save_user_settings(
|
||||
blur_nsfw: data.blur_nsfw,
|
||||
show_bot_accounts: data.show_bot_accounts,
|
||||
default_post_sort_type,
|
||||
default_post_time_range_seconds,
|
||||
default_comment_sort_type,
|
||||
default_listing_type,
|
||||
theme: data.theme.clone(),
|
||||
|
||||
@@ -56,6 +56,7 @@ pub async fn create_comment_report(
|
||||
comment_id,
|
||||
original_comment_text: comment_view.comment.content,
|
||||
reason,
|
||||
violates_instance_rules: data.violates_instance_rules.unwrap_or_default(),
|
||||
};
|
||||
|
||||
let report = CommentReport::report(&mut context.pool(), &report_form)
|
||||
|
||||
@@ -52,6 +52,7 @@ pub async fn create_post_report(
|
||||
original_post_url: post_view.post.url,
|
||||
original_post_body: post_view.post.body,
|
||||
reason,
|
||||
violates_instance_rules: data.violates_instance_rules.unwrap_or_default(),
|
||||
};
|
||||
|
||||
let report = PostReport::report(&mut context.pool(), &report_form)
|
||||
|
||||
@@ -14,7 +14,12 @@ pub async fn list_reports(
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<ListReportsResponse>> {
|
||||
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?;
|
||||
let my_reports_only = data.my_reports_only;
|
||||
|
||||
// Only check mod or admin status when not viewing my reports
|
||||
if !my_reports_only.unwrap_or_default() {
|
||||
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?;
|
||||
}
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
@@ -31,6 +36,8 @@ pub async fn list_reports(
|
||||
unresolved_only: data.unresolved_only,
|
||||
page_after,
|
||||
page_back,
|
||||
show_community_rule_violations: data.show_community_rule_violations,
|
||||
my_reports_only,
|
||||
}
|
||||
.list(&mut context.pool(), &local_user_view)
|
||||
.await?;
|
||||
|
||||
@@ -18,10 +18,11 @@ pub async fn get_mod_log(
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let type_ = data.type_;
|
||||
let listing_type = data.listing_type;
|
||||
let community_id = data.community_id;
|
||||
|
||||
let is_mod_or_admin = if let Some(local_user_view) = local_user_view {
|
||||
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool())
|
||||
let is_mod_or_admin = if let Some(local_user_view) = &local_user_view {
|
||||
check_community_mod_of_any_or_admin_action(local_user_view, &mut context.pool())
|
||||
.await
|
||||
.is_ok()
|
||||
} else {
|
||||
@@ -37,6 +38,7 @@ pub async fn get_mod_log(
|
||||
let other_person_id = data.other_person_id;
|
||||
let post_id = data.post_id;
|
||||
let comment_id = data.comment_id;
|
||||
let local_user = local_user_view.as_ref().map(|u| &u.local_user);
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
@@ -48,9 +50,11 @@ pub async fn get_mod_log(
|
||||
|
||||
let modlog = ModlogCombinedQuery {
|
||||
type_,
|
||||
listing_type,
|
||||
community_id,
|
||||
mod_person_id,
|
||||
other_person_id,
|
||||
local_user,
|
||||
post_id,
|
||||
comment_id,
|
||||
hide_modlog_names: Some(hide_modlog_names),
|
||||
|
||||
@@ -19,7 +19,6 @@ workspace = true
|
||||
[features]
|
||||
full = [
|
||||
"tracing",
|
||||
"rosetta-i18n",
|
||||
"lemmy_db_views/full",
|
||||
"lemmy_utils/full",
|
||||
"activitypub_federation",
|
||||
@@ -51,7 +50,6 @@ chrono = { workspace = true }
|
||||
tracing = { workspace = true, optional = true }
|
||||
reqwest-middleware = { workspace = true, optional = true }
|
||||
regex = { workspace = true }
|
||||
rosetta-i18n = { workspace = true, optional = true }
|
||||
futures = { workspace = true, optional = true }
|
||||
uuid = { workspace = true, optional = true }
|
||||
tokio = { workspace = true, optional = true }
|
||||
@@ -64,12 +62,12 @@ actix-web = { workspace = true, optional = true }
|
||||
urlencoding = { workspace = true }
|
||||
mime = { version = "0.3.17", optional = true }
|
||||
mime_guess = "2.0.5"
|
||||
infer = "0.16.0"
|
||||
infer = "0.19.0"
|
||||
webpage = { version = "2.0", default-features = false, optional = true, features = [
|
||||
"serde",
|
||||
] }
|
||||
encoding_rs = { version = "0.8.35", optional = true }
|
||||
jsonwebtoken = { version = "9.3.0", optional = true }
|
||||
jsonwebtoken = { version = "9.3.1", optional = true }
|
||||
actix-web-httpauth = { version = "0.8.2", optional = true }
|
||||
webmention = { version = "0.6.0", optional = true }
|
||||
|
||||
|
||||
@@ -3,12 +3,7 @@ use crate::{
|
||||
community::CommunityResponse,
|
||||
context::LemmyContext,
|
||||
post::PostResponse,
|
||||
utils::{
|
||||
check_person_instance_community_block,
|
||||
get_interface_language,
|
||||
is_mod_or_admin,
|
||||
send_email_to_user,
|
||||
},
|
||||
utils::{check_person_instance_community_block, is_mod_or_admin, send_email_to_user},
|
||||
};
|
||||
use actix_web::web::Json;
|
||||
use lemmy_db_schema::{
|
||||
@@ -141,14 +136,6 @@ pub async fn send_local_notifs(
|
||||
};
|
||||
|
||||
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
||||
let comment_link = |comment: &Comment| {
|
||||
format!(
|
||||
"{}/post/{}/{}",
|
||||
context.settings().get_protocol_and_hostname(),
|
||||
post.id,
|
||||
comment.id
|
||||
)
|
||||
};
|
||||
|
||||
// Send the local mentions
|
||||
for mention in mentions
|
||||
@@ -177,7 +164,10 @@ pub async fn send_local_notifs(
|
||||
PersonCommentMention::create(&mut context.pool(), &person_comment_mention_form)
|
||||
.await
|
||||
.ok();
|
||||
(comment_link(comment), comment.content.clone())
|
||||
(
|
||||
comment.local_url(context.settings())?,
|
||||
comment.content.clone(),
|
||||
)
|
||||
} else {
|
||||
let person_post_mention_form = PersonPostMentionInsertForm {
|
||||
recipient_id: mention_user_view.person.id,
|
||||
@@ -189,17 +179,15 @@ pub async fn send_local_notifs(
|
||||
PersonPostMention::create(&mut context.pool(), &person_post_mention_form)
|
||||
.await
|
||||
.ok();
|
||||
let post_link = format!(
|
||||
"{}/post/{}",
|
||||
context.settings().get_protocol_and_hostname(),
|
||||
post.id,
|
||||
);
|
||||
(post_link, post.body.clone().unwrap_or_default())
|
||||
(
|
||||
post.local_url(context.settings())?,
|
||||
post.body.clone().unwrap_or_default(),
|
||||
)
|
||||
};
|
||||
|
||||
// Send an email to those local users that have notifications on
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&mention_user_view);
|
||||
let lang = &mention_user_view.local_user.interface_i18n_language();
|
||||
let content = markdown_to_html(&comment_content_or_post_body);
|
||||
send_email_to_user(
|
||||
&mention_user_view,
|
||||
@@ -252,13 +240,13 @@ pub async fn send_local_notifs(
|
||||
.ok();
|
||||
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&parent_user_view);
|
||||
let lang = &parent_user_view.local_user.interface_i18n_language();
|
||||
let content = markdown_to_html(&comment.content);
|
||||
send_email_to_user(
|
||||
&parent_user_view,
|
||||
&lang.notification_comment_reply_subject(&person.name),
|
||||
&lang.notification_comment_reply_body(
|
||||
comment_link(comment),
|
||||
comment.local_url(context.settings())?,
|
||||
&content,
|
||||
&inbox_link,
|
||||
&parent_comment.content,
|
||||
@@ -305,13 +293,13 @@ pub async fn send_local_notifs(
|
||||
.ok();
|
||||
|
||||
if do_send_email {
|
||||
let lang = get_interface_language(&parent_user_view);
|
||||
let lang = &parent_user_view.local_user.interface_i18n_language();
|
||||
let content = markdown_to_html(&comment.content);
|
||||
send_email_to_user(
|
||||
&parent_user_view,
|
||||
&lang.notification_post_reply_subject(&person.name),
|
||||
&lang.notification_post_reply_body(
|
||||
comment_link(comment),
|
||||
comment.local_url(context.settings())?,
|
||||
&content,
|
||||
&inbox_link,
|
||||
&post.name,
|
||||
|
||||
@@ -117,6 +117,10 @@ pub struct GetComments {
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub sort: Option<CommentSortType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
/// Filter to within a given time range, in seconds.
|
||||
/// IE 60 would give results for the past minute.
|
||||
pub time_range_seconds: Option<i32>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub max_depth: Option<i32>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page: Option<i64>,
|
||||
|
||||
@@ -97,6 +97,10 @@ pub struct ListCommunities {
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub sort: Option<CommunitySortType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
/// Filter to within a given time range, in seconds.
|
||||
/// IE 60 would give results for the past minute.
|
||||
pub time_range_seconds: Option<i32>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub show_nsfw: Option<bool>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page: Option<i64>,
|
||||
|
||||
@@ -120,6 +120,9 @@ pub struct SaveUserSettings {
|
||||
/// The default post sort, usually "active"
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_post_sort_type: Option<PostSortType>,
|
||||
/// A default time range limit to apply to post sorts, in seconds. 0 means none.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_post_time_range_seconds: Option<i32>,
|
||||
/// The default comment sort, usually "hot"
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_comment_sort_type: Option<CommentSortType>,
|
||||
@@ -532,3 +535,11 @@ pub struct ListMediaResponse {
|
||||
pub struct ListLoginsResponse {
|
||||
pub logins: Vec<LoginToken>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Make a request to resend your verification email.
|
||||
pub struct ResendVerificationEmail {
|
||||
pub email: SensitiveString,
|
||||
}
|
||||
|
||||
@@ -4,13 +4,7 @@ use lemmy_db_schema::{
|
||||
PostFeatureType,
|
||||
PostSortType,
|
||||
};
|
||||
use lemmy_db_views::structs::{
|
||||
CommunityModeratorView,
|
||||
CommunityView,
|
||||
PaginationCursor,
|
||||
PostView,
|
||||
VoteView,
|
||||
};
|
||||
use lemmy_db_views::structs::{CommunityView, PaginationCursor, PostView, VoteView};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
#[cfg(feature = "full")]
|
||||
@@ -77,7 +71,6 @@ pub struct GetPost {
|
||||
pub struct GetPostResponse {
|
||||
pub post_view: PostView,
|
||||
pub community_view: CommunityView,
|
||||
pub moderators: Vec<CommunityModeratorView>,
|
||||
/// A list of cross-posts, or other times / communities this link has been posted to.
|
||||
pub cross_posts: Vec<PostView>,
|
||||
}
|
||||
@@ -92,6 +85,11 @@ pub struct GetPosts {
|
||||
pub type_: Option<ListingType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub sort: Option<PostSortType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
/// Filter to within a given time range, in seconds.
|
||||
/// IE 60 would give results for the past minute.
|
||||
/// Use Zero to override the local_site and local_user time_range.
|
||||
pub time_range_seconds: Option<i32>,
|
||||
/// DEPRECATED, use page_cursor
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page: Option<i64>,
|
||||
|
||||
@@ -30,6 +30,12 @@ pub struct ListReports {
|
||||
pub page_cursor: Option<ReportCombinedPaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
/// Only for admins: also show reports with `violates_instance_rules=false`
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub show_community_rule_violations: Option<bool>,
|
||||
/// If true, view all your created reports. Works for non-admins/mods also.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub my_reports_only: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
||||
@@ -11,6 +11,8 @@ use ts_rs::TS;
|
||||
pub struct CreateCommentReport {
|
||||
pub comment_id: CommentId,
|
||||
pub reason: String,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub violates_instance_rules: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
||||
@@ -11,6 +11,8 @@ use ts_rs::TS;
|
||||
pub struct CreatePostReport {
|
||||
pub post_id: PostId,
|
||||
pub reason: String,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub violates_instance_rules: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
||||
@@ -10,7 +10,7 @@ use chrono::{DateTime, Utc};
|
||||
use encoding_rs::{Encoding, UTF_8};
|
||||
use futures::StreamExt;
|
||||
use lemmy_db_schema::source::{
|
||||
images::{ImageDetailsForm, LocalImage, LocalImageForm},
|
||||
images::{ImageDetailsInsertForm, LocalImage, LocalImageForm},
|
||||
post::{Post, PostUpdateForm},
|
||||
site::Site,
|
||||
};
|
||||
@@ -339,17 +339,19 @@ pub struct PictrsFileDetails {
|
||||
pub height: u16,
|
||||
pub content_type: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub blurhash: Option<String>,
|
||||
}
|
||||
|
||||
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 {
|
||||
pub fn build_image_details_form(&self, thumbnail_url: &Url) -> ImageDetailsInsertForm {
|
||||
ImageDetailsInsertForm {
|
||||
link: thumbnail_url.clone().into(),
|
||||
width: self.width.into(),
|
||||
height: self.height.into(),
|
||||
content_type: self.content_type.clone(),
|
||||
blurhash: self.blurhash.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,10 @@ pub struct Search {
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub sort: Option<SearchSortType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
/// Filter to within a given time range, in seconds.
|
||||
/// IE 60 would give results for the past minute.
|
||||
pub time_range_seconds: Option<i32>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub listing_type: Option<ListingType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub title_only: Option<bool>,
|
||||
@@ -124,16 +128,26 @@ pub struct ResolveObjectResponse {
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Fetches the modlog.
|
||||
pub struct GetModlog {
|
||||
/// Filter by the moderator.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub mod_person_id: Option<PersonId>,
|
||||
/// Filter by the community.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub community_id: Option<CommunityId>,
|
||||
/// Filter by the modlog action type.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub type_: Option<ModlogActionType>,
|
||||
/// Filter by listing type. When not using All, it will remove the non-community modlog entries,
|
||||
/// such as site bans, instance blocks, adding an admin, etc.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub listing_type: Option<ListingType>,
|
||||
/// Filter by the other / modded person.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub other_person_id: Option<PersonId>,
|
||||
/// Filter by post. Will include comments of that post.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub post_id: Option<PostId>,
|
||||
/// Filter by comment.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub comment_id: Option<CommentId>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
@@ -179,6 +193,8 @@ pub struct CreateSite {
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_post_sort_type: Option<PostSortType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_post_time_range_seconds: Option<i32>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_comment_sort_type: Option<CommentSortType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub legal_information: Option<String>,
|
||||
@@ -278,6 +294,9 @@ pub struct EditSite {
|
||||
/// The default post sort, usually "active"
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_post_sort_type: Option<PostSortType>,
|
||||
/// A default time range limit to apply to post sorts, in seconds. 0 means none.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_post_time_range_seconds: Option<i32>,
|
||||
/// The default comment sort, usually "hot"
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub default_comment_sort_type: Option<CommentSortType>,
|
||||
|
||||
@@ -26,6 +26,7 @@ use lemmy_db_schema::{
|
||||
local_site::LocalSite,
|
||||
local_site_rate_limit::LocalSiteRateLimit,
|
||||
local_site_url_blocklist::LocalSiteUrlBlocklist,
|
||||
local_user::LocalUser,
|
||||
mod_log::moderator::{
|
||||
ModRemoveComment,
|
||||
ModRemoveCommentForm,
|
||||
@@ -37,6 +38,7 @@ use lemmy_db_schema::{
|
||||
person::{Person, PersonUpdateForm},
|
||||
person_block::PersonBlock,
|
||||
post::{Post, PostLike},
|
||||
private_message::PrivateMessage,
|
||||
registration_application::RegistrationApplication,
|
||||
site::Site,
|
||||
},
|
||||
@@ -59,7 +61,7 @@ use lemmy_db_views::{
|
||||
},
|
||||
};
|
||||
use lemmy_utils::{
|
||||
email::{send_email, translations::Lang},
|
||||
email::send_email,
|
||||
error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
|
||||
rate_limit::{ActionType, BucketConfig},
|
||||
settings::{
|
||||
@@ -77,7 +79,6 @@ use lemmy_utils::{
|
||||
};
|
||||
use moka::future::Cache;
|
||||
use regex::{escape, Regex, RegexSet};
|
||||
use rosetta_i18n::{Language, LanguageId};
|
||||
use std::sync::LazyLock;
|
||||
use tracing::{warn, Instrument};
|
||||
use url::{ParseError, Url};
|
||||
@@ -445,7 +446,7 @@ pub async fn send_password_reset_email(
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
||||
let lang = get_interface_language(user);
|
||||
let lang = &user.local_user.interface_i18n_language();
|
||||
let subject = &lang.password_reset_subject(&user.person.name);
|
||||
let protocol_and_hostname = settings.get_protocol_and_hostname();
|
||||
let reset_link = format!("{}/password_change/{}", protocol_and_hostname, &token);
|
||||
@@ -461,13 +462,15 @@ pub async fn send_password_reset_email(
|
||||
|
||||
/// Send a verification email
|
||||
pub async fn send_verification_email(
|
||||
user: &LocalUserView,
|
||||
local_site: &LocalSite,
|
||||
local_user: &LocalUser,
|
||||
person: &Person,
|
||||
new_email: &str,
|
||||
pool: &mut DbPool<'_>,
|
||||
settings: &Settings,
|
||||
) -> LemmyResult<()> {
|
||||
let form = EmailVerificationForm {
|
||||
local_user_id: user.local_user.id,
|
||||
local_user_id: local_user.id,
|
||||
email: new_email.to_string(),
|
||||
verification_token: uuid::Uuid::new_v4().to_string(),
|
||||
};
|
||||
@@ -478,29 +481,45 @@ pub async fn send_verification_email(
|
||||
);
|
||||
EmailVerification::create(pool, &form).await?;
|
||||
|
||||
let lang = get_interface_language(user);
|
||||
let lang = local_user.interface_i18n_language();
|
||||
let subject = lang.verify_email_subject(&settings.hostname);
|
||||
let body = lang.verify_email_body(&settings.hostname, &user.person.name, verify_link);
|
||||
send_email(&subject, new_email, &user.person.name, &body, settings).await?;
|
||||
|
||||
Ok(())
|
||||
// If an application is required, use a translation that includes that warning.
|
||||
let body = if local_site.registration_mode == RegistrationMode::RequireApplication {
|
||||
lang.verify_email_body_with_application(&settings.hostname, &person.name, verify_link)
|
||||
} else {
|
||||
lang.verify_email_body(&settings.hostname, &person.name, verify_link)
|
||||
};
|
||||
|
||||
send_email(&subject, new_email, &person.name, &body, settings).await
|
||||
}
|
||||
|
||||
pub fn get_interface_language(user: &LocalUserView) -> Lang {
|
||||
lang_str_to_lang(&user.local_user.interface_language)
|
||||
}
|
||||
/// Returns true if email was sent.
|
||||
pub async fn send_verification_email_if_required(
|
||||
context: &LemmyContext,
|
||||
local_site: &LocalSite,
|
||||
local_user: &LocalUser,
|
||||
person: &Person,
|
||||
) -> LemmyResult<bool> {
|
||||
let email = &local_user
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
||||
|
||||
pub fn get_interface_language_from_settings(user: &LocalUserView) -> Lang {
|
||||
lang_str_to_lang(&user.local_user.interface_language)
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
fn lang_str_to_lang(lang: &str) -> Lang {
|
||||
let lang_id = LanguageId::new(lang);
|
||||
Lang::from_language_id(&lang_id).unwrap_or_else(|| {
|
||||
let en = LanguageId::new("en");
|
||||
Lang::from_language_id(&en).expect("default language")
|
||||
})
|
||||
if !local_user.admin && local_site.require_email_verification && !local_user.email_verified {
|
||||
send_verification_email(
|
||||
local_site,
|
||||
local_user,
|
||||
person,
|
||||
email,
|
||||
&mut context.pool(),
|
||||
context.settings(),
|
||||
)
|
||||
.await?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_site_rate_limit_to_rate_limit_config(
|
||||
@@ -567,8 +586,8 @@ pub async fn send_application_approved_email(
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
||||
let lang = get_interface_language(user);
|
||||
let subject = lang.registration_approved_subject(&user.person.actor_id);
|
||||
let lang = &user.local_user.interface_i18n_language();
|
||||
let subject = lang.registration_approved_subject(&user.person.ap_id);
|
||||
let body = lang.registration_approved_body(&settings.hostname);
|
||||
send_email(&subject, email, &user.person.name, &body, settings).await
|
||||
}
|
||||
@@ -593,7 +612,7 @@ pub async fn send_new_applicant_email_to_admins(
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
||||
let lang = get_interface_language_from_settings(admin);
|
||||
let lang = &admin.local_user.interface_i18n_language();
|
||||
let subject = lang.new_application_subject(&settings.hostname, applicant_username);
|
||||
let body = lang.new_application_body(applications_link);
|
||||
send_email(&subject, email, &admin.person.name, &body, settings).await?;
|
||||
@@ -615,7 +634,7 @@ pub async fn send_new_report_email_to_admins(
|
||||
|
||||
for admin in &admins {
|
||||
if let Some(email) = &admin.local_user.email {
|
||||
let lang = get_interface_language_from_settings(admin);
|
||||
let lang = &admin.local_user.interface_i18n_language();
|
||||
let subject =
|
||||
lang.new_report_subject(&settings.hostname, reported_username, reporter_username);
|
||||
let body = lang.new_report_body(reports_link);
|
||||
@@ -633,14 +652,14 @@ pub fn check_private_instance_and_federation_enabled(local_site: &LocalSite) ->
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the site for an actor_id.
|
||||
/// Read the site for an ap_id.
|
||||
///
|
||||
/// Used for GetCommunityResponse and GetPersonDetails
|
||||
pub async fn read_site_for_actor(
|
||||
actor_id: DbUrl,
|
||||
ap_id: DbUrl,
|
||||
context: &LemmyContext,
|
||||
) -> LemmyResult<Option<Site>> {
|
||||
let site_id = Site::instance_actor_id_from_url(actor_id.clone().into());
|
||||
let site_id = Site::instance_ap_id_from_url(ap_id.clone().into());
|
||||
let site = Site::read_from_apub_id(&mut context.pool(), &site_id.into()).await?;
|
||||
Ok(site)
|
||||
}
|
||||
@@ -807,6 +826,9 @@ pub async fn remove_or_restore_user_data(
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Private messages
|
||||
PrivateMessage::update_removed_for_creator(pool, banned_person_id, removed).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -954,33 +976,8 @@ pub async fn purge_user_account(person_id: PersonId, context: &LemmyContext) ->
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub enum EndpointType {
|
||||
Community,
|
||||
Person,
|
||||
Post,
|
||||
Comment,
|
||||
PrivateMessage,
|
||||
}
|
||||
|
||||
/// Generates an apub endpoint for a given domain, IE xyz.tld
|
||||
pub fn generate_local_apub_endpoint(
|
||||
endpoint_type: EndpointType,
|
||||
name: &str,
|
||||
domain: &str,
|
||||
) -> Result<DbUrl, ParseError> {
|
||||
let point = match endpoint_type {
|
||||
EndpointType::Community => "c",
|
||||
EndpointType::Person => "u",
|
||||
EndpointType::Post => "post",
|
||||
EndpointType::Comment => "comment",
|
||||
EndpointType::PrivateMessage => "private_message",
|
||||
};
|
||||
|
||||
Ok(Url::parse(&format!("{domain}/{point}/{name}"))?.into())
|
||||
}
|
||||
|
||||
pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
||||
Ok(Url::parse(&format!("{actor_id}/followers"))?.into())
|
||||
pub fn generate_followers_url(ap_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
||||
Ok(Url::parse(&format!("{ap_id}/followers"))?.into())
|
||||
}
|
||||
|
||||
pub fn generate_inbox_url() -> LemmyResult<DbUrl> {
|
||||
@@ -988,12 +985,12 @@ pub fn generate_inbox_url() -> LemmyResult<DbUrl> {
|
||||
Ok(Url::parse(&url)?.into())
|
||||
}
|
||||
|
||||
pub fn generate_outbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
||||
Ok(Url::parse(&format!("{actor_id}/outbox"))?.into())
|
||||
pub fn generate_outbox_url(ap_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
||||
Ok(Url::parse(&format!("{ap_id}/outbox"))?.into())
|
||||
}
|
||||
|
||||
pub fn generate_featured_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
||||
Ok(Url::parse(&format!("{actor_id}/featured"))?.into())
|
||||
pub fn generate_featured_url(ap_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
||||
Ok(Url::parse(&format!("{ap_id}/featured"))?.into())
|
||||
}
|
||||
|
||||
pub fn generate_moderators_url(community_id: &DbUrl) -> LemmyResult<DbUrl> {
|
||||
|
||||
@@ -8,12 +8,10 @@ use lemmy_api_common::{
|
||||
utils::{
|
||||
generate_followers_url,
|
||||
generate_inbox_url,
|
||||
generate_local_apub_endpoint,
|
||||
get_url_blocklist,
|
||||
is_admin,
|
||||
local_site_to_slur_regex,
|
||||
process_markdown_opt,
|
||||
EndpointType,
|
||||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
@@ -82,13 +80,8 @@ pub async fn create_community(
|
||||
check_community_visibility_allowed(data.visibility, &local_user_view)?;
|
||||
|
||||
// Double check for duplicate community actor_ids
|
||||
let community_actor_id = generate_local_apub_endpoint(
|
||||
EndpointType::Community,
|
||||
&data.name,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let community_dupe =
|
||||
Community::read_from_apub_id(&mut context.pool(), &community_actor_id).await?;
|
||||
let community_ap_id = Community::local_url(&data.name, context.settings())?;
|
||||
let community_dupe = Community::read_from_apub_id(&mut context.pool(), &community_ap_id).await?;
|
||||
if community_dupe.is_some() {
|
||||
Err(LemmyErrorType::CommunityAlreadyExists)?
|
||||
}
|
||||
@@ -100,9 +93,9 @@ pub async fn create_community(
|
||||
sidebar,
|
||||
description,
|
||||
nsfw: data.nsfw,
|
||||
actor_id: Some(community_actor_id.clone()),
|
||||
ap_id: Some(community_ap_id.clone()),
|
||||
private_key: Some(keypair.private_key),
|
||||
followers_url: Some(generate_followers_url(&community_actor_id)?),
|
||||
followers_url: Some(generate_followers_url(&community_ap_id)?),
|
||||
inbox_url: Some(generate_inbox_url()?),
|
||||
posting_restricted_to_mods: data.posting_restricted_to_mods,
|
||||
visibility: data.visibility,
|
||||
|
||||
@@ -24,15 +24,18 @@ pub async fn list_communities(
|
||||
check_private_instance(&local_user_view, &local_site.local_site)?;
|
||||
|
||||
let sort = data.sort;
|
||||
let time_range_seconds = data.time_range_seconds;
|
||||
let listing_type = data.type_;
|
||||
let show_nsfw = data.show_nsfw.unwrap_or_default();
|
||||
let page = data.page;
|
||||
let limit = data.limit;
|
||||
let local_user = local_user_view.map(|l| l.local_user);
|
||||
|
||||
let communities = CommunityQuery {
|
||||
listing_type,
|
||||
show_nsfw,
|
||||
sort,
|
||||
time_range_seconds,
|
||||
local_user: local_user.as_ref(),
|
||||
page,
|
||||
limit,
|
||||
|
||||
@@ -13,7 +13,7 @@ use lemmy_db_schema::{
|
||||
};
|
||||
use lemmy_db_views::{
|
||||
post::post_view::PostQuery,
|
||||
structs::{CommunityModeratorView, CommunityView, LocalUserView, PostView, SiteView},
|
||||
structs::{CommunityView, LocalUserView, PostView, SiteView},
|
||||
};
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
@@ -84,8 +84,6 @@ pub async fn get_post(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
|
||||
|
||||
// Fetch the cross_posts
|
||||
let cross_posts = if let Some(url) = &post_view.post.url {
|
||||
let mut x_posts = PostQuery {
|
||||
@@ -108,7 +106,6 @@ pub async fn get_post(
|
||||
Ok(Json(GetPostResponse {
|
||||
post_view,
|
||||
community_view,
|
||||
moderators,
|
||||
cross_posts,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use lemmy_api_common::{
|
||||
send_activity::{ActivityChannel, SendActivityData},
|
||||
utils::{
|
||||
check_private_messages_enabled,
|
||||
get_interface_language,
|
||||
get_url_blocklist,
|
||||
local_site_to_slur_regex,
|
||||
process_markdown,
|
||||
@@ -72,7 +71,7 @@ pub async fn create_private_message(
|
||||
if view.recipient.local {
|
||||
let recipient_id = data.recipient_id;
|
||||
let local_recipient = LocalUserView::read_person(&mut context.pool(), recipient_id).await?;
|
||||
let lang = get_interface_language(&local_recipient);
|
||||
let lang = &local_recipient.local_user.interface_i18n_language();
|
||||
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
||||
let sender_name = &local_user_view.person.name;
|
||||
let content = markdown_to_html(&content);
|
||||
|
||||
@@ -53,7 +53,7 @@ pub async fn create_site(
|
||||
|
||||
validate_create_payload(&local_site, &data)?;
|
||||
|
||||
let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into();
|
||||
let ap_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into();
|
||||
let inbox_url = Some(generate_inbox_url()?);
|
||||
let keypair = generate_actor_keypair()?;
|
||||
|
||||
@@ -65,7 +65,7 @@ pub async fn create_site(
|
||||
name: Some(data.name.clone()),
|
||||
sidebar: diesel_string_update(sidebar.as_deref()),
|
||||
description: diesel_string_update(data.description.as_deref()),
|
||||
actor_id: Some(actor_id),
|
||||
ap_id: Some(ap_id),
|
||||
last_refreshed_at: Some(Utc::now()),
|
||||
inbox_url,
|
||||
private_key: Some(Some(keypair.private_key)),
|
||||
|
||||
@@ -24,7 +24,7 @@ use lemmy_db_schema::{
|
||||
site::{Site, SiteUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::diesel_string_update,
|
||||
utils::{diesel_opt_number_update, diesel_string_update},
|
||||
RegistrationMode,
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
@@ -68,6 +68,8 @@ pub async fn update_site(
|
||||
.await?
|
||||
.as_deref(),
|
||||
);
|
||||
let default_post_time_range_seconds =
|
||||
diesel_opt_number_update(data.default_post_time_range_seconds);
|
||||
|
||||
let site_form = SiteUpdateForm {
|
||||
name: data.name.clone(),
|
||||
@@ -93,6 +95,7 @@ pub async fn update_site(
|
||||
default_theme: data.default_theme.clone(),
|
||||
default_post_listing_type: data.default_post_listing_type,
|
||||
default_post_sort_type: data.default_post_sort_type,
|
||||
default_post_time_range_seconds,
|
||||
default_comment_sort_type: data.default_comment_sort_type,
|
||||
legal_information: diesel_string_update(data.legal_information.as_deref()),
|
||||
application_email_admins: data.application_email_admins,
|
||||
|
||||
@@ -10,17 +10,14 @@ use lemmy_api_common::{
|
||||
check_registration_application,
|
||||
check_user_valid,
|
||||
generate_inbox_url,
|
||||
generate_local_apub_endpoint,
|
||||
honeypot_check,
|
||||
local_site_to_slur_regex,
|
||||
password_length_check,
|
||||
send_new_applicant_email_to_admins,
|
||||
send_verification_email,
|
||||
EndpointType,
|
||||
send_verification_email_if_required,
|
||||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
aggregates::structs::PersonAggregates,
|
||||
newtypes::{InstanceId, OAuthProviderId},
|
||||
source::{
|
||||
actor_language::SiteLanguage,
|
||||
@@ -28,7 +25,6 @@ use lemmy_db_schema::{
|
||||
language::Language,
|
||||
local_site::LocalSite,
|
||||
local_user::{LocalUser, LocalUserInsertForm},
|
||||
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
||||
oauth_account::{OAuthAccount, OAuthAccountInsertForm},
|
||||
oauth_provider::OAuthProvider,
|
||||
person::{Person, PersonInsertForm},
|
||||
@@ -414,15 +410,11 @@ async fn create_person(
|
||||
) -> Result<Person, LemmyError> {
|
||||
let actor_keypair = generate_actor_keypair()?;
|
||||
is_valid_actor_name(&username, local_site.actor_name_max_length as usize)?;
|
||||
let actor_id = generate_local_apub_endpoint(
|
||||
EndpointType::Person,
|
||||
&username,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let ap_id = Person::local_url(&username, context.settings())?;
|
||||
|
||||
// Register the new person
|
||||
let person_form = PersonInsertForm {
|
||||
actor_id: Some(actor_id.clone()),
|
||||
ap_id: Some(ap_id.clone()),
|
||||
inbox_url: Some(generate_inbox_url()?),
|
||||
private_key: Some(actor_keypair.private_key),
|
||||
..PersonInsertForm::new(username.clone(), actor_keypair.public_key, instance_id)
|
||||
@@ -482,37 +474,6 @@ async fn create_local_user(
|
||||
Ok(inserted_local_user)
|
||||
}
|
||||
|
||||
async fn send_verification_email_if_required(
|
||||
context: &Data<LemmyContext>,
|
||||
local_site: &LocalSite,
|
||||
local_user: &LocalUser,
|
||||
person: &Person,
|
||||
) -> LemmyResult<bool> {
|
||||
let mut sent = false;
|
||||
if !local_user.admin && local_site.require_email_verification && !local_user.email_verified {
|
||||
let local_user_view = LocalUserView {
|
||||
local_user: local_user.clone(),
|
||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
||||
person: person.clone(),
|
||||
counts: PersonAggregates::default(),
|
||||
};
|
||||
|
||||
send_verification_email(
|
||||
&local_user_view,
|
||||
&local_user
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(LemmyErrorType::EmailRequired)?,
|
||||
&mut context.pool(),
|
||||
context.settings(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
sent = true;
|
||||
}
|
||||
Ok(sent)
|
||||
}
|
||||
|
||||
fn validate_registration_answer(
|
||||
require_registration_application: bool,
|
||||
answer: &Option<String>,
|
||||
|
||||
@@ -41,7 +41,7 @@ reqwest = { workspace = true }
|
||||
moka.workspace = true
|
||||
serde_with.workspace = true
|
||||
html2md = "0.2.15"
|
||||
html2text = "0.13.6"
|
||||
html2text = "0.14.0"
|
||||
stringreader = "0.1.1"
|
||||
enum_delegate = "0.2.0"
|
||||
semver = "1.0.25"
|
||||
|
||||
@@ -110,8 +110,8 @@ impl Object for SiteOrCommunity {
|
||||
impl SiteOrCommunity {
|
||||
fn id(&self) -> ObjectId<SiteOrCommunity> {
|
||||
match self {
|
||||
SiteOrCommunity::Site(s) => ObjectId::from(s.actor_id.clone()),
|
||||
SiteOrCommunity::Community(c) => ObjectId::from(c.actor_id.clone()),
|
||||
SiteOrCommunity::Site(s) => ObjectId::from(s.ap_id.clone()),
|
||||
SiteOrCommunity::Community(c) => ObjectId::from(c.ap_id.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ async fn generate_cc(target: &SiteOrCommunity, pool: &mut DbPool<'_>) -> LemmyRe
|
||||
SiteOrCommunity::Site(_) => Site::read_remote_sites(pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|s| s.actor_id.into())
|
||||
.map(|s| s.ap_id.into())
|
||||
.collect(),
|
||||
SiteOrCommunity::Community(c) => vec![c.id()],
|
||||
})
|
||||
|
||||
@@ -62,13 +62,13 @@ impl ActivityHandler for RawAnnouncableActivities {
|
||||
|
||||
// verify and receive activity
|
||||
activity.verify(context).await?;
|
||||
let actor_id = activity.actor().clone().into();
|
||||
let ap_id = activity.actor().clone().into();
|
||||
activity.receive(context).await?;
|
||||
|
||||
// if community is local, send activity to followers
|
||||
if let Some(community) = community {
|
||||
if community.local {
|
||||
verify_person_in_community(&actor_id, &community, context).await?;
|
||||
verify_person_in_community(&ap_id, &community, context).await?;
|
||||
AnnounceActivity::send(self, &community, context).await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ impl CollectionAdd {
|
||||
actor: actor.id().into(),
|
||||
to: generate_to(community)?,
|
||||
object: added_mod.id(),
|
||||
target: generate_moderators_url(&community.actor_id)?.into(),
|
||||
target: generate_moderators_url(&community.ap_id)?.into(),
|
||||
cc: vec![community.id()],
|
||||
kind: AddType::Add,
|
||||
id: id.clone(),
|
||||
@@ -80,7 +80,7 @@ impl CollectionAdd {
|
||||
actor: actor.id().into(),
|
||||
to: generate_to(community)?,
|
||||
object: featured_post.ap_id.clone().into(),
|
||||
target: generate_featured_url(&community.actor_id)?.into(),
|
||||
target: generate_featured_url(&community.ap_id)?.into(),
|
||||
cc: vec![community.id()],
|
||||
kind: AddType::Add,
|
||||
id: id.clone(),
|
||||
|
||||
@@ -50,7 +50,7 @@ impl CollectionRemove {
|
||||
actor: actor.id().into(),
|
||||
to: generate_to(community)?,
|
||||
object: removed_mod.id(),
|
||||
target: generate_moderators_url(&community.actor_id)?.into(),
|
||||
target: generate_moderators_url(&community.ap_id)?.into(),
|
||||
id: id.clone(),
|
||||
cc: vec![community.id()],
|
||||
kind: RemoveType::Remove,
|
||||
@@ -75,7 +75,7 @@ impl CollectionRemove {
|
||||
actor: actor.id().into(),
|
||||
to: generate_to(community)?,
|
||||
object: featured_post.ap_id.clone().into(),
|
||||
target: generate_featured_url(&community.actor_id)?.into(),
|
||||
target: generate_featured_url(&community.ap_id)?.into(),
|
||||
cc: vec![community.id()],
|
||||
kind: RemoveType::Remove,
|
||||
id: id.clone(),
|
||||
|
||||
@@ -135,9 +135,9 @@ pub(crate) async fn send_lock_post(
|
||||
LockType::Lock,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let community_id = community.actor_id.inner().clone();
|
||||
let community_id = community.ap_id.inner().clone();
|
||||
let lock = LockPage {
|
||||
actor: actor.actor_id.clone().into(),
|
||||
actor: actor.ap_id.clone().into(),
|
||||
to: generate_to(&community)?,
|
||||
object: ObjectId::from(post.ap_id),
|
||||
cc: vec![community_id.clone()],
|
||||
|
||||
@@ -105,6 +105,7 @@ impl ActivityHandler for Report {
|
||||
original_post_url: post.url.clone(),
|
||||
reason,
|
||||
original_post_body: post.body.clone(),
|
||||
violates_instance_rules: false,
|
||||
};
|
||||
PostReport::report(&mut context.pool(), &report_form).await?;
|
||||
}
|
||||
@@ -116,6 +117,7 @@ impl ActivityHandler for Report {
|
||||
comment_id: comment.id,
|
||||
original_comment_text: comment.content.clone(),
|
||||
reason,
|
||||
violates_instance_rules: false,
|
||||
};
|
||||
CommentReport::report(&mut context.pool(), &report_form).await?;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ impl ActivityHandler for UpdateCommunity {
|
||||
verify_visibility(&self.to, &self.cc, &community)?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
verify_mod_action(&self.actor, &community, context).await?;
|
||||
ApubCommunity::verify(&self.object, &community.actor_id.clone().into(), context).await?;
|
||||
ApubCommunity::verify(&self.object, &community.ap_id.clone().into(), context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ impl ActivityHandler for UpdateCommunity {
|
||||
published: self.object.published,
|
||||
updated: Some(self.object.updated),
|
||||
nsfw: Some(self.object.sensitive.unwrap_or(false)),
|
||||
actor_id: Some(self.object.id.into()),
|
||||
ap_id: Some(self.object.id.into()),
|
||||
public_key: Some(self.object.public_key.public_key_pem),
|
||||
last_refreshed_at: Some(Utc::now()),
|
||||
icon: Some(self.object.icon.map(|i| i.url.into())),
|
||||
|
||||
@@ -91,9 +91,9 @@ impl Delete {
|
||||
DeleteType::Delete,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
|
||||
let cc: Option<Url> = community.map(|c| c.ap_id.clone().into());
|
||||
Ok(Delete {
|
||||
actor: actor.actor_id.clone().into(),
|
||||
actor: actor.ap_id.clone().into(),
|
||||
to,
|
||||
object: IdOrNestedObject::Id(object.id()),
|
||||
cc: cc.into_iter().collect(),
|
||||
|
||||
@@ -180,7 +180,7 @@ pub(in crate::activities) async fn verify_delete_activity(
|
||||
DeletableObjects::Person(person) => {
|
||||
verify_is_public(&activity.to, &[])?;
|
||||
verify_person(&activity.actor, context).await?;
|
||||
verify_urls_match(person.actor_id.inner(), activity.object.id())?;
|
||||
verify_urls_match(person.ap_id.inner(), activity.object.id())?;
|
||||
}
|
||||
DeletableObjects::Post(p) => {
|
||||
let community = activity.community(context).await?;
|
||||
|
||||
@@ -77,9 +77,9 @@ impl UndoDelete {
|
||||
UndoType::Undo,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
|
||||
let cc: Option<Url> = community.map(|c| c.ap_id.clone().into());
|
||||
Ok(UndoDelete {
|
||||
actor: actor.actor_id.clone().into(),
|
||||
actor: actor.ap_id.clone().into(),
|
||||
to,
|
||||
object,
|
||||
cc: cc.into_iter().collect(),
|
||||
|
||||
@@ -21,12 +21,13 @@ use lemmy_db_schema::{
|
||||
source::{
|
||||
activity::ActivitySendTargets,
|
||||
community::{CommunityFollower, CommunityFollowerForm, CommunityFollowerState},
|
||||
instance::Instance,
|
||||
person::{PersonFollower, PersonFollowerForm},
|
||||
},
|
||||
traits::Followable,
|
||||
CommunityVisibility,
|
||||
};
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult};
|
||||
use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult};
|
||||
use url::Url;
|
||||
|
||||
impl Follow {
|
||||
@@ -102,6 +103,13 @@ impl ActivityHandler for Follow {
|
||||
AcceptFollow::send(self, context).await?;
|
||||
}
|
||||
UserOrCommunity::Community(c) => {
|
||||
if c.visibility == CommunityVisibility::Private {
|
||||
let instance = Instance::read(&mut context.pool(), actor.instance_id).await?;
|
||||
if [Some("kbin"), Some("mbin")].contains(&instance.software.as_deref()) {
|
||||
// TODO: change this to a minimum version check once private communities are supported
|
||||
return Err(FederationError::PlatformLackingPrivateCommunitySupport.into());
|
||||
}
|
||||
}
|
||||
let state = Some(match c.visibility {
|
||||
CommunityVisibility::Public => CommunityFollowerState::Accepted,
|
||||
CommunityVisibility::Private => CommunityFollowerState::ApprovalRequired,
|
||||
|
||||
@@ -47,9 +47,9 @@ pub async fn send_accept_or_reject_follow(
|
||||
let person = Person::read(&mut context.pool(), person_id).await?;
|
||||
|
||||
let follow = Follow {
|
||||
actor: person.actor_id.into(),
|
||||
to: Some([community.actor_id.clone().into()]),
|
||||
object: community.actor_id.into(),
|
||||
actor: person.ap_id.into(),
|
||||
to: Some([community.ap_id.clone().into()]),
|
||||
object: community.ap_id.into(),
|
||||
kind: FollowType::Follow,
|
||||
id: generate_activity_id(
|
||||
FollowType::Follow,
|
||||
|
||||
@@ -82,7 +82,7 @@ pub(crate) async fn verify_person_in_community(
|
||||
let person = person_id.dereference(context).await?;
|
||||
if person.banned {
|
||||
Err(FederationError::PersonIsBannedFromSite(
|
||||
person.actor_id.to_string(),
|
||||
person.ap_id.to_string(),
|
||||
))?
|
||||
}
|
||||
let person_id = person.id;
|
||||
@@ -103,7 +103,7 @@ pub(crate) async fn verify_mod_action(
|
||||
// mod action comes from the same instance as the community, so it was presumably done
|
||||
// by an instance admin.
|
||||
// TODO: federate instance admin status and check it here
|
||||
if mod_id.inner().domain() == community.actor_id.domain() {
|
||||
if mod_id.inner().domain() == community.ap_id.domain() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -134,13 +134,13 @@ pub(crate) fn verify_visibility(to: &[Url], cc: &[Url], community: &Community) -
|
||||
|
||||
/// Marks object as public only if the community is public
|
||||
pub(crate) fn generate_to(community: &Community) -> LemmyResult<Vec<Url>> {
|
||||
let actor_id = community.actor_id.clone().into();
|
||||
let ap_id = community.ap_id.clone().into();
|
||||
if community.visibility == CommunityVisibility::Public {
|
||||
Ok(vec![actor_id, public()])
|
||||
Ok(vec![ap_id, public()])
|
||||
} else {
|
||||
Ok(vec![
|
||||
actor_id.clone(),
|
||||
Url::parse(&format!("{}/followers", actor_id))?,
|
||||
ap_id.clone(),
|
||||
Url::parse(&format!("{}/followers", ap_id))?,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::comment_sort_type_with_default;
|
||||
use crate::{
|
||||
api::listing_type_with_default,
|
||||
fetcher::resolve_actor_identifier,
|
||||
fetcher::resolve_ap_identifier,
|
||||
objects::community::ApubCommunity,
|
||||
};
|
||||
use activitypub_federation::config::Data;
|
||||
@@ -32,7 +32,7 @@ async fn list_comments_common(
|
||||
|
||||
let community_id = if let Some(name) = &data.community_name {
|
||||
Some(
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(name, &context, &local_user_view, true)
|
||||
resolve_ap_identifier::<ApubCommunity, Community>(name, &context, &local_user_view, true)
|
||||
.await?,
|
||||
)
|
||||
.map(|c| c.id)
|
||||
@@ -45,6 +45,7 @@ async fn list_comments_common(
|
||||
local_user_ref,
|
||||
&site_view.local_site,
|
||||
));
|
||||
let time_range_seconds = data.time_range_seconds;
|
||||
let max_depth = data.max_depth;
|
||||
|
||||
let liked_only = data.liked_only;
|
||||
@@ -76,6 +77,7 @@ async fn list_comments_common(
|
||||
CommentQuery {
|
||||
listing_type,
|
||||
sort,
|
||||
time_range_seconds,
|
||||
max_depth,
|
||||
liked_only,
|
||||
disliked_only,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use crate::{
|
||||
api::{listing_type_with_default, post_sort_type_with_default},
|
||||
fetcher::resolve_actor_identifier,
|
||||
api::{
|
||||
listing_type_with_default,
|
||||
post_sort_type_with_default,
|
||||
post_time_range_seconds_with_default,
|
||||
},
|
||||
fetcher::resolve_ap_identifier,
|
||||
objects::community::ApubCommunity,
|
||||
};
|
||||
use activitypub_federation::config::Data;
|
||||
@@ -25,15 +29,15 @@ pub async fn list_posts(
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: Option<LocalUserView>,
|
||||
) -> LemmyResult<Json<GetPostsResponse>> {
|
||||
let local_site = SiteView::read_local(&mut context.pool()).await?;
|
||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
|
||||
check_private_instance(&local_user_view, &local_site.local_site)?;
|
||||
check_private_instance(&local_user_view, &site_view.local_site)?;
|
||||
|
||||
let page = data.page;
|
||||
let limit = data.limit;
|
||||
let community_id = if let Some(name) = &data.community_name {
|
||||
Some(
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(name, &context, &local_user_view, true)
|
||||
resolve_ap_identifier::<ApubCommunity, Community>(name, &context, &local_user_view, true)
|
||||
.await?,
|
||||
)
|
||||
.map(|c| c.id)
|
||||
@@ -55,15 +59,20 @@ pub async fn list_posts(
|
||||
let listing_type = Some(listing_type_with_default(
|
||||
data.type_,
|
||||
local_user,
|
||||
&local_site.local_site,
|
||||
&site_view.local_site,
|
||||
community_id,
|
||||
));
|
||||
|
||||
let sort = Some(post_sort_type_with_default(
|
||||
data.sort,
|
||||
local_user,
|
||||
&local_site.local_site,
|
||||
&site_view.local_site,
|
||||
));
|
||||
let time_range_seconds = post_time_range_seconds_with_default(
|
||||
data.time_range_seconds,
|
||||
local_user,
|
||||
&site_view.local_site,
|
||||
);
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
@@ -76,6 +85,7 @@ pub async fn list_posts(
|
||||
local_user,
|
||||
listing_type,
|
||||
sort,
|
||||
time_range_seconds,
|
||||
community_id,
|
||||
read_only,
|
||||
liked_only,
|
||||
@@ -90,7 +100,7 @@ pub async fn list_posts(
|
||||
no_comments_only,
|
||||
..Default::default()
|
||||
}
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.list(&site_view.site, &mut context.pool())
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntGetPosts)?;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{fetcher::resolve_actor_identifier, objects::person::ApubPerson};
|
||||
use crate::{fetcher::resolve_ap_identifier, objects::person::ApubPerson};
|
||||
use activitypub_federation::config::Data;
|
||||
use lemmy_api_common::{context::LemmyContext, LemmyErrorType};
|
||||
use lemmy_db_schema::{
|
||||
@@ -54,6 +54,26 @@ fn post_sort_type_with_default(
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a default post_time_range.
|
||||
/// Order is the given, then local user default, then site default.
|
||||
/// If zero is given, then the output is None.
|
||||
fn post_time_range_seconds_with_default(
|
||||
secs: Option<i32>,
|
||||
local_user: Option<&LocalUser>,
|
||||
local_site: &LocalSite,
|
||||
) -> Option<i32> {
|
||||
let out = secs
|
||||
.or(local_user.and_then(|u| u.default_post_time_range_seconds))
|
||||
.or(local_site.default_post_time_range_seconds);
|
||||
|
||||
// A zero is an override to None
|
||||
if out.is_some_and(|o| o == 0) {
|
||||
None
|
||||
} else {
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a default instance-level comment sort type, if none is given by the user.
|
||||
/// Order is type, local user default, then site default.
|
||||
fn comment_sort_type_with_default(
|
||||
@@ -83,7 +103,7 @@ async fn resolve_person_id_from_id_or_username(
|
||||
Some(id) => *id,
|
||||
None => {
|
||||
if let Some(username) = username {
|
||||
resolve_actor_identifier::<ApubPerson, Person>(username, context, local_user_view, true)
|
||||
resolve_ap_identifier::<ApubPerson, Person>(username, context, local_user_view, true)
|
||||
.await?
|
||||
.id
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
|
||||
use crate::{fetcher::resolve_ap_identifier, objects::community::ApubCommunity};
|
||||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::{Json, Query};
|
||||
use lemmy_api_common::{
|
||||
@@ -33,7 +33,7 @@ pub async fn get_community(
|
||||
Some(id) => id,
|
||||
None => {
|
||||
let name = data.name.clone().unwrap_or_else(|| "main".to_string());
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(&name, &context, &local_user_view, true)
|
||||
resolve_ap_identifier::<ApubCommunity, Community>(&name, &context, &local_user_view, true)
|
||||
.await?
|
||||
.id
|
||||
}
|
||||
@@ -57,7 +57,7 @@ pub async fn get_community(
|
||||
|
||||
let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
|
||||
|
||||
let site = read_site_for_actor(community_view.community.actor_id.clone(), &context).await?;
|
||||
let site = read_site_for_actor(community_view.community.ap_id.clone(), &context).await?;
|
||||
|
||||
let community_id = community_view.community.id;
|
||||
let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?;
|
||||
|
||||
@@ -40,7 +40,7 @@ pub async fn read_person(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let site = read_site_for_actor(person_view.person.actor_id.clone(), &context).await?;
|
||||
let site = read_site_for_actor(person_view.person.ap_id.clone(), &context).await?;
|
||||
|
||||
Ok(Json(GetPersonDetailsResponse {
|
||||
person_view,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
|
||||
use crate::{fetcher::resolve_ap_identifier, objects::community::ApubCommunity};
|
||||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::{Json, Query};
|
||||
use lemmy_api_common::{
|
||||
@@ -25,7 +25,7 @@ pub async fn search(
|
||||
|
||||
let community_id = if let Some(name) = &data.community_name {
|
||||
Some(
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(name, &context, &local_user_view, false)
|
||||
resolve_ap_identifier::<ApubCommunity, Community>(name, &context, &local_user_view, false)
|
||||
.await?,
|
||||
)
|
||||
.map(|c| c.id)
|
||||
@@ -33,6 +33,7 @@ pub async fn search(
|
||||
data.community_id
|
||||
};
|
||||
let search_term = data.search_term.clone();
|
||||
let time_range_seconds = data.time_range_seconds;
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
@@ -48,6 +49,7 @@ pub async fn search(
|
||||
creator_id: data.creator_id,
|
||||
type_: data.type_,
|
||||
sort: data.sort,
|
||||
time_range_seconds,
|
||||
listing_type: data.listing_type,
|
||||
title_only: data.title_only,
|
||||
post_url_only: data.post_url_only,
|
||||
|
||||
@@ -368,7 +368,7 @@ pub(crate) mod tests {
|
||||
|
||||
let follows = CommunityFollowerView::for_person(pool, import_user.person.id).await?;
|
||||
assert_eq!(follows.len(), 1);
|
||||
assert_eq!(follows[0].community.actor_id, community.actor_id);
|
||||
assert_eq!(follows[0].community.ap_id, community.ap_id);
|
||||
|
||||
Person::delete(pool, export_user.person.id).await?;
|
||||
Person::delete(pool, import_user.person.id).await?;
|
||||
|
||||
@@ -41,7 +41,7 @@ impl Collection for ApubCommunityFeatured {
|
||||
.await?;
|
||||
Ok(GroupFeatured {
|
||||
r#type: OrderedCollectionType::OrderedCollection,
|
||||
id: generate_featured_url(&owner.actor_id)?.into(),
|
||||
id: generate_featured_url(&owner.ap_id)?.into(),
|
||||
total_items: ordered_items.len() as i32,
|
||||
ordered_items,
|
||||
})
|
||||
|
||||
@@ -33,7 +33,7 @@ impl Collection for ApubCommunityFollower {
|
||||
CommunityFollowerView::count_community_followers(&mut context.pool(), community_id).await?;
|
||||
|
||||
Ok(GroupFollowers {
|
||||
id: generate_followers_url(&community.actor_id)?.into(),
|
||||
id: generate_followers_url(&community.ap_id)?.into(),
|
||||
r#type: CollectionType::Collection,
|
||||
total_items: community_followers as i32,
|
||||
items: vec![],
|
||||
|
||||
@@ -32,11 +32,11 @@ impl Collection for ApubCommunityModerators {
|
||||
let moderators = CommunityModeratorView::for_community(&mut data.pool(), owner.id).await?;
|
||||
let ordered_items = moderators
|
||||
.into_iter()
|
||||
.map(|m| ObjectId::<ApubPerson>::from(m.moderator.actor_id))
|
||||
.map(|m| ObjectId::<ApubPerson>::from(m.moderator.ap_id))
|
||||
.collect();
|
||||
Ok(GroupModerators {
|
||||
r#type: OrderedCollectionType::OrderedCollection,
|
||||
id: generate_moderators_url(&owner.actor_id)?.into(),
|
||||
id: generate_moderators_url(&owner.ap_id)?.into(),
|
||||
ordered_items,
|
||||
})
|
||||
}
|
||||
@@ -60,7 +60,7 @@ impl Collection for ApubCommunityModerators {
|
||||
CommunityModeratorView::for_community(&mut data.pool(), community_id).await?;
|
||||
// Remove old mods from database which arent in the moderators collection anymore
|
||||
for mod_user in ¤t_moderators {
|
||||
let mod_id = ObjectId::from(mod_user.moderator.actor_id.clone());
|
||||
let mod_id = ObjectId::from(mod_user.moderator.ap_id.clone());
|
||||
if !apub.ordered_items.contains(&mod_id) {
|
||||
let community_moderator_form = CommunityModeratorForm {
|
||||
community_id: mod_user.community.id,
|
||||
@@ -77,8 +77,8 @@ impl Collection for ApubCommunityModerators {
|
||||
if let Some(mod_user) = mod_user {
|
||||
if !current_moderators
|
||||
.iter()
|
||||
.map(|c| c.moderator.actor_id.clone())
|
||||
.any(|x| x == mod_user.actor_id)
|
||||
.map(|c| c.moderator.ap_id.clone())
|
||||
.any(|x| x == mod_user.ap_id)
|
||||
{
|
||||
let community_moderator_form = CommunityModeratorForm {
|
||||
community_id: owner.id,
|
||||
@@ -136,7 +136,7 @@ mod tests {
|
||||
|
||||
CommunityModerator::join(&mut context.pool(), &community_moderator_form).await?;
|
||||
|
||||
assert_eq!(site.actor_id.to_string(), "https://enterprise.lemmy.ml/");
|
||||
assert_eq!(site.ap_id.to_string(), "https://enterprise.lemmy.ml/");
|
||||
|
||||
let json: GroupModerators =
|
||||
file_to_json_object("assets/lemmy/collections/group_moderators.json")?;
|
||||
|
||||
@@ -62,7 +62,7 @@ impl Collection for ApubCommunityOutbox {
|
||||
|
||||
Ok(GroupOutbox {
|
||||
r#type: OrderedCollectionType::OrderedCollection,
|
||||
id: generate_outbox_url(&owner.actor_id)?.into(),
|
||||
id: generate_outbox_url(&owner.ap_id)?.into(),
|
||||
total_items: ordered_items.len() as i32,
|
||||
ordered_items,
|
||||
})
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use super::{search::SearchableObjects, user_or_community::UserOrCommunity};
|
||||
use crate::fetcher::post_or_comment::PostOrComment;
|
||||
use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
utils::{generate_local_apub_endpoint, EndpointType},
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{newtypes::InstanceId, source::instance::Instance};
|
||||
use lemmy_utils::{
|
||||
error::LemmyResult,
|
||||
@@ -61,12 +58,8 @@ pub(crate) async fn to_local_url(url: &str, context: &Data<LemmyContext>) -> Opt
|
||||
let dereferenced = object_id.dereference(context).await.ok()?;
|
||||
match dereferenced {
|
||||
SearchableObjects::PostOrComment(pc) => match *pc {
|
||||
PostOrComment::Post(post) => {
|
||||
generate_local_apub_endpoint(EndpointType::Post, &post.id.to_string(), local_domain)
|
||||
}
|
||||
PostOrComment::Comment(comment) => {
|
||||
generate_local_apub_endpoint(EndpointType::Comment, &comment.id.to_string(), local_domain)
|
||||
}
|
||||
PostOrComment::Post(post) => post.local_url(context.settings()),
|
||||
PostOrComment::Comment(comment) => comment.local_url(context.settings()),
|
||||
}
|
||||
.ok()
|
||||
.map(Into::into),
|
||||
@@ -150,7 +143,7 @@ mod tests {
|
||||
),
|
||||
(
|
||||
"rewrite community link",
|
||||
format!("[link]({})", community.actor_id),
|
||||
format!("[link]({})", community.ap_id),
|
||||
"[link](https://lemmy-alpha/c/my_community@example.com)",
|
||||
),
|
||||
(
|
||||
|
||||
@@ -20,7 +20,7 @@ pub mod user_or_community;
|
||||
///
|
||||
/// In case the requesting user is logged in and the object was not found locally, it is attempted
|
||||
/// to fetch via webfinger from the original instance.
|
||||
pub async fn resolve_actor_identifier<ActorType, DbActor>(
|
||||
pub async fn resolve_ap_identifier<ActorType, DbActor>(
|
||||
identifier: &str,
|
||||
context: &Data<LemmyContext>,
|
||||
local_user_view: &Option<LocalUserView>,
|
||||
|
||||
@@ -49,7 +49,7 @@ pub(crate) async fn get_apub_community_http(
|
||||
.into();
|
||||
|
||||
if community.deleted || community.removed {
|
||||
return create_apub_tombstone_response(community.actor_id.clone());
|
||||
return create_apub_tombstone_response(community.ap_id.clone());
|
||||
}
|
||||
check_community_fetchable(&community)?;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ pub(crate) async fn get_apub_person_http(
|
||||
|
||||
create_apub_response(&apub)
|
||||
} else {
|
||||
create_apub_tombstone_response(person.actor_id.clone())
|
||||
create_apub_tombstone_response(person.ap_id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ pub(crate) async fn get_apub_person_outbox(
|
||||
let person = Person::read_from_name(&mut context.pool(), &info.user_name, false)
|
||||
.await?
|
||||
.ok_or(LemmyErrorType::NotFound)?;
|
||||
let outbox_id = generate_outbox_url(&person.actor_id)?.into();
|
||||
let outbox_id = generate_outbox_url(&person.ap_id)?.into();
|
||||
let outbox = EmptyOutbox::new(outbox_id)?;
|
||||
create_apub_response(&outbox)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ pub async fn collect_non_local_mentions(
|
||||
|
||||
// Add the mention tag
|
||||
let parent_creator_tag = Mention {
|
||||
href: parent_creator.actor_id.clone().into(),
|
||||
href: parent_creator.ap_id.clone().into(),
|
||||
name: Some(format!(
|
||||
"@{}@{}",
|
||||
&parent_creator.name,
|
||||
@@ -74,7 +74,7 @@ pub async fn collect_non_local_mentions(
|
||||
let identifier = format!("{}@{}", mention.name, mention.domain);
|
||||
let person = webfinger_resolve_actor::<LemmyContext, ApubPerson>(&identifier, context).await;
|
||||
if let Ok(person) = person {
|
||||
addressed_ccs.push(person.actor_id.to_string().parse()?);
|
||||
addressed_ccs.push(person.ap_id.to_string().parse()?);
|
||||
|
||||
let mention_tag = Mention {
|
||||
href: person.id(),
|
||||
|
||||
@@ -110,7 +110,7 @@ impl Object for ApubComment {
|
||||
let note = Note {
|
||||
r#type: NoteType::Note,
|
||||
id: self.ap_id.clone().into(),
|
||||
attributed_to: creator.actor_id.into(),
|
||||
attributed_to: creator.ap_id.into(),
|
||||
to: generate_to(&community)?,
|
||||
cc: maa.ccs,
|
||||
content: markdown_to_html(&self.content),
|
||||
|
||||
@@ -109,9 +109,9 @@ impl Object for ApubCommunity {
|
||||
icon: self.icon.clone().map(ImageObject::new),
|
||||
image: self.banner.clone().map(ImageObject::new),
|
||||
sensitive: Some(self.nsfw),
|
||||
featured: Some(generate_featured_url(&self.actor_id)?.into()),
|
||||
featured: Some(generate_featured_url(&self.ap_id)?.into()),
|
||||
inbox: self.inbox_url.clone().into(),
|
||||
outbox: generate_outbox_url(&self.actor_id)?.into(),
|
||||
outbox: generate_outbox_url(&self.ap_id)?.into(),
|
||||
followers: self.followers_url.clone().map(Into::into),
|
||||
endpoints: None,
|
||||
public_key: self.public_key(),
|
||||
@@ -119,7 +119,7 @@ impl Object for ApubCommunity {
|
||||
published: Some(self.published),
|
||||
updated: self.updated,
|
||||
posting_restricted_to_mods: Some(self.posting_restricted_to_mods),
|
||||
attributed_to: Some(generate_moderators_url(&self.actor_id)?.into()),
|
||||
attributed_to: Some(generate_moderators_url(&self.ap_id)?.into()),
|
||||
manually_approves_followers: Some(self.visibility == CommunityVisibility::Private),
|
||||
};
|
||||
Ok(group)
|
||||
@@ -134,7 +134,6 @@ impl Object for ApubCommunity {
|
||||
}
|
||||
|
||||
/// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
|
||||
|
||||
async fn from_json(group: Group, context: &Data<Self::DataType>) -> LemmyResult<ApubCommunity> {
|
||||
let instance_id = fetch_instance_actor_for_object(&group.id, context).await?;
|
||||
|
||||
@@ -156,7 +155,7 @@ impl Object for ApubCommunity {
|
||||
updated: group.updated,
|
||||
deleted: Some(false),
|
||||
nsfw: Some(group.sensitive.unwrap_or(false)),
|
||||
actor_id: Some(group.id.into()),
|
||||
ap_id: Some(group.id.into()),
|
||||
local: Some(false),
|
||||
last_refreshed_at: Some(Utc::now()),
|
||||
icon,
|
||||
@@ -214,7 +213,7 @@ impl Object for ApubCommunity {
|
||||
|
||||
impl Actor for ApubCommunity {
|
||||
fn id(&self) -> Url {
|
||||
self.actor_id.inner().clone()
|
||||
self.ap_id.inner().clone()
|
||||
}
|
||||
|
||||
fn public_key_pem(&self) -> &str {
|
||||
|
||||
@@ -108,7 +108,7 @@ impl Object for ApubSite {
|
||||
icon: self.icon.clone().map(ImageObject::new),
|
||||
image: self.banner.clone().map(ImageObject::new),
|
||||
inbox: self.inbox_url.clone().into(),
|
||||
outbox: Url::parse(&format!("{}site_outbox", self.actor_id))?,
|
||||
outbox: Url::parse(&format!("{}site_outbox", self.ap_id))?,
|
||||
public_key: self.public_key(),
|
||||
language,
|
||||
content_warning: self.content_warning.clone(),
|
||||
@@ -159,7 +159,7 @@ impl Object for ApubSite {
|
||||
icon,
|
||||
banner,
|
||||
description: apub.summary,
|
||||
actor_id: Some(apub.id.clone().into()),
|
||||
ap_id: Some(apub.id.clone().into()),
|
||||
last_refreshed_at: Some(Utc::now()),
|
||||
inbox_url: Some(apub.inbox.clone().into()),
|
||||
public_key: Some(apub.public_key.public_key_pem.clone()),
|
||||
@@ -178,7 +178,7 @@ impl Object for ApubSite {
|
||||
|
||||
impl Actor for ApubSite {
|
||||
fn id(&self) -> Url {
|
||||
self.actor_id.inner().clone()
|
||||
self.ap_id.inner().clone()
|
||||
}
|
||||
|
||||
fn public_key_pem(&self) -> &str {
|
||||
@@ -205,7 +205,7 @@ pub(in crate::objects) async fn fetch_instance_actor_for_object<T: Into<Url> + C
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<InstanceId> {
|
||||
let object_id: Url = object_id.clone().into();
|
||||
let instance_id = Site::instance_actor_id_from_url(object_id);
|
||||
let instance_id = Site::instance_ap_id_from_url(object_id);
|
||||
let site = ObjectId::<ApubSite>::from(instance_id.clone())
|
||||
.dereference(context)
|
||||
.await;
|
||||
|
||||
@@ -100,7 +100,7 @@ impl Object for ApubPerson {
|
||||
|
||||
let person = Person {
|
||||
kind,
|
||||
id: self.actor_id.clone().into(),
|
||||
id: self.ap_id.clone().into(),
|
||||
preferred_username: self.name.clone(),
|
||||
name: self.display_name.clone(),
|
||||
summary: self.bio.as_ref().map(|b| markdown_to_html(b)),
|
||||
@@ -109,7 +109,7 @@ impl Object for ApubPerson {
|
||||
image: self.banner.clone().map(ImageObject::new),
|
||||
matrix_user_id: self.matrix_user_id.clone(),
|
||||
published: Some(self.published),
|
||||
outbox: generate_outbox_url(&self.actor_id)?.into(),
|
||||
outbox: generate_outbox_url(&self.ap_id)?.into(),
|
||||
endpoints: None,
|
||||
public_key: self.public_key(),
|
||||
updated: self.updated,
|
||||
@@ -163,7 +163,7 @@ impl Object for ApubPerson {
|
||||
banner,
|
||||
published: person.published,
|
||||
updated: person.updated,
|
||||
actor_id: Some(person.id.into()),
|
||||
ap_id: Some(person.id.into()),
|
||||
bio,
|
||||
local: Some(false),
|
||||
bot_account: Some(person.kind == UserTypes::Service),
|
||||
@@ -188,7 +188,7 @@ impl Object for ApubPerson {
|
||||
|
||||
impl Actor for ApubPerson {
|
||||
fn id(&self) -> Url {
|
||||
self.actor_id.inner().clone()
|
||||
self.ap_id.inner().clone()
|
||||
}
|
||||
|
||||
fn public_key_pem(&self) -> &str {
|
||||
@@ -268,7 +268,7 @@ pub(crate) mod tests {
|
||||
ApubPerson::verify(&json, &url, &context).await?;
|
||||
let person = ApubPerson::from_json(json, &context).await?;
|
||||
|
||||
assert_eq!(person.actor_id, url.into());
|
||||
assert_eq!(person.ap_id, url.into());
|
||||
assert_eq!(person.name, "lanodan");
|
||||
assert!(!person.local);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
@@ -133,7 +133,7 @@ impl Object for ApubPost {
|
||||
let page = Page {
|
||||
kind: PageType::Page,
|
||||
id: self.ap_id.clone().into(),
|
||||
attributed_to: AttributedTo::Lemmy(creator.actor_id.into()),
|
||||
attributed_to: AttributedTo::Lemmy(creator.ap_id.into()),
|
||||
to: generate_to(&community)?,
|
||||
cc: vec![],
|
||||
name: Some(self.name.clone()),
|
||||
|
||||
@@ -107,8 +107,8 @@ impl Object for ApubPrivateMessage {
|
||||
let note = PrivateMessage {
|
||||
kind,
|
||||
id: self.ap_id.clone().into(),
|
||||
attributed_to: creator.actor_id.into(),
|
||||
to: [recipient.actor_id.into()],
|
||||
attributed_to: creator.ap_id.into(),
|
||||
to: [recipient.ap_id.into()],
|
||||
content: markdown_to_html(&self.content),
|
||||
media_type: Some(MediaTypeHtml::Html),
|
||||
source: Some(Source::new(self.content.clone())),
|
||||
@@ -131,7 +131,7 @@ impl Object for ApubPrivateMessage {
|
||||
let person = note.attributed_to.dereference(context).await?;
|
||||
if person.banned {
|
||||
Err(FederationError::PersonIsBannedFromSite(
|
||||
person.actor_id.to_string(),
|
||||
person.ap_id.to_string(),
|
||||
))?
|
||||
} else {
|
||||
Ok(())
|
||||
|
||||
@@ -192,7 +192,7 @@ fn site() -> LemmyResult<Site> {
|
||||
icon: None,
|
||||
banner: None,
|
||||
description: None,
|
||||
actor_id: Url::parse("http://example.com")?.into(),
|
||||
ap_id: Url::parse("http://example.com")?.into(),
|
||||
last_refreshed_at: Default::default(),
|
||||
inbox_url: Url::parse("http://example.com")?.into(),
|
||||
private_key: None,
|
||||
|
||||
@@ -67,7 +67,7 @@ regex = { workspace = true, optional = true }
|
||||
diesel_ltree = { workspace = true, optional = true }
|
||||
async-trait = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
deadpool = { version = "0.12.1", optional = true, features = ["rt_tokio_1"] }
|
||||
deadpool = { version = "0.12.2", optional = true, features = ["rt_tokio_1"] }
|
||||
ts-rs = { workspace = true, optional = true }
|
||||
futures-util = { workspace = true }
|
||||
tokio = { workspace = true, optional = true }
|
||||
|
||||
@@ -392,10 +392,11 @@ BEGIN
|
||||
report_count = a.report_count + diff.report_count, unresolved_report_count = a.unresolved_report_count + diff.unresolved_report_count
|
||||
FROM (
|
||||
SELECT
|
||||
(post_report).post_id, coalesce(sum(count_diff), 0) AS report_count, coalesce(sum(count_diff) FILTER (WHERE NOT (post_report).resolved), 0) AS unresolved_report_count
|
||||
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (post_report).post_id) AS diff
|
||||
(post_report).post_id, coalesce(sum(count_diff), 0) AS report_count, coalesce(sum(count_diff) FILTER (WHERE NOT (post_report).resolved
|
||||
AND NOT (post_report).violates_instance_rules), 0) AS unresolved_report_count
|
||||
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (post_report).post_id) AS diff
|
||||
WHERE (diff.report_count, diff.unresolved_report_count) != (0, 0)
|
||||
AND a.post_id = diff.post_id;
|
||||
AND a.post_id = diff.post_id;
|
||||
|
||||
RETURN NULL;
|
||||
|
||||
@@ -411,10 +412,11 @@ BEGIN
|
||||
report_count = a.report_count + diff.report_count, unresolved_report_count = a.unresolved_report_count + diff.unresolved_report_count
|
||||
FROM (
|
||||
SELECT
|
||||
(comment_report).comment_id, coalesce(sum(count_diff), 0) AS report_count, coalesce(sum(count_diff) FILTER (WHERE NOT (comment_report).resolved), 0) AS unresolved_report_count
|
||||
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (comment_report).comment_id) AS diff
|
||||
(comment_report).comment_id, coalesce(sum(count_diff), 0) AS report_count, coalesce(sum(count_diff) FILTER (WHERE NOT (comment_report).resolved
|
||||
AND NOT (comment_report).violates_instance_rules), 0) AS unresolved_report_count
|
||||
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (comment_report).comment_id) AS diff
|
||||
WHERE (diff.report_count, diff.unresolved_report_count) != (0, 0)
|
||||
AND a.comment_id = diff.comment_id;
|
||||
AND a.comment_id = diff.comment_id;
|
||||
|
||||
RETURN NULL;
|
||||
|
||||
|
||||
@@ -70,14 +70,14 @@ pub struct CommunityAggregates {
|
||||
pub users_active_month: i64,
|
||||
/// The number of users with any activity in the last year.
|
||||
pub users_active_half_year: i64,
|
||||
/// Number of any interactions over the last month.
|
||||
#[serde(skip)]
|
||||
pub interactions_month: i64,
|
||||
#[serde(skip)]
|
||||
pub hot_rank: f64,
|
||||
pub subscribers_local: i64,
|
||||
pub report_count: i16,
|
||||
pub unresolved_report_count: i16,
|
||||
/// Number of any interactions over the last month.
|
||||
#[serde(skip)]
|
||||
pub interactions_month: i64,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Default)]
|
||||
@@ -99,6 +99,7 @@ pub struct PersonAggregates {
|
||||
pub comment_count: i64,
|
||||
#[serde(skip)]
|
||||
pub comment_score: i64,
|
||||
pub published: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::{
|
||||
diesel::{DecoratableTarget, OptionalExtension},
|
||||
impls::local_user::local_user_can_mod,
|
||||
newtypes::{CommentId, DbUrl, PersonId},
|
||||
schema::{comment, comment_actions},
|
||||
source::comment::{
|
||||
@@ -16,15 +17,17 @@ use crate::{
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use diesel::{
|
||||
dsl::insert_into,
|
||||
dsl::{case_when, insert_into, not},
|
||||
expression::SelectableHelper,
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
NullableExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use diesel_ltree::Ltree;
|
||||
use lemmy_utils::{error::LemmyResult, settings::structs::Settings};
|
||||
use url::Url;
|
||||
|
||||
impl Comment {
|
||||
@@ -116,6 +119,38 @@ impl Comment {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_url(&self, settings: &Settings) -> LemmyResult<DbUrl> {
|
||||
let domain = settings.get_protocol_and_hostname();
|
||||
Ok(Url::parse(&format!("{domain}/comment/{}", self.id))?.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Selects the comment columns, but gives an empty string for content when
|
||||
/// deleted or removed, and you're not a mod/admin.
|
||||
#[diesel::dsl::auto_type]
|
||||
pub fn comment_select_remove_deletes() -> _ {
|
||||
let deleted_or_removed = comment::deleted.or(comment::removed);
|
||||
|
||||
// You can only view the content if it hasn't been removed, or you can mod.
|
||||
let can_view_content = not(deleted_or_removed).or(local_user_can_mod());
|
||||
let content = case_when(can_view_content, comment::content).otherwise("");
|
||||
|
||||
(
|
||||
comment::id,
|
||||
comment::creator_id,
|
||||
comment::post_id,
|
||||
content,
|
||||
comment::removed,
|
||||
comment::published,
|
||||
comment::updated,
|
||||
comment::deleted,
|
||||
comment::ap_id,
|
||||
comment::local,
|
||||
comment::path,
|
||||
comment::distinguished,
|
||||
comment::language_id,
|
||||
)
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -33,7 +33,7 @@ use crate::{
|
||||
use chrono::{DateTime, Utc};
|
||||
use diesel::{
|
||||
deserialize,
|
||||
dsl::{self, exists, insert_into, not},
|
||||
dsl::{exists, insert_into, not},
|
||||
expression::SelectableHelper,
|
||||
pg::Pg,
|
||||
result::Error,
|
||||
@@ -47,7 +47,11 @@ use diesel::{
|
||||
Queryable,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyErrorType, LemmyResult},
|
||||
settings::structs::Settings,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
#[async_trait]
|
||||
impl Crud for Community {
|
||||
@@ -133,7 +137,7 @@ impl Community {
|
||||
timestamp: DateTime<Utc>,
|
||||
form: &CommunityInsertForm,
|
||||
) -> Result<Self, Error> {
|
||||
let is_new_community = match &form.actor_id {
|
||||
let is_new_community = match &form.ap_id {
|
||||
Some(id) => Community::read_from_apub_id(pool, id).await?.is_none(),
|
||||
None => true,
|
||||
};
|
||||
@@ -142,7 +146,7 @@ impl Community {
|
||||
// Can't do separate insert/update commands because InsertForm/UpdateForm aren't convertible
|
||||
let community_ = insert_into(community::table)
|
||||
.values(form)
|
||||
.on_conflict(community::actor_id)
|
||||
.on_conflict(community::ap_id)
|
||||
.filter_target(coalesce(community::updated, community::published).lt(timestamp))
|
||||
.do_update()
|
||||
.set(form)
|
||||
@@ -273,6 +277,11 @@ impl Community {
|
||||
.eq(false)
|
||||
.and(community::deleted.eq(false))
|
||||
}
|
||||
|
||||
pub fn local_url(name: &str, settings: &Settings) -> LemmyResult<DbUrl> {
|
||||
let domain = settings.get_protocol_and_hostname();
|
||||
Ok(Url::parse(&format!("{domain}/c/{name}"))?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl CommunityModerator {
|
||||
@@ -389,10 +398,6 @@ impl Bannable for CommunityPersonBan {
|
||||
}
|
||||
|
||||
impl CommunityFollower {
|
||||
pub fn select_subscribed_type() -> dsl::Nullable<community_actions::follow_state> {
|
||||
community_actions::follow_state.nullable()
|
||||
}
|
||||
|
||||
/// Check if a remote instance has any followers on local instance. For this it is enough to check
|
||||
/// if any follow relation is stored. Dont use this for local community.
|
||||
pub async fn check_has_local_followers(
|
||||
@@ -431,6 +436,14 @@ impl CommunityFollower {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// I'd really like to have these on the impl, but unfortunately they have to be top level,
|
||||
// according to https://diesel.rs/guides/composing-applications.html
|
||||
#[diesel::dsl::auto_type]
|
||||
pub fn community_follower_select_subscribed_type() -> _ {
|
||||
community_actions::follow_state.nullable()
|
||||
}
|
||||
|
||||
impl Queryable<sql_types::Nullable<crate::schema::sql_types::CommunityFollowerState>, Pg>
|
||||
for SubscribedType
|
||||
{
|
||||
@@ -500,7 +513,7 @@ impl ApubActor for Community {
|
||||
) -> Result<Option<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
community::table
|
||||
.filter(community::actor_id.eq(object_id))
|
||||
.filter(community::ap_id.eq(object_id))
|
||||
.first(conn)
|
||||
.await
|
||||
.optional()
|
||||
@@ -600,7 +613,7 @@ mod tests {
|
||||
deleted: false,
|
||||
published: inserted_community.published,
|
||||
updated: None,
|
||||
actor_id: inserted_community.actor_id.clone(),
|
||||
ap_id: inserted_community.ap_id.clone(),
|
||||
local: true,
|
||||
private_key: None,
|
||||
public_key: "pubkey".to_owned(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
newtypes::{DbUrl, LocalUserId},
|
||||
schema::{image_details, local_image, remote_image},
|
||||
source::images::{ImageDetails, ImageDetailsForm, LocalImage, LocalImageForm, RemoteImage},
|
||||
source::images::{ImageDetails, ImageDetailsInsertForm, LocalImage, LocalImageForm, RemoteImage},
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use diesel::{
|
||||
@@ -21,7 +21,7 @@ impl LocalImage {
|
||||
pub async fn create(
|
||||
pool: &mut DbPool<'_>,
|
||||
form: &LocalImageForm,
|
||||
image_details_form: &ImageDetailsForm,
|
||||
image_details_form: &ImageDetailsInsertForm,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
conn
|
||||
@@ -102,7 +102,10 @@ impl RemoteImage {
|
||||
}
|
||||
|
||||
impl ImageDetails {
|
||||
pub async fn create(pool: &mut DbPool<'_>, form: &ImageDetailsForm) -> Result<usize, Error> {
|
||||
pub async fn create(
|
||||
pool: &mut DbPool<'_>,
|
||||
form: &ImageDetailsInsertForm,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
insert_into(image_details::table)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
aliases::creator_community_actions,
|
||||
newtypes::{CommunityId, DbUrl, LanguageId, LocalUserId, PersonId},
|
||||
schema::{community, community_actions, local_user, person, registration_application},
|
||||
source::{
|
||||
@@ -19,13 +20,19 @@ use bcrypt::{hash, DEFAULT_COST};
|
||||
use diesel::{
|
||||
dsl::{insert_into, not, IntervalDsl},
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
CombineDsl,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
NullableExpressionMethods,
|
||||
PgExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
use lemmy_utils::{
|
||||
email::{lang_str_to_lang, translations::Lang},
|
||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
};
|
||||
|
||||
impl LocalUser {
|
||||
pub async fn create(
|
||||
@@ -171,7 +178,7 @@ impl LocalUser {
|
||||
.filter(community_actions::followed.is_not_null())
|
||||
.filter(community_actions::person_id.eq(person_id_))
|
||||
.inner_join(community::table)
|
||||
.select(community::actor_id)
|
||||
.select(community::ap_id)
|
||||
.get_results(conn)
|
||||
.await?;
|
||||
|
||||
@@ -195,7 +202,7 @@ impl LocalUser {
|
||||
.filter(community_actions::blocked.is_not_null())
|
||||
.filter(community_actions::person_id.eq(person_id_))
|
||||
.inner_join(community::table)
|
||||
.select(community::actor_id)
|
||||
.select(community::ap_id)
|
||||
.get_results(conn)
|
||||
.await?;
|
||||
|
||||
@@ -203,7 +210,7 @@ impl LocalUser {
|
||||
.filter(person_actions::blocked.is_not_null())
|
||||
.filter(person_actions::person_id.eq(person_id_))
|
||||
.inner_join(person::table.on(person_actions::target_id.eq(person::id)))
|
||||
.select(person::actor_id)
|
||||
.select(person::ap_id)
|
||||
.get_results(conn)
|
||||
.await?;
|
||||
|
||||
@@ -293,6 +300,33 @@ impl LocalUser {
|
||||
Err(LemmyErrorType::NotHigherMod)?
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interface_i18n_language(&self) -> Lang {
|
||||
lang_str_to_lang(&self.interface_language)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// I'd really like to have these on the impl, but unfortunately they have to be top level,
|
||||
// according to https://diesel.rs/guides/composing-applications.html
|
||||
/// Checks to see if you can mod an item.
|
||||
///
|
||||
/// Caveat: Since admin status isn't federated or ordered, it can't know whether
|
||||
/// item creator is a federated admin, or a higher admin.
|
||||
/// The back-end will reject an action for admin that is higher via
|
||||
/// LocalUser::is_higher_mod_or_admin_check
|
||||
#[diesel::dsl::auto_type]
|
||||
pub fn local_user_can_mod() -> _ {
|
||||
let am_admin = local_user::admin.nullable();
|
||||
let creator_became_moderator = creator_community_actions
|
||||
.field(community_actions::became_moderator)
|
||||
.nullable();
|
||||
|
||||
let am_higher_mod = community_actions::became_moderator
|
||||
.nullable()
|
||||
.le(creator_became_moderator);
|
||||
|
||||
am_admin.or(am_higher_mod).is_not_distinct_from(true)
|
||||
}
|
||||
|
||||
/// Adds some helper functions for an optional LocalUser
|
||||
|
||||
@@ -24,7 +24,11 @@ use diesel::{
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyErrorType, LemmyResult},
|
||||
settings::structs::Settings,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
#[async_trait]
|
||||
impl Crud for Person {
|
||||
@@ -71,7 +75,7 @@ impl Person {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(person::table)
|
||||
.values(form)
|
||||
.on_conflict(person::actor_id)
|
||||
.on_conflict(person::ap_id)
|
||||
.do_update()
|
||||
.set(form)
|
||||
.get_result::<Self>(conn)
|
||||
@@ -138,6 +142,11 @@ impl Person {
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::UsernameAlreadyExists.into())
|
||||
}
|
||||
|
||||
pub fn local_url(name: &str, settings: &Settings) -> LemmyResult<DbUrl> {
|
||||
let domain = settings.get_protocol_and_hostname();
|
||||
Ok(Url::parse(&format!("{domain}/u/{name}"))?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl PersonInsertForm {
|
||||
@@ -155,7 +164,7 @@ impl ApubActor for Person {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
person::table
|
||||
.filter(person::deleted.eq(false))
|
||||
.filter(person::actor_id.eq(object_id))
|
||||
.filter(person::ap_id.eq(object_id))
|
||||
.first(conn)
|
||||
.await
|
||||
.optional()
|
||||
@@ -282,7 +291,7 @@ mod tests {
|
||||
deleted: false,
|
||||
published: inserted_person.published,
|
||||
updated: None,
|
||||
actor_id: inserted_person.actor_id.clone(),
|
||||
ap_id: inserted_person.ap_id.clone(),
|
||||
bio: None,
|
||||
local: true,
|
||||
bot_account: false,
|
||||
@@ -298,7 +307,7 @@ mod tests {
|
||||
let read_person = Person::read(pool, inserted_person.id).await?;
|
||||
|
||||
let update_person_form = PersonUpdateForm {
|
||||
actor_id: Some(inserted_person.actor_id.clone()),
|
||||
ap_id: Some(inserted_person.ap_id.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let updated_person = Person::update(pool, inserted_person.id, &update_person_form).await?;
|
||||
|
||||
@@ -41,7 +41,10 @@ use diesel::{
|
||||
TextExpressionMethods,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
settings::structs::Settings,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
impl Crud for Post {
|
||||
@@ -270,6 +273,11 @@ impl Post {
|
||||
.first::<i64>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn local_url(&self, settings: &Settings) -> LemmyResult<DbUrl> {
|
||||
let domain = settings.get_protocol_and_hostname();
|
||||
Ok(Url::parse(&format!("{domain}/post/{}", self.id))?.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::{
|
||||
use chrono::{DateTime, Utc};
|
||||
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::{error::LemmyResult, settings::structs::Settings};
|
||||
use url::Url;
|
||||
|
||||
#[async_trait]
|
||||
@@ -82,6 +83,25 @@ impl PrivateMessage {
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
pub fn local_url(&self, settings: &Settings) -> LemmyResult<DbUrl> {
|
||||
let domain = settings.get_protocol_and_hostname();
|
||||
Ok(Url::parse(&format!("{domain}/private_message/{}", self.id))?.into())
|
||||
}
|
||||
|
||||
pub async fn update_removed_for_creator(
|
||||
pool: &mut DbPool<'_>,
|
||||
for_creator_id: PersonId,
|
||||
removed: bool,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(private_message::table.filter(private_message::creator_id.eq(for_creator_id)))
|
||||
.set((
|
||||
private_message::removed.eq(removed),
|
||||
private_message::updated.eq(Utc::now()),
|
||||
))
|
||||
.get_results::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -140,6 +160,7 @@ mod tests {
|
||||
))?
|
||||
.into(),
|
||||
local: true,
|
||||
removed: false,
|
||||
};
|
||||
|
||||
let read_private_message = PrivateMessage::read(pool, inserted_private_message.id).await?;
|
||||
|
||||
@@ -25,7 +25,7 @@ impl Crud for Site {
|
||||
}
|
||||
|
||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||
let is_new_site = match &form.actor_id {
|
||||
let is_new_site = match &form.ap_id {
|
||||
Some(id_) => Site::read_from_apub_id(pool, id_).await?.is_none(),
|
||||
None => true,
|
||||
};
|
||||
@@ -34,7 +34,7 @@ impl Crud for Site {
|
||||
// Can't do separate insert/update commands because InsertForm/UpdateForm aren't convertible
|
||||
let site_ = insert_into(site::table)
|
||||
.values(form)
|
||||
.on_conflict(site::actor_id)
|
||||
.on_conflict(site::ap_id)
|
||||
.do_update()
|
||||
.set(form)
|
||||
.get_result::<Self>(conn)
|
||||
@@ -80,7 +80,7 @@ impl Site {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
site::table
|
||||
.filter(site::actor_id.eq(object_id))
|
||||
.filter(site::ap_id.eq(object_id))
|
||||
.first(conn)
|
||||
.await
|
||||
.optional()
|
||||
@@ -97,7 +97,7 @@ impl Site {
|
||||
|
||||
/// Instance actor is at the root path, so we simply need to clear the path and other unnecessary
|
||||
/// parts of the url.
|
||||
pub fn instance_actor_id_from_url(mut url: Url) -> Url {
|
||||
pub fn instance_ap_id_from_url(mut url: Url) -> Url {
|
||||
url.set_fragment(None);
|
||||
url.set_path("");
|
||||
url.set_query(None);
|
||||
|
||||
@@ -23,9 +23,10 @@ pub mod sensitive;
|
||||
pub mod schema;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod aliases {
|
||||
use crate::schema::{community_actions, person};
|
||||
use crate::schema::{community_actions, local_user, person};
|
||||
diesel::alias!(
|
||||
community_actions as creator_community_actions: CreatorCommunityActions,
|
||||
local_user as creator_local_user: CreatorLocalUser,
|
||||
person as person1: Person1,
|
||||
person as person2: Person2,
|
||||
);
|
||||
@@ -66,19 +67,9 @@ pub enum PostSortType {
|
||||
Hot,
|
||||
New,
|
||||
Old,
|
||||
TopDay,
|
||||
TopWeek,
|
||||
TopMonth,
|
||||
TopYear,
|
||||
TopAll,
|
||||
Top,
|
||||
MostComments,
|
||||
NewComments,
|
||||
TopHour,
|
||||
TopSixHour,
|
||||
TopTwelveHour,
|
||||
TopThreeMonths,
|
||||
TopSixMonths,
|
||||
TopNineMonths,
|
||||
Controversial,
|
||||
Scaled,
|
||||
}
|
||||
@@ -341,7 +332,7 @@ pub type Person1AliasAllColumnsTuple = (
|
||||
AliasedField<aliases::Person1, person::banned>,
|
||||
AliasedField<aliases::Person1, person::published>,
|
||||
AliasedField<aliases::Person1, person::updated>,
|
||||
AliasedField<aliases::Person1, person::actor_id>,
|
||||
AliasedField<aliases::Person1, person::ap_id>,
|
||||
AliasedField<aliases::Person1, person::bio>,
|
||||
AliasedField<aliases::Person1, person::local>,
|
||||
AliasedField<aliases::Person1, person::private_key>,
|
||||
|
||||
@@ -179,6 +179,7 @@ diesel::table! {
|
||||
resolver_id -> Nullable<Int4>,
|
||||
published -> Timestamptz,
|
||||
updated -> Nullable<Timestamptz>,
|
||||
violates_instance_rules -> Bool,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +200,7 @@ diesel::table! {
|
||||
deleted -> Bool,
|
||||
nsfw -> Bool,
|
||||
#[max_length = 255]
|
||||
actor_id -> Varchar,
|
||||
ap_id -> Varchar,
|
||||
local -> Bool,
|
||||
private_key -> Nullable<Text>,
|
||||
public_key -> Text,
|
||||
@@ -252,11 +253,11 @@ diesel::table! {
|
||||
users_active_week -> Int8,
|
||||
users_active_month -> Int8,
|
||||
users_active_half_year -> Int8,
|
||||
interactions_month -> Int8,
|
||||
hot_rank -> Float8,
|
||||
subscribers_local -> Int8,
|
||||
report_count -> Int2,
|
||||
unresolved_report_count -> Int2,
|
||||
interactions_month -> Int8,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,6 +351,8 @@ diesel::table! {
|
||||
width -> Int4,
|
||||
height -> Int4,
|
||||
content_type -> Text,
|
||||
#[max_length = 50]
|
||||
blurhash -> Nullable<Varchar>,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,6 +448,7 @@ diesel::table! {
|
||||
comment_upvotes -> FederationModeEnum,
|
||||
comment_downvotes -> FederationModeEnum,
|
||||
disable_donation_dialog -> Bool,
|
||||
default_post_time_range_seconds -> Nullable<Int4>,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,6 +522,7 @@ diesel::table! {
|
||||
auto_mark_fetched_posts_as_read -> Bool,
|
||||
last_donation_notification -> Timestamptz,
|
||||
hide_media -> Bool,
|
||||
default_post_time_range_seconds -> Nullable<Int4>,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -745,7 +750,7 @@ diesel::table! {
|
||||
published -> Timestamptz,
|
||||
updated -> Nullable<Timestamptz>,
|
||||
#[max_length = 255]
|
||||
actor_id -> Varchar,
|
||||
ap_id -> Varchar,
|
||||
bio -> Nullable<Text>,
|
||||
local -> Bool,
|
||||
private_key -> Nullable<Text>,
|
||||
@@ -779,6 +784,7 @@ diesel::table! {
|
||||
post_score -> Int8,
|
||||
comment_count -> Int8,
|
||||
comment_score -> Int8,
|
||||
published -> Timestamptz,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,6 +918,7 @@ diesel::table! {
|
||||
resolver_id -> Nullable<Int4>,
|
||||
published -> Timestamptz,
|
||||
updated -> Nullable<Timestamptz>,
|
||||
violates_instance_rules -> Bool,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -943,6 +950,7 @@ diesel::table! {
|
||||
#[max_length = 255]
|
||||
ap_id -> Varchar,
|
||||
local -> Bool,
|
||||
removed -> Bool,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1046,7 +1054,7 @@ diesel::table! {
|
||||
#[max_length = 150]
|
||||
description -> Nullable<Varchar>,
|
||||
#[max_length = 255]
|
||||
actor_id -> Varchar,
|
||||
ap_id -> Varchar,
|
||||
last_refreshed_at -> Timestamptz,
|
||||
#[max_length = 255]
|
||||
inbox_url -> Varchar,
|
||||
|
||||
@@ -30,6 +30,7 @@ pub struct CommentReport {
|
||||
pub published: DateTime<Utc>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub updated: Option<DateTime<Utc>>,
|
||||
pub violates_instance_rules: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -40,4 +41,5 @@ pub struct CommentReportForm {
|
||||
pub comment_id: CommentId,
|
||||
pub original_comment_text: String,
|
||||
pub reason: String,
|
||||
pub violates_instance_rules: bool,
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ pub struct Community {
|
||||
pub deleted: bool,
|
||||
/// Whether its an NSFW community.
|
||||
pub nsfw: bool,
|
||||
/// The federated actor_id.
|
||||
pub actor_id: DbUrl,
|
||||
/// The federated ap_id.
|
||||
pub ap_id: DbUrl,
|
||||
/// Whether the community is local.
|
||||
pub local: bool,
|
||||
#[serde(skip)]
|
||||
@@ -101,7 +101,7 @@ pub struct CommunityInsertForm {
|
||||
#[new(default)]
|
||||
pub nsfw: Option<bool>,
|
||||
#[new(default)]
|
||||
pub actor_id: Option<DbUrl>,
|
||||
pub ap_id: Option<DbUrl>,
|
||||
#[new(default)]
|
||||
pub local: Option<bool>,
|
||||
#[new(default)]
|
||||
@@ -141,7 +141,7 @@ pub struct CommunityUpdateForm {
|
||||
pub updated: Option<Option<DateTime<Utc>>>,
|
||||
pub deleted: Option<bool>,
|
||||
pub nsfw: Option<bool>,
|
||||
pub actor_id: Option<DbUrl>,
|
||||
pub ap_id: Option<DbUrl>,
|
||||
pub local: Option<bool>,
|
||||
pub public_key: Option<String>,
|
||||
pub private_key: Option<Option<String>>,
|
||||
|
||||
@@ -62,14 +62,17 @@ pub struct ImageDetails {
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub content_type: String,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub blurhash: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = image_details))]
|
||||
pub struct ImageDetailsForm {
|
||||
pub struct ImageDetailsInsertForm {
|
||||
pub link: DbUrl,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub content_type: String,
|
||||
pub blurhash: Option<String>,
|
||||
}
|
||||
|
||||
@@ -86,6 +86,9 @@ pub struct LocalSite {
|
||||
/// If this is true, users will never see the dialog asking to support Lemmy development with
|
||||
/// donations.
|
||||
pub disable_donation_dialog: bool,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
/// A default time range limit to apply to post sorts, in seconds.
|
||||
pub default_post_time_range_seconds: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, derive_new::new)]
|
||||
@@ -147,6 +150,8 @@ pub struct LocalSiteInsertForm {
|
||||
pub comment_downvotes: Option<FederationMode>,
|
||||
#[new(default)]
|
||||
pub disable_donation_dialog: Option<bool>,
|
||||
#[new(default)]
|
||||
pub default_post_time_range_seconds: Option<Option<i32>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@@ -181,4 +186,5 @@ pub struct LocalSiteUpdateForm {
|
||||
pub comment_upvotes: Option<FederationMode>,
|
||||
pub comment_downvotes: Option<FederationMode>,
|
||||
pub disable_donation_dialog: Option<bool>,
|
||||
pub default_post_time_range_seconds: Option<Option<i32>>,
|
||||
}
|
||||
|
||||
@@ -76,6 +76,9 @@ pub struct LocalUser {
|
||||
pub last_donation_notification: DateTime<Utc>,
|
||||
/// Whether to hide posts containing images/videos
|
||||
pub hide_media: bool,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
/// A default time range limit to apply to post sorts, in seconds.
|
||||
pub default_post_time_range_seconds: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, derive_new::new)]
|
||||
@@ -138,6 +141,8 @@ pub struct LocalUserInsertForm {
|
||||
pub last_donation_notification: Option<DateTime<Utc>>,
|
||||
#[new(default)]
|
||||
pub hide_media: Option<bool>,
|
||||
#[new(default)]
|
||||
pub default_post_time_range_seconds: Option<Option<i32>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@@ -172,4 +177,5 @@ pub struct LocalUserUpdateForm {
|
||||
pub auto_mark_fetched_posts_as_read: Option<bool>,
|
||||
pub last_donation_notification: Option<DateTime<Utc>>,
|
||||
pub hide_media: Option<bool>,
|
||||
pub default_post_time_range_seconds: Option<Option<i32>>,
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ pub struct Person {
|
||||
pub published: DateTime<Utc>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub updated: Option<DateTime<Utc>>,
|
||||
/// The federated actor_id.
|
||||
pub actor_id: DbUrl,
|
||||
/// The federated ap_id.
|
||||
pub ap_id: DbUrl,
|
||||
/// An optional bio, in markdown.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub bio: Option<String>,
|
||||
@@ -84,7 +84,7 @@ pub struct PersonInsertForm {
|
||||
#[new(default)]
|
||||
pub updated: Option<DateTime<Utc>>,
|
||||
#[new(default)]
|
||||
pub actor_id: Option<DbUrl>,
|
||||
pub ap_id: Option<DbUrl>,
|
||||
#[new(default)]
|
||||
pub bio: Option<String>,
|
||||
#[new(default)]
|
||||
@@ -115,7 +115,7 @@ pub struct PersonUpdateForm {
|
||||
pub avatar: Option<Option<DbUrl>>,
|
||||
pub banned: Option<bool>,
|
||||
pub updated: Option<Option<DateTime<Utc>>>,
|
||||
pub actor_id: Option<DbUrl>,
|
||||
pub ap_id: Option<DbUrl>,
|
||||
pub bio: Option<Option<String>>,
|
||||
pub local: Option<bool>,
|
||||
pub public_key: Option<String>,
|
||||
|
||||
@@ -37,6 +37,7 @@ pub struct PostReport {
|
||||
pub published: DateTime<Utc>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub updated: Option<DateTime<Utc>>,
|
||||
pub violates_instance_rules: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@@ -49,4 +50,5 @@ pub struct PostReportForm {
|
||||
pub original_post_url: Option<DbUrl>,
|
||||
pub original_post_body: Option<String>,
|
||||
pub reason: String,
|
||||
pub violates_instance_rules: bool,
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ pub struct PrivateMessage {
|
||||
pub updated: Option<DateTime<Utc>>,
|
||||
pub ap_id: DbUrl,
|
||||
pub local: bool,
|
||||
pub removed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, derive_new::new)]
|
||||
@@ -67,4 +68,5 @@ pub struct PrivateMessageUpdateForm {
|
||||
pub updated: Option<Option<DateTime<Utc>>>,
|
||||
pub ap_id: Option<DbUrl>,
|
||||
pub local: Option<bool>,
|
||||
pub removed: Option<bool>,
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ pub struct Site {
|
||||
/// A shorter, one-line description of the site.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub description: Option<String>,
|
||||
/// The federated actor_id.
|
||||
pub actor_id: DbUrl,
|
||||
/// The federated ap_id.
|
||||
pub ap_id: DbUrl,
|
||||
/// The time the site was last refreshed.
|
||||
pub last_refreshed_at: DateTime<Utc>,
|
||||
/// The site inbox
|
||||
@@ -69,7 +69,7 @@ pub struct SiteInsertForm {
|
||||
#[new(default)]
|
||||
pub description: Option<String>,
|
||||
#[new(default)]
|
||||
pub actor_id: Option<DbUrl>,
|
||||
pub ap_id: Option<DbUrl>,
|
||||
#[new(default)]
|
||||
pub last_refreshed_at: Option<DateTime<Utc>>,
|
||||
#[new(default)]
|
||||
@@ -94,7 +94,7 @@ pub struct SiteUpdateForm {
|
||||
pub icon: Option<Option<DbUrl>>,
|
||||
pub banner: Option<Option<DbUrl>>,
|
||||
pub description: Option<Option<String>>,
|
||||
pub actor_id: Option<DbUrl>,
|
||||
pub ap_id: Option<DbUrl>,
|
||||
pub last_refreshed_at: Option<DateTime<Utc>>,
|
||||
pub inbox_url: Option<DbUrl>,
|
||||
pub private_key: Option<Option<String>>,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
pub mod uplete;
|
||||
|
||||
use crate::{newtypes::DbUrl, schema_setup, CommentSortType, PostSortType};
|
||||
use crate::{newtypes::DbUrl, schema_setup};
|
||||
use chrono::TimeDelta;
|
||||
use deadpool::Runtime;
|
||||
use diesel::{
|
||||
dsl,
|
||||
expression::AsExpression,
|
||||
helper_types::AsExprOf,
|
||||
pg::Pg,
|
||||
pg::{data_types::PgInterval, Pg},
|
||||
query_builder::{Query, QueryFragment},
|
||||
query_dsl::methods::LimitDsl,
|
||||
result::{
|
||||
@@ -303,6 +303,16 @@ pub fn diesel_string_update(opt: Option<&str>) -> Option<Option<String>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes an API optional number, and converts it to an optional diesel DB update. Zero means erase.
|
||||
pub fn diesel_opt_number_update(opt: Option<i32>) -> Option<Option<i32>> {
|
||||
match opt {
|
||||
// Zero is an erase
|
||||
Some(0) => Some(None),
|
||||
Some(num) => Some(Some(num)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes an API optional text input, and converts it to an optional diesel DB update (for non
|
||||
/// nullable properties).
|
||||
pub fn diesel_required_string_update(opt: Option<&str>) -> Option<String> {
|
||||
@@ -495,18 +505,6 @@ pub fn build_db_pool_for_tests() -> ActualDbPool {
|
||||
build_db_pool().expect("db pool missing")
|
||||
}
|
||||
|
||||
pub fn post_to_comment_sort_type(sort: PostSortType) -> CommentSortType {
|
||||
use PostSortType::*;
|
||||
match sort {
|
||||
Active | Hot | Scaled => CommentSortType::Hot,
|
||||
New | NewComments | MostComments => CommentSortType::New,
|
||||
Old => CommentSortType::Old,
|
||||
Controversial => CommentSortType::Controversial,
|
||||
TopHour | TopSixHour | TopTwelveHour | TopDay | TopAll | TopWeek | TopYear | TopMonth
|
||||
| TopThreeMonths | TopSixMonths | TopNineMonths => CommentSortType::Top,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
static EMAIL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
|
||||
@@ -557,6 +555,10 @@ pub fn now() -> AsExprOf<diesel::dsl::now, diesel::sql_types::Timestamptz> {
|
||||
diesel::dsl::now.into_sql::<Timestamptz>()
|
||||
}
|
||||
|
||||
pub fn seconds_to_pg_interval(seconds: i32) -> PgInterval {
|
||||
PgInterval::from_microseconds(i64::from(seconds) * 1_000_000)
|
||||
}
|
||||
|
||||
/// Trait alias for a type that can be converted to an SQL tuple using `IntoSql::into_sql`
|
||||
pub trait AsRecord: Expression + AsExpression<sql_types::Record<Self::SqlType>>
|
||||
where
|
||||
|
||||
@@ -48,4 +48,4 @@ serial_test = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
url = { workspace = true }
|
||||
test-context = "0.3.0"
|
||||
test-context = "0.4.1"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user