mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-23 03:11:32 +00:00
Merge remote-tracking branch 'origin/main' into post-tags
This commit is contained in:
commit
37246e96d6
202 changed files with 5470 additions and 3370 deletions
|
@ -91,6 +91,36 @@ steps:
|
|||
when:
|
||||
- event: pull_request
|
||||
|
||||
cargo_clippy:
|
||||
image: *rust_image
|
||||
environment:
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
- rustup component add clippy
|
||||
- cargo clippy --workspace --tests --all-targets -- -D warnings
|
||||
when: *slow_check_paths
|
||||
|
||||
cargo_test:
|
||||
image: *rust_image
|
||||
environment:
|
||||
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
||||
RUST_BACKTRACE: "1"
|
||||
CARGO_HOME: .cargo_home
|
||||
LEMMY_TEST_FAST_FEDERATION: "1"
|
||||
LEMMY_CONFIG_LOCATION: ../../config/config.hjson
|
||||
commands:
|
||||
- cargo test --workspace --no-fail-fast
|
||||
when: *slow_check_paths
|
||||
|
||||
check_ts_bindings:
|
||||
image: *rust_image
|
||||
environment:
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
- ./scripts/ts_bindings_check.sh
|
||||
when:
|
||||
- event: pull_request
|
||||
|
||||
# make sure api builds with default features (used by other crates relying on lemmy api)
|
||||
check_api_common_default_features:
|
||||
image: *rust_image
|
||||
|
@ -138,26 +168,6 @@ steps:
|
|||
- diff tmp.schema crates/db_schema/src/schema.rs
|
||||
when: *slow_check_paths
|
||||
|
||||
check_db_perf_tool:
|
||||
image: *rust_image
|
||||
environment:
|
||||
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
||||
RUST_BACKTRACE: "1"
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
# same as scripts/db_perf.sh but without creating a new database server
|
||||
- cargo run --package lemmy_db_perf -- --posts 10 --read-post-pages 1
|
||||
when: *slow_check_paths
|
||||
|
||||
cargo_clippy:
|
||||
image: *rust_image
|
||||
environment:
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
- rustup component add clippy
|
||||
- cargo clippy --workspace --tests --all-targets -- -D warnings
|
||||
when: *slow_check_paths
|
||||
|
||||
cargo_build:
|
||||
image: *rust_image
|
||||
environment:
|
||||
|
@ -167,27 +177,6 @@ steps:
|
|||
- mv target/debug/lemmy_server target/lemmy_server
|
||||
when: *slow_check_paths
|
||||
|
||||
cargo_test:
|
||||
image: *rust_image
|
||||
environment:
|
||||
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
||||
RUST_BACKTRACE: "1"
|
||||
CARGO_HOME: .cargo_home
|
||||
LEMMY_TEST_FAST_FEDERATION: "1"
|
||||
LEMMY_CONFIG_LOCATION: ../../config/config.hjson
|
||||
commands:
|
||||
- cargo test --workspace --no-fail-fast
|
||||
when: *slow_check_paths
|
||||
|
||||
check_ts_bindings:
|
||||
image: *rust_image
|
||||
environment:
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
- ./scripts/ts_bindings_check.sh
|
||||
when:
|
||||
- event: pull_request
|
||||
|
||||
check_diesel_migration:
|
||||
# TODO: use willsquire/diesel-cli image when shared libraries become optional in lemmy_server
|
||||
image: *rust_image
|
||||
|
@ -221,6 +210,17 @@ steps:
|
|||
- diff before.sqldump after.sqldump
|
||||
when: *slow_check_paths
|
||||
|
||||
check_db_perf_tool:
|
||||
image: *rust_image
|
||||
environment:
|
||||
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
||||
RUST_BACKTRACE: "1"
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
# same as scripts/db_perf.sh but without creating a new database server
|
||||
- cargo run --package lemmy_db_perf -- --posts 10 --read-post-pages 1
|
||||
when: *slow_check_paths
|
||||
|
||||
run_federation_tests:
|
||||
image: node:22-bookworm-slim
|
||||
environment:
|
||||
|
@ -280,24 +280,26 @@ steps:
|
|||
# using https://github.com/pksunkara/cargo-workspaces
|
||||
publish_to_crates_io:
|
||||
image: *rust_image
|
||||
environment:
|
||||
CARGO_API_TOKEN:
|
||||
from_secret: cargo_api_token
|
||||
commands:
|
||||
- *install_binstall
|
||||
# Install cargo-workspaces
|
||||
- cargo binstall -y cargo-workspaces
|
||||
- cp -r migrations crates/db_schema/
|
||||
- cargo workspaces publish --token "$CARGO_API_TOKEN" --from-git --allow-dirty --no-verify --allow-branch "${CI_COMMIT_TAG}" --yes custom "${CI_COMMIT_TAG}"
|
||||
secrets: [cargo_api_token]
|
||||
when:
|
||||
- event: tag
|
||||
|
||||
notify_on_failure:
|
||||
notify_on_build:
|
||||
image: alpine:3
|
||||
commands:
|
||||
- apk add curl
|
||||
- "curl -d'Lemmy CI build failed: ${CI_PIPELINE_URL}' ntfy.sh/lemmy_drone_ci"
|
||||
- "curl -d'Lemmy CI build ${CI_PIPELINE_STATUS}: ${CI_PIPELINE_URL}' ntfy.sh/lemmy_drone_ci"
|
||||
when:
|
||||
- event: [pull_request, tag]
|
||||
status: failure
|
||||
status: [failure, success]
|
||||
|
||||
notify_on_tag_deploy:
|
||||
image: alpine:3
|
||||
|
|
341
Cargo.lock
generated
341
Cargo.lock
generated
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "accept-language"
|
||||
|
@ -10,9 +10,9 @@ checksum = "8f27d075294830fcab6f66e320dab524bc6d048f4a151698e153205559113772"
|
|||
|
||||
[[package]]
|
||||
name = "activitypub_federation"
|
||||
version = "0.6.0-alpha2"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4877d467ddf2fac85e9ee33aba6f2560df14125b8bfa864f85ab40e9b87753a9"
|
||||
checksum = "ee819cada736b6e26c59706f9e6ff89a48060e635c0546ff984d84baefc8c13a"
|
||||
dependencies = [
|
||||
"activitystreams-kinds",
|
||||
"actix-web",
|
||||
|
@ -137,7 +137,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -272,7 +272,7 @@ dependencies = [
|
|||
"actix-router",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -435,9 +435,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.89"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
|
@ -484,13 +484,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
|
||||
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -635,7 +635,7 @@ dependencies = [
|
|||
"regex",
|
||||
"rustc-hash 1.1.0",
|
||||
"shlex",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
"which",
|
||||
]
|
||||
|
||||
|
@ -833,9 +833,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.19"
|
||||
version = "4.5.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615"
|
||||
checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
@ -843,9 +843,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.19"
|
||||
version = "4.5.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b"
|
||||
checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -862,7 +862,7 @@ dependencies = [
|
|||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1096,7 +1096,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.11.1",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1118,20 +1118,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
|||
dependencies = [
|
||||
"darling_core 0.20.10",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deadpool"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"deadpool-runtime",
|
||||
"num_cpus",
|
||||
"retain_mut",
|
||||
"tokio",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1194,7 +1181,7 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1215,7 +1202,7 @@ dependencies = [
|
|||
"darling 0.20.10",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1225,7 +1212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1238,7 +1225,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1252,9 +1239,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "diesel"
|
||||
version = "2.1.6"
|
||||
version = "2.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2"
|
||||
checksum = "158fe8e2e68695bd615d7e4f3227c0727b151330d3e253b525086c348d055d5e"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"byteorder",
|
||||
|
@ -1268,12 +1255,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "diesel-async"
|
||||
version = "0.4.1"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acada1517534c92d3f382217b485db8a8638f111b0e3f2a2a8e26165050f77be"
|
||||
checksum = "4c5c6ec8d5c7b8444d19a47161797cbe361e0fb1ee40c6a8124ec915b64a4125"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"deadpool 0.9.5",
|
||||
"deadpool",
|
||||
"diesel",
|
||||
"futures-util",
|
||||
"scoped-futures",
|
||||
|
@ -1281,6 +1268,15 @@ dependencies = [
|
|||
"tokio-postgres",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel-bind-if-some"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ed8ce9db476124d2eaf4c9db45dc6581b8e8c4c4d47d5e0f39de1fb55dfb2a7"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel-derive-enum"
|
||||
version = "2.1.0"
|
||||
|
@ -1290,7 +1286,7 @@ dependencies = [
|
|||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1301,19 +1297,20 @@ checksum = "d5adf688c584fe33726ce0e2898f608a2a92578ac94a4a92fcecf73214fe0716"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel_derives"
|
||||
version = "2.1.4"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c"
|
||||
checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4"
|
||||
dependencies = [
|
||||
"diesel_table_macro_syntax",
|
||||
"dsl_auto_type",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1328,9 +1325,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "diesel_migrations"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac"
|
||||
checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
"migrations_internals",
|
||||
|
@ -1339,11 +1336,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "diesel_table_macro_syntax"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
|
||||
checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25"
|
||||
dependencies = [
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1381,7 +1378,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1420,6 +1417,20 @@ version = "1.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||
|
||||
[[package]]
|
||||
name = "dsl_auto_type"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607"
|
||||
dependencies = [
|
||||
"darling 0.20.10",
|
||||
"either",
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dunce"
|
||||
version = "1.0.5"
|
||||
|
@ -1465,9 +1476,9 @@ checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
|
|||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
@ -1495,7 +1506,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1693,7 +1704,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1905,7 +1916,7 @@ dependencies = [
|
|||
"markup5ever 0.12.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2069,7 +2080,7 @@ dependencies = [
|
|||
"http 1.1.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper-util",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.16",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.0",
|
||||
|
@ -2114,7 +2125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "8215279f83f9b829403812f845aa2d0dd5966332aa2fd0334a375256f3dd0322"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2255,7 +2266,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2276,24 +2287,23 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.2"
|
||||
name = "idna_adapter"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd69211b9b519e98303c015e21a007e293db403b6c85b9b124e133d25e242cdd"
|
||||
checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2497,7 +2507,6 @@ dependencies = [
|
|||
"encoding_rs",
|
||||
"enum-map",
|
||||
"futures",
|
||||
"getrandom",
|
||||
"jsonwebtoken",
|
||||
"lemmy_db_schema",
|
||||
"lemmy_db_views",
|
||||
|
@ -2505,6 +2514,7 @@ dependencies = [
|
|||
"lemmy_db_views_moderator",
|
||||
"lemmy_utils",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"moka",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
|
@ -2539,7 +2549,6 @@ dependencies = [
|
|||
"lemmy_db_views",
|
||||
"lemmy_db_views_actor",
|
||||
"lemmy_utils",
|
||||
"moka",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
|
@ -2609,10 +2618,11 @@ dependencies = [
|
|||
"async-trait",
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
"deadpool 0.12.1",
|
||||
"deadpool",
|
||||
"derive-new",
|
||||
"diesel",
|
||||
"diesel-async",
|
||||
"diesel-bind-if-some",
|
||||
"diesel-derive-enum",
|
||||
"diesel-derive-newtype",
|
||||
"diesel_ltree",
|
||||
|
@ -2620,10 +2630,9 @@ dependencies = [
|
|||
"futures-util",
|
||||
"i-love-jesus",
|
||||
"lemmy_utils",
|
||||
"moka",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.16",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
|
@ -2634,6 +2643,7 @@ dependencies = [
|
|||
"tokio-postgres-rustls",
|
||||
"tracing",
|
||||
"ts-rs",
|
||||
"tuplex",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
@ -2775,7 +2785,7 @@ dependencies = [
|
|||
"reqwest 0.12.8",
|
||||
"reqwest-middleware",
|
||||
"reqwest-tracing",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.16",
|
||||
"serde_json",
|
||||
"serial_test",
|
||||
"tokio",
|
||||
|
@ -2807,6 +2817,7 @@ dependencies = [
|
|||
"markdown-it-ruby",
|
||||
"markdown-it-sub",
|
||||
"markdown-it-sup",
|
||||
"moka",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"reqwest-middleware",
|
||||
|
@ -2826,9 +2837,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lettre"
|
||||
version = "0.11.9"
|
||||
version = "0.11.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69f204773bab09b150320ea1c83db41dc6ee606a4bc36dc1f43005fe7b58ce06"
|
||||
checksum = "0161e452348e399deb685ba05e55ee116cae9410f4f51fe42d597361444521d9"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
|
@ -2839,12 +2850,12 @@ dependencies = [
|
|||
"futures-io",
|
||||
"futures-util",
|
||||
"httpdate",
|
||||
"idna 1.0.2",
|
||||
"idna 1.0.3",
|
||||
"mime",
|
||||
"nom",
|
||||
"percent-encoding",
|
||||
"quoted_printable",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.16",
|
||||
"rustls-pemfile 2.1.3",
|
||||
"rustls-pki-types",
|
||||
"socket2",
|
||||
|
@ -3111,9 +3122,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
|||
|
||||
[[package]]
|
||||
name = "migrations_internals"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada"
|
||||
checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"toml",
|
||||
|
@ -3121,9 +3132,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "migrations_macros"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08"
|
||||
checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd"
|
||||
dependencies = [
|
||||
"migrations_internals",
|
||||
"proc-macro2",
|
||||
|
@ -3136,6 +3147,16 @@ version = "0.3.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
|
@ -3203,7 +3224,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3515,7 +3536,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3685,7 +3706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3786,6 +3807,16 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.37.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f22f29bdff3987b4d8632ef95fd6424ec7e4e0a57e2f4fc63e489e75357f6a03"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.5"
|
||||
|
@ -3797,7 +3828,7 @@ dependencies = [
|
|||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.16",
|
||||
"socket2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
|
@ -3814,7 +3845,7 @@ dependencies = [
|
|||
"rand",
|
||||
"ring",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.16",
|
||||
"slab",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
|
@ -3896,7 +3927,7 @@ checksum = "a25d631e41bfb5fdcde1d4e2215f62f7f0afa3ff11e26563765bd6ea1d229aeb"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3910,9 +3941,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -4025,7 +4056,7 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.16",
|
||||
"rustls-pemfile 2.1.3",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
|
@ -4076,12 +4107,6 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "retain_mut"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0"
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.50"
|
||||
|
@ -4148,14 +4173,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rss"
|
||||
version = "2.0.9"
|
||||
version = "2.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27e92048f840d98c6d6dd870af9101610ea9ff413f11f1bcebf4f4c31d96d957"
|
||||
checksum = "554a62b3dd5450fcbb0435b3db809f9dd3c6e9f5726172408f7ad3b57ed59057"
|
||||
dependencies = [
|
||||
"atom_syndication",
|
||||
"derive_builder",
|
||||
"never",
|
||||
"quick-xml 0.36.1",
|
||||
"quick-xml 0.37.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4212,9 +4237,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.14"
|
||||
version = "0.23.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8"
|
||||
checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"log",
|
||||
|
@ -4247,9 +4272,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55"
|
||||
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
|
@ -4354,29 +4379,29 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
version = "1.0.215"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
version = "1.0.215"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.128"
|
||||
version = "1.0.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||
dependencies = [
|
||||
"indexmap 2.5.0",
|
||||
"itoa",
|
||||
|
@ -4433,14 +4458,14 @@ dependencies = [
|
|||
"darling 0.20.10",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test"
|
||||
version = "3.1.1"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d"
|
||||
checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"log",
|
||||
|
@ -4452,13 +4477,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serial_test_derive"
|
||||
version = "3.1.1"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67"
|
||||
checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4543,9 +4568,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
|||
|
||||
[[package]]
|
||||
name = "sitemap-rs"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88cc73a9aac975541c9054e74ceae8d8ee85edc89a322404c275c1d100fffa51"
|
||||
checksum = "3c4c6ab96128064ba085256d34e205153555b3803094d76e24d406c76f85a2c9"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"xml-builder",
|
||||
|
@ -4574,7 +4599,7 @@ checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4702,7 +4727,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4724,9 +4749,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.77"
|
||||
version = "2.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -4756,7 +4781,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4852,7 +4877,7 @@ checksum = "78ea17a2dc368aeca6f554343ced1b1e31f76d63683fa8016e5844bd7a5144a1"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4872,7 +4897,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4949,9 +4974,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.40.0"
|
||||
version = "1.41.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
||||
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
@ -4973,7 +4998,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5009,7 +5034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "04fb792ccd6bbcd4bba408eb8a292f70fc4a3589e5d793626f45190e6454b6ab"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.16",
|
||||
"tokio",
|
||||
"tokio-postgres",
|
||||
"tokio-rustls 0.26.0",
|
||||
|
@ -5032,7 +5057,7 @@ version = "0.26.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||
dependencies = [
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.16",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
|
@ -5052,9 +5077,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.7.8"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
|
@ -5073,9 +5098,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.19.15"
|
||||
version = "0.22.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||
dependencies = [
|
||||
"indexmap 2.5.0",
|
||||
"serde",
|
||||
|
@ -5160,7 +5185,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5233,7 +5258,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5269,16 +5294,28 @@ checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tuplex"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "676ac81d5454c4dcf37955d34fa8626ede3490f744b86ca14a7b90168d2a08aa"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.15"
|
||||
|
@ -5332,12 +5369,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
|||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.2"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||
checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna 0.5.0",
|
||||
"idna 1.0.3",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
@ -5380,9 +5417,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.10.0"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
||||
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"serde",
|
||||
|
@ -5459,7 +5496,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
@ -5493,7 +5530,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -5813,9 +5850,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.40"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -5924,7 +5961,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
@ -5946,7 +5983,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5966,7 +6003,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
@ -5987,7 +6024,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6009,7 +6046,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
47
Cargo.toml
47
Cargo.toml
|
@ -79,6 +79,8 @@ unused_self = "deny"
|
|||
unwrap_used = "deny"
|
||||
unimplemented = "deny"
|
||||
unused_async = "deny"
|
||||
map_err_ignore = "deny"
|
||||
expect_used = "deny"
|
||||
|
||||
[workspace.dependencies]
|
||||
lemmy_api = { version = "=0.19.6-beta.7", path = "./crates/api" }
|
||||
|
@ -92,13 +94,13 @@ lemmy_db_views = { version = "=0.19.6-beta.7", path = "./crates/db_views" }
|
|||
lemmy_db_views_actor = { version = "=0.19.6-beta.7", path = "./crates/db_views_actor" }
|
||||
lemmy_db_views_moderator = { version = "=0.19.6-beta.7", path = "./crates/db_views_moderator" }
|
||||
lemmy_federate = { version = "=0.19.6-beta.7", path = "./crates/federate" }
|
||||
activitypub_federation = { version = "0.6.0-alpha2", default-features = false, features = [
|
||||
activitypub_federation = { version = "0.6.1", default-features = false, features = [
|
||||
"actix-web",
|
||||
] }
|
||||
diesel = "2.1.6"
|
||||
diesel_migrations = "2.1.0"
|
||||
diesel-async = "0.4.1"
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
diesel = "2.2.4"
|
||||
diesel_migrations = "2.2.0"
|
||||
diesel-async = "0.5.1"
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
serde_with = "3.9.0"
|
||||
actix-web = { version = "4.9.0", default-features = false, features = [
|
||||
"macros",
|
||||
|
@ -111,7 +113,7 @@ actix-web = { version = "4.9.0", default-features = false, features = [
|
|||
tracing = "0.1.40"
|
||||
tracing-actix-web = { version = "0.7.10", default-features = false }
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
url = { version = "2.5.2", features = ["serde"] }
|
||||
url = { version = "2.5.3", features = ["serde"] }
|
||||
reqwest = { version = "0.12.7", default-features = false, features = [
|
||||
"json",
|
||||
"blocking",
|
||||
|
@ -123,24 +125,27 @@ reqwest-tracing = "0.5.3"
|
|||
clokwerk = "0.4.0"
|
||||
doku = { version = "0.21.1", features = ["url-2"] }
|
||||
bcrypt = "0.15.1"
|
||||
chrono = { version = "0.4.38", features = ["serde"], default-features = false }
|
||||
serde_json = { version = "1.0.121", features = ["preserve_order"] }
|
||||
chrono = { version = "0.4.38", features = [
|
||||
"serde",
|
||||
"now",
|
||||
], default-features = false }
|
||||
serde_json = { version = "1.0.132", features = ["preserve_order"] }
|
||||
base64 = "0.22.1"
|
||||
uuid = { version = "1.10.0", features = ["serde", "v4"] }
|
||||
async-trait = "0.1.81"
|
||||
uuid = { version = "1.11.0", features = ["serde"] }
|
||||
async-trait = "0.1.83"
|
||||
captcha = "0.0.9"
|
||||
anyhow = { version = "1.0.86", features = [
|
||||
anyhow = { version = "1.0.93", features = [
|
||||
"backtrace",
|
||||
] } # backtrace is on by default on nightly, but not stable rust
|
||||
diesel_ltree = "0.3.1"
|
||||
serial_test = "3.1.1"
|
||||
tokio = { version = "1.39.2", features = ["full"] }
|
||||
regex = "1.10.5"
|
||||
serial_test = "3.2.0"
|
||||
tokio = { version = "1.41.1", features = ["full"] }
|
||||
regex = "1.11.1"
|
||||
diesel-derive-newtype = "2.1.2"
|
||||
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
|
||||
strum = { version = "0.26.3", features = ["derive"] }
|
||||
itertools = "0.13.0"
|
||||
futures = "0.3.30"
|
||||
futures = "0.3.31"
|
||||
http = "1.1"
|
||||
rosetta-i18n = "0.1.3"
|
||||
ts-rs = { version = "10.0.0", features = [
|
||||
|
@ -149,17 +154,19 @@ ts-rs = { version = "10.0.0", features = [
|
|||
"no-serde-warnings",
|
||||
"url-impl",
|
||||
] }
|
||||
rustls = { version = "0.23.12", features = ["ring"] }
|
||||
futures-util = "0.3.30"
|
||||
tokio-postgres = "0.7.11"
|
||||
rustls = { version = "0.23.16", features = ["ring"] }
|
||||
futures-util = "0.3.31"
|
||||
tokio-postgres = "0.7.12"
|
||||
tokio-postgres-rustls = "0.12.0"
|
||||
urlencoding = "2.1.3"
|
||||
enum-map = "2.7"
|
||||
moka = { version = "0.12.8", features = ["future"] }
|
||||
i-love-jesus = { version = "0.1.0" }
|
||||
clap = { version = "4.5.13", features = ["derive", "env"] }
|
||||
pretty_assertions = "1.4.0"
|
||||
clap = { version = "4.5.21", features = ["derive", "env"] }
|
||||
pretty_assertions = "1.4.1"
|
||||
derive-new = "0.7.0"
|
||||
diesel-bind-if-some = "0.1.0"
|
||||
tuplex = "0.1.2"
|
||||
|
||||
[dependencies]
|
||||
lemmy_api = { workspace = true }
|
||||
|
|
|
@ -22,16 +22,16 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^22.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
||||
"@typescript-eslint/parser": "^8.1.0",
|
||||
"eslint": "^9.9.0",
|
||||
"@types/node": "^22.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.13.0",
|
||||
"@typescript-eslint/parser": "^8.13.0",
|
||||
"eslint": "^9.14.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"jest": "^29.5.0",
|
||||
"lemmy-js-client": "0.20.0-private-community.9",
|
||||
"lemmy-js-client": "0.20.0-instance-blocks.5",
|
||||
"prettier": "^3.2.5",
|
||||
"ts-jest": "^29.1.0",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript-eslint": "^8.1.0"
|
||||
"typescript-eslint": "^8.13.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,38 +12,38 @@ importers:
|
|||
specifier: ^29.5.12
|
||||
version: 29.5.14
|
||||
'@types/node':
|
||||
specifier: ^22.3.0
|
||||
version: 22.8.6
|
||||
specifier: ^22.9.0
|
||||
version: 22.9.0
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^8.1.0
|
||||
version: 8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
|
||||
specifier: ^8.13.0
|
||||
version: 8.13.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0)(typescript@5.6.3))(eslint@9.14.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^8.1.0
|
||||
version: 8.12.2(eslint@9.13.0)(typescript@5.6.3)
|
||||
specifier: ^8.13.0
|
||||
version: 8.13.0(eslint@9.14.0)(typescript@5.6.3)
|
||||
eslint:
|
||||
specifier: ^9.9.0
|
||||
version: 9.13.0
|
||||
specifier: ^9.14.0
|
||||
version: 9.14.0
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^5.1.3
|
||||
version: 5.2.1(eslint@9.13.0)(prettier@3.3.3)
|
||||
version: 5.2.1(eslint@9.14.0)(prettier@3.3.3)
|
||||
jest:
|
||||
specifier: ^29.5.0
|
||||
version: 29.7.0(@types/node@22.8.6)
|
||||
version: 29.7.0(@types/node@22.9.0)
|
||||
lemmy-js-client:
|
||||
specifier: 0.20.0-private-community.9
|
||||
version: 0.20.0-private-community.9
|
||||
specifier: 0.20.0-instance-blocks.5
|
||||
version: 0.20.0-instance-blocks.5
|
||||
prettier:
|
||||
specifier: ^3.2.5
|
||||
version: 3.3.3
|
||||
ts-jest:
|
||||
specifier: ^29.1.0
|
||||
version: 29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.8.6))(typescript@5.6.3)
|
||||
version: 29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.9.0))(typescript@5.6.3)
|
||||
typescript:
|
||||
specifier: ^5.5.4
|
||||
version: 5.6.3
|
||||
typescript-eslint:
|
||||
specifier: ^8.1.0
|
||||
version: 8.12.2(eslint@9.13.0)(typescript@5.6.3)
|
||||
specifier: ^8.13.0
|
||||
version: 8.13.0(eslint@9.14.0)(typescript@5.6.3)
|
||||
|
||||
packages:
|
||||
|
||||
|
@ -240,8 +240,8 @@ packages:
|
|||
resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/js@9.13.0':
|
||||
resolution: {integrity: sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==}
|
||||
'@eslint/js@9.14.0':
|
||||
resolution: {integrity: sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/object-schema@2.1.4':
|
||||
|
@ -268,6 +268,10 @@ packages:
|
|||
resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
|
||||
engines: {node: '>=18.18'}
|
||||
|
||||
'@humanwhocodes/retry@0.4.1':
|
||||
resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==}
|
||||
engines: {node: '>=18.18'}
|
||||
|
||||
'@istanbuljs/load-nyc-config@1.1.0':
|
||||
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -418,8 +422,8 @@ packages:
|
|||
'@types/json-schema@7.0.15':
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
|
||||
'@types/node@22.8.6':
|
||||
resolution: {integrity: sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==}
|
||||
'@types/node@22.9.0':
|
||||
resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==}
|
||||
|
||||
'@types/stack-utils@2.0.3':
|
||||
resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
|
||||
|
@ -430,8 +434,8 @@ packages:
|
|||
'@types/yargs@17.0.32':
|
||||
resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.12.2':
|
||||
resolution: {integrity: sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==}
|
||||
'@typescript-eslint/eslint-plugin@8.13.0':
|
||||
resolution: {integrity: sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
|
||||
|
@ -441,8 +445,8 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/parser@8.12.2':
|
||||
resolution: {integrity: sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==}
|
||||
'@typescript-eslint/parser@8.13.0':
|
||||
resolution: {integrity: sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
|
@ -451,12 +455,12 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/scope-manager@8.12.2':
|
||||
resolution: {integrity: sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==}
|
||||
'@typescript-eslint/scope-manager@8.13.0':
|
||||
resolution: {integrity: sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/type-utils@8.12.2':
|
||||
resolution: {integrity: sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==}
|
||||
'@typescript-eslint/type-utils@8.13.0':
|
||||
resolution: {integrity: sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
|
@ -464,12 +468,12 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/types@8.12.2':
|
||||
resolution: {integrity: sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==}
|
||||
'@typescript-eslint/types@8.13.0':
|
||||
resolution: {integrity: sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.12.2':
|
||||
resolution: {integrity: sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==}
|
||||
'@typescript-eslint/typescript-estree@8.13.0':
|
||||
resolution: {integrity: sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
|
@ -477,14 +481,14 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/utils@8.12.2':
|
||||
resolution: {integrity: sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==}
|
||||
'@typescript-eslint/utils@8.13.0':
|
||||
resolution: {integrity: sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.12.2':
|
||||
resolution: {integrity: sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==}
|
||||
'@typescript-eslint/visitor-keys@8.13.0':
|
||||
resolution: {integrity: sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
acorn-jsx@5.3.2:
|
||||
|
@ -649,6 +653,10 @@ packages:
|
|||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
cross-spawn@7.0.5:
|
||||
resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
debug@4.3.7:
|
||||
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
|
@ -737,8 +745,8 @@ packages:
|
|||
resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
eslint@9.13.0:
|
||||
resolution: {integrity: sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==}
|
||||
eslint@9.14.0:
|
||||
resolution: {integrity: sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
|
@ -1159,8 +1167,8 @@ packages:
|
|||
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
lemmy-js-client@0.20.0-private-community.9:
|
||||
resolution: {integrity: sha512-iuFezswCzIco5U5Q4Eo8HAWVE65pDW2zeO+fYLEyFl30SLw9a3gqJkip2vbDfVvoAjDXyUskZKddf1Nnj8mVcg==}
|
||||
lemmy-js-client@0.20.0-instance-blocks.5:
|
||||
resolution: {integrity: sha512-wDuRFzg32lbbJr4cNmd+cbzjgw+okw2/d5AujYjAm4gv0OEFfsYhP3QQ2WscwUR5HJTdzsR7IIyiBnvmaEUzUw==}
|
||||
|
||||
leven@3.1.0:
|
||||
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
|
||||
|
@ -1533,8 +1541,8 @@ packages:
|
|||
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
typescript-eslint@8.12.2:
|
||||
resolution: {integrity: sha512-UbuVUWSrHVR03q9CWx+JDHeO6B/Hr9p4U5lRH++5tq/EbFq1faYZe50ZSBePptgfIKLEti0aPQ3hFgnPVcd8ZQ==}
|
||||
typescript-eslint@8.13.0:
|
||||
resolution: {integrity: sha512-vIMpDRJrQd70au2G8w34mPps0ezFSPMEX4pXkTzUkrNbRX+36ais2ksGWN0esZL+ZMaFJEneOBHzCgSqle7DHw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
|
@ -1808,9 +1816,9 @@ snapshots:
|
|||
|
||||
'@bcoe/v8-coverage@0.2.3': {}
|
||||
|
||||
'@eslint-community/eslint-utils@4.4.1(eslint@9.13.0)':
|
||||
'@eslint-community/eslint-utils@4.4.1(eslint@9.14.0)':
|
||||
dependencies:
|
||||
eslint: 9.13.0
|
||||
eslint: 9.14.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/regexpp@4.12.1': {}
|
||||
|
@ -1839,7 +1847,7 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@eslint/js@9.13.0': {}
|
||||
'@eslint/js@9.14.0': {}
|
||||
|
||||
'@eslint/object-schema@2.1.4': {}
|
||||
|
||||
|
@ -1858,6 +1866,8 @@ snapshots:
|
|||
|
||||
'@humanwhocodes/retry@0.3.1': {}
|
||||
|
||||
'@humanwhocodes/retry@0.4.1': {}
|
||||
|
||||
'@istanbuljs/load-nyc-config@1.1.0':
|
||||
dependencies:
|
||||
camelcase: 5.3.1
|
||||
|
@ -1871,7 +1881,7 @@ snapshots:
|
|||
'@jest/console@29.7.0':
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
jest-message-util: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
|
@ -1884,14 +1894,14 @@ snapshots:
|
|||
'@jest/test-result': 29.7.0
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
exit: 0.1.2
|
||||
graceful-fs: 4.2.11
|
||||
jest-changed-files: 29.7.0
|
||||
jest-config: 29.7.0(@types/node@22.8.6)
|
||||
jest-config: 29.7.0(@types/node@22.9.0)
|
||||
jest-haste-map: 29.7.0
|
||||
jest-message-util: 29.7.0
|
||||
jest-regex-util: 29.6.3
|
||||
|
@ -1916,7 +1926,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@jest/fake-timers': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
jest-mock: 29.7.0
|
||||
|
||||
'@jest/expect-utils@29.7.0':
|
||||
|
@ -1934,7 +1944,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@sinonjs/fake-timers': 10.3.0
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
jest-message-util: 29.7.0
|
||||
jest-mock: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
|
@ -1956,7 +1966,7 @@ snapshots:
|
|||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@jridgewell/trace-mapping': 0.3.22
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
collect-v8-coverage: 1.0.2
|
||||
exit: 0.1.2
|
||||
|
@ -2026,7 +2036,7 @@ snapshots:
|
|||
'@jest/schemas': 29.6.3
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
'@types/istanbul-reports': 3.0.4
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
'@types/yargs': 17.0.32
|
||||
chalk: 4.1.2
|
||||
|
||||
|
@ -2096,7 +2106,7 @@ snapshots:
|
|||
|
||||
'@types/graceful-fs@4.1.9':
|
||||
dependencies:
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/istanbul-lib-coverage@2.0.6': {}
|
||||
|
||||
|
@ -2115,7 +2125,7 @@ snapshots:
|
|||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
||||
'@types/node@22.8.6':
|
||||
'@types/node@22.9.0':
|
||||
dependencies:
|
||||
undici-types: 6.19.8
|
||||
|
||||
|
@ -2127,15 +2137,15 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/yargs-parser': 21.0.3
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)':
|
||||
'@typescript-eslint/eslint-plugin@8.13.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0)(typescript@5.6.3))(eslint@9.14.0)(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@typescript-eslint/parser': 8.12.2(eslint@9.13.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/scope-manager': 8.12.2
|
||||
'@typescript-eslint/type-utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/visitor-keys': 8.12.2
|
||||
eslint: 9.13.0
|
||||
'@typescript-eslint/parser': 8.13.0(eslint@9.14.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/scope-manager': 8.13.0
|
||||
'@typescript-eslint/type-utils': 8.13.0(eslint@9.14.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/utils': 8.13.0(eslint@9.14.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/visitor-keys': 8.13.0
|
||||
eslint: 9.14.0
|
||||
graphemer: 1.4.0
|
||||
ignore: 5.3.2
|
||||
natural-compare: 1.4.0
|
||||
|
@ -2145,28 +2155,28 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@8.12.2(eslint@9.13.0)(typescript@5.6.3)':
|
||||
'@typescript-eslint/parser@8.13.0(eslint@9.14.0)(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 8.12.2
|
||||
'@typescript-eslint/types': 8.12.2
|
||||
'@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3)
|
||||
'@typescript-eslint/visitor-keys': 8.12.2
|
||||
'@typescript-eslint/scope-manager': 8.13.0
|
||||
'@typescript-eslint/types': 8.13.0
|
||||
'@typescript-eslint/typescript-estree': 8.13.0(typescript@5.6.3)
|
||||
'@typescript-eslint/visitor-keys': 8.13.0
|
||||
debug: 4.3.7
|
||||
eslint: 9.13.0
|
||||
eslint: 9.14.0
|
||||
optionalDependencies:
|
||||
typescript: 5.6.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/scope-manager@8.12.2':
|
||||
'@typescript-eslint/scope-manager@8.13.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.12.2
|
||||
'@typescript-eslint/visitor-keys': 8.12.2
|
||||
'@typescript-eslint/types': 8.13.0
|
||||
'@typescript-eslint/visitor-keys': 8.13.0
|
||||
|
||||
'@typescript-eslint/type-utils@8.12.2(eslint@9.13.0)(typescript@5.6.3)':
|
||||
'@typescript-eslint/type-utils@8.13.0(eslint@9.14.0)(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3)
|
||||
'@typescript-eslint/utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/typescript-estree': 8.13.0(typescript@5.6.3)
|
||||
'@typescript-eslint/utils': 8.13.0(eslint@9.14.0)(typescript@5.6.3)
|
||||
debug: 4.3.7
|
||||
ts-api-utils: 1.4.0(typescript@5.6.3)
|
||||
optionalDependencies:
|
||||
|
@ -2175,12 +2185,12 @@ snapshots:
|
|||
- eslint
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/types@8.12.2': {}
|
||||
'@typescript-eslint/types@8.13.0': {}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.12.2(typescript@5.6.3)':
|
||||
'@typescript-eslint/typescript-estree@8.13.0(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.12.2
|
||||
'@typescript-eslint/visitor-keys': 8.12.2
|
||||
'@typescript-eslint/types': 8.13.0
|
||||
'@typescript-eslint/visitor-keys': 8.13.0
|
||||
debug: 4.3.7
|
||||
fast-glob: 3.3.2
|
||||
is-glob: 4.0.3
|
||||
|
@ -2192,20 +2202,20 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.12.2(eslint@9.13.0)(typescript@5.6.3)':
|
||||
'@typescript-eslint/utils@8.13.0(eslint@9.14.0)(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.13.0)
|
||||
'@typescript-eslint/scope-manager': 8.12.2
|
||||
'@typescript-eslint/types': 8.12.2
|
||||
'@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3)
|
||||
eslint: 9.13.0
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0)
|
||||
'@typescript-eslint/scope-manager': 8.13.0
|
||||
'@typescript-eslint/types': 8.13.0
|
||||
'@typescript-eslint/typescript-estree': 8.13.0(typescript@5.6.3)
|
||||
eslint: 9.14.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.12.2':
|
||||
'@typescript-eslint/visitor-keys@8.13.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.12.2
|
||||
'@typescript-eslint/types': 8.13.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
acorn-jsx@5.3.2(acorn@8.14.0):
|
||||
|
@ -2373,13 +2383,13 @@ snapshots:
|
|||
|
||||
convert-source-map@2.0.0: {}
|
||||
|
||||
create-jest@29.7.0(@types/node@22.8.6):
|
||||
create-jest@29.7.0(@types/node@22.9.0):
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
chalk: 4.1.2
|
||||
exit: 0.1.2
|
||||
graceful-fs: 4.2.11
|
||||
jest-config: 29.7.0(@types/node@22.8.6)
|
||||
jest-config: 29.7.0(@types/node@22.9.0)
|
||||
jest-util: 29.7.0
|
||||
prompts: 2.4.2
|
||||
transitivePeerDependencies:
|
||||
|
@ -2394,6 +2404,12 @@ snapshots:
|
|||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
cross-spawn@7.0.5:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
debug@4.3.7:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
@ -2428,9 +2444,9 @@ snapshots:
|
|||
|
||||
escape-string-regexp@4.0.0: {}
|
||||
|
||||
eslint-plugin-prettier@5.2.1(eslint@9.13.0)(prettier@3.3.3):
|
||||
eslint-plugin-prettier@5.2.1(eslint@9.14.0)(prettier@3.3.3):
|
||||
dependencies:
|
||||
eslint: 9.13.0
|
||||
eslint: 9.14.0
|
||||
prettier: 3.3.3
|
||||
prettier-linter-helpers: 1.0.0
|
||||
synckit: 0.9.1
|
||||
|
@ -2444,23 +2460,23 @@ snapshots:
|
|||
|
||||
eslint-visitor-keys@4.2.0: {}
|
||||
|
||||
eslint@9.13.0:
|
||||
eslint@9.14.0:
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.13.0)
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0)
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@eslint/config-array': 0.18.0
|
||||
'@eslint/core': 0.7.0
|
||||
'@eslint/eslintrc': 3.1.0
|
||||
'@eslint/js': 9.13.0
|
||||
'@eslint/js': 9.14.0
|
||||
'@eslint/plugin-kit': 0.2.2
|
||||
'@humanfs/node': 0.16.6
|
||||
'@humanwhocodes/module-importer': 1.0.1
|
||||
'@humanwhocodes/retry': 0.3.1
|
||||
'@humanwhocodes/retry': 0.4.1
|
||||
'@types/estree': 1.0.6
|
||||
'@types/json-schema': 7.0.15
|
||||
ajv: 6.12.6
|
||||
chalk: 4.1.2
|
||||
cross-spawn: 7.0.3
|
||||
cross-spawn: 7.0.5
|
||||
debug: 4.3.7
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint-scope: 8.2.0
|
||||
|
@ -2736,7 +2752,7 @@ snapshots:
|
|||
'@jest/expect': 29.7.0
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
co: 4.6.0
|
||||
dedent: 1.5.1
|
||||
|
@ -2756,16 +2772,16 @@ snapshots:
|
|||
- babel-plugin-macros
|
||||
- supports-color
|
||||
|
||||
jest-cli@29.7.0(@types/node@22.8.6):
|
||||
jest-cli@29.7.0(@types/node@22.9.0):
|
||||
dependencies:
|
||||
'@jest/core': 29.7.0
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
chalk: 4.1.2
|
||||
create-jest: 29.7.0(@types/node@22.8.6)
|
||||
create-jest: 29.7.0(@types/node@22.9.0)
|
||||
exit: 0.1.2
|
||||
import-local: 3.1.0
|
||||
jest-config: 29.7.0(@types/node@22.8.6)
|
||||
jest-config: 29.7.0(@types/node@22.9.0)
|
||||
jest-util: 29.7.0
|
||||
jest-validate: 29.7.0
|
||||
yargs: 17.7.2
|
||||
|
@ -2775,7 +2791,7 @@ snapshots:
|
|||
- supports-color
|
||||
- ts-node
|
||||
|
||||
jest-config@29.7.0(@types/node@22.8.6):
|
||||
jest-config@29.7.0(@types/node@22.9.0):
|
||||
dependencies:
|
||||
'@babel/core': 7.23.9
|
||||
'@jest/test-sequencer': 29.7.0
|
||||
|
@ -2800,7 +2816,7 @@ snapshots:
|
|||
slash: 3.0.0
|
||||
strip-json-comments: 3.1.1
|
||||
optionalDependencies:
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
transitivePeerDependencies:
|
||||
- babel-plugin-macros
|
||||
- supports-color
|
||||
|
@ -2829,7 +2845,7 @@ snapshots:
|
|||
'@jest/environment': 29.7.0
|
||||
'@jest/fake-timers': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
jest-mock: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
|
||||
|
@ -2839,7 +2855,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/graceful-fs': 4.1.9
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
anymatch: 3.1.3
|
||||
fb-watchman: 2.0.2
|
||||
graceful-fs: 4.2.11
|
||||
|
@ -2878,7 +2894,7 @@ snapshots:
|
|||
jest-mock@29.7.0:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
jest-util: 29.7.0
|
||||
|
||||
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
|
||||
|
@ -2913,7 +2929,7 @@ snapshots:
|
|||
'@jest/test-result': 29.7.0
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
emittery: 0.13.1
|
||||
graceful-fs: 4.2.11
|
||||
|
@ -2941,7 +2957,7 @@ snapshots:
|
|||
'@jest/test-result': 29.7.0
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
cjs-module-lexer: 1.2.3
|
||||
collect-v8-coverage: 1.0.2
|
||||
|
@ -2987,7 +3003,7 @@ snapshots:
|
|||
jest-util@29.7.0:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
graceful-fs: 4.2.11
|
||||
|
@ -3006,7 +3022,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
emittery: 0.13.1
|
||||
|
@ -3015,17 +3031,17 @@ snapshots:
|
|||
|
||||
jest-worker@29.7.0:
|
||||
dependencies:
|
||||
'@types/node': 22.8.6
|
||||
'@types/node': 22.9.0
|
||||
jest-util: 29.7.0
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
|
||||
jest@29.7.0(@types/node@22.8.6):
|
||||
jest@29.7.0(@types/node@22.9.0):
|
||||
dependencies:
|
||||
'@jest/core': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
import-local: 3.1.0
|
||||
jest-cli: 29.7.0(@types/node@22.8.6)
|
||||
jest-cli: 29.7.0(@types/node@22.9.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- babel-plugin-macros
|
||||
|
@ -3061,7 +3077,7 @@ snapshots:
|
|||
|
||||
kleur@3.0.3: {}
|
||||
|
||||
lemmy-js-client@0.20.0-private-community.9: {}
|
||||
lemmy-js-client@0.20.0-instance-blocks.5: {}
|
||||
|
||||
leven@3.1.0: {}
|
||||
|
||||
|
@ -3342,12 +3358,12 @@ snapshots:
|
|||
dependencies:
|
||||
typescript: 5.6.3
|
||||
|
||||
ts-jest@29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.8.6))(typescript@5.6.3):
|
||||
ts-jest@29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.9.0))(typescript@5.6.3):
|
||||
dependencies:
|
||||
bs-logger: 0.2.6
|
||||
ejs: 3.1.10
|
||||
fast-json-stable-stringify: 2.1.0
|
||||
jest: 29.7.0(@types/node@22.8.6)
|
||||
jest: 29.7.0(@types/node@22.9.0)
|
||||
jest-util: 29.7.0
|
||||
json5: 2.2.3
|
||||
lodash.memoize: 4.1.2
|
||||
|
@ -3371,11 +3387,11 @@ snapshots:
|
|||
|
||||
type-fest@0.21.3: {}
|
||||
|
||||
typescript-eslint@8.12.2(eslint@9.13.0)(typescript@5.6.3):
|
||||
typescript-eslint@8.13.0(eslint@9.14.0)(typescript@5.6.3):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/parser': 8.12.2(eslint@9.13.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/utils': 8.12.2(eslint@9.13.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/eslint-plugin': 8.13.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0)(typescript@5.6.3))(eslint@9.14.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/parser': 8.13.0(eslint@9.14.0)(typescript@5.6.3)
|
||||
'@typescript-eslint/utils': 8.13.0(eslint@9.14.0)(typescript@5.6.3)
|
||||
optionalDependencies:
|
||||
typescript: 5.6.3
|
||||
transitivePeerDependencies:
|
||||
|
|
|
@ -25,16 +25,16 @@ import {
|
|||
getComments,
|
||||
createComment,
|
||||
getCommunityByName,
|
||||
blockInstance,
|
||||
waitUntil,
|
||||
alphaUrl,
|
||||
delta,
|
||||
betaAllowedInstances,
|
||||
searchPostLocal,
|
||||
longDelay,
|
||||
editCommunity,
|
||||
unfollows,
|
||||
userBlockInstance,
|
||||
} from "./shared";
|
||||
import { AdminAllowInstanceParams } from "lemmy-js-client/dist/types/AdminAllowInstanceParams";
|
||||
import { EditCommunity, EditSite } from "lemmy-js-client";
|
||||
|
||||
beforeAll(setupLogins);
|
||||
|
@ -363,7 +363,7 @@ test("User blocks instance, communities are hidden", async () => {
|
|||
expect(listing_ids).toContain(postRes.post_view.post.ap_id);
|
||||
|
||||
// block the beta instance
|
||||
await blockInstance(alpha, alphaPost.community.instance_id, true);
|
||||
await userBlockInstance(alpha, alphaPost.community.instance_id, true);
|
||||
|
||||
// after blocking, post should not be in listing
|
||||
let listing2 = await getPosts(alpha, "All");
|
||||
|
@ -371,7 +371,7 @@ test("User blocks instance, communities are hidden", async () => {
|
|||
expect(listing_ids2.indexOf(postRes.post_view.post.ap_id)).toBe(-1);
|
||||
|
||||
// unblock instance again
|
||||
await blockInstance(alpha, alphaPost.community.instance_id, false);
|
||||
await userBlockInstance(alpha, alphaPost.community.instance_id, false);
|
||||
|
||||
// post should be included in listing
|
||||
let listing3 = await getPosts(alpha, "All");
|
||||
|
@ -455,9 +455,12 @@ test("Dont receive community activities after unsubscribe", async () => {
|
|||
expect(communityRes1.community_view.counts.subscribers).toBe(2);
|
||||
|
||||
// temporarily block alpha, so that it doesn't know about unfollow
|
||||
let editSiteForm: EditSite = {};
|
||||
editSiteForm.allowed_instances = ["lemmy-epsilon"];
|
||||
await beta.editSite(editSiteForm);
|
||||
var allow_instance_params: AdminAllowInstanceParams = {
|
||||
instance: "lemmy-alpha",
|
||||
allow: false,
|
||||
reason: undefined,
|
||||
};
|
||||
await beta.adminAllowInstance(allow_instance_params);
|
||||
await longDelay();
|
||||
|
||||
// unfollow
|
||||
|
@ -471,8 +474,8 @@ test("Dont receive community activities after unsubscribe", async () => {
|
|||
expect(communityRes2.community_view.counts.subscribers).toBe(2);
|
||||
|
||||
// unblock alpha
|
||||
editSiteForm.allowed_instances = betaAllowedInstances;
|
||||
await beta.editSite(editSiteForm);
|
||||
allow_instance_params.allow = true;
|
||||
await beta.adminAllowInstance(allow_instance_params);
|
||||
await longDelay();
|
||||
|
||||
// create a post, it shouldnt reach beta
|
||||
|
|
|
@ -41,6 +41,9 @@ afterAll(async () => {
|
|||
});
|
||||
|
||||
test("Upload image and delete it", async () => {
|
||||
const healthz = await fetch(alphaUrl + "/pictrs/healthz");
|
||||
expect(healthz.status).toBe(200);
|
||||
|
||||
// Before running this test, you need to delete all previous images in the DB
|
||||
await deleteAllImages(alpha);
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ import {
|
|||
createCommunity,
|
||||
} from "./shared";
|
||||
import { PostView } from "lemmy-js-client/dist/types/PostView";
|
||||
import { AdminBlockInstanceParams } from "lemmy-js-client/dist/types/AdminBlockInstanceParams";
|
||||
import { EditSite, ResolveObject } from "lemmy-js-client";
|
||||
|
||||
let betaCommunity: CommunityView | undefined;
|
||||
|
@ -87,12 +88,12 @@ async function assertPostFederation(
|
|||
}
|
||||
|
||||
test("Create a post", async () => {
|
||||
// Setup some allowlists and blocklists
|
||||
const editSiteForm: EditSite = {};
|
||||
|
||||
editSiteForm.allowed_instances = [];
|
||||
editSiteForm.blocked_instances = ["lemmy-alpha"];
|
||||
await epsilon.editSite(editSiteForm);
|
||||
// Block alpha
|
||||
var block_instance_params: AdminBlockInstanceParams = {
|
||||
instance: "lemmy-alpha",
|
||||
block: true,
|
||||
};
|
||||
await epsilon.adminBlockInstance(block_instance_params);
|
||||
|
||||
if (!betaCommunity) {
|
||||
throw "Missing beta community";
|
||||
|
@ -132,11 +133,9 @@ test("Create a post", async () => {
|
|||
resolvePost(epsilon, postRes.post_view.post),
|
||||
).rejects.toStrictEqual(Error("not_found"));
|
||||
|
||||
// remove added allow/blocklists
|
||||
editSiteForm.allowed_instances = [];
|
||||
editSiteForm.blocked_instances = [];
|
||||
await delta.editSite(editSiteForm);
|
||||
await epsilon.editSite(editSiteForm);
|
||||
// remove blocked instance
|
||||
block_instance_params.block = false;
|
||||
await epsilon.adminBlockInstance(block_instance_params);
|
||||
});
|
||||
|
||||
test("Create a post in a non-existent community", async () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
jest.setTimeout(120000);
|
||||
|
||||
import { FollowCommunity } from "lemmy-js-client";
|
||||
import { FollowCommunity, LemmyHttp } from "lemmy-js-client";
|
||||
import {
|
||||
alpha,
|
||||
setupLogins,
|
||||
|
@ -21,6 +21,9 @@ import {
|
|||
resolveComment,
|
||||
likeComment,
|
||||
waitUntil,
|
||||
gamma,
|
||||
getPosts,
|
||||
getComments,
|
||||
} from "./shared";
|
||||
|
||||
beforeAll(setupLogins);
|
||||
|
@ -47,6 +50,7 @@ test("Follow a private community", async () => {
|
|||
await resolveCommunity(user, community.community_view.community.actor_id)
|
||||
).community;
|
||||
expect(betaCommunity).toBeDefined();
|
||||
expect(betaCommunity?.community.visibility).toBe("Private");
|
||||
const betaCommunityId = betaCommunity!.community.id;
|
||||
const follow_form: FollowCommunity = {
|
||||
community_id: betaCommunityId,
|
||||
|
@ -148,16 +152,7 @@ test("Only followers can view and interact with private community content", asyn
|
|||
follow: true,
|
||||
};
|
||||
await user.followCommunity(follow_form);
|
||||
const pendingFollows1 = await waitUntil(
|
||||
() => listCommunityPendingFollows(alpha),
|
||||
f => f.items.length == 1,
|
||||
);
|
||||
const approve = await approveCommunityPendingFollow(
|
||||
alpha,
|
||||
alphaCommunityId,
|
||||
pendingFollows1.items[0].person.id,
|
||||
);
|
||||
expect(approve.success).toBe(true);
|
||||
approveFollower(alpha, alphaCommunityId);
|
||||
|
||||
// now user can fetch posts and comments in community (using signed fetch), and create posts
|
||||
await waitUntil(
|
||||
|
@ -212,3 +207,151 @@ test("Reject follower", async () => {
|
|||
c => c.community_view.subscribed == "NotSubscribed",
|
||||
);
|
||||
});
|
||||
|
||||
test("Follow a private community and receive activities", async () => {
|
||||
// create private community
|
||||
const community = await createCommunity(alpha, randomString(10), "Private");
|
||||
expect(community.community_view.community.visibility).toBe("Private");
|
||||
const alphaCommunityId = community.community_view.community.id;
|
||||
|
||||
// follow with users from beta and gamma
|
||||
const betaCommunity = (
|
||||
await resolveCommunity(beta, community.community_view.community.actor_id)
|
||||
).community;
|
||||
expect(betaCommunity).toBeDefined();
|
||||
const betaCommunityId = betaCommunity!.community.id;
|
||||
const follow_form_beta: FollowCommunity = {
|
||||
community_id: betaCommunityId,
|
||||
follow: true,
|
||||
};
|
||||
await beta.followCommunity(follow_form_beta);
|
||||
await approveFollower(alpha, alphaCommunityId);
|
||||
|
||||
const gammaCommunityId = (
|
||||
await resolveCommunity(gamma, community.community_view.community.actor_id)
|
||||
).community!.community.id;
|
||||
const follow_form_gamma: FollowCommunity = {
|
||||
community_id: gammaCommunityId,
|
||||
follow: true,
|
||||
};
|
||||
await gamma.followCommunity(follow_form_gamma);
|
||||
await approveFollower(alpha, alphaCommunityId);
|
||||
|
||||
// Follow is confirmed
|
||||
await waitUntil(
|
||||
() => getCommunity(beta, betaCommunityId),
|
||||
c => c.community_view.subscribed == "Subscribed",
|
||||
);
|
||||
await waitUntil(
|
||||
() => getCommunity(gamma, gammaCommunityId),
|
||||
c => c.community_view.subscribed == "Subscribed",
|
||||
);
|
||||
|
||||
// create a post and comment from gamma
|
||||
const post = await createPost(gamma, gammaCommunityId);
|
||||
const post_id = post.post_view.post.id;
|
||||
expect(post_id).toBeDefined();
|
||||
const comment = await createComment(gamma, post_id);
|
||||
const comment_id = comment.comment_view.comment.id;
|
||||
expect(comment_id).toBeDefined();
|
||||
|
||||
// post and comment were federated to beta
|
||||
let posts = await waitUntil(
|
||||
() => getPosts(beta, "All", betaCommunityId),
|
||||
c => c.posts.length == 1,
|
||||
);
|
||||
expect(posts.posts[0].post.ap_id).toBe(post.post_view.post.ap_id);
|
||||
expect(posts.posts[0].post.name).toBe(post.post_view.post.name);
|
||||
let comments = await waitUntil(
|
||||
() => getComments(beta, posts.posts[0].post.id),
|
||||
c => c.comments.length == 1,
|
||||
);
|
||||
expect(comments.comments[0].comment.ap_id).toBe(
|
||||
comment.comment_view.comment.ap_id,
|
||||
);
|
||||
expect(comments.comments[0].comment.content).toBe(
|
||||
comment.comment_view.comment.content,
|
||||
);
|
||||
});
|
||||
|
||||
test("Fetch remote content in private community", async () => {
|
||||
// create private community
|
||||
const community = await createCommunity(alpha, randomString(10), "Private");
|
||||
expect(community.community_view.community.visibility).toBe("Private");
|
||||
const alphaCommunityId = community.community_view.community.id;
|
||||
|
||||
const betaCommunityId = (
|
||||
await resolveCommunity(beta, community.community_view.community.actor_id)
|
||||
).community!.community.id;
|
||||
const follow_form_beta: FollowCommunity = {
|
||||
community_id: betaCommunityId,
|
||||
follow: true,
|
||||
};
|
||||
await beta.followCommunity(follow_form_beta);
|
||||
await approveFollower(alpha, alphaCommunityId);
|
||||
|
||||
// Follow is confirmed
|
||||
await waitUntil(
|
||||
() => getCommunity(beta, betaCommunityId),
|
||||
c => c.community_view.subscribed == "Subscribed",
|
||||
);
|
||||
|
||||
// beta creates post and comment
|
||||
const post = await createPost(beta, betaCommunityId);
|
||||
const post_id = post.post_view.post.id;
|
||||
expect(post_id).toBeDefined();
|
||||
const comment = await createComment(beta, post_id);
|
||||
const comment_id = comment.comment_view.comment.id;
|
||||
expect(comment_id).toBeDefined();
|
||||
|
||||
// Wait for it to federate
|
||||
await waitUntil(
|
||||
() => resolveComment(alpha, comment.comment_view.comment),
|
||||
p => p?.comment?.comment.id != undefined,
|
||||
);
|
||||
|
||||
// create gamma user
|
||||
const gammaCommunityId = (
|
||||
await resolveCommunity(gamma, community.community_view.community.actor_id)
|
||||
).community!.community.id;
|
||||
const follow_form: FollowCommunity = {
|
||||
community_id: gammaCommunityId,
|
||||
follow: true,
|
||||
};
|
||||
|
||||
// cannot fetch post yet
|
||||
await expect(resolvePost(gamma, post.post_view.post)).rejects.toStrictEqual(
|
||||
Error("not_found"),
|
||||
);
|
||||
// follow community and approve
|
||||
await gamma.followCommunity(follow_form);
|
||||
await approveFollower(alpha, alphaCommunityId);
|
||||
|
||||
// now user can fetch posts and comments in community (using signed fetch), and create posts.
|
||||
// for this to work, beta checks with alpha if gamma is really an approved follower.
|
||||
let resolvedPost = await waitUntil(
|
||||
() => resolvePost(gamma, post.post_view.post),
|
||||
p => p?.post?.post.id != undefined,
|
||||
);
|
||||
expect(resolvedPost.post?.post.ap_id).toBe(post.post_view.post.ap_id);
|
||||
const resolvedComment = await waitUntil(
|
||||
() => resolveComment(gamma, comment.comment_view.comment),
|
||||
p => p?.comment?.comment.id != undefined,
|
||||
);
|
||||
expect(resolvedComment?.comment?.comment.ap_id).toBe(
|
||||
comment.comment_view.comment.ap_id,
|
||||
);
|
||||
});
|
||||
|
||||
async function approveFollower(user: LemmyHttp, community_id: number) {
|
||||
let pendingFollows1 = await waitUntil(
|
||||
() => listCommunityPendingFollows(user),
|
||||
f => f.items.length == 1,
|
||||
);
|
||||
const approve = await approveCommunityPendingFollow(
|
||||
alpha,
|
||||
community_id,
|
||||
pendingFollows1.items[0].person.id,
|
||||
);
|
||||
expect(approve.success).toBe(true);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import {
|
||||
AdminBlockInstanceParams,
|
||||
ApproveCommunityPendingFollower,
|
||||
BlockCommunity,
|
||||
BlockCommunityResponse,
|
||||
BlockInstance,
|
||||
BlockInstanceResponse,
|
||||
CommunityId,
|
||||
CommunityVisibility,
|
||||
CreatePrivateMessageReport,
|
||||
DeleteImage,
|
||||
EditCommunity,
|
||||
GetCommunityPendingFollowsCount,
|
||||
GetCommunityPendingFollowsCountResponse,
|
||||
GetReplies,
|
||||
GetRepliesResponse,
|
||||
|
@ -22,11 +20,13 @@ import {
|
|||
PostView,
|
||||
PrivateMessageReportResponse,
|
||||
SuccessResponse,
|
||||
UserBlockInstanceParams,
|
||||
} from "lemmy-js-client";
|
||||
import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
|
||||
import { DeletePost } from "lemmy-js-client/dist/types/DeletePost";
|
||||
import { EditPost } from "lemmy-js-client/dist/types/EditPost";
|
||||
import { EditSite } from "lemmy-js-client/dist/types/EditSite";
|
||||
import { AdminAllowInstanceParams } from "lemmy-js-client/dist/types/AdminAllowInstanceParams";
|
||||
import { FeaturePost } from "lemmy-js-client/dist/types/FeaturePost";
|
||||
import { GetComments } from "lemmy-js-client/dist/types/GetComments";
|
||||
import { GetCommentsResponse } from "lemmy-js-client/dist/types/GetCommentsResponse";
|
||||
|
@ -105,13 +105,6 @@ export const gamma = new LemmyHttp(gammaUrl, { fetchFunction });
|
|||
export const delta = new LemmyHttp(deltaUrl, { fetchFunction });
|
||||
export const epsilon = new LemmyHttp(epsilonUrl, { fetchFunction });
|
||||
|
||||
export const betaAllowedInstances = [
|
||||
"lemmy-alpha",
|
||||
"lemmy-gamma",
|
||||
"lemmy-delta",
|
||||
"lemmy-epsilon",
|
||||
];
|
||||
|
||||
const password = "lemmylemmy";
|
||||
|
||||
export async function setupLogins() {
|
||||
|
@ -169,30 +162,29 @@ export async function setupLogins() {
|
|||
rate_limit_comment: 999,
|
||||
rate_limit_search: 999,
|
||||
};
|
||||
|
||||
// Set the blocks and auths for each
|
||||
editSiteForm.allowed_instances = [
|
||||
"lemmy-beta",
|
||||
"lemmy-gamma",
|
||||
"lemmy-delta",
|
||||
"lemmy-epsilon",
|
||||
];
|
||||
await alpha.editSite(editSiteForm);
|
||||
|
||||
editSiteForm.allowed_instances = betaAllowedInstances;
|
||||
await beta.editSite(editSiteForm);
|
||||
|
||||
editSiteForm.allowed_instances = [
|
||||
"lemmy-alpha",
|
||||
"lemmy-beta",
|
||||
"lemmy-delta",
|
||||
"lemmy-epsilon",
|
||||
];
|
||||
await gamma.editSite(editSiteForm);
|
||||
|
||||
// Setup delta allowed instance
|
||||
editSiteForm.allowed_instances = ["lemmy-beta"];
|
||||
await delta.editSite(editSiteForm);
|
||||
await epsilon.editSite(editSiteForm);
|
||||
|
||||
// Set the blocks for each
|
||||
await allowInstance(alpha, "lemmy-beta");
|
||||
await allowInstance(alpha, "lemmy-gamma");
|
||||
await allowInstance(alpha, "lemmy-delta");
|
||||
await allowInstance(alpha, "lemmy-epsilon");
|
||||
|
||||
await allowInstance(beta, "lemmy-alpha");
|
||||
await allowInstance(beta, "lemmy-gamma");
|
||||
await allowInstance(beta, "lemmy-delta");
|
||||
await allowInstance(beta, "lemmy-epsilon");
|
||||
|
||||
await allowInstance(gamma, "lemmy-alpha");
|
||||
await allowInstance(gamma, "lemmy-beta");
|
||||
await allowInstance(gamma, "lemmy-delta");
|
||||
await allowInstance(gamma, "lemmy-epsilon");
|
||||
|
||||
await allowInstance(delta, "lemmy-beta");
|
||||
|
||||
// Create the main alpha/beta communities
|
||||
// Ignore thrown errors of duplicates
|
||||
|
@ -209,6 +201,17 @@ export async function setupLogins() {
|
|||
}
|
||||
}
|
||||
|
||||
async function allowInstance(api: LemmyHttp, instance: string) {
|
||||
const params: AdminAllowInstanceParams = {
|
||||
instance,
|
||||
allow: true,
|
||||
};
|
||||
// Ignore errors from duplicate allows (because setup gets called for each test file)
|
||||
try {
|
||||
await api.adminAllowInstance(params);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export async function createPost(
|
||||
api: LemmyHttp,
|
||||
community_id: number,
|
||||
|
@ -855,16 +858,16 @@ export function getPosts(
|
|||
return api.getPosts(form);
|
||||
}
|
||||
|
||||
export function blockInstance(
|
||||
export function userBlockInstance(
|
||||
api: LemmyHttp,
|
||||
instance_id: InstanceId,
|
||||
block: boolean,
|
||||
): Promise<BlockInstanceResponse> {
|
||||
let form: BlockInstance = {
|
||||
): Promise<SuccessResponse> {
|
||||
let form: UserBlockInstanceParams = {
|
||||
instance_id,
|
||||
block,
|
||||
};
|
||||
return api.blockInstance(form);
|
||||
return api.userBlockInstance(form);
|
||||
}
|
||||
|
||||
export function blockCommunity(
|
||||
|
@ -988,7 +991,7 @@ export function getCommentParentId(comment: Comment): number | undefined {
|
|||
if (split.length > 1) {
|
||||
return Number(split[split.length - 2]);
|
||||
} else {
|
||||
console.log(`Failed to extract comment parent id from ${comment.path}`);
|
||||
console.error(`Failed to extract comment parent id from ${comment.path}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
@ -1006,7 +1009,7 @@ export async function waitUntil<T>(
|
|||
result = await fetcher();
|
||||
if (checker(result)) return result;
|
||||
} catch (error) {
|
||||
//console.error(error);
|
||||
console.error(error);
|
||||
}
|
||||
await delay(
|
||||
delaySeconds[Math.min(retry - 1, delaySeconds.length - 1)] * 1000,
|
||||
|
|
|
@ -23,7 +23,12 @@ import {
|
|||
unfollows,
|
||||
saveUserSettingsBio,
|
||||
} from "./shared";
|
||||
import { LemmyHttp, SaveUserSettings, UploadImage } from "lemmy-js-client";
|
||||
import {
|
||||
EditSite,
|
||||
LemmyHttp,
|
||||
SaveUserSettings,
|
||||
UploadImage,
|
||||
} from "lemmy-js-client";
|
||||
import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
|
||||
|
||||
beforeAll(setupLogins);
|
||||
|
@ -149,9 +154,14 @@ test("Create user with Arabic name", async () => {
|
|||
});
|
||||
|
||||
test("Create user with accept-language", async () => {
|
||||
const edit: EditSite = {
|
||||
discussion_languages: [32],
|
||||
};
|
||||
await alpha.editSite(edit);
|
||||
|
||||
let lemmy_http = new LemmyHttp(alphaUrl, {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language#syntax
|
||||
headers: { "Accept-Language": "fr-CH, en;q=0.8, de;q=0.7, *;q=0.5" },
|
||||
headers: { "Accept-Language": "fr-CH, en;q=0.8, *;q=0.5" },
|
||||
});
|
||||
let user = await registerUser(lemmy_http, alphaUrl);
|
||||
|
||||
|
|
|
@ -73,6 +73,15 @@
|
|||
#
|
||||
# Requires pict-rs 0.5
|
||||
"ProxyAllImages"
|
||||
# Allows bypassing proxy for specific image hosts when using ProxyAllImages.
|
||||
#
|
||||
# imgur.com is bypassed by default to avoid rate limit errors. When specifying any bypass
|
||||
# in the config, this default is ignored and you need to list imgur explicitly. To proxy imgur
|
||||
# requests, specify a noop bypass list, eg `proxy_bypass_domains ["example.org"]`.
|
||||
proxy_bypass_domains: [
|
||||
"i.imgur.com"
|
||||
/* ... */
|
||||
]
|
||||
# Timeout for uploading images to pictrs (in seconds)
|
||||
upload_timeout: 30
|
||||
# Resize post thumbnails to this maximum width/height.
|
||||
|
@ -122,5 +131,5 @@
|
|||
}
|
||||
# Sets a response Access-Control-Allow-Origin CORS header
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
||||
cors_origin: "*"
|
||||
cors_origin: "lemmy.tld"
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ tracing = { workspace = true }
|
|||
chrono = { workspace = true }
|
||||
url = { workspace = true }
|
||||
hound = "3.5.1"
|
||||
sitemap-rs = "0.2.1"
|
||||
sitemap-rs = "0.2.2"
|
||||
totp-rs = { version = "5.6.0", features = ["gen_secret", "otpauth"] }
|
||||
actix-web-httpauth = "0.8.2"
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
community::{Community, CommunityModerator, CommunityModeratorForm},
|
||||
local_user::LocalUser,
|
||||
moderator::{ModAddCommunity, ModAddCommunityForm},
|
||||
mod_log::moderator::{ModAddCommunity, ModAddCommunityForm},
|
||||
},
|
||||
traits::{Crud, Joinable},
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@ use lemmy_db_schema::{
|
|||
CommunityPersonBanForm,
|
||||
},
|
||||
local_user::LocalUser,
|
||||
moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
|
||||
mod_log::moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
|
||||
},
|
||||
traits::{Bannable, Crud, Followable},
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
community::{Community, CommunityUpdateForm},
|
||||
moderator::{ModHideCommunity, ModHideCommunityForm},
|
||||
mod_log::moderator::{ModHideCommunity, ModHideCommunityForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
community::{Community, CommunityModerator, CommunityModeratorForm},
|
||||
moderator::{ModTransferCommunity, ModTransferCommunityForm},
|
||||
mod_log::moderator::{ModTransferCommunity, ModTransferCommunityForm},
|
||||
},
|
||||
traits::{Crud, Joinable},
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ use lemmy_db_schema::{
|
|||
CommunityPersonBanForm,
|
||||
},
|
||||
local_site::LocalSite,
|
||||
moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
|
||||
mod_log::moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
|
||||
person::Person,
|
||||
},
|
||||
traits::{Bannable, Crud, Followable},
|
||||
|
@ -145,7 +145,7 @@ fn build_totp_2fa(hostname: &str, username: &str, secret: &str) -> LemmyResult<T
|
|||
let sec = Secret::Raw(secret.as_bytes().to_vec());
|
||||
let sec_bytes = sec
|
||||
.to_bytes()
|
||||
.map_err(|_| LemmyErrorType::CouldntParseTotpSecret)?;
|
||||
.with_lemmy_type(LemmyErrorType::CouldntParseTotpSecret)?;
|
||||
|
||||
TOTP::new(
|
||||
totp_rs::Algorithm::SHA1,
|
||||
|
|
|
@ -7,7 +7,7 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
local_user::{LocalUser, LocalUserUpdateForm},
|
||||
moderator::{ModAdd, ModAddForm},
|
||||
mod_log::moderator::{ModAdd, ModAddForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -37,7 +37,7 @@ pub async fn add_admin(
|
|||
// Make sure that the person_id added is local
|
||||
let added_local_user = LocalUserView::read_person(&mut context.pool(), data.person_id)
|
||||
.await
|
||||
.map_err(|_| LemmyErrorType::ObjectNotLocal)?;
|
||||
.with_lemmy_type(LemmyErrorType::ObjectNotLocal)?;
|
||||
|
||||
LocalUser::update(
|
||||
&mut context.pool(),
|
||||
|
|
|
@ -11,7 +11,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
local_user::LocalUser,
|
||||
login_token::LoginToken,
|
||||
moderator::{ModBan, ModBanForm},
|
||||
mod_log::moderator::{ModBan, ModBanForm},
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
|
|
|
@ -10,7 +10,7 @@ use lemmy_db_schema::source::{
|
|||
login_token::LoginToken,
|
||||
password_reset_request::PasswordResetRequest,
|
||||
};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn change_password_after_reset(
|
||||
|
@ -32,9 +32,7 @@ pub async fn change_password_after_reset(
|
|||
|
||||
// Update the user with the new password
|
||||
let password = data.password.clone();
|
||||
LocalUser::update_password(&mut context.pool(), local_user_id, &password)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
|
||||
LocalUser::update_password(&mut context.pool(), local_user_id, &password).await?;
|
||||
|
||||
LoginToken::invalidate_all(&mut context.pool(), local_user_id).await?;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ use captcha::{gen, Difficulty};
|
|||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
person::{CaptchaResponse, GetCaptchaResponse},
|
||||
LemmyErrorType,
|
||||
};
|
||||
use lemmy_db_schema::source::{
|
||||
captcha_answer::{CaptchaAnswer, CaptchaAnswerForm},
|
||||
|
@ -37,7 +38,9 @@ pub async fn get_captcha(context: Data<LemmyContext>) -> LemmyResult<HttpRespons
|
|||
|
||||
let answer = captcha.chars_as_string();
|
||||
|
||||
let png = captcha.as_base64().expect("failed to generate captcha");
|
||||
let png = captcha
|
||||
.as_base64()
|
||||
.ok_or(LemmyErrorType::CouldntCreateImageCaptcha)?;
|
||||
|
||||
let wav = captcha_as_wav_base64(&captcha)?;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use lemmy_api_common::{
|
|||
SuccessResponse,
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn reset_password(
|
||||
|
@ -17,7 +17,7 @@ pub async fn reset_password(
|
|||
let email = data.email.to_lowercase();
|
||||
let local_user_view = LocalUserView::find_by_email(&mut context.pool(), &email)
|
||||
.await
|
||||
.map_err(|_| LemmyErrorType::IncorrectLogin)?;
|
||||
.with_lemmy_type(LemmyErrorType::IncorrectLogin)?;
|
||||
|
||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
check_email_verified(&local_user_view, &site_view)?;
|
||||
|
|
|
@ -143,6 +143,7 @@ pub async fn save_user_settings(
|
|||
enable_animated_images: data.enable_animated_images,
|
||||
enable_private_messages: data.enable_private_messages,
|
||||
collapse_bot_comments: data.collapse_bot_comments,
|
||||
auto_mark_fetched_posts_as_read: data.auto_mark_fetched_posts_as_read,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
community::Community,
|
||||
moderator::{ModFeaturePost, ModFeaturePostForm},
|
||||
mod_log::moderator::{ModFeaturePost, ModFeaturePostForm},
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
|
|
|
@ -1,34 +1,39 @@
|
|||
use actix_web::web::{Data, Json};
|
||||
use lemmy_api_common::{context::LemmyContext, post::HidePost, SuccessResponse};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
post::{HidePost, PostResponse},
|
||||
};
|
||||
use lemmy_db_schema::source::post::PostHide;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS};
|
||||
use std::collections::HashSet;
|
||||
use lemmy_db_views::structs::{LocalUserView, PostView};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn hide_post(
|
||||
data: Json<HidePost>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let post_ids = HashSet::from_iter(data.post_ids.clone());
|
||||
|
||||
if post_ids.len() > MAX_API_PARAM_ELEMENTS {
|
||||
Err(LemmyErrorType::TooManyItems)?;
|
||||
}
|
||||
|
||||
) -> LemmyResult<Json<PostResponse>> {
|
||||
let person_id = local_user_view.person.id;
|
||||
let post_id = data.post_id;
|
||||
|
||||
// Mark the post as hidden / unhidden
|
||||
if data.hide {
|
||||
PostHide::hide(&mut context.pool(), post_ids, person_id)
|
||||
PostHide::hide(&mut context.pool(), post_id, person_id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntHidePost)?;
|
||||
} else {
|
||||
PostHide::unhide(&mut context.pool(), post_ids, person_id)
|
||||
PostHide::unhide(&mut context.pool(), post_id, person_id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntHidePost)?;
|
||||
}
|
||||
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
let post_view = PostView::read(
|
||||
&mut context.pool(),
|
||||
post_id,
|
||||
Some(&local_user_view.local_user),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Json(PostResponse { post_view }))
|
||||
}
|
||||
|
|
|
@ -5,18 +5,12 @@ use lemmy_api_common::{
|
|||
context::LemmyContext,
|
||||
post::{CreatePostLike, PostResponse},
|
||||
send_activity::{ActivityChannel, SendActivityData},
|
||||
utils::{
|
||||
check_bot_account,
|
||||
check_community_user_action,
|
||||
check_local_vote_mode,
|
||||
mark_post_as_read,
|
||||
VoteItem,
|
||||
},
|
||||
utils::{check_bot_account, check_community_user_action, check_local_vote_mode, VoteItem},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
local_site::LocalSite,
|
||||
post::{PostLike, PostLikeForm},
|
||||
post::{PostLike, PostLikeForm, PostRead, PostReadForm},
|
||||
},
|
||||
traits::Likeable,
|
||||
};
|
||||
|
@ -53,11 +47,7 @@ pub async fn like_post(
|
|||
)
|
||||
.await?;
|
||||
|
||||
let like_form = PostLikeForm {
|
||||
post_id: data.post_id,
|
||||
person_id: local_user_view.person.id,
|
||||
score: data.score,
|
||||
};
|
||||
let like_form = PostLikeForm::new(data.post_id, local_user_view.person.id, data.score);
|
||||
|
||||
// Remove any likes first
|
||||
let person_id = local_user_view.person.id;
|
||||
|
@ -72,7 +62,9 @@ pub async fn like_post(
|
|||
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
|
||||
}
|
||||
|
||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
||||
// Mark Post Read
|
||||
let read_form = PostReadForm::new(post_id, person_id);
|
||||
PostRead::mark_as_read(&mut context.pool(), &read_form).await?;
|
||||
|
||||
ActivityChannel::submit_activity(
|
||||
SendActivityData::LikePostOrComment {
|
||||
|
|
|
@ -9,7 +9,7 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
moderator::{ModLockPost, ModLockPostForm},
|
||||
mod_log::moderator::{ModLockPost, ModLockPostForm},
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
|
|
24
crates/api/src/post/mark_many_read.rs
Normal file
24
crates/api/src/post/mark_many_read.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use actix_web::web::{Data, Json};
|
||||
use lemmy_api_common::{context::LemmyContext, post::MarkManyPostsAsRead, SuccessResponse};
|
||||
use lemmy_db_schema::source::post::PostRead;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn mark_posts_as_read(
|
||||
data: Json<MarkManyPostsAsRead>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let post_ids = &data.post_ids;
|
||||
if post_ids.len() > MAX_API_PARAM_ELEMENTS {
|
||||
Err(LemmyErrorType::TooManyItems)?;
|
||||
}
|
||||
|
||||
let person_id = local_user_view.person.id;
|
||||
|
||||
// Mark the posts as read
|
||||
PostRead::mark_many_as_read(&mut context.pool(), post_ids, person_id).await?;
|
||||
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
}
|
|
@ -1,34 +1,35 @@
|
|||
use actix_web::web::{Data, Json};
|
||||
use lemmy_api_common::{context::LemmyContext, post::MarkPostAsRead, SuccessResponse};
|
||||
use lemmy_db_schema::source::post::PostRead;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS};
|
||||
use std::collections::HashSet;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
post::{MarkPostAsRead, PostResponse},
|
||||
};
|
||||
use lemmy_db_schema::source::post::{PostRead, PostReadForm};
|
||||
use lemmy_db_views::structs::{LocalUserView, PostView};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn mark_post_as_read(
|
||||
data: Json<MarkPostAsRead>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let post_ids = HashSet::from_iter(data.post_ids.clone());
|
||||
|
||||
if post_ids.len() > MAX_API_PARAM_ELEMENTS {
|
||||
Err(LemmyErrorType::TooManyItems)?;
|
||||
}
|
||||
|
||||
) -> LemmyResult<Json<PostResponse>> {
|
||||
let person_id = local_user_view.person.id;
|
||||
let post_id = data.post_id;
|
||||
|
||||
// Mark the post as read / unread
|
||||
let form = PostReadForm::new(post_id, person_id);
|
||||
if data.read {
|
||||
PostRead::mark_as_read(&mut context.pool(), post_ids, person_id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)?;
|
||||
PostRead::mark_as_read(&mut context.pool(), &form).await?;
|
||||
} else {
|
||||
PostRead::mark_as_unread(&mut context.pool(), post_ids, person_id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)?;
|
||||
PostRead::mark_as_unread(&mut context.pool(), &form).await?;
|
||||
}
|
||||
let post_view = PostView::read(
|
||||
&mut context.pool(),
|
||||
post_id,
|
||||
Some(&local_user_view.local_user),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
Ok(Json(PostResponse { post_view }))
|
||||
}
|
||||
|
|
|
@ -4,5 +4,6 @@ pub mod hide;
|
|||
pub mod like;
|
||||
pub mod list_post_likes;
|
||||
pub mod lock;
|
||||
pub mod mark_many_read;
|
||||
pub mod mark_read;
|
||||
pub mod save;
|
||||
|
|
|
@ -2,10 +2,9 @@ use actix_web::web::{Data, Json};
|
|||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
post::{PostResponse, SavePost},
|
||||
utils::mark_post_as_read,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::post::{PostSaved, PostSavedForm},
|
||||
source::post::{PostRead, PostReadForm, PostSaved, PostSavedForm},
|
||||
traits::Saveable,
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, PostView};
|
||||
|
@ -17,10 +16,7 @@ pub async fn save_post(
|
|||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<PostResponse>> {
|
||||
let post_saved_form = PostSavedForm {
|
||||
post_id: data.post_id,
|
||||
person_id: local_user_view.person.id,
|
||||
};
|
||||
let post_saved_form = PostSavedForm::new(data.post_id, local_user_view.person.id);
|
||||
|
||||
if data.save {
|
||||
PostSaved::save(&mut context.pool(), &post_saved_form)
|
||||
|
@ -42,7 +38,8 @@ pub async fn save_post(
|
|||
)
|
||||
.await?;
|
||||
|
||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
||||
let read_form = PostReadForm::new(post_id, person_id);
|
||||
PostRead::mark_as_read(&mut context.pool(), &read_form).await?;
|
||||
|
||||
Ok(Json(PostResponse { post_view }))
|
||||
}
|
||||
|
|
53
crates/api/src/site/admin_allow_instance.rs
Normal file
53
crates/api/src/site/admin_allow_instance.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
site::AdminAllowInstanceParams,
|
||||
utils::is_admin,
|
||||
LemmyErrorType,
|
||||
SuccessResponse,
|
||||
};
|
||||
use lemmy_db_schema::source::{
|
||||
federation_allowlist::{FederationAllowList, FederationAllowListForm},
|
||||
instance::Instance,
|
||||
mod_log::admin::{AdminAllowInstance, AdminAllowInstanceForm},
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn admin_allow_instance(
|
||||
data: Json<AdminAllowInstanceParams>,
|
||||
local_user_view: LocalUserView,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
let blocklist = Instance::blocklist(&mut context.pool()).await?;
|
||||
if !blocklist.is_empty() {
|
||||
Err(LemmyErrorType::CannotCombineFederationBlocklistAndAllowlist)?;
|
||||
}
|
||||
|
||||
let instance_id = Instance::read_or_create(&mut context.pool(), data.instance.clone())
|
||||
.await?
|
||||
.id;
|
||||
let form = FederationAllowListForm {
|
||||
instance_id,
|
||||
updated: None,
|
||||
};
|
||||
if data.allow {
|
||||
FederationAllowList::allow(&mut context.pool(), &form).await?;
|
||||
} else {
|
||||
FederationAllowList::unallow(&mut context.pool(), instance_id).await?;
|
||||
}
|
||||
|
||||
let mod_log_form = AdminAllowInstanceForm {
|
||||
instance_id,
|
||||
admin_person_id: local_user_view.person.id,
|
||||
reason: data.reason.clone(),
|
||||
allowed: data.allow,
|
||||
};
|
||||
AdminAllowInstance::insert(&mut context.pool(), &mod_log_form).await?;
|
||||
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
}
|
56
crates/api/src/site/admin_block_instance.rs
Normal file
56
crates/api/src/site/admin_block_instance.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
site::AdminBlockInstanceParams,
|
||||
utils::is_admin,
|
||||
LemmyErrorType,
|
||||
SuccessResponse,
|
||||
};
|
||||
use lemmy_db_schema::source::{
|
||||
federation_blocklist::{FederationBlockList, FederationBlockListForm},
|
||||
instance::Instance,
|
||||
mod_log::admin::{AdminBlockInstance, AdminBlockInstanceForm},
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn admin_block_instance(
|
||||
data: Json<AdminBlockInstanceParams>,
|
||||
local_user_view: LocalUserView,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
let allowlist = Instance::allowlist(&mut context.pool()).await?;
|
||||
if !allowlist.is_empty() {
|
||||
Err(LemmyErrorType::CannotCombineFederationBlocklistAndAllowlist)?;
|
||||
}
|
||||
|
||||
let instance_id = Instance::read_or_create(&mut context.pool(), data.instance.clone())
|
||||
.await?
|
||||
.id;
|
||||
let form = FederationBlockListForm {
|
||||
instance_id,
|
||||
expires: data.expires,
|
||||
updated: None,
|
||||
};
|
||||
|
||||
if data.block {
|
||||
FederationBlockList::block(&mut context.pool(), &form).await?;
|
||||
} else {
|
||||
FederationBlockList::unblock(&mut context.pool(), instance_id).await?;
|
||||
}
|
||||
|
||||
let mod_log_form = AdminBlockInstanceForm {
|
||||
instance_id,
|
||||
admin_person_id: local_user_view.person.id,
|
||||
blocked: data.block,
|
||||
reason: data.reason.clone(),
|
||||
when_: data.expires,
|
||||
};
|
||||
AdminBlockInstance::insert(&mut context.pool(), &mod_log_form).await?;
|
||||
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
}
|
|
@ -6,7 +6,7 @@ use lemmy_db_schema::{
|
|||
language::Language,
|
||||
local_site_url_blocklist::LocalSiteUrlBlocklist,
|
||||
local_user::{LocalUser, LocalUserUpdateForm},
|
||||
moderator::{ModAdd, ModAddForm},
|
||||
mod_log::moderator::{ModAdd, ModAddForm},
|
||||
oauth_provider::OAuthProvider,
|
||||
tagline::Tagline,
|
||||
},
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
pub mod block;
|
||||
pub mod admin_allow_instance;
|
||||
pub mod admin_block_instance;
|
||||
pub mod federated_instances;
|
||||
pub mod leave_admin;
|
||||
pub mod list_all_media;
|
||||
pub mod mod_log;
|
||||
pub mod purge;
|
||||
pub mod registration_applications;
|
||||
pub mod user_block_instance;
|
||||
|
|
|
@ -7,6 +7,8 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{source::local_site::LocalSite, ModlogActionType};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_moderator::structs::{
|
||||
AdminAllowInstanceView,
|
||||
AdminBlockInstanceView,
|
||||
AdminPurgeCommentView,
|
||||
AdminPurgeCommunityView,
|
||||
AdminPurgePersonView,
|
||||
|
@ -121,6 +123,8 @@ pub async fn get_mod_log(
|
|||
admin_purged_communities,
|
||||
admin_purged_posts,
|
||||
admin_purged_comments,
|
||||
admin_block_instance,
|
||||
admin_allow_instance,
|
||||
) = if data.community_id.is_none() {
|
||||
(
|
||||
match type_ {
|
||||
|
@ -161,6 +165,18 @@ pub async fn get_mod_log(
|
|||
}
|
||||
_ => Default::default(),
|
||||
},
|
||||
match type_ {
|
||||
All | AdminBlockInstance if other_person_id.is_none() => {
|
||||
AdminBlockInstanceView::list(&mut context.pool(), params).await?
|
||||
}
|
||||
_ => Default::default(),
|
||||
},
|
||||
match type_ {
|
||||
All | AdminAllowInstance if other_person_id.is_none() => {
|
||||
AdminAllowInstanceView::list(&mut context.pool(), params).await?
|
||||
}
|
||||
_ => Default::default(),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Default::default()
|
||||
|
@ -183,5 +199,7 @@ pub async fn get_mod_log(
|
|||
admin_purged_posts,
|
||||
admin_purged_comments,
|
||||
hidden_communities,
|
||||
admin_block_instance,
|
||||
admin_allow_instance,
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
comment::Comment,
|
||||
local_user::LocalUser,
|
||||
moderator::{AdminPurgeComment, AdminPurgeCommentForm},
|
||||
mod_log::admin::{AdminPurgeComment, AdminPurgeCommentForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
community::Community,
|
||||
local_user::LocalUser,
|
||||
moderator::{AdminPurgeCommunity, AdminPurgeCommunityForm},
|
||||
mod_log::admin::{AdminPurgeCommunity, AdminPurgeCommunityForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
local_user::LocalUser,
|
||||
moderator::{AdminPurgePerson, AdminPurgePersonForm},
|
||||
mod_log::admin::{AdminPurgePerson, AdminPurgePersonForm},
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
|
|
|
@ -11,7 +11,7 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
local_user::LocalUser,
|
||||
moderator::{AdminPurgePost, AdminPurgePostForm},
|
||||
mod_log::admin::{AdminPurgePost, AdminPurgePostForm},
|
||||
post::Post,
|
||||
},
|
||||
traits::Crud,
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
site::{BlockInstance, BlockInstanceResponse},
|
||||
};
|
||||
use lemmy_api_common::{context::LemmyContext, site::UserBlockInstanceParams, SuccessResponse};
|
||||
use lemmy_db_schema::{
|
||||
source::instance_block::{InstanceBlock, InstanceBlockForm},
|
||||
traits::Blockable,
|
||||
|
@ -12,11 +9,11 @@ use lemmy_db_views::structs::LocalUserView;
|
|||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn block_instance(
|
||||
data: Json<BlockInstance>,
|
||||
pub async fn user_block_instance(
|
||||
data: Json<UserBlockInstanceParams>,
|
||||
local_user_view: LocalUserView,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<BlockInstanceResponse>> {
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let instance_id = data.instance_id;
|
||||
let person_id = local_user_view.person.id;
|
||||
if local_user_view.person.instance_id == instance_id {
|
||||
|
@ -38,7 +35,5 @@ pub async fn block_instance(
|
|||
.with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)?;
|
||||
}
|
||||
|
||||
Ok(Json(BlockInstanceResponse {
|
||||
blocked: data.block,
|
||||
}))
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
}
|
|
@ -36,6 +36,7 @@ full = [
|
|||
"futures",
|
||||
"jsonwebtoken",
|
||||
"mime",
|
||||
"moka",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
|
@ -58,22 +59,18 @@ uuid = { workspace = true, optional = true }
|
|||
tokio = { workspace = true, optional = true }
|
||||
reqwest = { workspace = true, optional = true }
|
||||
ts-rs = { workspace = true, optional = true }
|
||||
moka.workspace = true
|
||||
moka = { workspace = true, optional = true }
|
||||
anyhow.workspace = true
|
||||
actix-web = { workspace = true, optional = true }
|
||||
enum-map = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
mime = { version = "0.3.17", optional = true }
|
||||
mime_guess = "2.0.5"
|
||||
webpage = { version = "2.0", default-features = false, features = [
|
||||
"serde",
|
||||
], optional = true }
|
||||
encoding_rs = { version = "0.8.34", optional = true }
|
||||
encoding_rs = { version = "0.8.35", optional = true }
|
||||
jsonwebtoken = { version = "9.3.0", optional = true }
|
||||
# necessary for wasmt compilation
|
||||
getrandom = { version = "0.2.15", features = ["js"] }
|
||||
|
||||
[package.metadata.cargo-shear]
|
||||
ignored = ["getrandom"]
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = { workspace = true }
|
||||
|
|
|
@ -17,8 +17,10 @@ use lemmy_db_schema::{
|
|||
actor_language::CommunityLanguage,
|
||||
comment::Comment,
|
||||
comment_reply::{CommentReply, CommentReplyInsertForm},
|
||||
community::Community,
|
||||
person::Person,
|
||||
person_mention::{PersonMention, PersonMentionInsertForm},
|
||||
post::Post,
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -101,17 +103,28 @@ pub async fn send_local_notifs(
|
|||
let mut recipient_ids = Vec::new();
|
||||
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
||||
|
||||
// let person = my_local_user.person;
|
||||
// Read the comment view to get extra info
|
||||
let comment_view = CommentView::read(
|
||||
&mut context.pool(),
|
||||
comment_id,
|
||||
local_user_view.map(|view| &view.local_user),
|
||||
)
|
||||
.await?;
|
||||
let comment = comment_view.comment;
|
||||
let post = comment_view.post;
|
||||
let community = comment_view.community;
|
||||
// When called from api code, we have local user view and can read with CommentView
|
||||
// to reduce db queries. But when receiving a federated comment the user view is None,
|
||||
// which means that comments inside private communities cant be read. As a workaround
|
||||
// we need to read the items manually to bypass this check.
|
||||
let (comment, post, community) = if let Some(local_user_view) = local_user_view {
|
||||
let comment_view = CommentView::read(
|
||||
&mut context.pool(),
|
||||
comment_id,
|
||||
Some(&local_user_view.local_user),
|
||||
)
|
||||
.await?;
|
||||
(
|
||||
comment_view.comment,
|
||||
comment_view.post,
|
||||
comment_view.community,
|
||||
)
|
||||
} else {
|
||||
let comment = Comment::read(&mut context.pool(), comment_id).await?;
|
||||
let post = Post::read(&mut context.pool(), comment.post_id).await?;
|
||||
let community = Community::read(&mut context.pool(), post.community_id).await?;
|
||||
(comment, post, community)
|
||||
};
|
||||
|
||||
// Send the local mentions
|
||||
for mention in mentions
|
||||
|
|
|
@ -55,6 +55,7 @@ impl LemmyContext {
|
|||
/// Initialize a context for use in tests which blocks federation network calls.
|
||||
///
|
||||
/// Do not use this in production code.
|
||||
#[allow(clippy::expect_used)]
|
||||
pub async fn init_test_federation_config() -> FederationConfig<LemmyContext> {
|
||||
// call this to run migrations
|
||||
let pool = build_db_pool_for_tests();
|
||||
|
|
|
@ -178,6 +178,9 @@ pub struct SaveUserSettings {
|
|||
pub show_downvotes: Option<bool>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub show_upvote_percentage: Option<bool>,
|
||||
/// Whether to automatically mark fetched posts as read.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub auto_mark_fetched_posts_as_read: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -109,6 +109,9 @@ pub struct GetPosts {
|
|||
/// If true, then show the nsfw posts (even if your user setting is to hide them)
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub show_nsfw: Option<bool>,
|
||||
/// Whether to automatically mark fetched posts as read.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub mark_as_read: Option<bool>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
/// If true, then only show posts with no comments
|
||||
pub no_comments_only: Option<bool>,
|
||||
|
@ -195,17 +198,26 @@ pub struct RemovePost {
|
|||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Mark a post as read.
|
||||
pub struct MarkPostAsRead {
|
||||
pub post_ids: Vec<PostId>,
|
||||
pub post_id: PostId,
|
||||
pub read: bool,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Mark several posts as read.
|
||||
pub struct MarkManyPostsAsRead {
|
||||
pub post_ids: Vec<PostId>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Hide a post from list views
|
||||
pub struct HidePost {
|
||||
pub post_ids: Vec<PostId>,
|
||||
pub post_id: PostId,
|
||||
pub hide: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -18,12 +18,11 @@ use lemmy_db_schema::{
|
|||
},
|
||||
};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyError, LemmyErrorType, LemmyResult},
|
||||
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
settings::structs::{PictrsImageMode, Settings},
|
||||
REQWEST_TIMEOUT,
|
||||
VERSION,
|
||||
};
|
||||
use mime::Mime;
|
||||
use reqwest::{
|
||||
header::{CONTENT_TYPE, RANGE},
|
||||
Client,
|
||||
|
@ -61,13 +60,23 @@ pub async fn fetch_link_metadata(url: &Url, context: &LemmyContext) -> LemmyResu
|
|||
// server may ignore this and still respond with the full response
|
||||
.header(RANGE, format!("bytes=0-{}", bytes_to_fetch - 1)) /* -1 because inclusive */
|
||||
.send()
|
||||
.await?;
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
|
||||
let content_type: Option<Mime> = response
|
||||
.headers()
|
||||
.get(CONTENT_TYPE)
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.and_then(|h| h.parse().ok());
|
||||
// In some cases servers send a wrong mime type for images, which prevents thumbnail
|
||||
// generation. To avoid this we also try to guess the mime type from file extension.
|
||||
let content_type = mime_guess::from_path(url.path())
|
||||
.first()
|
||||
// If you can guess that its an image type, then return that first.
|
||||
.filter(|guess| guess.type_() == mime::IMAGE)
|
||||
// Otherwise, get the content type from the headers
|
||||
.or(
|
||||
response
|
||||
.headers()
|
||||
.get(CONTENT_TYPE)
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.and_then(|h| h.parse().ok()),
|
||||
);
|
||||
|
||||
let opengraph_data = {
|
||||
// if the content type is not text/html, we don't need to parse it
|
||||
|
@ -308,7 +317,8 @@ pub async fn purge_image_from_pictrs(image_url: &Url, context: &LemmyContext) ->
|
|||
.timeout(REQWEST_TIMEOUT)
|
||||
.header("x-api-token", pictrs_api_key)
|
||||
.send()
|
||||
.await?;
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
|
||||
let response: PictrsPurgeResponse = response.json().await.map_err(LemmyError::from)?;
|
||||
|
||||
|
@ -333,8 +343,8 @@ pub async fn delete_image_from_pictrs(
|
|||
.delete(&url)
|
||||
.timeout(REQWEST_TIMEOUT)
|
||||
.send()
|
||||
.await
|
||||
.map_err(LemmyError::from)?;
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -366,6 +376,7 @@ async fn generate_pictrs_thumbnail(image_url: &Url, context: &LemmyContext) -> L
|
|||
.timeout(REQWEST_TIMEOUT)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.json::<PictrsResponse>()
|
||||
.await?;
|
||||
|
||||
|
@ -406,16 +417,14 @@ pub async fn fetch_pictrs_proxied_image_details(
|
|||
// Pictrs needs you to fetch the proxied image before you can fetch the details
|
||||
let proxy_url = format!("{pictrs_url}image/original?proxy={encoded_image_url}");
|
||||
|
||||
let res = context
|
||||
context
|
||||
.client()
|
||||
.get(&proxy_url)
|
||||
.timeout(REQWEST_TIMEOUT)
|
||||
.send()
|
||||
.await?
|
||||
.status();
|
||||
if !res.is_success() {
|
||||
Err(LemmyErrorType::NotAnImageType)?
|
||||
}
|
||||
.error_for_status()
|
||||
.with_lemmy_type(LemmyErrorType::NotAnImageType)?;
|
||||
|
||||
let details_url = format!("{pictrs_url}image/details/original?proxy={encoded_image_url}");
|
||||
|
||||
|
@ -425,6 +434,7 @@ pub async fn fetch_pictrs_proxied_image_details(
|
|||
.timeout(REQWEST_TIMEOUT)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.json()
|
||||
.await?;
|
||||
|
||||
|
@ -521,7 +531,7 @@ mod tests {
|
|||
|
||||
// root relative url
|
||||
let html_bytes = b"<!DOCTYPE html><html><head><meta property='og:image' content='/image.jpg'></head><body></body></html>";
|
||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||
let metadata = extract_opengraph_data(html_bytes, &url)?;
|
||||
assert_eq!(
|
||||
metadata.image,
|
||||
Some(Url::parse("https://example.com/image.jpg")?.into())
|
||||
|
@ -529,7 +539,7 @@ mod tests {
|
|||
|
||||
// base relative url
|
||||
let html_bytes = b"<!DOCTYPE html><html><head><meta property='og:image' content='image.jpg'></head><body></body></html>";
|
||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||
let metadata = extract_opengraph_data(html_bytes, &url)?;
|
||||
assert_eq!(
|
||||
metadata.image,
|
||||
Some(Url::parse("https://example.com/one/image.jpg")?.into())
|
||||
|
@ -537,7 +547,7 @@ mod tests {
|
|||
|
||||
// absolute url
|
||||
let html_bytes = b"<!DOCTYPE html><html><head><meta property='og:image' content='https://cdn.host.com/image.jpg'></head><body></body></html>";
|
||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||
let metadata = extract_opengraph_data(html_bytes, &url)?;
|
||||
assert_eq!(
|
||||
metadata.image,
|
||||
Some(Url::parse("https://cdn.host.com/image.jpg")?.into())
|
||||
|
@ -545,7 +555,7 @@ mod tests {
|
|||
|
||||
// protocol relative url
|
||||
let html_bytes = b"<!DOCTYPE html><html><head><meta property='og:image' content='//example.com/image.jpg'></head><body></body></html>";
|
||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||
let metadata = extract_opengraph_data(html_bytes, &url)?;
|
||||
assert_eq!(
|
||||
metadata.image,
|
||||
Some(Url::parse("https://example.com/image.jpg")?.into())
|
||||
|
|
|
@ -43,6 +43,8 @@ use lemmy_db_views_actor::structs::{
|
|||
PersonView,
|
||||
};
|
||||
use lemmy_db_views_moderator::structs::{
|
||||
AdminAllowInstanceView,
|
||||
AdminBlockInstanceView,
|
||||
AdminPurgeCommentView,
|
||||
AdminPurgeCommunityView,
|
||||
AdminPurgePersonView,
|
||||
|
@ -183,6 +185,8 @@ pub struct GetModlogResponse {
|
|||
pub admin_purged_posts: Vec<AdminPurgePostView>,
|
||||
pub admin_purged_comments: Vec<AdminPurgeCommentView>,
|
||||
pub hidden_communities: Vec<ModHideCommunityView>,
|
||||
pub admin_block_instance: Vec<AdminBlockInstanceView>,
|
||||
pub admin_allow_instance: Vec<AdminAllowInstanceView>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
|
@ -265,10 +269,6 @@ pub struct CreateSite {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub captcha_difficulty: Option<String>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub allowed_instances: Option<Vec<String>>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub blocked_instances: Option<Vec<String>>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub registration_mode: Option<RegistrationMode>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub oauth_registration: Option<bool>,
|
||||
|
@ -394,12 +394,6 @@ pub struct EditSite {
|
|||
/// The captcha difficulty. Can be easy, medium, or hard
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub captcha_difficulty: Option<String>,
|
||||
/// A list of allowed instances. If none are set, federation is open.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub allowed_instances: Option<Vec<String>>,
|
||||
/// A list of blocked instances.
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub blocked_instances: Option<Vec<String>>,
|
||||
/// A list of blocked URLs
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub blocked_urls: Option<Vec<String>>,
|
||||
|
@ -514,6 +508,7 @@ pub struct ReadableFederationState {
|
|||
next_retry: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
impl From<FederationQueueState> for ReadableFederationState {
|
||||
fn from(internal_state: FederationQueueState) -> Self {
|
||||
ReadableFederationState {
|
||||
|
@ -647,15 +642,29 @@ pub struct GetUnreadRegistrationApplicationCountResponse {
|
|||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Block an instance as user
|
||||
pub struct BlockInstance {
|
||||
pub struct UserBlockInstanceParams {
|
||||
pub instance_id: InstanceId,
|
||||
pub block: bool,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct BlockInstanceResponse {
|
||||
pub blocked: bool,
|
||||
pub struct AdminBlockInstanceParams {
|
||||
pub instance: String,
|
||||
pub block: bool,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub reason: Option<String>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub expires: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct AdminAllowInstanceParams {
|
||||
pub instance: String,
|
||||
pub allow: bool,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub reason: Option<String>,
|
||||
}
|
||||
|
|
|
@ -23,12 +23,17 @@ use lemmy_db_schema::{
|
|||
local_site::LocalSite,
|
||||
local_site_rate_limit::LocalSiteRateLimit,
|
||||
local_site_url_blocklist::LocalSiteUrlBlocklist,
|
||||
moderator::{ModRemoveComment, ModRemoveCommentForm, ModRemovePost, ModRemovePostForm},
|
||||
mod_log::moderator::{
|
||||
ModRemoveComment,
|
||||
ModRemoveCommentForm,
|
||||
ModRemovePost,
|
||||
ModRemovePostForm,
|
||||
},
|
||||
oauth_account::OAuthAccount,
|
||||
password_reset_request::PasswordResetRequest,
|
||||
person::{Person, PersonUpdateForm},
|
||||
person_block::PersonBlock,
|
||||
post::{Post, PostLike, PostRead},
|
||||
post::{Post, PostLike},
|
||||
registration_application::RegistrationApplication,
|
||||
site::Site,
|
||||
},
|
||||
|
@ -60,12 +65,13 @@ use lemmy_utils::{
|
|||
slurs::{build_slur_regex, remove_slurs},
|
||||
validation::clean_urls_in_text,
|
||||
},
|
||||
CacheLock,
|
||||
CACHE_DURATION_FEDERATION,
|
||||
};
|
||||
use moka::future::Cache;
|
||||
use regex::{escape, Regex, RegexSet};
|
||||
use rosetta_i18n::{Language, LanguageId};
|
||||
use std::{collections::HashSet, sync::LazyLock};
|
||||
use std::sync::LazyLock;
|
||||
use tracing::warn;
|
||||
use url::{ParseError, Url};
|
||||
use urlencoding::encode;
|
||||
|
@ -141,19 +147,6 @@ pub fn is_top_mod(
|
|||
}
|
||||
}
|
||||
|
||||
/// Marks a post as read for a given person.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn mark_post_as_read(
|
||||
person_id: PersonId,
|
||||
post_id: PostId,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<()> {
|
||||
PostRead::mark_as_read(pool, HashSet::from([post_id]), person_id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates the read comment count for a post. Usually done when reading or creating a new comment.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn update_read_comments(
|
||||
|
@ -166,7 +159,6 @@ pub async fn update_read_comments(
|
|||
person_id,
|
||||
post_id,
|
||||
read_comments,
|
||||
..PersonPostAggregatesForm::default()
|
||||
};
|
||||
|
||||
PersonPostAggregates::upsert(pool, &person_post_agg_form).await?;
|
||||
|
@ -456,7 +448,11 @@ pub async fn send_password_reset_email(
|
|||
// Generate a random token
|
||||
let token = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
let email = &user.local_user.email.clone().expect("email");
|
||||
let email = &user
|
||||
.local_user
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
||||
let lang = get_interface_language(user);
|
||||
let subject = &lang.password_reset_subject(&user.person.name);
|
||||
let protocol_and_hostname = settings.get_protocol_and_hostname();
|
||||
|
@ -506,6 +502,7 @@ pub fn get_interface_language_from_settings(user: &LocalUserView) -> Lang {
|
|||
lang_str_to_lang(&user.local_user.interface_language)
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
fn lang_str_to_lang(lang: &str) -> Lang {
|
||||
let lang_id = LanguageId::new(lang);
|
||||
Lang::from_language_id(&lang_id).unwrap_or_else(|| {
|
||||
|
@ -532,11 +529,11 @@ pub fn local_site_rate_limit_to_rate_limit_config(
|
|||
})
|
||||
}
|
||||
|
||||
pub fn local_site_to_slur_regex(local_site: &LocalSite) -> Option<Regex> {
|
||||
pub fn local_site_to_slur_regex(local_site: &LocalSite) -> Option<LemmyResult<Regex>> {
|
||||
build_slur_regex(local_site.slur_filter_regex.as_deref())
|
||||
}
|
||||
|
||||
pub fn local_site_opt_to_slur_regex(local_site: &Option<LocalSite>) -> Option<Regex> {
|
||||
pub fn local_site_opt_to_slur_regex(local_site: &Option<LocalSite>) -> Option<LemmyResult<Regex>> {
|
||||
local_site
|
||||
.as_ref()
|
||||
.map(local_site_to_slur_regex)
|
||||
|
@ -544,7 +541,7 @@ pub fn local_site_opt_to_slur_regex(local_site: &Option<LocalSite>) -> Option<Re
|
|||
}
|
||||
|
||||
pub async fn get_url_blocklist(context: &LemmyContext) -> LemmyResult<RegexSet> {
|
||||
static URL_BLOCKLIST: LazyLock<Cache<(), RegexSet>> = LazyLock::new(|| {
|
||||
static URL_BLOCKLIST: CacheLock<RegexSet> = LazyLock::new(|| {
|
||||
Cache::builder()
|
||||
.max_capacity(1)
|
||||
.time_to_live(CACHE_DURATION_FEDERATION)
|
||||
|
@ -571,7 +568,11 @@ pub async fn send_application_approved_email(
|
|||
user: &LocalUserView,
|
||||
settings: &Settings,
|
||||
) -> LemmyResult<()> {
|
||||
let email = &user.local_user.email.clone().expect("email");
|
||||
let email = &user
|
||||
.local_user
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
||||
let lang = get_interface_language(user);
|
||||
let subject = lang.registration_approved_subject(&user.person.actor_id);
|
||||
let body = lang.registration_approved_body(&settings.hostname);
|
||||
|
@ -593,7 +594,11 @@ pub async fn send_new_applicant_email_to_admins(
|
|||
);
|
||||
|
||||
for admin in &admins {
|
||||
let email = &admin.local_user.email.clone().expect("email");
|
||||
let email = &admin
|
||||
.local_user
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
||||
let lang = get_interface_language_from_settings(admin);
|
||||
let subject = lang.new_application_subject(&settings.hostname, applicant_username);
|
||||
let body = lang.new_application_body(applications_link);
|
||||
|
@ -615,11 +620,13 @@ pub async fn send_new_report_email_to_admins(
|
|||
let reports_link = &format!("{}/reports", settings.get_protocol_and_hostname(),);
|
||||
|
||||
for admin in &admins {
|
||||
let email = &admin.local_user.email.clone().expect("email");
|
||||
let lang = get_interface_language_from_settings(admin);
|
||||
let subject = lang.new_report_subject(&settings.hostname, reported_username, reporter_username);
|
||||
let body = lang.new_report_body(reports_link);
|
||||
send_email(&subject, email, &admin.person.name, &body, settings).await?;
|
||||
if let Some(email) = &admin.local_user.email {
|
||||
let lang = get_interface_language_from_settings(admin);
|
||||
let subject =
|
||||
lang.new_report_subject(&settings.hostname, reported_username, reporter_username);
|
||||
let body = lang.new_report_body(reports_link);
|
||||
send_email(&subject, email, &admin.person.name, &body, settings).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1044,7 +1051,7 @@ pub fn check_conflicting_like_filters(
|
|||
|
||||
pub async fn process_markdown(
|
||||
text: &str,
|
||||
slur_regex: &Option<Regex>,
|
||||
slur_regex: &Option<LemmyResult<Regex>>,
|
||||
url_blocklist: &RegexSet,
|
||||
context: &LemmyContext,
|
||||
) -> LemmyResult<String> {
|
||||
|
@ -1076,7 +1083,7 @@ pub async fn process_markdown(
|
|||
|
||||
pub async fn process_markdown_opt(
|
||||
text: &Option<String>,
|
||||
slur_regex: &Option<Regex>,
|
||||
slur_regex: &Option<LemmyResult<Regex>>,
|
||||
url_blocklist: &RegexSet,
|
||||
context: &LemmyContext,
|
||||
) -> LemmyResult<Option<String>> {
|
||||
|
|
|
@ -25,7 +25,6 @@ tracing = { workspace = true }
|
|||
url = { workspace = true }
|
||||
futures.workspace = true
|
||||
uuid = { workspace = true }
|
||||
moka.workspace = true
|
||||
anyhow.workspace = true
|
||||
chrono.workspace = true
|
||||
webmention = "0.6.0"
|
||||
|
|
|
@ -16,9 +16,8 @@ use lemmy_api_common::{
|
|||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
impls::actor_language::default_post_language,
|
||||
impls::actor_language::validate_post_language,
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm},
|
||||
comment_reply::{CommentReply, CommentReplyUpdateForm},
|
||||
local_site::LocalSite,
|
||||
|
@ -93,21 +92,13 @@ pub async fn create_comment(
|
|||
check_comment_depth(parent)?;
|
||||
}
|
||||
|
||||
// attempt to set default language if none was provided
|
||||
let language_id = match data.language_id {
|
||||
Some(lid) => 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(), language_id, community_id)
|
||||
.await?;
|
||||
let language_id = validate_post_language(
|
||||
&mut context.pool(),
|
||||
data.language_id,
|
||||
community_id,
|
||||
local_user_view.local_user.id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let comment_form = CommentInsertForm {
|
||||
language_id: Some(language_id),
|
||||
|
|
|
@ -12,7 +12,7 @@ use lemmy_db_schema::{
|
|||
comment::{Comment, CommentUpdateForm},
|
||||
comment_report::CommentReport,
|
||||
local_user::LocalUser,
|
||||
moderator::{ModRemoveComment, ModRemoveCommentForm},
|
||||
mod_log::moderator::{ModRemoveComment, ModRemoveCommentForm},
|
||||
},
|
||||
traits::{Crud, Reportable},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use chrono::Utc;
|
||||
use lemmy_api_common::{
|
||||
build_response::{build_comment_response, send_local_notifs},
|
||||
comment::{CommentResponse, EditComment},
|
||||
|
@ -13,13 +14,12 @@ use lemmy_api_common::{
|
|||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
impls::actor_language::validate_post_language,
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
comment::{Comment, CommentUpdateForm},
|
||||
local_site::LocalSite,
|
||||
},
|
||||
traits::Crud,
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_db_views::structs::{CommentView, LocalUserView};
|
||||
use lemmy_utils::{
|
||||
|
@ -55,14 +55,13 @@ pub async fn update_comment(
|
|||
Err(LemmyErrorType::NoCommentEditAllowed)?
|
||||
}
|
||||
|
||||
if let Some(language_id) = data.language_id {
|
||||
CommunityLanguage::is_allowed_community_language(
|
||||
&mut context.pool(),
|
||||
language_id,
|
||||
orig_comment.community.id,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
let language_id = validate_post_language(
|
||||
&mut context.pool(),
|
||||
data.language_id,
|
||||
orig_comment.community.id,
|
||||
local_user_view.local_user.id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||
let url_blocklist = get_url_blocklist(&context).await?;
|
||||
|
@ -74,8 +73,8 @@ pub async fn update_comment(
|
|||
let comment_id = data.comment_id;
|
||||
let form = CommentUpdateForm {
|
||||
content,
|
||||
language_id: data.language_id,
|
||||
updated: Some(Some(naive_now())),
|
||||
language_id: Some(language_id),
|
||||
updated: Some(Some(Utc::now())),
|
||||
..Default::default()
|
||||
};
|
||||
let updated_comment = Comment::update(&mut context.pool(), comment_id, &form)
|
||||
|
|
|
@ -10,7 +10,7 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
community::{Community, CommunityUpdateForm},
|
||||
moderator::{ModRemoveCommunity, ModRemoveCommunityForm},
|
||||
mod_log::moderator::{ModRemoveCommunity, ModRemoveCommunityForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::check_community_visibility_allowed;
|
||||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use chrono::Utc;
|
||||
use lemmy_api_common::{
|
||||
build_response::build_community_response,
|
||||
community::{CommunityResponse, EditCommunity},
|
||||
|
@ -22,7 +23,7 @@ use lemmy_db_schema::{
|
|||
local_site::LocalSite,
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{diesel_string_update, diesel_url_update, naive_now},
|
||||
utils::{diesel_string_update, diesel_url_update},
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::{
|
||||
|
@ -95,7 +96,7 @@ pub async fn update_community(
|
|||
nsfw: data.nsfw,
|
||||
posting_restricted_to_mods: data.posting_restricted_to_mods,
|
||||
visibility: data.visibility,
|
||||
updated: Some(Some(naive_now())),
|
||||
updated: Some(Some(Utc::now())),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use chrono::Utc;
|
||||
use lemmy_api_common::{context::LemmyContext, oauth_provider::EditOAuthProvider, utils::is_admin};
|
||||
use lemmy_db_schema::{
|
||||
source::oauth_provider::{OAuthProvider, OAuthProviderUpdateForm},
|
||||
traits::Crud,
|
||||
utils::{diesel_required_string_update, diesel_required_url_update, naive_now},
|
||||
utils::{diesel_required_string_update, diesel_required_url_update},
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
@ -32,7 +33,7 @@ pub async fn update_oauth_provider(
|
|||
auto_verify_email: data.auto_verify_email,
|
||||
account_linking_enabled: data.account_linking_enabled,
|
||||
enabled: data.enabled,
|
||||
updated: Some(Some(naive_now())),
|
||||
updated: Some(Some(Utc::now())),
|
||||
};
|
||||
|
||||
let update_result =
|
||||
|
|
|
@ -12,17 +12,15 @@ use lemmy_api_common::{
|
|||
get_url_blocklist,
|
||||
honeypot_check,
|
||||
local_site_to_slur_regex,
|
||||
mark_post_as_read,
|
||||
process_markdown_opt,
|
||||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
impls::actor_language::default_post_language,
|
||||
impls::actor_language::validate_post_language,
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
community::Community,
|
||||
local_site::LocalSite,
|
||||
post::{Post, PostInsertForm, PostLike, PostLikeForm},
|
||||
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostRead, PostReadForm},
|
||||
},
|
||||
traits::{Crud, Likeable},
|
||||
utils::diesel_url_create,
|
||||
|
@ -98,23 +96,13 @@ pub async fn create_post(
|
|||
.await?;
|
||||
}
|
||||
|
||||
// attempt to set default language if none was provided
|
||||
let language_id = match data.language_id {
|
||||
Some(lid) => lid,
|
||||
None => {
|
||||
default_post_language(
|
||||
&mut context.pool(),
|
||||
community.id,
|
||||
local_user_view.local_user.id,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
};
|
||||
|
||||
// Only need to check if language is allowed in case user set it explicitly. When using default
|
||||
// language, it already only returns allowed languages.
|
||||
CommunityLanguage::is_allowed_community_language(&mut context.pool(), language_id, community.id)
|
||||
.await?;
|
||||
let language_id = validate_post_language(
|
||||
&mut context.pool(),
|
||||
data.language_id,
|
||||
data.community_id,
|
||||
local_user_view.local_user.id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let scheduled_publish_time =
|
||||
convert_published_time(data.scheduled_publish_time, &local_user_view, &context).await?;
|
||||
|
@ -154,17 +142,14 @@ pub async fn create_post(
|
|||
// They like their own post by default
|
||||
let person_id = local_user_view.person.id;
|
||||
let post_id = inserted_post.id;
|
||||
let like_form = PostLikeForm {
|
||||
post_id,
|
||||
person_id,
|
||||
score: 1,
|
||||
};
|
||||
let like_form = PostLikeForm::new(post_id, person_id, 1);
|
||||
|
||||
PostLike::like(&mut context.pool(), &like_form)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
|
||||
|
||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
||||
let read_form = PostReadForm::new(post_id, person_id);
|
||||
PostRead::mark_as_read(&mut context.pool(), &read_form).await?;
|
||||
|
||||
build_post_response(&context, community_id, local_user_view, post_id).await
|
||||
}
|
||||
|
|
|
@ -2,10 +2,13 @@ use actix_web::web::{Data, Json, Query};
|
|||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
post::{GetPost, GetPostResponse},
|
||||
utils::{check_private_instance, is_mod_or_admin_opt, mark_post_as_read, update_read_comments},
|
||||
utils::{check_private_instance, is_mod_or_admin_opt, update_read_comments},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{comment::Comment, post::Post},
|
||||
source::{
|
||||
comment::Comment,
|
||||
post::{Post, PostRead, PostReadForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::{
|
||||
|
@ -62,7 +65,8 @@ pub async fn get_post(
|
|||
|
||||
let post_id = post_view.post.id;
|
||||
if let Some(person_id) = person_id {
|
||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
||||
let read_form = PostReadForm::new(post_id, person_id);
|
||||
PostRead::mark_as_read(&mut context.pool(), &read_form).await?;
|
||||
|
||||
update_read_comments(
|
||||
person_id,
|
||||
|
|
|
@ -11,7 +11,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
community::Community,
|
||||
local_user::LocalUser,
|
||||
moderator::{ModRemovePost, ModRemovePostForm},
|
||||
mod_log::moderator::{ModRemovePost, ModRemovePostForm},
|
||||
post::{Post, PostUpdateForm},
|
||||
post_report::PostReport,
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::{convert_published_time, create::send_webmention};
|
||||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use chrono::Utc;
|
||||
use lemmy_api_common::{
|
||||
build_response::build_post_response,
|
||||
context::LemmyContext,
|
||||
|
@ -15,14 +16,14 @@ use lemmy_api_common::{
|
|||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
impls::actor_language::validate_post_language,
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
community::Community,
|
||||
local_site::LocalSite,
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{diesel_string_update, diesel_url_update, naive_now},
|
||||
utils::{diesel_string_update, diesel_url_update},
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, PostView};
|
||||
use lemmy_utils::{
|
||||
|
@ -101,14 +102,13 @@ pub async fn update_post(
|
|||
Err(LemmyErrorType::NoPostEditAllowed)?
|
||||
}
|
||||
|
||||
if let Some(language_id) = data.language_id {
|
||||
CommunityLanguage::is_allowed_community_language(
|
||||
&mut context.pool(),
|
||||
language_id,
|
||||
orig_post.community.id,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
let language_id = validate_post_language(
|
||||
&mut context.pool(),
|
||||
data.language_id,
|
||||
orig_post.post.community_id,
|
||||
local_user_view.local_user.id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// handle changes to scheduled_publish_time
|
||||
let scheduled_publish_time = match (
|
||||
|
@ -131,8 +131,8 @@ pub async fn update_post(
|
|||
body,
|
||||
alt_text,
|
||||
nsfw: data.nsfw,
|
||||
language_id: data.language_id,
|
||||
updated: Some(Some(naive_now())),
|
||||
language_id: Some(language_id),
|
||||
updated: Some(Some(Utc::now())),
|
||||
scheduled_publish_time,
|
||||
..Default::default()
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use chrono::Utc;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
private_message::{EditPrivateMessage, PrivateMessageResponse},
|
||||
|
@ -12,7 +13,6 @@ use lemmy_db_schema::{
|
|||
private_message::{PrivateMessage, PrivateMessageUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
|
||||
use lemmy_utils::{
|
||||
|
@ -47,7 +47,7 @@ pub async fn update_private_message(
|
|||
private_message_id,
|
||||
&PrivateMessageUpdateForm {
|
||||
content: Some(content),
|
||||
updated: Some(Some(naive_now())),
|
||||
updated: Some(Some(Utc::now())),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ use super::not_zero;
|
|||
use crate::site::{application_question_check, site_default_post_listing_type_check};
|
||||
use activitypub_federation::{config::Data, http_signatures::generate_actor_keypair};
|
||||
use actix_web::web::Json;
|
||||
use chrono::Utc;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
site::{CreateSite, SiteResponse},
|
||||
|
@ -23,7 +24,7 @@ use lemmy_db_schema::{
|
|||
site::{Site, SiteUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{diesel_string_update, diesel_url_create, naive_now},
|
||||
utils::{diesel_string_update, diesel_url_create},
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_utils::{
|
||||
|
@ -75,7 +76,7 @@ pub async fn create_site(
|
|||
icon: Some(icon),
|
||||
banner: Some(banner),
|
||||
actor_id: Some(actor_id),
|
||||
last_refreshed_at: Some(naive_now()),
|
||||
last_refreshed_at: Some(Utc::now()),
|
||||
inbox_url,
|
||||
private_key: Some(Some(keypair.private_key)),
|
||||
public_key: Some(keypair.public_key),
|
||||
|
@ -102,7 +103,7 @@ pub async fn create_site(
|
|||
legal_information: diesel_string_update(data.legal_information.as_deref()),
|
||||
application_email_admins: data.application_email_admins,
|
||||
hide_modlog_mod_names: data.hide_modlog_mod_names,
|
||||
updated: Some(Some(naive_now())),
|
||||
updated: Some(Some(Utc::now())),
|
||||
slur_filter_regex: diesel_string_update(data.slur_filter_regex.as_deref()),
|
||||
actor_name_max_length: data.actor_name_max_length,
|
||||
federation_enabled: data.federation_enabled,
|
||||
|
@ -161,7 +162,7 @@ fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) ->
|
|||
.slur_filter_regex
|
||||
.as_deref()
|
||||
.or(local_site.slur_filter_regex.as_deref()),
|
||||
)?;
|
||||
);
|
||||
|
||||
site_name_length_check(&create_site.name)?;
|
||||
check_slurs(&create_site.name, &slur_regex)?;
|
||||
|
|
|
@ -16,11 +16,11 @@ use lemmy_db_schema::source::{
|
|||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_db_views_actor::structs::{CommunityFollowerView, CommunityModeratorView, PersonView};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
CACHE_DURATION_API,
|
||||
build_cache,
|
||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
CacheLock,
|
||||
VERSION,
|
||||
};
|
||||
use moka::future::Cache;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
|
@ -28,41 +28,10 @@ pub async fn get_site(
|
|||
local_user_view: Option<LocalUserView>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<GetSiteResponse>> {
|
||||
static CACHE: LazyLock<Cache<(), GetSiteResponse>> = LazyLock::new(|| {
|
||||
Cache::builder()
|
||||
.max_capacity(1)
|
||||
.time_to_live(CACHE_DURATION_API)
|
||||
.build()
|
||||
});
|
||||
|
||||
// This data is independent from the user account so we can cache it across requests
|
||||
static CACHE: CacheLock<GetSiteResponse> = LazyLock::new(build_cache);
|
||||
let mut site_response = CACHE
|
||||
.try_get_with::<_, LemmyError>((), async {
|
||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
let admins = PersonView::admins(&mut context.pool()).await?;
|
||||
let all_languages = Language::read_all(&mut context.pool()).await?;
|
||||
let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
|
||||
let blocked_urls = LocalSiteUrlBlocklist::get_all(&mut context.pool()).await?;
|
||||
let tagline = Tagline::get_random(&mut context.pool()).await.ok();
|
||||
let admin_oauth_providers = OAuthProvider::get_all(&mut context.pool()).await?;
|
||||
let oauth_providers =
|
||||
OAuthProvider::convert_providers_to_public(admin_oauth_providers.clone());
|
||||
|
||||
Ok(GetSiteResponse {
|
||||
site_view,
|
||||
admins,
|
||||
version: VERSION.to_string(),
|
||||
my_user: None,
|
||||
all_languages,
|
||||
discussion_languages,
|
||||
blocked_urls,
|
||||
tagline,
|
||||
oauth_providers: Some(oauth_providers),
|
||||
admin_oauth_providers: Some(admin_oauth_providers),
|
||||
taglines: vec![],
|
||||
custom_emojis: vec![],
|
||||
})
|
||||
})
|
||||
.try_get_with((), read_site(&context))
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to construct site response: {e}"))?;
|
||||
|
||||
|
@ -112,3 +81,29 @@ pub async fn get_site(
|
|||
|
||||
Ok(Json(site_response))
|
||||
}
|
||||
|
||||
async fn read_site(context: &LemmyContext) -> LemmyResult<GetSiteResponse> {
|
||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
let admins = PersonView::admins(&mut context.pool()).await?;
|
||||
let all_languages = Language::read_all(&mut context.pool()).await?;
|
||||
let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
|
||||
let blocked_urls = LocalSiteUrlBlocklist::get_all(&mut context.pool()).await?;
|
||||
let tagline = Tagline::get_random(&mut context.pool()).await.ok();
|
||||
let admin_oauth_providers = OAuthProvider::get_all(&mut context.pool()).await?;
|
||||
let oauth_providers = OAuthProvider::convert_providers_to_public(admin_oauth_providers.clone());
|
||||
|
||||
Ok(GetSiteResponse {
|
||||
site_view,
|
||||
admins,
|
||||
version: VERSION.to_string(),
|
||||
my_user: None,
|
||||
all_languages,
|
||||
discussion_languages,
|
||||
blocked_urls,
|
||||
tagline,
|
||||
oauth_providers: Some(oauth_providers),
|
||||
admin_oauth_providers: Some(admin_oauth_providers),
|
||||
taglines: vec![],
|
||||
custom_emojis: vec![],
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use super::not_zero;
|
|||
use crate::site::{application_question_check, site_default_post_listing_type_check};
|
||||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use chrono::Utc;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
request::replace_image,
|
||||
|
@ -18,8 +19,6 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
actor_language::SiteLanguage,
|
||||
federation_allowlist::FederationAllowList,
|
||||
federation_blocklist::FederationBlockList,
|
||||
local_site::{LocalSite, LocalSiteUpdateForm},
|
||||
local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm},
|
||||
local_site_url_blocklist::LocalSiteUrlBlocklist,
|
||||
|
@ -27,7 +26,7 @@ use lemmy_db_schema::{
|
|||
site::{Site, SiteUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{diesel_string_update, diesel_url_update, naive_now},
|
||||
utils::{diesel_string_update, diesel_url_update},
|
||||
RegistrationMode,
|
||||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
|
@ -88,7 +87,7 @@ pub async fn update_site(
|
|||
icon,
|
||||
banner,
|
||||
content_warning: diesel_string_update(data.content_warning.as_deref()),
|
||||
updated: Some(Some(naive_now())),
|
||||
updated: Some(Some(Utc::now())),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -111,7 +110,7 @@ pub async fn update_site(
|
|||
legal_information: diesel_string_update(data.legal_information.as_deref()),
|
||||
application_email_admins: data.application_email_admins,
|
||||
hide_modlog_mod_names: data.hide_modlog_mod_names,
|
||||
updated: Some(Some(naive_now())),
|
||||
updated: Some(Some(Utc::now())),
|
||||
slur_filter_regex: diesel_string_update(data.slur_filter_regex.as_deref()),
|
||||
actor_name_max_length: data.actor_name_max_length,
|
||||
federation_enabled: data.federation_enabled,
|
||||
|
@ -151,12 +150,6 @@ pub async fn update_site(
|
|||
.await
|
||||
.ok();
|
||||
|
||||
// Replace the blocked and allowed instances
|
||||
let allowed = data.allowed_instances.clone();
|
||||
FederationAllowList::replace(&mut context.pool(), allowed).await?;
|
||||
let blocked = data.blocked_instances.clone();
|
||||
FederationBlockList::replace(&mut context.pool(), blocked).await?;
|
||||
|
||||
if let Some(url_blocklist) = data.blocked_urls.clone() {
|
||||
let parsed_urls = check_urls_are_valid(&url_blocklist)?;
|
||||
LocalSiteUrlBlocklist::replace(&mut context.pool(), parsed_urls).await?;
|
||||
|
@ -210,7 +203,7 @@ fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> Lemm
|
|||
.slur_filter_regex
|
||||
.as_deref()
|
||||
.or(local_site.slur_filter_regex.as_deref()),
|
||||
)?;
|
||||
);
|
||||
|
||||
if let Some(name) = &edit_site.name {
|
||||
// The name doesn't need to be updated, but if provided it cannot be blanked out...
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use chrono::Utc;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
tagline::{TaglineResponse, UpdateTagline},
|
||||
|
@ -11,7 +12,6 @@ use lemmy_db_schema::{
|
|||
tagline::{Tagline, TaglineUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
@ -33,7 +33,7 @@ pub async fn update_tagline(
|
|||
|
||||
let tagline_form = TaglineUpdateForm {
|
||||
content,
|
||||
updated: naive_now(),
|
||||
updated: Utc::now(),
|
||||
};
|
||||
|
||||
let tagline = Tagline::update(&mut context.pool(), data.id, &tagline_form).await?;
|
||||
|
|
|
@ -21,8 +21,9 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_db_schema::{
|
||||
aggregates::structs::PersonAggregates,
|
||||
newtypes::{InstanceId, OAuthProviderId},
|
||||
newtypes::{InstanceId, OAuthProviderId, SiteId},
|
||||
source::{
|
||||
actor_language::SiteLanguage,
|
||||
captcha_answer::{CaptchaAnswer, CheckCaptchaAnswer},
|
||||
language::Language,
|
||||
local_site::LocalSite,
|
||||
|
@ -145,17 +146,24 @@ pub async fn register(
|
|||
..LocalUserInsertForm::new(inserted_person.id, Some(data.password.to_string()))
|
||||
};
|
||||
|
||||
let inserted_local_user = create_local_user(&context, language_tags, &local_user_form).await?;
|
||||
let inserted_local_user = create_local_user(
|
||||
&context,
|
||||
language_tags,
|
||||
&local_user_form,
|
||||
local_site.site_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if local_site.site_setup && require_registration_application {
|
||||
// Create the registration application
|
||||
let form = RegistrationApplicationInsertForm {
|
||||
local_user_id: inserted_local_user.id,
|
||||
// We already made sure answer was not null above
|
||||
answer: data.answer.clone().expect("must have an answer"),
|
||||
};
|
||||
if let Some(answer) = data.answer.clone() {
|
||||
// Create the registration application
|
||||
let form = RegistrationApplicationInsertForm {
|
||||
local_user_id: inserted_local_user.id,
|
||||
answer,
|
||||
};
|
||||
|
||||
RegistrationApplication::create(&mut context.pool(), &form).await?;
|
||||
RegistrationApplication::create(&mut context.pool(), &form).await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Email the admins, only if email verification is not required
|
||||
|
@ -304,7 +312,7 @@ pub async fn authenticate_with_oauth(
|
|||
|
||||
OAuthAccount::create(&mut context.pool(), &oauth_account_form)
|
||||
.await
|
||||
.map_err(|_| LemmyErrorType::OauthLoginFailed)?;
|
||||
.with_lemmy_type(LemmyErrorType::OauthLoginFailed)?;
|
||||
|
||||
local_user = user_view.local_user.clone();
|
||||
} else {
|
||||
|
@ -357,7 +365,13 @@ pub async fn authenticate_with_oauth(
|
|||
..LocalUserInsertForm::new(person.id, None)
|
||||
};
|
||||
|
||||
local_user = create_local_user(&context, language_tags, &local_user_form).await?;
|
||||
local_user = create_local_user(
|
||||
&context,
|
||||
language_tags,
|
||||
&local_user_form,
|
||||
local_site.site_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Create the oauth account
|
||||
let oauth_account_form =
|
||||
|
@ -365,7 +379,7 @@ pub async fn authenticate_with_oauth(
|
|||
|
||||
OAuthAccount::create(&mut context.pool(), &oauth_account_form)
|
||||
.await
|
||||
.map_err(|_| LemmyErrorType::IncorrectLogin)?;
|
||||
.with_lemmy_type(LemmyErrorType::IncorrectLogin)?;
|
||||
|
||||
// prevent sign in until application is accepted
|
||||
if local_site.site_setup
|
||||
|
@ -373,17 +387,19 @@ pub async fn authenticate_with_oauth(
|
|||
&& !local_user.accepted_application
|
||||
&& !local_user.admin
|
||||
{
|
||||
// Create the registration application
|
||||
RegistrationApplication::create(
|
||||
&mut context.pool(),
|
||||
&RegistrationApplicationInsertForm {
|
||||
local_user_id: local_user.id,
|
||||
answer: data.answer.clone().expect("must have an answer"),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
if let Some(answer) = data.answer.clone() {
|
||||
// Create the registration application
|
||||
RegistrationApplication::create(
|
||||
&mut context.pool(),
|
||||
&RegistrationApplicationInsertForm {
|
||||
local_user_id: local_user.id,
|
||||
answer,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
login_response.registration_created = true;
|
||||
login_response.registration_created = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check email is verified when required
|
||||
|
@ -446,15 +462,23 @@ async fn create_local_user(
|
|||
context: &Data<LemmyContext>,
|
||||
language_tags: Vec<String>,
|
||||
local_user_form: &LocalUserInsertForm,
|
||||
local_site_id: SiteId,
|
||||
) -> Result<LocalUser, LemmyError> {
|
||||
let all_languages = Language::read_all(&mut context.pool()).await?;
|
||||
// use hashset to avoid duplicates
|
||||
let mut language_ids = HashSet::new();
|
||||
|
||||
// Enable languages from `Accept-Language` header
|
||||
for l in language_tags {
|
||||
if let Some(found) = all_languages.iter().find(|all| all.code == l) {
|
||||
language_ids.insert(found.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Enable site languages. Ignored if all languages are enabled.
|
||||
let discussion_languages = SiteLanguage::read(&mut context.pool(), local_site_id).await?;
|
||||
language_ids.extend(discussion_languages);
|
||||
|
||||
let language_ids = language_ids.into_iter().collect();
|
||||
|
||||
let inserted_local_user =
|
||||
|
@ -483,7 +507,7 @@ async fn send_verification_email_if_required(
|
|||
&local_user
|
||||
.email
|
||||
.clone()
|
||||
.expect("invalid verification email"),
|
||||
.ok_or(LemmyErrorType::EmailRequired)?,
|
||||
&mut context.pool(),
|
||||
context.settings(),
|
||||
)
|
||||
|
@ -524,18 +548,16 @@ async fn oauth_request_access_token(
|
|||
("client_secret", &oauth_provider.client_secret),
|
||||
])
|
||||
.send()
|
||||
.await;
|
||||
|
||||
let response = response.map_err(|_| LemmyErrorType::OauthLoginFailed)?;
|
||||
if !response.status().is_success() {
|
||||
Err(LemmyErrorType::OauthLoginFailed)?;
|
||||
}
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::OauthLoginFailed)?
|
||||
.error_for_status()
|
||||
.with_lemmy_type(LemmyErrorType::OauthLoginFailed)?;
|
||||
|
||||
// Extract the access token
|
||||
let token_response = response
|
||||
.json::<TokenResponse>()
|
||||
.await
|
||||
.map_err(|_| LemmyErrorType::OauthLoginFailed)?;
|
||||
.with_lemmy_type(LemmyErrorType::OauthLoginFailed)?;
|
||||
|
||||
Ok(token_response)
|
||||
}
|
||||
|
@ -552,18 +574,16 @@ async fn oidc_get_user_info(
|
|||
.header("Accept", "application/json")
|
||||
.bearer_auth(access_token)
|
||||
.send()
|
||||
.await;
|
||||
|
||||
let response = response.map_err(|_| LemmyErrorType::OauthLoginFailed)?;
|
||||
if !response.status().is_success() {
|
||||
Err(LemmyErrorType::OauthLoginFailed)?;
|
||||
}
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::OauthLoginFailed)?
|
||||
.error_for_status()
|
||||
.with_lemmy_type(LemmyErrorType::OauthLoginFailed)?;
|
||||
|
||||
// Extract the OAUTH user_id claim from the returned user_info
|
||||
let user_info = response
|
||||
.json::<serde_json::Value>()
|
||||
.await
|
||||
.map_err(|_| LemmyErrorType::OauthLoginFailed)?;
|
||||
.with_lemmy_type(LemmyErrorType::OauthLoginFailed)?;
|
||||
|
||||
Ok(user_info)
|
||||
}
|
||||
|
@ -571,7 +591,7 @@ async fn oidc_get_user_info(
|
|||
fn read_user_info(user_info: &serde_json::Value, key: &str) -> LemmyResult<String> {
|
||||
if let Some(value) = user_info.get(key) {
|
||||
let result = serde_json::from_value::<String>(value.clone())
|
||||
.map_err(|_| LemmyErrorType::OauthLoginFailed)?;
|
||||
.with_lemmy_type(LemmyErrorType::OauthLoginFailed)?;
|
||||
return Ok(result);
|
||||
}
|
||||
Err(LemmyErrorType::OauthLoginFailed)?
|
||||
|
|
|
@ -42,7 +42,7 @@ reqwest = { workspace = true }
|
|||
moka.workspace = true
|
||||
serde_with.workspace = true
|
||||
html2md = "0.2.14"
|
||||
html2text = "0.12.5"
|
||||
html2text = "0.12.6"
|
||||
stringreader = "0.1.1"
|
||||
enum_delegate = "0.2.0"
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ use lemmy_db_schema::{
|
|||
CommunityPersonBan,
|
||||
CommunityPersonBanForm,
|
||||
},
|
||||
moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
|
||||
mod_log::moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::{Bannable, Crud, Followable},
|
||||
|
|
|
@ -27,7 +27,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
activity::ActivitySendTargets,
|
||||
community::{CommunityPersonBan, CommunityPersonBanForm},
|
||||
moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
|
||||
mod_log::moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::{Bannable, Crud},
|
||||
|
|
|
@ -130,7 +130,7 @@ impl AnnounceActivity {
|
|||
actor: c.actor.clone().into_inner(),
|
||||
other: serde_json::to_value(c.object)?
|
||||
.as_object()
|
||||
.expect("is object")
|
||||
.ok_or(FederationError::Unreachable)?
|
||||
.clone(),
|
||||
};
|
||||
let announce_compat = AnnounceActivity::new(announcable_page, community, context)?;
|
||||
|
@ -215,7 +215,7 @@ async fn can_accept_activity_in_community(
|
|||
) -> LemmyResult<()> {
|
||||
if let Some(community) = community {
|
||||
// Local only community can't federate
|
||||
if community.visibility != CommunityVisibility::Public {
|
||||
if community.visibility == CommunityVisibility::LocalOnly {
|
||||
return Err(LemmyErrorType::NotFound.into());
|
||||
}
|
||||
if !community.local {
|
||||
|
|
|
@ -31,7 +31,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
activity::ActivitySendTargets,
|
||||
community::{Community, CommunityModerator, CommunityModeratorForm},
|
||||
moderator::{ModAddCommunity, ModAddCommunityForm},
|
||||
mod_log::moderator::{ModAddCommunity, ModAddCommunityForm},
|
||||
person::Person,
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
|
|
|
@ -27,7 +27,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
activity::ActivitySendTargets,
|
||||
community::{Community, CommunityModerator, CommunityModeratorForm},
|
||||
moderator::{ModAddCommunity, ModAddCommunityForm},
|
||||
mod_log::moderator::{ModAddCommunity, ModAddCommunityForm},
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::{Crud, Joinable},
|
||||
|
|
|
@ -27,7 +27,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
activity::ActivitySendTargets,
|
||||
community::Community,
|
||||
moderator::{ModLockPost, ModLockPostForm},
|
||||
mod_log::moderator::{ModLockPost, ModLockPostForm},
|
||||
person::Person,
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
|
|
|
@ -42,7 +42,7 @@ pub(crate) async fn send_activity_in_community(
|
|||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<()> {
|
||||
// If community is local only, don't send anything out
|
||||
if community.visibility != CommunityVisibility::Public {
|
||||
if community.visibility == CommunityVisibility::LocalOnly {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,8 @@ impl Report {
|
|||
let object_creator = Person::read(&mut context.pool(), object_creator_id).await?;
|
||||
let object_creator_site: Option<ApubSite> =
|
||||
Site::read_from_instance_id(&mut context.pool(), object_creator.instance_id)
|
||||
.await?
|
||||
.await
|
||||
.ok()
|
||||
.map(Into::into);
|
||||
if let Some(inbox) = object_creator_site.map(|s| s.shared_inbox_or_inbox()) {
|
||||
inboxes.add_inbox(inbox);
|
||||
|
|
|
@ -17,6 +17,7 @@ use activitypub_federation::{
|
|||
kinds::activity::UpdateType,
|
||||
traits::{ActivityHandler, Actor, Object},
|
||||
};
|
||||
use chrono::Utc;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
|
@ -25,7 +26,6 @@ use lemmy_db_schema::{
|
|||
person::Person,
|
||||
},
|
||||
traits::Crud,
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||
use url::Url;
|
||||
|
@ -103,7 +103,7 @@ impl ActivityHandler for UpdateCommunity {
|
|||
nsfw: Some(self.object.sensitive.unwrap_or(false)),
|
||||
actor_id: Some(self.object.id.into()),
|
||||
public_key: Some(self.object.public_key.public_key_pem),
|
||||
last_refreshed_at: Some(naive_now()),
|
||||
last_refreshed_at: Some(Utc::now()),
|
||||
icon: Some(self.object.icon.map(|i| i.url.into())),
|
||||
banner: Some(self.object.image.map(|i| i.url.into())),
|
||||
followers_url: self.object.followers.map(Into::into),
|
||||
|
|
|
@ -171,6 +171,9 @@ impl ActivityHandler for CreateOrUpdateNote {
|
|||
// TODO: for compatibility with other projects, it would be much better to read this from cc or
|
||||
// tags
|
||||
let mentions = scrape_text_for_mentions(&comment.content);
|
||||
|
||||
// TODO: this fails in local community comment as CommentView::read() returns nothing
|
||||
// without passing LocalUser
|
||||
send_local_notifs(mentions, comment.id, &actor, do_send_email, context, None).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -118,11 +118,7 @@ impl ActivityHandler for CreateOrUpdatePage {
|
|||
let post = ApubPost::from_json(self.object, context).await?;
|
||||
|
||||
// author likes their own post by default
|
||||
let like_form = PostLikeForm {
|
||||
post_id: post.id,
|
||||
person_id: post.creator_id,
|
||||
score: 1,
|
||||
};
|
||||
let like_form = PostLikeForm::new(post.id, post.creator_id, 1);
|
||||
PostLike::like(&mut context.pool(), &like_form).await?;
|
||||
|
||||
// Calculate initial hot_rank for post
|
||||
|
|
|
@ -14,7 +14,7 @@ use lemmy_db_schema::{
|
|||
comment::{Comment, CommentUpdateForm},
|
||||
comment_report::CommentReport,
|
||||
community::{Community, CommunityUpdateForm},
|
||||
moderator::{
|
||||
mod_log::moderator::{
|
||||
ModRemoveComment,
|
||||
ModRemoveCommentForm,
|
||||
ModRemoveCommunity,
|
||||
|
|
|
@ -13,7 +13,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
comment::{Comment, CommentUpdateForm},
|
||||
community::{Community, CommunityUpdateForm},
|
||||
moderator::{
|
||||
mod_log::moderator::{
|
||||
ModRemoveComment,
|
||||
ModRemoveCommentForm,
|
||||
ModRemoveCommunity,
|
||||
|
|
|
@ -79,11 +79,7 @@ async fn vote_post(
|
|||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<()> {
|
||||
let post_id = post.id;
|
||||
let like_form = PostLikeForm {
|
||||
post_id: post.id,
|
||||
person_id: actor.id,
|
||||
score: vote_type.into(),
|
||||
};
|
||||
let like_form = PostLikeForm::new(post.id, actor.id, vote_type.into());
|
||||
let person_id = actor.id;
|
||||
PostLike::remove(&mut context.pool(), person_id, post_id).await?;
|
||||
PostLike::like(&mut context.pool(), &like_form).await?;
|
||||
|
|
|
@ -10,7 +10,10 @@ use lemmy_api_common::{
|
|||
post::{GetPosts, GetPostsResponse},
|
||||
utils::{check_conflicting_like_filters, check_private_instance},
|
||||
};
|
||||
use lemmy_db_schema::source::community::Community;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::PostId,
|
||||
source::{community::Community, post::PostRead},
|
||||
};
|
||||
use lemmy_db_views::{
|
||||
post_view::PostQuery,
|
||||
structs::{LocalUserView, PaginationCursor, SiteView},
|
||||
|
@ -90,6 +93,17 @@ pub async fn list_posts(
|
|||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntGetPosts)?;
|
||||
|
||||
// If in their user settings (or as part of the API request), auto-mark fetched posts as read
|
||||
if let Some(local_user) = local_user {
|
||||
if data
|
||||
.mark_as_read
|
||||
.unwrap_or(local_user.auto_mark_fetched_posts_as_read)
|
||||
{
|
||||
let post_ids = posts.iter().map(|p| p.post.id).collect::<Vec<PostId>>();
|
||||
PostRead::mark_many_as_read(&mut context.pool(), &post_ids, local_user.person_id).await?;
|
||||
}
|
||||
}
|
||||
|
||||
// if this page wasn't empty, then there is a next page after the last post on this page
|
||||
let next_page = posts.last().map(PaginationCursor::after_post);
|
||||
Ok(Json(GetPostsResponse { posts, next_page }))
|
||||
|
|
|
@ -200,10 +200,7 @@ pub async fn import_settings(
|
|||
&context,
|
||||
|(saved, context)| async move {
|
||||
let post = saved.dereference(&context).await?;
|
||||
let form = PostSavedForm {
|
||||
person_id,
|
||||
post_id: post.id,
|
||||
};
|
||||
let form = PostSavedForm::new(post.id, person_id);
|
||||
PostSaved::save(&mut context.pool(), &form).await?;
|
||||
LemmyResult::Ok(())
|
||||
},
|
||||
|
@ -325,7 +322,7 @@ pub(crate) mod tests {
|
|||
CommunityFollowerState,
|
||||
CommunityInsertForm,
|
||||
},
|
||||
local_user::LocalUser,
|
||||
person::Person,
|
||||
},
|
||||
traits::{Crud, Followable},
|
||||
};
|
||||
|
@ -379,8 +376,8 @@ pub(crate) mod tests {
|
|||
assert_eq!(follows.len(), 1);
|
||||
assert_eq!(follows[0].community.actor_id, community.actor_id);
|
||||
|
||||
LocalUser::delete(pool, export_user.local_user.id).await?;
|
||||
LocalUser::delete(pool, import_user.local_user.id).await?;
|
||||
Person::delete(pool, export_user.person.id).await?;
|
||||
Person::delete(pool, import_user.person.id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -415,8 +412,8 @@ pub(crate) mod tests {
|
|||
Some(LemmyErrorType::TooManyItems)
|
||||
);
|
||||
|
||||
LocalUser::delete(pool, export_user.local_user.id).await?;
|
||||
LocalUser::delete(pool, import_user.local_user.id).await?;
|
||||
Person::delete(pool, export_user.person.id).await?;
|
||||
Person::delete(pool, import_user.person.id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use activitypub_federation::{
|
|||
};
|
||||
use diesel::NotFound;
|
||||
use itertools::Itertools;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_api_common::{context::LemmyContext, LemmyErrorType};
|
||||
use lemmy_db_schema::traits::ApubActor;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||
|
@ -42,7 +42,7 @@ where
|
|||
let (name, domain) = identifier
|
||||
.splitn(2, '@')
|
||||
.collect_tuple()
|
||||
.expect("invalid query");
|
||||
.ok_or(LemmyErrorType::InvalidUrl)?;
|
||||
let actor = DbActor::read_from_name_and_domain(&mut context.pool(), name, domain)
|
||||
.await
|
||||
.ok()
|
||||
|
|
|
@ -6,28 +6,41 @@ use crate::{
|
|||
community_moderators::ApubCommunityModerators,
|
||||
community_outbox::ApubCommunityOutbox,
|
||||
},
|
||||
fetcher::site_or_community_or_user::SiteOrCommunityOrUser,
|
||||
http::{check_community_fetchable, create_apub_response, create_apub_tombstone_response},
|
||||
objects::community::ApubCommunity,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
actix_web::signing_actor,
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
traits::{Collection, Object},
|
||||
};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use actix_web::{
|
||||
web::{Path, Query},
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{source::community::Community, traits::ApubActor};
|
||||
use lemmy_db_schema::{source::community::Community, traits::ApubActor, CommunityVisibility};
|
||||
use lemmy_db_views_actor::structs::CommunityFollowerView;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub(crate) struct CommunityQuery {
|
||||
pub(crate) struct CommunityPath {
|
||||
community_name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct CommunityIsFollowerQuery {
|
||||
is_follower: Option<ObjectId<SiteOrCommunityOrUser>>,
|
||||
}
|
||||
|
||||
/// Return the ActivityPub json representation of a local community over HTTP.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn get_apub_community_http(
|
||||
info: web::Path<CommunityQuery>,
|
||||
info: Path<CommunityPath>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<HttpResponse> {
|
||||
let community: ApubCommunity =
|
||||
|
@ -47,21 +60,59 @@ pub(crate) async fn get_apub_community_http(
|
|||
|
||||
/// Returns an empty followers collection, only populating the size (for privacy).
|
||||
pub(crate) async fn get_apub_community_followers(
|
||||
info: web::Path<CommunityQuery>,
|
||||
info: Path<CommunityPath>,
|
||||
query: Query<CommunityIsFollowerQuery>,
|
||||
context: Data<LemmyContext>,
|
||||
request: HttpRequest,
|
||||
) -> LemmyResult<HttpResponse> {
|
||||
let community = Community::read_from_name(&mut context.pool(), &info.community_name, false)
|
||||
.await?
|
||||
.ok_or(LemmyErrorType::NotFound)?;
|
||||
if let Some(is_follower) = &query.is_follower {
|
||||
return check_is_follower(community, is_follower, context, request).await;
|
||||
}
|
||||
check_community_fetchable(&community)?;
|
||||
let followers = ApubCommunityFollower::read_local(&community.into(), &context).await?;
|
||||
create_apub_response(&followers)
|
||||
}
|
||||
|
||||
/// Checks if a given actor follows the private community. Returns status 200 if true.
|
||||
async fn check_is_follower(
|
||||
community: Community,
|
||||
is_follower: &ObjectId<SiteOrCommunityOrUser>,
|
||||
context: Data<LemmyContext>,
|
||||
request: HttpRequest,
|
||||
) -> LemmyResult<HttpResponse> {
|
||||
if community.visibility != CommunityVisibility::Private {
|
||||
return Ok(HttpResponse::BadRequest().body("must be a private community"));
|
||||
}
|
||||
// also check for http sig so that followers are not exposed publicly
|
||||
let signing_actor = signing_actor::<SiteOrCommunityOrUser>(&request, None, &context).await?;
|
||||
CommunityFollowerView::check_has_followers_from_instance(
|
||||
community.id,
|
||||
signing_actor.instance_id(),
|
||||
&mut context.pool(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let instance_id = is_follower.dereference(&context).await?.instance_id();
|
||||
let has_followers = CommunityFollowerView::check_has_followers_from_instance(
|
||||
community.id,
|
||||
instance_id,
|
||||
&mut context.pool(),
|
||||
)
|
||||
.await;
|
||||
if has_followers.is_ok() {
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
} else {
|
||||
Ok(HttpResponse::NotFound().finish())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the community outbox, which is populated by a maximum of 20 posts (but no other
|
||||
/// activities like votes or comments).
|
||||
pub(crate) async fn get_apub_community_outbox(
|
||||
info: web::Path<CommunityQuery>,
|
||||
info: Path<CommunityPath>,
|
||||
context: Data<LemmyContext>,
|
||||
request: HttpRequest,
|
||||
) -> LemmyResult<HttpResponse> {
|
||||
|
@ -77,7 +128,7 @@ pub(crate) async fn get_apub_community_outbox(
|
|||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn get_apub_community_moderators(
|
||||
info: web::Path<CommunityQuery>,
|
||||
info: Path<CommunityPath>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<HttpResponse> {
|
||||
let community: ApubCommunity =
|
||||
|
@ -92,7 +143,7 @@ pub(crate) async fn get_apub_community_moderators(
|
|||
|
||||
/// Returns collection of featured (stickied) posts.
|
||||
pub(crate) async fn get_apub_community_featured(
|
||||
info: web::Path<CommunityQuery>,
|
||||
info: Path<CommunityPath>,
|
||||
context: Data<LemmyContext>,
|
||||
request: HttpRequest,
|
||||
) -> LemmyResult<HttpResponse> {
|
||||
|
@ -181,17 +232,17 @@ pub(crate) mod tests {
|
|||
let request = TestRequest::default().to_http_request();
|
||||
|
||||
// fetch invalid community
|
||||
let query = CommunityQuery {
|
||||
let query = CommunityPath {
|
||||
community_name: "asd".to_string(),
|
||||
};
|
||||
let res = get_apub_community_http(query.into(), context.reset_request_count()).await;
|
||||
assert!(res.is_err());
|
||||
|
||||
// fetch valid community
|
||||
let query = CommunityQuery {
|
||||
let path = CommunityPath {
|
||||
community_name: community.name.clone(),
|
||||
};
|
||||
let res = get_apub_community_http(query.clone().into(), context.reset_request_count()).await?;
|
||||
let res = get_apub_community_http(path.clone().into(), context.reset_request_count()).await?;
|
||||
assert_eq!(200, res.status());
|
||||
let res_group: Group = decode_response(res).await?;
|
||||
let community: ApubCommunity = community.into();
|
||||
|
@ -199,20 +250,26 @@ pub(crate) mod tests {
|
|||
assert_eq!(group, res_group);
|
||||
|
||||
let res = get_apub_community_featured(
|
||||
query.clone().into(),
|
||||
path.clone().into(),
|
||||
context.reset_request_count(),
|
||||
request.clone(),
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(200, res.status());
|
||||
let query = Query(CommunityIsFollowerQuery { is_follower: None });
|
||||
let res = get_apub_community_followers(
|
||||
path.clone().into(),
|
||||
query,
|
||||
context.reset_request_count(),
|
||||
request.clone(),
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(200, res.status());
|
||||
let res =
|
||||
get_apub_community_followers(query.clone().into(), context.reset_request_count()).await?;
|
||||
get_apub_community_moderators(path.clone().into(), context.reset_request_count()).await?;
|
||||
assert_eq!(200, res.status());
|
||||
let res =
|
||||
get_apub_community_moderators(query.clone().into(), context.reset_request_count()).await?;
|
||||
assert_eq!(200, res.status());
|
||||
let res =
|
||||
get_apub_community_outbox(query.into(), context.reset_request_count(), request).await?;
|
||||
get_apub_community_outbox(path.into(), context.reset_request_count(), request).await?;
|
||||
assert_eq!(200, res.status());
|
||||
|
||||
Instance::delete(&mut context.pool(), instance.id).await?;
|
||||
|
@ -227,28 +284,35 @@ pub(crate) mod tests {
|
|||
let request = TestRequest::default().to_http_request();
|
||||
|
||||
// should return tombstone
|
||||
let query = CommunityQuery {
|
||||
let path: Path<CommunityPath> = CommunityPath {
|
||||
community_name: community.name.clone(),
|
||||
};
|
||||
let res = get_apub_community_http(query.clone().into(), context.reset_request_count()).await?;
|
||||
}
|
||||
.into();
|
||||
let res = get_apub_community_http(path.clone().into(), context.reset_request_count()).await?;
|
||||
assert_eq!(410, res.status());
|
||||
let res_tombstone = decode_response::<Tombstone>(res).await;
|
||||
assert!(res_tombstone.is_ok());
|
||||
|
||||
let res = get_apub_community_featured(
|
||||
query.clone().into(),
|
||||
path.clone().into(),
|
||||
context.reset_request_count(),
|
||||
request.clone(),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_err());
|
||||
let query = Query(CommunityIsFollowerQuery { is_follower: None });
|
||||
let res = get_apub_community_followers(
|
||||
path.clone().into(),
|
||||
query,
|
||||
context.reset_request_count(),
|
||||
request.clone(),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_err());
|
||||
let res =
|
||||
get_apub_community_followers(query.clone().into(), context.reset_request_count()).await;
|
||||
get_apub_community_moderators(path.clone().into(), context.reset_request_count()).await;
|
||||
assert!(res.is_err());
|
||||
let res =
|
||||
get_apub_community_moderators(query.clone().into(), context.reset_request_count()).await;
|
||||
assert!(res.is_err());
|
||||
let res = get_apub_community_outbox(query.into(), context.reset_request_count(), request).await;
|
||||
let res = get_apub_community_outbox(path, context.reset_request_count(), request).await;
|
||||
assert!(res.is_err());
|
||||
|
||||
//Community::delete(&mut context.pool(), community.id).await?;
|
||||
|
@ -263,25 +327,32 @@ pub(crate) mod tests {
|
|||
let (instance, community) = init(false, CommunityVisibility::LocalOnly, &context).await?;
|
||||
let request = TestRequest::default().to_http_request();
|
||||
|
||||
let query = CommunityQuery {
|
||||
let path: Path<CommunityPath> = CommunityPath {
|
||||
community_name: community.name.clone(),
|
||||
};
|
||||
let res = get_apub_community_http(query.clone().into(), context.reset_request_count()).await;
|
||||
}
|
||||
.into();
|
||||
let res = get_apub_community_http(path.clone().into(), context.reset_request_count()).await;
|
||||
assert!(res.is_err());
|
||||
let res = get_apub_community_featured(
|
||||
query.clone().into(),
|
||||
path.clone().into(),
|
||||
context.reset_request_count(),
|
||||
request.clone(),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_err());
|
||||
let query = Query(CommunityIsFollowerQuery { is_follower: None });
|
||||
let res = get_apub_community_followers(
|
||||
path.clone().into(),
|
||||
query,
|
||||
context.reset_request_count(),
|
||||
request.clone(),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_err());
|
||||
let res =
|
||||
get_apub_community_followers(query.clone().into(), context.reset_request_count()).await;
|
||||
get_apub_community_moderators(path.clone().into(), context.reset_request_count()).await;
|
||||
assert!(res.is_err());
|
||||
let res =
|
||||
get_apub_community_moderators(query.clone().into(), context.reset_request_count()).await;
|
||||
assert!(res.is_err());
|
||||
let res = get_apub_community_outbox(query.into(), context.reset_request_count(), request).await;
|
||||
let res = get_apub_community_outbox(path, context.reset_request_count(), request).await;
|
||||
assert!(res.is_err());
|
||||
|
||||
Instance::delete(&mut context.pool(), instance.id).await?;
|
||||
|
|
|
@ -8,6 +8,7 @@ use activitypub_federation::{
|
|||
actix_web::{inbox::receive_activity, signing_actor},
|
||||
config::Data,
|
||||
protocol::context::WithContext,
|
||||
traits::Actor,
|
||||
FEDERATION_CONTENT_TYPE,
|
||||
};
|
||||
use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
|
||||
|
@ -18,7 +19,7 @@ use lemmy_db_schema::{
|
|||
CommunityVisibility,
|
||||
};
|
||||
use lemmy_db_views_actor::structs::CommunityFollowerView;
|
||||
use lemmy_utils::error::{FederationError, LemmyErrorType, LemmyResult};
|
||||
use lemmy_utils::error::{FederationError, LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{ops::Deref, time::Duration};
|
||||
use tokio::time::timeout;
|
||||
|
@ -46,7 +47,7 @@ pub async fn shared_inbox(
|
|||
// consider the activity broken and move on.
|
||||
timeout(INCOMING_ACTIVITY_TIMEOUT, receive_fut)
|
||||
.await
|
||||
.map_err(|_| FederationError::InboxTimeout)?
|
||||
.with_lemmy_type(FederationError::InboxTimeout.into())?
|
||||
}
|
||||
|
||||
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
|
||||
|
@ -109,7 +110,7 @@ pub(crate) async fn get_activity(
|
|||
.into();
|
||||
let activity = SentActivity::read_from_apub_id(&mut context.pool(), &activity_id)
|
||||
.await
|
||||
.map_err(|_| FederationError::CouldntFindActivity)?;
|
||||
.with_lemmy_type(FederationError::CouldntFindActivity.into())?;
|
||||
|
||||
let sensitive = activity.sensitive;
|
||||
if sensitive {
|
||||
|
@ -145,14 +146,27 @@ async fn check_community_content_fetchable(
|
|||
// from the fetching instance then fetching is allowed
|
||||
Private => {
|
||||
let signing_actor = signing_actor::<SiteOrCommunityOrUser>(request, None, context).await?;
|
||||
Ok(
|
||||
CommunityFollowerView::check_has_followers_from_instance(
|
||||
community.id,
|
||||
signing_actor.instance_id(),
|
||||
&mut context.pool(),
|
||||
if community.local {
|
||||
Ok(
|
||||
CommunityFollowerView::check_has_followers_from_instance(
|
||||
community.id,
|
||||
signing_actor.instance_id(),
|
||||
&mut context.pool(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
} else if let Some(followers_url) = community.followers_url.clone() {
|
||||
let mut followers_url = followers_url.inner().clone();
|
||||
followers_url
|
||||
.query_pairs_mut()
|
||||
.append_pair("is_follower", signing_actor.id().as_str());
|
||||
let req = context.client().get(followers_url.as_str());
|
||||
let req = context.sign_request(req, Bytes::new()).await?;
|
||||
context.client().execute(req).await?.error_for_status()?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(LemmyErrorType::NotFound.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use lemmy_db_schema::{
|
|||
};
|
||||
use lemmy_utils::{
|
||||
error::{FederationError, LemmyError, LemmyErrorType, LemmyResult},
|
||||
CacheLock,
|
||||
CACHE_DURATION_FEDERATION,
|
||||
};
|
||||
use moka::future::Cache;
|
||||
|
@ -50,7 +51,8 @@ impl UrlVerifier for VerifyUrlData {
|
|||
async fn verify(&self, url: &Url) -> Result<(), ActivityPubError> {
|
||||
let local_site_data = local_site_data_cached(&mut (&self.0).into())
|
||||
.await
|
||||
.expect("read local site data");
|
||||
.map_err(|e| ActivityPubError::Other(format!("Cant read local site data: {e}")))?;
|
||||
|
||||
use FederationError::*;
|
||||
check_apub_id_valid(url, &local_site_data).map_err(|err| match err {
|
||||
LemmyError {
|
||||
|
@ -138,7 +140,7 @@ pub(crate) async fn local_site_data_cached(
|
|||
// multiple times. This causes a huge number of database reads if we hit the db directly. So we
|
||||
// cache these values for a short time, which will already make a huge difference and ensures that
|
||||
// changes take effect quickly.
|
||||
static CACHE: LazyLock<Cache<(), Arc<LocalSiteData>>> = LazyLock::new(|| {
|
||||
static CACHE: CacheLock<Arc<LocalSiteData>> = LazyLock::new(|| {
|
||||
Cache::builder()
|
||||
.max_capacity(1)
|
||||
.time_to_live(CACHE_DURATION_FEDERATION)
|
||||
|
@ -176,10 +178,7 @@ pub(crate) async fn check_apub_id_valid_with_strictness(
|
|||
.domain()
|
||||
.ok_or(FederationError::UrlWithoutDomain)?
|
||||
.to_string();
|
||||
let local_instance = context
|
||||
.settings()
|
||||
.get_hostname_without_port()
|
||||
.expect("local hostname is valid");
|
||||
let local_instance = context.settings().get_hostname_without_port()?;
|
||||
if domain == local_instance {
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -196,10 +195,7 @@ pub(crate) async fn check_apub_id_valid_with_strictness(
|
|||
.iter()
|
||||
.map(|i| i.domain.clone())
|
||||
.collect::<Vec<String>>();
|
||||
let local_instance = context
|
||||
.settings()
|
||||
.get_hostname_without_port()
|
||||
.expect("local hostname is valid");
|
||||
let local_instance = context.settings().get_hostname_without_port()?;
|
||||
allowed_and_local.push(local_instance);
|
||||
|
||||
let domain = apub_id
|
||||
|
|
|
@ -30,7 +30,6 @@ use lemmy_db_schema::{
|
|||
post::Post,
|
||||
},
|
||||
traits::Crud,
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_utils::{
|
||||
error::{FederationError, LemmyError, LemmyResult},
|
||||
|
@ -204,7 +203,7 @@ impl Object for ApubComment {
|
|||
language_id,
|
||||
};
|
||||
let parent_comment_path = parent_comment.map(|t| t.0.path);
|
||||
let timestamp: DateTime<Utc> = note.updated.or(note.published).unwrap_or_else(naive_now);
|
||||
let timestamp: DateTime<Utc> = note.updated.or(note.published).unwrap_or_else(Utc::now);
|
||||
let comment = Comment::insert_apub(
|
||||
&mut context.pool(),
|
||||
Some(timestamp),
|
||||
|
|
|
@ -38,7 +38,6 @@ use lemmy_db_schema::{
|
|||
local_site::LocalSite,
|
||||
},
|
||||
traits::{ApubActor, Crud},
|
||||
utils::naive_now,
|
||||
CommunityVisibility,
|
||||
};
|
||||
use lemmy_db_views_actor::structs::CommunityFollowerView;
|
||||
|
@ -166,7 +165,7 @@ impl Object for ApubCommunity {
|
|||
nsfw: Some(group.sensitive.unwrap_or(false)),
|
||||
actor_id: Some(group.id.into()),
|
||||
local: Some(false),
|
||||
last_refreshed_at: Some(naive_now()),
|
||||
last_refreshed_at: Some(Utc::now()),
|
||||
icon,
|
||||
banner,
|
||||
sidebar,
|
||||
|
@ -193,11 +192,17 @@ impl Object for ApubCommunity {
|
|||
let languages =
|
||||
LanguageTag::to_language_id_multiple(group.language, &mut context.pool()).await?;
|
||||
|
||||
let timestamp = group.updated.or(group.published).unwrap_or_else(naive_now);
|
||||
let community = Community::insert_apub(&mut context.pool(), timestamp, &form).await?;
|
||||
let timestamp = group.updated.or(group.published).unwrap_or_else(Utc::now);
|
||||
let community: ApubCommunity = Community::insert_apub(&mut context.pool(), timestamp, &form)
|
||||
.await?
|
||||
.into();
|
||||
CommunityLanguage::update(&mut context.pool(), languages, community.id).await?;
|
||||
|
||||
let community: ApubCommunity = community.into();
|
||||
// Need to fetch mods synchronously, otherwise fetching a post in community with
|
||||
// `posting_restricted_to_mods` can fail if mods havent been fetched yet.
|
||||
if let Some(moderators) = group.attributed_to {
|
||||
moderators.dereference(&community, context).await.ok();
|
||||
}
|
||||
|
||||
// These collections are not necessary for Lemmy to work, so ignore errors.
|
||||
let community_ = community.clone();
|
||||
|
@ -210,9 +215,6 @@ impl Object for ApubCommunity {
|
|||
if let Some(featured) = group.featured {
|
||||
featured.dereference(&community_, &context_).await.ok();
|
||||
}
|
||||
if let Some(moderators) = group.attributed_to {
|
||||
moderators.dereference(&community_, &context_).await.ok();
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ use lemmy_db_schema::{
|
|||
site::{Site, SiteInsertForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_utils::{
|
||||
error::{FederationError, LemmyError, LemmyResult},
|
||||
|
@ -163,7 +162,7 @@ impl Object for ApubSite {
|
|||
banner,
|
||||
description: apub.summary,
|
||||
actor_id: Some(apub.id.clone().into()),
|
||||
last_refreshed_at: Some(naive_now()),
|
||||
last_refreshed_at: Some(Utc::now()),
|
||||
inbox_url: Some(apub.inbox.clone().into()),
|
||||
public_key: Some(apub.public_key.public_key_pem.clone()),
|
||||
private_key: None,
|
||||
|
|
|
@ -35,7 +35,6 @@ use lemmy_db_schema::{
|
|||
person::{Person as DbPerson, PersonInsertForm, PersonUpdateForm},
|
||||
},
|
||||
traits::{ApubActor, Crud},
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyError, LemmyResult},
|
||||
|
@ -176,7 +175,7 @@ impl Object for ApubPerson {
|
|||
bot_account: Some(person.kind == UserTypes::Service),
|
||||
private_key: None,
|
||||
public_key: person.public_key.public_key_pem,
|
||||
last_refreshed_at: Some(naive_now()),
|
||||
last_refreshed_at: Some(Utc::now()),
|
||||
inbox_url: Some(
|
||||
person
|
||||
.endpoints
|
||||
|
|
|
@ -35,7 +35,6 @@ use lemmy_db_schema::{
|
|||
post::{Post, PostInsertForm, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
||||
use lemmy_utils::{
|
||||
|
@ -260,7 +259,7 @@ impl Object for ApubPost {
|
|||
..PostInsertForm::new(name, creator.id, community.id)
|
||||
};
|
||||
|
||||
let timestamp = page.updated.or(page.published).unwrap_or_else(naive_now);
|
||||
let timestamp = page.updated.or(page.published).unwrap_or_else(Utc::now);
|
||||
let post = Post::insert_apub(&mut context.pool(), timestamp, &form).await?;
|
||||
let post_ = post.clone();
|
||||
let context_ = context.reset_request_count();
|
||||
|
|
|
@ -31,7 +31,6 @@ use lemmy_db_schema::{
|
|||
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::{
|
||||
|
@ -161,7 +160,7 @@ impl Object for ApubPrivateMessage {
|
|||
ap_id: Some(note.id.into()),
|
||||
local: Some(false),
|
||||
};
|
||||
let timestamp = note.updated.or(note.published).unwrap_or_else(naive_now);
|
||||
let timestamp = note.updated.or(note.published).unwrap_or_else(Utc::now);
|
||||
let pm = PrivateMessage::insert_apub(&mut context.pool(), timestamp, &form).await?;
|
||||
Ok(pm.into())
|
||||
}
|
||||
|
|
|
@ -60,9 +60,9 @@ impl<T, S: SelectableExpression<current_value>> SelectableExpression<T> for Valu
|
|||
|
||||
impl<T, S: SelectableExpression<current_value>> Insertable<T> for ValuesFromSeries<S>
|
||||
where
|
||||
dsl::BareSelect<Self>: AsQuery + Insertable<T>,
|
||||
dsl::select<Self>: AsQuery + Insertable<T>,
|
||||
{
|
||||
type Values = <dsl::BareSelect<Self> as Insertable<T>>::Values;
|
||||
type Values = <dsl::select<Self> as Insertable<T>>::Values;
|
||||
|
||||
fn values(self) -> Self::Values {
|
||||
dsl::select(self).values()
|
||||
|
|
|
@ -37,6 +37,8 @@ full = [
|
|||
"tokio-postgres-rustls",
|
||||
"rustls",
|
||||
"i-love-jesus",
|
||||
"tuplex",
|
||||
"diesel-bind-if-some",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
|
@ -73,11 +75,12 @@ tokio = { workspace = true, optional = 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
|
||||
i-love-jesus = { workspace = true, optional = true }
|
||||
anyhow = { workspace = true }
|
||||
moka.workspace = true
|
||||
diesel-bind-if-some = { workspace = true, optional = true }
|
||||
derive-new.workspace = true
|
||||
tuplex = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = { workspace = true }
|
||||
|
|
|
@ -38,7 +38,7 @@ AS $a$
|
|||
BEGIN
|
||||
EXECUTE replace($b$
|
||||
-- When a thing gets a vote, update its aggregates and its creator's aggregates
|
||||
CALL r.create_triggers ('thing_like', $$
|
||||
CALL r.create_triggers ('thing_actions', $$
|
||||
BEGIN
|
||||
WITH thing_diff AS ( UPDATE
|
||||
thing_aggregates AS a
|
||||
|
@ -46,7 +46,8 @@ BEGIN
|
|||
score = a.score + diff.upvotes - diff.downvotes, upvotes = a.upvotes + diff.upvotes, downvotes = a.downvotes + diff.downvotes, controversy_rank = r.controversy_rank ((a.upvotes + diff.upvotes)::numeric, (a.downvotes + diff.downvotes)::numeric)
|
||||
FROM (
|
||||
SELECT
|
||||
(thing_like).thing_id, coalesce(sum(count_diff) FILTER (WHERE (thing_like).score = 1), 0) AS upvotes, coalesce(sum(count_diff) FILTER (WHERE (thing_like).score != 1), 0) AS downvotes FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (thing_like).thing_id) AS diff
|
||||
(thing_actions).thing_id, coalesce(sum(count_diff) FILTER (WHERE (thing_actions).like_score = 1), 0) AS upvotes, coalesce(sum(count_diff) FILTER (WHERE (thing_actions).like_score != 1), 0) AS downvotes FROM select_old_and_new_rows AS old_and_new_rows
|
||||
WHERE (thing_actions).like_score IS NOT NULL GROUP BY (thing_actions).thing_id) AS diff
|
||||
WHERE
|
||||
a.thing_id = diff.thing_id
|
||||
AND (diff.upvotes, diff.downvotes) != (0, 0)
|
||||
|
@ -360,7 +361,7 @@ CREATE TRIGGER comment_count
|
|||
-- Count subscribers for communities.
|
||||
-- subscribers should be updated only when a local community is followed by a local or remote person.
|
||||
-- subscribers_local should be updated only when a local person follows a local or remote community.
|
||||
CALL r.create_triggers ('community_follower', $$
|
||||
CALL r.create_triggers ('community_actions', $$
|
||||
BEGIN
|
||||
UPDATE
|
||||
community_aggregates AS a
|
||||
|
@ -368,10 +369,11 @@ BEGIN
|
|||
subscribers = a.subscribers + diff.subscribers, subscribers_local = a.subscribers_local + diff.subscribers_local
|
||||
FROM (
|
||||
SELECT
|
||||
(community_follower).community_id, coalesce(sum(count_diff) FILTER (WHERE community.local), 0) AS subscribers, coalesce(sum(count_diff) FILTER (WHERE person.local), 0) AS subscribers_local
|
||||
(community_actions).community_id, coalesce(sum(count_diff) FILTER (WHERE community.local), 0) AS subscribers, coalesce(sum(count_diff) FILTER (WHERE person.local), 0) AS subscribers_local
|
||||
FROM select_old_and_new_rows AS old_and_new_rows
|
||||
LEFT JOIN community ON community.id = (community_follower).community_id
|
||||
LEFT JOIN person ON person.id = (community_follower).person_id GROUP BY (community_follower).community_id) AS diff
|
||||
LEFT JOIN community ON community.id = (community_actions).community_id
|
||||
LEFT JOIN person ON person.id = (community_actions).person_id
|
||||
WHERE (community_actions).followed IS NOT NULL GROUP BY (community_actions).community_id) AS diff
|
||||
WHERE
|
||||
a.community_id = diff.community_id
|
||||
AND (diff.subscribers, diff.subscribers_local) != (0, 0);
|
||||
|
@ -382,6 +384,44 @@ END;
|
|||
|
||||
$$);
|
||||
|
||||
CALL r.create_triggers ('post_report', $$
|
||||
BEGIN
|
||||
UPDATE
|
||||
post_aggregates AS a
|
||||
SET
|
||||
report_count = a.report_count + diff.report_count, unresolved_report_count = a.unresolved_report_count + diff.unresolved_report_count
|
||||
FROM (
|
||||
SELECT
|
||||
(post_report).post_id, coalesce(sum(count_diff), 0) AS report_count, coalesce(sum(count_diff) FILTER (WHERE NOT (post_report).resolved), 0) AS unresolved_report_count
|
||||
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (post_report).post_id) AS diff
|
||||
WHERE (diff.report_count, diff.unresolved_report_count) != (0, 0)
|
||||
AND a.post_id = diff.post_id;
|
||||
|
||||
RETURN NULL;
|
||||
|
||||
END;
|
||||
|
||||
$$);
|
||||
|
||||
CALL r.create_triggers ('comment_report', $$
|
||||
BEGIN
|
||||
UPDATE
|
||||
comment_aggregates AS a
|
||||
SET
|
||||
report_count = a.report_count + diff.report_count, unresolved_report_count = a.unresolved_report_count + diff.unresolved_report_count
|
||||
FROM (
|
||||
SELECT
|
||||
(comment_report).comment_id, coalesce(sum(count_diff), 0) AS report_count, coalesce(sum(count_diff) FILTER (WHERE NOT (comment_report).resolved), 0) AS unresolved_report_count
|
||||
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (comment_report).comment_id) AS diff
|
||||
WHERE (diff.report_count, diff.unresolved_report_count) != (0, 0)
|
||||
AND a.comment_id = diff.comment_id;
|
||||
|
||||
RETURN NULL;
|
||||
|
||||
END;
|
||||
|
||||
$$);
|
||||
|
||||
-- These triggers create and update rows in each aggregates table to match its associated table's rows.
|
||||
-- Deleting rows and updating IDs are already handled by `CASCADE` in foreign key constraints.
|
||||
CREATE FUNCTION r.comment_aggregates_from_comment ()
|
||||
|
@ -541,7 +581,7 @@ CREATE FUNCTION r.delete_follow_before_person ()
|
|||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
DELETE FROM community_follower AS c
|
||||
DELETE FROM community_actions AS c
|
||||
WHERE c.person_id = OLD.id;
|
||||
RETURN OLD;
|
||||
END;
|
||||
|
|
|
@ -151,3 +151,118 @@ DECLARE
|
|||
END;
|
||||
$a$;
|
||||
|
||||
-- Edit community aggregates to include voters as active users
|
||||
CREATE OR REPLACE FUNCTION r.community_aggregates_activity (i text)
|
||||
RETURNS TABLE (
|
||||
count_ bigint,
|
||||
community_id_ integer)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN query
|
||||
SELECT
|
||||
count(*),
|
||||
community_id
|
||||
FROM (
|
||||
SELECT
|
||||
c.creator_id,
|
||||
p.community_id
|
||||
FROM
|
||||
comment c
|
||||
INNER JOIN post p ON c.post_id = p.id
|
||||
INNER JOIN person pe ON c.creator_id = pe.id
|
||||
WHERE
|
||||
c.published > ('now'::timestamp - i::interval)
|
||||
AND pe.bot_account = FALSE
|
||||
UNION
|
||||
SELECT
|
||||
p.creator_id,
|
||||
p.community_id
|
||||
FROM
|
||||
post p
|
||||
INNER JOIN person pe ON p.creator_id = pe.id
|
||||
WHERE
|
||||
p.published > ('now'::timestamp - i::interval)
|
||||
AND pe.bot_account = FALSE
|
||||
UNION
|
||||
SELECT
|
||||
pl.person_id,
|
||||
p.community_id
|
||||
FROM
|
||||
post_like pl
|
||||
INNER JOIN post p ON pl.post_id = p.id
|
||||
INNER JOIN person pe ON pl.person_id = pe.id
|
||||
WHERE
|
||||
pl.published > ('now'::timestamp - i::interval)
|
||||
AND pe.bot_account = FALSE
|
||||
UNION
|
||||
SELECT
|
||||
cl.person_id,
|
||||
p.community_id
|
||||
FROM
|
||||
comment_like cl
|
||||
INNER JOIN comment c ON cl.comment_id = c.id
|
||||
INNER JOIN post p ON c.post_id = p.id
|
||||
INNER JOIN person pe ON cl.person_id = pe.id
|
||||
WHERE
|
||||
cl.published > ('now'::timestamp - i::interval)
|
||||
AND pe.bot_account = FALSE) a
|
||||
GROUP BY
|
||||
community_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Edit site aggregates to include voters and people who have read posts as active users
|
||||
CREATE OR REPLACE FUNCTION r.site_aggregates_activity (i text)
|
||||
RETURNS integer
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
count_ integer;
|
||||
BEGIN
|
||||
SELECT
|
||||
count(*) INTO count_
|
||||
FROM (
|
||||
SELECT
|
||||
c.creator_id
|
||||
FROM
|
||||
comment c
|
||||
INNER JOIN person pe ON c.creator_id = pe.id
|
||||
WHERE
|
||||
c.published > ('now'::timestamp - i::interval)
|
||||
AND pe.local = TRUE
|
||||
AND pe.bot_account = FALSE
|
||||
UNION
|
||||
SELECT
|
||||
p.creator_id
|
||||
FROM
|
||||
post p
|
||||
INNER JOIN person pe ON p.creator_id = pe.id
|
||||
WHERE
|
||||
p.published > ('now'::timestamp - i::interval)
|
||||
AND pe.local = TRUE
|
||||
AND pe.bot_account = FALSE
|
||||
UNION
|
||||
SELECT
|
||||
pl.person_id
|
||||
FROM
|
||||
post_like pl
|
||||
INNER JOIN person pe ON pl.person_id = pe.id
|
||||
WHERE
|
||||
pl.published > ('now'::timestamp - i::interval)
|
||||
AND pe.local = TRUE
|
||||
AND pe.bot_account = FALSE
|
||||
UNION
|
||||
SELECT
|
||||
cl.person_id
|
||||
FROM
|
||||
comment_like cl
|
||||
INNER JOIN person pe ON cl.person_id = pe.id
|
||||
WHERE
|
||||
cl.published > ('now'::timestamp - i::interval)
|
||||
AND pe.local = TRUE
|
||||
AND pe.bot_account = FALSE) a;
|
||||
RETURN count_;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
|
|
@ -65,11 +65,7 @@ mod tests {
|
|||
);
|
||||
let inserted_post = Post::create(pool, &new_post).await?;
|
||||
|
||||
let post_like = PostLikeForm {
|
||||
post_id: inserted_post.id,
|
||||
person_id: inserted_person.id,
|
||||
score: 1,
|
||||
};
|
||||
let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, 1);
|
||||
let _inserted_post_like = PostLike::like(pool, &post_like).await?;
|
||||
|
||||
let comment_form = CommentInsertForm::new(
|
||||
|
|
|
@ -2,10 +2,17 @@ use crate::{
|
|||
aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
|
||||
diesel::OptionalExtension,
|
||||
newtypes::{PersonId, PostId},
|
||||
schema::person_post_aggregates::dsl::{person_id, person_post_aggregates, post_id},
|
||||
utils::{get_conn, DbPool},
|
||||
schema::post_actions,
|
||||
utils::{find_action, get_conn, now, DbPool},
|
||||
};
|
||||
use diesel::{
|
||||
expression::SelectableHelper,
|
||||
insert_into,
|
||||
result::Error,
|
||||
ExpressionMethods,
|
||||
NullableExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel::{insert_into, result::Error, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
|
||||
impl PersonPostAggregates {
|
||||
|
@ -14,11 +21,13 @@ impl PersonPostAggregates {
|
|||
form: &PersonPostAggregatesForm,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(person_post_aggregates)
|
||||
let form = (form, post_actions::read_comments.eq(now().nullable()));
|
||||
insert_into(post_actions::table)
|
||||
.values(form)
|
||||
.on_conflict((person_id, post_id))
|
||||
.on_conflict((post_actions::person_id, post_actions::post_id))
|
||||
.do_update()
|
||||
.set(form)
|
||||
.returning(Self::as_select())
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
@ -28,8 +37,8 @@ impl PersonPostAggregates {
|
|||
post_id_: PostId,
|
||||
) -> Result<Option<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
person_post_aggregates
|
||||
.find((person_id_, post_id_))
|
||||
find_action(post_actions::read_comments, (person_id_, post_id_))
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
.optional()
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue