mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-27 03:41:02 +00:00
Merge remote-tracking branch 'upstream/main' into defaults
This commit is contained in:
commit
374b0f6f9e
169 changed files with 2693 additions and 1653 deletions
|
@ -1,5 +1,5 @@
|
||||||
tab_spaces = 2
|
tab_spaces = 2
|
||||||
edition="2021"
|
edition = "2021"
|
||||||
imports_layout="HorizontalVertical"
|
imports_layout = "HorizontalVertical"
|
||||||
imports_granularity="Crate"
|
imports_granularity = "Crate"
|
||||||
group_imports="One"
|
group_imports = "One"
|
||||||
|
|
147
.woodpecker.yml
147
.woodpecker.yml
|
@ -18,7 +18,6 @@ pipeline:
|
||||||
image: alpine:3
|
image: alpine:3
|
||||||
commands:
|
commands:
|
||||||
- apk add git
|
- apk add git
|
||||||
#- git fetch --tags
|
|
||||||
- git submodule init
|
- git submodule init
|
||||||
- git submodule update
|
- git submodule update
|
||||||
|
|
||||||
|
@ -27,7 +26,34 @@ pipeline:
|
||||||
commands:
|
commands:
|
||||||
- prettier -c . '!**/volumes' '!**/dist' '!target' '!**/translations'
|
- prettier -c . '!**/volumes' '!**/dist' '!target' '!**/translations'
|
||||||
|
|
||||||
# use minimum supported rust version for most steps
|
restore-cache:
|
||||||
|
image: meltwater/drone-cache:v1
|
||||||
|
pull: true
|
||||||
|
settings:
|
||||||
|
restore: true
|
||||||
|
endpoint:
|
||||||
|
from_secret: MINIO_ENDPOINT
|
||||||
|
access-key:
|
||||||
|
from_secret: MINIO_WRITE_USER
|
||||||
|
secret-key:
|
||||||
|
from_secret: MINIO_WRITE_PASSWORD
|
||||||
|
bucket:
|
||||||
|
from_secret: MINIO_BUCKET
|
||||||
|
region: us-east-1
|
||||||
|
cache_key: "rust-cache"
|
||||||
|
path-style: true
|
||||||
|
mount:
|
||||||
|
- ".cargo"
|
||||||
|
- "target"
|
||||||
|
- "api_tests/node_modules"
|
||||||
|
secrets:
|
||||||
|
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
|
||||||
|
|
||||||
|
toml_fmt:
|
||||||
|
image: tamasfe/taplo:0.8.1
|
||||||
|
commands:
|
||||||
|
- taplo format --check
|
||||||
|
|
||||||
cargo_fmt:
|
cargo_fmt:
|
||||||
image: *muslrust_image
|
image: *muslrust_image
|
||||||
environment:
|
environment:
|
||||||
|
@ -35,42 +61,15 @@ pipeline:
|
||||||
CARGO_HOME: .cargo
|
CARGO_HOME: .cargo
|
||||||
commands:
|
commands:
|
||||||
# need make existing toolchain available
|
# need make existing toolchain available
|
||||||
- cp ~/.cargo . -r
|
- cp -n ~/.cargo . -r
|
||||||
- rustup toolchain install nightly
|
- rustup toolchain install nightly-2023-07-10
|
||||||
- rustup component add rustfmt --toolchain nightly
|
- rustup component add rustfmt --toolchain nightly-2023-07-10
|
||||||
- cargo +nightly fmt -- --check
|
- cargo +nightly-2023-07-10 fmt -- --check
|
||||||
# when:
|
|
||||||
# platform: linux/amd64
|
|
||||||
|
|
||||||
cargo_clippy:
|
|
||||||
image: *muslrust_image
|
|
||||||
environment:
|
|
||||||
CARGO_HOME: .cargo
|
|
||||||
commands:
|
|
||||||
# latest rust for clippy to get extra checks
|
|
||||||
# when adding new clippy lints, make sure to also add them in scripts/fix-clippy.sh
|
|
||||||
- rustup component add clippy
|
|
||||||
- cargo clippy --workspace --tests --all-targets --features console --
|
|
||||||
-D warnings -D deprecated -D clippy::perf -D clippy::complexity
|
|
||||||
-D clippy::style -D clippy::correctness -D clippy::suspicious
|
|
||||||
-D clippy::dbg_macro -D clippy::inefficient_to_string
|
|
||||||
-D clippy::items-after-statements -D clippy::implicit_clone
|
|
||||||
-D clippy::cast_lossless -D clippy::manual_string_new
|
|
||||||
-D clippy::redundant_closure_for_method_calls
|
|
||||||
-D clippy::unused_self
|
|
||||||
-A clippy::uninlined_format_args
|
|
||||||
-D clippy::get_first
|
|
||||||
-D clippy::explicit_into_iter_loop
|
|
||||||
-D clippy::explicit_iter_loop
|
|
||||||
-D clippy::needless_collect
|
|
||||||
- cargo clippy --workspace --features console --
|
|
||||||
-D clippy::unwrap_used
|
|
||||||
-D clippy::indexing_slicing
|
|
||||||
# when:
|
# when:
|
||||||
# platform: linux/amd64
|
# platform: linux/amd64
|
||||||
|
|
||||||
# make sure api builds with default features (used by other crates relying on lemmy api)
|
# make sure api builds with default features (used by other crates relying on lemmy api)
|
||||||
cargo_check:
|
check_api_common_default_features:
|
||||||
image: *muslrust_image
|
image: *muslrust_image
|
||||||
environment:
|
environment:
|
||||||
CARGO_HOME: .cargo
|
CARGO_HOME: .cargo
|
||||||
|
@ -88,6 +87,14 @@ pipeline:
|
||||||
# when:
|
# when:
|
||||||
# platform: linux/amd64
|
# platform: linux/amd64
|
||||||
|
|
||||||
|
lemmy_api_common_works_with_wasm:
|
||||||
|
image: *muslrust_image
|
||||||
|
environment:
|
||||||
|
CARGO_HOME: .cargo
|
||||||
|
commands:
|
||||||
|
- "rustup target add wasm32-unknown-unknown"
|
||||||
|
- "cargo check --target wasm32-unknown-unknown -p lemmy_api_common"
|
||||||
|
|
||||||
check_defaults_hjson_updated:
|
check_defaults_hjson_updated:
|
||||||
image: *muslrust_image
|
image: *muslrust_image
|
||||||
environment:
|
environment:
|
||||||
|
@ -109,12 +116,45 @@ pipeline:
|
||||||
- diesel print-schema --config-file=diesel.toml > tmp.schema
|
- diesel print-schema --config-file=diesel.toml > tmp.schema
|
||||||
- diff tmp.schema crates/db_schema/src/schema.rs
|
- diff tmp.schema crates/db_schema/src/schema.rs
|
||||||
|
|
||||||
|
check_diesel_migration_revertable:
|
||||||
|
image: willsquire/diesel-cli
|
||||||
|
environment:
|
||||||
|
CARGO_HOME: .cargo
|
||||||
|
DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
||||||
|
commands:
|
||||||
|
- diesel migration run
|
||||||
|
- diesel migration redo
|
||||||
|
|
||||||
|
cargo_clippy:
|
||||||
|
image: *muslrust_image
|
||||||
|
environment:
|
||||||
|
CARGO_HOME: .cargo
|
||||||
|
commands:
|
||||||
|
# when adding new clippy lints, make sure to also add them in scripts/fix-clippy.sh
|
||||||
|
- rustup component add clippy
|
||||||
|
- cargo clippy --workspace --tests --all-targets --features console --
|
||||||
|
-D warnings -D deprecated -D clippy::perf -D clippy::complexity
|
||||||
|
-D clippy::style -D clippy::correctness -D clippy::suspicious
|
||||||
|
-D clippy::dbg_macro -D clippy::inefficient_to_string
|
||||||
|
-D clippy::items-after-statements -D clippy::implicit_clone
|
||||||
|
-D clippy::cast_lossless -D clippy::manual_string_new
|
||||||
|
-D clippy::redundant_closure_for_method_calls
|
||||||
|
-D clippy::unused_self
|
||||||
|
-A clippy::uninlined_format_args
|
||||||
|
-D clippy::get_first
|
||||||
|
-D clippy::explicit_into_iter_loop
|
||||||
|
-D clippy::explicit_iter_loop
|
||||||
|
-D clippy::needless_collect
|
||||||
|
-D clippy::unwrap_used
|
||||||
|
-D clippy::indexing_slicing
|
||||||
|
# when:
|
||||||
|
# platform: linux/amd64
|
||||||
|
|
||||||
cargo_test:
|
cargo_test:
|
||||||
image: *muslrust_image
|
image: *muslrust_image
|
||||||
environment:
|
environment:
|
||||||
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
||||||
RUST_BACKTRACE: "1"
|
RUST_BACKTRACE: "1"
|
||||||
RUST_TEST_THREADS: "1"
|
|
||||||
CARGO_HOME: .cargo
|
CARGO_HOME: .cargo
|
||||||
commands:
|
commands:
|
||||||
- export LEMMY_CONFIG_LOCATION=../../config/config.hjson
|
- export LEMMY_CONFIG_LOCATION=../../config/config.hjson
|
||||||
|
@ -146,6 +186,29 @@ pipeline:
|
||||||
# when:
|
# when:
|
||||||
# platform: linux/amd64
|
# platform: linux/amd64
|
||||||
|
|
||||||
|
rebuild-cache:
|
||||||
|
image: meltwater/drone-cache:v1
|
||||||
|
pull: true
|
||||||
|
settings:
|
||||||
|
rebuild: true
|
||||||
|
endpoint:
|
||||||
|
from_secret: MINIO_ENDPOINT
|
||||||
|
access-key:
|
||||||
|
from_secret: MINIO_WRITE_USER
|
||||||
|
secret-key:
|
||||||
|
from_secret: MINIO_WRITE_PASSWORD
|
||||||
|
bucket:
|
||||||
|
from_secret: MINIO_BUCKET
|
||||||
|
cache_key: "rust-cache"
|
||||||
|
region: us-east-1
|
||||||
|
path-style: true
|
||||||
|
mount:
|
||||||
|
- ".cargo"
|
||||||
|
- "target"
|
||||||
|
- "api_tests/node_modules"
|
||||||
|
secrets:
|
||||||
|
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
|
||||||
|
|
||||||
publish_release_docker:
|
publish_release_docker:
|
||||||
image: woodpeckerci/plugin-docker-buildx
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
secrets: [docker_username, docker_password]
|
secrets: [docker_username, docker_password]
|
||||||
|
@ -172,20 +235,6 @@ pipeline:
|
||||||
when:
|
when:
|
||||||
event: cron
|
event: cron
|
||||||
|
|
||||||
# using https://github.com/pksunkara/cargo-workspaces
|
|
||||||
publish_to_crates_io:
|
|
||||||
image: *muslrust_image
|
|
||||||
commands:
|
|
||||||
- 'echo "pub const VERSION: &str = \"$(git describe --tag)\";" > "crates/utils/src/version.rs"'
|
|
||||||
- cargo install cargo-workspaces
|
|
||||||
- cp -r migrations crates/db_schema/
|
|
||||||
- cargo login "$CARGO_API_TOKEN"
|
|
||||||
- cargo workspaces publish --from-git --allow-dirty --no-verify --allow-branch "${CI_COMMIT_TAG}" --yes custom "${CI_COMMIT_TAG}"
|
|
||||||
secrets: [cargo_api_token]
|
|
||||||
when:
|
|
||||||
event: tag
|
|
||||||
#platform: linux/amd64
|
|
||||||
|
|
||||||
notify_on_failure:
|
notify_on_failure:
|
||||||
image: alpine:3
|
image: alpine:3
|
||||||
commands:
|
commands:
|
||||||
|
|
56
Cargo.lock
generated
56
Cargo.lock
generated
|
@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "activitypub_federation"
|
name = "activitypub_federation"
|
||||||
version = "0.4.5"
|
version = "0.4.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ab3ac148d9c0b4163a6d41040c17de7558a42224b9ecbd4e8f033aef6c254d9"
|
checksum = "4e6e7fefba6602240fcf612931b70640ad1e249dff833551ebc218f1c96a4193"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitystreams-kinds",
|
"activitystreams-kinds",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -22,7 +22,6 @@ dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
"derive_builder",
|
"derive_builder",
|
||||||
"displaydoc",
|
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"enum_delegate",
|
"enum_delegate",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
@ -346,7 +345,7 @@ version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.8",
|
"getrandom 0.2.10",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
@ -358,7 +357,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom 0.2.8",
|
"getrandom 0.2.10",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
@ -678,7 +677,7 @@ checksum = "28d1c9c15093eb224f0baa400f38fcd713fc1391a6f1c389d886beef146d60a3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.2",
|
"base64 0.21.2",
|
||||||
"blowfish",
|
"blowfish",
|
||||||
"getrandom 0.2.8",
|
"getrandom 0.2.10",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
@ -1548,17 +1547,6 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "displaydoc"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.25",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dlv-list"
|
name = "dlv-list"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -2045,9 +2033,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.8"
|
version = "0.2.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
@ -2397,17 +2385,6 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "idna"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
|
||||||
dependencies = [
|
|
||||||
"matches",
|
|
||||||
"unicode-bidi",
|
|
||||||
"unicode-normalization",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -2644,16 +2621,19 @@ dependencies = [
|
||||||
name = "lemmy_api_common"
|
name = "lemmy_api_common"
|
||||||
version = "0.18.1"
|
version = "0.18.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"activitypub_federation",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
"encoding",
|
"encoding",
|
||||||
"futures",
|
"futures",
|
||||||
|
"getrandom 0.2.10",
|
||||||
"lemmy_db_schema",
|
"lemmy_db_schema",
|
||||||
"lemmy_db_views",
|
"lemmy_db_views",
|
||||||
"lemmy_db_views_actor",
|
"lemmy_db_views_actor",
|
||||||
"lemmy_db_views_moderator",
|
"lemmy_db_views_moderator",
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
|
"once_cell",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
@ -2779,7 +2759,6 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"typed-builder",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2792,7 +2771,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"typed-builder",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3162,12 +3140,6 @@ dependencies = [
|
||||||
"regex-automata 0.1.10",
|
"regex-automata 0.1.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matches"
|
|
||||||
version = "0.1.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchit"
|
name = "matchit"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -3195,7 +3167,7 @@ version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5736ba45bbac8f7ccc99a897f88ce85e508a18baec973a040f2514e6cdbff0d2"
|
checksum = "5736ba45bbac8f7ccc99a897f88ce85e508a18baec973a040f2514e6cdbff0d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"idna 0.2.3",
|
"idna 0.3.0",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
@ -4271,7 +4243,7 @@ version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.8",
|
"getrandom 0.2.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4437,7 +4409,7 @@ checksum = "1b97ad83c2fc18113346b7158d79732242002427c30f620fa817c1f32901e0a8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"getrandom 0.2.8",
|
"getrandom 0.2.10",
|
||||||
"matchit 0.7.0",
|
"matchit 0.7.0",
|
||||||
"opentelemetry 0.16.0",
|
"opentelemetry 0.16.0",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
@ -5915,7 +5887,7 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be"
|
checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.8",
|
"getrandom 0.2.10",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
53
Cargo.toml
53
Cargo.toml
|
@ -24,25 +24,36 @@ doctest = false
|
||||||
debug = 0
|
debug = 0
|
||||||
lto = "thin"
|
lto = "thin"
|
||||||
|
|
||||||
|
# This profile significantly speeds up build time. If debug info is needed you can comment the line
|
||||||
|
# out temporarily, but make sure to leave this in the main branch.
|
||||||
|
[profile.dev]
|
||||||
|
debug = 0
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
embed-pictrs = ["pict-rs"]
|
embed-pictrs = ["pict-rs"]
|
||||||
console = ["console-subscriber", "opentelemetry", "opentelemetry-otlp", "tracing-opentelemetry", "reqwest-tracing/opentelemetry_0_16"]
|
console = [
|
||||||
|
"console-subscriber",
|
||||||
|
"opentelemetry",
|
||||||
|
"opentelemetry-otlp",
|
||||||
|
"tracing-opentelemetry",
|
||||||
|
"reqwest-tracing/opentelemetry_0_16",
|
||||||
|
]
|
||||||
json-log = ["tracing-subscriber/json"]
|
json-log = ["tracing-subscriber/json"]
|
||||||
prometheus-metrics = ["prometheus", "actix-web-prom"]
|
prometheus-metrics = ["prometheus", "actix-web-prom"]
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"crates/api",
|
"crates/api",
|
||||||
"crates/api_crud",
|
"crates/api_crud",
|
||||||
"crates/api_common",
|
"crates/api_common",
|
||||||
"crates/apub",
|
"crates/apub",
|
||||||
"crates/utils",
|
"crates/utils",
|
||||||
"crates/db_schema",
|
"crates/db_schema",
|
||||||
"crates/db_views",
|
"crates/db_views",
|
||||||
"crates/db_views_actor",
|
"crates/db_views_actor",
|
||||||
"crates/db_views_actor",
|
"crates/db_views_actor",
|
||||||
"crates/routes"
|
"crates/routes",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
@ -56,13 +67,21 @@ lemmy_routes = { version = "=0.18.1", path = "./crates/routes" }
|
||||||
lemmy_db_views = { version = "=0.18.1", path = "./crates/db_views" }
|
lemmy_db_views = { version = "=0.18.1", path = "./crates/db_views" }
|
||||||
lemmy_db_views_actor = { version = "=0.18.1", path = "./crates/db_views_actor" }
|
lemmy_db_views_actor = { version = "=0.18.1", path = "./crates/db_views_actor" }
|
||||||
lemmy_db_views_moderator = { version = "=0.18.1", path = "./crates/db_views_moderator" }
|
lemmy_db_views_moderator = { version = "=0.18.1", path = "./crates/db_views_moderator" }
|
||||||
activitypub_federation = { version = "0.4.5", default-features = false, features = ["actix-web"] }
|
activitypub_federation = { version = "0.4.6", default-features = false, features = [
|
||||||
|
"actix-web",
|
||||||
|
] }
|
||||||
diesel = "2.1.0"
|
diesel = "2.1.0"
|
||||||
diesel_migrations = "2.1.0"
|
diesel_migrations = "2.1.0"
|
||||||
diesel-async = "0.3.1"
|
diesel-async = "0.3.1"
|
||||||
serde = { version = "1.0.167", features = ["derive"] }
|
serde = { version = "1.0.167", features = ["derive"] }
|
||||||
serde_with = "3.0.0"
|
serde_with = "3.0.0"
|
||||||
actix-web = { version = "4.3.1", default-features = false, features = ["macros", "rustls", "compress-brotli", "compress-gzip", "compress-zstd"] }
|
actix-web = { version = "4.3.1", default-features = false, features = [
|
||||||
|
"macros",
|
||||||
|
"rustls",
|
||||||
|
"compress-brotli",
|
||||||
|
"compress-gzip",
|
||||||
|
"compress-zstd",
|
||||||
|
] }
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
tracing-actix-web = { version = "0.7.5", default-features = false }
|
tracing-actix-web = { version = "0.7.5", default-features = false }
|
||||||
tracing-error = "0.2.0"
|
tracing-error = "0.2.0"
|
||||||
|
@ -82,7 +101,9 @@ base64 = "0.21.2"
|
||||||
uuid = { version = "1.4.0", features = ["serde", "v4"] }
|
uuid = { version = "1.4.0", features = ["serde", "v4"] }
|
||||||
async-trait = "0.1.71"
|
async-trait = "0.1.71"
|
||||||
captcha = "0.0.9"
|
captcha = "0.0.9"
|
||||||
anyhow = { version = "1.0.71", features = ["backtrace"] } # backtrace is on by default on nightly, but not stable rust
|
anyhow = { version = "1.0.71", features = [
|
||||||
|
"backtrace",
|
||||||
|
] } # backtrace is on by default on nightly, but not stable rust
|
||||||
diesel_ltree = "0.3.0"
|
diesel_ltree = "0.3.0"
|
||||||
typed-builder = "0.15.0"
|
typed-builder = "0.15.0"
|
||||||
serial_test = "2.0.0"
|
serial_test = "2.0.0"
|
||||||
|
@ -91,7 +112,7 @@ sha2 = "0.10.7"
|
||||||
regex = "1.9.0"
|
regex = "1.9.0"
|
||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
diesel-derive-newtype = "2.1.0"
|
diesel-derive-newtype = "2.1.0"
|
||||||
diesel-derive-enum = {version = "2.1.0", features = ["postgres"] }
|
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
|
||||||
strum = "0.25.0"
|
strum = "0.25.0"
|
||||||
strum_macros = "0.25.1"
|
strum_macros = "0.25.1"
|
||||||
itertools = "0.11.0"
|
itertools = "0.11.0"
|
||||||
|
@ -103,7 +124,7 @@ rand = "0.8.5"
|
||||||
opentelemetry = { version = "0.19.0", features = ["rt-tokio"] }
|
opentelemetry = { version = "0.19.0", features = ["rt-tokio"] }
|
||||||
tracing-opentelemetry = { version = "0.19.0" }
|
tracing-opentelemetry = { version = "0.19.0" }
|
||||||
ts-rs = { version = "6.2", features = ["serde-compat", "chrono-impl"] }
|
ts-rs = { version = "6.2", features = ["serde-compat", "chrono-impl"] }
|
||||||
rustls = { version ="0.21.3", features = ["dangerous_configuration"]}
|
rustls = { version = "0.21.3", features = ["dangerous_configuration"] }
|
||||||
futures-util = "0.3.28"
|
futures-util = "0.3.28"
|
||||||
tokio-postgres = "0.7.8"
|
tokio-postgres = "0.7.8"
|
||||||
tokio-postgres-rustls = "0.10.0"
|
tokio-postgres-rustls = "0.10.0"
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
<a href="readmes/README.es.md">Español</a> |
|
<a href="readmes/README.es.md">Español</a> |
|
||||||
<a href="readmes/README.ru.md">Русский</a> |
|
<a href="readmes/README.ru.md">Русский</a> |
|
||||||
<a href="readmes/README.zh.hans.md">汉语</a> |
|
<a href="readmes/README.zh.hans.md">汉语</a> |
|
||||||
<a href="readmes/README.zh.hant.md">漢語</a>
|
<a href="readmes/README.zh.hant.md">漢語</a> |
|
||||||
|
<a href="readmes/README.ja.md">日本語</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
|
|
@ -470,7 +470,7 @@ The installation instructions have been slightly updated. However there are no b
|
||||||
|
|
||||||
Follow the upgrade instructions for [ansible](https://github.com/LemmyNet/lemmy-ansible#upgrading) or [docker](https://join-lemmy.org/docs/en/administration/install_docker.html#updating).
|
Follow the upgrade instructions for [ansible](https://github.com/LemmyNet/lemmy-ansible#upgrading) or [docker](https://join-lemmy.org/docs/en/administration/install_docker.html#updating).
|
||||||
|
|
||||||
If you need help with the upgrade, you can ask in our [support forum](https://lemmy.ml/c/lemmy_support) or on the [Matrix Chat](https://matrix.to/#/!OwmdVYiZSXrXbtCNLw:matrix.org).
|
If you need help with the upgrade, you can ask in our [support forum](https://lemmy.ml/c/lemmy_support) or on the [Matrix Chat](https://matrix.to/#/#lemmy-admin-support-topics:discuss.online).
|
||||||
|
|
||||||
## Support development
|
## Support development
|
||||||
|
|
||||||
|
@ -1016,8 +1016,8 @@ Next, **manually edit** your [lemmy.hjson](https://github.com/LemmyNet/lemmy/blo
|
||||||
- `pictrs_url` is removed, and the pictrs config is now a block. If using docker, it should look like:
|
- `pictrs_url` is removed, and the pictrs config is now a block. If using docker, it should look like:
|
||||||
```
|
```
|
||||||
pictrs: {
|
pictrs: {
|
||||||
url: "http://pictrs:8080/"
|
url: "http://pictrs:8080/"
|
||||||
# api_key: "API_KEY"
|
api_key: "{{ postgres_password }}"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
- The `rate_limit`, `federation`, `captcha`, and `slur_filter` blocks should be removed, as they are now in the database, and can be updated through the UI.
|
- The `rate_limit`, `federation`, `captcha`, and `slur_filter` blocks should be removed, as they are now in the database, and can be updated through the UI.
|
||||||
|
@ -1048,7 +1048,7 @@ _Note_: On production databases with thousands of comments, this upgrade **takes
|
||||||
|
|
||||||
_Note_: If you have any issues upgrading, you can restore your old database using the [backup and restore instructions here](https://join-lemmy.org/docs/en/administration/backup_and_restore.html).
|
_Note_: If you have any issues upgrading, you can restore your old database using the [backup and restore instructions here](https://join-lemmy.org/docs/en/administration/backup_and_restore.html).
|
||||||
|
|
||||||
If you need help with the upgrade, you can ask in our [support forum](https://lemmy.ml/c/lemmy_support) or on the [Matrix Chat](https://matrix.to/#/!BZVTUuEiNmRcbFeLeI:matrix.org).
|
If you need help with the upgrade, you can ask in our [support forum](https://lemmy.ml/c/lemmy_support) or on the [Matrix Chat](https://matrix.to/#/#lemmy-admin-support-topics:discuss.online).
|
||||||
|
|
||||||
## Support development
|
## Support development
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
"eslint": "^8.40.0",
|
"eslint": "^8.40.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"lemmy-js-client": "0.17.2-rc.13",
|
"lemmy-js-client": "0.18.3-rc.3",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
"typescript": "^5.0.4"
|
"typescript": "^5.0.4"
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
# IMPORTANT NOTE: this script does not use the normal LEMMY_DATABASE_URL format
|
||||||
|
# it is expected that this script is called by run-federation-test.sh script.
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
export RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug"
|
export RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug"
|
||||||
|
|
||||||
for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do
|
for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do
|
||||||
|
echo "DB URL: ${LEMMY_DATABASE_URL} INSTANCE: $INSTANCE"
|
||||||
psql "${LEMMY_DATABASE_URL}/lemmy" -c "DROP DATABASE IF EXISTS $INSTANCE"
|
psql "${LEMMY_DATABASE_URL}/lemmy" -c "DROP DATABASE IF EXISTS $INSTANCE"
|
||||||
|
echo "create database"
|
||||||
psql "${LEMMY_DATABASE_URL}/lemmy" -c "CREATE DATABASE $INSTANCE"
|
psql "${LEMMY_DATABASE_URL}/lemmy" -c "CREATE DATABASE $INSTANCE"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -26,6 +30,7 @@ else
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "killall existing lemmy_server processes"
|
||||||
killall lemmy_server || true
|
killall lemmy_server || true
|
||||||
|
|
||||||
echo "$PWD"
|
echo "$PWD"
|
||||||
|
@ -59,7 +64,12 @@ target/lemmy_server >/tmp/lemmy_epsilon.out 2>&1 &
|
||||||
|
|
||||||
echo "wait for all instances to start"
|
echo "wait for all instances to start"
|
||||||
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-alpha:8541/api/v3/site')" != "200" ]]; do sleep 1; done
|
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-alpha:8541/api/v3/site')" != "200" ]]; do sleep 1; done
|
||||||
|
echo "alpha started"
|
||||||
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-beta:8551/api/v3/site')" != "200" ]]; do sleep 1; done
|
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-beta:8551/api/v3/site')" != "200" ]]; do sleep 1; done
|
||||||
|
echo "beta started"
|
||||||
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-gamma:8561/api/v3/site')" != "200" ]]; do sleep 1; done
|
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-gamma:8561/api/v3/site')" != "200" ]]; do sleep 1; done
|
||||||
|
echo "gamma started"
|
||||||
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-delta:8571/api/v3/site')" != "200" ]]; do sleep 1; done
|
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-delta:8571/api/v3/site')" != "200" ]]; do sleep 1; done
|
||||||
|
echo "delta started"
|
||||||
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-epsilon:8581/api/v3/site')" != "200" ]]; do sleep 1; done
|
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-epsilon:8581/api/v3/site')" != "200" ]]; do sleep 1; done
|
||||||
|
echo "epsilon started. All started"
|
||||||
|
|
|
@ -29,6 +29,7 @@ import {
|
||||||
getComments,
|
getComments,
|
||||||
getCommentParentId,
|
getCommentParentId,
|
||||||
resolveCommunity,
|
resolveCommunity,
|
||||||
|
getPersonDetails,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { CommentView } from "lemmy-js-client/dist/types/CommentView";
|
import { CommentView } from "lemmy-js-client/dist/types/CommentView";
|
||||||
|
|
||||||
|
@ -82,8 +83,7 @@ test("Create a comment", async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Create a comment in a non-existent post", async () => {
|
test("Create a comment in a non-existent post", async () => {
|
||||||
let commentRes = (await createComment(alpha, -1)) as any;
|
await expect(createComment(alpha, -1)).rejects.toBe("couldnt_find_post");
|
||||||
expect(commentRes.error).toBe("couldnt_find_post");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Update a comment", async () => {
|
test("Update a comment", async () => {
|
||||||
|
@ -122,11 +122,9 @@ test("Delete a comment", async () => {
|
||||||
expect(deleteCommentRes.comment_view.comment.deleted).toBe(true);
|
expect(deleteCommentRes.comment_view.comment.deleted).toBe(true);
|
||||||
|
|
||||||
// Make sure that comment is undefined on beta
|
// Make sure that comment is undefined on beta
|
||||||
let betaCommentRes = (await resolveComment(
|
await expect(
|
||||||
beta,
|
resolveComment(beta, commentRes.comment_view.comment),
|
||||||
commentRes.comment_view.comment,
|
).rejects.toBe("couldnt_find_object");
|
||||||
)) as any;
|
|
||||||
expect(betaCommentRes.error).toBe("couldnt_find_object");
|
|
||||||
|
|
||||||
let undeleteCommentRes = await deleteComment(
|
let undeleteCommentRes = await deleteComment(
|
||||||
alpha,
|
alpha,
|
||||||
|
@ -160,9 +158,9 @@ test("Remove a comment from admin and community on the same instance", async ()
|
||||||
expect(removeCommentRes.comment_view.comment.removed).toBe(true);
|
expect(removeCommentRes.comment_view.comment.removed).toBe(true);
|
||||||
|
|
||||||
// Make sure that comment is removed on alpha (it gets pushed since an admin from beta removed it)
|
// Make sure that comment is removed on alpha (it gets pushed since an admin from beta removed it)
|
||||||
let refetchedPostComments = await getComments(
|
let refetchedPostComments = await getPersonDetails(
|
||||||
alpha,
|
alpha,
|
||||||
postRes.post_view.post.id,
|
commentRes.comment_view.comment.creator_id,
|
||||||
);
|
);
|
||||||
expect(refetchedPostComments.comments[0].comment.removed).toBe(true);
|
expect(refetchedPostComments.comments[0].comment.removed).toBe(true);
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,9 @@ test("Create community", async () => {
|
||||||
|
|
||||||
// A dupe check
|
// A dupe check
|
||||||
let prevName = communityRes.community_view.community.name;
|
let prevName = communityRes.community_view.community.name;
|
||||||
let communityRes2: any = await createCommunity(alpha, prevName);
|
await expect(createCommunity(alpha, prevName)).rejects.toBe(
|
||||||
expect(communityRes2["error"]).toBe("community_already_exists");
|
"community_already_exists",
|
||||||
|
);
|
||||||
|
|
||||||
// Cache the community on beta, make sure it has the other fields
|
// Cache the community on beta, make sure it has the other fields
|
||||||
let searchShort = `!${prevName}@lemmy-alpha:8541`;
|
let searchShort = `!${prevName}@lemmy-alpha:8541`;
|
||||||
|
|
|
@ -88,17 +88,18 @@ test("Create a post", async () => {
|
||||||
assertPostFederation(betaPost, postRes.post_view);
|
assertPostFederation(betaPost, postRes.post_view);
|
||||||
|
|
||||||
// Delta only follows beta, so it should not see an alpha ap_id
|
// Delta only follows beta, so it should not see an alpha ap_id
|
||||||
let deltaPost = (await resolvePost(delta, postRes.post_view.post)).post;
|
await expect(resolvePost(delta, postRes.post_view.post)).rejects.toBe(
|
||||||
expect(deltaPost).toBeUndefined();
|
"couldnt_find_object",
|
||||||
|
);
|
||||||
|
|
||||||
// Epsilon has alpha blocked, it should not see the alpha post
|
// Epsilon has alpha blocked, it should not see the alpha post
|
||||||
let epsilonPost = (await resolvePost(epsilon, postRes.post_view.post)).post;
|
await expect(resolvePost(epsilon, postRes.post_view.post)).rejects.toBe(
|
||||||
expect(epsilonPost).toBeUndefined();
|
"couldnt_find_object",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Create a post in a non-existent community", async () => {
|
test("Create a post in a non-existent community", async () => {
|
||||||
let postRes = (await createPost(alpha, -2)) as any;
|
await expect(createPost(alpha, -2)).rejects.toBe("couldnt_find_community");
|
||||||
expect(postRes.error).toBe("couldnt_find_community");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Unlike a post", async () => {
|
test("Unlike a post", async () => {
|
||||||
|
@ -145,8 +146,9 @@ test("Update a post", async () => {
|
||||||
assertPostFederation(betaPost, updatedPost.post_view);
|
assertPostFederation(betaPost, updatedPost.post_view);
|
||||||
|
|
||||||
// Make sure lemmy beta cannot update the post
|
// Make sure lemmy beta cannot update the post
|
||||||
let updatedPostBeta = (await editPost(beta, betaPost.post)) as any;
|
await expect(editPost(beta, betaPost.post)).rejects.toBe(
|
||||||
expect(updatedPostBeta.error).toBe("no_post_edit_allowed");
|
"no_post_edit_allowed",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Sticky a post", async () => {
|
test("Sticky a post", async () => {
|
||||||
|
@ -210,8 +212,7 @@ test("Lock a post", async () => {
|
||||||
expect(alphaPost1.post.locked).toBe(true);
|
expect(alphaPost1.post.locked).toBe(true);
|
||||||
|
|
||||||
// Try to make a new comment there, on alpha
|
// Try to make a new comment there, on alpha
|
||||||
let comment: any = await createComment(alpha, alphaPost1.post.id);
|
await expect(createComment(alpha, alphaPost1.post.id)).rejects.toBe("locked");
|
||||||
expect(comment["error"]).toBe("locked");
|
|
||||||
|
|
||||||
// Unlock a post
|
// Unlock a post
|
||||||
let unlockedPost = await lockPost(beta, false, betaPost1.post);
|
let unlockedPost = await lockPost(beta, false, betaPost1.post);
|
||||||
|
@ -242,9 +243,10 @@ test("Delete a post", async () => {
|
||||||
expect(deletedPost.post_view.post.name).toBe(postRes.post_view.post.name);
|
expect(deletedPost.post_view.post.name).toBe(postRes.post_view.post.name);
|
||||||
|
|
||||||
// Make sure lemmy beta sees post is deleted
|
// Make sure lemmy beta sees post is deleted
|
||||||
let betaPost = (await resolvePost(beta, postRes.post_view.post)).post;
|
|
||||||
// This will be undefined because of the tombstone
|
// This will be undefined because of the tombstone
|
||||||
expect(betaPost).toBeUndefined();
|
await expect(resolvePost(beta, postRes.post_view.post)).rejects.toBe(
|
||||||
|
"couldnt_find_object",
|
||||||
|
);
|
||||||
|
|
||||||
// Undelete
|
// Undelete
|
||||||
let undeletedPost = await deletePost(alpha, false, postRes.post_view.post);
|
let undeletedPost = await deletePost(alpha, false, postRes.post_view.post);
|
||||||
|
@ -259,8 +261,9 @@ test("Delete a post", async () => {
|
||||||
assertPostFederation(betaPost2, undeletedPost.post_view);
|
assertPostFederation(betaPost2, undeletedPost.post_view);
|
||||||
|
|
||||||
// Make sure lemmy beta cannot delete the post
|
// Make sure lemmy beta cannot delete the post
|
||||||
let deletedPostBeta = (await deletePost(beta, true, betaPost2.post)) as any;
|
await expect(deletePost(beta, true, betaPost2.post)).rejects.toBe(
|
||||||
expect(deletedPostBeta.error).toStrictEqual("no_post_edit_allowed");
|
"no_post_edit_allowed",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Remove a post from admin and community on different instance", async () => {
|
test("Remove a post from admin and community on different instance", async () => {
|
||||||
|
@ -388,8 +391,8 @@ test("Enforce site ban for federated user", async () => {
|
||||||
expect(alphaUserOnBeta1.person?.person.banned).toBe(true);
|
expect(alphaUserOnBeta1.person?.person.banned).toBe(true);
|
||||||
|
|
||||||
// existing alpha post should be removed on beta
|
// existing alpha post should be removed on beta
|
||||||
let searchBeta2 = await searchPostLocal(beta, postRes1.post_view.post);
|
let searchBeta2 = await getPost(beta, searchBeta1.posts[0].post.id);
|
||||||
expect(searchBeta2.posts[0].post.removed).toBe(true);
|
expect(searchBeta2.post_view.post.removed).toBe(true);
|
||||||
|
|
||||||
// Unban alpha
|
// Unban alpha
|
||||||
let unBanAlpha = await banPersonFromSite(
|
let unBanAlpha = await banPersonFromSite(
|
||||||
|
@ -436,12 +439,14 @@ test("Enforce community ban for federated user", async () => {
|
||||||
expect(banAlpha.banned).toBe(true);
|
expect(banAlpha.banned).toBe(true);
|
||||||
|
|
||||||
// ensure that the post by alpha got removed
|
// ensure that the post by alpha got removed
|
||||||
let searchAlpha1 = await searchPostLocal(alpha, postRes1.post_view.post);
|
await expect(getPost(alpha, searchBeta1.posts[0].post.id)).rejects.toBe(
|
||||||
expect(searchAlpha1.posts[0].post.removed).toBe(true);
|
"unknown",
|
||||||
|
);
|
||||||
|
|
||||||
// Alpha tries to make post on beta, but it fails because of ban
|
// Alpha tries to make post on beta, but it fails because of ban
|
||||||
let postRes2 = await createPost(alpha, betaCommunity.community.id);
|
await expect(createPost(alpha, betaCommunity.community.id)).rejects.toBe(
|
||||||
expect(postRes2.post_view).toBeUndefined();
|
"banned_from_community",
|
||||||
|
);
|
||||||
|
|
||||||
// Unban alpha
|
// Unban alpha
|
||||||
let unBanAlpha = await banPersonFromCommunity(
|
let unBanAlpha = await banPersonFromCommunity(
|
||||||
|
|
|
@ -58,6 +58,8 @@ import { CommentReportResponse } from "lemmy-js-client/dist/types/CommentReportR
|
||||||
import { CreateCommentReport } from "lemmy-js-client/dist/types/CreateCommentReport";
|
import { CreateCommentReport } from "lemmy-js-client/dist/types/CreateCommentReport";
|
||||||
import { ListCommentReportsResponse } from "lemmy-js-client/dist/types/ListCommentReportsResponse";
|
import { ListCommentReportsResponse } from "lemmy-js-client/dist/types/ListCommentReportsResponse";
|
||||||
import { ListCommentReports } from "lemmy-js-client/dist/types/ListCommentReports";
|
import { ListCommentReports } from "lemmy-js-client/dist/types/ListCommentReports";
|
||||||
|
import { GetPersonDetailsResponse } from "lemmy-js-client/dist/types/GetPersonDetailsResponse";
|
||||||
|
import { GetPersonDetails } from "lemmy-js-client/dist/types/GetPersonDetails";
|
||||||
|
|
||||||
export interface API {
|
export interface API {
|
||||||
client: LemmyHttp;
|
client: LemmyHttp;
|
||||||
|
@ -186,8 +188,11 @@ export async function setupLogins() {
|
||||||
await epsilon.client.editSite(editSiteForm);
|
await epsilon.client.editSite(editSiteForm);
|
||||||
|
|
||||||
// Create the main alpha/beta communities
|
// Create the main alpha/beta communities
|
||||||
await createCommunity(alpha, "main");
|
// Ignore thrown errors of duplicates
|
||||||
await createCommunity(beta, "main");
|
try {
|
||||||
|
await createCommunity(alpha, "main");
|
||||||
|
await createCommunity(beta, "main");
|
||||||
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createPost(
|
export async function createPost(
|
||||||
|
@ -646,6 +651,16 @@ export async function saveUserSettings(
|
||||||
): Promise<LoginResponse> {
|
): Promise<LoginResponse> {
|
||||||
return api.client.saveUserSettings(form);
|
return api.client.saveUserSettings(form);
|
||||||
}
|
}
|
||||||
|
export async function getPersonDetails(
|
||||||
|
api: API,
|
||||||
|
person_id: number,
|
||||||
|
): Promise<GetPersonDetailsResponse> {
|
||||||
|
let form: GetPersonDetails = {
|
||||||
|
auth: api.auth,
|
||||||
|
person_id: person_id,
|
||||||
|
};
|
||||||
|
return api.client.getPersonDetails(form);
|
||||||
|
}
|
||||||
|
|
||||||
export async function deleteUser(api: API): Promise<DeleteAccountResponse> {
|
export async function deleteUser(api: API): Promise<DeleteAccountResponse> {
|
||||||
let form: DeleteAccount = {
|
let form: DeleteAccount = {
|
||||||
|
|
|
@ -92,10 +92,18 @@ test("Delete user", async () => {
|
||||||
|
|
||||||
await deleteUser(user);
|
await deleteUser(user);
|
||||||
|
|
||||||
expect((await resolvePost(alpha, localPost)).post).toBeUndefined();
|
await expect(resolvePost(alpha, localPost)).rejects.toBe(
|
||||||
expect((await resolveComment(alpha, localComment)).comment).toBeUndefined();
|
"couldnt_find_object",
|
||||||
expect((await resolvePost(alpha, remotePost)).post).toBeUndefined();
|
);
|
||||||
expect((await resolveComment(alpha, remoteComment)).comment).toBeUndefined();
|
await expect(resolveComment(alpha, localComment)).rejects.toBe(
|
||||||
|
"couldnt_find_object",
|
||||||
|
);
|
||||||
|
await expect(resolvePost(alpha, remotePost)).rejects.toBe(
|
||||||
|
"couldnt_find_object",
|
||||||
|
);
|
||||||
|
await expect(resolveComment(alpha, remoteComment)).rejects.toBe(
|
||||||
|
"couldnt_find_object",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Requests with invalid auth should be treated as unauthenticated", async () => {
|
test("Requests with invalid auth should be treated as unauthenticated", async () => {
|
||||||
|
|
|
@ -2157,10 +2157,10 @@ kleur@^3.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||||
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
||||||
|
|
||||||
lemmy-js-client@0.17.2-rc.13:
|
lemmy-js-client@0.18.3-rc.3:
|
||||||
version "0.17.2-rc.13"
|
version "0.18.3-rc.3"
|
||||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.2-rc.13.tgz#f2a61050c1308e85cb39c0e1f561e392e84e3921"
|
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.18.3-rc.3.tgz#fc6489eb141bd09558bca38d9e46b40771a29f37"
|
||||||
integrity sha512-4IyR1pisCumJ9L8fEPISC+Su1kVTI4pL/gWLsuOXxZC/lK36mG2+NfaNPiUmIklpCF5TUN+1F7E9bEvtTGogww==
|
integrity sha512-njixgXk4uMU4gGifnljwhSe9Kf445C4wAXcXhtpTtwPPLXpHQgxA1RASMb9Uq4zblfE6nC2JbrAka8y8N2N/Bw==
|
||||||
dependencies:
|
dependencies:
|
||||||
cross-fetch "^3.1.5"
|
cross-fetch "^3.1.5"
|
||||||
form-data "^4.0.0"
|
form-data "^4.0.0"
|
||||||
|
|
|
@ -22,24 +22,19 @@ impl Perform for ListCommentReports {
|
||||||
let data: &ListCommentReports = self;
|
let data: &ListCommentReports = self;
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
||||||
|
|
||||||
let person_id = local_user_view.person.id;
|
|
||||||
let admin = local_user_view.person.admin;
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let unresolved_only = data.unresolved_only;
|
let unresolved_only = data.unresolved_only;
|
||||||
|
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let comment_reports = CommentReportQuery::builder()
|
let comment_reports = CommentReportQuery {
|
||||||
.pool(&mut context.pool())
|
community_id,
|
||||||
.my_person_id(person_id)
|
unresolved_only,
|
||||||
.admin(admin)
|
page,
|
||||||
.community_id(community_id)
|
limit,
|
||||||
.unresolved_only(unresolved_only)
|
}
|
||||||
.page(page)
|
.list(&mut context.pool(), &local_user_view.person)
|
||||||
.limit(limit)
|
.await?;
|
||||||
.build()
|
|
||||||
.list()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(ListCommentReportsResponse { comment_reports })
|
Ok(ListCommentReportsResponse { comment_reports })
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,9 @@ pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Resul
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use lemmy_api_common::utils::check_validator_time;
|
use lemmy_api_common::utils::check_validator_time;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -27,18 +27,17 @@ impl Perform for GetPersonMentions {
|
||||||
let person_id = Some(local_user_view.person.id);
|
let person_id = Some(local_user_view.person.id);
|
||||||
let show_bot_accounts = Some(local_user_view.local_user.show_bot_accounts);
|
let show_bot_accounts = Some(local_user_view.local_user.show_bot_accounts);
|
||||||
|
|
||||||
let mentions = PersonMentionQuery::builder()
|
let mentions = PersonMentionQuery {
|
||||||
.pool(&mut context.pool())
|
recipient_id: person_id,
|
||||||
.recipient_id(person_id)
|
my_person_id: person_id,
|
||||||
.my_person_id(person_id)
|
sort,
|
||||||
.sort(sort)
|
unread_only,
|
||||||
.unread_only(unread_only)
|
show_bot_accounts,
|
||||||
.show_bot_accounts(show_bot_accounts)
|
page,
|
||||||
.page(page)
|
limit,
|
||||||
.limit(limit)
|
}
|
||||||
.build()
|
.list(&mut context.pool())
|
||||||
.list()
|
.await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(GetPersonMentionsResponse { mentions })
|
Ok(GetPersonMentionsResponse { mentions })
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,18 +24,17 @@ impl Perform for GetReplies {
|
||||||
let person_id = Some(local_user_view.person.id);
|
let person_id = Some(local_user_view.person.id);
|
||||||
let show_bot_accounts = Some(local_user_view.local_user.show_bot_accounts);
|
let show_bot_accounts = Some(local_user_view.local_user.show_bot_accounts);
|
||||||
|
|
||||||
let replies = CommentReplyQuery::builder()
|
let replies = CommentReplyQuery {
|
||||||
.pool(&mut context.pool())
|
recipient_id: person_id,
|
||||||
.recipient_id(person_id)
|
my_person_id: person_id,
|
||||||
.my_person_id(person_id)
|
sort,
|
||||||
.sort(sort)
|
unread_only,
|
||||||
.unread_only(unread_only)
|
show_bot_accounts,
|
||||||
.show_bot_accounts(show_bot_accounts)
|
page,
|
||||||
.page(page)
|
limit,
|
||||||
.limit(limit)
|
}
|
||||||
.build()
|
.list(&mut context.pool())
|
||||||
.list()
|
.await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(GetRepliesResponse { replies })
|
Ok(GetRepliesResponse { replies })
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,6 +133,7 @@ impl Perform for SaveUserSettings {
|
||||||
.totp_2fa_secret(totp_2fa_secret)
|
.totp_2fa_secret(totp_2fa_secret)
|
||||||
.totp_2fa_url(totp_2fa_url)
|
.totp_2fa_url(totp_2fa_url)
|
||||||
.open_links_in_new_tab(data.open_links_in_new_tab)
|
.open_links_in_new_tab(data.open_links_in_new_tab)
|
||||||
|
.infinite_scroll_enabled(data.infinite_scroll_enabled)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let local_user_res =
|
let local_user_res =
|
||||||
|
|
|
@ -22,24 +22,19 @@ impl Perform for ListPostReports {
|
||||||
let data: &ListPostReports = self;
|
let data: &ListPostReports = self;
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
||||||
|
|
||||||
let person_id = local_user_view.person.id;
|
|
||||||
let admin = local_user_view.person.admin;
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let unresolved_only = data.unresolved_only;
|
let unresolved_only = data.unresolved_only;
|
||||||
|
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let post_reports = PostReportQuery::builder()
|
let post_reports = PostReportQuery {
|
||||||
.pool(&mut context.pool())
|
community_id,
|
||||||
.my_person_id(person_id)
|
unresolved_only,
|
||||||
.admin(admin)
|
page,
|
||||||
.community_id(community_id)
|
limit,
|
||||||
.unresolved_only(unresolved_only)
|
}
|
||||||
.page(page)
|
.list(&mut context.pool(), &local_user_view.person)
|
||||||
.limit(limit)
|
.await?;
|
||||||
.build()
|
|
||||||
.list()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(ListPostReportsResponse { post_reports })
|
Ok(ListPostReportsResponse { post_reports })
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,13 @@ impl Perform for ListPrivateMessageReports {
|
||||||
let unresolved_only = self.unresolved_only;
|
let unresolved_only = self.unresolved_only;
|
||||||
let page = self.page;
|
let page = self.page;
|
||||||
let limit = self.limit;
|
let limit = self.limit;
|
||||||
let private_message_reports = PrivateMessageReportQuery::builder()
|
let private_message_reports = PrivateMessageReportQuery {
|
||||||
.pool(&mut context.pool())
|
unresolved_only,
|
||||||
.unresolved_only(unresolved_only)
|
page,
|
||||||
.page(page)
|
limit,
|
||||||
.limit(limit)
|
}
|
||||||
.build()
|
.list(&mut context.pool())
|
||||||
.list()
|
.await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(ListPrivateMessageReportsResponse {
|
Ok(ListPrivateMessageReportsResponse {
|
||||||
private_message_reports,
|
private_message_reports,
|
||||||
|
|
|
@ -23,19 +23,18 @@ impl Perform for ListRegistrationApplications {
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
|
||||||
let unread_only = data.unread_only;
|
let unread_only = data.unread_only;
|
||||||
let verified_email_only = local_site.require_email_verification;
|
let verified_email_only = Some(local_site.require_email_verification);
|
||||||
|
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let registration_applications = RegistrationApplicationQuery::builder()
|
let registration_applications = RegistrationApplicationQuery {
|
||||||
.pool(&mut context.pool())
|
unread_only,
|
||||||
.unread_only(unread_only)
|
verified_email_only,
|
||||||
.verified_email_only(Some(verified_email_only))
|
page,
|
||||||
.page(page)
|
limit,
|
||||||
.limit(limit)
|
}
|
||||||
.build()
|
.list(&mut context.pool())
|
||||||
.list()
|
.await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Self::Response {
|
Ok(Self::Response {
|
||||||
registration_applications,
|
registration_applications,
|
||||||
|
|
|
@ -14,9 +14,27 @@ path = "src/lib.rs"
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
full = ["tracing", "rosetta-i18n", "chrono", "lemmy_utils",
|
full = [
|
||||||
"lemmy_db_views/full", "lemmy_db_views_actor/full", "lemmy_db_views_moderator/full",
|
"tracing",
|
||||||
"percent-encoding", "encoding", "reqwest-middleware", "webpage", "ts-rs"]
|
"rosetta-i18n",
|
||||||
|
"chrono",
|
||||||
|
"lemmy_utils",
|
||||||
|
"lemmy_db_views/full",
|
||||||
|
"lemmy_db_views_actor/full",
|
||||||
|
"lemmy_db_views_moderator/full",
|
||||||
|
"activitypub_federation",
|
||||||
|
"percent-encoding",
|
||||||
|
"encoding",
|
||||||
|
"reqwest-middleware",
|
||||||
|
"webpage",
|
||||||
|
"ts-rs",
|
||||||
|
"tokio",
|
||||||
|
"uuid",
|
||||||
|
"reqwest",
|
||||||
|
"actix-web",
|
||||||
|
"futures",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lemmy_db_views = { workspace = true }
|
lemmy_db_views = { workspace = true }
|
||||||
|
@ -24,6 +42,7 @@ lemmy_db_views_moderator = { workspace = true }
|
||||||
lemmy_db_views_actor = { workspace = true }
|
lemmy_db_views_actor = { workspace = true }
|
||||||
lemmy_db_schema = { workspace = true }
|
lemmy_db_schema = { workspace = true }
|
||||||
lemmy_utils = { workspace = true, optional = true }
|
lemmy_utils = { workspace = true, optional = true }
|
||||||
|
activitypub_federation = { workspace = true, optional = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_with = { workspace = true }
|
serde_with = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
@ -33,12 +52,17 @@ reqwest-middleware = { workspace = true, optional = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
rosetta-i18n = { workspace = true, optional = true }
|
rosetta-i18n = { workspace = true, optional = true }
|
||||||
percent-encoding = { workspace = true, optional = true }
|
percent-encoding = { workspace = true, optional = true }
|
||||||
webpage = { version = "1.6", default-features = false, features = ["serde"], optional = true }
|
webpage = { version = "1.6", default-features = false, features = [
|
||||||
|
"serde",
|
||||||
|
], optional = true }
|
||||||
encoding = { version = "0.2.33", optional = true }
|
encoding = { version = "0.2.33", optional = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true, optional = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true, optional = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, optional = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true, optional = true }
|
||||||
ts-rs = { workspace = true, optional = true }
|
ts-rs = { workspace = true, optional = true }
|
||||||
actix-web = { workspace = true }
|
once_cell = { workspace = true, optional = true }
|
||||||
|
actix-web = { workspace = true, optional = true }
|
||||||
|
# necessary for wasmt compilation
|
||||||
|
getrandom = { version = "0.2.10", features = ["js"] }
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub async fn build_community_response(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn build_post_response(
|
pub async fn build_post_response(
|
||||||
context: &Data<LemmyContext>,
|
context: &LemmyContext,
|
||||||
community_id: CommunityId,
|
community_id: CommunityId,
|
||||||
person_id: PersonId,
|
person_id: PersonId,
|
||||||
post_id: PostId,
|
post_id: PostId,
|
||||||
|
|
|
@ -10,6 +10,8 @@ pub mod post;
|
||||||
pub mod private_message;
|
pub mod private_message;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod request;
|
pub mod request;
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
pub mod send_activity;
|
||||||
pub mod sensitive;
|
pub mod sensitive;
|
||||||
pub mod site;
|
pub mod site;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
|
|
|
@ -133,6 +133,8 @@ pub struct SaveUserSettings {
|
||||||
pub auth: Sensitive<String>,
|
pub auth: Sensitive<String>,
|
||||||
/// Open links in a new tab
|
/// Open links in a new tab
|
||||||
pub open_links_in_new_tab: Option<bool>,
|
pub open_links_in_new_tab: Option<bool>,
|
||||||
|
/// Enable infinite scroll
|
||||||
|
pub infinite_scroll_enabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||||
|
|
|
@ -270,6 +270,9 @@ pub fn build_user_agent(settings: &Settings) -> String {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::request::{
|
use crate::request::{
|
||||||
build_user_agent,
|
build_user_agent,
|
||||||
fetch_site_metadata,
|
fetch_site_metadata,
|
||||||
|
|
74
crates/api_common/src/send_activity.rs
Normal file
74
crates/api_common/src/send_activity.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
use crate::context::LemmyContext;
|
||||||
|
use activitypub_federation::config::Data;
|
||||||
|
use futures::future::BoxFuture;
|
||||||
|
use lemmy_db_schema::source::post::Post;
|
||||||
|
use lemmy_utils::{error::LemmyResult, SYNCHRONOUS_FEDERATION};
|
||||||
|
use once_cell::sync::{Lazy, OnceCell};
|
||||||
|
use tokio::{
|
||||||
|
sync::{
|
||||||
|
mpsc,
|
||||||
|
mpsc::{UnboundedReceiver, UnboundedSender, WeakUnboundedSender},
|
||||||
|
Mutex,
|
||||||
|
},
|
||||||
|
task::JoinHandle,
|
||||||
|
};
|
||||||
|
|
||||||
|
type MatchOutgoingActivitiesBoxed =
|
||||||
|
Box<for<'a> fn(SendActivityData, &'a Data<LemmyContext>) -> BoxFuture<'a, LemmyResult<()>>>;
|
||||||
|
|
||||||
|
/// This static is necessary so that activities can be sent out synchronously for tests.
|
||||||
|
pub static MATCH_OUTGOING_ACTIVITIES: OnceCell<MatchOutgoingActivitiesBoxed> = OnceCell::new();
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SendActivityData {
|
||||||
|
CreatePost(Post),
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: instead of static, move this into LemmyContext. make sure that stopping the process with
|
||||||
|
// ctrl+c still works.
|
||||||
|
static ACTIVITY_CHANNEL: Lazy<ActivityChannel> = Lazy::new(|| {
|
||||||
|
let (sender, receiver) = mpsc::unbounded_channel();
|
||||||
|
let weak_sender = sender.downgrade();
|
||||||
|
ActivityChannel {
|
||||||
|
weak_sender,
|
||||||
|
receiver: Mutex::new(receiver),
|
||||||
|
keepalive_sender: Mutex::new(Some(sender)),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct ActivityChannel {
|
||||||
|
weak_sender: WeakUnboundedSender<SendActivityData>,
|
||||||
|
receiver: Mutex<UnboundedReceiver<SendActivityData>>,
|
||||||
|
keepalive_sender: Mutex<Option<UnboundedSender<SendActivityData>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActivityChannel {
|
||||||
|
pub async fn retrieve_activity() -> Option<SendActivityData> {
|
||||||
|
let mut lock = ACTIVITY_CHANNEL.receiver.lock().await;
|
||||||
|
lock.recv().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn submit_activity(
|
||||||
|
data: SendActivityData,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
if *SYNCHRONOUS_FEDERATION {
|
||||||
|
MATCH_OUTGOING_ACTIVITIES
|
||||||
|
.get()
|
||||||
|
.expect("retrieve function pointer")(data, context)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
// could do `ACTIVITY_CHANNEL.keepalive_sender.lock()` instead and get rid of weak_sender,
|
||||||
|
// not sure which way is more efficient
|
||||||
|
else if let Some(sender) = ACTIVITY_CHANNEL.weak_sender.upgrade() {
|
||||||
|
sender.send(data)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn close(outgoing_activities_task: JoinHandle<LemmyResult<()>>) -> LemmyResult<()> {
|
||||||
|
ACTIVITY_CHANNEL.keepalive_sender.lock().await.take();
|
||||||
|
outgoing_activities_task.await??;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -667,13 +667,13 @@ pub async fn remove_user_data_in_community(
|
||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
// TODO Diesel doesn't allow updates with joins, so this has to be a loop
|
// TODO Diesel doesn't allow updates with joins, so this has to be a loop
|
||||||
let comments = CommentQuery::builder()
|
let comments = CommentQuery {
|
||||||
.pool(pool)
|
creator_id: Some(banned_person_id),
|
||||||
.creator_id(Some(banned_person_id))
|
community_id: Some(community_id),
|
||||||
.community_id(Some(community_id))
|
..Default::default()
|
||||||
.build()
|
}
|
||||||
.list()
|
.list(pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for comment_view in &comments {
|
for comment_view in &comments {
|
||||||
let comment_id = comment_view.comment.id;
|
let comment_id = comment_view.comment.id;
|
||||||
|
@ -731,6 +731,9 @@ pub async fn delete_user_account(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::utils::{honeypot_check, password_length_check};
|
use crate::utils::{honeypot_check, password_length_check};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -23,4 +23,4 @@ url = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
webmention = "0.4.0"
|
webmention = "0.4.0"
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
|
|
|
@ -16,6 +16,7 @@ use lemmy_api_common::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
impls::actor_language::default_post_language,
|
||||||
source::{
|
source::{
|
||||||
actor_language::CommunityLanguage,
|
actor_language::CommunityLanguage,
|
||||||
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm},
|
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm},
|
||||||
|
@ -82,25 +83,31 @@ impl PerformCrud for CreateComment {
|
||||||
check_comment_depth(parent)?;
|
check_comment_depth(parent)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no language is set, copy language from parent post/comment
|
|
||||||
let parent_language = parent_opt
|
|
||||||
.as_ref()
|
|
||||||
.map(|p| p.language_id)
|
|
||||||
.unwrap_or(post.language_id);
|
|
||||||
let language_id = data.language_id.unwrap_or(parent_language);
|
|
||||||
|
|
||||||
CommunityLanguage::is_allowed_community_language(
|
CommunityLanguage::is_allowed_community_language(
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
Some(language_id),
|
data.language_id,
|
||||||
community_id,
|
community_id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// attempt to set default language if none was provided
|
||||||
|
let language_id = match data.language_id {
|
||||||
|
Some(lid) => Some(lid),
|
||||||
|
None => {
|
||||||
|
default_post_language(
|
||||||
|
&mut context.pool(),
|
||||||
|
community_id,
|
||||||
|
local_user_view.local_user.id,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::builder()
|
let comment_form = CommentInsertForm::builder()
|
||||||
.content(content_slurs_removed.clone())
|
.content(content_slurs_removed.clone())
|
||||||
.post_id(data.post_id)
|
.post_id(data.post_id)
|
||||||
.creator_id(local_user_view.person.id)
|
.creator_id(local_user_view.person.id)
|
||||||
.language_id(Some(language_id))
|
.language_id(language_id)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Create the comment
|
// Create the comment
|
||||||
|
|
|
@ -31,18 +31,18 @@ impl PerformCrud for ListCommunities {
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let local_user = local_user_view.map(|l| l.local_user);
|
let local_user = local_user_view.map(|l| l.local_user);
|
||||||
let communities = CommunityQuery::builder()
|
let communities = CommunityQuery {
|
||||||
.pool(&mut context.pool())
|
listing_type,
|
||||||
.listing_type(listing_type)
|
show_nsfw,
|
||||||
.show_nsfw(show_nsfw)
|
sort,
|
||||||
.sort(sort)
|
local_user: local_user.as_ref(),
|
||||||
.local_user(local_user.as_ref())
|
page,
|
||||||
.page(page)
|
limit,
|
||||||
.limit(limit)
|
is_mod_or_admin: is_admin,
|
||||||
.is_mod_or_admin(is_admin)
|
..Default::default()
|
||||||
.build()
|
}
|
||||||
.list()
|
.list(&mut context.pool())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(ListCommunitiesResponse { communities })
|
Ok(ListCommunitiesResponse { communities })
|
||||||
|
|
|
@ -5,7 +5,7 @@ use lemmy_utils::error::LemmyError;
|
||||||
mod comment;
|
mod comment;
|
||||||
mod community;
|
mod community;
|
||||||
mod custom_emoji;
|
mod custom_emoji;
|
||||||
mod post;
|
pub mod post;
|
||||||
mod private_message;
|
mod private_message;
|
||||||
mod site;
|
mod site;
|
||||||
mod user;
|
mod user;
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_post_response,
|
build_response::build_post_response,
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{CreatePost, PostResponse},
|
post::{CreatePost, PostResponse},
|
||||||
request::fetch_site_data,
|
request::fetch_site_data,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
check_community_deleted_or_removed,
|
check_community_deleted_or_removed,
|
||||||
|
@ -40,147 +41,153 @@ use tracing::Instrument;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use webmention::{Webmention, WebmentionError};
|
use webmention::{Webmention, WebmentionError};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for CreatePost {
|
pub async fn create_post(
|
||||||
type Response = PostResponse;
|
data: Json<CreatePost>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<PostResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
|
check_slurs(&data.name, &slur_regex)?;
|
||||||
let data: &CreatePost = self;
|
check_slurs_opt(&data.body, &slur_regex)?;
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
honeypot_check(&data.honeypot)?;
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
|
||||||
|
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let data_url = data.url.as_ref();
|
||||||
check_slurs(&data.name, &slur_regex)?;
|
let url = data_url.map(clean_url_params).map(Into::into); // TODO no good way to handle a "clear"
|
||||||
check_slurs_opt(&data.body, &slur_regex)?;
|
|
||||||
honeypot_check(&data.honeypot)?;
|
|
||||||
|
|
||||||
let data_url = data.url.as_ref();
|
is_valid_post_title(&data.name)?;
|
||||||
let url = data_url.map(clean_url_params).map(Into::into); // TODO no good way to handle a "clear"
|
is_valid_body_field(&data.body, true)?;
|
||||||
|
check_url_scheme(&data.url)?;
|
||||||
|
|
||||||
is_valid_post_title(&data.name)?;
|
check_community_ban(
|
||||||
is_valid_body_field(&data.body, true)?;
|
local_user_view.person.id,
|
||||||
check_url_scheme(&data.url)?;
|
data.community_id,
|
||||||
|
&mut context.pool(),
|
||||||
check_community_ban(
|
)
|
||||||
local_user_view.person.id,
|
.await?;
|
||||||
data.community_id,
|
check_community_deleted_or_removed(data.community_id, &mut context.pool()).await?;
|
||||||
&mut context.pool(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
check_community_deleted_or_removed(data.community_id, &mut context.pool()).await?;
|
|
||||||
|
|
||||||
|
let community_id = data.community_id;
|
||||||
|
let community = Community::read(&mut context.pool(), community_id).await?;
|
||||||
|
if community.posting_restricted_to_mods {
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let community = Community::read(&mut context.pool(), community_id).await?;
|
let is_mod = CommunityView::is_mod_or_admin(
|
||||||
if community.posting_restricted_to_mods {
|
|
||||||
let community_id = data.community_id;
|
|
||||||
let is_mod = CommunityView::is_mod_or_admin(
|
|
||||||
&mut context.pool(),
|
|
||||||
local_user_view.local_user.person_id,
|
|
||||||
community_id,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
if !is_mod {
|
|
||||||
return Err(LemmyErrorType::OnlyModsCanPostInCommunity)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch post links and pictrs cached image
|
|
||||||
let (metadata_res, thumbnail_url) =
|
|
||||||
fetch_site_data(context.client(), context.settings(), data_url, true).await;
|
|
||||||
let (embed_title, embed_description, embed_video_url) = metadata_res
|
|
||||||
.map(|u| (u.title, u.description, u.embed_video_url))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let language_id = match data.language_id {
|
|
||||||
Some(lid) => Some(lid),
|
|
||||||
None => {
|
|
||||||
default_post_language(
|
|
||||||
&mut context.pool(),
|
|
||||||
community_id,
|
|
||||||
local_user_view.local_user.id,
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
CommunityLanguage::is_allowed_community_language(
|
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
language_id,
|
local_user_view.local_user.person_id,
|
||||||
community_id,
|
community_id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
if !is_mod {
|
||||||
|
return Err(LemmyErrorType::OnlyModsCanPostInCommunity)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let post_form = PostInsertForm::builder()
|
// Fetch post links and pictrs cached image
|
||||||
.name(data.name.trim().to_owned())
|
let (metadata_res, thumbnail_url) =
|
||||||
.url(url)
|
fetch_site_data(context.client(), context.settings(), data_url, true).await;
|
||||||
.body(data.body.clone())
|
let (embed_title, embed_description, embed_video_url) = metadata_res
|
||||||
.community_id(data.community_id)
|
.map(|u| (u.title, u.description, u.embed_video_url))
|
||||||
.creator_id(local_user_view.person.id)
|
.unwrap_or_default();
|
||||||
.nsfw(data.nsfw)
|
|
||||||
.embed_title(embed_title)
|
|
||||||
.embed_description(embed_description)
|
|
||||||
.embed_video_url(embed_video_url)
|
|
||||||
.language_id(language_id)
|
|
||||||
.thumbnail_url(thumbnail_url)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_post = Post::create(&mut context.pool(), &post_form)
|
// Only need to check if language is allowed in case user set it explicitly. When using default
|
||||||
.await
|
// language, it already only returns allowed languages.
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
|
CommunityLanguage::is_allowed_community_language(
|
||||||
|
&mut context.pool(),
|
||||||
|
data.language_id,
|
||||||
|
community_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let inserted_post_id = inserted_post.id;
|
// attempt to set default language if none was provided
|
||||||
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
let language_id = match data.language_id {
|
||||||
let apub_id = generate_local_apub_endpoint(
|
Some(lid) => Some(lid),
|
||||||
EndpointType::Post,
|
None => {
|
||||||
&inserted_post_id.to_string(),
|
default_post_language(
|
||||||
&protocol_and_hostname,
|
&mut context.pool(),
|
||||||
)?;
|
community_id,
|
||||||
let updated_post = Post::update(
|
local_user_view.local_user.id,
|
||||||
&mut context.pool(),
|
)
|
||||||
inserted_post_id,
|
.await?
|
||||||
&PostUpdateForm::builder().ap_id(Some(apub_id)).build(),
|
}
|
||||||
)
|
};
|
||||||
|
|
||||||
|
let post_form = PostInsertForm::builder()
|
||||||
|
.name(data.name.trim().to_owned())
|
||||||
|
.url(url)
|
||||||
|
.body(data.body.clone())
|
||||||
|
.community_id(data.community_id)
|
||||||
|
.creator_id(local_user_view.person.id)
|
||||||
|
.nsfw(data.nsfw)
|
||||||
|
.embed_title(embed_title)
|
||||||
|
.embed_description(embed_description)
|
||||||
|
.embed_video_url(embed_video_url)
|
||||||
|
.language_id(language_id)
|
||||||
|
.thumbnail_url(thumbnail_url)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let inserted_post = Post::create(&mut context.pool(), &post_form)
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
|
.with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
|
||||||
|
|
||||||
// They like their own post by default
|
let inserted_post_id = inserted_post.id;
|
||||||
let person_id = local_user_view.person.id;
|
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
let post_id = inserted_post.id;
|
let apub_id = generate_local_apub_endpoint(
|
||||||
let like_form = PostLikeForm {
|
EndpointType::Post,
|
||||||
post_id,
|
&inserted_post_id.to_string(),
|
||||||
person_id,
|
&protocol_and_hostname,
|
||||||
score: 1,
|
)?;
|
||||||
};
|
let updated_post = Post::update(
|
||||||
|
&mut context.pool(),
|
||||||
|
inserted_post_id,
|
||||||
|
&PostUpdateForm::builder().ap_id(Some(apub_id)).build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
|
||||||
|
|
||||||
PostLike::like(&mut context.pool(), &like_form)
|
// They like their own post by default
|
||||||
.await
|
let person_id = local_user_view.person.id;
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
|
let post_id = inserted_post.id;
|
||||||
|
let like_form = PostLikeForm {
|
||||||
|
post_id,
|
||||||
|
person_id,
|
||||||
|
score: 1,
|
||||||
|
};
|
||||||
|
|
||||||
// Mark the post as read
|
PostLike::like(&mut context.pool(), &like_form)
|
||||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
|
||||||
|
|
||||||
if let Some(url) = updated_post.url.clone() {
|
ActivityChannel::submit_activity(SendActivityData::CreatePost(updated_post.clone()), &context)
|
||||||
let task = async move {
|
.await?;
|
||||||
let mut webmention =
|
|
||||||
Webmention::new::<Url>(updated_post.ap_id.clone().into(), url.clone().into())?;
|
// Mark the post as read
|
||||||
webmention.set_checked(true);
|
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
||||||
match webmention
|
|
||||||
.send()
|
if let Some(url) = updated_post.url.clone() {
|
||||||
.instrument(tracing::info_span!("Sending webmention"))
|
let task = async move {
|
||||||
.await
|
let mut webmention =
|
||||||
{
|
Webmention::new::<Url>(updated_post.ap_id.clone().into(), url.clone().into())?;
|
||||||
Err(WebmentionError::NoEndpointDiscovered(_)) => Ok(()),
|
webmention.set_checked(true);
|
||||||
Ok(_) => Ok(()),
|
match webmention
|
||||||
Err(e) => Err(e).with_lemmy_type(LemmyErrorType::CouldntSendWebmention),
|
.send()
|
||||||
}
|
.instrument(tracing::info_span!("Sending webmention"))
|
||||||
};
|
.await
|
||||||
if *SYNCHRONOUS_FEDERATION {
|
{
|
||||||
task.await?;
|
Err(WebmentionError::NoEndpointDiscovered(_)) => Ok(()),
|
||||||
} else {
|
Ok(_) => Ok(()),
|
||||||
spawn_try_task(task);
|
Err(e) => Err(e).with_lemmy_type(LemmyErrorType::CouldntSendWebmention),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if *SYNCHRONOUS_FEDERATION {
|
||||||
|
task.await?;
|
||||||
|
} else {
|
||||||
|
spawn_try_task(task);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
build_post_response(context, community_id, person_id, post_id).await
|
Ok(Json(
|
||||||
}
|
build_post_response(&context, community_id, person_id, post_id).await?,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
mod create;
|
pub mod create;
|
||||||
mod delete;
|
mod delete;
|
||||||
mod read;
|
mod read;
|
||||||
mod remove;
|
mod remove;
|
||||||
|
|
|
@ -100,12 +100,12 @@ impl PerformCrud for GetPost {
|
||||||
|
|
||||||
// Fetch the cross_posts
|
// Fetch the cross_posts
|
||||||
let cross_posts = if let Some(url) = &post_view.post.url {
|
let cross_posts = if let Some(url) = &post_view.post.url {
|
||||||
let mut x_posts = PostQuery::builder()
|
let mut x_posts = PostQuery {
|
||||||
.pool(&mut context.pool())
|
url_search: Some(url.inner().as_str().into()),
|
||||||
.url_search(Some(url.inner().as_str().into()))
|
..Default::default()
|
||||||
.build()
|
}
|
||||||
.list()
|
.list(&mut context.pool())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Don't return this post as one of the cross_posts
|
// Don't return this post as one of the cross_posts
|
||||||
x_posts.retain(|x| x.post.id != post_id);
|
x_posts.retain(|x| x.post.id != post_id);
|
||||||
|
|
|
@ -24,15 +24,13 @@ impl PerformCrud for GetPrivateMessages {
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let unread_only = data.unread_only;
|
let unread_only = data.unread_only;
|
||||||
let mut messages = PrivateMessageQuery::builder()
|
let mut messages = PrivateMessageQuery {
|
||||||
.pool(&mut context.pool())
|
page,
|
||||||
.recipient_id(person_id)
|
limit,
|
||||||
.page(page)
|
unread_only,
|
||||||
.limit(limit)
|
}
|
||||||
.unread_only(unread_only)
|
.list(&mut context.pool(), person_id)
|
||||||
.build()
|
.await?;
|
||||||
.list()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Messages sent by ourselves should be marked as read. The `read` column in database is only
|
// Messages sent by ourselves should be marked as read. The `read` column in database is only
|
||||||
// for the recipient, and shouldnt be exposed to sender.
|
// for the recipient, and shouldnt be exposed to sender.
|
||||||
|
|
|
@ -183,6 +183,9 @@ fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) ->
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::site::create::validate_create_payload;
|
use crate::site::create::validate_create_payload;
|
||||||
use lemmy_api_common::site::CreateSite;
|
use lemmy_api_common::site::CreateSite;
|
||||||
use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode};
|
use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode};
|
||||||
|
|
|
@ -42,6 +42,9 @@ pub fn application_question_check(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::site::{application_question_check, site_default_post_listing_type_check};
|
use crate::site::{application_question_check, site_default_post_listing_type_check};
|
||||||
use lemmy_db_schema::{ListingType, RegistrationMode};
|
use lemmy_db_schema::{ListingType, RegistrationMode};
|
||||||
|
|
||||||
|
|
|
@ -217,6 +217,9 @@ fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> Lemm
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::site::update::validate_update_payload;
|
use crate::site::update::validate_update_payload;
|
||||||
use lemmy_api_common::site::EditSite;
|
use lemmy_api_common::site::EditSite;
|
||||||
use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode};
|
use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode};
|
||||||
|
|
|
@ -138,6 +138,7 @@ impl PerformCrud for Register {
|
||||||
.password_encrypted(data.password.to_string())
|
.password_encrypted(data.password.to_string())
|
||||||
.show_nsfw(Some(data.show_nsfw))
|
.show_nsfw(Some(data.show_nsfw))
|
||||||
.accepted_application(accepted_application)
|
.accepted_application(accepted_application)
|
||||||
|
.default_listing_type(Some(local_site.default_post_listing_type))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let inserted_local_user = LocalUser::create(&mut context.pool(), &local_user_form).await?;
|
let inserted_local_user = LocalUser::create(&mut context.pool(), &local_user_form).await?;
|
||||||
|
|
|
@ -25,7 +25,7 @@ chrono = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
actix-web = { workspace = true }
|
actix-web = { workspace = true }
|
||||||
tokio = {workspace = true}
|
tokio = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
strum_macros = { workspace = true }
|
strum_macros = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{instance::remote_instance_inboxes, person::ApubPerson},
|
objects::{instance::remote_instance_inboxes, person::ApubPerson},
|
||||||
protocol::activities::block::block_user::BlockUser,
|
protocol::activities::block::block_user::BlockUser,
|
||||||
};
|
};
|
||||||
|
@ -124,6 +124,7 @@ impl ActivityHandler for BlockUser {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_is_public(&self.to, &self.cc)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
match self.target.dereference(context).await? {
|
match self.target.dereference(context).await? {
|
||||||
SiteOrCommunity::Site(site) => {
|
SiteOrCommunity::Site(site) => {
|
||||||
|
@ -147,7 +148,6 @@ impl ActivityHandler for BlockUser {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
let expires = self.expires.map(|u| u.naive_local());
|
let expires = self.expires.map(|u| u.naive_local());
|
||||||
let mod_person = self.actor.dereference(context).await?;
|
let mod_person = self.actor.dereference(context).await?;
|
||||||
let blocked_person = self.object.dereference(context).await?;
|
let blocked_person = self.object.dereference(context).await?;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
verify_is_public,
|
verify_is_public,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{instance::remote_instance_inboxes, person::ApubPerson},
|
objects::{instance::remote_instance_inboxes, person::ApubPerson},
|
||||||
protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
|
protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
|
||||||
};
|
};
|
||||||
|
@ -88,6 +88,7 @@ impl ActivityHandler for UndoBlockUser {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_is_public(&self.to, &self.cc)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_domains_match(self.actor.inner(), self.object.actor.inner())?;
|
verify_domains_match(self.actor.inner(), self.object.actor.inner())?;
|
||||||
self.object.verify(context).await?;
|
self.object.verify(context).await?;
|
||||||
|
@ -96,7 +97,6 @@ impl ActivityHandler for UndoBlockUser {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
let expires = self.object.expires.map(|u| u.naive_local());
|
let expires = self.object.expires.map(|u| u.naive_local());
|
||||||
let mod_person = self.actor.dereference(context).await?;
|
let mod_person = self.actor.dereference(context).await?;
|
||||||
let blocked_person = self.object.object.dereference(context).await?;
|
let blocked_person = self.object.object.dereference(context).await?;
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::community::ApubCommunity,
|
objects::community::ApubCommunity,
|
||||||
protocol::{
|
protocol::{
|
||||||
activities::community::announce::{AnnounceActivity, RawAnnouncableActivities},
|
activities::community::announce::{AnnounceActivity, RawAnnouncableActivities},
|
||||||
|
@ -133,14 +133,14 @@ impl ActivityHandler for AnnounceActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_is_public(&self.to, &self.cc)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
let object: AnnouncableActivities = self.object.object(context).await?.try_into()?;
|
let object: AnnouncableActivities = self.object.object(context).await?.try_into()?;
|
||||||
// This is only for sending, not receiving so we reject it.
|
// This is only for sending, not receiving so we reject it.
|
||||||
if let AnnouncableActivities::Page(_) = object {
|
if let AnnouncableActivities::Page(_) = object {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||||
protocol::{
|
protocol::{
|
||||||
activities::community::{collection_add::CollectionAdd, collection_remove::CollectionRemove},
|
activities::community::{collection_add::CollectionAdd, collection_remove::CollectionRemove},
|
||||||
|
@ -108,6 +108,7 @@ impl ActivityHandler for CollectionAdd {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_is_public(&self.to, &self.cc)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
let community = self.community(context).await?;
|
let community = self.community(context).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context).await?;
|
verify_person_in_community(&self.actor, &community, context).await?;
|
||||||
|
@ -117,7 +118,6 @@ impl ActivityHandler for CollectionAdd {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
let (community, collection_type) =
|
let (community, collection_type) =
|
||||||
Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?;
|
Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?;
|
||||||
match collection_type {
|
match collection_type {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||||
protocol::{activities::community::collection_remove::CollectionRemove, InCommunity},
|
protocol::{activities::community::collection_remove::CollectionRemove, InCommunity},
|
||||||
};
|
};
|
||||||
|
@ -101,6 +101,7 @@ impl ActivityHandler for CollectionRemove {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_is_public(&self.to, &self.cc)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
let community = self.community(context).await?;
|
let community = self.community(context).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context).await?;
|
verify_person_in_community(&self.actor, &community, context).await?;
|
||||||
|
@ -110,7 +111,6 @@ impl ActivityHandler for CollectionRemove {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
let (community, collection_type) =
|
let (community, collection_type) =
|
||||||
Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?;
|
Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?;
|
||||||
match collection_type {
|
match collection_type {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
protocol::{
|
protocol::{
|
||||||
activities::community::lock_page::{LockPage, LockType, UndoLockPage},
|
activities::community::lock_page::{LockPage, LockType, UndoLockPage},
|
||||||
InCommunity,
|
InCommunity,
|
||||||
|
@ -79,6 +79,7 @@ impl ActivityHandler for UndoLockPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_is_public(&self.to, &self.cc)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
let community = self.community(context).await?;
|
let community = self.community(context).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context).await?;
|
verify_person_in_community(&self.actor, &community, context).await?;
|
||||||
|
@ -94,7 +95,6 @@ impl ActivityHandler for UndoLockPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
let form = PostUpdateForm::builder().locked(Some(false)).build();
|
let form = PostUpdateForm::builder().locked(Some(false)).build();
|
||||||
let post = self.object.object.dereference(context).await?;
|
let post = self.object.object.dereference(context).await?;
|
||||||
Post::update(&mut context.pool(), post.id, &form).await?;
|
Post::update(&mut context.pool(), post.id, &form).await?;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
|
activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::{activities::community::report::Report, InCommunity},
|
protocol::{activities::community::report::Report, InCommunity},
|
||||||
PostOrComment,
|
PostOrComment,
|
||||||
|
@ -115,6 +115,7 @@ impl ActivityHandler for Report {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
let community = self.community(context).await?;
|
let community = self.community(context).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context).await?;
|
verify_person_in_community(&self.actor, &community, context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -122,7 +123,6 @@ impl ActivityHandler for Report {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, true, context).await?;
|
|
||||||
let actor = self.actor.dereference(context).await?;
|
let actor = self.actor.dereference(context).await?;
|
||||||
match self.object.dereference(context).await? {
|
match self.object.dereference(context).await? {
|
||||||
PostOrComment::Post(post) => {
|
PostOrComment::Post(post) => {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::{activities::community::update::UpdateCommunity, InCommunity},
|
protocol::{activities::community::update::UpdateCommunity, InCommunity},
|
||||||
SendActivity,
|
SendActivity,
|
||||||
|
@ -82,6 +82,7 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_is_public(&self.to, &self.cc)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
let community = self.community(context).await?;
|
let community = self.community(context).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context).await?;
|
verify_person_in_community(&self.actor, &community, context).await?;
|
||||||
|
@ -92,7 +93,6 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
let community = self.community(context).await?;
|
let community = self.community(context).await?;
|
||||||
|
|
||||||
let community_update_form = self.object.into_update_form();
|
let community_update_form = self.object.into_update_form();
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
mentions::MentionOrValue,
|
mentions::MentionOrValue,
|
||||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
|
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::{
|
protocol::{
|
||||||
|
@ -154,6 +154,7 @@ impl ActivityHandler for CreateOrUpdateNote {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_is_public(&self.to, &self.cc)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
let post = self.object.get_parents(context).await?.0;
|
let post = self.object.get_parents(context).await?.0;
|
||||||
let community = self.community(context).await?;
|
let community = self.community(context).await?;
|
||||||
|
@ -169,7 +170,6 @@ impl ActivityHandler for CreateOrUpdateNote {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
// Need to do this check here instead of Note::from_json because we need the person who
|
// Need to do this check here instead of Note::from_json because we need the person who
|
||||||
// send the activity, not the comment author.
|
// send the activity, not the comment author.
|
||||||
let existing_comment = self.object.id.dereference_local(context).await.ok();
|
let existing_comment = self.object.id.dereference_local(context).await.ok();
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||||
protocol::{
|
protocol::{
|
||||||
activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
|
activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
|
||||||
|
@ -24,7 +24,7 @@ use activitypub_federation::{
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{CreatePost, EditPost, PostResponse},
|
post::{EditPost, PostResponse},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::PostAggregates,
|
aggregates::structs::PostAggregates,
|
||||||
|
@ -39,25 +39,6 @@ use lemmy_db_schema::{
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for CreatePost {
|
|
||||||
type Response = PostResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
_request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
CreateOrUpdatePage::send(
|
|
||||||
&response.post_view.post,
|
|
||||||
response.post_view.creator.id,
|
|
||||||
CreateOrUpdateType::Create,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl SendActivity for EditPost {
|
impl SendActivity for EditPost {
|
||||||
type Response = PostResponse;
|
type Response = PostResponse;
|
||||||
|
@ -68,10 +49,10 @@ impl SendActivity for EditPost {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
CreateOrUpdatePage::send(
|
CreateOrUpdatePage::send(
|
||||||
&response.post_view.post,
|
response.post_view.post.clone(),
|
||||||
response.post_view.creator.id,
|
response.post_view.creator.id,
|
||||||
CreateOrUpdateType::Update,
|
CreateOrUpdateType::Update,
|
||||||
context,
|
context.reset_request_count(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -102,12 +83,12 @@ impl CreateOrUpdatePage {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub(crate) async fn send(
|
pub(crate) async fn send(
|
||||||
post: &Post,
|
post: Post,
|
||||||
person_id: PersonId,
|
person_id: PersonId,
|
||||||
kind: CreateOrUpdateType,
|
kind: CreateOrUpdateType,
|
||||||
context: &Data<LemmyContext>,
|
context: Data<LemmyContext>,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let post = ApubPost(post.clone());
|
let post = ApubPost(post);
|
||||||
let community_id = post.community_id;
|
let community_id = post.community_id;
|
||||||
let person: ApubPerson = Person::read(&mut context.pool(), person_id).await?.into();
|
let person: ApubPerson = Person::read(&mut context.pool(), person_id).await?.into();
|
||||||
let community: ApubCommunity = Community::read(&mut context.pool(), community_id)
|
let community: ApubCommunity = Community::read(&mut context.pool(), community_id)
|
||||||
|
@ -115,8 +96,8 @@ impl CreateOrUpdatePage {
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let create_or_update =
|
let create_or_update =
|
||||||
CreateOrUpdatePage::new(post, &person, &community, kind, context).await?;
|
CreateOrUpdatePage::new(post, &person, &community, kind, &context).await?;
|
||||||
let is_mod_action = create_or_update.object.is_mod_action(context).await?;
|
let is_mod_action = create_or_update.object.is_mod_action(&context).await?;
|
||||||
let activity = AnnouncableActivities::CreateOrUpdatePost(create_or_update);
|
let activity = AnnouncableActivities::CreateOrUpdatePost(create_or_update);
|
||||||
send_activity_in_community(
|
send_activity_in_community(
|
||||||
activity,
|
activity,
|
||||||
|
@ -124,7 +105,7 @@ impl CreateOrUpdatePage {
|
||||||
&community,
|
&community,
|
||||||
vec![],
|
vec![],
|
||||||
is_mod_action,
|
is_mod_action,
|
||||||
context,
|
&context,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -146,6 +127,7 @@ impl ActivityHandler for CreateOrUpdatePage {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_is_public(&self.to, &self.cc)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
let community = self.community(context).await?;
|
let community = self.community(context).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context).await?;
|
verify_person_in_community(&self.actor, &community, context).await?;
|
||||||
|
@ -180,7 +162,6 @@ impl ActivityHandler for CreateOrUpdatePage {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
let post = ApubPost::from_json(self.object, context).await?;
|
let post = ApubPost::from_json(self.object, context).await?;
|
||||||
|
|
||||||
// author likes their own post by default
|
// author likes their own post by default
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_person},
|
activities::{generate_activity_id, send_lemmy_activity, verify_person},
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
||||||
protocol::activities::{
|
protocol::activities::{
|
||||||
create_or_update::chat_message::CreateOrUpdateChatMessage,
|
create_or_update::chat_message::CreateOrUpdateChatMessage,
|
||||||
|
@ -109,6 +109,7 @@ impl ActivityHandler for CreateOrUpdateChatMessage {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_person(&self.actor, context).await?;
|
verify_person(&self.actor, context).await?;
|
||||||
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
|
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
|
||||||
verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?;
|
verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?;
|
||||||
|
@ -118,7 +119,6 @@ impl ActivityHandler for CreateOrUpdateChatMessage {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, true, context).await?;
|
|
||||||
ApubPrivateMessage::from_json(self.object, context).await?;
|
ApubPrivateMessage::from_json(self.object, context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
|
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
|
||||||
generate_activity_id,
|
generate_activity_id,
|
||||||
},
|
},
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
protocol::{activities::deletion::delete::Delete, IdOrNestedObject},
|
protocol::{activities::deletion::delete::Delete, IdOrNestedObject},
|
||||||
};
|
};
|
||||||
|
@ -43,13 +43,13 @@ impl ActivityHandler for Delete {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_delete_activity(self, self.summary.is_some(), context).await?;
|
verify_delete_activity(self, self.summary.is_some(), context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
if let Some(reason) = self.summary {
|
if let Some(reason) = self.summary {
|
||||||
// We set reason to empty string if it doesn't exist, to distinguish between delete and
|
// We set reason to empty string if it doesn't exist, to distinguish between delete and
|
||||||
// remove. Here we change it back to option, so we don't write it to db.
|
// remove. Here we change it back to option, so we don't write it to db.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
|
activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{instance::remote_instance_inboxes, person::ApubPerson},
|
objects::{instance::remote_instance_inboxes, person::ApubPerson},
|
||||||
protocol::activities::deletion::delete_user::DeleteUser,
|
protocol::activities::deletion::delete_user::DeleteUser,
|
||||||
SendActivity,
|
SendActivity,
|
||||||
|
@ -73,6 +73,7 @@ impl ActivityHandler for DeleteUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_is_public(&self.to, &[])?;
|
verify_is_public(&self.to, &[])?;
|
||||||
verify_person(&self.actor, context).await?;
|
verify_person(&self.actor, context).await?;
|
||||||
verify_urls_match(self.actor.inner(), self.object.inner())?;
|
verify_urls_match(self.actor.inner(), self.object.inner())?;
|
||||||
|
@ -80,7 +81,6 @@ impl ActivityHandler for DeleteUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
let actor = self.actor.dereference(context).await?;
|
let actor = self.actor.dereference(context).await?;
|
||||||
delete_user_account(
|
delete_user_account(
|
||||||
actor.id,
|
actor.id,
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
|
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
|
||||||
generate_activity_id,
|
generate_activity_id,
|
||||||
},
|
},
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||||
};
|
};
|
||||||
|
@ -42,6 +42,7 @@ impl ActivityHandler for UndoDelete {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verify(&self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
async fn verify(&self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||||
|
insert_received_activity(&self.id, data).await?;
|
||||||
self.object.verify(data).await?;
|
self.object.verify(data).await?;
|
||||||
verify_delete_activity(&self.object, self.object.summary.is_some(), data).await?;
|
verify_delete_activity(&self.object, self.object.summary.is_some(), data).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -49,7 +50,6 @@ impl ActivityHandler for UndoDelete {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, false, context).await?;
|
|
||||||
if self.object.summary.is_some() {
|
if self.object.summary.is_some() {
|
||||||
UndoDelete::receive_undo_remove_action(
|
UndoDelete::receive_undo_remove_action(
|
||||||
&self.actor.dereference(context).await?,
|
&self.actor.dereference(context).await?,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity},
|
activities::{generate_activity_id, send_lemmy_activity},
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
protocol::activities::following::{accept::AcceptFollow, follow::Follow},
|
protocol::activities::following::{accept::AcceptFollow, follow::Follow},
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
|
@ -50,6 +50,7 @@ impl ActivityHandler for AcceptFollow {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_urls_match(self.actor.inner(), self.object.object.inner())?;
|
verify_urls_match(self.actor.inner(), self.object.object.inner())?;
|
||||||
self.object.verify(context).await?;
|
self.object.verify(context).await?;
|
||||||
if let Some(to) = &self.to {
|
if let Some(to) = &self.to {
|
||||||
|
@ -60,7 +61,6 @@ impl ActivityHandler for AcceptFollow {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, true, context).await?;
|
|
||||||
let community = self.actor.dereference(context).await?;
|
let community = self.actor.dereference(context).await?;
|
||||||
let person = self.object.actor.dereference(context).await?;
|
let person = self.object.actor.dereference(context).await?;
|
||||||
// This will throw an error if no follow was requested
|
// This will throw an error if no follow was requested
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
fetcher::user_or_community::UserOrCommunity,
|
fetcher::user_or_community::UserOrCommunity,
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::following::{
|
protocol::activities::following::{
|
||||||
accept::AcceptFollow,
|
accept::AcceptFollow,
|
||||||
|
@ -90,6 +90,7 @@ impl ActivityHandler for Follow {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_person(&self.actor, context).await?;
|
verify_person(&self.actor, context).await?;
|
||||||
let object = self.object.dereference(context).await?;
|
let object = self.object.dereference(context).await?;
|
||||||
if let UserOrCommunity::Community(c) = object {
|
if let UserOrCommunity::Community(c) = object {
|
||||||
|
@ -103,7 +104,6 @@ impl ActivityHandler for Follow {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, true, context).await?;
|
|
||||||
let actor = self.actor.dereference(context).await?;
|
let actor = self.actor.dereference(context).await?;
|
||||||
let object = self.object.dereference(context).await?;
|
let object = self.object.dereference(context).await?;
|
||||||
match object {
|
match object {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_person},
|
activities::{generate_activity_id, send_lemmy_activity, verify_person},
|
||||||
fetcher::user_or_community::UserOrCommunity,
|
fetcher::user_or_community::UserOrCommunity,
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::following::{follow::Follow, undo_follow::UndoFollow},
|
protocol::activities::following::{follow::Follow, undo_follow::UndoFollow},
|
||||||
};
|
};
|
||||||
|
@ -60,6 +60,7 @@ impl ActivityHandler for UndoFollow {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
|
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
|
||||||
verify_person(&self.actor, context).await?;
|
verify_person(&self.actor, context).await?;
|
||||||
self.object.verify(context).await?;
|
self.object.verify(context).await?;
|
||||||
|
@ -71,7 +72,6 @@ impl ActivityHandler for UndoFollow {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, true, context).await?;
|
|
||||||
let person = self.actor.dereference(context).await?;
|
let person = self.actor.dereference(context).await?;
|
||||||
let object = self.object.object.dereference(context).await?;
|
let object = self.object.object.dereference(context).await?;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
insert_activity,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
|
||||||
CONTEXT,
|
CONTEXT,
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
|
@ -12,12 +12,28 @@ use activitypub_federation::{
|
||||||
traits::{ActivityHandler, Actor},
|
traits::{ActivityHandler, Actor},
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lemmy_api_common::context::LemmyContext;
|
use lemmy_api_common::{
|
||||||
use lemmy_db_schema::{newtypes::CommunityId, source::community::Community};
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
newtypes::CommunityId,
|
||||||
|
source::{
|
||||||
|
activity::{SentActivity, SentActivityForm},
|
||||||
|
community::Community,
|
||||||
|
instance::Instance,
|
||||||
|
},
|
||||||
|
};
|
||||||
use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
|
use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::{
|
||||||
|
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||||
|
spawn_try_task,
|
||||||
|
SYNCHRONOUS_FEDERATION,
|
||||||
|
};
|
||||||
|
use moka::future::Cache;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::ops::Deref;
|
use std::{ops::Deref, sync::Arc, time::Duration};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -30,6 +46,10 @@ pub mod following;
|
||||||
pub mod unfederated;
|
pub mod unfederated;
|
||||||
pub mod voting;
|
pub mod voting;
|
||||||
|
|
||||||
|
/// Amount of time that the list of dead instances is cached. This is only updated once a day,
|
||||||
|
/// so there is no harm in caching it for a longer time.
|
||||||
|
pub static DEAD_INSTANCE_LIST_CACHE_DURATION: Duration = Duration::from_secs(30 * 60);
|
||||||
|
|
||||||
/// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
|
/// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
|
||||||
/// doesn't have a site ban.
|
/// doesn't have a site ban.
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
|
@ -148,7 +168,7 @@ async fn send_lemmy_activity<Activity, ActorT>(
|
||||||
data: &Data<LemmyContext>,
|
data: &Data<LemmyContext>,
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
actor: &ActorT,
|
actor: &ActorT,
|
||||||
inbox: Vec<Url>,
|
mut inbox: Vec<Url>,
|
||||||
sensitive: bool,
|
sensitive: bool,
|
||||||
) -> Result<(), LemmyError>
|
) -> Result<(), LemmyError>
|
||||||
where
|
where
|
||||||
|
@ -156,11 +176,62 @@ where
|
||||||
ActorT: Actor,
|
ActorT: Actor,
|
||||||
Activity: ActivityHandler<Error = LemmyError>,
|
Activity: ActivityHandler<Error = LemmyError>,
|
||||||
{
|
{
|
||||||
|
static CACHE: Lazy<Cache<(), Arc<Vec<String>>>> = Lazy::new(|| {
|
||||||
|
Cache::builder()
|
||||||
|
.max_capacity(1)
|
||||||
|
.time_to_live(DEAD_INSTANCE_LIST_CACHE_DURATION)
|
||||||
|
.build()
|
||||||
|
});
|
||||||
|
let dead_instances = CACHE
|
||||||
|
.try_get_with((), async {
|
||||||
|
Ok::<_, diesel::result::Error>(Arc::new(Instance::dead_instances(&mut data.pool()).await?))
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
inbox.retain(|i| {
|
||||||
|
let domain = i.domain().expect("has domain").to_string();
|
||||||
|
!dead_instances.contains(&domain)
|
||||||
|
});
|
||||||
info!("Sending activity {}", activity.id().to_string());
|
info!("Sending activity {}", activity.id().to_string());
|
||||||
let activity = WithContext::new(activity, CONTEXT.deref().clone());
|
let activity = WithContext::new(activity, CONTEXT.deref().clone());
|
||||||
|
|
||||||
insert_activity(activity.id(), &activity, true, sensitive, data).await?;
|
let form = SentActivityForm {
|
||||||
|
ap_id: activity.id().clone().into(),
|
||||||
|
data: serde_json::to_value(activity.clone())?,
|
||||||
|
sensitive,
|
||||||
|
};
|
||||||
|
SentActivity::create(&mut data.pool(), form).await?;
|
||||||
send_activity(activity, actor, inbox, data).await?;
|
send_activity(activity, actor, inbox, data).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn handle_outgoing_activities(context: Data<LemmyContext>) -> LemmyResult<()> {
|
||||||
|
while let Some(data) = ActivityChannel::retrieve_activity().await {
|
||||||
|
match_outgoing_activities(data, &context.reset_request_count()).await?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn match_outgoing_activities(
|
||||||
|
data: SendActivityData,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
let fed_task = match data {
|
||||||
|
SendActivityData::CreatePost(post) => {
|
||||||
|
let creator_id = post.creator_id;
|
||||||
|
CreateOrUpdatePage::send(
|
||||||
|
post,
|
||||||
|
creator_id,
|
||||||
|
CreateOrUpdateType::Create,
|
||||||
|
context.reset_request_count(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if *SYNCHRONOUS_FEDERATION {
|
||||||
|
fed_task.await?;
|
||||||
|
} else {
|
||||||
|
spawn_try_task(fed_task);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
voting::{undo_vote_comment, undo_vote_post},
|
voting::{undo_vote_comment, undo_vote_post},
|
||||||
},
|
},
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::{
|
protocol::{
|
||||||
activities::voting::{undo_vote::UndoVote, vote::Vote},
|
activities::voting::{undo_vote::UndoVote, vote::Vote},
|
||||||
|
@ -57,6 +57,7 @@ impl ActivityHandler for UndoVote {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
let community = self.community(context).await?;
|
let community = self.community(context).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context).await?;
|
verify_person_in_community(&self.actor, &community, context).await?;
|
||||||
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
|
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
|
||||||
|
@ -66,7 +67,6 @@ impl ActivityHandler for UndoVote {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, true, context).await?;
|
|
||||||
let actor = self.actor.dereference(context).await?;
|
let actor = self.actor.dereference(context).await?;
|
||||||
let object = self.object.object.dereference(context).await?;
|
let object = self.object.object.dereference(context).await?;
|
||||||
match object {
|
match object {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
voting::{vote_comment, vote_post},
|
voting::{vote_comment, vote_post},
|
||||||
},
|
},
|
||||||
insert_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::{
|
protocol::{
|
||||||
activities::voting::vote::{Vote, VoteType},
|
activities::voting::vote::{Vote, VoteType},
|
||||||
|
@ -56,6 +56,7 @@ impl ActivityHandler for Vote {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
let community = self.community(context).await?;
|
let community = self.community(context).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context).await?;
|
verify_person_in_community(&self.actor, &community, context).await?;
|
||||||
let enable_downvotes = LocalSite::read(&mut context.pool())
|
let enable_downvotes = LocalSite::read(&mut context.pool())
|
||||||
|
@ -70,7 +71,6 @@ impl ActivityHandler for Vote {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
insert_activity(&self.id, &self, false, true, context).await?;
|
|
||||||
let actor = self.actor.dereference(context).await?;
|
let actor = self.actor.dereference(context).await?;
|
||||||
let object = self.object.dereference(context).await?;
|
let object = self.object.dereference(context).await?;
|
||||||
match object {
|
match object {
|
||||||
|
|
|
@ -134,6 +134,9 @@ impl InCommunity for AnnouncableActivities {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
activity_lists::{
|
activity_lists::{
|
||||||
GroupInboxActivities,
|
GroupInboxActivities,
|
||||||
|
|
|
@ -39,7 +39,11 @@ pub async fn list_comments(
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let parent_id = data.parent_id;
|
let parent_id = data.parent_id;
|
||||||
|
|
||||||
let listing_type = listing_type_with_default(data.type_, &local_site, community_id)?;
|
let listing_type = Some(listing_type_with_default(
|
||||||
|
data.type_,
|
||||||
|
&local_site,
|
||||||
|
community_id,
|
||||||
|
)?);
|
||||||
|
|
||||||
// If a parent_id is given, fetch the comment to get the path
|
// If a parent_id is given, fetch the comment to get the path
|
||||||
let parent_path = if let Some(parent_id) = parent_id {
|
let parent_path = if let Some(parent_id) = parent_id {
|
||||||
|
@ -50,23 +54,22 @@ pub async fn list_comments(
|
||||||
|
|
||||||
let parent_path_cloned = parent_path.clone();
|
let parent_path_cloned = parent_path.clone();
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
let local_user = local_user_view.map(|l| l.local_user);
|
let comments = CommentQuery {
|
||||||
let comments = CommentQuery::builder()
|
listing_type,
|
||||||
.pool(&mut context.pool())
|
sort,
|
||||||
.listing_type(Some(listing_type))
|
max_depth,
|
||||||
.sort(sort)
|
saved_only,
|
||||||
.max_depth(max_depth)
|
community_id,
|
||||||
.saved_only(saved_only)
|
parent_path: parent_path_cloned,
|
||||||
.community_id(community_id)
|
post_id,
|
||||||
.parent_path(parent_path_cloned)
|
local_user: local_user_view.as_ref(),
|
||||||
.post_id(post_id)
|
page,
|
||||||
.local_user(local_user.as_ref())
|
limit,
|
||||||
.page(page)
|
..Default::default()
|
||||||
.limit(limit)
|
}
|
||||||
.build()
|
.list(&mut context.pool())
|
||||||
.list()
|
.await
|
||||||
.await
|
.with_lemmy_type(LemmyErrorType::CouldntGetComments)?;
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntGetComments)?;
|
|
||||||
|
|
||||||
Ok(Json(GetCommentsResponse { comments }))
|
Ok(Json(GetCommentsResponse { comments }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use actix_web::web::{Json, Query};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{GetPosts, GetPostsResponse},
|
post::{GetPosts, GetPostsResponse},
|
||||||
utils::{check_private_instance, is_mod_or_admin_opt, local_user_view_from_jwt_opt},
|
utils::{check_private_instance, local_user_view_from_jwt_opt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::source::{community::Community, local_site::LocalSite};
|
use lemmy_db_schema::source::{community::Community, local_site::LocalSite};
|
||||||
use lemmy_db_views::post_view::PostQuery;
|
use lemmy_db_views::post_view::PostQuery;
|
||||||
|
@ -36,27 +36,25 @@ pub async fn list_posts(
|
||||||
};
|
};
|
||||||
let saved_only = data.saved_only;
|
let saved_only = data.saved_only;
|
||||||
|
|
||||||
let listing_type = listing_type_with_default(data.type_, &local_site, community_id)?;
|
let listing_type = Some(listing_type_with_default(
|
||||||
|
data.type_,
|
||||||
|
&local_site,
|
||||||
|
community_id,
|
||||||
|
)?);
|
||||||
|
|
||||||
let is_mod_or_admin =
|
let posts = PostQuery {
|
||||||
is_mod_or_admin_opt(&mut context.pool(), local_user_view.as_ref(), community_id)
|
local_user: local_user_view.as_ref(),
|
||||||
.await
|
listing_type,
|
||||||
.is_ok();
|
sort,
|
||||||
|
community_id,
|
||||||
let posts = PostQuery::builder()
|
saved_only,
|
||||||
.pool(&mut context.pool())
|
page,
|
||||||
.local_user(local_user_view.map(|l| l.local_user).as_ref())
|
limit,
|
||||||
.listing_type(Some(listing_type))
|
..Default::default()
|
||||||
.sort(sort)
|
}
|
||||||
.community_id(community_id)
|
.list(&mut context.pool())
|
||||||
.saved_only(saved_only)
|
.await
|
||||||
.page(page)
|
.with_lemmy_type(LemmyErrorType::CouldntGetPosts)?;
|
||||||
.limit(limit)
|
|
||||||
.is_mod_or_admin(Some(is_mod_or_admin))
|
|
||||||
.build()
|
|
||||||
.list()
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntGetPosts)?;
|
|
||||||
|
|
||||||
Ok(Json(GetPostsResponse { posts }))
|
Ok(Json(GetPostsResponse { posts }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use actix_web::web::{Json, Query};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
person::{GetPersonDetails, GetPersonDetailsResponse},
|
person::{GetPersonDetails, GetPersonDetailsResponse},
|
||||||
utils::{check_private_instance, is_admin, local_user_view_from_jwt_opt},
|
utils::{check_private_instance, local_user_view_from_jwt_opt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{local_site::LocalSite, person::Person},
|
source::{local_site::LocalSite, person::Person},
|
||||||
|
@ -26,7 +26,6 @@ pub async fn read_person(
|
||||||
|
|
||||||
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await;
|
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await;
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
let is_admin = local_user_view.as_ref().map(|luv| is_admin(luv).is_ok());
|
|
||||||
|
|
||||||
check_private_instance(&local_user_view, &local_site)?;
|
check_private_instance(&local_user_view, &local_site)?;
|
||||||
|
|
||||||
|
@ -53,52 +52,42 @@ pub async fn read_person(
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let saved_only = data.saved_only;
|
let saved_only = data.saved_only;
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let local_user = local_user_view.map(|l| l.local_user);
|
// If its saved only, you don't care what creator it was
|
||||||
let local_user_clone = local_user.clone();
|
// Or, if its not saved, then you only want it for that specific creator
|
||||||
|
let creator_id = if !saved_only.unwrap_or(false) {
|
||||||
|
Some(person_details_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let posts = PostQuery::builder()
|
let posts = PostQuery {
|
||||||
.pool(&mut context.pool())
|
sort,
|
||||||
.sort(sort)
|
saved_only,
|
||||||
.saved_only(saved_only)
|
local_user: local_user_view.as_ref(),
|
||||||
.local_user(local_user.as_ref())
|
community_id,
|
||||||
.community_id(community_id)
|
is_profile_view: Some(true),
|
||||||
.is_mod_or_admin(is_admin)
|
page,
|
||||||
.page(page)
|
limit,
|
||||||
.limit(limit)
|
creator_id,
|
||||||
.creator_id(
|
..Default::default()
|
||||||
// If its saved only, you don't care what creator it was
|
}
|
||||||
// Or, if its not saved, then you only want it for that specific creator
|
.list(&mut context.pool())
|
||||||
if !saved_only.unwrap_or(false) {
|
.await?;
|
||||||
Some(person_details_id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
.list()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let comments = CommentQuery::builder()
|
let comments = CommentQuery {
|
||||||
.pool(&mut context.pool())
|
local_user: (local_user_view.as_ref()),
|
||||||
.local_user(local_user_clone.as_ref())
|
sort: (sort.map(post_to_comment_sort_type)),
|
||||||
.sort(sort.map(post_to_comment_sort_type))
|
saved_only: (saved_only),
|
||||||
.saved_only(saved_only)
|
show_deleted_and_removed: (Some(false)),
|
||||||
.show_deleted_and_removed(Some(false))
|
community_id: (community_id),
|
||||||
.community_id(community_id)
|
is_profile_view: Some(true),
|
||||||
.page(page)
|
page: (page),
|
||||||
.limit(limit)
|
limit: (limit),
|
||||||
.creator_id(
|
creator_id,
|
||||||
// If its saved only, you don't care what creator it was
|
..Default::default()
|
||||||
// Or, if its not saved, then you only want it for that specific creator
|
}
|
||||||
if !saved_only.unwrap_or(false) {
|
.list(&mut context.pool())
|
||||||
Some(person_details_id)
|
.await?;
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
.list()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let moderates =
|
let moderates =
|
||||||
CommunityModeratorView::for_person(&mut context.pool(), person_details_id).await?;
|
CommunityModeratorView::for_person(&mut context.pool(), person_details_id).await?;
|
||||||
|
|
|
@ -50,119 +50,116 @@ pub async fn search(
|
||||||
data.community_id
|
data.community_id
|
||||||
};
|
};
|
||||||
let creator_id = data.creator_id;
|
let creator_id = data.creator_id;
|
||||||
let local_user = local_user_view.map(|l| l.local_user);
|
let local_user = local_user_view.as_ref().map(|l| l.local_user.clone());
|
||||||
match search_type {
|
match search_type {
|
||||||
SearchType::Posts => {
|
SearchType::Posts => {
|
||||||
posts = PostQuery::builder()
|
posts = PostQuery {
|
||||||
.pool(&mut context.pool())
|
sort: (sort),
|
||||||
.sort(sort)
|
listing_type: (listing_type),
|
||||||
.listing_type(listing_type)
|
community_id: (community_id),
|
||||||
.community_id(community_id)
|
creator_id: (creator_id),
|
||||||
.creator_id(creator_id)
|
local_user: (local_user_view.as_ref()),
|
||||||
.local_user(local_user.as_ref())
|
search_term: (Some(q)),
|
||||||
.search_term(Some(q))
|
page: (page),
|
||||||
.is_mod_or_admin(is_admin)
|
limit: (limit),
|
||||||
.page(page)
|
..Default::default()
|
||||||
.limit(limit)
|
}
|
||||||
.build()
|
.list(&mut context.pool())
|
||||||
.list()
|
.await?;
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
SearchType::Comments => {
|
SearchType::Comments => {
|
||||||
comments = CommentQuery::builder()
|
comments = CommentQuery {
|
||||||
.pool(&mut context.pool())
|
sort: (sort.map(post_to_comment_sort_type)),
|
||||||
.sort(sort.map(post_to_comment_sort_type))
|
listing_type: (listing_type),
|
||||||
.listing_type(listing_type)
|
search_term: (Some(q)),
|
||||||
.search_term(Some(q))
|
community_id: (community_id),
|
||||||
.community_id(community_id)
|
creator_id: (creator_id),
|
||||||
.creator_id(creator_id)
|
local_user: (local_user_view.as_ref()),
|
||||||
.local_user(local_user.as_ref())
|
page: (page),
|
||||||
.page(page)
|
limit: (limit),
|
||||||
.limit(limit)
|
..Default::default()
|
||||||
.build()
|
}
|
||||||
.list()
|
.list(&mut context.pool())
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
SearchType::Communities => {
|
SearchType::Communities => {
|
||||||
communities = CommunityQuery::builder()
|
communities = CommunityQuery {
|
||||||
.pool(&mut context.pool())
|
sort: (sort),
|
||||||
.sort(sort)
|
listing_type: (listing_type),
|
||||||
.listing_type(listing_type)
|
search_term: (Some(q)),
|
||||||
.search_term(Some(q))
|
local_user: (local_user.as_ref()),
|
||||||
.local_user(local_user.as_ref())
|
is_mod_or_admin: (is_admin),
|
||||||
.is_mod_or_admin(is_admin)
|
page: (page),
|
||||||
.page(page)
|
limit: (limit),
|
||||||
.limit(limit)
|
..Default::default()
|
||||||
.build()
|
}
|
||||||
.list()
|
.list(&mut context.pool())
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
SearchType::Users => {
|
SearchType::Users => {
|
||||||
users = PersonQuery::builder()
|
users = PersonQuery {
|
||||||
.pool(&mut context.pool())
|
sort: (sort),
|
||||||
.sort(sort)
|
search_term: (Some(q)),
|
||||||
.search_term(Some(q))
|
page: (page),
|
||||||
.page(page)
|
limit: (limit),
|
||||||
.limit(limit)
|
}
|
||||||
.build()
|
.list(&mut context.pool())
|
||||||
.list()
|
.await?;
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
SearchType::All => {
|
SearchType::All => {
|
||||||
// If the community or creator is included, dont search communities or users
|
// If the community or creator is included, dont search communities or users
|
||||||
let community_or_creator_included =
|
let community_or_creator_included =
|
||||||
data.community_id.is_some() || data.community_name.is_some() || data.creator_id.is_some();
|
data.community_id.is_some() || data.community_name.is_some() || data.creator_id.is_some();
|
||||||
|
|
||||||
let local_user_ = local_user.clone();
|
let q = data.q.clone();
|
||||||
posts = PostQuery::builder()
|
|
||||||
.pool(&mut context.pool())
|
posts = PostQuery {
|
||||||
.sort(sort)
|
sort: (sort),
|
||||||
.listing_type(listing_type)
|
listing_type: (listing_type),
|
||||||
.community_id(community_id)
|
community_id: (community_id),
|
||||||
.creator_id(creator_id)
|
creator_id: (creator_id),
|
||||||
.local_user(local_user_.as_ref())
|
local_user: (local_user_view.as_ref()),
|
||||||
.search_term(Some(q))
|
search_term: (Some(q)),
|
||||||
.is_mod_or_admin(is_admin)
|
page: (page),
|
||||||
.page(page)
|
limit: (limit),
|
||||||
.limit(limit)
|
..Default::default()
|
||||||
.build()
|
}
|
||||||
.list()
|
.list(&mut context.pool())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let q = data.q.clone();
|
let q = data.q.clone();
|
||||||
|
|
||||||
let local_user_ = local_user.clone();
|
comments = CommentQuery {
|
||||||
comments = CommentQuery::builder()
|
sort: (sort.map(post_to_comment_sort_type)),
|
||||||
.pool(&mut context.pool())
|
listing_type: (listing_type),
|
||||||
.sort(sort.map(post_to_comment_sort_type))
|
search_term: (Some(q)),
|
||||||
.listing_type(listing_type)
|
community_id: (community_id),
|
||||||
.search_term(Some(q))
|
creator_id: (creator_id),
|
||||||
.community_id(community_id)
|
local_user: (local_user_view.as_ref()),
|
||||||
.creator_id(creator_id)
|
page: (page),
|
||||||
.local_user(local_user_.as_ref())
|
limit: (limit),
|
||||||
.page(page)
|
..Default::default()
|
||||||
.limit(limit)
|
}
|
||||||
.build()
|
.list(&mut context.pool())
|
||||||
.list()
|
.await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
let q = data.q.clone();
|
let q = data.q.clone();
|
||||||
|
|
||||||
communities = if community_or_creator_included {
|
communities = if community_or_creator_included {
|
||||||
vec![]
|
vec![]
|
||||||
} else {
|
} else {
|
||||||
CommunityQuery::builder()
|
CommunityQuery {
|
||||||
.pool(&mut context.pool())
|
sort: (sort),
|
||||||
.sort(sort)
|
listing_type: (listing_type),
|
||||||
.listing_type(listing_type)
|
search_term: (Some(q)),
|
||||||
.search_term(Some(q))
|
local_user: (local_user.as_ref()),
|
||||||
.local_user(local_user.as_ref())
|
is_mod_or_admin: (is_admin),
|
||||||
.is_mod_or_admin(is_admin)
|
page: (page),
|
||||||
.page(page)
|
limit: (limit),
|
||||||
.limit(limit)
|
..Default::default()
|
||||||
.build()
|
}
|
||||||
.list()
|
.list(&mut context.pool())
|
||||||
.await?
|
.await?
|
||||||
};
|
};
|
||||||
|
|
||||||
let q = data.q.clone();
|
let q = data.q.clone();
|
||||||
|
@ -170,31 +167,29 @@ pub async fn search(
|
||||||
users = if community_or_creator_included {
|
users = if community_or_creator_included {
|
||||||
vec![]
|
vec![]
|
||||||
} else {
|
} else {
|
||||||
PersonQuery::builder()
|
PersonQuery {
|
||||||
.pool(&mut context.pool())
|
sort: (sort),
|
||||||
.sort(sort)
|
search_term: (Some(q)),
|
||||||
.search_term(Some(q))
|
page: (page),
|
||||||
.page(page)
|
limit: (limit),
|
||||||
.limit(limit)
|
}
|
||||||
.build()
|
.list(&mut context.pool())
|
||||||
.list()
|
.await?
|
||||||
.await?
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
SearchType::Url => {
|
SearchType::Url => {
|
||||||
posts = PostQuery::builder()
|
posts = PostQuery {
|
||||||
.pool(&mut context.pool())
|
sort: (sort),
|
||||||
.sort(sort)
|
listing_type: (listing_type),
|
||||||
.listing_type(listing_type)
|
community_id: (community_id),
|
||||||
.community_id(community_id)
|
creator_id: (creator_id),
|
||||||
.creator_id(creator_id)
|
url_search: (Some(q)),
|
||||||
.url_search(Some(q))
|
page: (page),
|
||||||
.is_mod_or_admin(is_admin)
|
limit: (limit),
|
||||||
.page(page)
|
..Default::default()
|
||||||
.limit(limit)
|
}
|
||||||
.build()
|
.list(&mut context.pool())
|
||||||
.list()
|
.await?;
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -78,18 +78,20 @@ impl Collection for ApubCommunityModerators {
|
||||||
|
|
||||||
// Add new mods to database which have been added to moderators collection
|
// Add new mods to database which have been added to moderators collection
|
||||||
for mod_id in apub.ordered_items {
|
for mod_id in apub.ordered_items {
|
||||||
let mod_user: ApubPerson = mod_id.dereference(data).await?;
|
// Ignore errors as mod accounts might be deleted or instances unavailable.
|
||||||
|
let mod_user: Option<ApubPerson> = mod_id.dereference(data).await.ok();
|
||||||
if !current_moderators
|
if let Some(mod_user) = mod_user {
|
||||||
.iter()
|
if !current_moderators
|
||||||
.map(|c| c.moderator.actor_id.clone())
|
.iter()
|
||||||
.any(|x| x == mod_user.actor_id)
|
.map(|c| c.moderator.actor_id.clone())
|
||||||
{
|
.any(|x| x == mod_user.actor_id)
|
||||||
let community_moderator_form = CommunityModeratorForm {
|
{
|
||||||
community_id: owner.id,
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
person_id: mod_user.id,
|
community_id: owner.id,
|
||||||
};
|
person_id: mod_user.id,
|
||||||
CommunityModerator::join(&mut data.pool(), &community_moderator_form).await?;
|
};
|
||||||
|
CommunityModerator::join(&mut data.pool(), &community_moderator_form).await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +102,9 @@ impl Collection for ApubCommunityModerators {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{
|
||||||
|
|
|
@ -13,7 +13,7 @@ use activitypub_federation::{
|
||||||
use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
|
use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use lemmy_api_common::context::LemmyContext;
|
use lemmy_api_common::context::LemmyContext;
|
||||||
use lemmy_db_schema::source::activity::Activity;
|
use lemmy_db_schema::source::activity::SentActivity;
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult};
|
use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -88,12 +88,10 @@ pub(crate) async fn get_activity(
|
||||||
info.id
|
info.id
|
||||||
))?
|
))?
|
||||||
.into();
|
.into();
|
||||||
let activity = Activity::read_from_apub_id(&mut context.pool(), &activity_id).await?;
|
let activity = SentActivity::read_from_apub_id(&mut context.pool(), &activity_id).await?;
|
||||||
|
|
||||||
let sensitive = activity.sensitive;
|
let sensitive = activity.sensitive;
|
||||||
if !activity.local {
|
if sensitive {
|
||||||
Err(err_object_not_local())
|
|
||||||
} else if sensitive {
|
|
||||||
Ok(HttpResponse::Forbidden().finish())
|
Ok(HttpResponse::Forbidden().finish())
|
||||||
} else {
|
} else {
|
||||||
create_apub_response(&activity.data)
|
create_apub_response(&activity.data)
|
||||||
|
|
|
@ -3,18 +3,12 @@ use activitypub_federation::config::{Data, UrlVerifier};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use lemmy_api_common::context::LemmyContext;
|
use lemmy_api_common::context::LemmyContext;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{activity::ReceivedActivity, instance::Instance, local_site::LocalSite},
|
||||||
activity::{Activity, ActivityInsertForm},
|
|
||||||
instance::Instance,
|
|
||||||
local_site::LocalSite,
|
|
||||||
},
|
|
||||||
traits::Crud,
|
|
||||||
utils::{ActualDbPool, DbPool},
|
utils::{ActualDbPool, DbPool},
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult};
|
use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult};
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use serde::Serialize;
|
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -178,30 +172,16 @@ pub(crate) async fn check_apub_id_valid_with_strictness(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store a sent or received activity in the database.
|
/// Store received activities in the database.
|
||||||
///
|
///
|
||||||
/// Stored activities are served over the HTTP endpoint `GET /activities/{type_}/{id}`. This also
|
/// This ensures that the same activity doesnt get received and processed more than once, which
|
||||||
/// ensures that the same activity cannot be received more than once.
|
/// would be a waste of resources.
|
||||||
#[tracing::instrument(skip(data, activity))]
|
#[tracing::instrument(skip(data))]
|
||||||
async fn insert_activity<T>(
|
async fn insert_received_activity(
|
||||||
ap_id: &Url,
|
ap_id: &Url,
|
||||||
activity: &T,
|
|
||||||
local: bool,
|
|
||||||
sensitive: bool,
|
|
||||||
data: &Data<LemmyContext>,
|
data: &Data<LemmyContext>,
|
||||||
) -> Result<(), LemmyError>
|
) -> Result<(), LemmyError> {
|
||||||
where
|
ReceivedActivity::create(&mut data.pool(), &ap_id.clone().into()).await?;
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
let ap_id = ap_id.clone().into();
|
|
||||||
let form = ActivityInsertForm {
|
|
||||||
ap_id,
|
|
||||||
data: serde_json::to_value(activity)?,
|
|
||||||
local: Some(local),
|
|
||||||
sensitive: Some(sensitive),
|
|
||||||
updated: None,
|
|
||||||
};
|
|
||||||
Activity::create(&mut data.pool(), &form).await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -179,6 +179,9 @@ impl Object for ApubComment {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{
|
||||||
|
|
|
@ -204,6 +204,9 @@ impl ApubCommunity {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{instance::tests::parse_lemmy_instance, tests::init_context},
|
objects::{instance::tests::parse_lemmy_instance, tests::init_context},
|
||||||
|
|
|
@ -206,6 +206,9 @@ pub(crate) async fn remote_instance_inboxes(pool: &mut DbPool<'_>) -> Result<Vec
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{objects::tests::init_context, protocol::tests::file_to_json_object};
|
use crate::{objects::tests::init_context, protocol::tests::file_to_json_object};
|
||||||
use lemmy_db_schema::traits::Crud;
|
use lemmy_db_schema::traits::Crud;
|
||||||
|
|
|
@ -54,6 +54,9 @@ pub(crate) fn verify_is_remote_object(id: &Url, settings: &Settings) -> Result<(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use activitypub_federation::config::{Data, FederationConfig};
|
use activitypub_federation::config::{Data, FederationConfig};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lemmy_api_common::{context::LemmyContext, request::build_user_agent};
|
use lemmy_api_common::{context::LemmyContext, request::build_user_agent};
|
||||||
|
|
|
@ -195,6 +195,9 @@ impl Actor for ApubPerson {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{
|
||||||
|
|
|
@ -280,6 +280,9 @@ impl Object for ApubPost {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{
|
||||||
|
|
|
@ -136,6 +136,9 @@ impl Object for ApubPrivateMessage {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{
|
||||||
|
|
|
@ -3,6 +3,9 @@ pub mod undo_block_user;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
|
activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
|
||||||
tests::test_parse_lemmy_item,
|
tests::test_parse_lemmy_item,
|
||||||
|
|
|
@ -7,6 +7,9 @@ pub mod update;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
activities::community::{
|
activities::community::{
|
||||||
announce::AnnounceActivity,
|
announce::AnnounceActivity,
|
||||||
|
|
|
@ -4,6 +4,9 @@ pub mod page;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
activities::create_or_update::{
|
activities::create_or_update::{
|
||||||
chat_message::CreateOrUpdateChatMessage,
|
chat_message::CreateOrUpdateChatMessage,
|
||||||
|
|
|
@ -4,6 +4,9 @@ pub mod undo_delete;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
activities::deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete},
|
activities::deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete},
|
||||||
tests::test_parse_lemmy_item,
|
tests::test_parse_lemmy_item,
|
||||||
|
|
|
@ -4,6 +4,9 @@ pub mod undo_follow;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
activities::following::{accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow},
|
activities::following::{accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow},
|
||||||
tests::test_parse_lemmy_item,
|
tests::test_parse_lemmy_item,
|
||||||
|
|
|
@ -16,6 +16,9 @@ pub enum CreateOrUpdateType {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
activities::{
|
activities::{
|
||||||
community::announce::AnnounceActivity,
|
community::announce::AnnounceActivity,
|
||||||
|
|
|
@ -3,6 +3,9 @@ pub mod vote;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
activities::voting::{undo_vote::UndoVote, vote::Vote},
|
activities::voting::{undo_vote::UndoVote, vote::Vote},
|
||||||
tests::test_parse_lemmy_item,
|
tests::test_parse_lemmy_item,
|
||||||
|
|
|
@ -6,6 +6,9 @@ pub(crate) mod group_outbox;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
collections::{
|
collections::{
|
||||||
empty_outbox::EmptyOutbox,
|
empty_outbox::EmptyOutbox,
|
||||||
|
|
|
@ -89,6 +89,9 @@ pub trait InCommunity {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use activitypub_federation::protocol::context::WithContext;
|
use activitypub_federation::protocol::context::WithContext;
|
||||||
use assert_json_diff::assert_json_include;
|
use assert_json_diff::assert_json_include;
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
|
|
|
@ -95,6 +95,9 @@ impl LanguageTag {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
objects::{
|
objects::{
|
||||||
chat_message::ChatMessage,
|
chat_message::ChatMessage,
|
||||||
|
|
|
@ -242,6 +242,9 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::protocol::{objects::page::Page, tests::test_parse_lemmy_item};
|
use crate::protocol::{objects::page::Page, tests::test_parse_lemmy_item};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -14,9 +14,27 @@ path = "src/lib.rs"
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
full = ["diesel", "diesel-derive-newtype", "diesel-derive-enum", "diesel_migrations", "bcrypt", "lemmy_utils",
|
full = [
|
||||||
"activitypub_federation", "sha2", "regex", "once_cell", "serde_json", "diesel_ltree",
|
"diesel",
|
||||||
"diesel-async", "deadpool", "ts-rs"]
|
"diesel-derive-newtype",
|
||||||
|
"diesel-derive-enum",
|
||||||
|
"diesel_migrations",
|
||||||
|
"bcrypt",
|
||||||
|
"lemmy_utils",
|
||||||
|
"activitypub_federation",
|
||||||
|
"sha2",
|
||||||
|
"regex",
|
||||||
|
"once_cell",
|
||||||
|
"serde_json",
|
||||||
|
"diesel_ltree",
|
||||||
|
"diesel-async",
|
||||||
|
"deadpool",
|
||||||
|
"ts-rs",
|
||||||
|
"tokio",
|
||||||
|
"tokio-postgres",
|
||||||
|
"tokio-postgres-rustls",
|
||||||
|
"rustls",
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
|
@ -29,25 +47,33 @@ serde_json = { workspace = true, optional = true }
|
||||||
activitypub_federation = { workspace = true, optional = true }
|
activitypub_federation = { workspace = true, optional = true }
|
||||||
lemmy_utils = { workspace = true, optional = true }
|
lemmy_utils = { workspace = true, optional = true }
|
||||||
bcrypt = { workspace = true, optional = true }
|
bcrypt = { workspace = true, optional = true }
|
||||||
diesel = { workspace = true, features = ["postgres","chrono", "serde_json", "uuid"], optional = true }
|
diesel = { workspace = true, features = [
|
||||||
|
"postgres",
|
||||||
|
"chrono",
|
||||||
|
"serde_json",
|
||||||
|
"uuid",
|
||||||
|
], optional = true }
|
||||||
diesel-derive-newtype = { workspace = true, optional = true }
|
diesel-derive-newtype = { workspace = true, optional = true }
|
||||||
diesel-derive-enum = { workspace = true, optional = true }
|
diesel-derive-enum = { workspace = true, optional = true }
|
||||||
diesel_migrations = { workspace = true, optional = true }
|
diesel_migrations = { workspace = true, optional = true }
|
||||||
diesel-async = { workspace = true, features = ["postgres", "deadpool"], optional = true }
|
diesel-async = { workspace = true, features = [
|
||||||
|
"postgres",
|
||||||
|
"deadpool",
|
||||||
|
], optional = true }
|
||||||
sha2 = { workspace = true, optional = true }
|
sha2 = { workspace = true, optional = true }
|
||||||
regex = { workspace = true, optional = true }
|
regex = { workspace = true, optional = true }
|
||||||
once_cell = { workspace = true, optional = true }
|
once_cell = { workspace = true, optional = true }
|
||||||
diesel_ltree = { workspace = true, optional = true }
|
diesel_ltree = { workspace = true, optional = true }
|
||||||
typed-builder = { workspace = true }
|
typed-builder = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
tokio = { workspace = true }
|
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
deadpool = { version = "0.9.5", features = ["rt_tokio_1"], optional = true }
|
deadpool = { version = "0.9.5", features = ["rt_tokio_1"], optional = true }
|
||||||
ts-rs = { workspace = true, optional = true }
|
ts-rs = { workspace = true, optional = true }
|
||||||
rustls = { workspace = true }
|
|
||||||
futures-util = { workspace = true }
|
futures-util = { workspace = true }
|
||||||
tokio-postgres = { workspace = true }
|
tokio = { workspace = true, optional = true }
|
||||||
tokio-postgres-rustls = { workspace = true }
|
tokio-postgres = { workspace = true, optional = true }
|
||||||
|
tokio-postgres-rustls = { workspace = true, optional = true }
|
||||||
|
rustls = { workspace = true, optional = true }
|
||||||
uuid = { workspace = true, features = ["v4"] }
|
uuid = { workspace = true, features = ["v4"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -35,6 +35,9 @@ impl CommentAggregates {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
aggregates::comment_aggregates::CommentAggregates,
|
aggregates::comment_aggregates::CommentAggregates,
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -19,6 +19,9 @@ impl CommunityAggregates {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
aggregates::community_aggregates::CommunityAggregates,
|
aggregates::community_aggregates::CommunityAggregates,
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -19,6 +19,9 @@ impl PersonAggregates {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
aggregates::person_aggregates::PersonAggregates,
|
aggregates::person_aggregates::PersonAggregates,
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -35,6 +35,9 @@ impl PostAggregates {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
aggregates::post_aggregates::PostAggregates,
|
aggregates::post_aggregates::PostAggregates,
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -15,6 +15,9 @@ impl SiteAggregates {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
aggregates::site_aggregates::SiteAggregates,
|
aggregates::site_aggregates::SiteAggregates,
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -96,6 +96,8 @@ pub struct PostAggregates {
|
||||||
pub featured_local: bool,
|
pub featured_local: bool,
|
||||||
pub hot_rank: i32,
|
pub hot_rank: i32,
|
||||||
pub hot_rank_active: i32,
|
pub hot_rank_active: i32,
|
||||||
|
pub community_id: CommunityId,
|
||||||
|
pub creator_id: PersonId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
|
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|
|
@ -1,139 +1,115 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
diesel::OptionalExtension,
|
||||||
newtypes::DbUrl,
|
newtypes::DbUrl,
|
||||||
schema::activity::dsl::{activity, ap_id},
|
source::activity::{ReceivedActivity, SentActivity, SentActivityForm},
|
||||||
source::activity::{Activity, ActivityInsertForm, ActivityUpdateForm},
|
|
||||||
traits::Crud,
|
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
|
use diesel::{
|
||||||
|
dsl::insert_into,
|
||||||
|
result::{DatabaseErrorKind, Error, Error::DatabaseError},
|
||||||
|
ExpressionMethods,
|
||||||
|
QueryDsl,
|
||||||
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Crud for Activity {
|
impl Crud for Activity {
|
||||||
type InsertForm = ActivityInsertForm;
|
pub async fn create(pool: &mut DbPool<'_>, form: SentActivityForm) -> Result<Self, Error> {
|
||||||
type UpdateForm = ActivityUpdateForm;
|
use crate::schema::sent_activity::dsl::sent_activity;
|
||||||
type IdType = i32;
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, new_activity: &Self::InsertForm) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(activity)
|
insert_into(sent_activity)
|
||||||
.values(new_activity)
|
.values(form)
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update(
|
pub async fn read_from_apub_id(pool: &mut DbPool<'_>, object_id: &DbUrl) -> Result<Self, Error> {
|
||||||
pool: &mut DbPool<'_>,
|
use crate::schema::sent_activity::dsl::{ap_id, sent_activity};
|
||||||
activity_id: i32,
|
|
||||||
new_activity: &Self::UpdateForm,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
diesel::update(activity.find(activity_id))
|
sent_activity
|
||||||
.set(new_activity)
|
|
||||||
.get_result::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
async fn delete(pool: &mut DbPool<'_>, activity_id: i32) -> Result<usize, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::delete(activity.find(activity_id))
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Activity {
|
|
||||||
pub async fn read_from_apub_id(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
object_id: &DbUrl,
|
|
||||||
) -> Result<Activity, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
activity
|
|
||||||
.filter(ap_id.eq(object_id))
|
.filter(ap_id.eq(object_id))
|
||||||
.first::<Self>(conn)
|
.first::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ReceivedActivity {
|
||||||
|
pub async fn create(pool: &mut DbPool<'_>, ap_id_: &DbUrl) -> Result<(), Error> {
|
||||||
|
use crate::schema::received_activity::dsl::{ap_id, id, received_activity};
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
let res = insert_into(received_activity)
|
||||||
|
.values(ap_id.eq(ap_id_))
|
||||||
|
.on_conflict_do_nothing()
|
||||||
|
.returning(id)
|
||||||
|
.get_result::<i64>(conn)
|
||||||
|
.await
|
||||||
|
.optional()?;
|
||||||
|
if res.is_some() {
|
||||||
|
// new activity inserted successfully
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// duplicate activity
|
||||||
|
Err(DatabaseError(
|
||||||
|
DatabaseErrorKind::UniqueViolation,
|
||||||
|
Box::<String>::default(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::utils::build_db_pool_for_tests;
|
||||||
newtypes::DbUrl,
|
use serde_json::json;
|
||||||
source::{
|
|
||||||
activity::{Activity, ActivityInsertForm},
|
|
||||||
instance::Instance,
|
|
||||||
person::{Person, PersonInsertForm},
|
|
||||||
},
|
|
||||||
utils::build_db_pool_for_tests,
|
|
||||||
};
|
|
||||||
use serde_json::Value;
|
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn receive_activity_duplicate() {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
let ap_id: DbUrl = Url::parse("http://example.com/activity/531")
|
||||||
|
.unwrap()
|
||||||
|
.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
// inserting activity for first time
|
||||||
.await
|
let res = ReceivedActivity::create(pool, &ap_id).await;
|
||||||
.unwrap();
|
assert!(res.is_ok());
|
||||||
|
|
||||||
let creator_form = PersonInsertForm::builder()
|
let res = ReceivedActivity::create(pool, &ap_id).await;
|
||||||
.name("activity_creator_ pm".into())
|
assert!(res.is_err());
|
||||||
.public_key("pubkey".to_string())
|
}
|
||||||
.instance_id(inserted_instance.id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_creator = Person::create(pool, &creator_form).await.unwrap();
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn sent_activity_write_read() {
|
||||||
|
let pool = &build_db_pool_for_tests().await;
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
let ap_id: DbUrl = Url::parse("http://example.com/activity/412")
|
||||||
|
.unwrap()
|
||||||
|
.into();
|
||||||
|
let data = json!({
|
||||||
|
"key1": "0xF9BA143B95FF6D82",
|
||||||
|
"key2": "42",
|
||||||
|
});
|
||||||
|
let sensitive = false;
|
||||||
|
|
||||||
let ap_id_: DbUrl = Url::parse(
|
let form = SentActivityForm {
|
||||||
"https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
|
ap_id: ap_id.clone(),
|
||||||
)
|
data: data.clone(),
|
||||||
.unwrap()
|
sensitive,
|
||||||
.into();
|
|
||||||
let test_json: Value = serde_json::from_str(
|
|
||||||
r#"{
|
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
|
||||||
"id": "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
|
|
||||||
"type": "Delete",
|
|
||||||
"actor": "https://enterprise.lemmy.ml/u/riker",
|
|
||||||
"to": "https://www.w3.org/ns/activitystreams#Public",
|
|
||||||
"cc": [
|
|
||||||
"https://enterprise.lemmy.ml/c/main/"
|
|
||||||
],
|
|
||||||
"object": "https://enterprise.lemmy.ml/post/32"
|
|
||||||
}"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let activity_form = ActivityInsertForm {
|
|
||||||
ap_id: ap_id_.clone(),
|
|
||||||
data: test_json.clone(),
|
|
||||||
local: Some(true),
|
|
||||||
sensitive: Some(false),
|
|
||||||
updated: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_activity = Activity::create(pool, &activity_form).await.unwrap();
|
SentActivity::create(pool, form).await.unwrap();
|
||||||
|
|
||||||
let expected_activity = Activity {
|
let res = SentActivity::read_from_apub_id(pool, &ap_id).await.unwrap();
|
||||||
ap_id: ap_id_.clone(),
|
assert_eq!(res.ap_id, ap_id);
|
||||||
id: inserted_activity.id,
|
assert_eq!(res.data, data);
|
||||||
data: test_json,
|
assert_eq!(res.sensitive, sensitive);
|
||||||
local: true,
|
|
||||||
sensitive: false,
|
|
||||||
published: inserted_activity.published,
|
|
||||||
updated: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let read_activity = Activity::read(pool, inserted_activity.id).await.unwrap();
|
|
||||||
let read_activity_by_apub_id = Activity::read_from_apub_id(pool, &ap_id_).await.unwrap();
|
|
||||||
Person::delete(pool, inserted_creator.id).await.unwrap();
|
|
||||||
Activity::delete(pool, inserted_activity.id).await.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(expected_activity, read_activity);
|
|
||||||
assert_eq!(expected_activity, read_activity_by_apub_id);
|
|
||||||
assert_eq!(expected_activity, inserted_activity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,25 +275,37 @@ impl CommunityLanguage {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let form = lang_ids
|
||||||
|
.into_iter()
|
||||||
|
.map(|language_id| CommunityLanguageForm {
|
||||||
|
community_id: for_community_id,
|
||||||
|
language_id,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
conn
|
conn
|
||||||
.build_transaction()
|
.build_transaction()
|
||||||
.run(|conn| {
|
.run(|conn| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
use crate::schema::community_language::dsl::{community_id, community_language};
|
use crate::schema::community_language::dsl::{community_id, community_language};
|
||||||
|
use diesel::result::DatabaseErrorKind::UniqueViolation;
|
||||||
// Clear the current languages
|
// Clear the current languages
|
||||||
delete(community_language.filter(community_id.eq(for_community_id)))
|
delete(community_language.filter(community_id.eq(for_community_id)))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for l in lang_ids {
|
let insert_res = insert_into(community_language)
|
||||||
let form = CommunityLanguageForm {
|
.values(form)
|
||||||
community_id: for_community_id,
|
.get_result::<Self>(conn)
|
||||||
language_id: l,
|
.await;
|
||||||
};
|
|
||||||
insert_into(community_language)
|
if let Err(Error::DatabaseError(UniqueViolation, _info)) = insert_res {
|
||||||
.values(form)
|
// race condition: this function was probably called simultaneously from another caller. ignore error
|
||||||
.get_result::<Self>(conn)
|
// tracing::warn!("unique error: {_info:#?}");
|
||||||
.await?;
|
// _info.constraint_name() should be = "community_language_community_id_language_id_key"
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
insert_res?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}) as _
|
}) as _
|
||||||
|
@ -372,6 +384,9 @@ async fn convert_read_languages(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
impls::actor_language::{
|
impls::actor_language::{
|
||||||
|
|
|
@ -50,6 +50,9 @@ impl CaptchaAnswer {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
source::captcha_answer::{CaptchaAnswer, CaptchaAnswerForm, CheckCaptchaAnswer},
|
source::captcha_answer::{CaptchaAnswer, CaptchaAnswerForm, CheckCaptchaAnswer},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
|
|
|
@ -247,6 +247,9 @@ impl Saveable for CommentSaved {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::LanguageId,
|
newtypes::LanguageId,
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -74,6 +74,9 @@ impl CommentReply {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
source::{
|
source::{
|
||||||
comment::{Comment, CommentInsertForm},
|
comment::{Comment, CommentInsertForm},
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue